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 | |
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 | |
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(count: cacheSets * cacheAssoc, size: glyphSize); |
100 | if (cache != nullptr) { |
101 | cacheTags = (SplashFontCacheTag *)gmallocn(count: cacheSets * cacheAssoc, size: 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 | |
110 | SplashFont::~SplashFont() |
111 | { |
112 | fontFile->decRefCnt(); |
113 | if (cache) { |
114 | gfree(p: cache); |
115 | } |
116 | if (cacheTags) { |
117 | gfree(p: cacheTags); |
118 | } |
119 | } |
120 | |
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(rectXMin: x0 - bitmap->x, rectYMin: y0 - bitmap->y, rectXMax: x0 - bitmap->x + bitmap->w - 1, rectYMax: y0 - bitmap->y + bitmap->h - 1); |
153 | |
154 | return true; |
155 | } |
156 | } |
157 | |
158 | // generate the glyph bitmap |
159 | if (!makeGlyph(c, xFrac, yFrac, bitmap: &bitmap2, x0, y0, clip, clipRes)) { |
160 | return false; |
161 | } |
162 | |
163 | if (*clipRes == splashClipAllOutside) { |
164 | bitmap->freeData = false; |
165 | if (bitmap2.freeData) { |
166 | gfree(p: bitmap2.data); |
167 | } |
168 | return true; |
169 | } |
170 | |
171 | // if the glyph doesn't fit in the bounding box, return a temporary |
172 | // uncached bitmap |
173 | if (bitmap2.w > glyphW || bitmap2.h > glyphH) { |
174 | *bitmap = bitmap2; |
175 | return true; |
176 | } |
177 | |
178 | // insert glyph pixmap in cache |
179 | if (aa) { |
180 | size = bitmap2.w * bitmap2.h; |
181 | } else { |
182 | size = ((bitmap2.w + 7) >> 3) * bitmap2.h; |
183 | } |
184 | p = nullptr; // make gcc happy |
185 | if (cacheAssoc == 0) { |
186 | // we had problems on the malloc of the cache, so ignore it |
187 | *bitmap = bitmap2; |
188 | } else { |
189 | for (j = 0; j < cacheAssoc; ++j) { |
190 | if ((cacheTags[i + j].mru & 0x7fffffff) == cacheAssoc - 1) { |
191 | cacheTags[i + j].mru = 0x80000000; |
192 | cacheTags[i + j].c = c; |
193 | cacheTags[i + j].xFrac = (short)xFrac; |
194 | cacheTags[i + j].yFrac = (short)yFrac; |
195 | cacheTags[i + j].x = bitmap2.x; |
196 | cacheTags[i + j].y = bitmap2.y; |
197 | cacheTags[i + j].w = bitmap2.w; |
198 | cacheTags[i + j].h = bitmap2.h; |
199 | p = cache + (i + j) * glyphSize; |
200 | memcpy(dest: p, src: bitmap2.data, n: size); |
201 | } else { |
202 | ++cacheTags[i + j].mru; |
203 | } |
204 | } |
205 | *bitmap = bitmap2; |
206 | bitmap->data = p; |
207 | bitmap->freeData = false; |
208 | if (bitmap2.freeData) { |
209 | gfree(p: bitmap2.data); |
210 | } |
211 | } |
212 | return true; |
213 | } |
214 | |