blob: d55f6f221137ee5f453f5134cc3bce1c707b3ac7 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
2 Copyright 2010 Google Inc.
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
18#include "GrContext.h"
19#include "GrTextContext.h"
20
reed@google.comac10a2d2010-12-22 21:39:39 +000021#include "SkGpuDevice.h"
reed@google.com7b201d22011-01-11 18:59:23 +000022#include "SkGpuDeviceFactory.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000023#include "SkGrTexturePixelRef.h"
24
25#include "SkDrawProcs.h"
26#include "SkGlyphCache.h"
27
28#define CACHE_LAYER_TEXTURES 1
29
30#if 0
31 extern bool (*gShouldDrawProc)();
32 #define CHECK_SHOULD_DRAW(draw) \
33 do { \
34 if (gShouldDrawProc && !gShouldDrawProc()) return; \
35 this->prepareRenderTarget(draw); \
36 } while (0)
37#else
38 #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
39#endif
40
41class SkAutoExtMatrix {
42public:
43 SkAutoExtMatrix(const SkMatrix* extMatrix) {
44 if (extMatrix) {
45 SkGr::SkMatrix2GrMatrix(*extMatrix, &fMatrix);
46 fExtMatrix = &fMatrix;
47 } else {
48 fExtMatrix = NULL;
49 }
50 }
51 const GrMatrix* extMatrix() const { return fExtMatrix; }
52
53private:
54 GrMatrix fMatrix;
55 GrMatrix* fExtMatrix; // NULL or &fMatrix
56};
57
58///////////////////////////////////////////////////////////////////////////////
59
60SkGpuDevice::SkAutoCachedTexture::
61 SkAutoCachedTexture(SkGpuDevice* device,
62 const SkBitmap& bitmap,
63 const GrSamplerState& sampler,
64 GrTexture** texture) {
65 GrAssert(texture);
66 fTex = NULL;
67 *texture = this->set(device, bitmap, sampler);
68}
69
70SkGpuDevice::SkAutoCachedTexture::SkAutoCachedTexture() {
71 fTex = NULL;
72}
73
74GrTexture* SkGpuDevice::SkAutoCachedTexture::set(SkGpuDevice* device,
75 const SkBitmap& bitmap,
76 const GrSamplerState& sampler) {
77 if (fTex) {
78 fDevice->unlockCachedTexture(fTex);
79 }
80 fDevice = device;
81 GrTexture* texture = (GrTexture*)bitmap.getTexture();
82 if (texture) {
83 // return the native texture
84 fTex = NULL;
bsalomon@google.com8531c1c2011-01-13 19:52:45 +000085 device->context()->setTexture(0, texture);
reed@google.comac10a2d2010-12-22 21:39:39 +000086 } else {
87 // look it up in our cache
88 fTex = device->lockCachedTexture(bitmap, sampler, &texture, false);
89 }
90 return texture;
91}
92
93SkGpuDevice::SkAutoCachedTexture::~SkAutoCachedTexture() {
94 if (fTex) {
95 fDevice->unlockCachedTexture(fTex);
96 }
97}
98
99///////////////////////////////////////////////////////////////////////////////
100
101bool gDoTraceDraw;
102
103struct GrSkDrawProcs : public SkDrawProcs {
104public:
105 GrContext* fContext;
106 GrTextContext* fTextContext;
107 GrFontScaler* fFontScaler; // cached in the skia glyphcache
108};
109
110///////////////////////////////////////////////////////////////////////////////
111
reed@google.com7b201d22011-01-11 18:59:23 +0000112SkGpuDevice::SkGpuDevice(GrContext* context, const SkBitmap& bitmap, bool isLayer)
113 : SkDevice(NULL, bitmap, false) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000114
115 fNeedPrepareRenderTarget = false;
116 fDrawProcs = NULL;
117
reed@google.com7b201d22011-01-11 18:59:23 +0000118 // should I ref() this, and then unref in destructor? <mrr>
119 fContext = context;
reed@google.comac10a2d2010-12-22 21:39:39 +0000120
121 fCache = NULL;
122 fTexture = NULL;
123 fRenderTarget = NULL;
124 fNeedClear = false;
125
126 if (isLayer) {
127 SkBitmap::Config c = bitmap.config();
128 if (c != SkBitmap::kRGB_565_Config) {
129 c = SkBitmap::kARGB_8888_Config;
130 }
131 SkBitmap bm;
132 bm.setConfig(c, this->width(), this->height());
133
134#if CACHE_LAYER_TEXTURES
135
136 fCache = this->lockCachedTexture(bm, GrSamplerState::ClampNoFilter(),
137 &fTexture, true);
138 if (fCache) {
139 SkASSERT(NULL != fTexture);
140 SkASSERT(fTexture->isRenderTarget());
141 }
142#else
143 const GrGpu::TextureDesc desc = {
144 GrGpu::kRenderTarget_TextureFlag,
145 GrGpu::kNone_AALevel,
146 this->width(),
147 this->height(),
148 SkGr::Bitmap2PixelConfig(bm)
149 };
150
151 fTexture = fContext->createUncachedTexture(desc, NULL, 0);
152#endif
153 if (NULL != fTexture) {
154 fRenderTarget = fTexture->asRenderTarget();
155
156 GrAssert(NULL != fRenderTarget);
157
158 // we defer the actual clear until our gainFocus()
159 fNeedClear = true;
160
161 // wrap the bitmap with a pixelref to expose our texture
162 SkGrTexturePixelRef* pr = new SkGrTexturePixelRef(fTexture);
163 this->setPixelRef(pr, 0)->unref();
164 } else {
165 GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
166 this->width(), this->height());
167 }
168 }
169
170 if (NULL == fRenderTarget) {
171 GrAssert(NULL == fCache);
172 GrAssert(NULL == fTexture);
173
174 fRenderTarget = fContext->currentRenderTarget();
175 fRenderTarget->ref();
176 fContext->setDefaultRenderTargetSize(this->width(), this->height());
177 }
178}
179
180SkGpuDevice::~SkGpuDevice() {
181 if (fDrawProcs) {
182 delete fDrawProcs;
183 }
184
185 if (fCache) {
186 GrAssert(NULL != fTexture);
187 GrAssert(fRenderTarget == fTexture->asRenderTarget());
188 // IMPORTANT: reattach the rendertarget/tex back to the cache.
189 fContext->reattachAndUnlockCachedTexture((GrTextureEntry*)fCache);
190 } else if (NULL != fTexture) {
191 GrAssert(!CACHE_LAYER_TEXTURES);
192 GrAssert(fRenderTarget == fTexture->asRenderTarget());
193 fTexture->unref();
194 } else if (NULL != fRenderTarget) {
195 fRenderTarget->unref();
196 }
197}
198
199void SkGpuDevice::bindDeviceToTargetHandle(intptr_t handle) {
200 if (fCache) {
201 GrAssert(NULL != fTexture);
202 GrAssert(fRenderTarget == fTexture->asRenderTarget());
203 // IMPORTANT: reattach the rendertarget/tex back to the cache.
204 fContext->reattachAndUnlockCachedTexture((GrTextureEntry*)fCache);
205 } else if (NULL != fTexture) {
206 GrAssert(!CACHE_LAYER_TEXTURES);
207 fTexture->unref();
208 } else if (NULL != fRenderTarget) {
209 fRenderTarget->unref();
210 }
211
212 fCache = NULL;
213 fTexture = NULL;
214 fRenderTarget = fContext->createPlatformRenderTarget(handle,
215 this->width(),
216 this->height());
217}
218
219intptr_t SkGpuDevice::getLayerTextureHandle() const {
220 if (fTexture) {
221 return fTexture->getTextureHandle();
222 } else {
223 return 0;
224 }
225}
226///////////////////////////////////////////////////////////////////////////////
227
228void SkGpuDevice::makeRenderTargetCurrent() {
229 fContext->setRenderTarget(fRenderTarget);
230 fContext->flush(true);
231 fNeedPrepareRenderTarget = true;
232}
233
234///////////////////////////////////////////////////////////////////////////////
235
236bool SkGpuDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
237 SkIRect bounds;
238 bounds.set(0, 0, this->width(), this->height());
239 if (!bounds.intersect(srcRect)) {
240 return false;
241 }
242
243 const int w = bounds.width();
244 const int h = bounds.height();
245 SkBitmap tmp;
246 // note we explicitly specify our rowBytes to be snug (no gap between rows)
247 tmp.setConfig(SkBitmap::kARGB_8888_Config, w, h, w * 4);
248 if (!tmp.allocPixels()) {
249 return false;
250 }
251
252 SkAutoLockPixels alp(tmp);
253 fContext->setRenderTarget(fRenderTarget);
254 // we aren't setting the clip or matrix, so mark as dirty
255 // we don't need to set them for this call and don't have them anyway
256 fNeedPrepareRenderTarget = true;
257
258 if (!fContext->readPixels(bounds.fLeft, bounds.fTop,
259 bounds.width(), bounds.height(),
260 GrTexture::kRGBA_8888_PixelConfig,
261 tmp.getPixels())) {
262 return false;
263 }
264
265 tmp.swap(*bitmap);
266 return true;
267}
268
269void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) {
270 SkAutoLockPixels alp(bitmap);
271 if (!bitmap.readyToDraw()) {
272 return;
273 }
274 GrTexture::PixelConfig config = SkGr::BitmapConfig2PixelConfig(bitmap.config(),
275 bitmap.isOpaque());
276 fContext->setRenderTarget(fRenderTarget);
277 // we aren't setting the clip or matrix, so mark as dirty
278 // we don't need to set them for this call and don't have them anyway
279 fNeedPrepareRenderTarget = true;
280
281 fContext->writePixels(x, y, bitmap.width(), bitmap.height(),
282 config, bitmap.getPixels(), bitmap.rowBytes());
283}
284
285///////////////////////////////////////////////////////////////////////////////
286
287static void convert_matrixclip(GrContext* context, const SkMatrix& matrix,
288 const SkRegion& clip) {
289 GrMatrix grmat;
290 SkGr::SkMatrix2GrMatrix(matrix, &grmat);
291 context->setViewMatrix(grmat);
292
293 SkGrClipIterator iter;
294 iter.reset(clip);
295 GrClip grc(&iter);
296 if (context->getClip() == grc) {
297 } else {
298 context->setClip(grc);
299 }
300}
301
302// call this ever each draw call, to ensure that the context reflects our state,
303// and not the state from some other canvas/device
304void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) {
305 if (fNeedPrepareRenderTarget ||
306 fContext->currentRenderTarget() != fRenderTarget) {
307
308 fContext->setRenderTarget(fRenderTarget);
309 convert_matrixclip(fContext, *draw.fMatrix, *draw.fClip);
310 fNeedPrepareRenderTarget = false;
311 }
312}
313
314void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) {
315 this->INHERITED::setMatrixClip(matrix, clip);
316
317 convert_matrixclip(fContext, matrix, clip);
318}
319
320void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
321 const SkRegion& clip) {
322 fContext->setRenderTarget(fRenderTarget);
323
324 this->INHERITED::gainFocus(canvas, matrix, clip);
325
326 convert_matrixclip(fContext, matrix, clip);
327
328 if (fNeedClear) {
329 fContext->eraseColor(0x0);
330 fNeedClear = false;
331 }
332}
333
334bool SkGpuDevice::bindDeviceAsTexture(SkPoint* max) {
335 if (NULL != fTexture) {
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000336 fContext->setTexture(0, fTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000337 if (NULL != max) {
338 max->set(SkFixedToScalar((width() << 16) /
339 fTexture->allocWidth()),
340 SkFixedToScalar((height() << 16) /
341 fTexture->allocHeight()));
342 }
343 return true;
344 }
345 return false;
346}
347
348///////////////////////////////////////////////////////////////////////////////
349
350// must be in the same order as SkXfermode::Coeff in SkXfermode.h
351
352SkGpuDevice::AutoPaintShader::AutoPaintShader() {
353 fSuccess = false;
354 fTexture = NULL;
355}
356
357SkGpuDevice::AutoPaintShader::AutoPaintShader(SkGpuDevice* device,
358 const SkPaint& paint,
359 const SkMatrix& matrix) {
360 fSuccess = false;
361 fTexture = NULL;
362 this->init(device, paint, matrix);
363}
364
365void SkGpuDevice::AutoPaintShader::init(SkGpuDevice* device,
366 const SkPaint& paint,
367 const SkMatrix& ctm) {
368 fSuccess = true;
369 GrContext* ctx = device->context();
370 sk_gr_set_paint(ctx, paint); // should we pass true for justAlpha if we have a shader/texture?
371
372 SkShader* shader = paint.getShader();
373 if (NULL == shader) {
374 return;
375 }
376
377 if (!shader->setContext(device->accessBitmap(false), paint, ctm)) {
378 fSuccess = false;
379 return;
380 }
381
382 GrSamplerState::SampleMode sampleMode;
383 SkBitmap bitmap;
384 SkMatrix matrix;
385 SkShader::TileMode tileModes[2];
386 SkScalar twoPointParams[3];
387 SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, &matrix,
388 tileModes, twoPointParams);
389
390 switch (bmptype) {
391 case SkShader::kNone_BitmapType:
392 SkDebugf("shader->asABitmap() == kNone_BitmapType");
393 return;
394 case SkShader::kDefault_BitmapType:
395 sampleMode = GrSamplerState::kNormal_SampleMode;
396 break;
397 case SkShader::kRadial_BitmapType:
398 sampleMode = GrSamplerState::kRadial_SampleMode;
399 break;
400 case SkShader::kSweep_BitmapType:
401 sampleMode = GrSamplerState::kSweep_SampleMode;
402 break;
403 case SkShader::kTwoPointRadial_BitmapType:
404 sampleMode = GrSamplerState::kRadial2_SampleMode;
405 break;
406 default:
407 SkASSERT("Unexpected return from asABitmap");
408 return;
409 }
410
411 bitmap.lockPixels();
412 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
413 return;
414 }
415
416 // see if we've already cached the bitmap from the shader
417 GrSamplerState samplerState(sk_tile_mode_to_grwrap(tileModes[0]),
418 sk_tile_mode_to_grwrap(tileModes[1]),
419 sampleMode,
420 paint.isFilterBitmap());
421
422 if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
423 samplerState.setRadial2Params(twoPointParams[0],
424 twoPointParams[1],
425 twoPointParams[2] < 0);
426 }
427
428 GrTexture* texture = fCachedTexture.set(device, bitmap, samplerState);
429 if (NULL == texture) {
430 return;
431 }
432
433 // the lock has already called setTexture for us
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000434 ctx->setSamplerState(0, samplerState);
reed@google.comac10a2d2010-12-22 21:39:39 +0000435
436 // since our texture coords will be in local space, we wack the texture
437 // matrix to map them back into 0...1 before we load it
438 SkMatrix localM;
439 if (shader->getLocalMatrix(&localM)) {
440 SkMatrix inverse;
441 if (localM.invert(&inverse)) {
442 matrix.preConcat(inverse);
443 }
444 }
445 if (SkShader::kDefault_BitmapType == bmptype) {
446 GrScalar sx = (GR_Scalar1 * texture->contentWidth()) /
447 (bitmap.width() * texture->allocWidth());
448 GrScalar sy = (GR_Scalar1 * texture->contentHeight()) /
449 (bitmap.height() * texture->allocHeight());
450 matrix.postScale(sx, sy);
451
452 } else if (SkShader::kRadial_BitmapType == bmptype) {
453 GrScalar s = (GR_Scalar1 * texture->contentWidth()) /
454 (bitmap.width() * texture->allocWidth());
455 matrix.postScale(s, s);
456 }
457 GrMatrix grmat;
458 SkGr::SkMatrix2GrMatrix(matrix, &grmat);
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000459 ctx->setTextureMatrix(0, grmat);
reed@google.comac10a2d2010-12-22 21:39:39 +0000460
461 // since we're going to use a shader/texture, we don't want the color,
462 // just its alpha
463 ctx->setAlpha(paint.getAlpha());
464 // report that we have setup the texture
465 fSuccess = true;
466 fTexture = texture;
467}
468
469///////////////////////////////////////////////////////////////////////////////
470///////////////////////////////////////////////////////////////////////////////
471
472void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
473 CHECK_SHOULD_DRAW(draw);
474
475 AutoPaintShader shader(this, paint, *draw.fMatrix);
476 if (shader.failed()) {
477 return;
478 }
479 fContext->drawFull(shader.useTex());
480}
481
482// must be in SkCanvas::PointMode order
483static const GrGpu::PrimitiveType gPointMode2PrimtiveType[] = {
484 GrGpu::kPoints_PrimitiveType,
485 GrGpu::kLines_PrimitiveType,
486 GrGpu::kLineStrip_PrimitiveType
487};
488
489void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
490 size_t count, const SkPoint pts[], const SkPaint& paint) {
491 CHECK_SHOULD_DRAW(draw);
492
493 SkScalar width = paint.getStrokeWidth();
494 if (width < 0) {
495 return;
496 }
497
498 // we only handle hairlines here, else we let the SkDraw call our drawPath()
499 if (width > 0) {
500 draw.drawPoints(mode, count, pts, paint, true);
501 return;
502 }
503
504 AutoPaintShader shader(this, paint, *draw.fMatrix);
505 if (shader.failed()) {
506 return;
507 }
508
509 GrVertexLayout layout = shader.useTex() ?
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000510 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0) :
reed@google.comac10a2d2010-12-22 21:39:39 +0000511 0;
512#if SK_SCALAR_IS_GR_SCALAR
513 fContext->setVertexSourceToArray(pts, layout);
514 fContext->drawNonIndexed(gPointMode2PrimtiveType[mode], 0, count);
515#else
516 GrPoint* v;
517 fContext->reserveAndLockGeometry(layout, count, 0, (void**)&v, NULL);
reed@google.com72cf4922011-01-04 19:58:20 +0000518 for (size_t i = 0; i < count; ++i) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000519 v[i].set(SkScalarToGrScalar(pts[i].fX), SkScalarToGrScalar(pts[i].fY));
520 }
reed@google.com72cf4922011-01-04 19:58:20 +0000521 fContext->drawNonIndexed(gPointMode2PrimtiveType[mode], 0, count);
reed@google.comac10a2d2010-12-22 21:39:39 +0000522 fContext->releaseReservedGeometry();
523#endif
524
525}
526
527void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
528 const SkPaint& paint) {
529 CHECK_SHOULD_DRAW(draw);
530
531 bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
532 SkScalar width = paint.getStrokeWidth();
533
534 /*
535 We have special code for hairline strokes, miter-strokes, and fills.
536 Anything else we just call our path code. (i.e. non-miter thick stroke)
537 */
538 if (doStroke && width > 0 && paint.getStrokeJoin() != SkPaint::kMiter_Join) {
539 SkPath path;
540 path.addRect(rect);
541 this->drawPath(draw, path, paint, NULL, true);
542 return;
543 }
544
545 AutoPaintShader shader(this, paint, *draw.fMatrix);
546 if (shader.failed()) {
547 return;
548 }
549
550 fContext->drawRect(Sk2Gr(rect), shader.useTex(), doStroke ? width : -1);
551}
552
553void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& path,
554 const SkPaint& paint, const SkMatrix* prePathMatrix,
555 bool pathIsMutable) {
556 CHECK_SHOULD_DRAW(draw);
557
558 AutoPaintShader shader(this, paint, *draw.fMatrix);
559 if (shader.failed()) {
560 return;
561 }
562
563 const SkPath* pathPtr = &path;
564 SkPath tmpPath;
565
566 if (prePathMatrix) {
567 if (pathIsMutable) {
568 const_cast<SkPath*>(pathPtr)->transform(*prePathMatrix);
569 } else {
570 path.transform(*prePathMatrix, &tmpPath);
571 pathPtr = &tmpPath;
572 }
573 }
574
575 SkPath fillPath;
576 GrContext::PathFills fill = GrContext::kHairLine_PathFill;
577
578 if (paint.getFillPath(*pathPtr, &fillPath)) {
579 switch (fillPath.getFillType()) {
580 case SkPath::kWinding_FillType:
581 fill = GrContext::kWinding_PathFill;
582 break;
583 case SkPath::kEvenOdd_FillType:
584 fill = GrContext::kEvenOdd_PathFill;
585 break;
586 case SkPath::kInverseWinding_FillType:
587 fill = GrContext::kInverseWinding_PathFill;
588 break;
589 case SkPath::kInverseEvenOdd_FillType:
590 fill = GrContext::kInverseEvenOdd_PathFill;
591 break;
592 default:
593 SkDebugf("Unsupported path fill type");
594 return;
595 }
596 }
597
598 SkGrPathIter iter(fillPath);
599 fContext->drawPath(&iter, fill, shader.useTex());
600}
601
602/*
603 * This value must not exceed the GPU's texture dimension limit, but it can
604 * be smaller, if that helps avoid very large single textures hurting the
605 * cache.
606 */
607#define MAX_TEXTURE_DIM 512
608
609void SkGpuDevice::drawBitmap(const SkDraw& draw,
610 const SkBitmap& bitmap,
611 const SkIRect* srcRectPtr,
612 const SkMatrix& m,
613 const SkPaint& paint) {
614 CHECK_SHOULD_DRAW(draw);
615
616 SkIRect srcRect;
617 if (NULL == srcRectPtr) {
618 srcRect.set(0, 0, bitmap.width(), bitmap.height());
619 } else {
620 srcRect = *srcRectPtr;
621 }
622
623 if (bitmap.getTexture() || (bitmap.width() <= MAX_TEXTURE_DIM &&
624 bitmap.height() <= MAX_TEXTURE_DIM)) {
625 // take the fast case
626 this->internalDrawBitmap(draw, bitmap, srcRect, m, paint);
627 return;
628 }
629
630 // undo the translate done by SkCanvas
631 int DX = SkMax32(0, srcRect.fLeft);
632 int DY = SkMax32(0, srcRect.fTop);
633 // compute clip bounds in local coordinates
634 SkIRect clipRect;
635 {
636 SkRect r;
637 r.set(draw.fClip->getBounds());
638 SkMatrix matrix, inverse;
639 matrix.setConcat(*draw.fMatrix, m);
640 if (!matrix.invert(&inverse)) {
641 return;
642 }
643 inverse.mapRect(&r);
644 r.roundOut(&clipRect);
645 // apply the canvas' translate to our local clip
646 clipRect.offset(DX, DY);
647 }
648
649 int nx = bitmap.width() / MAX_TEXTURE_DIM;
650 int ny = bitmap.height() / MAX_TEXTURE_DIM;
651 for (int x = 0; x <= nx; x++) {
652 for (int y = 0; y <= ny; y++) {
653 SkIRect tileR;
654 tileR.set(x * MAX_TEXTURE_DIM, y * MAX_TEXTURE_DIM,
655 (x + 1) * MAX_TEXTURE_DIM, (y + 1) * MAX_TEXTURE_DIM);
656 if (!SkIRect::Intersects(tileR, clipRect)) {
657 continue;
658 }
659
660 SkIRect srcR = tileR;
661 if (!srcR.intersect(srcRect)) {
662 continue;
663 }
664
665 SkBitmap tmpB;
666 if (bitmap.extractSubset(&tmpB, tileR)) {
667 // now offset it to make it "local" to our tmp bitmap
668 srcR.offset(-tileR.fLeft, -tileR.fTop);
669
670 SkMatrix tmpM(m);
671 {
672 int dx = tileR.fLeft - DX + SkMax32(0, srcR.fLeft);
673 int dy = tileR.fTop - DY + SkMax32(0, srcR.fTop);
674 tmpM.preTranslate(SkIntToScalar(dx), SkIntToScalar(dy));
675 }
676 this->internalDrawBitmap(draw, tmpB, srcR, tmpM, paint);
677 }
678 }
679 }
680}
681
682/*
683 * This is called by drawBitmap(), which has to handle images that may be too
684 * large to be represented by a single texture.
685 *
686 * internalDrawBitmap assumes that the specified bitmap will fit in a texture.
687 */
688void SkGpuDevice::internalDrawBitmap(const SkDraw& draw,
689 const SkBitmap& bitmap,
690 const SkIRect& srcRect,
691 const SkMatrix& m,
692 const SkPaint& paint) {
693 SkASSERT(bitmap.width() <= MAX_TEXTURE_DIM &&
694 bitmap.height() <= MAX_TEXTURE_DIM);
695
696 SkAutoLockPixels alp(bitmap);
697 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
698 return;
699 }
700
701 GrSamplerState sampler(paint.isFilterBitmap()); // defaults to clamp
702 // the lock has already called setTexture for us
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000703 fContext->setSamplerState(0, sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +0000704
705 GrTexture* texture;
706 SkAutoCachedTexture act(this, bitmap, sampler, &texture);
707 if (NULL == texture) {
708 return;
709 }
710
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000711 GrVertexLayout layout = GrDrawTarget::StageTexCoordVertexLayoutBit(0, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000712
713 GrPoint* vertex;
714 if (!fContext->reserveAndLockGeometry(layout, 4,
reed@google.com1fcd51e2011-01-05 15:50:27 +0000715 0, GrTCast<void**>(&vertex), NULL)) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000716 return;
717 }
718
719 {
720 GrMatrix grmat;
721 SkGr::SkMatrix2GrMatrix(m, &grmat);
722 vertex[0].setIRectFan(0, 0, srcRect.width(), srcRect.height(),
723 2*sizeof(GrPoint));
724 grmat.mapPointsWithStride(vertex, 2*sizeof(GrPoint), 4);
725 }
726
727 SkScalar left = SkFixedToScalar((srcRect.fLeft << 16) /
728 texture->allocWidth());
729 SkScalar right = SkFixedToScalar((srcRect.fRight << 16) /
730 texture->allocWidth());
731 SkScalar top = SkFixedToScalar((srcRect.fTop << 16) /
732 texture->allocHeight());
733 SkScalar bottom = SkFixedToScalar((srcRect.fBottom << 16) /
734 texture->allocHeight());
735 vertex[1].setRectFan(left, top, right, bottom, 2*sizeof(GrPoint));
736
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000737 fContext->setTextureMatrix(0, GrMatrix::I());
reed@google.comac10a2d2010-12-22 21:39:39 +0000738 // now draw the mesh
739 sk_gr_set_paint(fContext, paint, true);
740 fContext->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType, 0, 4);
741 fContext->releaseReservedGeometry();
742}
743
744static void gl_drawSprite(GrContext* ctx,
745 int x, int y, int w, int h, const SkPoint& max,
746 const SkPaint& paint) {
747 GrAutoViewMatrix avm(ctx, GrMatrix::I());
748
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000749 ctx->setSamplerState(0, GrSamplerState::ClampNoFilter());
750 ctx->setTextureMatrix(0, GrMatrix::I());
reed@google.comac10a2d2010-12-22 21:39:39 +0000751
752 GrPoint* vertex;
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000753 GrVertexLayout layout = GrGpu::StageTexCoordVertexLayoutBit(0, 0);
reed@google.com1fcd51e2011-01-05 15:50:27 +0000754 if (!ctx->reserveAndLockGeometry(layout, 4, 0,
755 GrTCast<void**>(&vertex), NULL)) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000756 return;
757 }
758
759 vertex[1].setRectFan(0, 0, max.fX, max.fY, 2*sizeof(GrPoint));
760
761 vertex[0].setIRectFan(x, y, x + w, y + h, 2*sizeof(GrPoint));
762
763 sk_gr_set_paint(ctx, paint, true);
764 // should look to use glDrawTexi() has we do for text...
765 ctx->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType, 0, 4);
766 ctx->releaseReservedGeometry();
767}
768
769void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
770 int left, int top, const SkPaint& paint) {
771 CHECK_SHOULD_DRAW(draw);
772
773 SkAutoLockPixels alp(bitmap);
774 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
775 return;
776 }
777
778 SkPoint max;
779 GrTexture* texture;
780 SkAutoCachedTexture act(this, bitmap, GrSamplerState::ClampNoFilter(),
781 &texture);
782
783 max.set(SkFixedToScalar((texture->contentWidth() << 16) /
784 texture->allocWidth()),
785 SkFixedToScalar((texture->contentHeight() << 16) /
786 texture->allocHeight()));
787 gl_drawSprite(fContext, left, top, bitmap.width(), bitmap.height(), max, paint);
788}
789
790void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
791 int x, int y, const SkPaint& paint) {
792 CHECK_SHOULD_DRAW(draw);
793
794 SkPoint max;
795 if (((SkGpuDevice*)dev)->bindDeviceAsTexture(&max)) {
796 const SkBitmap& bm = dev->accessBitmap(false);
797 int w = bm.width();
798 int h = bm.height();
799 gl_drawSprite(fContext, x, y, w, h, max, paint);
800 }
801}
802
803///////////////////////////////////////////////////////////////////////////////
804
805// must be in SkCanvas::VertexMode order
806static const GrGpu::PrimitiveType gVertexMode2PrimitiveType[] = {
807 GrGpu::kTriangles_PrimitiveType,
808 GrGpu::kTriangleStrip_PrimitiveType,
809 GrGpu::kTriangleFan_PrimitiveType,
810};
811
812void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
813 int vertexCount, const SkPoint vertices[],
814 const SkPoint texs[], const SkColor colors[],
815 SkXfermode* xmode,
816 const uint16_t indices[], int indexCount,
817 const SkPaint& paint) {
818 CHECK_SHOULD_DRAW(draw);
819
820 sk_gr_set_paint(fContext, paint);
821
822 TexCache* cache = NULL;
823
824 bool useTexture = false;
825
826 AutoPaintShader autoShader;
827
828 if (texs) {
829 autoShader.init(this, paint, *draw.fMatrix);
830
831 if (autoShader.failed()) {
832 return;
833 }
834 useTexture = autoShader.useTex();
835 }
836
837 bool releaseVerts = false;
838 GrVertexLayout layout = 0;
839 if (useTexture) {
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000840 layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000841 }
842 if (NULL != colors) {
843 layout |= GrDrawTarget::kColor_VertexLayoutBit;
844 }
845
846 #if SK_SCALAR_IS_GR_SCALAR
847 if (!layout) {
848 fContext->setVertexSourceToArray(vertices, layout);
849 } else
850 #endif
851 {
852 void* verts;
853 releaseVerts = true;
854 if (!fContext->reserveAndLockGeometry(layout, vertexCount, 0,
855 &verts, NULL)) {
856 return;
857 }
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000858 int texOffsets[GrDrawTarget::kNumStages];
859 int colorOffset;
860 uint32_t stride = GrDrawTarget::VertexSizeAndOffsetsByStage(layout,
861 texOffsets,
862 &colorOffset);
reed@google.comac10a2d2010-12-22 21:39:39 +0000863 for (int i = 0; i < vertexCount; ++i) {
864 GrPoint* p = (GrPoint*)((intptr_t)verts + i * stride);
865 p->set(SkScalarToGrScalar(vertices[i].fX),
866 SkScalarToGrScalar(vertices[i].fY));
bsalomon@google.com8531c1c2011-01-13 19:52:45 +0000867 if (texOffsets[0] > 0) {
868 GrPoint* t = (GrPoint*)((intptr_t)p + texOffsets[0]);
reed@google.comac10a2d2010-12-22 21:39:39 +0000869 t->set(SkScalarToGrScalar(texs[i].fX),
870 SkScalarToGrScalar(texs[i].fY));
871 }
872 if (colorOffset > 0) {
873 uint32_t* color = (uint32_t*) ((intptr_t)p + colorOffset);
874 *color = SkGr::SkColor2GrColor(colors[i]);
875 }
876 }
877 }
878 if (indices) {
879 fContext->setIndexSourceToArray(indices);
880 fContext->drawIndexed(gVertexMode2PrimitiveType[vmode], 0, 0,
881 vertexCount, indexCount);
882 } else {
883 fContext->drawNonIndexed(gVertexMode2PrimitiveType[vmode],
884 0, vertexCount);
885 }
886 if (cache) {
887 this->unlockCachedTexture(cache);
888 }
889 if (releaseVerts) {
890 fContext->releaseReservedGeometry();
891 }
892}
893
894///////////////////////////////////////////////////////////////////////////////
895
896static void GlyphCacheAuxProc(void* data) {
897 delete (GrFontScaler*)data;
898}
899
900static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
901 void* auxData;
902 GrFontScaler* scaler = NULL;
903 if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
904 scaler = (GrFontScaler*)auxData;
905 }
906 if (NULL == scaler) {
907 scaler = new SkGrFontScaler(cache);
908 cache->setAuxProc(GlyphCacheAuxProc, scaler);
909 }
910 return scaler;
911}
912
913static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
914 SkFixed fx, SkFixed fy,
915 const SkGlyph& glyph) {
916 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
917
918 GrSkDrawProcs* procs = (GrSkDrawProcs*)state.fDraw->fProcs;
919
920 if (NULL == procs->fFontScaler) {
921 procs->fFontScaler = get_gr_font_scaler(state.fCache);
922 }
923 procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), fx, 0),
924 SkIntToFixed(SkFixedFloor(fx)), fy,
925 procs->fFontScaler);
926}
927
928SkDrawProcs* SkGpuDevice::initDrawForText(const SkPaint& paint,
929 GrTextContext* context) {
930
931 // deferred allocation
932 if (NULL == fDrawProcs) {
933 fDrawProcs = new GrSkDrawProcs;
934 fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
935 fDrawProcs->fContext = fContext;
936 }
937
938 // init our (and GL's) state
939 fDrawProcs->fTextContext = context;
940 fDrawProcs->fFontScaler = NULL;
941 return fDrawProcs;
942}
943
944void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
945 size_t byteLength, SkScalar x, SkScalar y,
946 const SkPaint& paint) {
947 CHECK_SHOULD_DRAW(draw);
948
949 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
950 // this guy will just call our drawPath()
951 draw.drawText((const char*)text, byteLength, x, y, paint);
952 } else {
953 SkAutoExtMatrix aem(draw.fExtMatrix);
954 SkDraw myDraw(draw);
955 sk_gr_set_paint(fContext, paint);
956 GrTextContext context(fContext, aem.extMatrix());
957 myDraw.fProcs = this->initDrawForText(paint, &context);
958 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
959 }
960}
961
962void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
963 size_t byteLength, const SkScalar pos[],
964 SkScalar constY, int scalarsPerPos,
965 const SkPaint& paint) {
966 CHECK_SHOULD_DRAW(draw);
967
968 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
969 // this guy will just call our drawPath()
970 draw.drawPosText((const char*)text, byteLength, pos, constY,
971 scalarsPerPos, paint);
972 } else {
973 SkAutoExtMatrix aem(draw.fExtMatrix);
974 SkDraw myDraw(draw);
975 sk_gr_set_paint(fContext, paint);
976 GrTextContext context(fContext, aem.extMatrix());
977 myDraw.fProcs = this->initDrawForText(paint, &context);
978 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
979 scalarsPerPos, paint);
980 }
981}
982
983void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
984 size_t len, const SkPath& path,
985 const SkMatrix* m, const SkPaint& paint) {
986 CHECK_SHOULD_DRAW(draw);
987
988 SkASSERT(draw.fDevice == this);
989 draw.drawTextOnPath((const char*)text, len, path, m, paint);
990}
991
992///////////////////////////////////////////////////////////////////////////////
993
994SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
995 const GrSamplerState& sampler,
996 GrTexture** texture,
997 bool forDeviceRenderTarget) {
998 GrContext* ctx = this->context();
999 uint32_t p0, p1;
1000 if (forDeviceRenderTarget) {
1001 p0 = p1 = -1;
1002 } else {
1003 p0 = bitmap.getGenerationID();
1004 p1 = bitmap.pixelRefOffset();
1005 }
1006
1007 GrTexture* newTexture = NULL;
1008 GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
1009 GrTextureEntry* entry = ctx->findAndLockTexture(&key, sampler);
1010
1011 if (NULL == entry) {
1012
1013 if (forDeviceRenderTarget) {
1014 const GrGpu::TextureDesc desc = {
1015 GrGpu::kRenderTarget_TextureFlag,
1016 GrGpu::kNone_AALevel,
1017 bitmap.width(),
1018 bitmap.height(),
1019 SkGr::Bitmap2PixelConfig(bitmap)
1020 };
1021 entry = ctx->createAndLockTexture(&key, sampler, desc, NULL, 0);
1022
1023 } else {
1024 entry = sk_gr_create_bitmap_texture(ctx, &key, sampler, bitmap);
1025 }
1026 if (NULL == entry) {
1027 GrPrintf("---- failed to create texture for cache [%d %d]\n",
1028 bitmap.width(), bitmap.height());
1029 }
1030 }
1031
1032 if (NULL != entry) {
1033 newTexture = entry->texture();
bsalomon@google.com8531c1c2011-01-13 19:52:45 +00001034 ctx->setTexture(0, newTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +00001035 if (texture) {
1036 *texture = newTexture;
1037 }
1038 // IMPORTANT: We can't allow another SkGpuDevice to get this
1039 // cache entry until this one is destroyed!
1040 if (forDeviceRenderTarget) {
1041 ctx->detachCachedTexture(entry);
1042 }
1043 }
1044 return (TexCache*)entry;
1045}
1046
1047void SkGpuDevice::unlockCachedTexture(TexCache* cache) {
1048 this->context()->unlockTexture((GrTextureEntry*)cache);
1049}
1050
reed@google.com7b201d22011-01-11 18:59:23 +00001051///////////////////////////////////////////////////////////////////////////////
1052
1053SkGpuDeviceFactory::SkGpuDeviceFactory(GrContext* context) : fContext(context) {
1054 context->ref();
1055}
1056
1057SkGpuDeviceFactory::~SkGpuDeviceFactory() {
1058 fContext->unref();
1059}
1060
1061SkDevice* SkGpuDeviceFactory::newDevice(SkCanvas*, SkBitmap::Config config,
1062 int width, int height,
1063 bool isOpaque, bool isLayer) {
1064 SkBitmap bm;
1065 bm.setConfig(config, width, height);
1066 bm.setIsOpaque(isOpaque);
1067 return new SkGpuDevice(fContext, bm, isLayer);
1068}
reed@google.comac10a2d2010-12-22 21:39:39 +00001069