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