blob: 9903ff8032feabef3403e8d7bd018b97079d9810 [file]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkPaint.h"
#include "SkColorFilter.h"
#include "SkDraw.h"
#include "SkFontDescriptor.h"
#include "SkGraphics.h"
#include "SkPaintDefaults.h"
#include "SkPaintPriv.h"
#include "SkPathEffect.h"
#include "SkSafeRange.h"
#include "SkScalar.h"
#include "SkScalerContext.h"
#include "SkShader.h"
#include "SkShaderBase.h"
#include "SkStrike.h"
#include "SkStringUtils.h"
#include "SkTLazy.h"
#include "SkTextBlob.h"
#include "SkTextBlobPriv.h"
#include "SkTextFormatParams.h"
#include "SkTextToPathIter.h"
#include "SkTo.h"
#include "SkTypeface.h"
///////////////////////////////////////////////////////////////////////////////
static SkScalar mag2(SkScalar x, SkScalar y) {
return x * x + y * y;
}
static bool tooBig(const SkMatrix& m, SkScalar ma2max) {
return mag2(m[SkMatrix::kMScaleX], m[SkMatrix::kMSkewY]) > ma2max
||
mag2(m[SkMatrix::kMSkewX], m[SkMatrix::kMScaleY]) > ma2max;
}
bool SkFontPriv::TooBigToUseCache(const SkMatrix& ctm, const SkMatrix& textM, SkScalar maxLimit) {
SkASSERT(!ctm.hasPerspective());
SkASSERT(!textM.hasPerspective());
SkMatrix matrix;
matrix.setConcat(ctm, textM);
return tooBig(matrix, MaxCacheSize2(maxLimit));
}
SkScalar SkFontPriv::MaxCacheSize2(SkScalar maxLimit) {
// we have a self-imposed maximum, just for memory-usage sanity
const int limit = SkMin32(SkGraphics::GetFontCachePointSizeLimit(), maxLimit);
const SkScalar maxSize = SkIntToScalar(limit);
return maxSize * maxSize;
}
///////////////////////////////////////////////////////////////////////////////
#include "SkUtils.h"
static const SkGlyph& sk_getMetrics_utf8_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
return cache->getUnicharMetrics(SkUTF::NextUTF8(text, stop));
}
static const SkGlyph& sk_getMetrics_utf16_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
return cache->getUnicharMetrics(
SkUTF::NextUTF16((const uint16_t**)text, (const uint16_t*)stop));
}
static const SkGlyph& sk_getMetrics_utf32_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
return cache->getUnicharMetrics(SkUTF::NextUTF32((const int32_t**)text, (const int32_t*)stop));
}
static const SkGlyph& sk_getMetrics_glyph_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
const uint16_t* ptr = *(const uint16_t**)text;
unsigned glyphID = *ptr;
ptr += 1;
*text = (const char*)ptr;
return cache->getGlyphIDMetrics(glyphID);
}
static const SkGlyph& sk_getAdvance_utf8_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
return cache->getUnicharAdvance(SkUTF::NextUTF8(text, stop));
}
static const SkGlyph& sk_getAdvance_utf16_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
return cache->getUnicharAdvance(
SkUTF::NextUTF16((const uint16_t**)text, (const uint16_t*)stop));
}
static const SkGlyph& sk_getAdvance_utf32_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
return cache->getUnicharAdvance(SkUTF::NextUTF32((const int32_t**)text, (const int32_t*)stop));
}
static const SkGlyph& sk_getAdvance_glyph_next(SkStrike* cache,
const char** text,
const char* stop) {
SkASSERT(cache != nullptr);
SkASSERT(text != nullptr);
const uint16_t* ptr = *(const uint16_t**)text;
unsigned glyphID = *ptr;
ptr += 1;
*text = (const char*)ptr;
return cache->getGlyphIDAdvance(glyphID);
}
SkFontPriv::GlyphCacheProc SkFontPriv::GetGlyphCacheProc(SkTextEncoding encoding,
bool needFullMetrics) {
static const GlyphCacheProc gGlyphCacheProcs[] = {
sk_getMetrics_utf8_next,
sk_getMetrics_utf16_next,
sk_getMetrics_utf32_next,
sk_getMetrics_glyph_next,
sk_getAdvance_utf8_next,
sk_getAdvance_utf16_next,
sk_getAdvance_utf32_next,
sk_getAdvance_glyph_next,
};
unsigned index = static_cast<unsigned>(encoding);
if (!needFullMetrics) {
index += 4;
}
SkASSERT(index < SK_ARRAY_COUNT(gGlyphCacheProcs));
return gGlyphCacheProcs[index];
}
///////////////////////////////////////////////////////////////////////////////
// xyIndex is 0 for fAdvanceX or 1 for fAdvanceY
static SkScalar advance(const SkGlyph& glyph) {
return SkFloatToScalar(glyph.fAdvanceX);
}
///////////////////////////////////////////////////////////////////////////////
int SkPaint::getTextBlobIntercepts(const SkTextBlob* blob, const SkScalar bounds[2],
SkScalar* intervals) const {
return blob->getIntercepts(bounds, intervals, this);
}
// return true if the paint is just a single color (i.e. not a shader). If its
// a shader, then we can't compute a const luminance for it :(
static bool just_a_color(const SkPaint& paint, SkColor* color) {
SkColor c = paint.getColor();
const auto* shader = as_SB(paint.getShader());
if (shader && !shader->asLuminanceColor(&c)) {
return false;
}
if (paint.getColorFilter()) {
c = paint.getColorFilter()->filterColor(c);
}
if (color) {
*color = c;
}
return true;
}
SkColor SkPaint::computeLuminanceColor() const {
SkColor c;
if (!just_a_color(*this, &c)) {
c = SkColorSetRGB(0x7F, 0x80, 0x7F);
}
return c;
}
///////////////////////////////////////////////////////////////////////////////
static bool has_thick_frame(const SkPaint& paint) {
return paint.getStrokeWidth() > 0 &&
paint.getStyle() != SkPaint::kFill_Style;
}
SkTextBaseIter::SkTextBaseIter(const SkGlyphID glyphs[], int count, const SkFont& font,
const SkPaint* paint)
: fFont(font)
{
SkAssertResult(count >= 0);
fFont.setLinearMetrics(true);
if (paint) {
fPaint = *paint;
}
fPaint.setMaskFilter(nullptr); // don't want this affecting our path-cache lookup
// can't use our canonical size if we need to apply patheffects
if (fPaint.getPathEffect() == nullptr) {
fScale = fFont.getSize() / SkFontPriv::kCanonicalTextSizeForPaths;
fFont.setSize(SkIntToScalar(SkFontPriv::kCanonicalTextSizeForPaths));
// Note: fScale can be zero here (even if it wasn't before the divide). It can also
// be very very small. We call sk_ieee_float_divide below to ensure IEEE divide behavior,
// since downstream we will check for the resulting coordinates being non-finite anyway.
// Thus we don't need to check for zero here.
if (has_thick_frame(fPaint)) {
fPaint.setStrokeWidth(sk_ieee_float_divide(fPaint.getStrokeWidth(), fScale));
}
} else {
fScale = SK_Scalar1;
}
SkPaint::Style prevStyle = fPaint.getStyle();
auto prevPE = fPaint.refPathEffect();
auto prevMF = fPaint.refMaskFilter();
fPaint.setStyle(SkPaint::kFill_Style);
fPaint.setPathEffect(nullptr);
fCache = SkStrikeCache::FindOrCreateStrikeWithNoDeviceExclusive(fFont, fPaint);
fPaint.setStyle(prevStyle);
fPaint.setPathEffect(std::move(prevPE));
fPaint.setMaskFilter(std::move(prevMF));
// now compute fXOffset if needed
SkScalar xOffset = 0;
fXPos = xOffset;
fPrevAdvance = 0;
fGlyphs = glyphs;
fStop = glyphs + count;
}
bool SkTextInterceptsIter::next(SkScalar* array, int* count) {
SkASSERT(fGlyphs < fStop);
const SkGlyph& glyph = fCache->getGlyphIDMetrics(*fGlyphs++);
fXPos += fPrevAdvance * fScale;
fPrevAdvance = advance(glyph); // + fPaint.getTextTracking();
if (fCache->findPath(glyph)) {
fCache->findIntercepts(fBounds, fScale, fXPos, false,
const_cast<SkGlyph*>(&glyph), array, count);
}
return fGlyphs < fStop;
}