xref: /aosp_15_r20/external/skia/src/core/SkScalerContext.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkFourByteTag.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkScalar.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkSurfaceProps.h"
20 #include "include/core/SkTypeface.h"
21 #include "include/core/SkTypes.h"
22 #include "include/private/base/SkMacros.h"
23 #include "include/private/base/SkPoint_impl.h"
24 #include "include/private/base/SkTemplates.h"
25 #include "include/private/base/SkTo.h"
26 #include "src/core/SkGlyph.h"
27 #include "src/core/SkMask.h"
28 #include "src/core/SkMaskGamma.h"
29 
30 #include <cstddef>
31 #include <cstdint>
32 #include <memory>
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     /** If the kEmbolden_Flag is set, drop it and use stroking instead. */
140     void useStrokeForFakeBold();
141 
142     SkMask::Format fMaskFormat;
143 
144 private:
145     uint8_t        fStrokeJoin : 4;
146     uint8_t        fStrokeCap  : 4;
147 
148 public:
149     uint16_t    fFlags;
150 
151     // Warning: when adding members note that the size of this structure
152     // must be a multiple of 4. SkDescriptor requires that its arguments be
153     // multiples of four and this structure is put in an SkDescriptor in
154     // SkPaint::MakeRecAndEffects.
155 
dumpSkScalerContextRec156     SkString dump() const {
157         SkString msg;
158         msg.appendf("    Rec\n");
159         msg.appendf("      textsize %a prescale %a preskew %a post [%a %a %a %a]\n",
160                    fTextSize, fPreScaleX, fPreSkewX, fPost2x2[0][0],
161                    fPost2x2[0][1], fPost2x2[1][0], fPost2x2[1][1]);
162         msg.appendf("      frame %g miter %g format %d join %d cap %d flags %#hx\n",
163                    fFrameWidth, fMiterLimit, fMaskFormat, fStrokeJoin, fStrokeCap, fFlags);
164         msg.appendf("      lum bits %x, device gamma %d, contrast %d\n", fLumBits,
165                     fDeviceGamma, fContrast);
166         msg.appendf("      foreground color %x\n", fForegroundColor);
167         return msg;
168     }
169 
170     void    getMatrixFrom2x2(SkMatrix*) const;
171     void    getLocalMatrix(SkMatrix*) const;
172     void    getSingleMatrix(SkMatrix*) const;
173 
174     /** The kind of scale which will be applied by the underlying port (pre-matrix). */
175     enum class PreMatrixScale {
176         kFull,  // The underlying port can apply both x and y scale.
177         kVertical,  // The underlying port can only apply a y scale.
178         kVerticalInteger  // The underlying port can only apply an integer y scale.
179     };
180     /**
181      *  Compute useful matrices for use with sizing in underlying libraries.
182      *
183      *  There are two kinds of text size, a 'requested/logical size' which is like asking for size
184      *  '12' and a 'real' size which is the size after the matrix is applied. The matrices produced
185      *  by this method are based on the 'real' size. This method effectively finds the total device
186      *  matrix and decomposes it in various ways.
187      *
188      *  The most useful decomposition is into 'scale' and 'remaining'. The 'scale' is applied first
189      *  and then the 'remaining' to fully apply the total matrix. This decomposition is useful when
190      *  the text size ('scale') may have meaning apart from the total matrix. This is true when
191      *  hinting, and sometimes true for other properties as well.
192      *
193      *  The second (optional) decomposition is of 'remaining' into a non-rotational part
194      *  'remainingWithoutRotation' and a rotational part 'remainingRotation'. The 'scale' is applied
195      *  first, then 'remainingWithoutRotation', then 'remainingRotation' to fully apply the total
196      *  matrix. This decomposition is helpful when only horizontal metrics can be trusted, so the
197      *  'scale' and 'remainingWithoutRotation' will be handled by the underlying library, but
198      *  the final rotation 'remainingRotation' will be handled manually.
199      *
200      *  The 'total' matrix is also (optionally) available. This is useful in cases where the
201      *  underlying library will not be used, often when working directly with font data.
202      *
203      *  The parameters 'scale' and 'remaining' are required, the other pointers may be nullptr.
204      *
205      *  @param preMatrixScale the kind of scale to extract from the total matrix.
206      *  @param scale the scale extracted from the total matrix (both values positive).
207      *  @param remaining apply after scale to apply the total matrix.
208      *  @param remainingWithoutRotation apply after scale to apply the total matrix sans rotation.
209      *  @param remainingRotation apply after remainingWithoutRotation to apply the total matrix.
210      *  @param total the total matrix.
211      *  @return false if the matrix was singular. The output will be valid but not invertible.
212      */
213     bool computeMatrices(PreMatrixScale preMatrixScale,
214                          SkVector* scale, SkMatrix* remaining,
215                          SkMatrix* remainingWithoutRotation = nullptr,
216                          SkMatrix* remainingRotation = nullptr,
217                          SkMatrix* total = nullptr);
218 
219     SkAxisAlignment computeAxisAlignmentForHText() const;
220 
221     inline SkFontHinting getHinting() const;
222     inline void setHinting(SkFontHinting);
223 
getFormatSkScalerContextRec224     SkMask::Format getFormat() const {
225         return fMaskFormat;
226     }
227 
getLuminanceColorSkScalerContextRec228     SkColor getLuminanceColor() const {
229         return fLumBits;
230     }
231 
232     // setLuminanceColor forces the alpha to be 0xFF because the blitter that draws the glyph
233     // will apply the alpha from the paint. Don't apply the alpha twice.
234     void setLuminanceColor(SkColor c);
235 
236 private:
237     // TODO: remove
238     friend class SkScalerContext;
239 };
240 SK_END_REQUIRE_DENSE
241 
242 // TODO: rename SkScalerContextEffects -> SkStrikeEffects
243 struct SkScalerContextEffects {
SkScalerContextEffectsSkScalerContextEffects244     SkScalerContextEffects() : fPathEffect(nullptr), fMaskFilter(nullptr) {}
SkScalerContextEffectsSkScalerContextEffects245     SkScalerContextEffects(SkPathEffect* pe, SkMaskFilter* mf)
246             : fPathEffect(pe), fMaskFilter(mf) {}
SkScalerContextEffectsSkScalerContextEffects247     explicit SkScalerContextEffects(const SkPaint& paint)
248             : fPathEffect(paint.getPathEffect())
249             , fMaskFilter(paint.getMaskFilter()) {}
250 
251     SkPathEffect*   fPathEffect;
252     SkMaskFilter*   fMaskFilter;
253 };
254 
255 class SkScalerContext {
256 public:
257     enum Flags {
258         kFrameAndFill_Flag        = 0x0001,
259         kUnused                   = 0x0002,
260         kEmbeddedBitmapText_Flag  = 0x0004,
261         kEmbolden_Flag            = 0x0008,
262         kSubpixelPositioning_Flag = 0x0010,
263         kForceAutohinting_Flag    = 0x0020,  // Use auto instead of bytcode hinting if hinting.
264 
265         // together, these two flags resulting in a two bit value which matches
266         // up with the SkPaint::Hinting enum.
267         kHinting_Shift            = 7, // to shift into the other flags above
268         kHintingBit1_Flag         = 0x0080,
269         kHintingBit2_Flag         = 0x0100,
270 
271         // Pixel geometry information.
272         // only meaningful if fMaskFormat is kLCD16
273         kLCD_Vertical_Flag        = 0x0200,    // else Horizontal
274         kLCD_BGROrder_Flag        = 0x0400,    // else RGB order
275 
276         // Generate A8 from LCD source (for GDI and CoreGraphics).
277         // only meaningful if fMaskFormat is kA8
278         kGenA8FromLCD_Flag        = 0x0800, // could be 0x200 (bit meaning dependent on fMaskFormat)
279         kLinearMetrics_Flag       = 0x1000,
280         kBaselineSnap_Flag        = 0x2000,
281 
282         kNeedsForegroundColor_Flag = 0x4000,
283     };
284 
285     // computed values
286     enum {
287         kHinting_Mask   = kHintingBit1_Flag | kHintingBit2_Flag,
288     };
289 
290     SkScalerContext(sk_sp<SkTypeface>, const SkScalerContextEffects&, const SkDescriptor*);
291     virtual ~SkScalerContext();
292 
getTypeface()293     SkTypeface* getTypeface() const { return fTypeface.get(); }
294 
getMaskFormat()295     SkMask::Format getMaskFormat() const {
296         return fRec.fMaskFormat;
297     }
298 
isSubpixel()299     bool isSubpixel() const {
300         return SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
301     }
302 
isLinearMetrics()303     bool isLinearMetrics() const {
304         return SkToBool(fRec.fFlags & kLinearMetrics_Flag);
305     }
306 
307     // DEPRECATED
isVertical()308     bool isVertical() const { return false; }
309 
310     SkGlyph     makeGlyph(SkPackedGlyphID, SkArenaAlloc*);
311     void        getImage(const SkGlyph&);
312     void        getPath(SkGlyph&, SkArenaAlloc*);
313     sk_sp<SkDrawable> getDrawable(SkGlyph&);
314     void        getFontMetrics(SkFontMetrics*);
315 
316     /** Return the size in bytes of the associated gamma lookup table
317      */
318     static size_t GetGammaLUTSize(SkScalar contrast, SkScalar deviceGamma,
319                                   int* width, int* height);
320 
321     /** Get the associated gamma lookup table. The 'data' pointer must point to pre-allocated
322      *  memory, with size in bytes greater than or equal to the return value of getGammaLUTSize().
323      *
324      *  If the lookup table hasn't been initialized (e.g., it's linear), this will return false.
325      */
326     static bool GetGammaLUTData(SkScalar contrast, SkScalar deviceGamma, uint8_t* data);
327 
328     static void MakeRecAndEffects(const SkFont& font, const SkPaint& paint,
329                                   const SkSurfaceProps& surfaceProps,
330                                   SkScalerContextFlags scalerContextFlags,
331                                   const SkMatrix& deviceMatrix,
332                                   SkScalerContextRec* rec,
333                                   SkScalerContextEffects* effects);
334 
335     // 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)336     static void MakeRecAndEffectsFromFont(const SkFont& font,
337                                           SkScalerContextRec* rec,
338                                           SkScalerContextEffects* effects) {
339         SkPaint paint;
340         return MakeRecAndEffects(
341                 font, paint, SkSurfaceProps(),
342                 SkScalerContextFlags::kNone, SkMatrix::I(), rec, effects);
343     }
344 
345     static std::unique_ptr<SkScalerContext> MakeEmpty(
346             sk_sp<SkTypeface> typeface, const SkScalerContextEffects& effects,
347             const SkDescriptor* desc);
348 
349     static SkDescriptor* AutoDescriptorGivenRecAndEffects(
350         const SkScalerContextRec& rec,
351         const SkScalerContextEffects& effects,
352         SkAutoDescriptor* ad);
353 
354     static std::unique_ptr<SkDescriptor> DescriptorGivenRecAndEffects(
355         const SkScalerContextRec& rec,
356         const SkScalerContextEffects& effects);
357 
358     static void DescriptorBufferGiveRec(const SkScalerContextRec& rec, void* buffer);
359     static bool CheckBufferSizeForRec(const SkScalerContextRec& rec,
360                                       const SkScalerContextEffects& effects,
361                                       size_t size);
362 
363     static SkMaskGamma::PreBlend GetMaskPreBlend(const SkScalerContextRec& rec);
364 
getRec()365     const SkScalerContextRec& getRec() const { return fRec; }
366 
getEffects()367     SkScalerContextEffects getEffects() const {
368         return { fPathEffect.get(), fMaskFilter.get() };
369     }
370 
371     /**
372     *  Return the axis (if any) that the baseline for horizontal text should land on.
373     *  As an example, the identity matrix will return SkAxisAlignment::kX.
374     */
375     SkAxisAlignment computeAxisAlignmentForHText() const;
376 
377     static SkDescriptor* CreateDescriptorAndEffectsUsingPaint(
378         const SkFont&, const SkPaint&, const SkSurfaceProps&,
379         SkScalerContextFlags scalerContextFlags,
380         const SkMatrix& deviceMatrix, SkAutoDescriptor* ad,
381         SkScalerContextEffects* effects);
382 
383 protected:
384     SkScalerContextRec fRec;
385 
386     struct GlyphMetrics {
387         SkVector       advance;
388         SkRect         bounds;
389         SkMask::Format maskFormat;
390         uint16_t       extraBits;
391         bool           neverRequestPath;
392         bool           computeFromPath;
393 
GlyphMetricsGlyphMetrics394         GlyphMetrics(SkMask::Format format)
395             : advance{0, 0}
396             , bounds{0, 0, 0, 0}
397             , maskFormat(format)
398             , extraBits(0)
399             , neverRequestPath(false)
400             , computeFromPath(false)
401         {}
402     };
403 
404     virtual GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) = 0;
405 
406     static void GenerateMetricsFromPath(
407         SkGlyph* glyph, const SkPath& path, SkMask::Format format,
408         bool verticalLCD, bool a8FromLCD, bool hairline);
409 
410     static void SaturateGlyphBounds(SkGlyph* glyph, SkRect&&);
411     static void SaturateGlyphBounds(SkGlyph* glyph, SkIRect const &);
412 
413     /** Generates the contents of glyph.fImage.
414      *  When called, glyph.fImage will be pointing to a pre-allocated,
415      *  uninitialized region of memory of size glyph.imageSize().
416      *  This method may not change glyph.fMaskFormat.
417      *
418      *  Because glyph.imageSize() will determine the size of fImage,
419      *  generateMetrics will be called before generateImage.
420      */
421     virtual void generateImage(const SkGlyph& glyph, void* imageBuffer) = 0;
422     static void GenerateImageFromPath(
423         SkMaskBuilder& dst, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend,
424         bool doBGR, bool verticalLCD, bool a8FromLCD, bool hairline);
425 
426     /** Sets the passed path to the glyph outline.
427      *  If this cannot be done the path is set to empty;
428      *  Does not apply subpixel positioning to the path.
429      *  @return false if this glyph does not have any path.
430      */
431     [[nodiscard]] virtual bool generatePath(const SkGlyph&, SkPath*, bool* modified) = 0;
432 
433     /** Returns the drawable for the glyph (if any).
434      *
435      *  The generated drawable will be lifetime scoped to the lifetime of this scaler context.
436      *  This means the drawable may refer to the scaler context and associated font data.
437      *
438      *  The drawable does not need to be flattenable (e.g. implement getFactory and getTypeName).
439      *  Any necessary serialization will be done with makePictureSnapshot.
440      */
441     virtual sk_sp<SkDrawable> generateDrawable(const SkGlyph&); // TODO: = 0
442 
443     /** Retrieves font metrics. */
444     virtual void generateFontMetrics(SkFontMetrics*) = 0;
445 
forceGenerateImageFromPath()446     void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
forceOffGenerateImageFromPath()447     void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; }
448 
449 private:
450     friend class PathText;  // For debug purposes
451     friend class PathTextBench;  // For debug purposes
452     friend class RandomScalerContext;  // For debug purposes
453     friend class SkScalerContext_proxy;
454 
455     static SkScalerContextRec PreprocessRec(const SkTypeface&,
456                                             const SkScalerContextEffects&,
457                                             const SkDescriptor&);
458 
459     // never null
460     sk_sp<SkTypeface> fTypeface;
461 
462     // optional objects, which may be null
463     sk_sp<SkPathEffect> fPathEffect;
464     sk_sp<SkMaskFilter> fMaskFilter;
465 
466     // if this is set, we draw the image from a path, rather than
467     // calling generateImage.
468     bool fGenerateImageFromPath;
469 
470     void internalGetPath(SkGlyph&, SkArenaAlloc*);
471     SkGlyph internalMakeGlyph(SkPackedGlyphID, SkMask::Format, SkArenaAlloc*);
472 
473 protected:
474     // SkMaskGamma::PreBlend converts linear masks to gamma correcting masks.
475     // Visible to subclasses so that generateImage can apply the pre-blend directly.
476     const SkMaskGamma::PreBlend fPreBlend;
477 };
478 
479 #define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
480 #define kEffects_SkDescriptorTag        SkSetFourByteTag('e', 'f', 'c', 't')
481 
482 ///////////////////////////////////////////////////////////////////////////////
483 
getHinting()484 SkFontHinting SkScalerContextRec::getHinting() const {
485     unsigned hint = (fFlags & SkScalerContext::kHinting_Mask) >>
486                                             SkScalerContext::kHinting_Shift;
487     return static_cast<SkFontHinting>(hint);
488 }
489 
setHinting(SkFontHinting hinting)490 void SkScalerContextRec::setHinting(SkFontHinting hinting) {
491     fFlags = (fFlags & ~SkScalerContext::kHinting_Mask) |
492                         (static_cast<unsigned>(hinting) << SkScalerContext::kHinting_Shift);
493 }
494 
495 
496 #endif
497