/* * Copyright 2019 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/core/SkStrikeSpec.h" #include "include/core/SkGraphics.h" #include "src/core/SkDraw.h" #include "src/core/SkFontPriv.h" #include "src/core/SkStrikeCache.h" #include "src/core/SkTLazy.h" SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps, SkScalerContextFlags scalerContextFlags, const SkMatrix& deviceMatrix) { SkStrikeSpec storage; storage.commonSetup(font, paint, surfaceProps, scalerContextFlags, deviceMatrix); return storage; } SkStrikeSpec SkStrikeSpec::MakePath(const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps, SkScalerContextFlags scalerContextFlags) { SkStrikeSpec storage; // setup our std runPaint, in hopes of getting hits in the cache SkPaint pathPaint{paint}; SkFont pathFont{font}; // The factor to get from the size stored in the strike to the size needed for // the source. storage.fStrikeToSourceRatio = pathFont.setupForAsPaths(&pathPaint); // The sub-pixel position will always happen when transforming to the screen. pathFont.setSubpixel(false); storage.commonSetup(pathFont, pathPaint, surfaceProps, scalerContextFlags, SkMatrix::I()); return storage; } SkStrikeSpec SkStrikeSpec::MakeSourceFallback( const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps, SkScalerContextFlags scalerContextFlags, SkScalar maxSourceGlyphDimension) { SkStrikeSpec storage; // Subtract 2 to account for the bilerp pad around the glyph SkScalar maxAtlasDimension = SkStrikeCommon::kSkSideTooBigForAtlas - 2; SkScalar runFontTextSize = font.getSize(); // Scale the text size down so the long side of all the glyphs will fit in the atlas. SkScalar fallbackTextSize = SkScalarFloorToScalar( (maxAtlasDimension / maxSourceGlyphDimension) * runFontTextSize); SkFont fallbackFont{font}; fallbackFont.setSize(fallbackTextSize); // No sub-pixel needed. The transform to the screen will take care of sub-pixel positioning. fallbackFont.setSubpixel(false); // The scale factor to go from strike size to the source size for glyphs. storage.fStrikeToSourceRatio = runFontTextSize / fallbackTextSize; storage.commonSetup(fallbackFont, paint, surfaceProps, scalerContextFlags, SkMatrix::I()); return storage; } SkStrikeSpec SkStrikeSpec::MakeCanonicalized(const SkFont& font, const SkPaint* paint) { SkStrikeSpec storage; SkPaint canonicalizedPaint; if (paint != nullptr) { canonicalizedPaint = *paint; } const SkFont* canonicalizedFont = &font; SkTLazy pathFont; if (ShouldDrawAsPath(canonicalizedPaint, font, SkMatrix::I())) { canonicalizedFont = pathFont.set(font); storage.fStrikeToSourceRatio = pathFont->setupForAsPaths(nullptr); canonicalizedPaint.reset(); } storage.commonSetup(*canonicalizedFont, canonicalizedPaint, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), kFakeGammaAndBoostContrast, SkMatrix::I()); return storage; } SkStrikeSpec SkStrikeSpec::MakeWithNoDevice(const SkFont& font, const SkPaint* paint) { SkStrikeSpec storage; SkPaint setupPaint; if (paint != nullptr) { setupPaint = *paint; } storage.commonSetup(font, setupPaint, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), kFakeGammaAndBoostContrast, SkMatrix::I()); return storage; } SkStrikeSpec SkStrikeSpec::MakeDefault() { SkFont defaultFont; return MakeCanonicalized(defaultFont); } bool SkStrikeSpec::ShouldDrawAsPath( const SkPaint& paint, const SkFont& font, const SkMatrix& viewMatrix) { // hairline glyphs are fast enough so we don't need to cache them if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) { return true; } // we don't cache perspective if (viewMatrix.hasPerspective()) { return true; } SkMatrix textMatrix = SkFontPriv::MakeTextMatrix(font); textMatrix.postConcat(viewMatrix); // we have a self-imposed maximum, just for memory-usage sanity SkScalar limit = SkMinScalar(SkGraphics::GetFontCachePointSizeLimit(), 1024); SkScalar maxSizeSquared = limit * limit; auto distance = [&textMatrix](int XIndex, int YIndex) { return textMatrix[XIndex] * textMatrix[XIndex] + textMatrix[YIndex] * textMatrix[YIndex]; }; return distance(SkMatrix::kMScaleX, SkMatrix::kMSkewY ) > maxSizeSquared || distance(SkMatrix::kMSkewX, SkMatrix::kMScaleY) > maxSizeSquared; } SkStrikeSpec SkStrikeSpec::MakePDFVector(const SkTypeface& typeface, int* size) { SkFont font; font.setHinting(SkFontHinting::kNone); font.setEdging(SkFont::Edging::kAlias); font.setTypeface(sk_ref_sp(&typeface)); int unitsPerEm = typeface.getUnitsPerEm(); if (unitsPerEm <= 0) { unitsPerEm = 1024; } if (size) { *size = unitsPerEm; } font.setSize((SkScalar)unitsPerEm); SkStrikeSpec storage; storage.commonSetup(font, SkPaint(), SkSurfaceProps(0, kUnknown_SkPixelGeometry), kFakeGammaAndBoostContrast, SkMatrix::I()); return storage; } #if SK_SUPPORT_GPU std::tuple SkStrikeSpec::MakeSDFT(const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix, const GrTextContext::Options& options) { SkStrikeSpec storage; SkPaint dfPaint = GrTextContext::InitDistanceFieldPaint(paint); SkFont dfFont = GrTextContext::InitDistanceFieldFont( font, deviceMatrix, options, &storage.fStrikeToSourceRatio); // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). SkScalerContextFlags flags = SkScalerContextFlags::kNone; SkScalar minScale, maxScale; std::tie(minScale, maxScale) = GrTextContext::InitDistanceFieldMinMaxScale( font.getSize(), deviceMatrix, options); storage.commonSetup(dfFont, dfPaint, surfaceProps, flags, SkMatrix::I()); return std::tie(storage, minScale, maxScale); } sk_sp SkStrikeSpec::findOrCreateGrStrike(GrStrikeCache* cache) const { return cache->getStrike(*fAutoDescriptor.getDesc()); } #endif void SkStrikeSpec::commonSetup(const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps, SkScalerContextFlags scalerContextFlags, const SkMatrix& deviceMatrix) { SkScalerContextEffects effects; SkScalerContext::CreateDescriptorAndEffectsUsingPaint( font, paint, surfaceProps, scalerContextFlags, deviceMatrix, &fAutoDescriptor, &effects); fMaskFilter = sk_ref_sp(effects.fMaskFilter); fPathEffect = sk_ref_sp(effects.fPathEffect); fTypeface = font.refTypefaceOrDefault(); } SkScopedStrike SkStrikeSpec::findOrCreateScopedStrike(SkStrikeCacheInterface* cache) const { SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()}; return cache->findOrCreateScopedStrike(*fAutoDescriptor.getDesc(), effects, *fTypeface); } SkExclusiveStrikePtr SkStrikeSpec::findOrCreateExclusiveStrike(SkStrikeCache* cache) const { SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()}; return cache->findOrCreateStrikeExclusive(*fAutoDescriptor.getDesc(), effects, *fTypeface); } SkBulkGlyphMetrics::SkBulkGlyphMetrics(const SkStrikeSpec& spec) : fStrike{spec.findOrCreateExclusiveStrike()} { } SkSpan SkBulkGlyphMetrics::glyphs(SkSpan glyphIDs) { fGlyphs.reset(glyphIDs.size()); return fStrike->metrics(glyphIDs, fGlyphs.get()); } SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec) : fStrike{spec.findOrCreateExclusiveStrike()} { } SkSpan SkBulkGlyphMetricsAndPaths::glyphs(SkSpan glyphIDs) { fGlyphs.reset(glyphIDs.size()); return fStrike->preparePaths(glyphIDs, fGlyphs.get()); } SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec) : fStrike{spec.findOrCreateExclusiveStrike()} { } SkSpan SkBulkGlyphMetricsAndImages::glyphs(SkSpan glyphIDs) { fGlyphs.reset(glyphIDs.size()); return fStrike->prepareImages(glyphIDs, fGlyphs.get()); }