diff --git a/src/dlangui/core/types.d b/src/dlangui/core/types.d
index 421bf017..b4b11b62 100644
--- a/src/dlangui/core/types.d
+++ b/src/dlangui/core/types.d
@@ -201,8 +201,10 @@ int pixelsToPoints(int px) {
enum SubpixelRenderingMode : ubyte {
/// no sub
None,
- /// subpixel rendering on, subpixel order: B,G,R
+ /// subpixel rendering on, subpixel order on device: B,G,R
BGR,
+ /// subpixel rendering on, subpixel order on device: R,G,B
+ RGB,
}
/**
@@ -215,6 +217,9 @@ struct Glyph
{
/// 0: width of glyph black box
ushort blackBoxX;
+
+ @property ushort correctedBlackBoxX() { return subpixelMode ? blackBoxX / 3 : blackBoxX; }
+
/// 2: height of glyph black box
ubyte blackBoxY;
/// 3: X origin for glyph
diff --git a/src/dlangui/graphics/colors.d b/src/dlangui/graphics/colors.d
index 23f4d82c..eb16f5fd 100644
--- a/src/dlangui/graphics/colors.d
+++ b/src/dlangui/graphics/colors.d
@@ -18,6 +18,8 @@ Authors: Vadim Lopatin, coolreader.org@gmail.com
*/
module dlangui.graphics.colors;
+import dlangui.core.types;
+
private import std.string : strip;
/// special color constant to identify value as not a color (to use default/parent value instead)
@@ -51,6 +53,37 @@ uint blendARGB(uint dst, uint src, uint alpha) {
return (r << 16) | (g << 8) | b;
}
+//immutable int COMPONENT_OFFSET_BGR[3] = [2, 1, 0];
+immutable int COMPONENT_OFFSET_BGR[3] = [2, 1, 0];
+//immutable int COMPONENT_OFFSET_BGR[3] = [1, 2, 0];
+immutable int COMPONENT_OFFSET_RGB[3] = [0, 1, 2];
+immutable int COMPONENT_OFFSET_ALPHA = 3;
+int subpixelComponentIndex(int x0, SubpixelRenderingMode mode) {
+ switch (mode) {
+ case SubpixelRenderingMode.RGB:
+ return COMPONENT_OFFSET_BGR[x0];
+ case SubpixelRenderingMode.BGR:
+ default:
+ return COMPONENT_OFFSET_BGR[x0];
+ }
+}
+
+/// blend subpixel using alpha
+void blendSubpixel(ubyte * dst, ubyte * src, uint alpha, int x0, SubpixelRenderingMode mode) {
+ uint dstalpha = dst[COMPONENT_OFFSET_ALPHA];
+ int offset = subpixelComponentIndex(x0, mode);
+ uint srcr = src[offset];
+ dst[COMPONENT_OFFSET_ALPHA] = 0;
+ if (dstalpha > 0x80) {
+ dst[offset] = cast(ubyte)srcr;
+ return;
+ }
+ uint dstr = dst[offset];
+ uint ialpha = 256 - alpha;
+ uint r = ((srcr * ialpha + dstr * alpha) >> 8) & 0xFF;
+ dst[offset] = cast(ubyte)r;
+}
+
/// blend two alpha values 0..255 (255 is fully transparent, 0 is opaque)
uint blendAlpha(uint a1, uint a2) {
if (!a1)
diff --git a/src/dlangui/graphics/drawbuf.d b/src/dlangui/graphics/drawbuf.d
index c55cdf5d..3344bda0 100644
--- a/src/dlangui/graphics/drawbuf.d
+++ b/src/dlangui/graphics/drawbuf.d
@@ -562,6 +562,7 @@ class ColorDrawBufBase : DrawBuf {
int srcdy = glyph.blackBoxY;
bool clipping = true; //!_clipRect.empty();
color = applyAlpha(color);
+ bool subpixel = glyph.subpixelMode != SubpixelRenderingMode.None;
for (int yy = 0; yy < srcdy; yy++) {
int liney = y + yy;
if (clipping && (liney < _clipRect.top || liney >= _clipRect.bottom))
@@ -571,21 +572,30 @@ class ColorDrawBufBase : DrawBuf {
uint * row = scanLine(liney);
ubyte * srcrow = src.ptr + yy * srcdx;
for (int xx = 0; xx < srcdx; xx++) {
- int colx = xx + x;
+ int colx = x + (subpixel ? xx / 3 : xx);
if (clipping && (colx < _clipRect.left || colx >= _clipRect.right))
continue;
if (colx < 0 || colx >= _dx)
continue;
- uint alpha1 = srcrow[xx] ^ 255;
uint alpha2 = (color >> 24);
- uint alpha = ((((alpha1 ^ 255) * (alpha2 ^ 255)) >> 8) ^ 255) & 255;
- uint pixel = row[colx];
- if (!alpha)
- row[colx] = pixel;
- else if (alpha < 255) {
- // apply blending
- row[colx] = blendARGB(pixel, color, alpha);
- }
+ uint alpha1 = srcrow[xx] ^ 255;
+ uint alpha = ((((alpha1 ^ 255) * (alpha2 ^ 255)) >> 8) ^ 255) & 255;
+ if (subpixel) {
+ int x0 = xx % 3;
+ ubyte * dst = cast(ubyte*)(row + colx);
+ ubyte * pcolor = cast(ubyte*)(&color);
+ blendSubpixel(dst, pcolor, alpha, x0, glyph.subpixelMode);
+ } else {
+ uint pixel = row[colx];
+ if (alpha < 255) {
+ if (!alpha)
+ row[colx] = pixel;
+ else {
+ // apply blending
+ row[colx] = blendARGB(pixel, color, alpha);
+ }
+ }
+ }
}
}
}
diff --git a/src/dlangui/graphics/fonts.d b/src/dlangui/graphics/fonts.d
index 310f6226..691a0608 100644
--- a/src/dlangui/graphics/fonts.d
+++ b/src/dlangui/graphics/fonts.d
@@ -243,7 +243,7 @@ class Font : RefCountedObject {
continue;
}
int w = x + glyph.width; // using advance
- int w2 = x + glyph.originX + glyph.blackBoxX; // using black box
+ int w2 = x + glyph.originX + glyph.correctedBlackBoxX; // using black box
if (w < w2) // choose bigger value
w = w2;
pwidths[i] = w;
@@ -334,7 +334,7 @@ class Font : RefCountedObject {
continue;
if ( glyph.blackBoxX && glyph.blackBoxY ) {
int gx = x + xx + glyph.originX;
- if (gx + glyph.blackBoxX < clip.left)
+ if (gx + glyph.correctedBlackBoxX < clip.left)
continue;
buf.drawGlyph( gx,
y + _baseline - glyph.originY,
@@ -405,7 +405,7 @@ class Font : RefCountedObject {
continue;
if ( glyph.blackBoxX && glyph.blackBoxY ) {
int gx = x + xx + glyph.originX;
- if (gx + glyph.blackBoxX < clip.left)
+ if (gx + glyph.correctedBlackBoxX < clip.left)
continue;
buf.drawGlyph( gx,
y + _baseline - glyph.originY,
diff --git a/src/dlangui/graphics/ftfonts.d b/src/dlangui/graphics/ftfonts.d
index e9aed16d..de4966c6 100644
--- a/src/dlangui/graphics/ftfonts.d
+++ b/src/dlangui/graphics/ftfonts.d
@@ -266,6 +266,7 @@ private class FreeTypeFontFile {
glyph.originX = cast(byte)(_slot.metrics.horiBearingX >> 6);
glyph.originY = cast(byte)(_slot.metrics.horiBearingY >> 6);
glyph.width = cast(ubyte)(myabs(cast(int)(_slot.metrics.horiAdvance)) >> 6);
+ glyph.subpixelMode = SubpixelRenderingMode.None;
//glyph.glyphIndex = cast(ushort)code;
if (withImage) {
FT_Bitmap* bitmap = &_slot.bitmap;
diff --git a/src/dlangui/graphics/gldrawbuf.d b/src/dlangui/graphics/gldrawbuf.d
index 2bf67d61..5aeee356 100644
--- a/src/dlangui/graphics/gldrawbuf.d
+++ b/src/dlangui/graphics/gldrawbuf.d
@@ -92,8 +92,8 @@ class GLDrawBuf : DrawBuf {
/// draw 8bit alpha image - usually font glyph using specified color (clipping is applied)
override void drawGlyph(int x, int y, Glyph * glyph, uint color) {
assert(_scene !is null);
- Rect dstrect = Rect(x,y, x + glyph.blackBoxX, y + glyph.blackBoxY);
- Rect srcrect = Rect(0, 0, glyph.blackBoxX, glyph.blackBoxY);
+ Rect dstrect = Rect(x,y, x + glyph.correctedBlackBoxX, y + glyph.blackBoxY);
+ Rect srcrect = Rect(0, 0, glyph.correctedBlackBoxX, glyph.blackBoxY);
//Log.v("GLDrawBuf.drawGlyph dst=", dstrect, " src=", srcrect, " color=", color);
color = applyAlpha(color);
if (!isFullyTransparentColor(color) && applyClipping(dstrect, srcrect)) {
@@ -659,7 +659,7 @@ private class GLGlyphCache {
return _itemCount;
}
GLGlyphCacheItem addItem(Glyph * glyph) {
- GLGlyphCacheItem cacheItem = reserveSpace(glyph.id, glyph.blackBoxX, glyph.blackBoxY);
+ GLGlyphCacheItem cacheItem = reserveSpace(glyph.id, glyph.correctedBlackBoxX, glyph.blackBoxY);
if (cacheItem is null)
return null;
_drawbuf.drawGlyph(cacheItem._rc.left, cacheItem._rc.top, glyph, 0xFFFFFF);
diff --git a/src/dlangui/platforms/windows/win32fonts.d b/src/dlangui/platforms/windows/win32fonts.d
index df3d2ee6..9e83d41d 100644
--- a/src/dlangui/platforms/windows/win32fonts.d
+++ b/src/dlangui/platforms/windows/win32fonts.d
@@ -107,6 +107,22 @@ __gshared static this() {
lut = lcd_distribution_lut!65(0.5, 0.25, 0.125);
}
+private int myabs(int n) {
+ return n < 0 ? -n : n;
+}
+private int colorStat(ubyte * p) {
+ int avg = (cast(int)p[0] + cast(int)p[1] + cast(int)p[2]) / 3;
+ return myabs(avg - cast(int)p[0]) + myabs(avg - cast(int)p[1]) + myabs(avg - cast(int)p[2]);
+}
+
+private int minIndex(int n0, int n1, int n2) {
+ if (n0 <= n1 && n0 <= n2)
+ return 0;
+ if (n1 <= n0 && n1 <= n2)
+ return 1;
+ return n2;
+}
+
// This function prepares the alpha-channel information
// for the glyph averaging the values in accordance with
// the method suggested by Steve Gibson. The function
@@ -118,8 +134,10 @@ __gshared static this() {
//---------------------------------
ushort prepare_lcd_glyph(ubyte * gbuf1,
ref GLYPHMETRICS gm,
- ref ubyte[] gbuf2)
+ ref ubyte[] gbuf2,
+ ref int shiftedBy)
{
+ shiftedBy = 0;
uint src_stride = (gm.gmBlackBoxX + 3) / 4 * 4;
uint dst_width = src_stride + 4;
gbuf2 = new ubyte[dst_width * gm.gmBlackBoxY];
@@ -128,8 +146,7 @@ ushort prepare_lcd_glyph(ubyte * gbuf1,
{
ubyte * src_ptr = gbuf1 + src_stride * y;
ubyte * dst_ptr = gbuf2.ptr + dst_width * y;
- uint x;
- for(x = 0; x < gm.gmBlackBoxX; ++x)
+ for(uint x = 0; x < gm.gmBlackBoxX; ++x)
{
uint v = *src_ptr++;
dst_ptr[0] += lut.tertiary(v);
@@ -140,6 +157,31 @@ ushort prepare_lcd_glyph(ubyte * gbuf1,
++dst_ptr;
}
}
+ /*
+ int dx = (dst_width - 2) / 3;
+ int stats[3] = [0, 0, 0];
+ for (uint y = 0; y < gm.gmBlackBoxY; y++) {
+ for(uint x = 0; x < dx; ++x)
+ {
+ for (uint x0 = 0; x0 < 3; x0++) {
+ stats[x0] += colorStat(gbuf2.ptr + dst_width * y + x0);
+ }
+ }
+ }
+ shiftedBy = 0; //minIndex(stats[0], stats[1], stats[2]);
+ if (shiftedBy) {
+ for (uint y = 0; y < gm.gmBlackBoxY; y++) {
+ ubyte * dst_ptr = gbuf2.ptr + dst_width * y;
+ for(uint x = 0; x < gm.gmBlackBoxX; ++x)
+ {
+ if (x + shiftedBy < gm.gmBlackBoxX)
+ dst_ptr[x] = dst_ptr[x + shiftedBy];
+ else
+ dst_ptr[x] = 0;
+ }
+ }
+ }
+ */
return cast(ushort) dst_width;
}
@@ -271,9 +313,9 @@ class Win32Font : Font {
}
g.blackBoxX = cast(ushort)metrics.gmBlackBoxX;
g.blackBoxY = cast(ubyte)metrics.gmBlackBoxY;
- g.originX = cast(byte)(needSubpixelRendering ? metrics.gmptGlyphOrigin.x / 3: metrics.gmptGlyphOrigin.x);
+ g.originX = cast(byte)(needSubpixelRendering ? ((metrics.gmptGlyphOrigin.x + 0) / 3) : (metrics.gmptGlyphOrigin.x));
g.originY = cast(byte)metrics.gmptGlyphOrigin.y;
- g.width = cast(ubyte)(needSubpixelRendering ? metrics.gmCellIncX / 3 : metrics.gmCellIncX);
+ g.width = cast(ubyte)(needSubpixelRendering ? (metrics.gmCellIncX + 0) / 3 : metrics.gmCellIncX);
g.subpixelMode = needSubpixelRendering ? FontManager.subpixelRenderingMode : SubpixelRenderingMode.None;
//g.glyphIndex = cast(ushort)glyphIndex;
@@ -296,11 +338,14 @@ class Win32Font : Font {
}
if (needSubpixelRendering) {
ubyte[] newglyph;
- g.blackBoxX = prepare_lcd_glyph(glyph.ptr,
+ int shiftedBy = 0;
+ g.blackBoxX = prepare_lcd_glyph(glyph.ptr,
metrics,
- newglyph);
+ newglyph,
+ shiftedBy);
g.glyph = newglyph;
- //g.width = g.width / 3;
+ //g.originX = cast(ubyte)((metrics.gmptGlyphOrigin.x + 2 - shiftedBy) / 3);
+ //g.width = cast(ubyte)((metrics.gmCellIncX + 2 - shiftedBy) / 3);
} else {
int glyph_row_size = (g.blackBoxX + 3) / 4 * 4;
ubyte * src = glyph.ptr;
diff --git a/views/res/theme_default.xml b/views/res/theme_default.xml
index 0b1b8bbe..cc3763a0 100644
--- a/views/res/theme_default.xml
+++ b/views/res/theme_default.xml
@@ -315,7 +315,7 @@
layoutHeight="WRAP_CONTENT"
fontFace="Arial"
fontFamily="SansSerif"
- fontSize="12">
+ fontSize="16">