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