1 //========================================================================
2 //
3 // SplashFont.cc
4 //
5 //========================================================================
6
7 //========================================================================
8 //
9 // Modified under the Poppler project - http://poppler.freedesktop.org
10 //
11 // All changes made under the Poppler project to this file are licensed
12 // under GPL version 2 or later
13 //
14 // Copyright (C) 2007-2008, 2010, 2014, 2019 Albert Astals Cid <aacid@kde.org>
15 // Copyright (C) 2018 Oliver Sander <oliver.sander@tu-dresden.de>
16 //
17 // To see a description of the changes please see the Changelog file that
18 // came with your tarball or type make ChangeLog if you are building from git
19 //
20 //========================================================================
21
22 #include <config.h>
23
24 #include <climits>
25 #include <cstring>
26 #include "goo/gmem.h"
27 #include "SplashMath.h"
28 #include "SplashGlyphBitmap.h"
29 #include "SplashFontFile.h"
30 #include "SplashFont.h"
31
32 //------------------------------------------------------------------------
33
34 struct SplashFontCacheTag
35 {
36 int c;
37 short xFrac, yFrac; // x and y fractions
38 int mru; // valid bit (0x80000000) and MRU index
39 int x, y, w, h; // offset and size of glyph
40 };
41
42 //------------------------------------------------------------------------
43 // SplashFont
44 //------------------------------------------------------------------------
45
SplashFont(SplashFontFile * fontFileA,const SplashCoord * matA,const SplashCoord * textMatA,bool aaA)46 SplashFont::SplashFont(SplashFontFile *fontFileA, const SplashCoord *matA, const SplashCoord *textMatA, bool aaA)
47 {
48 fontFile = fontFileA;
49 fontFile->incRefCnt();
50 mat[0] = matA[0];
51 mat[1] = matA[1];
52 mat[2] = matA[2];
53 mat[3] = matA[3];
54 textMat[0] = textMatA[0];
55 textMat[1] = textMatA[1];
56 textMat[2] = textMatA[2];
57 textMat[3] = textMatA[3];
58 aa = aaA;
59
60 cache = nullptr;
61 cacheTags = nullptr;
62
63 xMin = yMin = xMax = yMax = 0;
64 }
65
initCache()66 void SplashFont::initCache()
67 {
68 int i;
69
70 // this should be (max - min + 1), but we add some padding to
71 // deal with rounding errors
72 glyphW = xMax - xMin + 3;
73 glyphH = yMax - yMin + 3;
74 if (glyphW > INT_MAX / glyphH) {
75 glyphSize = -1;
76 } else {
77 if (aa) {
78 glyphSize = glyphW * glyphH;
79 } else {
80 glyphSize = ((glyphW + 7) >> 3) * glyphH;
81 }
82 }
83
84 // set up the glyph pixmap cache
85 cacheAssoc = 8;
86 if (glyphSize <= 64) {
87 cacheSets = 32;
88 } else if (glyphSize <= 128) {
89 cacheSets = 16;
90 } else if (glyphSize <= 256) {
91 cacheSets = 8;
92 } else if (glyphSize <= 512) {
93 cacheSets = 4;
94 } else if (glyphSize <= 1024) {
95 cacheSets = 2;
96 } else {
97 cacheSets = 1;
98 }
99 cache = (unsigned char *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize);
100 if (cache != nullptr) {
101 cacheTags = (SplashFontCacheTag *)gmallocn(cacheSets * cacheAssoc, sizeof(SplashFontCacheTag));
102 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
103 cacheTags[i].mru = i & (cacheAssoc - 1);
104 }
105 } else {
106 cacheAssoc = 0;
107 }
108 }
109
~SplashFont()110 SplashFont::~SplashFont()
111 {
112 fontFile->decRefCnt();
113 if (cache) {
114 gfree(cache);
115 }
116 if (cacheTags) {
117 gfree(cacheTags);
118 }
119 }
120
getGlyph(int c,int xFrac,int yFrac,SplashGlyphBitmap * bitmap,int x0,int y0,SplashClip * clip,SplashClipResult * clipRes)121 bool SplashFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes)
122 {
123 SplashGlyphBitmap bitmap2;
124 int size;
125 unsigned char *p;
126 int i, j, k;
127
128 // no fractional coordinates for large glyphs or non-anti-aliased
129 // glyphs
130 if (!aa || glyphH > 50) {
131 xFrac = yFrac = 0;
132 }
133
134 // check the cache
135 i = (c & (cacheSets - 1)) * cacheAssoc;
136 for (j = 0; j < cacheAssoc; ++j) {
137 if ((cacheTags[i + j].mru & 0x80000000) && cacheTags[i + j].c == c && (int)cacheTags[i + j].xFrac == xFrac && (int)cacheTags[i + j].yFrac == yFrac) {
138 bitmap->x = cacheTags[i + j].x;
139 bitmap->y = cacheTags[i + j].y;
140 bitmap->w = cacheTags[i + j].w;
141 bitmap->h = cacheTags[i + j].h;
142 for (k = 0; k < cacheAssoc; ++k) {
143 if (k != j && (cacheTags[i + k].mru & 0x7fffffff) < (cacheTags[i + j].mru & 0x7fffffff)) {
144 ++cacheTags[i + k].mru;
145 }
146 }
147 cacheTags[i + j].mru = 0x80000000;
148 bitmap->aa = aa;
149 bitmap->data = cache + (i + j) * glyphSize;
150 bitmap->freeData = false;
151
152 *clipRes = clip->testRect(x0 - bitmap->x, y0 - bitmap->y, x0 - bitmap->x + bitmap->w - 1, y0 - bitmap->y + bitmap->h - 1);
153
154 return true;
155 }
156 }
157
158 // generate the glyph bitmap
159 if (!makeGlyph(c, xFrac, yFrac, &bitmap2, x0, y0, clip, clipRes)) {
160 return false;
161 }
162
163 if (*clipRes == splashClipAllOutside) {
164 bitmap->freeData = false;
165 if (bitmap2.freeData)
166 gfree(bitmap2.data);
167 return true;
168 }
169
170 // if the glyph doesn't fit in the bounding box, return a temporary
171 // uncached bitmap
172 if (bitmap2.w > glyphW || bitmap2.h > glyphH) {
173 *bitmap = bitmap2;
174 return true;
175 }
176
177 // insert glyph pixmap in cache
178 if (aa) {
179 size = bitmap2.w * bitmap2.h;
180 } else {
181 size = ((bitmap2.w + 7) >> 3) * bitmap2.h;
182 }
183 p = nullptr; // make gcc happy
184 if (cacheAssoc == 0) {
185 // we had problems on the malloc of the cache, so ignore it
186 *bitmap = bitmap2;
187 } else {
188 for (j = 0; j < cacheAssoc; ++j) {
189 if ((cacheTags[i + j].mru & 0x7fffffff) == cacheAssoc - 1) {
190 cacheTags[i + j].mru = 0x80000000;
191 cacheTags[i + j].c = c;
192 cacheTags[i + j].xFrac = (short)xFrac;
193 cacheTags[i + j].yFrac = (short)yFrac;
194 cacheTags[i + j].x = bitmap2.x;
195 cacheTags[i + j].y = bitmap2.y;
196 cacheTags[i + j].w = bitmap2.w;
197 cacheTags[i + j].h = bitmap2.h;
198 p = cache + (i + j) * glyphSize;
199 memcpy(p, bitmap2.data, size);
200 } else {
201 ++cacheTags[i + j].mru;
202 }
203 }
204 *bitmap = bitmap2;
205 bitmap->data = p;
206 bitmap->freeData = false;
207 if (bitmap2.freeData) {
208 gfree(bitmap2.data);
209 }
210 }
211 return true;
212 }
213