blob: a4fb9869a0f11c2e0c60cf7db6ac0c1819c5504c [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;
85 device->context()->setTexture(texture);
86 } 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) {
336 fContext->setTexture(fTexture);
337 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
434 ctx->setSamplerState(samplerState);
435
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);
459 ctx->setTextureMatrix(grmat);
460
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() ?
510 GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit :
511 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
703 fContext->setSamplerState(sampler);
704
705 GrTexture* texture;
706 SkAutoCachedTexture act(this, bitmap, sampler, &texture);
707 if (NULL == texture) {
708 return;
709 }
710
711 GrVertexLayout layout = GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;
712
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
737 fContext->setTextureMatrix(GrMatrix::I());
738 // 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
749 ctx->setSamplerState(GrSamplerState::ClampNoFilter());
750 ctx->setTextureMatrix(GrMatrix::I());
751
752 GrPoint* vertex;
753 GrVertexLayout layout = GrGpu::kSeparateTexCoord_VertexLayoutBit;
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) {
840 layout |= GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;
841 }
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 }
858 int texOffset, colorOffset;
859 uint32_t stride = GrDrawTarget::VertexSizeAndOffsets(layout,
860 &texOffset,
861 &colorOffset);
862 for (int i = 0; i < vertexCount; ++i) {
863 GrPoint* p = (GrPoint*)((intptr_t)verts + i * stride);
864 p->set(SkScalarToGrScalar(vertices[i].fX),
865 SkScalarToGrScalar(vertices[i].fY));
866 if (texOffset > 0) {
867 GrPoint* t = (GrPoint*)((intptr_t)p + texOffset);
868 t->set(SkScalarToGrScalar(texs[i].fX),
869 SkScalarToGrScalar(texs[i].fY));
870 }
871 if (colorOffset > 0) {
872 uint32_t* color = (uint32_t*) ((intptr_t)p + colorOffset);
873 *color = SkGr::SkColor2GrColor(colors[i]);
874 }
875 }
876 }
877 if (indices) {
878 fContext->setIndexSourceToArray(indices);
879 fContext->drawIndexed(gVertexMode2PrimitiveType[vmode], 0, 0,
880 vertexCount, indexCount);
881 } else {
882 fContext->drawNonIndexed(gVertexMode2PrimitiveType[vmode],
883 0, vertexCount);
884 }
885 if (cache) {
886 this->unlockCachedTexture(cache);
887 }
888 if (releaseVerts) {
889 fContext->releaseReservedGeometry();
890 }
891}
892
893///////////////////////////////////////////////////////////////////////////////
894
895static void GlyphCacheAuxProc(void* data) {
896 delete (GrFontScaler*)data;
897}
898
899static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
900 void* auxData;
901 GrFontScaler* scaler = NULL;
902 if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
903 scaler = (GrFontScaler*)auxData;
904 }
905 if (NULL == scaler) {
906 scaler = new SkGrFontScaler(cache);
907 cache->setAuxProc(GlyphCacheAuxProc, scaler);
908 }
909 return scaler;
910}
911
912static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
913 SkFixed fx, SkFixed fy,
914 const SkGlyph& glyph) {
915 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
916
917 GrSkDrawProcs* procs = (GrSkDrawProcs*)state.fDraw->fProcs;
918
919 if (NULL == procs->fFontScaler) {
920 procs->fFontScaler = get_gr_font_scaler(state.fCache);
921 }
922 procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), fx, 0),
923 SkIntToFixed(SkFixedFloor(fx)), fy,
924 procs->fFontScaler);
925}
926
927SkDrawProcs* SkGpuDevice::initDrawForText(const SkPaint& paint,
928 GrTextContext* context) {
929
930 // deferred allocation
931 if (NULL == fDrawProcs) {
932 fDrawProcs = new GrSkDrawProcs;
933 fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
934 fDrawProcs->fContext = fContext;
935 }
936
937 // init our (and GL's) state
938 fDrawProcs->fTextContext = context;
939 fDrawProcs->fFontScaler = NULL;
940 return fDrawProcs;
941}
942
943void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
944 size_t byteLength, SkScalar x, SkScalar y,
945 const SkPaint& paint) {
946 CHECK_SHOULD_DRAW(draw);
947
948 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
949 // this guy will just call our drawPath()
950 draw.drawText((const char*)text, byteLength, x, y, paint);
951 } else {
952 SkAutoExtMatrix aem(draw.fExtMatrix);
953 SkDraw myDraw(draw);
954 sk_gr_set_paint(fContext, paint);
955 GrTextContext context(fContext, aem.extMatrix());
956 myDraw.fProcs = this->initDrawForText(paint, &context);
957 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
958 }
959}
960
961void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
962 size_t byteLength, const SkScalar pos[],
963 SkScalar constY, int scalarsPerPos,
964 const SkPaint& paint) {
965 CHECK_SHOULD_DRAW(draw);
966
967 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
968 // this guy will just call our drawPath()
969 draw.drawPosText((const char*)text, byteLength, pos, constY,
970 scalarsPerPos, paint);
971 } else {
972 SkAutoExtMatrix aem(draw.fExtMatrix);
973 SkDraw myDraw(draw);
974 sk_gr_set_paint(fContext, paint);
975 GrTextContext context(fContext, aem.extMatrix());
976 myDraw.fProcs = this->initDrawForText(paint, &context);
977 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
978 scalarsPerPos, paint);
979 }
980}
981
982void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
983 size_t len, const SkPath& path,
984 const SkMatrix* m, const SkPaint& paint) {
985 CHECK_SHOULD_DRAW(draw);
986
987 SkASSERT(draw.fDevice == this);
988 draw.drawTextOnPath((const char*)text, len, path, m, paint);
989}
990
991///////////////////////////////////////////////////////////////////////////////
992
993SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
994 const GrSamplerState& sampler,
995 GrTexture** texture,
996 bool forDeviceRenderTarget) {
997 GrContext* ctx = this->context();
998 uint32_t p0, p1;
999 if (forDeviceRenderTarget) {
1000 p0 = p1 = -1;
1001 } else {
1002 p0 = bitmap.getGenerationID();
1003 p1 = bitmap.pixelRefOffset();
1004 }
1005
1006 GrTexture* newTexture = NULL;
1007 GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
1008 GrTextureEntry* entry = ctx->findAndLockTexture(&key, sampler);
1009
1010 if (NULL == entry) {
1011
1012 if (forDeviceRenderTarget) {
1013 const GrGpu::TextureDesc desc = {
1014 GrGpu::kRenderTarget_TextureFlag,
1015 GrGpu::kNone_AALevel,
1016 bitmap.width(),
1017 bitmap.height(),
1018 SkGr::Bitmap2PixelConfig(bitmap)
1019 };
1020 entry = ctx->createAndLockTexture(&key, sampler, desc, NULL, 0);
1021
1022 } else {
1023 entry = sk_gr_create_bitmap_texture(ctx, &key, sampler, bitmap);
1024 }
1025 if (NULL == entry) {
1026 GrPrintf("---- failed to create texture for cache [%d %d]\n",
1027 bitmap.width(), bitmap.height());
1028 }
1029 }
1030
1031 if (NULL != entry) {
1032 newTexture = entry->texture();
1033 ctx->setTexture(newTexture);
1034 if (texture) {
1035 *texture = newTexture;
1036 }
1037 // IMPORTANT: We can't allow another SkGpuDevice to get this
1038 // cache entry until this one is destroyed!
1039 if (forDeviceRenderTarget) {
1040 ctx->detachCachedTexture(entry);
1041 }
1042 }
1043 return (TexCache*)entry;
1044}
1045
1046void SkGpuDevice::unlockCachedTexture(TexCache* cache) {
1047 this->context()->unlockTexture((GrTextureEntry*)cache);
1048}
1049
reed@google.com7b201d22011-01-11 18:59:23 +00001050///////////////////////////////////////////////////////////////////////////////
1051
1052SkGpuDeviceFactory::SkGpuDeviceFactory(GrContext* context) : fContext(context) {
1053 context->ref();
1054}
1055
1056SkGpuDeviceFactory::~SkGpuDeviceFactory() {
1057 fContext->unref();
1058}
1059
1060SkDevice* SkGpuDeviceFactory::newDevice(SkCanvas*, SkBitmap::Config config,
1061 int width, int height,
1062 bool isOpaque, bool isLayer) {
1063 SkBitmap bm;
1064 bm.setConfig(config, width, height);
1065 bm.setIsOpaque(isOpaque);
1066 return new SkGpuDevice(fContext, bm, isLayer);
1067}
reed@google.comac10a2d2010-12-22 21:39:39 +00001068