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