381 lines
13 KiB
C++
381 lines
13 KiB
C++
|
|
// Copyright 2016 The PDFium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
|
||
|
|
|
||
|
|
#include "core/fxge/cfx_glyphcache.h"
|
||
|
|
|
||
|
|
#include <stdarg.h>
|
||
|
|
|
||
|
|
#include <algorithm>
|
||
|
|
#include <limits>
|
||
|
|
#include <memory>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "build/build_config.h"
|
||
|
|
#include "core/fxcrt/fx_codepage.h"
|
||
|
|
#include "core/fxge/cfx_defaultrenderdevice.h"
|
||
|
|
#include "core/fxge/cfx_font.h"
|
||
|
|
#include "core/fxge/cfx_fontmgr.h"
|
||
|
|
#include "core/fxge/cfx_gemodule.h"
|
||
|
|
#include "core/fxge/cfx_glyphbitmap.h"
|
||
|
|
#include "core/fxge/cfx_path.h"
|
||
|
|
#include "core/fxge/cfx_substfont.h"
|
||
|
|
#include "core/fxge/dib/cfx_dibitmap.h"
|
||
|
|
#include "core/fxge/freetype/fx_freetype.h"
|
||
|
|
#include "core/fxge/scoped_font_transform.h"
|
||
|
|
#include "third_party/base/numerics/safe_math.h"
|
||
|
|
|
||
|
|
#if defined(_SKIA_SUPPORT_)
|
||
|
|
#include "third_party/skia/include/core/SkStream.h" // nogncheck
|
||
|
|
#include "third_party/skia/include/core/SkTypeface.h" // nogncheck
|
||
|
|
|
||
|
|
#if BUILDFLAG(IS_WIN)
|
||
|
|
#include "third_party/skia/include/core/SkFontMgr.h" // nogncheck
|
||
|
|
#include "third_party/skia/include/ports/SkFontMgr_empty.h" // nogncheck
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if BUILDFLAG(IS_APPLE)
|
||
|
|
#include "core/fxge/cfx_textrenderoptions.h"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
constexpr uint32_t kInvalidGlyphIndex = static_cast<uint32_t>(-1);
|
||
|
|
|
||
|
|
constexpr int kMaxGlyphDimension = 2048;
|
||
|
|
|
||
|
|
struct UniqueKeyGen {
|
||
|
|
void Generate(int count, ...);
|
||
|
|
|
||
|
|
int key_len_;
|
||
|
|
char key_[128];
|
||
|
|
};
|
||
|
|
|
||
|
|
void UniqueKeyGen::Generate(int count, ...) {
|
||
|
|
va_list argList;
|
||
|
|
va_start(argList, count);
|
||
|
|
for (int i = 0; i < count; i++) {
|
||
|
|
int p = va_arg(argList, int);
|
||
|
|
reinterpret_cast<uint32_t*>(key_)[i] = p;
|
||
|
|
}
|
||
|
|
va_end(argList);
|
||
|
|
key_len_ = count * sizeof(uint32_t);
|
||
|
|
}
|
||
|
|
|
||
|
|
void GenKey(UniqueKeyGen* pKeyGen,
|
||
|
|
const CFX_Font* pFont,
|
||
|
|
const CFX_Matrix& matrix,
|
||
|
|
int dest_width,
|
||
|
|
int anti_alias,
|
||
|
|
bool bNative) {
|
||
|
|
int nMatrixA = static_cast<int>(matrix.a * 10000);
|
||
|
|
int nMatrixB = static_cast<int>(matrix.b * 10000);
|
||
|
|
int nMatrixC = static_cast<int>(matrix.c * 10000);
|
||
|
|
int nMatrixD = static_cast<int>(matrix.d * 10000);
|
||
|
|
|
||
|
|
#if BUILDFLAG(IS_APPLE)
|
||
|
|
if (bNative) {
|
||
|
|
if (pFont->GetSubstFont()) {
|
||
|
|
pKeyGen->Generate(10, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
|
||
|
|
anti_alias, pFont->GetSubstFont()->m_Weight,
|
||
|
|
pFont->GetSubstFont()->m_ItalicAngle,
|
||
|
|
pFont->IsVertical(), 3);
|
||
|
|
} else {
|
||
|
|
pKeyGen->Generate(7, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
|
||
|
|
anti_alias, 3);
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
CHECK(!bNative);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if (pFont->GetSubstFont()) {
|
||
|
|
pKeyGen->Generate(9, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
|
||
|
|
anti_alias, pFont->GetSubstFont()->m_Weight,
|
||
|
|
pFont->GetSubstFont()->m_ItalicAngle,
|
||
|
|
pFont->IsVertical());
|
||
|
|
} else {
|
||
|
|
pKeyGen->Generate(6, nMatrixA, nMatrixB, nMatrixC, nMatrixD, dest_width,
|
||
|
|
anti_alias);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
CFX_GlyphCache::CFX_GlyphCache(RetainPtr<CFX_Face> face)
|
||
|
|
: m_Face(std::move(face)) {}
|
||
|
|
|
||
|
|
CFX_GlyphCache::~CFX_GlyphCache() = default;
|
||
|
|
|
||
|
|
std::unique_ptr<CFX_GlyphBitmap> CFX_GlyphCache::RenderGlyph(
|
||
|
|
const CFX_Font* pFont,
|
||
|
|
uint32_t glyph_index,
|
||
|
|
bool bFontStyle,
|
||
|
|
const CFX_Matrix& matrix,
|
||
|
|
int dest_width,
|
||
|
|
int anti_alias) {
|
||
|
|
if (!GetFaceRec())
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
FT_Matrix ft_matrix;
|
||
|
|
ft_matrix.xx = matrix.a / 64 * 65536;
|
||
|
|
ft_matrix.xy = matrix.c / 64 * 65536;
|
||
|
|
ft_matrix.yx = matrix.b / 64 * 65536;
|
||
|
|
ft_matrix.yy = matrix.d / 64 * 65536;
|
||
|
|
bool bUseCJKSubFont = false;
|
||
|
|
const CFX_SubstFont* pSubstFont = pFont->GetSubstFont();
|
||
|
|
if (pSubstFont) {
|
||
|
|
bUseCJKSubFont = pSubstFont->m_bSubstCJK && bFontStyle;
|
||
|
|
int angle;
|
||
|
|
if (bUseCJKSubFont)
|
||
|
|
angle = pSubstFont->m_bItalicCJK ? -15 : 0;
|
||
|
|
else
|
||
|
|
angle = pSubstFont->m_ItalicAngle;
|
||
|
|
if (angle) {
|
||
|
|
int skew = CFX_Font::GetSkewFromAngle(angle);
|
||
|
|
if (pFont->IsVertical())
|
||
|
|
ft_matrix.yx += ft_matrix.yy * skew / 100;
|
||
|
|
else
|
||
|
|
ft_matrix.xy -= ft_matrix.xx * skew / 100;
|
||
|
|
}
|
||
|
|
if (pSubstFont->IsBuiltInGenericFont()) {
|
||
|
|
pFont->AdjustMMParams(glyph_index, dest_width,
|
||
|
|
pFont->GetSubstFont()->m_Weight);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ScopedFontTransform scoped_transform(GetFace(), &ft_matrix);
|
||
|
|
int load_flags = FT_LOAD_NO_BITMAP | FT_LOAD_PEDANTIC;
|
||
|
|
if (!(GetFaceRec()->face_flags & FT_FACE_FLAG_SFNT))
|
||
|
|
load_flags |= FT_LOAD_NO_HINTING;
|
||
|
|
int error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags);
|
||
|
|
if (error) {
|
||
|
|
// if an error is returned, try to reload glyphs without hinting.
|
||
|
|
if (load_flags & FT_LOAD_NO_HINTING)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
load_flags |= FT_LOAD_NO_HINTING;
|
||
|
|
load_flags &= ~FT_LOAD_PEDANTIC;
|
||
|
|
error = FT_Load_Glyph(GetFaceRec(), glyph_index, load_flags);
|
||
|
|
if (error)
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
int weight;
|
||
|
|
if (bUseCJKSubFont)
|
||
|
|
weight = pSubstFont->m_WeightCJK;
|
||
|
|
else
|
||
|
|
weight = pSubstFont ? pSubstFont->m_Weight : 0;
|
||
|
|
if (pSubstFont && !pSubstFont->IsBuiltInGenericFont() && weight > 400) {
|
||
|
|
uint32_t index = (weight - 400) / 10;
|
||
|
|
pdfium::base::CheckedNumeric<signed long> level =
|
||
|
|
CFX_Font::GetWeightLevel(pSubstFont->m_Charset, index);
|
||
|
|
if (level.ValueOrDefault(-1) < 0)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
level = level *
|
||
|
|
(abs(static_cast<int>(ft_matrix.xx)) +
|
||
|
|
abs(static_cast<int>(ft_matrix.xy))) /
|
||
|
|
36655;
|
||
|
|
FT_Outline_Embolden(FXFT_Get_Glyph_Outline(GetFaceRec()),
|
||
|
|
level.ValueOrDefault(0));
|
||
|
|
}
|
||
|
|
FT_Library_SetLcdFilter(CFX_GEModule::Get()->GetFontMgr()->GetFTLibrary(),
|
||
|
|
FT_LCD_FILTER_DEFAULT);
|
||
|
|
error = FXFT_Render_Glyph(GetFaceRec(), anti_alias);
|
||
|
|
if (error)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
int bmwidth = FXFT_Get_Bitmap_Width(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
|
||
|
|
int bmheight = FXFT_Get_Bitmap_Rows(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
|
||
|
|
if (bmwidth > kMaxGlyphDimension || bmheight > kMaxGlyphDimension)
|
||
|
|
return nullptr;
|
||
|
|
int dib_width = bmwidth;
|
||
|
|
auto pGlyphBitmap =
|
||
|
|
std::make_unique<CFX_GlyphBitmap>(FXFT_Get_Glyph_BitmapLeft(GetFaceRec()),
|
||
|
|
FXFT_Get_Glyph_BitmapTop(GetFaceRec()));
|
||
|
|
pGlyphBitmap->GetBitmap()->Create(dib_width, bmheight,
|
||
|
|
anti_alias == FT_RENDER_MODE_MONO
|
||
|
|
? FXDIB_Format::k1bppMask
|
||
|
|
: FXDIB_Format::k8bppMask);
|
||
|
|
int dest_pitch = pGlyphBitmap->GetBitmap()->GetPitch();
|
||
|
|
int src_pitch = FXFT_Get_Bitmap_Pitch(FXFT_Get_Glyph_Bitmap(GetFaceRec()));
|
||
|
|
uint8_t* pDestBuf = pGlyphBitmap->GetBitmap()->GetBuffer().data();
|
||
|
|
uint8_t* pSrcBuf = static_cast<uint8_t*>(
|
||
|
|
FXFT_Get_Bitmap_Buffer(FXFT_Get_Glyph_Bitmap(GetFaceRec())));
|
||
|
|
if (anti_alias != FT_RENDER_MODE_MONO &&
|
||
|
|
FXFT_Get_Bitmap_PixelMode(FXFT_Get_Glyph_Bitmap(GetFaceRec())) ==
|
||
|
|
FT_PIXEL_MODE_MONO) {
|
||
|
|
int bytes = anti_alias == FT_RENDER_MODE_LCD ? 3 : 1;
|
||
|
|
for (int i = 0; i < bmheight; i++) {
|
||
|
|
for (int n = 0; n < bmwidth; n++) {
|
||
|
|
uint8_t data =
|
||
|
|
(pSrcBuf[i * src_pitch + n / 8] & (0x80 >> (n % 8))) ? 255 : 0;
|
||
|
|
for (int b = 0; b < bytes; b++)
|
||
|
|
pDestBuf[i * dest_pitch + n * bytes + b] = data;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
memset(pDestBuf, 0, dest_pitch * bmheight);
|
||
|
|
int rowbytes = std::min(abs(src_pitch), dest_pitch);
|
||
|
|
for (int row = 0; row < bmheight; row++)
|
||
|
|
memcpy(pDestBuf + row * dest_pitch, pSrcBuf + row * src_pitch, rowbytes);
|
||
|
|
}
|
||
|
|
return pGlyphBitmap;
|
||
|
|
}
|
||
|
|
|
||
|
|
const CFX_Path* CFX_GlyphCache::LoadGlyphPath(const CFX_Font* pFont,
|
||
|
|
uint32_t glyph_index,
|
||
|
|
int dest_width) {
|
||
|
|
if (!GetFaceRec() || glyph_index == kInvalidGlyphIndex)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
const auto* pSubstFont = pFont->GetSubstFont();
|
||
|
|
int weight = pSubstFont ? pSubstFont->m_Weight : 0;
|
||
|
|
int angle = pSubstFont ? pSubstFont->m_ItalicAngle : 0;
|
||
|
|
bool vertical = pSubstFont && pFont->IsVertical();
|
||
|
|
const PathMapKey key =
|
||
|
|
std::make_tuple(glyph_index, dest_width, weight, angle, vertical);
|
||
|
|
auto it = m_PathMap.find(key);
|
||
|
|
if (it != m_PathMap.end())
|
||
|
|
return it->second.get();
|
||
|
|
|
||
|
|
m_PathMap[key] = pFont->LoadGlyphPathImpl(glyph_index, dest_width);
|
||
|
|
return m_PathMap[key].get();
|
||
|
|
}
|
||
|
|
|
||
|
|
const CFX_GlyphBitmap* CFX_GlyphCache::LoadGlyphBitmap(
|
||
|
|
const CFX_Font* pFont,
|
||
|
|
uint32_t glyph_index,
|
||
|
|
bool bFontStyle,
|
||
|
|
const CFX_Matrix& matrix,
|
||
|
|
int dest_width,
|
||
|
|
int anti_alias,
|
||
|
|
CFX_TextRenderOptions* text_options) {
|
||
|
|
if (glyph_index == kInvalidGlyphIndex)
|
||
|
|
return nullptr;
|
||
|
|
|
||
|
|
UniqueKeyGen keygen;
|
||
|
|
#if BUILDFLAG(IS_APPLE)
|
||
|
|
const bool bNative = text_options->native_text;
|
||
|
|
#else
|
||
|
|
const bool bNative = false;
|
||
|
|
#endif
|
||
|
|
GenKey(&keygen, pFont, matrix, dest_width, anti_alias, bNative);
|
||
|
|
ByteString FaceGlyphsKey(keygen.key_, keygen.key_len_);
|
||
|
|
|
||
|
|
#if BUILDFLAG(IS_APPLE)
|
||
|
|
const bool bDoLookUp = !text_options->native_text ||
|
||
|
|
CFX_DefaultRenderDevice::SkiaIsDefaultRenderer();
|
||
|
|
#else
|
||
|
|
const bool bDoLookUp = true;
|
||
|
|
#endif
|
||
|
|
if (bDoLookUp) {
|
||
|
|
return LookUpGlyphBitmap(pFont, matrix, FaceGlyphsKey, glyph_index,
|
||
|
|
bFontStyle, dest_width, anti_alias);
|
||
|
|
}
|
||
|
|
|
||
|
|
#if BUILDFLAG(IS_APPLE)
|
||
|
|
DCHECK(!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer());
|
||
|
|
|
||
|
|
std::unique_ptr<CFX_GlyphBitmap> pGlyphBitmap;
|
||
|
|
auto it = m_SizeMap.find(FaceGlyphsKey);
|
||
|
|
if (it != m_SizeMap.end()) {
|
||
|
|
SizeGlyphCache* pSizeCache = &(it->second);
|
||
|
|
auto it2 = pSizeCache->find(glyph_index);
|
||
|
|
if (it2 != pSizeCache->end())
|
||
|
|
return it2->second.get();
|
||
|
|
|
||
|
|
pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, matrix,
|
||
|
|
dest_width, anti_alias);
|
||
|
|
if (pGlyphBitmap) {
|
||
|
|
CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
|
||
|
|
(*pSizeCache)[glyph_index] = std::move(pGlyphBitmap);
|
||
|
|
return pResult;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
pGlyphBitmap = RenderGlyph_Nativetext(pFont, glyph_index, matrix,
|
||
|
|
dest_width, anti_alias);
|
||
|
|
if (pGlyphBitmap) {
|
||
|
|
CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
|
||
|
|
|
||
|
|
SizeGlyphCache cache;
|
||
|
|
cache[glyph_index] = std::move(pGlyphBitmap);
|
||
|
|
|
||
|
|
m_SizeMap[FaceGlyphsKey] = std::move(cache);
|
||
|
|
return pResult;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
GenKey(&keygen, pFont, matrix, dest_width, anti_alias, /*bNative=*/false);
|
||
|
|
ByteString FaceGlyphsKey2(keygen.key_, keygen.key_len_);
|
||
|
|
text_options->native_text = false;
|
||
|
|
return LookUpGlyphBitmap(pFont, matrix, FaceGlyphsKey2, glyph_index,
|
||
|
|
bFontStyle, dest_width, anti_alias);
|
||
|
|
#endif // BUILDFLAG(IS_APPLE)
|
||
|
|
}
|
||
|
|
|
||
|
|
int CFX_GlyphCache::GetGlyphWidth(const CFX_Font* font,
|
||
|
|
uint32_t glyph_index,
|
||
|
|
int dest_width,
|
||
|
|
int weight) {
|
||
|
|
const WidthMapKey key = std::make_tuple(glyph_index, dest_width, weight);
|
||
|
|
auto it = m_WidthMap.find(key);
|
||
|
|
if (it != m_WidthMap.end()) {
|
||
|
|
return it->second;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_WidthMap[key] = font->GetGlyphWidthImpl(glyph_index, dest_width, weight);
|
||
|
|
return m_WidthMap[key];
|
||
|
|
}
|
||
|
|
|
||
|
|
#if defined(_SKIA_SUPPORT_)
|
||
|
|
CFX_TypeFace* CFX_GlyphCache::GetDeviceCache(const CFX_Font* pFont) {
|
||
|
|
if (!m_pTypeface) {
|
||
|
|
pdfium::span<const uint8_t> span = pFont->GetFontSpan();
|
||
|
|
m_pTypeface = SkTypeface::MakeFromStream(
|
||
|
|
std::make_unique<SkMemoryStream>(span.data(), span.size()));
|
||
|
|
}
|
||
|
|
#if BUILDFLAG(IS_WIN)
|
||
|
|
if (!m_pTypeface) {
|
||
|
|
sk_sp<SkFontMgr> customMgr(SkFontMgr_New_Custom_Empty());
|
||
|
|
pdfium::span<const uint8_t> span = pFont->GetFontSpan();
|
||
|
|
m_pTypeface = customMgr->makeFromStream(
|
||
|
|
std::make_unique<SkMemoryStream>(span.data(), span.size()));
|
||
|
|
}
|
||
|
|
#endif // BUILDFLAG(IS_WIN)
|
||
|
|
return m_pTypeface.get();
|
||
|
|
}
|
||
|
|
#endif // defined(_SKIA_SUPPORT_)
|
||
|
|
|
||
|
|
CFX_GlyphBitmap* CFX_GlyphCache::LookUpGlyphBitmap(
|
||
|
|
const CFX_Font* pFont,
|
||
|
|
const CFX_Matrix& matrix,
|
||
|
|
const ByteString& FaceGlyphsKey,
|
||
|
|
uint32_t glyph_index,
|
||
|
|
bool bFontStyle,
|
||
|
|
int dest_width,
|
||
|
|
int anti_alias) {
|
||
|
|
SizeGlyphCache* pSizeCache;
|
||
|
|
auto it = m_SizeMap.find(FaceGlyphsKey);
|
||
|
|
if (it == m_SizeMap.end()) {
|
||
|
|
m_SizeMap[FaceGlyphsKey] = SizeGlyphCache();
|
||
|
|
pSizeCache = &(m_SizeMap[FaceGlyphsKey]);
|
||
|
|
} else {
|
||
|
|
pSizeCache = &(it->second);
|
||
|
|
}
|
||
|
|
|
||
|
|
auto it2 = pSizeCache->find(glyph_index);
|
||
|
|
if (it2 != pSizeCache->end())
|
||
|
|
return it2->second.get();
|
||
|
|
|
||
|
|
std::unique_ptr<CFX_GlyphBitmap> pGlyphBitmap = RenderGlyph(
|
||
|
|
pFont, glyph_index, bFontStyle, matrix, dest_width, anti_alias);
|
||
|
|
CFX_GlyphBitmap* pResult = pGlyphBitmap.get();
|
||
|
|
(*pSizeCache)[glyph_index] = std::move(pGlyphBitmap);
|
||
|
|
return pResult;
|
||
|
|
}
|