reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2006 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef SkScalerContext_DEFINED |
| 18 | #define SkScalerContext_DEFINED |
| 19 | |
| 20 | #include "SkMask.h" |
| 21 | #include "SkMatrix.h" |
| 22 | #include "SkPaint.h" |
| 23 | #include "SkPath.h" |
| 24 | #include "SkPoint.h" |
| 25 | |
| 26 | class SkDescriptor; |
| 27 | class SkMaskFilter; |
| 28 | class SkPathEffect; |
| 29 | class SkRasterizer; |
| 30 | |
| 31 | // needs to be != to any valid SkMask::Format |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 32 | #define MASK_FORMAT_UNKNOWN (0xFF) |
| 33 | #define MASK_FORMAT_JUST_ADVANCE MASK_FORMAT_UNKNOWN |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 34 | |
scarybeasts@gmail.com | 17f694b | 2010-10-18 23:29:36 +0000 | [diff] [blame] | 35 | #define kMaxGlyphWidth (1<<13) |
| 36 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 37 | struct SkGlyph { |
| 38 | void* fImage; |
| 39 | SkPath* fPath; |
| 40 | SkFixed fAdvanceX, fAdvanceY; |
| 41 | |
| 42 | uint32_t fID; |
| 43 | uint16_t fWidth, fHeight; |
| 44 | int16_t fTop, fLeft; |
| 45 | |
| 46 | uint8_t fMaskFormat; |
| 47 | int8_t fRsbDelta, fLsbDelta; // used by auto-kerning |
reed@android.com | f2b98d6 | 2010-12-20 18:26:13 +0000 | [diff] [blame] | 48 | |
| 49 | void init(uint32_t id) { |
| 50 | fID = id; |
| 51 | fImage = NULL; |
| 52 | fPath = NULL; |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 53 | fMaskFormat = MASK_FORMAT_UNKNOWN; |
reed@android.com | f2b98d6 | 2010-12-20 18:26:13 +0000 | [diff] [blame] | 54 | } |
| 55 | |
reed@google.com | f88d676 | 2011-03-10 15:06:27 +0000 | [diff] [blame] | 56 | /** |
| 57 | * Compute the rowbytes for the specified width and mask-format. |
| 58 | */ |
| 59 | static unsigned ComputeRowBytes(unsigned width, SkMask::Format format) { |
| 60 | unsigned rb = width; |
| 61 | if (SkMask::kBW_Format == format) { |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 62 | rb = (rb + 7) >> 3; |
reed@google.com | f88d676 | 2011-03-10 15:06:27 +0000 | [diff] [blame] | 63 | } else if (SkMask::kARGB32_Format == format) { |
reed@android.com | f2b98d6 | 2010-12-20 18:26:13 +0000 | [diff] [blame] | 64 | rb <<= 2; |
reed@google.com | f88d676 | 2011-03-10 15:06:27 +0000 | [diff] [blame] | 65 | } else if (SkMask::kLCD16_Format == format) { |
| 66 | rb = SkAlign4(rb << 1); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 67 | } else { |
| 68 | rb = SkAlign4(rb); |
| 69 | } |
| 70 | return rb; |
| 71 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 72 | |
reed@google.com | f88d676 | 2011-03-10 15:06:27 +0000 | [diff] [blame] | 73 | unsigned rowBytes() const { |
| 74 | return ComputeRowBytes(fWidth, (SkMask::Format)fMaskFormat); |
| 75 | } |
| 76 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 77 | bool isJustAdvance() const { |
| 78 | return MASK_FORMAT_JUST_ADVANCE == fMaskFormat; |
| 79 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 80 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 81 | bool isFullMetrics() const { |
| 82 | return MASK_FORMAT_JUST_ADVANCE != fMaskFormat; |
| 83 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 84 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 85 | uint16_t getGlyphID() const { |
| 86 | return ID2Code(fID); |
| 87 | } |
| 88 | |
| 89 | unsigned getGlyphID(unsigned baseGlyphCount) const { |
| 90 | unsigned code = ID2Code(fID); |
| 91 | SkASSERT(code >= baseGlyphCount); |
| 92 | return code - baseGlyphCount; |
| 93 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 94 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 95 | unsigned getSubX() const { |
| 96 | return ID2SubX(fID); |
| 97 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 98 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 99 | SkFixed getSubXFixed() const { |
| 100 | return SubToFixed(ID2SubX(fID)); |
| 101 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 102 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 103 | SkFixed getSubYFixed() const { |
| 104 | return SubToFixed(ID2SubY(fID)); |
| 105 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 106 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 107 | size_t computeImageSize() const; |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 108 | |
reed@android.com | 62900b4 | 2009-02-11 15:07:19 +0000 | [diff] [blame] | 109 | /** Call this to set all of the metrics fields to 0 (e.g. if the scaler |
| 110 | encounters an error measuring a glyph). Note: this does not alter the |
| 111 | fImage, fPath, fID, fMaskFormat fields. |
| 112 | */ |
| 113 | void zeroMetrics(); |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 114 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 115 | enum { |
| 116 | kSubBits = 2, |
| 117 | kSubMask = ((1 << kSubBits) - 1), |
| 118 | kSubShift = 24, // must be large enough for glyphs and unichars |
| 119 | kCodeMask = ((1 << kSubShift) - 1), |
| 120 | // relative offsets for X and Y subpixel bits |
| 121 | kSubShiftX = kSubBits, |
| 122 | kSubShiftY = 0 |
| 123 | }; |
| 124 | |
| 125 | static unsigned ID2Code(uint32_t id) { |
| 126 | return id & kCodeMask; |
| 127 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 128 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 129 | static unsigned ID2SubX(uint32_t id) { |
| 130 | return id >> (kSubShift + kSubShiftX); |
| 131 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 132 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 133 | static unsigned ID2SubY(uint32_t id) { |
| 134 | return (id >> (kSubShift + kSubShiftY)) & kSubMask; |
| 135 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 136 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 137 | static unsigned FixedToSub(SkFixed n) { |
| 138 | return (n >> (16 - kSubBits)) & kSubMask; |
| 139 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 140 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 141 | static SkFixed SubToFixed(unsigned sub) { |
| 142 | SkASSERT(sub <= kSubMask); |
| 143 | return sub << (16 - kSubBits); |
| 144 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 145 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 146 | static uint32_t MakeID(unsigned code) { |
| 147 | return code; |
| 148 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 149 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 150 | static uint32_t MakeID(unsigned code, SkFixed x, SkFixed y) { |
| 151 | SkASSERT(code <= kCodeMask); |
| 152 | x = FixedToSub(x); |
| 153 | y = FixedToSub(y); |
| 154 | return (x << (kSubShift + kSubShiftX)) | |
| 155 | (y << (kSubShift + kSubShiftY)) | |
| 156 | code; |
| 157 | } |
reed@google.com | d3b13bd | 2011-01-13 16:33:36 +0000 | [diff] [blame] | 158 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 159 | void toMask(SkMask* mask) const; |
agl@chromium.org | 309485b | 2009-07-21 17:41:32 +0000 | [diff] [blame] | 160 | |
| 161 | /** Given a glyph which is has a mask format of LCD or VerticalLCD, take |
| 162 | the A8 plane in fImage and produce a valid LCD plane from it. |
| 163 | */ |
| 164 | void expandA8ToLCD() const; |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 165 | }; |
| 166 | |
| 167 | class SkScalerContext { |
| 168 | public: |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 169 | enum Flags { |
| 170 | kFrameAndFill_Flag = 0x01, |
| 171 | kDevKernText_Flag = 0x02, |
| 172 | kGammaForBlack_Flag = 0x04, // illegal to set both Gamma flags |
agl@chromium.org | 309485b | 2009-07-21 17:41:32 +0000 | [diff] [blame] | 173 | kGammaForWhite_Flag = 0x08, // illegal to set both Gamma flags |
| 174 | // together, these two flags resulting in a two bit value which matches |
| 175 | // up with the SkPaint::Hinting enum. |
reed@android.com | 36a4c2a | 2009-07-22 19:52:11 +0000 | [diff] [blame] | 176 | kHintingBit1_Flag = 0x10, |
| 177 | kHintingBit2_Flag = 0x20, |
agl@chromium.org | 13c8558 | 2010-01-04 23:56:43 +0000 | [diff] [blame] | 178 | kEmbeddedBitmapText_Flag = 0x40, |
senorblanco@chromium.org | 4526a84 | 2010-02-05 23:08:20 +0000 | [diff] [blame] | 179 | kEmbolden_Flag = 0x80, |
agl@chromium.org | a2c71cb | 2010-06-17 20:49:17 +0000 | [diff] [blame] | 180 | kSubpixelPositioning_Flag = 0x100, |
| 181 | kAutohinting_Flag = 0x200, |
reed@google.com | 02b5331 | 2011-05-18 19:00:53 +0000 | [diff] [blame^] | 182 | // these should only ever be set if fMaskFormat is LCD |
| 183 | kLCD_Vertical_Flag = 0x400, // else Horizontal |
| 184 | kLCD_BGROrder_Flag = 0x800, // else RGB order |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 185 | }; |
reed@android.com | e2ca207 | 2009-07-27 16:39:38 +0000 | [diff] [blame] | 186 | private: |
| 187 | enum { |
| 188 | kHintingMask = kHintingBit1_Flag | kHintingBit2_Flag |
| 189 | }; |
| 190 | public: |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 191 | struct Rec { |
| 192 | uint32_t fFontID; |
| 193 | SkScalar fTextSize, fPreScaleX, fPreSkewX; |
| 194 | SkScalar fPost2x2[2][2]; |
| 195 | SkScalar fFrameWidth, fMiterLimit; |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 196 | uint8_t fMaskFormat; |
| 197 | uint8_t fStrokeJoin; |
agl@chromium.org | a2c71cb | 2010-06-17 20:49:17 +0000 | [diff] [blame] | 198 | uint16_t fFlags; |
agl@chromium.org | 13c8558 | 2010-01-04 23:56:43 +0000 | [diff] [blame] | 199 | // Warning: when adding members note that the size of this structure |
| 200 | // must be a multiple of 4. SkDescriptor requires that its arguments be |
| 201 | // multiples of four and this structure is put in an SkDescriptor in |
| 202 | // SkPaint::MakeRec. |
agl@chromium.org | 309485b | 2009-07-21 17:41:32 +0000 | [diff] [blame] | 203 | |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 204 | void getMatrixFrom2x2(SkMatrix*) const; |
| 205 | void getLocalMatrix(SkMatrix*) const; |
| 206 | void getSingleMatrix(SkMatrix*) const; |
agl@chromium.org | 309485b | 2009-07-21 17:41:32 +0000 | [diff] [blame] | 207 | |
| 208 | SkPaint::Hinting getHinting() const { |
reed@android.com | e2ca207 | 2009-07-27 16:39:38 +0000 | [diff] [blame] | 209 | return static_cast<SkPaint::Hinting>((fFlags & kHintingMask) >> 4); |
agl@chromium.org | 309485b | 2009-07-21 17:41:32 +0000 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | void setHinting(SkPaint::Hinting hinting) { |
reed@android.com | e2ca207 | 2009-07-27 16:39:38 +0000 | [diff] [blame] | 213 | fFlags = (fFlags & ~kHintingMask) | (hinting << 4); |
agl@chromium.org | 309485b | 2009-07-21 17:41:32 +0000 | [diff] [blame] | 214 | } |
reed@android.com | 36a4c2a | 2009-07-22 19:52:11 +0000 | [diff] [blame] | 215 | |
| 216 | SkMask::Format getFormat() const { |
| 217 | return static_cast<SkMask::Format>(fMaskFormat); |
| 218 | } |
| 219 | |
| 220 | bool isLCD() const { |
| 221 | return SkMask::FormatIsLCD(this->getFormat()); |
| 222 | } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 223 | }; |
| 224 | |
| 225 | SkScalerContext(const SkDescriptor* desc); |
| 226 | virtual ~SkScalerContext(); |
| 227 | |
reed@google.com | 98539c6 | 2011-03-15 15:40:16 +0000 | [diff] [blame] | 228 | SkMask::Format getMaskFormat() const { |
| 229 | return (SkMask::Format)fRec.fMaskFormat; |
| 230 | } |
| 231 | |
reed@android.com | a14ea0e | 2009-03-17 17:59:53 +0000 | [diff] [blame] | 232 | // remember our glyph offset/base |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 233 | void setBaseGlyphCount(unsigned baseGlyphCount) { |
| 234 | fBaseGlyphCount = baseGlyphCount; |
| 235 | } |
| 236 | |
reed@android.com | a14ea0e | 2009-03-17 17:59:53 +0000 | [diff] [blame] | 237 | /** Return the corresponding glyph for the specified unichar. Since contexts |
| 238 | may be chained (under the hood), the glyphID that is returned may in |
| 239 | fact correspond to a different font/context. In that case, we use the |
| 240 | base-glyph-count to know how to translate back into local glyph space. |
| 241 | */ |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 242 | uint16_t charToGlyphID(SkUnichar uni); |
| 243 | |
reed@android.com | 9d3a985 | 2010-01-08 14:07:42 +0000 | [diff] [blame] | 244 | /** Map the glyphID to its glyph index, and then to its char code. Unmapped |
| 245 | glyphs return zero. |
| 246 | */ |
| 247 | SkUnichar glyphIDToChar(uint16_t glyphID); |
| 248 | |
ctguil@chromium.org | 0bc7bf5 | 2011-03-04 19:04:57 +0000 | [diff] [blame] | 249 | unsigned getGlyphCount() { return this->generateGlyphCount(); } |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 250 | void getAdvance(SkGlyph*); |
| 251 | void getMetrics(SkGlyph*); |
| 252 | void getImage(const SkGlyph&); |
| 253 | void getPath(const SkGlyph&, SkPath*); |
| 254 | void getFontMetrics(SkPaint::FontMetrics* mX, |
| 255 | SkPaint::FontMetrics* mY); |
| 256 | |
| 257 | static inline void MakeRec(const SkPaint&, const SkMatrix*, Rec* rec); |
| 258 | static SkScalerContext* Create(const SkDescriptor*); |
| 259 | |
| 260 | protected: |
| 261 | Rec fRec; |
| 262 | unsigned fBaseGlyphCount; |
| 263 | |
ctguil@chromium.org | 0bc7bf5 | 2011-03-04 19:04:57 +0000 | [diff] [blame] | 264 | virtual unsigned generateGlyphCount() = 0; |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 265 | virtual uint16_t generateCharToGlyph(SkUnichar) = 0; |
| 266 | virtual void generateAdvance(SkGlyph*) = 0; |
| 267 | virtual void generateMetrics(SkGlyph*) = 0; |
| 268 | virtual void generateImage(const SkGlyph&) = 0; |
| 269 | virtual void generatePath(const SkGlyph&, SkPath*) = 0; |
| 270 | virtual void generateFontMetrics(SkPaint::FontMetrics* mX, |
| 271 | SkPaint::FontMetrics* mY) = 0; |
reed@android.com | 9d3a985 | 2010-01-08 14:07:42 +0000 | [diff] [blame] | 272 | // default impl returns 0, indicating failure. |
| 273 | virtual SkUnichar generateGlyphToChar(uint16_t); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 274 | |
| 275 | private: |
| 276 | SkPathEffect* fPathEffect; |
| 277 | SkMaskFilter* fMaskFilter; |
| 278 | SkRasterizer* fRasterizer; |
| 279 | SkScalar fDevFrameWidth; |
| 280 | |
| 281 | void internalGetPath(const SkGlyph& glyph, SkPath* fillPath, |
| 282 | SkPath* devPath, SkMatrix* fillToDevMatrix); |
| 283 | |
reed@android.com | a14ea0e | 2009-03-17 17:59:53 +0000 | [diff] [blame] | 284 | // return the next context, treating fNextContext as a cache of the answer |
| 285 | SkScalerContext* getNextContext(); |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 286 | |
reed@android.com | a14ea0e | 2009-03-17 17:59:53 +0000 | [diff] [blame] | 287 | // returns the right context from our link-list for this glyph. If no match |
| 288 | // is found, just returns the original context (this) |
| 289 | SkScalerContext* getGlyphContext(const SkGlyph& glyph); |
| 290 | |
| 291 | // link-list of context, to handle missing chars. null-terminated. |
| 292 | SkScalerContext* fNextContext; |
reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 293 | }; |
| 294 | |
| 295 | #define kRec_SkDescriptorTag SkSetFourByteTag('s', 'r', 'e', 'c') |
| 296 | #define kPathEffect_SkDescriptorTag SkSetFourByteTag('p', 't', 'h', 'e') |
| 297 | #define kMaskFilter_SkDescriptorTag SkSetFourByteTag('m', 's', 'k', 'f') |
| 298 | #define kRasterizer_SkDescriptorTag SkSetFourByteTag('r', 'a', 's', 't') |
| 299 | |
| 300 | #endif |
| 301 | |