• 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 <memory>
12 
13 #include "SkFont.h"
14 #include "SkFontTypes.h"
15 #include "SkGlyph.h"
16 #include "SkMacros.h"
17 #include "SkMask.h"
18 #include "SkMaskFilter.h"
19 #include "SkMaskGamma.h"
20 #include "SkMatrix.h"
21 #include "SkPaint.h"
22 #include "SkSurfacePriv.h"
23 #include "SkTypeface.h"
24 #include "SkWriteBuffer.h"
25 
26 class SkAutoDescriptor;
27 class SkDescriptor;
28 class SkMaskFilter;
29 class SkPathEffect;
30 class SkScalerContext;
31 class SkScalerContext_DW;
32 
33 enum SkScalerContextFlags : uint32_t {
34     kNone                      = 0,
35     kFakeGamma                 = 1 << 0,
36     kBoostContrast             = 1 << 1,
37     kFakeGammaAndBoostContrast = kFakeGamma | kBoostContrast,
38 };
39 
40 struct SkScalerContextEffects {
SkScalerContextEffectsSkScalerContextEffects41     SkScalerContextEffects() : fPathEffect(nullptr), fMaskFilter(nullptr) {}
SkScalerContextEffectsSkScalerContextEffects42     SkScalerContextEffects(SkPathEffect* pe, SkMaskFilter* mf)
43         : fPathEffect(pe), fMaskFilter(mf) {}
SkScalerContextEffectsSkScalerContextEffects44     explicit SkScalerContextEffects(const SkPaint& paint)
45         : fPathEffect(paint.getPathEffect())
46         , fMaskFilter(paint.getMaskFilter()) {}
47 
48     SkPathEffect*   fPathEffect;
49     SkMaskFilter*   fMaskFilter;
50 };
51 
52 enum SkAxisAlignment : uint32_t {
53     kNone_SkAxisAlignment,
54     kX_SkAxisAlignment,
55     kY_SkAxisAlignment
56 };
57 
58 /*
59  *  To allow this to be forward-declared, it must be its own typename, rather
60  *  than a nested struct inside SkScalerContext (where it started).
61  *
62  *  SkScalerContextRec must be dense, and all bytes must be set to a know quantity because this
63  *  structure is used to calculate a checksum.
64  */
65 SK_BEGIN_REQUIRE_DENSE
66 struct SkScalerContextRec {
67     uint32_t    fFontID;
68     SkScalar    fTextSize, fPreScaleX, fPreSkewX;
69     SkScalar    fPost2x2[2][2];
70     SkScalar    fFrameWidth, fMiterLimit;
71 
72 private:
73     //These describe the parameters to create (uniquely identify) the pre-blend.
74     uint32_t      fLumBits;
75     uint8_t       fDeviceGamma; //2.6, (0.0, 4.0) gamma, 0.0 for sRGB
76     uint8_t       fPaintGamma;  //2.6, (0.0, 4.0) gamma, 0.0 for sRGB
77     uint8_t       fContrast;    //0.8+1, [0.0, 1.0] artificial contrast
78     const uint8_t fReservedAlign{0};
79 
80 public:
81 
getDeviceGammaSkScalerContextRec82     SkScalar getDeviceGamma() const {
83         return SkIntToScalar(fDeviceGamma) / (1 << 6);
84     }
setDeviceGammaSkScalerContextRec85     void setDeviceGamma(SkScalar dg) {
86         SkASSERT(0 <= dg && dg < SkIntToScalar(4));
87         fDeviceGamma = SkScalarFloorToInt(dg * (1 << 6));
88     }
89 
getPaintGammaSkScalerContextRec90     SkScalar getPaintGamma() const {
91         return SkIntToScalar(fPaintGamma) / (1 << 6);
92     }
setPaintGammaSkScalerContextRec93     void setPaintGamma(SkScalar pg) {
94         SkASSERT(0 <= pg && pg < SkIntToScalar(4));
95         fPaintGamma = SkScalarFloorToInt(pg * (1 << 6));
96     }
97 
getContrastSkScalerContextRec98     SkScalar getContrast() const {
99         return SkIntToScalar(fContrast) / ((1 << 8) - 1);
100     }
setContrastSkScalerContextRec101     void setContrast(SkScalar c) {
102         SkASSERT(0 <= c && c <= SK_Scalar1);
103         fContrast = SkScalarRoundToInt(c * ((1 << 8) - 1));
104     }
105 
106     /**
107      *  Causes the luminance color to be ignored, and the paint and device
108      *  gamma to be effectively 1.0
109      */
ignoreGammaSkScalerContextRec110     void ignoreGamma() {
111         setLuminanceColor(SK_ColorTRANSPARENT);
112         setPaintGamma(SK_Scalar1);
113         setDeviceGamma(SK_Scalar1);
114     }
115 
116     /**
117      *  Causes the luminance color and contrast to be ignored, and the
118      *  paint and device gamma to be effectively 1.0.
119      */
ignorePreBlendSkScalerContextRec120     void ignorePreBlend() {
121         ignoreGamma();
122         setContrast(0);
123     }
124 
125     uint8_t     fMaskFormat;
126 private:
127     uint8_t     fStrokeJoin : 4;
128     uint8_t     fStrokeCap : 4;
129 
130 public:
131     uint16_t    fFlags;
132 
133     // Warning: when adding members note that the size of this structure
134     // must be a multiple of 4. SkDescriptor requires that its arguments be
135     // multiples of four and this structure is put in an SkDescriptor in
136     // SkPaint::MakeRecAndEffects.
137 
dumpSkScalerContextRec138     SkString dump() const {
139         SkString msg;
140         msg.appendf("Rec\n");
141         msg.appendf("  textsize %g prescale %g preskew %g post [%g %g %g %g]\n",
142                    fTextSize, fPreScaleX, fPreSkewX, fPost2x2[0][0],
143                    fPost2x2[0][1], fPost2x2[1][0], fPost2x2[1][1]);
144         msg.appendf("  frame %g miter %g format %d join %d cap %d flags %#hx\n",
145                    fFrameWidth, fMiterLimit, fMaskFormat, fStrokeJoin, fStrokeCap, fFlags);
146         msg.appendf("  lum bits %x, device gamma %d, paint gamma %d contrast %d\n", fLumBits,
147                     fDeviceGamma, fPaintGamma, fContrast);
148         return msg;
149     }
150 
151     void    getMatrixFrom2x2(SkMatrix*) const;
152     void    getLocalMatrix(SkMatrix*) const;
153     void    getSingleMatrix(SkMatrix*) const;
154 
155     /** The kind of scale which will be applied by the underlying port (pre-matrix). */
156     enum PreMatrixScale {
157         kFull_PreMatrixScale,  // The underlying port can apply both x and y scale.
158         kVertical_PreMatrixScale,  // The underlying port can only apply a y scale.
159         kVerticalInteger_PreMatrixScale  // The underlying port can only apply an integer y scale.
160     };
161     /**
162      *  Compute useful matrices for use with sizing in underlying libraries.
163      *
164      *  There are two kinds of text size, a 'requested/logical size' which is like asking for size
165      *  '12' and a 'real' size which is the size after the matrix is applied. The matrices produced
166      *  by this method are based on the 'real' size. This method effectively finds the total device
167      *  matrix and decomposes it in various ways.
168      *
169      *  The most useful decomposition is into 'scale' and 'remaining'. The 'scale' is applied first
170      *  and then the 'remaining' to fully apply the total matrix. This decomposition is useful when
171      *  the text size ('scale') may have meaning apart from the total matrix. This is true when
172      *  hinting, and sometimes true for other properties as well.
173      *
174      *  The second (optional) decomposition is of 'remaining' into a non-rotational part
175      *  'remainingWithoutRotation' and a rotational part 'remainingRotation'. The 'scale' is applied
176      *  first, then 'remainingWithoutRotation', then 'remainingRotation' to fully apply the total
177      *  matrix. This decomposition is helpful when only horizontal metrics can be trusted, so the
178      *  'scale' and 'remainingWithoutRotation' will be handled by the underlying library, but
179      *  the final rotation 'remainingRotation' will be handled manually.
180      *
181      *  The 'total' matrix is also (optionally) available. This is useful in cases where the
182      *  underlying library will not be used, often when working directly with font data.
183      *
184      *  The parameters 'scale' and 'remaining' are required, the other pointers may be nullptr.
185      *
186      *  @param preMatrixScale the kind of scale to extract from the total matrix.
187      *  @param scale the scale extracted from the total matrix (both values positive).
188      *  @param remaining apply after scale to apply the total matrix.
189      *  @param remainingWithoutRotation apply after scale to apply the total matrix sans rotation.
190      *  @param remainingRotation apply after remainingWithoutRotation to apply the total matrix.
191      *  @param total the total matrix.
192      *  @return false if the matrix was singular. The output will be valid but not invertible.
193      */
194     bool computeMatrices(PreMatrixScale preMatrixScale,
195                          SkVector* scale, SkMatrix* remaining,
196                          SkMatrix* remainingWithoutRotation = nullptr,
197                          SkMatrix* remainingRotation = nullptr,
198                          SkMatrix* total = nullptr);
199 
200     SkAxisAlignment computeAxisAlignmentForHText() const;
201 
202     inline SkFontHinting getHinting() const;
203     inline void setHinting(SkFontHinting);
204 
getFormatSkScalerContextRec205     SkMask::Format getFormat() const {
206         return static_cast<SkMask::Format>(fMaskFormat);
207     }
208 
209 private:
210     // TODO: get rid of these bad friends.
211     friend class SkScalerContext;
212     friend class SkScalerContext_DW;
213 
getLuminanceColorSkScalerContextRec214     SkColor getLuminanceColor() const {
215         return fLumBits;
216     }
217 
218 
setLuminanceColorSkScalerContextRec219     void setLuminanceColor(SkColor c) {
220         fLumBits = c;
221     }
222 };
223 SK_END_REQUIRE_DENSE
224 
225 //The following typedef hides from the rest of the implementation the number of
226 //most significant bits to consider when creating mask gamma tables. Two bits
227 //per channel was chosen as a balance between fidelity (more bits) and cache
228 //sizes (fewer bits). Three bits per channel was chosen when #303942; (used by
229 //the Chrome UI) turned out too green.
230 typedef SkTMaskGamma<3, 3, 3> SkMaskGamma;
231 
232 class SkScalerContext {
233 public:
234     enum Flags {
235         kFrameAndFill_Flag        = 0x0001,
236         kUnused                   = 0x0002,
237         kEmbeddedBitmapText_Flag  = 0x0004,
238         kEmbolden_Flag            = 0x0008,
239         kSubpixelPositioning_Flag = 0x0010,
240         kForceAutohinting_Flag    = 0x0020,  // Use auto instead of bytcode hinting if hinting.
241 
242         // together, these two flags resulting in a two bit value which matches
243         // up with the SkPaint::Hinting enum.
244         kHinting_Shift            = 7, // to shift into the other flags above
245         kHintingBit1_Flag         = 0x0080,
246         kHintingBit2_Flag         = 0x0100,
247 
248         // Pixel geometry information.
249         // only meaningful if fMaskFormat is kLCD16
250         kLCD_Vertical_Flag        = 0x0200,    // else Horizontal
251         kLCD_BGROrder_Flag        = 0x0400,    // else RGB order
252 
253         // Generate A8 from LCD source (for GDI and CoreGraphics).
254         // only meaningful if fMaskFormat is kA8
255         kGenA8FromLCD_Flag        = 0x0800, // could be 0x200 (bit meaning dependent on fMaskFormat)
256     };
257 
258     // computed values
259     enum {
260         kHinting_Mask   = kHintingBit1_Flag | kHintingBit2_Flag,
261     };
262 
263     SkScalerContext(sk_sp<SkTypeface>, const SkScalerContextEffects&, const SkDescriptor*);
264     virtual ~SkScalerContext();
265 
getTypeface()266     SkTypeface* getTypeface() const { return fTypeface.get(); }
267 
getMaskFormat()268     SkMask::Format getMaskFormat() const {
269         return (SkMask::Format)fRec.fMaskFormat;
270     }
271 
isSubpixel()272     bool isSubpixel() const {
273         return SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
274     }
275 
276     // DEPRECATED
isVertical()277     bool isVertical() const { return false; }
278 
279     /** Return the corresponding glyph for the specified unichar. Since contexts
280         may be chained (under the hood), the glyphID that is returned may in
281         fact correspond to a different font/context. In that case, we use the
282         base-glyph-count to know how to translate back into local glyph space.
283      */
charToGlyphID(SkUnichar uni)284     uint16_t charToGlyphID(SkUnichar uni) {
285         return generateCharToGlyph(uni);
286     }
287 
getGlyphCount()288     unsigned    getGlyphCount() { return this->generateGlyphCount(); }
289     void        getAdvance(SkGlyph*);
290     void        getMetrics(SkGlyph*);
291     void        getImage(const SkGlyph&);
292     bool SK_WARN_UNUSED_RESULT getPath(SkPackedGlyphID, SkPath*);
293     void        getFontMetrics(SkFontMetrics*);
294 
295     /** Return the size in bytes of the associated gamma lookup table
296      */
297     static size_t GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
298                                   int* width, int* height);
299 
300     /** Get the associated gamma lookup table. The 'data' pointer must point to pre-allocated
301      *  memory, with size in bytes greater than or equal to the return value of getGammaLUTSize().
302      *
303      *  If the lookup table hasn't been initialized (e.g., it's linear), this will return false.
304      */
305     static bool   GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
306                                   uint8_t* data);
307 
308     static void MakeRecAndEffects(const SkFont& font, const SkPaint& paint,
309                                   const SkSurfaceProps& surfaceProps,
310                                   SkScalerContextFlags scalerContextFlags,
311                                   const SkMatrix& deviceMatrix,
312                                   SkScalerContextRec* rec,
313                                   SkScalerContextEffects* effects,
314                                   bool enableTypefaceFiltering = true);
315 
316     // If we are creating rec and effects from a font only, then there is no device around either.
317     static void MakeRecAndEffectsFromFont(const SkFont& font,
318                                           SkScalerContextRec* rec,
319                                           SkScalerContextEffects* effects,
320                                           bool enableTypefaceFiltering = true) {
321         SkPaint paint;
322         return MakeRecAndEffects(
323                 font, paint, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType),
324                 SkScalerContextFlags::kNone, SkMatrix::I(), rec, effects, enableTypefaceFiltering);
325     }
326 
327     static SkDescriptor*  MakeDescriptorForPaths(SkFontID fontID,
328                                                  SkAutoDescriptor* ad);
329 
330     static SkDescriptor* AutoDescriptorGivenRecAndEffects(
331         const SkScalerContextRec& rec,
332         const SkScalerContextEffects& effects,
333         SkAutoDescriptor* ad);
334 
335     static std::unique_ptr<SkDescriptor> DescriptorGivenRecAndEffects(
336         const SkScalerContextRec& rec,
337         const SkScalerContextEffects& effects);
338 
339     static void DescriptorBufferGiveRec(const SkScalerContextRec& rec, void* buffer);
340     static bool CheckBufferSizeForRec(const SkScalerContextRec& rec,
341                                       const SkScalerContextEffects& effects,
342                                       size_t size);
343 
344     static SkMaskGamma::PreBlend GetMaskPreBlend(const SkScalerContextRec& rec);
345 
getRec()346     const SkScalerContextRec& getRec() const { return fRec; }
347 
getEffects()348     SkScalerContextEffects getEffects() const {
349         return { fPathEffect.get(), fMaskFilter.get() };
350     }
351 
352     /**
353     *  Return the axis (if any) that the baseline for horizontal text should land on.
354     *  As an example, the identity matrix will return kX_SkAxisAlignment
355     */
356     SkAxisAlignment computeAxisAlignmentForHText() const;
357 
358     static SkDescriptor* CreateDescriptorAndEffectsUsingPaint(
359         const SkFont&, const SkPaint&, const SkSurfaceProps&,
360         SkScalerContextFlags scalerContextFlags,
361         const SkMatrix& deviceMatrix, SkAutoDescriptor* ad,
362         SkScalerContextEffects* effects);
363 
364 protected:
365     SkScalerContextRec fRec;
366 
367     /** Generates the contents of glyph.fAdvanceX and glyph.fAdvanceY if it can do so quickly.
368      *  Returns true if it could, false otherwise.
369      */
370     virtual bool generateAdvance(SkGlyph* glyph) = 0;
371 
372     /** Generates the contents of glyph.fWidth, fHeight, fTop, fLeft,
373      *  as well as fAdvanceX and fAdvanceY if not already set.
374      *
375      *  TODO: fMaskFormat is set by getMetrics later; cannot be set here.
376      */
377     virtual void generateMetrics(SkGlyph* glyph) = 0;
378 
379     /** Generates the contents of glyph.fImage.
380      *  When called, glyph.fImage will be pointing to a pre-allocated,
381      *  uninitialized region of memory of size glyph.computeImageSize().
382      *  This method may change glyph.fMaskFormat if the new image size is
383      *  less than or equal to the old image size.
384      *
385      *  Because glyph.computeImageSize() will determine the size of fImage,
386      *  generateMetrics will be called before generateImage.
387      */
388     virtual void generateImage(const SkGlyph& glyph) = 0;
389 
390     /** Sets the passed path to the glyph outline.
391      *  If this cannot be done the path is set to empty;
392      *  @return false if this glyph does not have any path.
393      */
394     virtual bool SK_WARN_UNUSED_RESULT generatePath(SkGlyphID glyphId, SkPath* path) = 0;
395 
396     /** Retrieves font metrics. */
397     virtual void generateFontMetrics(SkFontMetrics*) = 0;
398 
399     /** Returns the number of glyphs in the font. */
400     virtual unsigned generateGlyphCount() = 0;
401 
402     /** Returns the glyph id for the given unichar.
403      *  If there is no 1:1 mapping from the unichar to a glyph id, returns 0.
404      */
405     virtual uint16_t generateCharToGlyph(SkUnichar unichar) = 0;
406 
forceGenerateImageFromPath()407     void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
forceOffGenerateImageFromPath()408     void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; }
409 
410 private:
411     friend class SkRandomScalerContext; // For debug purposes
412 
413     // never null
414     sk_sp<SkTypeface> fTypeface;
415 
416     // optional objects, which may be null
417     sk_sp<SkPathEffect> fPathEffect;
418     sk_sp<SkMaskFilter> fMaskFilter;
419 
420     // if this is set, we draw the image from a path, rather than
421     // calling generateImage.
422     bool fGenerateImageFromPath;
423 
424     /** Returns false if the glyph has no path at all. */
425     bool internalGetPath(SkPackedGlyphID id, SkPath* devPath);
426 
427     // SkMaskGamma::PreBlend converts linear masks to gamma correcting masks.
428 protected:
429     // Visible to subclasses so that generateImage can apply the pre-blend directly.
430     const SkMaskGamma::PreBlend fPreBlend;
431 private:
432     // When there is a filter, previous steps must create a linear mask
433     // and the pre-blend applied as a final step.
434     const SkMaskGamma::PreBlend fPreBlendForFilter;
435 };
436 
437 #define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
438 #define kEffects_SkDescriptorTag        SkSetFourByteTag('e', 'f', 'c', 't')
439 
440 ///////////////////////////////////////////////////////////////////////////////
441 
getHinting()442 SkFontHinting SkScalerContextRec::getHinting() const {
443     unsigned hint = (fFlags & SkScalerContext::kHinting_Mask) >>
444                                             SkScalerContext::kHinting_Shift;
445     return static_cast<SkFontHinting>(hint);
446 }
447 
setHinting(SkFontHinting hinting)448 void SkScalerContextRec::setHinting(SkFontHinting hinting) {
449     fFlags = (fFlags & ~SkScalerContext::kHinting_Mask) |
450                         (static_cast<unsigned>(hinting) << SkScalerContext::kHinting_Shift);
451 }
452 
453 
454 #endif
455