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/SkMacros.h" |
20 | #include "src/core/SkGlyph.h" |
21 | #include "src/core/SkMask.h" |
22 | #include "src/core/SkMaskGamma.h" |
23 | #include "src/core/SkStrikeForGPU.h" |
24 | #include "src/core/SkSurfacePriv.h" |
25 | #include "src/core/SkWriteBuffer.h" |
26 | |
27 | class SkAutoDescriptor; |
28 | class SkDescriptor; |
29 | class SkMaskFilter; |
30 | class SkPathEffect; |
31 | class SkScalerContext; |
32 | class SkScalerContext_DW; |
33 | |
34 | enum SkScalerContextFlags : uint32_t { |
35 | kNone = 0, |
36 | kFakeGamma = 1 << 0, |
37 | kBoostContrast = 1 << 1, |
38 | kFakeGammaAndBoostContrast = kFakeGamma | kBoostContrast, |
39 | }; |
40 | |
41 | enum SkAxisAlignment : uint32_t { |
42 | kNone_SkAxisAlignment, |
43 | kX_SkAxisAlignment, |
44 | kY_SkAxisAlignment |
45 | }; |
46 | |
47 | /* |
48 | * To allow this to be forward-declared, it must be its own typename, rather |
49 | * than a nested struct inside SkScalerContext (where it started). |
50 | * |
51 | * SkScalerContextRec must be dense, and all bytes must be set to a know quantity because this |
52 | * structure is used to calculate a checksum. |
53 | */ |
54 | SK_BEGIN_REQUIRE_DENSE |
55 | struct SkScalerContextRec { |
56 | uint32_t fFontID; |
57 | SkScalar fTextSize, fPreScaleX, fPreSkewX; |
58 | SkScalar fPost2x2[2][2]; |
59 | SkScalar fFrameWidth, fMiterLimit; |
60 | |
61 | private: |
62 | //These describe the parameters to create (uniquely identify) the pre-blend. |
63 | uint32_t fLumBits; |
64 | uint8_t fDeviceGamma; //2.6, (0.0, 4.0) gamma, 0.0 for sRGB |
65 | uint8_t fPaintGamma; //2.6, (0.0, 4.0) gamma, 0.0 for sRGB |
66 | uint8_t fContrast; //0.8+1, [0.0, 1.0] artificial contrast |
67 | const uint8_t fReservedAlign{0}; |
68 | |
69 | public: |
70 | |
71 | SkScalar getDeviceGamma() const { |
72 | return SkIntToScalar(fDeviceGamma) / (1 << 6); |
73 | } |
74 | void setDeviceGamma(SkScalar dg) { |
75 | SkASSERT(0 <= dg && dg < SkIntToScalar(4)); |
76 | fDeviceGamma = SkScalarFloorToInt(dg * (1 << 6)); |
77 | } |
78 | |
79 | SkScalar getPaintGamma() const { |
80 | return SkIntToScalar(fPaintGamma) / (1 << 6); |
81 | } |
82 | void setPaintGamma(SkScalar pg) { |
83 | SkASSERT(0 <= pg && pg < SkIntToScalar(4)); |
84 | fPaintGamma = SkScalarFloorToInt(pg * (1 << 6)); |
85 | } |
86 | |
87 | SkScalar getContrast() const { |
88 | sk_ignore_unused_variable(fReservedAlign); |
89 | return SkIntToScalar(fContrast) / ((1 << 8) - 1); |
90 | } |
91 | void setContrast(SkScalar c) { |
92 | SkASSERT(0 <= c && c <= SK_Scalar1); |
93 | fContrast = SkScalarRoundToInt(c * ((1 << 8) - 1)); |
94 | } |
95 | |
96 | /** |
97 | * Causes the luminance color to be ignored, and the paint and device |
98 | * gamma to be effectively 1.0 |
99 | */ |
100 | void ignoreGamma() { |
101 | setLuminanceColor(SK_ColorTRANSPARENT); |
102 | setPaintGamma(SK_Scalar1); |
103 | setDeviceGamma(SK_Scalar1); |
104 | } |
105 | |
106 | /** |
107 | * Causes the luminance color and contrast to be ignored, and the |
108 | * paint and device gamma to be effectively 1.0. |
109 | */ |
110 | void ignorePreBlend() { |
111 | ignoreGamma(); |
112 | setContrast(0); |
113 | } |
114 | |
115 | uint8_t fMaskFormat; |
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 | |
128 | SkString dump() const { |
129 | SkString msg; |
130 | msg.appendf("Rec\n" ); |
131 | msg.appendf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\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 | return msg; |
139 | } |
140 | |
141 | void getMatrixFrom2x2(SkMatrix*) const; |
142 | void getLocalMatrix(SkMatrix*) const; |
143 | void getSingleMatrix(SkMatrix*) const; |
144 | |
145 | /** The kind of scale which will be applied by the underlying port (pre-matrix). */ |
146 | enum PreMatrixScale { |
147 | kFull_PreMatrixScale, // The underlying port can apply both x and y scale. |
148 | kVertical_PreMatrixScale, // The underlying port can only apply a y scale. |
149 | kVerticalInteger_PreMatrixScale // The underlying port can only apply an integer y scale. |
150 | }; |
151 | /** |
152 | * Compute useful matrices for use with sizing in underlying libraries. |
153 | * |
154 | * There are two kinds of text size, a 'requested/logical size' which is like asking for size |
155 | * '12' and a 'real' size which is the size after the matrix is applied. The matrices produced |
156 | * by this method are based on the 'real' size. This method effectively finds the total device |
157 | * matrix and decomposes it in various ways. |
158 | * |
159 | * The most useful decomposition is into 'scale' and 'remaining'. The 'scale' is applied first |
160 | * and then the 'remaining' to fully apply the total matrix. This decomposition is useful when |
161 | * the text size ('scale') may have meaning apart from the total matrix. This is true when |
162 | * hinting, and sometimes true for other properties as well. |
163 | * |
164 | * The second (optional) decomposition is of 'remaining' into a non-rotational part |
165 | * 'remainingWithoutRotation' and a rotational part 'remainingRotation'. The 'scale' is applied |
166 | * first, then 'remainingWithoutRotation', then 'remainingRotation' to fully apply the total |
167 | * matrix. This decomposition is helpful when only horizontal metrics can be trusted, so the |
168 | * 'scale' and 'remainingWithoutRotation' will be handled by the underlying library, but |
169 | * the final rotation 'remainingRotation' will be handled manually. |
170 | * |
171 | * The 'total' matrix is also (optionally) available. This is useful in cases where the |
172 | * underlying library will not be used, often when working directly with font data. |
173 | * |
174 | * The parameters 'scale' and 'remaining' are required, the other pointers may be nullptr. |
175 | * |
176 | * @param preMatrixScale the kind of scale to extract from the total matrix. |
177 | * @param scale the scale extracted from the total matrix (both values positive). |
178 | * @param remaining apply after scale to apply the total matrix. |
179 | * @param remainingWithoutRotation apply after scale to apply the total matrix sans rotation. |
180 | * @param remainingRotation apply after remainingWithoutRotation to apply the total matrix. |
181 | * @param total the total matrix. |
182 | * @return false if the matrix was singular. The output will be valid but not invertible. |
183 | */ |
184 | bool computeMatrices(PreMatrixScale preMatrixScale, |
185 | SkVector* scale, SkMatrix* remaining, |
186 | SkMatrix* remainingWithoutRotation = nullptr, |
187 | SkMatrix* remainingRotation = nullptr, |
188 | SkMatrix* total = nullptr); |
189 | |
190 | SkAxisAlignment computeAxisAlignmentForHText() const; |
191 | |
192 | inline SkFontHinting getHinting() const; |
193 | inline void setHinting(SkFontHinting); |
194 | |
195 | SkMask::Format getFormat() const { |
196 | return static_cast<SkMask::Format>(fMaskFormat); |
197 | } |
198 | |
199 | SkColor getLuminanceColor() const { |
200 | return fLumBits; |
201 | } |
202 | |
203 | // setLuminanceColor forces the alpha to be 0xFF because the blitter that draws the glyph |
204 | // will apply the alpha from the paint. Don't apply the alpha twice. |
205 | void setLuminanceColor(SkColor c); |
206 | |
207 | private: |
208 | // TODO: remove |
209 | friend class SkScalerContext; |
210 | }; |
211 | SK_END_REQUIRE_DENSE |
212 | |
213 | // TODO: rename SkScalerContextEffects -> SkStrikeEffects |
214 | struct SkScalerContextEffects { |
215 | SkScalerContextEffects() : fPathEffect(nullptr), fMaskFilter(nullptr) {} |
216 | SkScalerContextEffects(SkPathEffect* pe, SkMaskFilter* mf) |
217 | : fPathEffect(pe), fMaskFilter(mf) {} |
218 | explicit SkScalerContextEffects(const SkPaint& paint) |
219 | : fPathEffect(paint.getPathEffect()) |
220 | , fMaskFilter(paint.getMaskFilter()) {} |
221 | |
222 | SkPathEffect* fPathEffect; |
223 | SkMaskFilter* fMaskFilter; |
224 | }; |
225 | |
226 | //The following typedef hides from the rest of the implementation the number of |
227 | //most significant bits to consider when creating mask gamma tables. Two bits |
228 | //per channel was chosen as a balance between fidelity (more bits) and cache |
229 | //sizes (fewer bits). Three bits per channel was chosen when #303942; (used by |
230 | //the Chrome UI) turned out too green. |
231 | typedef SkTMaskGamma<3, 3, 3> SkMaskGamma; |
232 | |
233 | class SkScalerContext { |
234 | public: |
235 | enum Flags { |
236 | kFrameAndFill_Flag = 0x0001, |
237 | kUnused = 0x0002, |
238 | kEmbeddedBitmapText_Flag = 0x0004, |
239 | kEmbolden_Flag = 0x0008, |
240 | kSubpixelPositioning_Flag = 0x0010, |
241 | kForceAutohinting_Flag = 0x0020, // Use auto instead of bytcode hinting if hinting. |
242 | |
243 | // together, these two flags resulting in a two bit value which matches |
244 | // up with the SkPaint::Hinting enum. |
245 | kHinting_Shift = 7, // to shift into the other flags above |
246 | kHintingBit1_Flag = 0x0080, |
247 | kHintingBit2_Flag = 0x0100, |
248 | |
249 | // Pixel geometry information. |
250 | // only meaningful if fMaskFormat is kLCD16 |
251 | kLCD_Vertical_Flag = 0x0200, // else Horizontal |
252 | kLCD_BGROrder_Flag = 0x0400, // else RGB order |
253 | |
254 | // Generate A8 from LCD source (for GDI and CoreGraphics). |
255 | // only meaningful if fMaskFormat is kA8 |
256 | kGenA8FromLCD_Flag = 0x0800, // could be 0x200 (bit meaning dependent on fMaskFormat) |
257 | kLinearMetrics_Flag = 0x1000, |
258 | kBaselineSnap_Flag = 0x2000, |
259 | }; |
260 | |
261 | // computed values |
262 | enum { |
263 | kHinting_Mask = kHintingBit1_Flag | kHintingBit2_Flag, |
264 | }; |
265 | |
266 | SkScalerContext(sk_sp<SkTypeface>, const SkScalerContextEffects&, const SkDescriptor*); |
267 | virtual ~SkScalerContext(); |
268 | |
269 | SkTypeface* getTypeface() const { return fTypeface.get(); } |
270 | |
271 | SkMask::Format getMaskFormat() const { |
272 | return (SkMask::Format)fRec.fMaskFormat; |
273 | } |
274 | |
275 | bool isSubpixel() const { |
276 | return SkToBool(fRec.fFlags & kSubpixelPositioning_Flag); |
277 | } |
278 | |
279 | bool isLinearMetrics() const { |
280 | return SkToBool(fRec.fFlags & kLinearMetrics_Flag); |
281 | } |
282 | |
283 | // DEPRECATED |
284 | bool isVertical() const { return false; } |
285 | |
286 | unsigned getGlyphCount() { return this->generateGlyphCount(); } |
287 | void getAdvance(SkGlyph*); |
288 | void getMetrics(SkGlyph*); |
289 | void getImage(const SkGlyph&); |
290 | bool SK_WARN_UNUSED_RESULT getPath(SkPackedGlyphID, SkPath*); |
291 | void getFontMetrics(SkFontMetrics*); |
292 | |
293 | /** Return the size in bytes of the associated gamma lookup table |
294 | */ |
295 | static size_t GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma, |
296 | int* width, int* height); |
297 | |
298 | /** Get the associated gamma lookup table. The 'data' pointer must point to pre-allocated |
299 | * memory, with size in bytes greater than or equal to the return value of getGammaLUTSize(). |
300 | * |
301 | * If the lookup table hasn't been initialized (e.g., it's linear), this will return false. |
302 | */ |
303 | static bool GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma, |
304 | uint8_t* data); |
305 | |
306 | static void MakeRecAndEffects(const SkFont& font, const SkPaint& paint, |
307 | const SkSurfaceProps& surfaceProps, |
308 | SkScalerContextFlags scalerContextFlags, |
309 | const SkMatrix& deviceMatrix, |
310 | SkScalerContextRec* rec, |
311 | SkScalerContextEffects* effects); |
312 | |
313 | // If we are creating rec and effects from a font only, then there is no device around either. |
314 | static void MakeRecAndEffectsFromFont(const SkFont& font, |
315 | SkScalerContextRec* rec, |
316 | SkScalerContextEffects* effects) { |
317 | SkPaint paint; |
318 | return MakeRecAndEffects( |
319 | font, paint, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), |
320 | SkScalerContextFlags::kNone, SkMatrix::I(), rec, effects); |
321 | } |
322 | |
323 | static SkDescriptor* MakeDescriptorForPaths(SkFontID fontID, |
324 | SkAutoDescriptor* ad); |
325 | |
326 | static SkScalerContext* MakeEmptyContext( |
327 | sk_sp<SkTypeface> typeface, const SkScalerContextEffects& effects, |
328 | const SkDescriptor* desc); |
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 | |
346 | const SkScalerContextRec& getRec() const { return fRec; } |
347 | |
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.imageSize(). |
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.imageSize() 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 | void forceGenerateImageFromPath() { fGenerateImageFromPath = true; } |
403 | void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; } |
404 | |
405 | private: |
406 | friend class RandomScalerContext; // For debug purposes |
407 | |
408 | static SkScalerContextRec PreprocessRec(const SkTypeface& typeface, |
409 | const SkScalerContextEffects& effects, |
410 | const SkDescriptor& desc); |
411 | |
412 | // never null |
413 | sk_sp<SkTypeface> fTypeface; |
414 | |
415 | // optional objects, which may be null |
416 | sk_sp<SkPathEffect> fPathEffect; |
417 | sk_sp<SkMaskFilter> fMaskFilter; |
418 | |
419 | // if this is set, we draw the image from a path, rather than |
420 | // calling generateImage. |
421 | bool fGenerateImageFromPath; |
422 | |
423 | /** Returns false if the glyph has no path at all. */ |
424 | bool internalGetPath(SkPackedGlyphID id, SkPath* devPath); |
425 | |
426 | // SkMaskGamma::PreBlend converts linear masks to gamma correcting masks. |
427 | protected: |
428 | // Visible to subclasses so that generateImage can apply the pre-blend directly. |
429 | const SkMaskGamma::PreBlend fPreBlend; |
430 | }; |
431 | |
432 | #define kRec_SkDescriptorTag SkSetFourByteTag('s', 'r', 'e', 'c') |
433 | #define kEffects_SkDescriptorTag SkSetFourByteTag('e', 'f', 'c', 't') |
434 | |
435 | /////////////////////////////////////////////////////////////////////////////// |
436 | |
437 | SkFontHinting SkScalerContextRec::getHinting() const { |
438 | unsigned hint = (fFlags & SkScalerContext::kHinting_Mask) >> |
439 | SkScalerContext::kHinting_Shift; |
440 | return static_cast<SkFontHinting>(hint); |
441 | } |
442 | |
443 | void SkScalerContextRec::setHinting(SkFontHinting hinting) { |
444 | fFlags = (fFlags & ~SkScalerContext::kHinting_Mask) | |
445 | (static_cast<unsigned>(hinting) << SkScalerContext::kHinting_Shift); |
446 | } |
447 | |
448 | |
449 | #endif |
450 | |