1/* 2 * Copyright 2020 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "src/gpu/text/GrSDFTControl.h" 9 10#include "include/core/SkFont.h" 11#include "include/core/SkGraphics.h" 12#include "include/core/SkMatrix.h" 13#include "include/core/SkPaint.h" 14#include "include/core/SkScalar.h" 15#include "include/core/SkSurfaceProps.h" 16#include "src/core/SkGlyphRunPainter.h" 17 18#include <tuple> 19 20// DF sizes and thresholds for usage of the small and medium sizes. For example, above 21// kSmallDFFontLimit we will use the medium size. The large size is used up until the size at 22// which we switch over to drawing as paths as controlled by Control. 23static const int kSmallDFFontSize = 32; 24static const int kSmallDFFontLimit = 32; 25static const int kMediumDFFontSize = 72; 26static const int kMediumDFFontLimit = 72; 27static const int kLargeDFFontSize = 162; 28#ifdef SK_BUILD_FOR_MAC 29static const int kLargeDFFontLimit = 162; 30static const int kExtraLargeDFFontSize = 256; 31#endif 32 33SkScalar GrSDFTControl::MinSDFTRange(bool useSDFTForSmallText, SkScalar min) { 34 if (!useSDFTForSmallText) { 35 return kLargeDFFontSize; 36 } 37 return min; 38} 39 40GrSDFTControl::GrSDFTControl( 41 bool ableToUseSDFT, bool useSDFTForSmallText, SkScalar min, SkScalar max) 42 : fMinDistanceFieldFontSize{MinSDFTRange(useSDFTForSmallText, min)} 43 , fMaxDistanceFieldFontSize{max} 44 , fAbleToUseSDFT{ableToUseSDFT} { 45 SkASSERT_RELEASE(0 < min && min <= max); 46} 47 48auto GrSDFTControl::drawingType( 49 const SkFont& font, const SkPaint& paint, const SkMatrix& viewMatrix) const -> DrawingType { 50 51 // Use paths as the first choice for hairlines and perspective. 52 if ((paint.getStyle() == SkPaint::kStroke_Style && paint.getStrokeWidth() == 0) 53 || viewMatrix.hasPerspective()) { 54 return kPath; 55 } 56 57 SkScalar maxScale = viewMatrix.getMaxScale(); 58 SkScalar scaledTextSize = maxScale * font.getSize(); 59 60 // If we can't use SDFT, then make a simple choice between direct or path. 61 if (!fAbleToUseSDFT || paint.getMaskFilter() || paint.getStyle() != SkPaint::kFill_Style) { 62 constexpr int kAboveIsPath = SkStrikeCommon::kSkSideTooBigForAtlas; 63 return scaledTextSize < kAboveIsPath ? kDirect : kPath; 64 } 65 66 // Hinted text looks far better at small resolutions 67 // Scaling up beyond 2x yields undesirable artifacts 68#ifdef SKIA_OHOS_FOR_OHOS_TRACE 69 if (scaledTextSize <= fMaxDistanceFieldFontSize) { 70#else 71 if (scaledTextSize < fMinDistanceFieldFontSize) { 72#endif 73 return kDirect; 74 } else if (fMaxDistanceFieldFontSize < scaledTextSize) { 75 return kPath; 76 } 77 78 return kSDFT; 79} 80 81SkScalar scaled_text_size(const SkScalar textSize, const SkMatrix& viewMatrix) { 82 SkScalar scaledTextSize = textSize; 83 84 if (viewMatrix.hasPerspective()) { 85 // for perspective, we simply force to the medium size 86 // TODO: compute a size based on approximate screen area 87 scaledTextSize = kMediumDFFontLimit; 88 } else { 89 SkScalar maxScale = viewMatrix.getMaxScale(); 90 // if we have non-unity scale, we need to choose our base text size 91 // based on the SkPaint's text size multiplied by the max scale factor 92 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? 93 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { 94 scaledTextSize *= maxScale; 95 } 96 } 97 98 return scaledTextSize; 99} 100 101SkFont GrSDFTControl::getSDFFont(const SkFont& font, 102 const SkMatrix& viewMatrix, 103 SkScalar* textRatio) const { 104 SkScalar textSize = font.getSize(); 105 SkScalar scaledTextSize = scaled_text_size(textSize, viewMatrix); 106 107 SkFont dfFont{font}; 108 109 if (scaledTextSize <= kSmallDFFontLimit) { 110 *textRatio = textSize / kSmallDFFontSize; 111 dfFont.setSize(SkIntToScalar(kSmallDFFontSize)); 112 } else if (scaledTextSize <= kMediumDFFontLimit) { 113 *textRatio = textSize / kMediumDFFontSize; 114 dfFont.setSize(SkIntToScalar(kMediumDFFontSize)); 115#ifdef SK_BUILD_FOR_MAC 116 } else if (scaledTextSize <= kLargeDFFontLimit) { 117 *textRatio = textSize / kLargeDFFontSize; 118 dfFont.setSize(SkIntToScalar(kLargeDFFontSize)); 119 } else { 120 *textRatio = textSize / kExtraLargeDFFontSize; 121 dfFont.setSize(SkIntToScalar(kExtraLargeDFFontSize)); 122 } 123#else 124 } else { 125 *textRatio = textSize / kLargeDFFontSize; 126 dfFont.setSize(SkIntToScalar(kLargeDFFontSize)); 127 } 128#endif 129 130 dfFont.setEdging(SkFont::Edging::kAntiAlias); 131 dfFont.setForceAutoHinting(false); 132 dfFont.setHinting(SkFontHinting::kNormal); 133 134 // The sub-pixel position will always happen when transforming to the screen. 135 dfFont.setSubpixel(false); 136 return dfFont; 137} 138 139std::pair<SkScalar, SkScalar> GrSDFTControl::computeSDFMinMaxScale( 140 SkScalar textSize, const SkMatrix& viewMatrix) const { 141 142 SkScalar scaledTextSize = scaled_text_size(textSize, viewMatrix); 143 144 // We have three sizes of distance field text, and within each size 'bucket' there is a floor 145 // and ceiling. A scale outside of this range would require regenerating the distance fields 146 SkScalar dfMaskScaleFloor; 147 SkScalar dfMaskScaleCeil; 148 if (scaledTextSize <= kSmallDFFontLimit) { 149 dfMaskScaleFloor = fMinDistanceFieldFontSize; 150 dfMaskScaleCeil = kSmallDFFontLimit; 151 } else if (scaledTextSize <= kMediumDFFontLimit) { 152 dfMaskScaleFloor = kSmallDFFontLimit; 153 dfMaskScaleCeil = kMediumDFFontLimit; 154 } else { 155 dfMaskScaleFloor = kMediumDFFontLimit; 156 dfMaskScaleCeil = fMaxDistanceFieldFontSize; 157 } 158 159 // Because there can be multiple runs in the blob, we want the overall maxMinScale, and 160 // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale 161 // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can 162 // tolerate before we'd have to move to a large mip size. When we actually test these values 163 // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test 164 // against these values to decide if we can reuse or not(ie, will a given scale change our mip 165 // level) 166 SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil); 167 168 return std::make_pair(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize); 169} 170 171 172