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 "SkStrikeInterface.h"
23 #include "SkSurfacePriv.h"
24 #include "SkTypeface.h"
25 #include "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
getDeviceGammaSkScalerContextRec71 SkScalar getDeviceGamma() const {
72 return SkIntToScalar(fDeviceGamma) / (1 << 6);
73 }
setDeviceGammaSkScalerContextRec74 void setDeviceGamma(SkScalar dg) {
75 SkASSERT(0 <= dg && dg < SkIntToScalar(4));
76 fDeviceGamma = SkScalarFloorToInt(dg * (1 << 6));
77 }
78
getPaintGammaSkScalerContextRec79 SkScalar getPaintGamma() const {
80 return SkIntToScalar(fPaintGamma) / (1 << 6);
81 }
setPaintGammaSkScalerContextRec82 void setPaintGamma(SkScalar pg) {
83 SkASSERT(0 <= pg && pg < SkIntToScalar(4));
84 fPaintGamma = SkScalarFloorToInt(pg * (1 << 6));
85 }
86
getContrastSkScalerContextRec87 SkScalar getContrast() const {
88 sk_ignore_unused_variable(fReservedAlign);
89 return SkIntToScalar(fContrast) / ((1 << 8) - 1);
90 }
setContrastSkScalerContextRec91 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 */
ignoreGammaSkScalerContextRec100 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 */
ignorePreBlendSkScalerContextRec110 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
dumpSkScalerContextRec128 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
getFormatSkScalerContextRec195 SkMask::Format getFormat() const {
196 return static_cast<SkMask::Format>(fMaskFormat);
197 }
198
getLuminanceColorSkScalerContextRec199 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.
setLuminanceColorSkScalerContextRec205 void setLuminanceColor(SkColor c) {
206 fLumBits = SkColorSetRGB(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));
207 }
208
209 private:
210 // TODO: remove
211 friend class SkScalerContext;
212 };
213 SK_END_REQUIRE_DENSE
214
215 //The following typedef hides from the rest of the implementation the number of
216 //most significant bits to consider when creating mask gamma tables. Two bits
217 //per channel was chosen as a balance between fidelity (more bits) and cache
218 //sizes (fewer bits). Three bits per channel was chosen when #303942; (used by
219 //the Chrome UI) turned out too green.
220 typedef SkTMaskGamma<3, 3, 3> SkMaskGamma;
221
222 class SkScalerContext {
223 public:
224 enum Flags {
225 kFrameAndFill_Flag = 0x0001,
226 kUnused = 0x0002,
227 kEmbeddedBitmapText_Flag = 0x0004,
228 kEmbolden_Flag = 0x0008,
229 kSubpixelPositioning_Flag = 0x0010,
230 kForceAutohinting_Flag = 0x0020, // Use auto instead of bytcode hinting if hinting.
231
232 // together, these two flags resulting in a two bit value which matches
233 // up with the SkPaint::Hinting enum.
234 kHinting_Shift = 7, // to shift into the other flags above
235 kHintingBit1_Flag = 0x0080,
236 kHintingBit2_Flag = 0x0100,
237
238 // Pixel geometry information.
239 // only meaningful if fMaskFormat is kLCD16
240 kLCD_Vertical_Flag = 0x0200, // else Horizontal
241 kLCD_BGROrder_Flag = 0x0400, // else RGB order
242
243 // Generate A8 from LCD source (for GDI and CoreGraphics).
244 // only meaningful if fMaskFormat is kA8
245 kGenA8FromLCD_Flag = 0x0800, // could be 0x200 (bit meaning dependent on fMaskFormat)
246 };
247
248 // computed values
249 enum {
250 kHinting_Mask = kHintingBit1_Flag | kHintingBit2_Flag,
251 };
252
253 SkScalerContext(sk_sp<SkTypeface>, const SkScalerContextEffects&, const SkDescriptor*);
254 virtual ~SkScalerContext();
255
getTypeface()256 SkTypeface* getTypeface() const { return fTypeface.get(); }
257
getMaskFormat()258 SkMask::Format getMaskFormat() const {
259 return (SkMask::Format)fRec.fMaskFormat;
260 }
261
isSubpixel()262 bool isSubpixel() const {
263 return SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
264 }
265
266 // DEPRECATED
isVertical()267 bool isVertical() const { return false; }
268
269 /** Return the corresponding glyph for the specified unichar. Since contexts
270 may be chained (under the hood), the glyphID that is returned may in
271 fact correspond to a different font/context. In that case, we use the
272 base-glyph-count to know how to translate back into local glyph space.
273 */
charToGlyphID(SkUnichar uni)274 uint16_t charToGlyphID(SkUnichar uni) {
275 return generateCharToGlyph(uni);
276 }
277
getGlyphCount()278 unsigned getGlyphCount() { return this->generateGlyphCount(); }
279 void getAdvance(SkGlyph*);
280 void getMetrics(SkGlyph*);
281 void getImage(const SkGlyph&);
282 bool SK_WARN_UNUSED_RESULT getPath(SkPackedGlyphID, SkPath*);
283 void getFontMetrics(SkFontMetrics*);
284
285 /** Return the size in bytes of the associated gamma lookup table
286 */
287 static size_t GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
288 int* width, int* height);
289
290 /** Get the associated gamma lookup table. The 'data' pointer must point to pre-allocated
291 * memory, with size in bytes greater than or equal to the return value of getGammaLUTSize().
292 *
293 * If the lookup table hasn't been initialized (e.g., it's linear), this will return false.
294 */
295 static bool GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
296 uint8_t* data);
297
298 static void MakeRecAndEffects(const SkFont& font, const SkPaint& paint,
299 const SkSurfaceProps& surfaceProps,
300 SkScalerContextFlags scalerContextFlags,
301 const SkMatrix& deviceMatrix,
302 SkScalerContextRec* rec,
303 SkScalerContextEffects* effects);
304
305 // 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)306 static void MakeRecAndEffectsFromFont(const SkFont& font,
307 SkScalerContextRec* rec,
308 SkScalerContextEffects* effects) {
309 SkPaint paint;
310 return MakeRecAndEffects(
311 font, paint, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType),
312 SkScalerContextFlags::kNone, SkMatrix::I(), rec, effects);
313 }
314
315 static SkDescriptor* MakeDescriptorForPaths(SkFontID fontID,
316 SkAutoDescriptor* ad);
317
318 static SkDescriptor* AutoDescriptorGivenRecAndEffects(
319 const SkScalerContextRec& rec,
320 const SkScalerContextEffects& effects,
321 SkAutoDescriptor* ad);
322
323 static std::unique_ptr<SkDescriptor> DescriptorGivenRecAndEffects(
324 const SkScalerContextRec& rec,
325 const SkScalerContextEffects& effects);
326
327 static void DescriptorBufferGiveRec(const SkScalerContextRec& rec, void* buffer);
328 static bool CheckBufferSizeForRec(const SkScalerContextRec& rec,
329 const SkScalerContextEffects& effects,
330 size_t size);
331
332 static SkMaskGamma::PreBlend GetMaskPreBlend(const SkScalerContextRec& rec);
333
getRec()334 const SkScalerContextRec& getRec() const { return fRec; }
335
getEffects()336 SkScalerContextEffects getEffects() const {
337 return { fPathEffect.get(), fMaskFilter.get() };
338 }
339
340 /**
341 * Return the axis (if any) that the baseline for horizontal text should land on.
342 * As an example, the identity matrix will return kX_SkAxisAlignment
343 */
344 SkAxisAlignment computeAxisAlignmentForHText() const;
345
346 static SkDescriptor* CreateDescriptorAndEffectsUsingPaint(
347 const SkFont&, const SkPaint&, const SkSurfaceProps&,
348 SkScalerContextFlags scalerContextFlags,
349 const SkMatrix& deviceMatrix, SkAutoDescriptor* ad,
350 SkScalerContextEffects* effects);
351
352 protected:
353 SkScalerContextRec fRec;
354
355 /** Generates the contents of glyph.fAdvanceX and glyph.fAdvanceY if it can do so quickly.
356 * Returns true if it could, false otherwise.
357 */
358 virtual bool generateAdvance(SkGlyph* glyph) = 0;
359
360 /** Generates the contents of glyph.fWidth, fHeight, fTop, fLeft,
361 * as well as fAdvanceX and fAdvanceY if not already set.
362 *
363 * TODO: fMaskFormat is set by getMetrics later; cannot be set here.
364 */
365 virtual void generateMetrics(SkGlyph* glyph) = 0;
366
367 /** Generates the contents of glyph.fImage.
368 * When called, glyph.fImage will be pointing to a pre-allocated,
369 * uninitialized region of memory of size glyph.computeImageSize().
370 * This method may change glyph.fMaskFormat if the new image size is
371 * less than or equal to the old image size.
372 *
373 * Because glyph.computeImageSize() will determine the size of fImage,
374 * generateMetrics will be called before generateImage.
375 */
376 virtual void generateImage(const SkGlyph& glyph) = 0;
377
378 /** Sets the passed path to the glyph outline.
379 * If this cannot be done the path is set to empty;
380 * @return false if this glyph does not have any path.
381 */
382 virtual bool SK_WARN_UNUSED_RESULT generatePath(SkGlyphID glyphId, SkPath* path) = 0;
383
384 /** Retrieves font metrics. */
385 virtual void generateFontMetrics(SkFontMetrics*) = 0;
386
387 /** Returns the number of glyphs in the font. */
388 virtual unsigned generateGlyphCount() = 0;
389
390 /** Returns the glyph id for the given unichar.
391 * If there is no 1:1 mapping from the unichar to a glyph id, returns 0.
392 */
393 virtual uint16_t generateCharToGlyph(SkUnichar unichar) = 0;
394
forceGenerateImageFromPath()395 void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
forceOffGenerateImageFromPath()396 void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; }
397
398 private:
399 friend class SkRandomScalerContext; // For debug purposes
400
401 static SkScalerContextRec PreprocessRec(const SkTypeface& typeface,
402 const SkScalerContextEffects& effects,
403 const SkDescriptor& desc);
404
405 // never null
406 sk_sp<SkTypeface> fTypeface;
407
408 // optional objects, which may be null
409 sk_sp<SkPathEffect> fPathEffect;
410 sk_sp<SkMaskFilter> fMaskFilter;
411
412 // if this is set, we draw the image from a path, rather than
413 // calling generateImage.
414 bool fGenerateImageFromPath;
415
416 /** Returns false if the glyph has no path at all. */
417 bool internalGetPath(SkPackedGlyphID id, SkPath* devPath);
418
419 // SkMaskGamma::PreBlend converts linear masks to gamma correcting masks.
420 protected:
421 // Visible to subclasses so that generateImage can apply the pre-blend directly.
422 const SkMaskGamma::PreBlend fPreBlend;
423 };
424
425 #define kRec_SkDescriptorTag SkSetFourByteTag('s', 'r', 'e', 'c')
426 #define kEffects_SkDescriptorTag SkSetFourByteTag('e', 'f', 'c', 't')
427
428 ///////////////////////////////////////////////////////////////////////////////
429
getHinting()430 SkFontHinting SkScalerContextRec::getHinting() const {
431 unsigned hint = (fFlags & SkScalerContext::kHinting_Mask) >>
432 SkScalerContext::kHinting_Shift;
433 return static_cast<SkFontHinting>(hint);
434 }
435
setHinting(SkFontHinting hinting)436 void SkScalerContextRec::setHinting(SkFontHinting hinting) {
437 fFlags = (fFlags & ~SkScalerContext::kHinting_Mask) |
438 (static_cast<unsigned>(hinting) << SkScalerContext::kHinting_Shift);
439 }
440
441
442 #endif
443