From e33478c678634725547ca6003d3b2553a91631c2 Mon Sep 17 00:00:00 2001 From: ketmar Date: Sun, 21 Nov 2021 01:35:43 +0000 Subject: [PATCH] egra: and even more faster text rendering FossilOrigin-Name: 588a51122df72324ae520e5fd435d71358c5485f7f577bd07b02175109a45344 --- egra/gfx/text.d | 223 ++++++++++++++++++++++++++++++++++++++++++-------------- egra/test.d | 1 + 2 files changed, 168 insertions(+), 56 deletions(-) diff --git a/egra/gfx/text.d b/egra/gfx/text.d index 10ebe4d..1f8cb5f 100644 --- a/egra/gfx/text.d +++ b/egra/gfx/text.d @@ -39,10 +39,11 @@ version (DigitalMars) { // ////////////////////////////////////////////////////////////////////////// // enum ReplacementChar = 0xFFFD; -//__gshared string chiFontName = "Arial:pixelsize=16"; -//__gshared string chiFontName = "Verdana:pixelsize=16"; -__gshared string chiFontName = "Verdana:weight=bold:pixelsize=16"; -__gshared string chiFontFile; +//__gshared string egraFontName = "Arial:pixelsize=16"; +//__gshared string egraFontName = "Verdana:pixelsize=16"; +public __gshared string egraFontName = "Verdana:weight=bold:pixelsize=16"; +public __gshared int egraDefaultFontSize = 0; +__gshared string egraFontFile; __gshared int fontSize; __gshared int fontHeight; __gshared int fontBaselineOfs; @@ -91,9 +92,9 @@ extern(C) nothrow { if (face_id == FontID) { try { if (ttfontdata is null) { - conwriteln("TTF CACHE: loading '", chiFontFile, "'..."); + conwriteln("TTF CACHE: loading '", egraFontFile, "'..."); import core.stdc.stdlib : malloc; - auto fl = VFile(chiFontFile); + auto fl = VFile(egraFontFile); auto fsz = fl.size; if (fsz < 16 || fsz > int.max/8) throw new Exception("invalid ttf size"); ttfontdatasize = cast(uint)fsz; @@ -102,7 +103,7 @@ extern(C) nothrow { fl.rawReadExact(ttfontdata[0..ttfontdatasize]); } auto res = FT_New_Memory_Face(library, cast(const(FT_Byte)*)ttfontdata, ttfontdatasize, 0, aface); - if (res != 0) throw new Exception("error loading ttf: '"~chiFontFile~"'"); + if (res != 0) throw new Exception("error loading ttf: '"~egraFontFile~"'"); (*aface).generic.data = ttfontdata; (*aface).generic.finalizer = &ttfFontFinalizer; } catch (Exception e) { @@ -151,7 +152,7 @@ void initFontEngine () nothrow { import std.string : fromStringz/*, toStringz*/; import std.internal.cstring : tempCString; if (!FcInit()) assert(0, "cannot init fontconfig"); - iv.fontconfig.FcPattern* pat = FcNameParse(chiFontName.tempCString); + iv.fontconfig.FcPattern* pat = FcNameParse(egraFontName.tempCString); if (pat is null) assert(0, "cannot parse font name"); if (!FcConfigSubstitute(null, pat, FcMatchPattern)) assert(0, "cannot find fontconfig substitute"); FcDefaultSubstitute(pat); @@ -162,7 +163,7 @@ void initFontEngine () nothrow { char* file = null; if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) { version(aliced) conwriteln("font file: [", file, "]"); - chiFontFile = file.fromStringz.idup; + egraFontFile = file.fromStringz.idup; } double pixelsize; if (FcPatternGetDouble(font, FC_PIXEL_SIZE, 0, &pixelsize) == FcResultMatch) { @@ -174,6 +175,7 @@ void initFontEngine () nothrow { // arbitrary limits if (fontSize < 6) fontSize = 6; if (fontSize > 42) fontSize = 42; + if (egraDefaultFontSize > 0) fontSize = egraDefaultFontSize; ttfLoad(); } } @@ -203,63 +205,172 @@ public void utfByDCharSPos (const(char)[] s, scope void delegate (dchar ch, usiz // ////////////////////////////////////////////////////////////////////////// // -void drawFTBitmap (int x, int y, in ref FT_Bitmap bitmap, in uint clr) nothrow @trusted @nogc { - if (bitmap.pixel_mode != FT_PIXEL_MODE_MONO) return; // alas - if (bitmap.rows < 1 || bitmap.width < 1) return; // nothing to do - if (gxIsTransparent(clr)) return; // just in case - // prepare - bool topdown = true; - const(ubyte)* src = bitmap.buffer; - int spt = bitmap.pitch; - if (spt < 0) { - topdown = false; - spt = -spt; - src += spt*(bitmap.rows-1); - } - if (!gxIsSolid(clr)) { - // let this be slow - foreach (immutable int dy; 0..bitmap.rows) { - ubyte count = 0, b = 0; - auto ss = src; - foreach (immutable int dx; 0..bitmap.width) { - if (count-- == 0) { count = 7; b = *ss++; } else b <<= 1; - if (b&0x80) gxPutPixel(x+dx, y, clr); +private void drawMonoBMP (int x, int y, int wdt, int hgt, const(ubyte)* bmp, in usize bpitch, in uint clr) nothrow @trusted @nogc { + immutable int origWdt = wdt; + + int leftSkip, topSkip; + if (!gxClipRect.clipHVStripes(ref x, ref y, ref wdt, ref hgt, &leftSkip, &topSkip)) return; + if (!GxRect(0, 0, VBufWidth, VBufHeight).clipHVStripes(ref x, ref y, ref wdt, ref hgt, &leftSkip, &topSkip)) return; + + // skip unused top part + if (topSkip) bmp += bpitch*cast(uint)topSkip; + // skip unused bytes + bmp += cast(usize)(leftSkip>>3); + leftSkip &= 0x07; + + if (gxIsSolid(clr) && leftSkip == 0) { + // yay, the fastest one! + enum RenderBitMixin = `if (b&0x80) *curptr = clr; ++curptr; b <<= 1;`; + uint* dptr = vglTexBuf+y*VBufWidth+x; + while (hgt--) { + const(ubyte)* ss = bmp; + int left = wdt; + uint* curptr = dptr; + while (left >= 8) { + ubyte b = *ss++; + if (b == 0xff) { + curptr[0..8] = clr; + curptr += 8; + } else if (b) { + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + } else { + curptr += 8; + } + left -= 8; + } + //assert(left >= 0 && left < 8); + if (left) { + ubyte b = *ss; + if (b) { + final switch (left) { + case 7: mixin(RenderBitMixin); goto case; + case 6: mixin(RenderBitMixin); goto case; + case 5: mixin(RenderBitMixin); goto case; + case 4: mixin(RenderBitMixin); goto case; + case 3: mixin(RenderBitMixin); goto case; + case 2: mixin(RenderBitMixin); goto case; + case 1: if (b&0x80) *curptr = clr; break; + //case 0: break; + } + } + //while (left--) { mixin(RenderBitMixin); } } - ++y; - if (topdown) src += spt; else src -= spt; + bmp += bpitch; + dptr += cast(usize)VBufWidth; } return; } - // check if we can use the fastest path - auto brc = GxRect(x, y, bitmap.width, bitmap.rows); - if (gxClipRect.contains(brc) && GxRect(0, 0, VBufWidth, VBufHeight).contains(brc)) { - // yay, the fastest one! + + if (gxIsSolid(clr)) { + // yay, the fast one! + enum RenderBitMixin = `if (b&0x80) *curptr = clr; ++curptr; b <<= 1;`; uint* dptr = vglTexBuf+y*VBufWidth+x; - foreach (immutable int dy; 0..bitmap.rows) { - ubyte count = 0, b = 0; - auto ss = src; - auto curptr = dptr; - foreach (immutable int dx; 0..bitmap.width) { - if (count-- == 0) { count = 7; b = *ss++; } else b <<= 1; - if (b&0x80) *curptr = clr; - ++curptr; + int ldraw = 8-leftSkip; + if (ldraw > wdt) ldraw = wdt; + while (hgt--) { + const(ubyte)* ss = bmp; + uint* curptr = dptr; + { + ubyte b = cast(ubyte)((*ss++)<= 8) { + ubyte b = *ss++; + if (b == 0xff) { + curptr[0..8] = clr; + curptr += 8; + } else if (b) { + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + mixin(RenderBitMixin); + } else { + curptr += 8; + } + left -= 8; } - if (topdown) src += spt; else src -= spt; - dptr += VBufWidth; + //assert(left >= 0 && left < 8); + if (left) { + ubyte b = *ss; + if (b) { + final switch (left) { + case 7: mixin(RenderBitMixin); goto case; + case 6: mixin(RenderBitMixin); goto case; + case 5: mixin(RenderBitMixin); goto case; + case 4: mixin(RenderBitMixin); goto case; + case 3: mixin(RenderBitMixin); goto case; + case 2: mixin(RenderBitMixin); goto case; + case 1: if (b&0x80) *curptr = clr; break; + //case 0: break; + } + } + //while (left--) { mixin(RenderBitMixin); } + } + bmp += bpitch; + dptr += cast(usize)VBufWidth; } - } else { - // do it slow - foreach (immutable int dy; 0..bitmap.rows) { - ubyte count = 0, b = 0; - auto ss = src; - foreach (immutable int dx; 0..bitmap.width) { - if (count-- == 0) { count = 7; b = *ss++; } else b <<= 1; - if (b&0x80) gxSetPixel(x+dx, y, clr); + return; + } + + // the slowest path + x -= leftSkip; + wdt += leftSkip; + while (hgt--) { + const(ubyte)* ss = bmp; + ubyte count = 1, b = void; + int cx = x; + int left = wdt; + while (left > 0) { + if (--count == 0) { + b = *ss++; + if (!b || b == 0xff) { + if (b) gxHLine(cx, y, (left >= 8 ? 8 : left), clr); + cx += 8; + left -= 8; + count = 1; + continue; + } + count = 8; + } else { + b <<= 1; } - ++y; - if (topdown) src += spt; else src -= spt; + if (b&0x80) gxPutPixel(cx, y, clr); + ++cx; + --left; } + ++y; + bmp += bpitch; + } +} + + +// ////////////////////////////////////////////////////////////////////////// // +void drawFTBitmap (int x, int y, in ref FT_Bitmap bitmap, in uint clr) nothrow @trusted @nogc { + if (bitmap.pixel_mode != FT_PIXEL_MODE_MONO) return; // alas + if (bitmap.rows < 1 || bitmap.width < 1) return; // nothing to do + if (gxIsTransparent(clr)) return; // just in case + // prepare + const(ubyte)* src = bitmap.buffer; + usize bpitch = void; + if (bitmap.pitch >= 0) { + bpitch = cast(usize)bitmap.pitch; + } else { + bpitch = cast(usize)0-cast(usize)(-bitmap.pitch); + src += bpitch*(cast(uint)bitmap.rows-1u); } + drawMonoBMP(x, y, bitmap.width, bitmap.rows, src, bpitch, clr); } diff --git a/egra/test.d b/egra/test.d index 0889ac4..23ed005 100644 --- a/egra/test.d +++ b/egra/test.d @@ -283,6 +283,7 @@ final class MainPaneWindow : SubWindow { // ////////////////////////////////////////////////////////////////////////// // void main (string[] args) { defaultColorStyle.parseStyle(TestStyle); + egraDefaultFontSize = 48; glconAllowOpenGLRender = false; -- 2.11.4.GIT