Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /*
2 * Copyright 2013 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 */
8 #include "GrBitmapTextContext.h"
9 #include "GrAtlas.h"
10 #include "GrDrawTarget.h"
11 #include "GrFontScaler.h"
12 #include "GrIndexBuffer.h"
13 #include "GrTextStrike.h"
14 #include "GrTextStrike_impl.h"
15 #include "SkColorPriv.h"
16 #include "SkPath.h"
17 #include "SkRTConf.h"
18 #include "SkStrokeRec.h"
19 #include "effects/GrCustomCoordsTextureEffect.h"
21 #include "SkAutoKern.h"
22 #include "SkDraw.h"
23 #include "SkGlyphCache.h"
24 #include "SkGpuDevice.h"
25 #include "SkGr.h"
27 static const int kGlyphCoordsAttributeIndex = 1;
29 SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
30 "Dump the contents of the font cache before every purge.");
32 GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
33 const SkDeviceProperties& properties)
34 : GrTextContext(context, properties) {
35 fStrike = NULL;
37 fCurrTexture = NULL;
38 fCurrVertex = 0;
40 fVertices = NULL;
41 fMaxVertices = 0;
43 fVertexBounds.setLargestInverted();
44 }
46 GrBitmapTextContext::~GrBitmapTextContext() {
47 this->flushGlyphs();
48 }
50 bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
51 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
52 }
54 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
55 unsigned r = SkColorGetR(c);
56 unsigned g = SkColorGetG(c);
57 unsigned b = SkColorGetB(c);
58 return GrColorPackRGBA(r, g, b, 0xff);
59 }
61 void GrBitmapTextContext::flushGlyphs() {
62 if (NULL == fDrawTarget) {
63 return;
64 }
66 GrDrawState* drawState = fDrawTarget->drawState();
67 GrDrawState::AutoRestoreEffects are(drawState);
68 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
70 if (fCurrVertex > 0) {
71 // setup our sampler state for our text texture/atlas
72 SkASSERT(GrIsALIGN4(fCurrVertex));
73 SkASSERT(fCurrTexture);
74 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
76 // This effect could be stored with one of the cache objects (atlas?)
77 drawState->addCoverageEffect(
78 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
79 kGlyphCoordsAttributeIndex)->unref();
81 if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) {
82 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
83 drawState->setColor(0xffffffff);
84 } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
85 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
86 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
87 fPaint.numColorStages()) {
88 GrPrintf("LCD Text will not draw correctly.\n");
89 }
90 // We don't use the GrPaint's color in this case because it's been premultiplied by
91 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
92 // the mask texture color. The end result is that we get
93 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
94 int a = SkColorGetA(fSkPaint.getColor());
95 // paintAlpha
96 drawState->setColor(SkColorSetARGB(a, a, a, a));
97 // paintColor
98 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
99 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
100 } else {
101 // set back to normal in case we took LCD path previously.
102 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
103 drawState->setColor(fPaint.getColor());
104 }
106 int nGlyphs = fCurrVertex / 4;
107 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
108 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
109 nGlyphs,
110 4, 6, &fVertexBounds);
112 fDrawTarget->resetVertexSource();
113 fVertices = NULL;
114 fMaxVertices = 0;
115 fCurrVertex = 0;
116 fVertexBounds.setLargestInverted();
117 SkSafeSetNull(fCurrTexture);
118 }
119 }
121 inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
122 GrTextContext::init(paint, skPaint);
124 fStrike = NULL;
126 fCurrTexture = NULL;
127 fCurrVertex = 0;
129 fVertices = NULL;
130 fMaxVertices = 0;
131 }
133 inline void GrBitmapTextContext::finish() {
134 flushGlyphs();
136 GrTextContext::finish();
137 }
139 void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
140 const char text[], size_t byteLength,
141 SkScalar x, SkScalar y) {
142 SkASSERT(byteLength == 0 || text != NULL);
144 // nothing to draw
145 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
146 return;
147 }
149 this->init(paint, skPaint);
151 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
153 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
154 SkGlyphCache* cache = autoCache.getCache();
155 GrFontScaler* fontScaler = GetGrFontScaler(cache);
157 // transform our starting point
158 {
159 SkPoint loc;
160 fContext->getMatrix().mapXY(x, y, &loc);
161 x = loc.fX;
162 y = loc.fY;
163 }
165 // need to measure first
166 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
167 SkVector stop;
169 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
171 SkScalar stopX = stop.fX;
172 SkScalar stopY = stop.fY;
174 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
175 stopX = SkScalarHalf(stopX);
176 stopY = SkScalarHalf(stopY);
177 }
178 x -= stopX;
179 y -= stopY;
180 }
182 const char* stop = text + byteLength;
184 SkAutoKern autokern;
186 SkFixed fxMask = ~0;
187 SkFixed fyMask = ~0;
188 SkFixed halfSampleX, halfSampleY;
189 if (cache->isSubpixel()) {
190 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
191 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
192 if (kX_SkAxisAlignment == baseline) {
193 fyMask = 0;
194 halfSampleY = SK_FixedHalf;
195 } else if (kY_SkAxisAlignment == baseline) {
196 fxMask = 0;
197 halfSampleX = SK_FixedHalf;
198 }
199 } else {
200 halfSampleX = halfSampleY = SK_FixedHalf;
201 }
203 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
204 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
206 GrContext::AutoMatrix autoMatrix;
207 autoMatrix.setIdentity(fContext, &fPaint);
209 while (text < stop) {
210 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
212 fx += autokern.adjust(glyph);
214 if (glyph.fWidth) {
215 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
216 glyph.getSubXFixed(),
217 glyph.getSubYFixed()),
218 SkFixedFloorToFixed(fx),
219 SkFixedFloorToFixed(fy),
220 fontScaler);
221 }
223 fx += glyph.fAdvanceX;
224 fy += glyph.fAdvanceY;
225 }
227 this->finish();
228 }
230 ///////////////////////////////////////////////////////////////////////////////
231 // Copied from SkDraw
233 // last parameter is interpreted as SkFixed [x, y]
234 // return the fixed position, which may be rounded or not by the caller
235 // e.g. subpixel doesn't round
236 typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
238 static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
239 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
240 }
242 static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
243 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
244 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
245 }
247 static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
248 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
249 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
250 }
252 static AlignProc pick_align_proc(SkPaint::Align align) {
253 static const AlignProc gProcs[] = {
254 leftAlignProc, centerAlignProc, rightAlignProc
255 };
257 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
259 return gProcs[align];
260 }
262 class BitmapTextMapState {
263 public:
264 mutable SkPoint fLoc;
266 BitmapTextMapState(const SkMatrix& matrix, SkScalar y)
267 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
269 typedef void (*Proc)(const BitmapTextMapState&, const SkScalar pos[]);
271 Proc pickProc(int scalarsPerPosition);
273 private:
274 const SkMatrix& fMatrix;
275 SkMatrix::MapXYProc fProc;
276 SkScalar fY; // ignored by MapXYProc
277 // these are only used by Only... procs
278 SkScalar fScaleX, fTransX, fTransformedY;
280 static void MapXProc(const BitmapTextMapState& state, const SkScalar pos[]) {
281 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
282 }
284 static void MapXYProc(const BitmapTextMapState& state, const SkScalar pos[]) {
285 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
286 }
288 static void MapOnlyScaleXProc(const BitmapTextMapState& state,
289 const SkScalar pos[]) {
290 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
291 state.fTransformedY);
292 }
294 static void MapOnlyTransXProc(const BitmapTextMapState& state,
295 const SkScalar pos[]) {
296 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
297 }
298 };
300 BitmapTextMapState::Proc BitmapTextMapState::pickProc(int scalarsPerPosition) {
301 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
303 if (1 == scalarsPerPosition) {
304 unsigned mtype = fMatrix.getType();
305 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
306 return MapXProc;
307 } else {
308 fScaleX = fMatrix.getScaleX();
309 fTransX = fMatrix.getTranslateX();
310 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
311 fMatrix.getTranslateY();
312 return (mtype & SkMatrix::kScale_Mask) ?
313 MapOnlyScaleXProc : MapOnlyTransXProc;
314 }
315 } else {
316 return MapXYProc;
317 }
318 }
320 ///////////////////////////////////////////////////////////////////////////////
322 void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
323 const char text[], size_t byteLength,
324 const SkScalar pos[], SkScalar constY,
325 int scalarsPerPosition) {
326 SkASSERT(byteLength == 0 || text != NULL);
327 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
329 // nothing to draw
330 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
331 return;
332 }
334 this->init(paint, skPaint);
336 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
338 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
339 SkGlyphCache* cache = autoCache.getCache();
340 GrFontScaler* fontScaler = GetGrFontScaler(cache);
342 // store original matrix before we reset, so we can use it to transform positions
343 SkMatrix ctm = fContext->getMatrix();
344 GrContext::AutoMatrix autoMatrix;
345 autoMatrix.setIdentity(fContext, &fPaint);
347 const char* stop = text + byteLength;
348 AlignProc alignProc = pick_align_proc(fSkPaint.getTextAlign());
349 BitmapTextMapState tms(ctm, constY);
350 BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
351 SkFixed halfSampleX = 0, halfSampleY = 0;
353 if (cache->isSubpixel()) {
354 // maybe we should skip the rounding if linearText is set
355 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
357 SkFixed fxMask = ~0;
358 SkFixed fyMask = ~0;
359 if (kX_SkAxisAlignment == baseline) {
360 fyMask = 0;
361 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
362 halfSampleY = SK_FixedHalf;
363 #endif
364 } else if (kY_SkAxisAlignment == baseline) {
365 fxMask = 0;
366 #ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
367 halfSampleX = SK_FixedHalf;
368 #endif
369 }
371 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
372 while (text < stop) {
373 tmsProc(tms, pos);
374 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + halfSampleX;
375 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + halfSampleY;
377 const SkGlyph& glyph = glyphCacheProc(cache, &text,
378 fx & fxMask, fy & fyMask);
380 if (glyph.fWidth) {
381 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
382 glyph.getSubXFixed(),
383 glyph.getSubYFixed()),
384 SkFixedFloorToFixed(fx),
385 SkFixedFloorToFixed(fy),
386 fontScaler);
387 }
388 pos += scalarsPerPosition;
389 }
390 } else {
391 while (text < stop) {
392 const char* currentText = text;
393 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
395 if (metricGlyph.fWidth) {
396 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
397 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
399 tmsProc(tms, pos);
400 SkIPoint fixedLoc;
401 alignProc(tms.fLoc, metricGlyph, &fixedLoc);
403 SkFixed fx = fixedLoc.fX + halfSampleX;
404 SkFixed fy = fixedLoc.fY + halfSampleY;
406 // have to call again, now that we've been "aligned"
407 const SkGlyph& glyph = glyphCacheProc(cache, ¤tText,
408 fx & fxMask, fy & fyMask);
409 // the assumption is that the metrics haven't changed
410 SkASSERT(prevAdvX == glyph.fAdvanceX);
411 SkASSERT(prevAdvY == glyph.fAdvanceY);
412 SkASSERT(glyph.fWidth);
414 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
415 glyph.getSubXFixed(),
416 glyph.getSubYFixed()),
417 SkFixedFloorToFixed(fx),
418 SkFixedFloorToFixed(fy),
419 fontScaler);
420 }
421 pos += scalarsPerPosition;
422 }
423 }
424 } else { // not subpixel
426 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
427 while (text < stop) {
428 // the last 2 parameters are ignored
429 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
431 if (glyph.fWidth) {
432 tmsProc(tms, pos);
434 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX;
435 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY;
436 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
437 glyph.getSubXFixed(),
438 glyph.getSubYFixed()),
439 SkFixedFloorToFixed(fx),
440 SkFixedFloorToFixed(fy),
441 fontScaler);
442 }
443 pos += scalarsPerPosition;
444 }
445 } else {
446 while (text < stop) {
447 // the last 2 parameters are ignored
448 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
450 if (glyph.fWidth) {
451 tmsProc(tms, pos);
453 SkIPoint fixedLoc;
454 alignProc(tms.fLoc, glyph, &fixedLoc);
456 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
457 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
458 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
459 glyph.getSubXFixed(),
460 glyph.getSubYFixed()),
461 SkFixedFloorToFixed(fx),
462 SkFixedFloorToFixed(fy),
463 fontScaler);
464 }
465 pos += scalarsPerPosition;
466 }
467 }
468 }
470 this->finish();
471 }
473 namespace {
475 // position + texture coord
476 extern const GrVertexAttrib gTextVertexAttribs[] = {
477 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
478 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
479 };
481 };
483 void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
484 GrFixed vx, GrFixed vy,
485 GrFontScaler* scaler) {
486 if (NULL == fDrawTarget) {
487 return;
488 }
490 if (NULL == fStrike) {
491 fStrike = fContext->getFontCache()->getStrike(scaler, false);
492 }
494 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
495 if (NULL == glyph || glyph->fBounds.isEmpty()) {
496 return;
497 }
499 vx += SkIntToFixed(glyph->fBounds.fLeft);
500 vy += SkIntToFixed(glyph->fBounds.fTop);
502 // keep them as ints until we've done the clip-test
503 GrFixed width = glyph->fBounds.width();
504 GrFixed height = glyph->fBounds.height();
506 // check if we clipped out
507 if (true || NULL == glyph->fPlot) {
508 int x = vx >> 16;
509 int y = vy >> 16;
510 if (fClipRect.quickReject(x, y, x + width, y + height)) {
511 // SkCLZ(3); // so we can set a break-point in the debugger
512 return;
513 }
514 }
516 if (NULL == glyph->fPlot) {
517 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
518 goto HAS_ATLAS;
519 }
521 // try to clear out an unused plot before we flush
522 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
523 fStrike->addGlyphToAtlas(glyph, scaler)) {
524 goto HAS_ATLAS;
525 }
527 if (c_DumpFontCache) {
528 #ifdef SK_DEVELOPER
529 fContext->getFontCache()->dump();
530 #endif
531 }
533 // flush any accumulated draws to allow us to free up a plot
534 this->flushGlyphs();
535 fContext->flush();
537 // we should have an unused plot now
538 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
539 fStrike->addGlyphToAtlas(glyph, scaler)) {
540 goto HAS_ATLAS;
541 }
543 if (NULL == glyph->fPath) {
544 SkPath* path = SkNEW(SkPath);
545 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
546 // flag the glyph as being dead?
547 delete path;
548 return;
549 }
550 glyph->fPath = path;
551 }
553 GrContext::AutoMatrix am;
554 SkMatrix translate;
555 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
556 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
557 GrPaint tmpPaint(fPaint);
558 am.setPreConcat(fContext, translate, &tmpPaint);
559 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
560 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
561 return;
562 }
564 HAS_ATLAS:
565 SkASSERT(glyph->fPlot);
566 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
567 glyph->fPlot->setDrawToken(drawToken);
569 // now promote them to fixed (TODO: Rethink using fixed pt).
570 width = SkIntToFixed(width);
571 height = SkIntToFixed(height);
573 GrTexture* texture = glyph->fPlot->texture();
574 SkASSERT(texture);
576 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
577 this->flushGlyphs();
578 fCurrTexture = texture;
579 fCurrTexture->ref();
580 }
582 if (NULL == fVertices) {
583 // If we need to reserve vertices allow the draw target to suggest
584 // a number of verts to reserve and whether to perform a flush.
585 fMaxVertices = kMinRequestedVerts;
586 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
587 SK_ARRAY_COUNT(gTextVertexAttribs));
588 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
589 if (flush) {
590 this->flushGlyphs();
591 fContext->flush();
592 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
593 SK_ARRAY_COUNT(gTextVertexAttribs));
594 }
595 fMaxVertices = kDefaultRequestedVerts;
596 // ignore return, no point in flushing again.
597 fDrawTarget->geometryHints(&fMaxVertices, NULL);
599 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
600 if (fMaxVertices < kMinRequestedVerts) {
601 fMaxVertices = kDefaultRequestedVerts;
602 } else if (fMaxVertices > maxQuadVertices) {
603 // don't exceed the limit of the index buffer
604 fMaxVertices = maxQuadVertices;
605 }
606 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
607 0,
608 GrTCast<void**>(&fVertices),
609 NULL);
610 GrAlwaysAssert(success);
611 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
612 }
614 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
615 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
617 SkRect r;
618 r.fLeft = SkFixedToFloat(vx);
619 r.fTop = SkFixedToFloat(vy);
620 r.fRight = SkFixedToFloat(vx + width);
621 r.fBottom = SkFixedToFloat(vy + height);
623 fVertexBounds.growToInclude(r);
625 fVertices[2*fCurrVertex].setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom,
626 2 * sizeof(SkPoint));
627 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
628 SkFixedToFloat(texture->normalizeFixedY(ty)),
629 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
630 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
631 2 * sizeof(SkPoint));
632 fCurrVertex += 4;
633 }