• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2006 The Android Open Source Project
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 #ifndef SkScalerContext_DEFINED
9 #define SkScalerContext_DEFINED
10 
11 #include "include/core/SkColor.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkString.h"
18 #include "include/core/SkSurfaceProps.h"
19 #include "include/core/SkTypeface.h"
20 #include "include/core/SkTypes.h"
21 #include "include/private/base/SkMacros.h"
22 #include "include/private/base/SkPoint_impl.h"
23 #include "include/private/base/SkTemplates.h"
24 #include "include/private/base/SkTo.h"
25 #include "src/core/SkGlyph.h"
26 #include "src/core/SkMask.h"
27 #include "src/core/SkMaskGamma.h"
28 
29 #include <cstddef>
30 #include <cstdint>
31 #include <memory>
32 #include <utility>
33 
34 class SkArenaAlloc;
35 class SkAutoDescriptor;
36 class SkDescriptor;
37 class SkDrawable;
38 class SkFont;
39 class SkMaskFilter;
40 class SkPath;
41 class SkPathEffect;
42 enum class SkFontHinting;
43 struct SkFontMetrics;
44 
45 //The following typedef hides from the rest of the implementation the number of
46 //most significant bits to consider when creating mask gamma tables. Two bits
47 //per channel was chosen as a balance between fidelity (more bits) and cache
48 //sizes (fewer bits). Three bits per channel was chosen when #303942; (used by
49 //the Chrome UI) turned out too green.
50 typedef SkTMaskGamma<3, 3, 3> SkMaskGamma;
51 
52 enum class SkScalerContextFlags : uint32_t {
53     kNone                      = 0,
54     kFakeGamma                 = 1 << 0,
55     kBoostContrast             = 1 << 1,
56     kFakeGammaAndBoostContrast = kFakeGamma | kBoostContrast,
57 };
58 SK_MAKE_BITFIELD_OPS(SkScalerContextFlags)
59 
60 /*
61  *  To allow this to be forward-declared, it must be its own typename, rather
62  *  than a nested struct inside SkScalerContext (where it started).
63  *
64  *  SkScalerContextRec must be dense, and all bytes must be set to a know quantity because this
65  *  structure is used to calculate a checksum.
66  */
67 SK_BEGIN_REQUIRE_DENSE
68 struct SkScalerContextRec {
69     SkTypefaceID fTypefaceID;
70     SkScalar     fTextSize, fPreScaleX, fPreSkewX;
71     SkScalar     fPost2x2[2][2];
72     SkScalar     fFrameWidth, fMiterLimit;
73 
74     // This will be set if to the paint's foreground color if
75     // kNeedsForegroundColor is set, which will usually be the case for COLRv0 and
76     // COLRv1 fonts.
77     uint32_t fForegroundColor{SK_ColorBLACK};
78 
79 private:
80     //These describe the parameters to create (uniquely identify) the pre-blend.
81     uint32_t      fLumBits;
82     uint8_t       fDeviceGamma; //2.6, (0.0, 4.0) gamma, 0.0 for sRGB
83     const uint8_t fReservedAlign2{0};
84     uint8_t       fContrast;    //0.8+1, [0.0, 1.0] artificial contrast
85     const uint8_t fReservedAlign{0};
86 
ExternalGammaFromInternalSkScalerContextRec87     static constexpr SkScalar ExternalGammaFromInternal(uint8_t g) {
88         return SkIntToScalar(g) / (1 << 6);
89     }
InternalGammaFromExternalSkScalerContextRec90     static constexpr uint8_t InternalGammaFromExternal(SkScalar g) {
91         // C++23 use constexpr std::floor
92         return static_cast<uint8_t>(g * (1 << 6));
93     }
ExternalContrastFromInternalSkScalerContextRec94     static constexpr SkScalar ExternalContrastFromInternal(uint8_t c) {
95         return SkIntToScalar(c) / ((1 << 8) - 1);
96     }
InternalContrastFromExternalSkScalerContextRec97     static constexpr uint8_t InternalContrastFromExternal(SkScalar c) {
98         // C++23 use constexpr std::round
99         return static_cast<uint8_t>((c * ((1 << 8) - 1)) + 0.5f);
100     }
101 public:
setDeviceGammaSkScalerContextRec102     void setDeviceGamma(SkScalar g) {
103         sk_ignore_unused_variable(fReservedAlign2);
104         SkASSERT(SkSurfaceProps::kMinGammaInclusive <= g &&
105                  g < SkIntToScalar(SkSurfaceProps::kMaxGammaExclusive));
106         fDeviceGamma = InternalGammaFromExternal(g);
107     }
108 
setContrastSkScalerContextRec109     void setContrast(SkScalar c) {
110         sk_ignore_unused_variable(fReservedAlign);
111         SkASSERT(SkSurfaceProps::kMinContrastInclusive <= c &&
112                  c <= SkIntToScalar(SkSurfaceProps::kMaxContrastInclusive));
113         fContrast = InternalContrastFromExternal(c);
114     }
115 
116     static const SkMaskGamma& CachedMaskGamma(uint8_t contrast, uint8_t gamma);
cachedMaskGammaSkScalerContextRec117     const SkMaskGamma& cachedMaskGamma() const {
118         return CachedMaskGamma(fContrast, fDeviceGamma);
119     }
120 
121     /**
122      *  Causes the luminance color to be ignored, and the paint and device
123      *  gamma to be effectively 1.0
124      */
ignoreGammaSkScalerContextRec125     void ignoreGamma() {
126         setLuminanceColor(SK_ColorTRANSPARENT);
127         setDeviceGamma(SK_Scalar1);
128     }
129 
130     /**
131      *  Causes the luminance color and contrast to be ignored, and the
132      *  paint and device gamma to be effectively 1.0.
133      */
ignorePreBlendSkScalerContextRec134     void ignorePreBlend() {
135         ignoreGamma();
136         setContrast(0);
137     }
138 
139     SkMask::Format fMaskFormat;
140 
141 private:
142     uint8_t        fStrokeJoin : 4;
143     uint8_t        fStrokeCap  : 4;
144 
145 public:
146     uint16_t    fFlags;
147 
148     // Warning: when adding members note that the size of this structure
149     // must be a multiple of 4. SkDescriptor requires that its arguments be
150     // multiples of four and this structure is put in an SkDescriptor in
151     // SkPaint::MakeRecAndEffects.
152 
dumpSkScalerContextRec153     SkString dump() const {
154         SkString msg;
155         msg.appendf("    Rec\n");
156         msg.appendf("      textsize %a prescale %a preskew %a post [%a %a %a %a]\n",
157                    fTextSize, fPreScaleX, fPreSkewX, fPost2x2[0][0],
158                    fPost2x2[0][1], fPost2x2[1][0], fPost2x2[1][1]);
159         msg.appendf("      frame %g miter %g format %d join %d cap %d flags %#hx\n",
160                    fFrameWidth, fMiterLimit, fMaskFormat, fStrokeJoin, fStrokeCap, fFlags);
161         msg.appendf("      lum bits %x, device gamma %d, contrast %d\n", fLumBits,
162                     fDeviceGamma, fContrast);
163         msg.appendf("      foreground color %x\n", fForegroundColor);
164         return msg;
165     }
166 
167     void    getMatrixFrom2x2(SkMatrix*) const;
168     void    getLocalMatrix(SkMatrix*) const;
169     void    getSingleMatrix(SkMatrix*) const;
170 
171     /** The kind of scale which will be applied by the underlying port (pre-matrix). */
172     enum class PreMatrixScale {
173         kFull,  // The underlying port can apply both x and y scale.
174         kVertical,  // The underlying port can only apply a y scale.
175         kVerticalInteger  // The underlying port can only apply an integer y scale.
176     };
177     /**
178      *  Compute useful matrices for use with sizing in underlying libraries.
179      *
180      *  There are two kinds of text size, a 'requested/logical size' which is like asking for size
181      *  '12' and a 'real' size which is the size after the matrix is applied. The matrices produced
182      *  by this method are based on the 'real' size. This method effectively finds the total device
183      *  matrix and decomposes it in various ways.
184      *
185      *  The most useful decomposition is into 'scale' and 'remaining'. The 'scale' is applied first
186      *  and then the 'remaining' to fully apply the total matrix. This decomposition is useful when
187      *  the text size ('scale') may have meaning apart from the total matrix. This is true when
188      *  hinting, and sometimes true for other properties as well.
189      *
190      *  The second (optional) decomposition is of 'remaining' into a non-rotational part
191      *  'remainingWithoutRotation' and a rotational part 'remainingRotation'. The 'scale' is applied
192      *  first, then 'remainingWithoutRotation', then 'remainingRotation' to fully apply the total
193      *  matrix. This decomposition is helpful when only horizontal metrics can be trusted, so the
194      *  'scale' and 'remainingWithoutRotation' will be handled by the underlying library, but
195      *  the final rotation 'remainingRotation' will be handled manually.
196      *
197      *  The 'total' matrix is also (optionally) available. This is useful in cases where the
198      *  underlying library will not be used, often when working directly with font data.
199      *
200      *  The parameters 'scale' and 'remaining' are required, the other pointers may be nullptr.
201      *
202      *  @param preMatrixScale the kind of scale to extract from the total matrix.
203      *  @param scale the scale extracted from the total matrix (both values positive).
204      *  @param remaining apply after scale to apply the total matrix.
205      *  @param remainingWithoutRotation apply after scale to apply the total matrix sans rotation.
206      *  @param remainingRotation apply after remainingWithoutRotation to apply the total matrix.
207      *  @param total the total matrix.
208      *  @return false if the matrix was singular. The output will be valid but not invertible.
209      */
210     bool computeMatrices(PreMatrixScale preMatrixScale,
211                          SkVector* scale, SkMatrix* remaining,
212                          SkMatrix* remainingWithoutRotation = nullptr,
213                          SkMatrix* remainingRotation = nullptr,
214                          SkMatrix* total = nullptr);
215 
216     SkAxisAlignment computeAxisAlignmentForHText() const;
217 
218     inline SkFontHinting getHinting() const;
219     inline void setHinting(SkFontHinting);
220 
getFormatSkScalerContextRec221     SkMask::Format getFormat() const {
222         return fMaskFormat;
223     }
224 
getLuminanceColorSkScalerContextRec225     SkColor getLuminanceColor() const {
226         return fLumBits;
227     }
228 
229     // setLuminanceColor forces the alpha to be 0xFF because the blitter that draws the glyph
230     // will apply the alpha from the paint. Don't apply the alpha twice.
231     void setLuminanceColor(SkColor c);
232 
233 private:
234     // TODO: remove
235     friend class SkScalerContext;
236 };
237 SK_END_REQUIRE_DENSE
238 
239 // TODO: rename SkScalerContextEffects -> SkStrikeEffects
240 struct SkScalerContextEffects {
SkScalerContextEffectsSkScalerContextEffects241     SkScalerContextEffects() : fPathEffect(nullptr), fMaskFilter(nullptr) {}
SkScalerContextEffectsSkScalerContextEffects242     SkScalerContextEffects(SkPathEffect* pe, SkMaskFilter* mf)
243             : fPathEffect(pe), fMaskFilter(mf) {}
SkScalerContextEffectsSkScalerContextEffects244     explicit SkScalerContextEffects(const SkPaint& paint)
245             : fPathEffect(paint.getPathEffect())
246             , fMaskFilter(paint.getMaskFilter()) {}
247 
248     SkPathEffect*   fPathEffect;
249     SkMaskFilter*   fMaskFilter;
250 };
251 
252 class SkScalerContext {
253 public:
254     enum Flags {
255         kFrameAndFill_Flag        = 0x0001,
256         kUnused                   = 0x0002,
257         kEmbeddedBitmapText_Flag  = 0x0004,
258         kEmbolden_Flag            = 0x0008,
259         kSubpixelPositioning_Flag = 0x0010,
260         kForceAutohinting_Flag    = 0x0020,  // Use auto instead of bytcode hinting if hinting.
261 
262         // together, these two flags resulting in a two bit value which matches
263         // up with the SkPaint::Hinting enum.
264         kHinting_Shift            = 7, // to shift into the other flags above
265         kHintingBit1_Flag         = 0x0080,
266         kHintingBit2_Flag         = 0x0100,
267 
268         // Pixel geometry information.
269         // only meaningful if fMaskFormat is kLCD16
270         kLCD_Vertical_Flag        = 0x0200,    // else Horizontal
271         kLCD_BGROrder_Flag        = 0x0400,    // else RGB order
272 
273         // Generate A8 from LCD source (for GDI and CoreGraphics).
274         // only meaningful if fMaskFormat is kA8
275         kGenA8FromLCD_Flag        = 0x0800, // could be 0x200 (bit meaning dependent on fMaskFormat)
276         kLinearMetrics_Flag       = 0x1000,
277         kBaselineSnap_Flag        = 0x2000,
278 
279         kNeedsForegroundColor_Flag = 0x4000,
280     };
281 
282     // computed values
283     enum {
284         kHinting_Mask   = kHintingBit1_Flag | kHintingBit2_Flag,
285     };
286 
287     SkScalerContext(sk_sp<SkTypeface>, const SkScalerContextEffects&, const SkDescriptor*);
288     virtual ~SkScalerContext();
289 
getTypeface()290     SkTypeface* getTypeface() const { return fTypeface.get(); }
291 
getMaskFormat()292     SkMask::Format getMaskFormat() const {
293         return fRec.fMaskFormat;
294     }
295 
isSubpixel()296     bool isSubpixel() const {
297         return SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
298     }
299 
isLinearMetrics()300     bool isLinearMetrics() const {
301         return SkToBool(fRec.fFlags & kLinearMetrics_Flag);
302     }
303 
304     // DEPRECATED
isVertical()305     bool isVertical() const { return false; }
306 
307     SkGlyph     makeGlyph(SkPackedGlyphID, SkArenaAlloc*);
308     void        getImage(const SkGlyph&);
309     void        getPath(SkGlyph&, SkArenaAlloc*);
310     sk_sp<SkDrawable> getDrawable(SkGlyph&);
311     void        getFontMetrics(SkFontMetrics*);
312 
313     /** Return the size in bytes of the associated gamma lookup table
314      */
315     static size_t GetGammaLUTSize(SkScalar contrast, SkScalar deviceGamma,
316                                   int* width, int* height);
317 
318     /** Get the associated gamma lookup table. The 'data' pointer must point to pre-allocated
319      *  memory, with size in bytes greater than or equal to the return value of getGammaLUTSize().
320      *
321      *  If the lookup table hasn't been initialized (e.g., it's linear), this will return false.
322      */
323     static bool GetGammaLUTData(SkScalar contrast, SkScalar deviceGamma, uint8_t* data);
324 
325     static void MakeRecAndEffects(const SkFont& font, const SkPaint& paint,
326                                   const SkSurfaceProps& surfaceProps,
327                                   SkScalerContextFlags scalerContextFlags,
328                                   const SkMatrix& deviceMatrix,
329                                   SkScalerContextRec* rec,
330                                   SkScalerContextEffects* effects);
331 
332     // If we are creating rec and effects from a font only, then there is no device around either.
MakeRecAndEffectsFromFont(const SkFont & font,SkScalerContextRec * rec,SkScalerContextEffects * effects)333     static void MakeRecAndEffectsFromFont(const SkFont& font,
334                                           SkScalerContextRec* rec,
335                                           SkScalerContextEffects* effects) {
336         SkPaint paint;
337         return MakeRecAndEffects(
338                 font, paint, SkSurfaceProps(),
339                 SkScalerContextFlags::kNone, SkMatrix::I(), rec, effects);
340     }
341 
342     static std::unique_ptr<SkScalerContext> MakeEmpty(
343             sk_sp<SkTypeface> typeface, const SkScalerContextEffects& effects,
344             const SkDescriptor* desc);
345 
346     static SkDescriptor* AutoDescriptorGivenRecAndEffects(
347         const SkScalerContextRec& rec,
348         const SkScalerContextEffects& effects,
349         SkAutoDescriptor* ad);
350 
351     static std::unique_ptr<SkDescriptor> DescriptorGivenRecAndEffects(
352         const SkScalerContextRec& rec,
353         const SkScalerContextEffects& effects);
354 
355     static void DescriptorBufferGiveRec(const SkScalerContextRec& rec, void* buffer);
356     static bool CheckBufferSizeForRec(const SkScalerContextRec& rec,
357                                       const SkScalerContextEffects& effects,
358                                       size_t size);
359 
360     static SkMaskGamma::PreBlend GetMaskPreBlend(const SkScalerContextRec& rec);
361 
getRec()362     const SkScalerContextRec& getRec() const { return fRec; }
363 
getEffects()364     SkScalerContextEffects getEffects() const {
365         return { fPathEffect.get(), fMaskFilter.get() };
366     }
367 
368     /**
369     *  Return the axis (if any) that the baseline for horizontal text should land on.
370     *  As an example, the identity matrix will return SkAxisAlignment::kX.
371     */
372     SkAxisAlignment computeAxisAlignmentForHText() const;
373 
374     static SkDescriptor* CreateDescriptorAndEffectsUsingPaint(
375         const SkFont&, const SkPaint&, const SkSurfaceProps&,
376         SkScalerContextFlags scalerContextFlags,
377         const SkMatrix& deviceMatrix, SkAutoDescriptor* ad,
378         SkScalerContextEffects* effects);
379 
380 protected:
381     SkScalerContextRec fRec;
382 
383     struct GlyphMetrics {
384         SkVector       advance;
385         SkRect         bounds;
386         SkMask::Format maskFormat;
387         uint16_t       extraBits;
388         bool           neverRequestPath;
389         bool           computeFromPath;
390 
GlyphMetricsGlyphMetrics391         GlyphMetrics(SkMask::Format format)
392             : advance{0, 0}
393             , bounds{0, 0, 0, 0}
394             , maskFormat(format)
395             , extraBits(0)
396             , neverRequestPath(false)
397             , computeFromPath(false)
398         {}
399     };
400 
401     virtual GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) = 0;
402 
403     static void GenerateMetricsFromPath(
404         SkGlyph* glyph, const SkPath& path, SkMask::Format format,
405         bool verticalLCD, bool a8FromLCD, bool hairline);
406 
407     static void SaturateGlyphBounds(SkGlyph* glyph, SkRect&&);
408     static void SaturateGlyphBounds(SkGlyph* glyph, SkIRect const &);
409 
410     /** Generates the contents of glyph.fImage.
411      *  When called, glyph.fImage will be pointing to a pre-allocated,
412      *  uninitialized region of memory of size glyph.imageSize().
413      *  This method may not change glyph.fMaskFormat.
414      *
415      *  Because glyph.imageSize() will determine the size of fImage,
416      *  generateMetrics will be called before generateImage.
417      */
418     virtual void generateImage(const SkGlyph& glyph, void* imageBuffer) = 0;
419     static void GenerateImageFromPath(
420         SkMaskBuilder& dst, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend,
421         bool doBGR, bool verticalLCD, bool a8FromLCD, bool hairline);
422 
423     /** Sets the passed path to the glyph outline.
424      *  If this cannot be done the path is set to empty;
425      *  Does not apply subpixel positioning to the path.
426      *  @return false if this glyph does not have any path.
427      */
428     [[nodiscard]] virtual bool generatePath(const SkGlyph&, SkPath*) = 0;
429 
430     /** Returns the drawable for the glyph (if any).
431      *
432      *  The generated drawable will be lifetime scoped to the lifetime of this scaler context.
433      *  This means the drawable may refer to the scaler context and associated font data.
434      *
435      *  The drawable does not need to be flattenable (e.g. implement getFactory and getTypeName).
436      *  Any necessary serialization will be done with makePictureSnapshot.
437      */
438     virtual sk_sp<SkDrawable> generateDrawable(const SkGlyph&); // TODO: = 0
439 
440     /** Retrieves font metrics. */
441     virtual void generateFontMetrics(SkFontMetrics*) = 0;
442 
forceGenerateImageFromPath()443     void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
forceOffGenerateImageFromPath()444     void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; }
445 
446 private:
447     friend class PathText;  // For debug purposes
448     friend class PathTextBench;  // For debug purposes
449     friend class RandomScalerContext;  // For debug purposes
450 
451     static SkScalerContextRec PreprocessRec(const SkTypeface&,
452                                             const SkScalerContextEffects&,
453                                             const SkDescriptor&);
454 
455     // never null
456     sk_sp<SkTypeface> fTypeface;
457 
458     // optional objects, which may be null
459     sk_sp<SkPathEffect> fPathEffect;
460     sk_sp<SkMaskFilter> fMaskFilter;
461 
462     // if this is set, we draw the image from a path, rather than
463     // calling generateImage.
464     bool fGenerateImageFromPath;
465 
466     void internalGetPath(SkGlyph&, SkArenaAlloc*);
467     SkGlyph internalMakeGlyph(SkPackedGlyphID, SkMask::Format, SkArenaAlloc*);
468 
469 protected:
470     // SkMaskGamma::PreBlend converts linear masks to gamma correcting masks.
471     // Visible to subclasses so that generateImage can apply the pre-blend directly.
472     const SkMaskGamma::PreBlend fPreBlend;
473 };
474 
475 #define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
476 #define kEffects_SkDescriptorTag        SkSetFourByteTag('e', 'f', 'c', 't')
477 
478 ///////////////////////////////////////////////////////////////////////////////
479 
getHinting()480 SkFontHinting SkScalerContextRec::getHinting() const {
481     unsigned hint = (fFlags & SkScalerContext::kHinting_Mask) >>
482                                             SkScalerContext::kHinting_Shift;
483     return static_cast<SkFontHinting>(hint);
484 }
485 
setHinting(SkFontHinting hinting)486 void SkScalerContextRec::setHinting(SkFontHinting hinting) {
487     fFlags = (fFlags & ~SkScalerContext::kHinting_Mask) |
488                         (static_cast<unsigned>(hinting) << SkScalerContext::kHinting_Shift);
489 }
490 
491 
492 #endif
493