| //======================================================================== |
| // |
| // SplashFont.cc |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2007-2008, 2010, 2014 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2018 Oliver Sander <oliver.sander@tu-dresden.de> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include <limits.h> |
| #include <string.h> |
| #include "goo/gmem.h" |
| #include "SplashMath.h" |
| #include "SplashGlyphBitmap.h" |
| #include "SplashFontFile.h" |
| #include "SplashFont.h" |
| |
| //------------------------------------------------------------------------ |
| |
| struct SplashFontCacheTag { |
| int c; |
| short xFrac, yFrac; // x and y fractions |
| int mru; // valid bit (0x80000000) and MRU index |
| int x, y, w, h; // offset and size of glyph |
| }; |
| |
| //------------------------------------------------------------------------ |
| // SplashFont |
| //------------------------------------------------------------------------ |
| |
| SplashFont::SplashFont(SplashFontFile *fontFileA, SplashCoord *matA, |
| const SplashCoord *textMatA, bool aaA) { |
| fontFile = fontFileA; |
| fontFile->incRefCnt(); |
| mat[0] = matA[0]; |
| mat[1] = matA[1]; |
| mat[2] = matA[2]; |
| mat[3] = matA[3]; |
| textMat[0] = textMatA[0]; |
| textMat[1] = textMatA[1]; |
| textMat[2] = textMatA[2]; |
| textMat[3] = textMatA[3]; |
| aa = aaA; |
| |
| cache = nullptr; |
| cacheTags = nullptr; |
| |
| xMin = yMin = xMax = yMax = 0; |
| } |
| |
| void SplashFont::initCache() { |
| int i; |
| |
| // this should be (max - min + 1), but we add some padding to |
| // deal with rounding errors |
| glyphW = xMax - xMin + 3; |
| glyphH = yMax - yMin + 3; |
| if (glyphW > INT_MAX / glyphH) { |
| glyphSize = -1; |
| } else { |
| if (aa) { |
| glyphSize = glyphW * glyphH; |
| } else { |
| glyphSize = ((glyphW + 7) >> 3) * glyphH; |
| } |
| } |
| |
| // set up the glyph pixmap cache |
| cacheAssoc = 8; |
| if (glyphSize <= 64) { |
| cacheSets = 32; |
| } else if (glyphSize <= 128) { |
| cacheSets = 16; |
| } else if (glyphSize <= 256) { |
| cacheSets = 8; |
| } else if (glyphSize <= 512) { |
| cacheSets = 4; |
| } else if (glyphSize <= 1024) { |
| cacheSets = 2; |
| } else { |
| cacheSets = 1; |
| } |
| cache = (unsigned char *)gmallocn_checkoverflow(cacheSets* cacheAssoc, glyphSize); |
| if (cache != nullptr) { |
| cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc, |
| sizeof(SplashFontCacheTag)); |
| for (i = 0; i < cacheSets * cacheAssoc; ++i) { |
| cacheTags[i].mru = i & (cacheAssoc - 1); |
| } |
| } else { |
| cacheAssoc = 0; |
| } |
| } |
| |
| SplashFont::~SplashFont() { |
| fontFile->decRefCnt(); |
| if (cache) { |
| gfree(cache); |
| } |
| if (cacheTags) { |
| gfree(cacheTags); |
| } |
| } |
| |
| bool SplashFont::getGlyph(int c, int xFrac, int yFrac, |
| SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) { |
| SplashGlyphBitmap bitmap2; |
| int size; |
| unsigned char *p; |
| int i, j, k; |
| |
| // no fractional coordinates for large glyphs or non-anti-aliased |
| // glyphs |
| if (!aa || glyphH > 50) { |
| xFrac = yFrac = 0; |
| } |
| |
| // check the cache |
| i = (c & (cacheSets - 1)) * cacheAssoc; |
| for (j = 0; j < cacheAssoc; ++j) { |
| if ((cacheTags[i+j].mru & 0x80000000) && |
| cacheTags[i+j].c == c && |
| (int)cacheTags[i+j].xFrac == xFrac && |
| (int)cacheTags[i+j].yFrac == yFrac) { |
| bitmap->x = cacheTags[i+j].x; |
| bitmap->y = cacheTags[i+j].y; |
| bitmap->w = cacheTags[i+j].w; |
| bitmap->h = cacheTags[i+j].h; |
| for (k = 0; k < cacheAssoc; ++k) { |
| if (k != j && |
| (cacheTags[i+k].mru & 0x7fffffff) < |
| (cacheTags[i+j].mru & 0x7fffffff)) { |
| ++cacheTags[i+k].mru; |
| } |
| } |
| cacheTags[i+j].mru = 0x80000000; |
| bitmap->aa = aa; |
| bitmap->data = cache + (i+j) * glyphSize; |
| bitmap->freeData = false; |
| |
| *clipRes = clip->testRect(x0 - bitmap->x, |
| y0 - bitmap->y, |
| x0 - bitmap->x + bitmap->w - 1, |
| y0 - bitmap->y + bitmap->h - 1); |
| |
| return true; |
| } |
| } |
| |
| // generate the glyph bitmap |
| if (!makeGlyph(c, xFrac, yFrac, &bitmap2, x0, y0, clip, clipRes)) { |
| return false; |
| } |
| |
| if (*clipRes == splashClipAllOutside) |
| { |
| bitmap->freeData = false; |
| if (bitmap2.freeData) gfree(bitmap2.data); |
| return true; |
| } |
| |
| // if the glyph doesn't fit in the bounding box, return a temporary |
| // uncached bitmap |
| if (bitmap2.w > glyphW || bitmap2.h > glyphH) { |
| *bitmap = bitmap2; |
| return true; |
| } |
| |
| // insert glyph pixmap in cache |
| if (aa) { |
| size = bitmap2.w * bitmap2.h; |
| } else { |
| size = ((bitmap2.w + 7) >> 3) * bitmap2.h; |
| } |
| p = nullptr; // make gcc happy |
| if (cacheAssoc == 0) |
| { |
| // we had problems on the malloc of the cache, so ignore it |
| *bitmap = bitmap2; |
| } |
| else |
| { |
| for (j = 0; j < cacheAssoc; ++j) { |
| if ((cacheTags[i+j].mru & 0x7fffffff) == cacheAssoc - 1) { |
| cacheTags[i+j].mru = 0x80000000; |
| cacheTags[i+j].c = c; |
| cacheTags[i+j].xFrac = (short)xFrac; |
| cacheTags[i+j].yFrac = (short)yFrac; |
| cacheTags[i+j].x = bitmap2.x; |
| cacheTags[i+j].y = bitmap2.y; |
| cacheTags[i+j].w = bitmap2.w; |
| cacheTags[i+j].h = bitmap2.h; |
| p = cache + (i+j) * glyphSize; |
| memcpy(p, bitmap2.data, size); |
| } else { |
| ++cacheTags[i+j].mru; |
| } |
| } |
| *bitmap = bitmap2; |
| bitmap->data = p; |
| bitmap->freeData = false; |
| if (bitmap2.freeData) { |
| gfree(bitmap2.data); |
| } |
| } |
| return true; |
| } |