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">