Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright 2014 Google Inc. | |
| 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 #include "GrStencilAndCoverTextContext.h" | |
| 9 #include "GrDrawTarget.h" | |
| 10 #include "GrFontScaler.h" | |
| 11 #include "GrGpu.h" | |
| 12 #include "GrPath.h" | |
| 13 #include "GrTextStrike.h" | |
| 14 #include "GrTextStrike_impl.h" | |
| 15 #include "SkAutoKern.h" | |
| 16 #include "SkDraw.h" | |
| 17 #include "SkDrawProcs.h" | |
| 18 #include "SkGlyphCache.h" | |
| 19 #include "SkGpuDevice.h" | |
| 20 #include "SkPath.h" | |
| 21 #include "SkTextMapState.h" | |
| 22 | |
| 23 static const int kMaxReservedGlyphs = 64; | |
| 24 | |
| 25 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( | |
| 26 GrContext* context, const SkDeviceProperties& properties) | |
| 27 : GrTextContext(context, properties) | |
| 28 , fStroke(SkStrokeRec::kFill_InitStyle) { | |
| 29 } | |
| 30 | |
| 31 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { | |
| 32 } | |
| 33 | |
| 34 void GrStencilAndCoverTextContext::drawText(const GrPaint& paint, | |
| 35 const SkPaint& skPaint, | |
| 36 const char text[], | |
| 37 size_t byteLength, | |
| 38 SkScalar x, SkScalar y) { | |
| 39 SkASSERT(byteLength == 0 || text != NULL); | |
| 40 | |
| 41 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { | |
| 42 return; | |
| 43 } | |
| 44 | |
| 45 this->init(paint, skPaint, byteLength); | |
| 46 | |
| 47 | |
| 48 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
| 49 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | |
| 50 SkGlyphCache* cache = autoCache.getCache(); | |
| 51 GrFontScaler* scaler = GetGrFontScaler(cache); | |
| 52 GrTextStrike* strike = | |
| 53 fContext->getFontCache()->getStrike(scaler, true); | |
| 54 | |
| 55 const char* stop = text + byteLength; | |
| 56 | |
| 57 // Measure first if needed. | |
| 58 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { | |
| 59 SkFixed stopX = 0; | |
| 60 SkFixed stopY = 0; | |
| 61 | |
| 62 const char* textPtr = text; | |
| 63 while (textPtr < stop) { | |
| 64 // We don't need x, y here, since all subpixel variants will have th e | |
| 65 // same advance. | |
| 66 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); | |
| 67 | |
| 68 stopX += glyph.fAdvanceX; | |
| 69 stopY += glyph.fAdvanceY; | |
| 70 } | |
| 71 SkASSERT(textPtr == stop); | |
| 72 | |
| 73 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; | |
| 74 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; | |
| 75 | |
| 76 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { | |
| 77 alignX = SkScalarHalf(alignX); | |
| 78 alignY = SkScalarHalf(alignY); | |
| 79 } | |
| 80 | |
| 81 x -= alignX; | |
| 82 y -= alignY; | |
| 83 } | |
| 84 | |
| 85 SkAutoKern autokern; | |
| 86 | |
| 87 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); | |
| 88 SkFixed halfSampleX, halfSampleY; | |
| 89 if (cache->isSubpixel()) { | |
| 90 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits); | |
| 91 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getM atrix()); | |
| 92 if (kX_SkAxisAlignment == baseline) { | |
| 93 halfSampleY = SK_FixedHalf; | |
| 94 } else if (kY_SkAxisAlignment == baseline) { | |
| 95 halfSampleX = SK_FixedHalf; | |
| 96 } | |
| 97 } else { | |
| 98 halfSampleX = halfSampleY = SK_FixedHalf; | |
| 99 } | |
| 100 | |
| 101 SkFixed fx = SkScalarToFixed(x) + halfSampleX; | |
|
jvanverth1
2014/06/09 13:48:07
Doesn't this lead to glyphs being off by half a pi
bungeman-skia
2014/06/09 15:00:07
Yeah, this was done in SkDraw because of SkDraw1Gl
Kimmo Kinnunen
2014/06/11 12:33:24
Done.
| |
| 102 SkFixed fy = SkScalarToFixed(y) + halfSampleY; | |
| 103 while (text < stop) { | |
| 104 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 105 fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); | |
| 106 if (glyph.fWidth) { | |
| 107 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
| 108 glyph.getSubXFixed(), | |
| 109 glyph.getSubYFixed()), | |
| 110 SkPoint::Make( | |
| 111 SkFixedToScalar(fx), | |
| 112 SkFixedToScalar(fy)), | |
| 113 strike, | |
| 114 scaler); | |
| 115 } | |
| 116 | |
| 117 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); | |
| 118 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio); | |
| 119 } | |
| 120 | |
| 121 this->finish(); | |
| 122 } | |
| 123 | |
| 124 void GrStencilAndCoverTextContext::drawPosText(const GrPaint& paint, const SkPai nt& skPaint, | |
| 125 const char text[], | |
| 126 size_t byteLength, | |
| 127 const SkScalar pos[], | |
| 128 SkScalar constY, | |
| 129 int scalarsPerPosition) { | |
| 130 SkASSERT(byteLength == 0 || text != NULL); | |
| 131 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
| 132 | |
| 133 // nothing to draw | |
| 134 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) { | |
| 135 return; | |
| 136 } | |
| 137 | |
| 138 this->init(paint, skPaint, byteLength); | |
| 139 | |
| 140 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); | |
| 141 | |
| 142 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); | |
| 143 SkGlyphCache* cache = autoCache.getCache(); | |
| 144 GrFontScaler* scaler = GetGrFontScaler(cache); | |
| 145 GrTextStrike* strike = | |
| 146 fContext->getFontCache()->getStrike(scaler, true); | |
| 147 | |
| 148 SkMatrix ctm = fContext->getMatrix(); | |
| 149 | |
| 150 const char* stop = text + byteLength; | |
| 151 SkTextAlignProcScalar alignProc(fSkPaint.getTextAlign()); | |
| 152 SkTextMapState tms(SkMatrix::I(), constY); | |
| 153 SkTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition); | |
| 154 | |
| 155 SkTDArray<const GrPath*> paths; | |
| 156 SkTDArray<SkMatrix> transforms; | |
| 157 SkScalar halfSampleX = 0, halfSampleY = 0; | |
| 158 | |
| 159 if (cache->isSubpixel()) { | |
| 160 // maybe we should skip the rounding if linearText is set | |
| 161 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm); | |
| 162 | |
| 163 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX | |
| 164 if (kX_SkAxisAlignment == baseline) { | |
| 165 halfSampleY = SK_ScalarHalf; | |
| 166 } else if (kY_SkAxisAlignment == baseline) { | |
| 167 halfSampleX = SK_ScalarHalf; | |
| 168 } | |
| 169 #endif | |
| 170 | |
| 171 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
| 172 while (text < stop) { | |
| 173 tmsProc(tms, pos); | |
| 174 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 175 if (glyph.fWidth) { | |
| 176 SkScalar x = tms.fLoc.fX + halfSampleX; | |
|
jvanverth1
2014/06/09 13:48:07
Same issue with half-pixel alignment as above? (a
Kimmo Kinnunen
2014/06/11 12:33:24
Done.
| |
| 177 SkScalar y = tms.fLoc.fY + halfSampleY; | |
| 178 | |
| 179 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
| 180 glyph.getSubXFixed(), | |
| 181 glyph.getSubYFixed()), | |
| 182 SkPoint::Make(x, y), | |
| 183 strike, | |
| 184 scaler); | |
| 185 } | |
| 186 pos += scalarsPerPosition; | |
| 187 } | |
| 188 } else { | |
| 189 while (text < stop) { | |
| 190 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 191 | |
| 192 if (glyph.fWidth) { | |
| 193 tmsProc(tms, pos); | |
| 194 SkPoint loc; | |
| 195 alignProc(tms.fLoc, glyph, &loc); | |
| 196 | |
| 197 loc.fX += halfSampleX; | |
| 198 loc.fY += halfSampleY; | |
| 199 | |
| 200 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
| 201 glyph.getSubXFixed(), | |
| 202 glyph.getSubYFixed()), | |
| 203 loc, | |
| 204 strike, | |
| 205 scaler); | |
| 206 | |
| 207 } | |
| 208 pos += scalarsPerPosition; | |
| 209 } | |
| 210 } | |
| 211 } else { // Codepath for "not subpixel" case. | |
| 212 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { | |
|
jvanverth1
2014/06/09 13:48:07
Do we need to support non-subpixel layout when dra
Kimmo Kinnunen
2014/06/11 12:33:24
Done.
Kimmo Kinnunen
2014/06/11 12:39:21
Actually, I mean to say that I couldn't get the gl
| |
| 213 while (text < stop) { | |
| 214 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 215 | |
| 216 if (glyph.fWidth) { | |
| 217 tmsProc(tms, pos); | |
| 218 | |
| 219 SkScalar x = tms.fLoc.fX + SK_ScalarHalf; //halfSampleX; | |
| 220 SkScalar y = tms.fLoc.fY + SK_ScalarHalf; //halfSampleY; | |
| 221 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
| 222 glyph.getSubXFixed(), | |
| 223 glyph.getSubYFixed()), | |
| 224 SkPoint::Make(x, y), | |
| 225 strike, | |
| 226 scaler); | |
| 227 } | |
| 228 pos += scalarsPerPosition; | |
| 229 } | |
| 230 } else { | |
| 231 while (text < stop) { | |
| 232 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); | |
| 233 | |
| 234 if (glyph.fWidth) { | |
| 235 tmsProc(tms, pos); | |
| 236 | |
| 237 SkPoint loc; | |
| 238 alignProc(tms.fLoc, glyph, &loc); | |
| 239 | |
| 240 loc.fX += SK_ScalarHalf; //halfSampleX; | |
| 241 loc.fY += SK_ScalarHalf; //halfSampleY; | |
| 242 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), | |
| 243 glyph.getSubXFixed(), | |
| 244 glyph.getSubYFixed()), | |
| 245 loc, | |
| 246 strike, | |
| 247 scaler); | |
| 248 } | |
| 249 pos += scalarsPerPosition; | |
| 250 } | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 this->finish(); | |
| 255 } | |
| 256 | |
| 257 bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { | |
| 258 if (paint.getRasterizer()) { | |
| 259 return false; | |
| 260 } | |
| 261 if (paint.getMaskFilter()) { | |
| 262 return false; | |
| 263 } | |
| 264 if (paint.getPathEffect()) { | |
| 265 return false; | |
| 266 } | |
| 267 | |
| 268 // No hairlines unless we can map the 1 px width to the object space. | |
| 269 if (paint.getStyle() != SkPaint::kFill_Style | |
| 270 && paint.getStrokeWidth() == 0 | |
| 271 && (fContext->getMatrix().hasPerspective() | |
| 272 || !fContext->getMatrix().invert(NULL))) { | |
| 273 return false; | |
| 274 } | |
| 275 | |
| 276 // No color bitmap fonts. | |
| 277 SkScalerContext::Rec rec; | |
| 278 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); | |
| 279 return rec.getFormat() != SkMask::kARGB32_Format; | |
| 280 } | |
| 281 | |
| 282 void GrStencilAndCoverTextContext::init(const GrPaint& paint, | |
| 283 const SkPaint& skPaint, | |
| 284 size_t textByteLength) { | |
| 285 GrTextContext::init(paint, skPaint); | |
| 286 | |
| 287 if (SkDraw::ShouldDrawTextAsPaths(skPaint, fContext->getMatrix())) { | |
| 288 // This is to reproduce SkDraw::drawText_asPaths glyph positions. | |
| 289 fSkPaint.setLinearText(true); | |
| 290 fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPath s; | |
| 291 fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)) ; | |
| 292 } else { | |
| 293 fTextRatio = 1.0f; | |
| 294 } | |
| 295 // Note: if Chrome ever draws text in very different text sizes in real | |
| 296 // time, then glyphs need to be looked up untransformed and scaled, but | |
| 297 // still based on the requested text size. This will let the path cache | |
| 298 // work. This will probably not be needed until 1) text size is animating, | |
| 299 // 2) layout change animations are fast enough 3) pictures are rasterized | |
| 300 // directly to framebuffer. | |
| 301 | |
| 302 if (fSkPaint.getStyle() != SkPaint::kFill_Style) { | |
| 303 SkScalar strokeWidth = fSkPaint.getStrokeWidth(); | |
| 304 if (0 == strokeWidth) { | |
| 305 SkMatrix inv; | |
| 306 if (fContext->getMatrix().invert(&inv)) { | |
|
jvanverth1
2014/06/09 13:48:07
This won't work for non-uniform scale. And if you'
Kimmo Kinnunen
2014/06/11 12:33:24
Right..
| |
| 307 strokeWidth = SK_Scalar1 * inv.getScaleX(); | |
| 308 } else { | |
| 309 // Avoid unused return value warning. | |
| 310 SkASSERT(false); | |
| 311 strokeWidth = SK_Scalar1; | |
| 312 } | |
| 313 } | |
| 314 // Compensate the glyphs being scaled up by fTextRatio by scaling the | |
| 315 // stroke down. | |
| 316 fSkPaint.setStrokeWidth(strokeWidth / fTextRatio); | |
| 317 } | |
| 318 fStroke = SkStrokeRec(fSkPaint); | |
| 319 | |
| 320 // Make glyph cache produce paths geometry for fill. We will stroke them | |
| 321 // by passing fStroke to drawPath. | |
| 322 fSkPaint.setStyle(SkPaint::kFill_Style); | |
| 323 | |
| 324 fStateRestore.set(fDrawTarget->drawState()); | |
| 325 | |
| 326 fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), | |
| 327 fContext->getRenderTarget()); | |
| 328 | |
| 329 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | |
| 330 kZero_StencilOp, | |
| 331 kZero_StencilOp, | |
| 332 kNotEqual_StencilFunc, | |
| 333 0xffff, | |
| 334 0x0000, | |
| 335 0xffff); | |
| 336 | |
| 337 *fDrawTarget->drawState()->stencil() = kStencilPass; | |
| 338 | |
| 339 size_t reserveAmount; | |
| 340 switch (skPaint.getTextEncoding()) { | |
| 341 default: | |
| 342 SkASSERT(false); | |
| 343 case SkPaint::kUTF8_TextEncoding: | |
| 344 reserveAmount = textByteLength; | |
| 345 break; | |
| 346 case SkPaint::kUTF16_TextEncoding: | |
| 347 reserveAmount = textByteLength / 2; | |
| 348 break; | |
| 349 case SkPaint::kUTF32_TextEncoding: | |
| 350 case SkPaint::kGlyphID_TextEncoding: | |
| 351 reserveAmount = textByteLength / 4; | |
| 352 break; | |
| 353 } | |
| 354 fPaths.setReserve(reserveAmount); | |
| 355 fTransforms.setReserve(reserveAmount); | |
| 356 } | |
| 357 | |
| 358 inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID, | |
| 359 const SkPoint& pos, | |
| 360 GrTextStrike* strike, | |
| 361 GrFontScaler* scaler) { | |
| 362 GrGlyph* glyph = strike->getGlyph(glyphID, scaler); | |
| 363 if (NULL == glyph || glyph->fBounds.isEmpty()) { | |
| 364 return; | |
| 365 } | |
| 366 | |
| 367 if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) { | |
| 368 if (!fTmpPath.isEmpty()) { | |
| 369 *fPaths.append() = fContext->createPath(fTmpPath, fStroke); | |
| 370 SkMatrix* t = fTransforms.append(); | |
| 371 t->setTranslate(pos.fX, pos.fY); | |
| 372 t->preScale(fTextRatio, fTextRatio); | |
| 373 } | |
| 374 } | |
| 375 } | |
| 376 | |
| 377 void GrStencilAndCoverTextContext::finish() { | |
| 378 if (fPaths.count() > 0) { | |
| 379 fDrawTarget->drawPaths(static_cast<size_t>(fPaths.count()), | |
| 380 fPaths.begin(), fTransforms.begin(), | |
| 381 SkPath::kWinding_FillType, fStroke.getStyle()); | |
| 382 for (int i = 0; i < fPaths.count(); ++i) { | |
| 383 fPaths[i]->unref(); | |
| 384 } | |
| 385 if (fPaths.count() > kMaxReservedGlyphs) { | |
| 386 fPaths.reset(); | |
| 387 fTransforms.reset(); | |
| 388 } else { | |
| 389 fPaths.rewind(); | |
| 390 fTransforms.rewind(); | |
| 391 } | |
| 392 } | |
| 393 fTmpPath.reset(); | |
| 394 | |
| 395 fDrawTarget->drawState()->stencil()->setDisabled(); | |
| 396 fStateRestore.set(NULL); | |
| 397 GrTextContext::finish(); | |
| 398 } | |
| 399 | |
| OLD | NEW |