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