1 // Copyright 2016 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxge/cfx_facecache.h"
8
9 #include <algorithm>
10 #include <limits>
11 #include <memory>
12
13 #include "core/fxge/cfx_fontmgr.h"
14 #include "core/fxge/cfx_gemodule.h"
15 #include "core/fxge/cfx_pathdata.h"
16 #include "core/fxge/cfx_substfont.h"
17 #include "core/fxge/fx_freetype.h"
18 #include "core/fxge/ge/fx_text_int.h"
19 #include "third_party/base/numerics/safe_math.h"
20
21 #if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
22 #include "third_party/skia/include/core/SkStream.h"
23 #include "third_party/skia/include/core/SkTypeface.h"
24 #endif
25
26 namespace {
27
28 constexpr uint32_t kInvalidGlyphIndex = static_cast<uint32_t>(-1);
29
GammaAdjust(uint8_t * pData,int nHeight,int src_pitch,const uint8_t * gammaTable)30 void GammaAdjust(uint8_t* pData,
31 int nHeight,
32 int src_pitch,
33 const uint8_t* gammaTable) {
34 int count = nHeight * src_pitch;
35 for (int i = 0; i < count; i++)
36 pData[i] = gammaTable[pData[i]];
37 }
38
ContrastAdjust(uint8_t * pDataIn,uint8_t * pDataOut,int nWidth,int nHeight,int nSrcRowBytes,int nDstRowBytes)39 void ContrastAdjust(uint8_t* pDataIn,
40 uint8_t* pDataOut,
41 int nWidth,
42 int nHeight,
43 int nSrcRowBytes,
44 int nDstRowBytes) {
45 int col, row, temp;
46 int max = 0, min = 255;
47 FX_FLOAT rate;
48 for (row = 0; row < nHeight; row++) {
49 uint8_t* pRow = pDataIn + row * nSrcRowBytes;
50 for (col = 0; col < nWidth; col++) {
51 temp = *pRow++;
52 max = std::max(temp, max);
53 min = std::min(temp, min);
54 }
55 }
56 temp = max - min;
57 if (temp == 0 || temp == 255) {
58 int rowbytes = std::min(FXSYS_abs(nSrcRowBytes), nDstRowBytes);
59 for (row = 0; row < nHeight; row++) {
60 FXSYS_memcpy(pDataOut + row * nDstRowBytes, pDataIn + row * nSrcRowBytes,
61 rowbytes);
62 }
63 return;
64 }
65 rate = 255.f / temp;
66 for (row = 0; row < nHeight; row++) {
67 uint8_t* pSrcRow = pDataIn + row * nSrcRowBytes;
68 uint8_t* pDstRow = pDataOut + row * nDstRowBytes;
69 for (col = 0; col < nWidth; col++) {
70 temp = static_cast<int>((*(pSrcRow++) - min) * rate + 0.5);
71 temp = std::min(temp, 255);
72 temp = std::max(temp, 0);
73 *pDstRow++ = (uint8_t)temp;
74 }
75 }
76 }
77 } // namespace
78
CFX_FaceCache(FXFT_Face face)79 CFX_FaceCache::CFX_FaceCache(FXFT_Face face)
80 : m_Face(face)
81 #if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
82 ,
83 m_pTypeface(nullptr)
84 #endif
85 {
86 }
87
~CFX_FaceCache()88 CFX_FaceCache::~CFX_FaceCache() {
89 #if defined _SKIA_SUPPORT_ || _SKIA_SUPPORT_PATHS_
90 SkSafeUnref(m_pTypeface);
91 #endif
92 }
93
RenderGlyph(const CFX_Font * pFont,uint32_t glyph_index,bool bFontStyle,const CFX_Matrix * pMatrix,int dest_width,int anti_alias)94 CFX_GlyphBitmap* CFX_FaceCache::RenderGlyph(const CFX_Font* pFont,
95 uint32_t glyph_index,
96 bool bFontStyle,
97 const CFX_Matrix* pMatrix,
98 int dest_width,
99 int anti_alias) {
100 if (!m_Face)
101 return nullptr;
102
103 FXFT_Matrix ft_matrix;
104 ft_matrix.xx = (signed long)(pMatrix->a / 64 * 65536);
105 ft_matrix.xy = (signed long)(pMatrix->c / 64 * 65536);
106 ft_matrix.yx = (signed long)(pMatrix->b / 64 * 65536);
107 ft_matrix.yy = (signed long)(pMatrix->d / 64 * 65536);
108 bool bUseCJKSubFont = false;
109 const CFX_SubstFont* pSubstFont = pFont->GetSubstFont();
110 if (pSubstFont) {
111 bUseCJKSubFont = pSubstFont->m_bSubstCJK && bFontStyle;
112 int skew = 0;
113 if (bUseCJKSubFont)
114 skew = pSubstFont->m_bItalicCJK ? -15 : 0;
115 else
116 skew = pSubstFont->m_ItalicAngle;
117 if (skew) {
118 // |skew| is nonpositive so |-skew| is used as the index. We need to make
119 // sure |skew| != INT_MIN since -INT_MIN is undefined.
120 if (skew <= 0 && skew != std::numeric_limits<int>::min() &&
121 static_cast<size_t>(-skew) < CFX_Font::kAngleSkewArraySize) {
122 skew = -CFX_Font::s_AngleSkew[-skew];
123 } else {
124 skew = -58;
125 }
126 if (pFont->IsVertical())
127 ft_matrix.yx += ft_matrix.yy * skew / 100;
128 else
129 ft_matrix.xy -= ft_matrix.xx * skew / 100;
130 }
131 if (pSubstFont->m_SubstFlags & FXFONT_SUBST_MM) {
132 pFont->AdjustMMParams(glyph_index, dest_width,
133 pFont->GetSubstFont()->m_Weight);
134 }
135 }
136 ScopedFontTransform scoped_transform(m_Face, &ft_matrix);
137 int load_flags = (m_Face->face_flags & FT_FACE_FLAG_SFNT)
138 ? FXFT_LOAD_NO_BITMAP
139 : (FXFT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
140 int error = FXFT_Load_Glyph(m_Face, glyph_index, load_flags);
141 if (error) {
142 // if an error is returned, try to reload glyphs without hinting.
143 if (load_flags & FT_LOAD_NO_HINTING || load_flags & FT_LOAD_NO_SCALE)
144 return nullptr;
145
146 load_flags |= FT_LOAD_NO_HINTING;
147 error = FXFT_Load_Glyph(m_Face, glyph_index, load_flags);
148
149 if (error)
150 return nullptr;
151 }
152 int weight = 0;
153 if (bUseCJKSubFont)
154 weight = pSubstFont->m_WeightCJK;
155 else
156 weight = pSubstFont ? pSubstFont->m_Weight : 0;
157 if (pSubstFont && !(pSubstFont->m_SubstFlags & FXFONT_SUBST_MM) &&
158 weight > 400) {
159 uint32_t index = (weight - 400) / 10;
160 if (index >= CFX_Font::kWeightPowArraySize)
161 return nullptr;
162 pdfium::base::CheckedNumeric<signed long> level = 0;
163 if (pSubstFont->m_Charset == FXFONT_SHIFTJIS_CHARSET)
164 level = CFX_Font::s_WeightPow_SHIFTJIS[index] * 2;
165 else
166 level = CFX_Font::s_WeightPow_11[index];
167
168 level = level * (FXSYS_abs(static_cast<int>(ft_matrix.xx)) +
169 FXSYS_abs(static_cast<int>(ft_matrix.xy))) /
170 36655;
171 FXFT_Outline_Embolden(FXFT_Get_Glyph_Outline(m_Face),
172 level.ValueOrDefault(0));
173 }
174 FXFT_Library_SetLcdFilter(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(),
175 FT_LCD_FILTER_DEFAULT);
176 error = FXFT_Render_Glyph(m_Face, anti_alias);
177 if (error)
178 return nullptr;
179 int bmwidth = FXFT_Get_Bitmap_Width(FXFT_Get_Glyph_Bitmap(m_Face));
180 int bmheight = FXFT_Get_Bitmap_Rows(FXFT_Get_Glyph_Bitmap(m_Face));
181 if (bmwidth > 2048 || bmheight > 2048)
182 return nullptr;
183 int dib_width = bmwidth;
184 CFX_GlyphBitmap* pGlyphBitmap = new CFX_GlyphBitmap;
185 pGlyphBitmap->m_Bitmap.Create(
186 dib_width, bmheight,
187 anti_alias == FXFT_RENDER_MODE_MONO ? FXDIB_1bppMask : FXDIB_8bppMask);
188 pGlyphBitmap->m_Left = FXFT_Get_Glyph_BitmapLeft(m_Face);
189 pGlyphBitmap->m_Top = FXFT_Get_Glyph_BitmapTop(m_Face);
190 int dest_pitch = pGlyphBitmap->m_Bitmap.GetPitch();
191 int src_pitch = FXFT_Get_Bitmap_Pitch(FXFT_Get_Glyph_Bitmap(m_Face));
192 uint8_t* pDestBuf = pGlyphBitmap->m_Bitmap.GetBuffer();
193 uint8_t* pSrcBuf =
194 (uint8_t*)FXFT_Get_Bitmap_Buffer(FXFT_Get_Glyph_Bitmap(m_Face));
195 if (anti_alias != FXFT_RENDER_MODE_MONO &&
196 FXFT_Get_Bitmap_PixelMode(FXFT_Get_Glyph_Bitmap(m_Face)) ==
197 FXFT_PIXEL_MODE_MONO) {
198 int bytes = anti_alias == FXFT_RENDER_MODE_LCD ? 3 : 1;
199 for (int i = 0; i < bmheight; i++) {
200 for (int n = 0; n < bmwidth; n++) {
201 uint8_t data =
202 (pSrcBuf[i * src_pitch + n / 8] & (0x80 >> (n % 8))) ? 255 : 0;
203 for (int b = 0; b < bytes; b++)
204 pDestBuf[i * dest_pitch + n * bytes + b] = data;
205 }
206 }
207 } else {
208 FXSYS_memset(pDestBuf, 0, dest_pitch * bmheight);
209 if (anti_alias == FXFT_RENDER_MODE_MONO &&
210 FXFT_Get_Bitmap_PixelMode(FXFT_Get_Glyph_Bitmap(m_Face)) ==
211 FXFT_PIXEL_MODE_MONO) {
212 int rowbytes =
213 FXSYS_abs(src_pitch) > dest_pitch ? dest_pitch : FXSYS_abs(src_pitch);
214 for (int row = 0; row < bmheight; row++) {
215 FXSYS_memcpy(pDestBuf + row * dest_pitch, pSrcBuf + row * src_pitch,
216 rowbytes);
217 }
218 } else {
219 ContrastAdjust(pSrcBuf, pDestBuf, bmwidth, bmheight, src_pitch,
220 dest_pitch);
221 GammaAdjust(pDestBuf, bmheight, dest_pitch,
222 CFX_GEModule::Get()->GetTextGammaTable());
223 }
224 }
225 return pGlyphBitmap;
226 }
227
LoadGlyphPath(const CFX_Font * pFont,uint32_t glyph_index,int dest_width)228 const CFX_PathData* CFX_FaceCache::LoadGlyphPath(const CFX_Font* pFont,
229 uint32_t glyph_index,
230 int dest_width) {
231 if (!m_Face || glyph_index == kInvalidGlyphIndex || dest_width < 0)
232 return nullptr;
233
234 uint32_t key = glyph_index;
235 auto* pSubstFont = pFont->GetSubstFont();
236 if (pSubstFont) {
237 if (pSubstFont->m_Weight < 0 || pSubstFont->m_ItalicAngle < 0)
238 return nullptr;
239 uint32_t weight = static_cast<uint32_t>(pSubstFont->m_Weight);
240 uint32_t angle = static_cast<uint32_t>(pSubstFont->m_ItalicAngle);
241 uint32_t key_modifier = (weight / 16) << 15;
242 key_modifier += (angle / 2) << 21;
243 key_modifier += (static_cast<uint32_t>(dest_width) / 16) << 25;
244 if (pFont->IsVertical())
245 key_modifier += 1U << 31;
246 key += key_modifier;
247 }
248 auto it = m_PathMap.find(key);
249 if (it != m_PathMap.end())
250 return it->second.get();
251
252 CFX_PathData* pGlyphPath = pFont->LoadGlyphPathImpl(glyph_index, dest_width);
253 m_PathMap[key] = std::unique_ptr<CFX_PathData>(pGlyphPath);
254 return pGlyphPath;
255 }
256
LoadGlyphBitmap(const CFX_Font * pFont,uint32_t glyph_index,bool bFontStyle,const CFX_Matrix * pMatrix,int dest_width,int anti_alias,int & text_flags)257 const CFX_GlyphBitmap* CFX_FaceCache::LoadGlyphBitmap(const CFX_Font* pFont,
258 uint32_t glyph_index,
259 bool bFontStyle,
260 const CFX_Matrix* pMatrix,
261 int dest_width,
262 int anti_alias,
263 int& text_flags) {
264 if (glyph_index == kInvalidGlyphIndex)
265 return nullptr;
266
267 _CFX_UniqueKeyGen keygen;
268 int nMatrixA = static_cast<int>(pMatrix->a * 10000);
269 int nMatrixB = static_cast<int>(pMatrix->b * 10000);
270 int nMatrixC = static_cast<int>(pMatrix->c * 10000);
271 int nMatrixD = static_cast<int>(pMatrix->d * 10000);
272 #if _FXM_PLATFORM_ != _FXM_PLATFORM_APPLE_
273 if (pFont->GetSubstFont()) {
274 keygen.Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
275 anti_alias, pFont->GetSubstFont()->m_Weight,
276 pFont->GetSubstFont()->m_ItalicAngle, pFont->IsVertical());
277 } else {
278 keygen.Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
279 anti_alias);
280 }
281 #else
282 if (text_flags & FXTEXT_NO_NATIVETEXT) {
283 if (pFont->GetSubstFont()) {
284 keygen.Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
285 anti_alias, pFont->GetSubstFont()->m_Weight,
286 pFont->GetSubstFont()->m_ItalicAngle,
287 pFont->IsVertical());
288 } else {
289 keygen.Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
290 anti_alias);
291 }
292 } else {
293 if (pFont->GetSubstFont()) {
294 keygen.Generate(10, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
295 anti_alias, pFont->GetSubstFont()->m_Weight,
296 pFont->GetSubstFont()->m_ItalicAngle, pFont->IsVertical(),
297 3);
298 } else {
299 keygen.Generate(7, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
300 anti_alias, 3);
301 }
302 }
303 #endif
304 CFX_ByteString FaceGlyphsKey(keygen.m_Key, keygen.m_KeyLen);
305 #if _FXM_PLATFORM_ != _FXM_PLATFORM_APPLE_ || defined _SKIA_SUPPORT_ || \
306 defined _SKIA_SUPPORT_PATHS_
307 return LookUpGlyphBitmap(pFont, pMatrix, FaceGlyphsKey, glyph_index,
308 bFontStyle, dest_width, anti_alias);
309 #else
310 if (text_flags & FXTEXT_NO_NATIVETEXT) {
311 return LookUpGlyphBitmap(pFont, pMatrix, FaceGlyphsKey, glyph_index,
312 bFontStyle, dest_width, anti_alias);
313 }
314 CFX_GlyphBitmap* pGlyphBitmap;
315 auto it = m_SizeMap.find(FaceGlyphsKey);
316 if (it != m_SizeMap.end()) {
317 CFX_SizeGlyphCache* pSizeCache = it->second.get();
318 auto it2 = pSizeCache->m_GlyphMap.find(glyph_index);
319 if (it2 != pSizeCache->m_GlyphMap.end())
320 return it2->second;
321
322 pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, pMatrix,
323 dest_width, anti_alias);
324 if (pGlyphBitmap) {
325 pSizeCache->m_GlyphMap[glyph_index] = pGlyphBitmap;
326 return pGlyphBitmap;
327 }
328 } else {
329 pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, pMatrix,
330 dest_width, anti_alias);
331 if (pGlyphBitmap) {
332 CFX_SizeGlyphCache* pSizeCache = new CFX_SizeGlyphCache;
333 m_SizeMap[FaceGlyphsKey] =
334 std::unique_ptr<CFX_SizeGlyphCache>(pSizeCache);
335 pSizeCache->m_GlyphMap[glyph_index] = pGlyphBitmap;
336 return pGlyphBitmap;
337 }
338 }
339 if (pFont->GetSubstFont()) {
340 keygen.Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
341 anti_alias, pFont->GetSubstFont()->m_Weight,
342 pFont->GetSubstFont()->m_ItalicAngle, pFont->IsVertical());
343 } else {
344 keygen.Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
345 anti_alias);
346 }
347 CFX_ByteString FaceGlyphsKey2(keygen.m_Key, keygen.m_KeyLen);
348 text_flags |= FXTEXT_NO_NATIVETEXT;
349 return LookUpGlyphBitmap(pFont, pMatrix, FaceGlyphsKey2, glyph_index,
350 bFontStyle, dest_width, anti_alias);
351 #endif
352 }
353
354 #if defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
GetDeviceCache(const CFX_Font * pFont)355 CFX_TypeFace* CFX_FaceCache::GetDeviceCache(const CFX_Font* pFont) {
356 if (!m_pTypeface) {
357 m_pTypeface =
358 SkTypeface::MakeFromStream(
359 new SkMemoryStream(pFont->GetFontData(), pFont->GetSize()))
360 .release();
361 }
362 return m_pTypeface;
363 }
364 #endif
365
366 #if _FXM_PLATFORM_ != _FXM_PLATFORM_APPLE_
InitPlatform()367 void CFX_FaceCache::InitPlatform() {}
368 #endif
369
LookUpGlyphBitmap(const CFX_Font * pFont,const CFX_Matrix * pMatrix,const CFX_ByteString & FaceGlyphsKey,uint32_t glyph_index,bool bFontStyle,int dest_width,int anti_alias)370 CFX_GlyphBitmap* CFX_FaceCache::LookUpGlyphBitmap(
371 const CFX_Font* pFont,
372 const CFX_Matrix* pMatrix,
373 const CFX_ByteString& FaceGlyphsKey,
374 uint32_t glyph_index,
375 bool bFontStyle,
376 int dest_width,
377 int anti_alias) {
378 CFX_SizeGlyphCache* pSizeCache;
379 auto it = m_SizeMap.find(FaceGlyphsKey);
380 if (it == m_SizeMap.end()) {
381 pSizeCache = new CFX_SizeGlyphCache;
382 m_SizeMap[FaceGlyphsKey] = std::unique_ptr<CFX_SizeGlyphCache>(pSizeCache);
383 } else {
384 pSizeCache = it->second.get();
385 }
386 auto it2 = pSizeCache->m_GlyphMap.find(glyph_index);
387 if (it2 != pSizeCache->m_GlyphMap.end())
388 return it2->second;
389
390 CFX_GlyphBitmap* pGlyphBitmap = RenderGlyph(pFont, glyph_index, bFontStyle,
391 pMatrix, dest_width, anti_alias);
392 pSizeCache->m_GlyphMap[glyph_index] = pGlyphBitmap;
393 return pGlyphBitmap;
394 }
395