blob: 26463dba72e8f3c46d5c109c35d801b0a818113f [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
bsalomon@google.com1da07462011-03-10 14:51:57 +00002 Copyright 2011 Google Inc.
reed@google.comac10a2d2010-12-22 21:39:39 +00003
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"
22#include "SkGrTexturePixelRef.h"
23
Scroggo97c88c22011-05-11 14:05:25 +000024#include "SkColorFilter.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000025#include "SkDrawProcs.h"
26#include "SkGlyphCache.h"
reed@google.comc9aa5872011-04-05 21:05:37 +000027#include "SkUtils.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000028
29#define CACHE_LAYER_TEXTURES 1
30
31#if 0
32 extern bool (*gShouldDrawProc)();
33 #define CHECK_SHOULD_DRAW(draw) \
34 do { \
35 if (gShouldDrawProc && !gShouldDrawProc()) return; \
36 this->prepareRenderTarget(draw); \
37 } while (0)
38#else
39 #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
40#endif
41
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +000042// we use the same texture slot on GrPaint for bitmaps and shaders
43// (since drawBitmap, drawSprite, and drawDevice ignore skia's shader)
44enum {
45 kBitmapTextureIdx = 0,
46 kShaderTextureIdx = 0
47};
48
reed@google.comcde92112011-07-06 20:00:52 +000049
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +000050#define USE_GPU_BLUR false
51#define MAX_SIGMA 4.0f
reed@google.comac10a2d2010-12-22 21:39:39 +000052///////////////////////////////////////////////////////////////////////////////
53
54SkGpuDevice::SkAutoCachedTexture::
55 SkAutoCachedTexture(SkGpuDevice* device,
56 const SkBitmap& bitmap,
57 const GrSamplerState& sampler,
58 GrTexture** texture) {
59 GrAssert(texture);
60 fTex = NULL;
61 *texture = this->set(device, bitmap, sampler);
62}
63
64SkGpuDevice::SkAutoCachedTexture::SkAutoCachedTexture() {
65 fTex = NULL;
66}
67
68GrTexture* SkGpuDevice::SkAutoCachedTexture::set(SkGpuDevice* device,
69 const SkBitmap& bitmap,
70 const GrSamplerState& sampler) {
71 if (fTex) {
72 fDevice->unlockCachedTexture(fTex);
73 }
74 fDevice = device;
75 GrTexture* texture = (GrTexture*)bitmap.getTexture();
76 if (texture) {
77 // return the native texture
78 fTex = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000079 } else {
80 // look it up in our cache
bsalomon@google.come97f0852011-06-17 13:10:25 +000081 fTex = device->lockCachedTexture(bitmap, sampler, &texture);
reed@google.comac10a2d2010-12-22 21:39:39 +000082 }
83 return texture;
84}
85
86SkGpuDevice::SkAutoCachedTexture::~SkAutoCachedTexture() {
87 if (fTex) {
88 fDevice->unlockCachedTexture(fTex);
89 }
90}
91
92///////////////////////////////////////////////////////////////////////////////
93
94bool gDoTraceDraw;
95
96struct GrSkDrawProcs : public SkDrawProcs {
97public:
98 GrContext* fContext;
99 GrTextContext* fTextContext;
100 GrFontScaler* fFontScaler; // cached in the skia glyphcache
101};
102
103///////////////////////////////////////////////////////////////////////////////
104
bsalomon@google.com2e7b43d2011-01-18 20:57:22 +0000105GrRenderTarget* SkGpuDevice::Current3DApiRenderTarget() {
106 return (GrRenderTarget*) -1;
107}
108
reed@google.comaf951c92011-06-16 19:10:39 +0000109static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {
110 switch (config) {
111 case kAlpha_8_GrPixelConfig:
112 *isOpaque = false;
113 return SkBitmap::kA8_Config;
114 case kRGB_565_GrPixelConfig:
115 *isOpaque = true;
116 return SkBitmap::kRGB_565_Config;
117 case kRGBA_4444_GrPixelConfig:
118 *isOpaque = false;
119 return SkBitmap::kARGB_4444_Config;
120 case kRGBA_8888_GrPixelConfig:
121 case kRGBX_8888_GrPixelConfig:
122 *isOpaque = (kRGBX_8888_GrPixelConfig == config);
123 return SkBitmap::kARGB_8888_Config;
124 default:
125 *isOpaque = false;
126 return SkBitmap::kNo_Config;
127 }
128}
reed@google.comac10a2d2010-12-22 21:39:39 +0000129
reed@google.comaf951c92011-06-16 19:10:39 +0000130static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {
131 if (SkGpuDevice::Current3DApiRenderTarget() == renderTarget) {
132 renderTarget = context->createRenderTargetFrom3DApiState();
133 }
134 GrTexture* texture = renderTarget->asTexture();
135 GrPixelConfig config = texture ? texture->config() : kRGBA_8888_GrPixelConfig;
136
137 bool isOpaque;
138 SkBitmap bitmap;
139 bitmap.setConfig(grConfig2skConfig(config, &isOpaque),
140 renderTarget->width(), renderTarget->height());
141 bitmap.setIsOpaque(isOpaque);
142 return bitmap;
143}
144
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000145SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)
146: SkDevice(make_bitmap(context, texture->asRenderTarget())) {
147 this->initFromRenderTarget(context, texture->asRenderTarget());
148}
149
reed@google.comaf951c92011-06-16 19:10:39 +0000150SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)
151: SkDevice(make_bitmap(context, renderTarget)) {
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000152 this->initFromRenderTarget(context, renderTarget);
153}
154
155void SkGpuDevice::initFromRenderTarget(GrContext* context,
156 GrRenderTarget* renderTarget) {
reed@google.comaf951c92011-06-16 19:10:39 +0000157 fNeedPrepareRenderTarget = false;
158 fDrawProcs = NULL;
159
160 fContext = context;
161 fContext->ref();
162
163 fCache = NULL;
164 fTexture = NULL;
165 fRenderTarget = NULL;
166 fNeedClear = false;
167
168 if (Current3DApiRenderTarget() == renderTarget) {
169 fRenderTarget = fContext->createRenderTargetFrom3DApiState();
170 } else {
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000171 GrAssert(NULL != renderTarget);
reed@google.comaf951c92011-06-16 19:10:39 +0000172 fRenderTarget = renderTarget;
173 fRenderTarget->ref();
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000174 // if this RT is also a texture, hold a ref on it
175 fTexture = fRenderTarget->asTexture();
176 SkSafeRef(fTexture);
reed@google.comaf951c92011-06-16 19:10:39 +0000177 }
178
179 SkGrRenderTargetPixelRef* pr = new SkGrRenderTargetPixelRef(fRenderTarget);
180 this->setPixelRef(pr, 0)->unref();
181}
182
183SkGpuDevice::SkGpuDevice(GrContext* context, SkBitmap::Config config, int width,
bsalomon@google.come97f0852011-06-17 13:10:25 +0000184 int height, Usage usage)
reed@google.comaf951c92011-06-16 19:10:39 +0000185: SkDevice(config, width, height, false /*isOpaque*/) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000186 fNeedPrepareRenderTarget = false;
187 fDrawProcs = NULL;
188
reed@google.com7b201d22011-01-11 18:59:23 +0000189 fContext = context;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000190 fContext->ref();
reed@google.comac10a2d2010-12-22 21:39:39 +0000191
192 fCache = NULL;
193 fTexture = NULL;
194 fRenderTarget = NULL;
195 fNeedClear = false;
196
reed@google.comaf951c92011-06-16 19:10:39 +0000197 if (config != SkBitmap::kRGB_565_Config) {
198 config = SkBitmap::kARGB_8888_Config;
199 }
200 SkBitmap bm;
201 bm.setConfig(config, width, height);
reed@google.comac10a2d2010-12-22 21:39:39 +0000202
203#if CACHE_LAYER_TEXTURES
bsalomon@google.come97f0852011-06-17 13:10:25 +0000204 TexType type = (kSaveLayer_Usage == usage) ?
205 kSaveLayerDeviceRenderTarget_TexType :
206 kDeviceRenderTarget_TexType;
reed@google.comaf951c92011-06-16 19:10:39 +0000207 fCache = this->lockCachedTexture(bm, GrSamplerState::ClampNoFilter(),
bsalomon@google.come97f0852011-06-17 13:10:25 +0000208 &fTexture, type);
reed@google.comaf951c92011-06-16 19:10:39 +0000209 if (fCache) {
210 SkASSERT(NULL != fTexture);
211 SkASSERT(NULL != fTexture->asRenderTarget());
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000212 // hold a ref directly on fTexture (even though fCache has one) to match
213 // other constructor paths. Simplifies cleanup.
214 fTexture->ref();
reed@google.comaf951c92011-06-16 19:10:39 +0000215 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000216#else
reed@google.comaf951c92011-06-16 19:10:39 +0000217 const GrTextureDesc desc = {
218 kRenderTarget_GrTextureFlagBit,
219 kNone_GrAALevel,
220 width,
221 height,
222 SkGr::Bitmap2PixelConfig(bm)
223 };
reed@google.comac10a2d2010-12-22 21:39:39 +0000224
reed@google.comaf951c92011-06-16 19:10:39 +0000225 fTexture = fContext->createUncachedTexture(desc, NULL, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000226#endif
reed@google.comaf951c92011-06-16 19:10:39 +0000227 if (NULL != fTexture) {
228 fRenderTarget = fTexture->asRenderTarget();
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000229 fRenderTarget->ref();
reed@google.comac10a2d2010-12-22 21:39:39 +0000230
reed@google.comaf951c92011-06-16 19:10:39 +0000231 GrAssert(NULL != fRenderTarget);
reed@google.comac10a2d2010-12-22 21:39:39 +0000232
reed@google.comaf951c92011-06-16 19:10:39 +0000233 // we defer the actual clear until our gainFocus()
234 fNeedClear = true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000235
reed@google.comaf951c92011-06-16 19:10:39 +0000236 // wrap the bitmap with a pixelref to expose our texture
237 SkGrTexturePixelRef* pr = new SkGrTexturePixelRef(fTexture);
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000238 this->setPixelRef(pr, 0)->unref();
reed@google.comaf951c92011-06-16 19:10:39 +0000239 } else {
240 GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
241 width, height);
242 GrAssert(false);
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 }
244}
245
246SkGpuDevice::~SkGpuDevice() {
247 if (fDrawProcs) {
248 delete fDrawProcs;
249 }
250
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000251 SkSafeUnref(fTexture);
252 SkSafeUnref(fRenderTarget);
reed@google.comac10a2d2010-12-22 21:39:39 +0000253 if (fCache) {
254 GrAssert(NULL != fTexture);
255 GrAssert(fRenderTarget == fTexture->asRenderTarget());
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000256 fContext->unlockTexture((GrTextureEntry*)fCache);
bsalomon@google.comf9046fe2011-06-17 15:10:21 +0000257 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000258 fContext->unref();
reed@google.comac10a2d2010-12-22 21:39:39 +0000259}
260
reed@google.comac10a2d2010-12-22 21:39:39 +0000261intptr_t SkGpuDevice::getLayerTextureHandle() const {
262 if (fTexture) {
263 return fTexture->getTextureHandle();
264 } else {
265 return 0;
266 }
267}
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000268
reed@google.comac10a2d2010-12-22 21:39:39 +0000269///////////////////////////////////////////////////////////////////////////////
270
271void SkGpuDevice::makeRenderTargetCurrent() {
272 fContext->setRenderTarget(fRenderTarget);
273 fContext->flush(true);
274 fNeedPrepareRenderTarget = true;
275}
276
277///////////////////////////////////////////////////////////////////////////////
278
279bool SkGpuDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
280 SkIRect bounds;
281 bounds.set(0, 0, this->width(), this->height());
282 if (!bounds.intersect(srcRect)) {
283 return false;
284 }
285
286 const int w = bounds.width();
287 const int h = bounds.height();
288 SkBitmap tmp;
289 // note we explicitly specify our rowBytes to be snug (no gap between rows)
290 tmp.setConfig(SkBitmap::kARGB_8888_Config, w, h, w * 4);
291 if (!tmp.allocPixels()) {
292 return false;
293 }
294
Scroggo813c33c2011-04-07 20:56:21 +0000295 tmp.lockPixels();
reed@google.comac10a2d2010-12-22 21:39:39 +0000296
Scroggoeb176032011-04-07 21:11:49 +0000297 bool read = fContext->readRenderTargetPixels(fRenderTarget,
298 bounds.fLeft, bounds.fTop,
299 bounds.width(), bounds.height(),
300 kRGBA_8888_GrPixelConfig,
301 tmp.getPixels());
Scroggo813c33c2011-04-07 20:56:21 +0000302 tmp.unlockPixels();
303 if (!read) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000304 return false;
305 }
306
307 tmp.swap(*bitmap);
308 return true;
309}
310
311void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) {
312 SkAutoLockPixels alp(bitmap);
313 if (!bitmap.readyToDraw()) {
314 return;
315 }
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000316 GrPixelConfig config = SkGr::BitmapConfig2PixelConfig(bitmap.config(),
317 bitmap.isOpaque());
reed@google.comac10a2d2010-12-22 21:39:39 +0000318 fContext->setRenderTarget(fRenderTarget);
319 // we aren't setting the clip or matrix, so mark as dirty
320 // we don't need to set them for this call and don't have them anyway
321 fNeedPrepareRenderTarget = true;
322
323 fContext->writePixels(x, y, bitmap.width(), bitmap.height(),
324 config, bitmap.getPixels(), bitmap.rowBytes());
325}
326
327///////////////////////////////////////////////////////////////////////////////
328
329static void convert_matrixclip(GrContext* context, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000330 const SkClipStack& clipStack,
reed@google.com6f8f2922011-03-04 22:27:10 +0000331 const SkRegion& clipRegion,
332 const SkIPoint& origin) {
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000333 context->setMatrix(matrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000334
335 SkGrClipIterator iter;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000336 iter.reset(clipStack);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000337 const SkIRect& skBounds = clipRegion.getBounds();
338 GrRect bounds;
339 bounds.setLTRB(GrIntToScalar(skBounds.fLeft),
340 GrIntToScalar(skBounds.fTop),
341 GrIntToScalar(skBounds.fRight),
342 GrIntToScalar(skBounds.fBottom));
reed@google.com6f8f2922011-03-04 22:27:10 +0000343 GrClip grc(&iter, GrIntToScalar(-origin.x()), GrIntToScalar(-origin.y()),
344 &bounds);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000345 context->setClip(grc);
reed@google.comac10a2d2010-12-22 21:39:39 +0000346}
347
348// call this ever each draw call, to ensure that the context reflects our state,
349// and not the state from some other canvas/device
350void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) {
351 if (fNeedPrepareRenderTarget ||
bsalomon@google.com5782d712011-01-21 21:03:59 +0000352 fContext->getRenderTarget() != fRenderTarget) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000353
354 fContext->setRenderTarget(fRenderTarget);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000355 SkASSERT(draw.fClipStack);
356 convert_matrixclip(fContext, *draw.fMatrix,
reed@google.com6f8f2922011-03-04 22:27:10 +0000357 *draw.fClipStack, *draw.fClip, this->getOrigin());
reed@google.comac10a2d2010-12-22 21:39:39 +0000358 fNeedPrepareRenderTarget = false;
359 }
360}
361
reed@google.com46799cd2011-02-22 20:56:26 +0000362void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip,
363 const SkClipStack& clipStack) {
364 this->INHERITED::setMatrixClip(matrix, clip, clipStack);
bsalomon@google.coma7bf6e22011-04-11 19:20:46 +0000365 // We don't need to set them now because the context may not reflect this device.
366 fNeedPrepareRenderTarget = true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000367}
368
369void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000370 const SkRegion& clip, const SkClipStack& clipStack) {
371
reed@google.comac10a2d2010-12-22 21:39:39 +0000372 fContext->setRenderTarget(fRenderTarget);
373
bsalomon@google.comd302f142011-03-03 13:54:13 +0000374 this->INHERITED::gainFocus(canvas, matrix, clip, clipStack);
reed@google.comac10a2d2010-12-22 21:39:39 +0000375
reed@google.com6f8f2922011-03-04 22:27:10 +0000376 convert_matrixclip(fContext, matrix, clipStack, clip, this->getOrigin());
reed@google.comac10a2d2010-12-22 21:39:39 +0000377
378 if (fNeedClear) {
bsalomon@google.com31a58402011-04-27 21:00:02 +0000379 fContext->clear(NULL, 0x0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000380 fNeedClear = false;
381 }
382}
383
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000384bool SkGpuDevice::bindDeviceAsTexture(GrPaint* paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000385 if (NULL != fTexture) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000386 paint->setTexture(kBitmapTextureIdx, fTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000387 return true;
388 }
389 return false;
390}
391
392///////////////////////////////////////////////////////////////////////////////
393
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000394SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
395SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
396SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
397SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
398SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
399 shader_type_mismatch);
400SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 4, shader_type_mismatch);
reed@google.comac10a2d2010-12-22 21:39:39 +0000401
bsalomon@google.com5782d712011-01-21 21:03:59 +0000402static const GrSamplerState::SampleMode sk_bmp_type_to_sample_mode[] = {
403 (GrSamplerState::SampleMode) -1, // kNone_BitmapType
404 GrSamplerState::kNormal_SampleMode, // kDefault_BitmapType
405 GrSamplerState::kRadial_SampleMode, // kRadial_BitmapType
406 GrSamplerState::kSweep_SampleMode, // kSweep_BitmapType
407 GrSamplerState::kRadial2_SampleMode, // kTwoPointRadial_BitmapType
408};
409
410bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint,
411 bool justAlpha,
Scroggod757df22011-05-16 13:11:16 +0000412 GrPaint* grPaint,
413 bool constantColor) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000414
415 grPaint->fDither = skPaint.isDither();
416 grPaint->fAntiAlias = skPaint.isAntiAlias();
417
418 SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
419 SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
420
421 SkXfermode* mode = skPaint.getXfermode();
422 if (mode) {
423 if (!mode->asCoeff(&sm, &dm)) {
reed@google.com1a2e8d22011-01-21 22:08:29 +0000424 SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
bsalomon@google.com5782d712011-01-21 21:03:59 +0000425#if 0
426 return false;
427#endif
428 }
429 }
430 grPaint->fSrcBlendCoeff = sk_blend_to_grblend(sm);
431 grPaint->fDstBlendCoeff = sk_blend_to_grblend(dm);
432
433 if (justAlpha) {
434 uint8_t alpha = skPaint.getAlpha();
435 grPaint->fColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
Scroggod757df22011-05-16 13:11:16 +0000436 // justAlpha is currently set to true only if there is a texture,
437 // so constantColor should not also be true.
438 GrAssert(!constantColor);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000439 } else {
440 grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor());
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000441 grPaint->setTexture(kShaderTextureIdx, NULL);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000442 }
Scroggo97c88c22011-05-11 14:05:25 +0000443 SkColorFilter* colorFilter = skPaint.getColorFilter();
444 SkColor color;
445 SkXfermode::Mode filterMode;
446 if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
Scroggod757df22011-05-16 13:11:16 +0000447 if (!constantColor) {
448 grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
449 grPaint->fColorFilterXfermode = filterMode;
450 return true;
451 }
452 SkColor filtered = colorFilter->filterColor(skPaint.getColor());
453 grPaint->fColor = SkGr::SkColor2GrColor(filtered);
Scroggo97c88c22011-05-11 14:05:25 +0000454 }
Scroggod757df22011-05-16 13:11:16 +0000455 grPaint->resetColorFilter();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000456 return true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000457}
458
bsalomon@google.com5782d712011-01-21 21:03:59 +0000459bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint,
460 SkAutoCachedTexture* act,
461 const SkMatrix& ctm,
Scroggod757df22011-05-16 13:11:16 +0000462 GrPaint* grPaint,
463 bool constantColor) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000464
bsalomon@google.com5782d712011-01-21 21:03:59 +0000465 SkASSERT(NULL != act);
reed@google.comac10a2d2010-12-22 21:39:39 +0000466
bsalomon@google.com5782d712011-01-21 21:03:59 +0000467 SkShader* shader = skPaint.getShader();
reed@google.comac10a2d2010-12-22 21:39:39 +0000468 if (NULL == shader) {
Scroggod757df22011-05-16 13:11:16 +0000469 return this->skPaint2GrPaintNoShader(skPaint,
470 false,
471 grPaint,
472 constantColor);
473 } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000474 return false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000475 }
476
reed@google.comac10a2d2010-12-22 21:39:39 +0000477 SkBitmap bitmap;
478 SkMatrix matrix;
479 SkShader::TileMode tileModes[2];
480 SkScalar twoPointParams[3];
481 SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, &matrix,
482 tileModes, twoPointParams);
483
bsalomon@google.com5782d712011-01-21 21:03:59 +0000484 GrSamplerState::SampleMode sampleMode = sk_bmp_type_to_sample_mode[bmptype];
485 if (-1 == sampleMode) {
reed@google.com2be9e8b2011-07-06 21:18:09 +0000486 SkShader::GradientInfo info;
487 SkColor color;
488
489 info.fColors = &color;
490 info.fColorOffsets = NULL;
491 info.fColorCount = 1;
492 if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
493 SkPaint copy(skPaint);
494 copy.setShader(NULL);
bsalomon@google.comcd9cfd72011-07-08 16:55:04 +0000495 // modulate the paint alpha by the shader's solid color alpha
496 U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
497 copy.setColor(SkColorSetA(color, newA));
reed@google.com2be9e8b2011-07-06 21:18:09 +0000498 return this->skPaint2GrPaintNoShader(copy,
499 false,
500 grPaint,
501 constantColor);
502 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000503 return false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000504 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000505 GrSamplerState* sampler = grPaint->getTextureSampler(kShaderTextureIdx);
506 sampler->setSampleMode(sampleMode);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000507 if (skPaint.isFilterBitmap()) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000508 sampler->setFilter(GrSamplerState::kBilinear_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000509 } else {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000510 sampler->setFilter(GrSamplerState::kNearest_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000511 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000512 sampler->setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
513 sampler->setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
reed@google.comac10a2d2010-12-22 21:39:39 +0000514 if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000515 sampler->setRadial2Params(twoPointParams[0],
516 twoPointParams[1],
517 twoPointParams[2] < 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000518 }
519
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000520 GrTexture* texture = act->set(this, bitmap, *sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +0000521 if (NULL == texture) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000522 SkDebugf("Couldn't convert bitmap to texture.\n");
523 return false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000524 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000525 grPaint->setTexture(kShaderTextureIdx, texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000526
527 // since our texture coords will be in local space, we wack the texture
528 // matrix to map them back into 0...1 before we load it
529 SkMatrix localM;
530 if (shader->getLocalMatrix(&localM)) {
531 SkMatrix inverse;
532 if (localM.invert(&inverse)) {
533 matrix.preConcat(inverse);
534 }
535 }
536 if (SkShader::kDefault_BitmapType == bmptype) {
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000537 GrScalar sx = GrFixedToScalar(GR_Fixed1 / bitmap.width());
538 GrScalar sy = GrFixedToScalar(GR_Fixed1 / bitmap.height());
reed@google.comac10a2d2010-12-22 21:39:39 +0000539 matrix.postScale(sx, sy);
reed@google.comac10a2d2010-12-22 21:39:39 +0000540 } else if (SkShader::kRadial_BitmapType == bmptype) {
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000541 GrScalar s = GrFixedToScalar(GR_Fixed1 / bitmap.width());
reed@google.comac10a2d2010-12-22 21:39:39 +0000542 matrix.postScale(s, s);
543 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000544 sampler->setMatrix(matrix);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000545
546 return true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000547}
548
549///////////////////////////////////////////////////////////////////////////////
bsalomon@google.com5782d712011-01-21 21:03:59 +0000550
551class SkPositionSource {
552public:
553 SkPositionSource(const SkPoint* points, int count)
554 : fPoints(points), fCount(count) {}
555
556 int count() const { return fCount; }
557
558 void writeValue(int i, GrPoint* dstPosition) const {
559 SkASSERT(i < fCount);
560 dstPosition->fX = SkScalarToGrScalar(fPoints[i].fX);
561 dstPosition->fY = SkScalarToGrScalar(fPoints[i].fY);
562 }
563private:
bsalomon@google.com5782d712011-01-21 21:03:59 +0000564 const SkPoint* fPoints;
bsalomon@google.com19628322011-02-03 21:30:17 +0000565 int fCount;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000566};
567
568class SkTexCoordSource {
569public:
570 SkTexCoordSource(const SkPoint* coords)
571 : fCoords(coords) {}
572
573 void writeValue(int i, GrPoint* dstCoord) const {
574 dstCoord->fX = SkScalarToGrScalar(fCoords[i].fX);
575 dstCoord->fY = SkScalarToGrScalar(fCoords[i].fY);
576 }
577private:
578 const SkPoint* fCoords;
579};
580
581class SkColorSource {
582public:
583 SkColorSource(const SkColor* colors) : fColors(colors) {}
584
585 void writeValue(int i, GrColor* dstColor) const {
586 *dstColor = SkGr::SkColor2GrColor(fColors[i]);
587 }
588private:
589 const SkColor* fColors;
590};
591
592class SkIndexSource {
593public:
594 SkIndexSource(const uint16_t* indices, int count)
595 : fIndices(indices), fCount(count) {
596 }
597
598 int count() const { return fCount; }
599
600 void writeValue(int i, uint16_t* dstIndex) const {
601 *dstIndex = fIndices[i];
602 }
603
604private:
bsalomon@google.com5782d712011-01-21 21:03:59 +0000605 const uint16_t* fIndices;
bsalomon@google.com19628322011-02-03 21:30:17 +0000606 int fCount;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000607};
608
609///////////////////////////////////////////////////////////////////////////////
610
bsalomon@google.com6f7fbc92011-02-01 19:12:40 +0000611#if 0 // not currently being used so don't compile,
612
bsalomon@google.com5782d712011-01-21 21:03:59 +0000613// can be used for positions or texture coordinates
bsalomon@google.com6f7fbc92011-02-01 19:12:40 +0000614
bsalomon@google.com5782d712011-01-21 21:03:59 +0000615class SkRectFanSource {
616public:
617 SkRectFanSource(const SkRect& rect) : fRect(rect) {}
618
619 int count() const { return 4; }
620
621 void writeValue(int i, GrPoint* dstPoint) const {
622 SkASSERT(i < 4);
623 dstPoint->fX = SkScalarToGrScalar((i % 3) ? fRect.fRight :
624 fRect.fLeft);
625 dstPoint->fY = SkScalarToGrScalar((i < 2) ? fRect.fTop :
626 fRect.fBottom);
627 }
628private:
629 const SkRect& fRect;
630};
631
632class SkIRectFanSource {
633public:
634 SkIRectFanSource(const SkIRect& rect) : fRect(rect) {}
635
636 int count() const { return 4; }
637
638 void writeValue(int i, GrPoint* dstPoint) const {
639 SkASSERT(i < 4);
640 dstPoint->fX = (i % 3) ? GrIntToScalar(fRect.fRight) :
641 GrIntToScalar(fRect.fLeft);
642 dstPoint->fY = (i < 2) ? GrIntToScalar(fRect.fTop) :
643 GrIntToScalar(fRect.fBottom);
644 }
645private:
646 const SkIRect& fRect;
647};
648
649class SkMatRectFanSource {
650public:
651 SkMatRectFanSource(const SkRect& rect, const SkMatrix& matrix)
652 : fRect(rect), fMatrix(matrix) {}
653
654 int count() const { return 4; }
655
656 void writeValue(int i, GrPoint* dstPoint) const {
657 SkASSERT(i < 4);
658
659#if SK_SCALAR_IS_GR_SCALAR
660 fMatrix.mapXY((i % 3) ? fRect.fRight : fRect.fLeft,
661 (i < 2) ? fRect.fTop : fRect.fBottom,
662 (SkPoint*)dstPoint);
663#else
664 SkPoint dst;
665 fMatrix.mapXY((i % 3) ? fRect.fRight : fRect.fLeft,
666 (i < 2) ? fRect.fTop : fRect.fBottom,
667 &dst);
668 dstPoint->fX = SkScalarToGrScalar(dst.fX);
669 dstPoint->fY = SkScalarToGrScalar(dst.fY);
670#endif
671 }
672private:
673 const SkRect& fRect;
674 const SkMatrix& fMatrix;
675};
676
bsalomon@google.com6f7fbc92011-02-01 19:12:40 +0000677#endif
678
reed@google.comac10a2d2010-12-22 21:39:39 +0000679///////////////////////////////////////////////////////////////////////////////
680
bsalomon@google.com398109c2011-04-14 18:40:27 +0000681void SkGpuDevice::clear(SkColor color) {
bsalomon@google.com31a58402011-04-27 21:00:02 +0000682 fContext->clear(NULL, color);
bsalomon@google.com398109c2011-04-14 18:40:27 +0000683}
684
reed@google.comac10a2d2010-12-22 21:39:39 +0000685void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
686 CHECK_SHOULD_DRAW(draw);
687
bsalomon@google.com5782d712011-01-21 21:03:59 +0000688 GrPaint grPaint;
689 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +0000690 if (!this->skPaint2GrPaintShader(paint,
691 &act,
692 *draw.fMatrix,
693 &grPaint,
694 true)) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000695 return;
696 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000697
698 fContext->drawPaint(grPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000699}
700
701// must be in SkCanvas::PointMode order
bsalomon@google.comffca4002011-02-22 20:34:01 +0000702static const GrPrimitiveType gPointMode2PrimtiveType[] = {
703 kPoints_PrimitiveType,
704 kLines_PrimitiveType,
705 kLineStrip_PrimitiveType
reed@google.comac10a2d2010-12-22 21:39:39 +0000706};
707
708void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000709 size_t count, const SkPoint pts[], const SkPaint& paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000710 CHECK_SHOULD_DRAW(draw);
711
712 SkScalar width = paint.getStrokeWidth();
713 if (width < 0) {
714 return;
715 }
716
717 // we only handle hairlines here, else we let the SkDraw call our drawPath()
718 if (width > 0) {
719 draw.drawPoints(mode, count, pts, paint, true);
720 return;
721 }
722
bsalomon@google.com5782d712011-01-21 21:03:59 +0000723 GrPaint grPaint;
724 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +0000725 if (!this->skPaint2GrPaintShader(paint,
726 &act,
727 *draw.fMatrix,
728 &grPaint,
729 true)) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000730 return;
731 }
732
reed@google.comac10a2d2010-12-22 21:39:39 +0000733#if SK_SCALAR_IS_GR_SCALAR
bsalomon@google.com5782d712011-01-21 21:03:59 +0000734 fContext->drawVertices(grPaint,
735 gPointMode2PrimtiveType[mode],
736 count,
737 (GrPoint*)pts,
738 NULL,
739 NULL,
740 NULL,
741 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000742#else
bsalomon@google.com5782d712011-01-21 21:03:59 +0000743 fContext->drawCustomVertices(grPaint,
744 gPointMode2PrimtiveType[mode],
745 SkPositionSource(pts, count));
reed@google.comac10a2d2010-12-22 21:39:39 +0000746#endif
reed@google.comac10a2d2010-12-22 21:39:39 +0000747}
748
reed@google.comc9aa5872011-04-05 21:05:37 +0000749///////////////////////////////////////////////////////////////////////////////
750
reed@google.comac10a2d2010-12-22 21:39:39 +0000751void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
752 const SkPaint& paint) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000753 CHECK_SHOULD_DRAW(draw);
754
755 bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
756 SkScalar width = paint.getStrokeWidth();
757
758 /*
759 We have special code for hairline strokes, miter-strokes, and fills.
760 Anything else we just call our path code.
761 */
762 bool usePath = doStroke && width > 0 &&
763 paint.getStrokeJoin() != SkPaint::kMiter_Join;
764 // another reason we might need to call drawPath...
765 if (paint.getMaskFilter()) {
766 usePath = true;
767 }
reed@google.com67db6642011-05-26 11:46:35 +0000768 // until we aa rotated rects...
769 if (!usePath && paint.isAntiAlias() && !draw.fMatrix->rectStaysRect()) {
770 usePath = true;
771 }
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000772
773 if (usePath) {
774 SkPath path;
775 path.addRect(rect);
776 this->drawPath(draw, path, paint, NULL, true);
777 return;
778 }
779
780 GrPaint grPaint;
781 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +0000782 if (!this->skPaint2GrPaintShader(paint,
783 &act,
784 *draw.fMatrix,
785 &grPaint,
786 true)) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000787 return;
788 }
reed@google.com20efde72011-05-09 17:00:02 +0000789 fContext->drawRect(grPaint, rect, doStroke ? width : -1);
reed@google.comac10a2d2010-12-22 21:39:39 +0000790}
791
reed@google.com69302852011-02-16 18:08:07 +0000792#include "SkMaskFilter.h"
793#include "SkBounder.h"
794
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000795static GrPathFill skToGrFillType(SkPath::FillType fillType) {
796 switch (fillType) {
797 case SkPath::kWinding_FillType:
798 return kWinding_PathFill;
799 case SkPath::kEvenOdd_FillType:
800 return kEvenOdd_PathFill;
801 case SkPath::kInverseWinding_FillType:
802 return kInverseWinding_PathFill;
803 case SkPath::kInverseEvenOdd_FillType:
804 return kInverseEvenOdd_PathFill;
805 default:
806 SkDebugf("Unsupported path fill type\n");
807 return kHairLine_PathFill;
808 }
809}
810
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000811static void buildKernel(float sigma, float* kernel, int kernelWidth) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000812 int halfWidth = (kernelWidth - 1) / 2;
813 float sum = 0.0f;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000814 float denom = 1.0f / (2.0f * sigma * sigma);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000815 for (int i = 0; i < kernelWidth; ++i) {
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000816 float x = static_cast<float>(i - halfWidth);
817 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
818 // is dropped here, since we renormalize the kernel below.
819 kernel[i] = sk_float_exp(- x * x * denom);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000820 sum += kernel[i];
821 }
822 // Normalize the kernel
823 float scale = 1.0f / sum;
824 for (int i = 0; i < kernelWidth; ++i)
825 kernel[i] *= scale;
826}
827
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000828static void scaleRect(SkRect* rect, float scale) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000829 rect->fLeft *= scale;
830 rect->fTop *= scale;
831 rect->fRight *= scale;
832 rect->fBottom *= scale;
833}
834
835static bool drawWithGPUMaskFilter(GrContext* context, const SkPath& path,
836 SkMaskFilter* filter, const SkMatrix& matrix,
837 const SkRegion& clip, SkBounder* bounder,
838 GrPaint* grp) {
839 SkMaskFilter::BlurInfo info;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000840#if USE_GPU_BLUR
841 SkMaskFilter::BlurType blurType = filter->asABlur(&info);
842#else
843 SkMaskFilter::BlurType blurType = SkMaskFilter::kNone_BlurType;
844#endif
845 if (SkMaskFilter::kNone_BlurType == blurType) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000846 return false;
847 }
848 float radius = info.fIgnoreTransform ? info.fRadius
849 : matrix.mapRadius(info.fRadius);
850 float sigma = radius * 0.6666f;
851 SkRect srcRect = path.getBounds();
852
853 int scaleFactor = 1;
854
855 while (sigma > MAX_SIGMA) {
856 scaleFactor *= 2;
857 sigma *= 0.5f;
858 }
859 scaleRect(&srcRect, 1.0f / scaleFactor);
860 int halfWidth = static_cast<int>(sigma * 3.0f);
861 int kernelWidth = halfWidth * 2 + 1;
862
863 SkIRect srcIRect;
864 srcRect.roundOut(&srcIRect);
865 srcRect.set(srcIRect);
866 srcRect.inset(-halfWidth, -halfWidth);
867
868 scaleRect(&srcRect, scaleFactor);
869 SkRect finalRect = srcRect;
870
871 SkIRect finalIRect;
872 finalRect.roundOut(&finalIRect);
873 if (clip.quickReject(finalIRect)) {
874 return false;
875 }
876 if (bounder && !bounder->doIRect(finalIRect)) {
877 return false;
878 }
879 GrPoint offset = GrPoint::Make(-srcRect.fLeft, -srcRect.fTop);
880 srcRect.offset(-srcRect.fLeft, -srcRect.fTop);
881 const GrTextureDesc desc = {
882 kRenderTarget_GrTextureFlagBit,
883 kNone_GrAALevel,
884 srcRect.width(),
885 srcRect.height(),
886 // We actually only need A8, but it often isn't supported as a
887 // render target
888 kRGBA_8888_GrPixelConfig
889 };
890
891 GrTextureEntry* srcEntry = context->findApproximateKeylessTexture(desc);
892 GrTextureEntry* dstEntry = context->findApproximateKeylessTexture(desc);
893 if (NULL == srcEntry || NULL == dstEntry) {
894 return false;
895 }
896 GrTexture* srcTexture = srcEntry->texture();
897 GrTexture* dstTexture = dstEntry->texture();
898 if (NULL == srcTexture || NULL == dstTexture) {
899 return false;
900 }
901 GrRenderTarget* oldRenderTarget = context->getRenderTarget();
902 context->setRenderTarget(dstTexture->asRenderTarget());
903 // FIXME: could just clear bounds
904 context->clear(NULL, 0);
905 GrMatrix transM;
906 GrPaint tempPaint;
907 tempPaint.reset();
908
909 GrAutoMatrix avm(context, GrMatrix::I());
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000910 tempPaint.fAntiAlias = grp->fAntiAlias;
911 if (tempPaint.fAntiAlias) {
912 // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
913 // blend coeff of zero requires dual source blending support in order
914 // to properly blend partially covered pixels. This means the AA
915 // code path may not be taken. So we use a dst blend coeff of ISA. We
916 // could special case AA draws to a dst surface with known alpha=0 to
917 // use a zero dst coeff when dual source blending isn't available.
918 tempPaint.fSrcBlendCoeff = kOne_BlendCoeff;
919 tempPaint.fDstBlendCoeff = kISC_BlendCoeff;
920 }
921 // Draw hard shadow to dstTexture with path topleft at origin 0,0.
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000922 context->drawPath(tempPaint, path, skToGrFillType(path.getFillType()), &offset);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000923 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000924
925 GrMatrix sampleM;
926 sampleM.setScale(GR_Scalar1 / srcTexture->width(),
927 GR_Scalar1 / srcTexture->height());
928 GrPaint paint;
929 paint.reset();
930 paint.getTextureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter);
931 paint.getTextureSampler(0)->setMatrix(sampleM);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000932 GrTextureEntry* origEntry = NULL;
933 if (blurType != SkMaskFilter::kNormal_BlurType) {
934 // Stash away a copy of the unblurred image.
935 origEntry = context->findApproximateKeylessTexture(desc);
936 if (NULL == origEntry) {
937 return false;
938 }
939 context->setRenderTarget(origEntry->texture()->asRenderTarget());
940 paint.setTexture(0, srcTexture);
941 context->drawRect(paint, srcRect);
942 }
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000943 for (int i = 1; i < scaleFactor; i *= 2) {
944 context->setRenderTarget(dstTexture->asRenderTarget());
945 SkRect dstRect(srcRect);
946 scaleRect(&dstRect, 0.5f);
947 // Clear out 1 pixel border for linear filtering.
948 // FIXME: for now, clear everything
949 context->clear(NULL, 0);
950 paint.setTexture(0, srcTexture);
951 context->drawRectToRect(paint, dstRect, srcRect);
952 srcRect = dstRect;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000953 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000954 }
955
956 SkAutoTMalloc<float> kernelStorage(kernelWidth);
957 float* kernel = kernelStorage.get();
958 buildKernel(sigma, kernel, kernelWidth);
959
960 float imageIncrementX[2] = {1.0f / srcTexture->width(), 0.0f};
961 context->setRenderTarget(dstTexture->asRenderTarget());
962 context->clear(NULL, 0);
963 context->convolveRect(srcTexture, srcRect, imageIncrementX, kernel,
964 kernelWidth);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000965 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000966
967 float imageIncrementY[2] = {0.0f, 1.0f / srcTexture->height()};
968 context->setRenderTarget(dstTexture->asRenderTarget());
969 context->clear(NULL, 0);
970 context->convolveRect(srcTexture, srcRect, imageIncrementY, kernel,
971 kernelWidth);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000972 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000973
974 if (scaleFactor > 1) {
975 // FIXME: This should be mitchell, not bilinear.
976 paint.getTextureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter);
977 sampleM.setScale(GR_Scalar1 / srcTexture->width(),
978 GR_Scalar1 / srcTexture->height());
979 paint.getTextureSampler(0)->setMatrix(sampleM);
980 context->setRenderTarget(dstTexture->asRenderTarget());
981 // Clear out 2 pixel border for bicubic filtering.
982 // FIXME: for now, clear everything
983 context->clear(NULL, 0);
984 paint.setTexture(0, srcTexture);
985 SkRect dstRect(srcRect);
986 scaleRect(&dstRect, scaleFactor);
987 context->drawRectToRect(paint, dstRect, srcRect);
988 srcRect = dstRect;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000989 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000990 }
991
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000992 if (blurType != SkMaskFilter::kNormal_BlurType) {
993 GrTexture* origTexture = origEntry->texture();
994 paint.getTextureSampler(0)->setFilter(GrSamplerState::kNearest_Filter);
995 sampleM.setScale(GR_Scalar1 / origTexture->width(),
996 GR_Scalar1 / origTexture->height());
997 paint.getTextureSampler(0)->setMatrix(sampleM);
998 // Blend origTexture over srcTexture.
999 context->setRenderTarget(srcTexture->asRenderTarget());
1000 paint.setTexture(0, origTexture);
1001 if (SkMaskFilter::kInner_BlurType == blurType) {
1002 // inner: dst = dst * src
1003 paint.fSrcBlendCoeff = kDC_BlendCoeff;
1004 paint.fDstBlendCoeff = kZero_BlendCoeff;
1005 } else if (SkMaskFilter::kSolid_BlurType == blurType) {
1006 // solid: dst = src + dst - src * dst
1007 // = (1 - dst) * src + 1 * dst
1008 paint.fSrcBlendCoeff = kIDC_BlendCoeff;
1009 paint.fDstBlendCoeff = kOne_BlendCoeff;
1010 } else if (SkMaskFilter::kOuter_BlurType == blurType) {
1011 // outer: dst = dst * (1 - src)
1012 // = 0 * src + (1 - src) * dst
1013 paint.fSrcBlendCoeff = kZero_BlendCoeff;
1014 paint.fDstBlendCoeff = kISC_BlendCoeff;
1015 }
1016 context->drawRect(paint, srcRect);
1017 }
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001018 context->setRenderTarget(oldRenderTarget);
1019
1020 if (grp->hasTextureOrMask()) {
1021 GrMatrix inverse;
1022 if (!matrix.invert(&inverse)) {
1023 return false;
1024 }
1025 grp->preConcatActiveSamplerMatrices(inverse);
1026 }
1027
1028 static const int MASK_IDX = GrPaint::kMaxMasks - 1;
1029 // we assume the last mask index is available for use
1030 GrAssert(NULL == grp->getMask(MASK_IDX));
1031 grp->setMask(MASK_IDX, srcTexture);
1032 grp->getMaskSampler(MASK_IDX)->setClampNoFilter();
1033
1034 GrMatrix m;
1035 m.setTranslate(-finalRect.fLeft, -finalRect.fTop);
1036 m.postIDiv(srcTexture->width(), srcTexture->height());
1037 grp->getMaskSampler(MASK_IDX)->setMatrix(m);
1038 context->drawRect(*grp, finalRect);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +00001039 // FIXME: these unlockTexture() calls could be more safely done with
1040 // an RAII guard class.
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001041 context->unlockTexture(srcEntry);
1042 context->unlockTexture(dstEntry);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +00001043 if (origEntry) {
1044 context->unlockTexture(origEntry);
1045 }
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001046 return true;
1047}
1048
reed@google.com69302852011-02-16 18:08:07 +00001049static bool drawWithMaskFilter(GrContext* context, const SkPath& path,
1050 SkMaskFilter* filter, const SkMatrix& matrix,
1051 const SkRegion& clip, SkBounder* bounder,
1052 GrPaint* grp) {
1053 SkMask srcM, dstM;
1054
1055 if (!SkDraw::DrawToMask(path, &clip.getBounds(), filter, &matrix, &srcM,
1056 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
1057 return false;
1058 }
1059
1060 SkAutoMaskImage autoSrc(&srcM, false);
1061
1062 if (!filter->filterMask(&dstM, srcM, matrix, NULL)) {
1063 return false;
1064 }
1065 // this will free-up dstM when we're done (allocated in filterMask())
1066 SkAutoMaskImage autoDst(&dstM, false);
1067
1068 if (clip.quickReject(dstM.fBounds)) {
1069 return false;
1070 }
1071 if (bounder && !bounder->doIRect(dstM.fBounds)) {
1072 return false;
1073 }
1074
1075 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
1076 // the current clip (and identity matrix) and grpaint settings
1077
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001078 // used to compute inverse view, if necessary
1079 GrMatrix ivm = context->getMatrix();
1080
reed@google.com0c219b62011-02-16 21:31:18 +00001081 GrAutoMatrix avm(context, GrMatrix::I());
reed@google.com69302852011-02-16 18:08:07 +00001082
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001083 const GrTextureDesc desc = {
1084 kNone_GrTextureFlags,
1085 kNone_GrAALevel,
reed@google.com69302852011-02-16 18:08:07 +00001086 dstM.fBounds.width(),
1087 dstM.fBounds.height(),
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001088 kAlpha_8_GrPixelConfig
reed@google.com69302852011-02-16 18:08:07 +00001089 };
1090
1091 GrTexture* texture = context->createUncachedTexture(desc, dstM.fImage,
1092 dstM.fRowBytes);
1093 if (NULL == texture) {
1094 return false;
1095 }
1096
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001097 if (grp->hasTextureOrMask() && ivm.invert(&ivm)) {
1098 grp->preConcatActiveSamplerMatrices(ivm);
1099 }
1100
1101 static const int MASK_IDX = GrPaint::kMaxMasks - 1;
1102 // we assume the last mask index is available for use
1103 GrAssert(NULL == grp->getMask(MASK_IDX));
1104 grp->setMask(MASK_IDX, texture);
reed@google.com0c219b62011-02-16 21:31:18 +00001105 texture->unref();
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001106 grp->getMaskSampler(MASK_IDX)->setClampNoFilter();
reed@google.com69302852011-02-16 18:08:07 +00001107
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001108 GrRect d;
1109 d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
reed@google.com0c219b62011-02-16 21:31:18 +00001110 GrIntToScalar(dstM.fBounds.fTop),
1111 GrIntToScalar(dstM.fBounds.fRight),
1112 GrIntToScalar(dstM.fBounds.fBottom));
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001113
1114 GrMatrix m;
1115 m.setTranslate(-dstM.fBounds.fLeft, -dstM.fBounds.fTop);
1116 m.postIDiv(dstM.fBounds.width(), dstM.fBounds.height());
1117 grp->getMaskSampler(MASK_IDX)->setMatrix(m);
1118
1119 context->drawRect(*grp, d);
reed@google.com69302852011-02-16 18:08:07 +00001120 return true;
1121}
reed@google.com69302852011-02-16 18:08:07 +00001122
reed@google.com0c219b62011-02-16 21:31:18 +00001123void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
reed@google.comac10a2d2010-12-22 21:39:39 +00001124 const SkPaint& paint, const SkMatrix* prePathMatrix,
1125 bool pathIsMutable) {
1126 CHECK_SHOULD_DRAW(draw);
1127
bsalomon@google.com5782d712011-01-21 21:03:59 +00001128 GrPaint grPaint;
1129 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +00001130 if (!this->skPaint2GrPaintShader(paint,
1131 &act,
1132 *draw.fMatrix,
1133 &grPaint,
1134 true)) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001135 return;
1136 }
1137
reed@google.com0c219b62011-02-16 21:31:18 +00001138 // BEGIN lift from SkDraw::drawPath()
1139
1140 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
1141 bool doFill = true;
1142 SkPath tmpPath;
reed@google.comac10a2d2010-12-22 21:39:39 +00001143
1144 if (prePathMatrix) {
reed@google.come3445642011-02-16 23:20:39 +00001145 SkPath* result = pathPtr;
reed@google.com0c219b62011-02-16 21:31:18 +00001146
reed@google.come3445642011-02-16 23:20:39 +00001147 if (!pathIsMutable) {
1148 result = &tmpPath;
1149 pathIsMutable = true;
reed@google.comac10a2d2010-12-22 21:39:39 +00001150 }
reed@google.come3445642011-02-16 23:20:39 +00001151 // should I push prePathMatrix on our MV stack temporarily, instead
1152 // of applying it here? See SkDraw.cpp
1153 pathPtr->transform(*prePathMatrix, result);
1154 pathPtr = result;
reed@google.comac10a2d2010-12-22 21:39:39 +00001155 }
reed@google.com0c219b62011-02-16 21:31:18 +00001156 // at this point we're done with prePathMatrix
1157 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.comac10a2d2010-12-22 21:39:39 +00001158
bsalomon@google.com04de7822011-03-25 18:04:43 +00001159 // This "if" is not part of the SkDraw::drawPath() lift.
1160 // When we get a 1.0 wide stroke we hairline stroke it instead of creating
1161 // a new stroked-path. This is motivated by canvas2D sites that draw
1162 // lines as 1.0 wide stroked paths. We can consider doing an alpha-modulated-
1163 // hairline for width < 1.0 when AA is enabled.
1164 static const int gMatrixMask = ~(SkMatrix::kIdentity_Mask |
1165 SkMatrix::kTranslate_Mask);
1166 if (!paint.getPathEffect() &&
1167 SkPaint::kStroke_Style == paint.getStyle() &&
1168 !(draw.fMatrix->getType() & gMatrixMask) &&
1169 SK_Scalar1 == paint.getStrokeWidth()) {
1170 doFill = false;
1171 }
1172
1173 if (doFill && (paint.getPathEffect() ||
1174 paint.getStyle() != SkPaint::kFill_Style)) {
reed@google.com0c219b62011-02-16 21:31:18 +00001175 doFill = paint.getFillPath(*pathPtr, &tmpPath);
1176 pathPtr = &tmpPath;
1177 }
1178
1179 // END lift from SkDraw::drawPath()
1180
reed@google.com69302852011-02-16 18:08:07 +00001181 if (paint.getMaskFilter()) {
reed@google.com0c219b62011-02-16 21:31:18 +00001182 // avoid possibly allocating a new path in transform if we can
1183 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1184
1185 // transform the path into device space
reed@google.come3445642011-02-16 23:20:39 +00001186 pathPtr->transform(*draw.fMatrix, devPathPtr);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +00001187 if (!drawWithGPUMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
1188 *draw.fMatrix, *draw.fClip, draw.fBounder,
1189 &grPaint)) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001190 drawWithMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
1191 *draw.fMatrix, *draw.fClip, draw.fBounder,
1192 &grPaint);
1193 }
reed@google.com69302852011-02-16 18:08:07 +00001194 return;
1195 }
reed@google.com69302852011-02-16 18:08:07 +00001196
bsalomon@google.comffca4002011-02-22 20:34:01 +00001197 GrPathFill fill = kHairLine_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001198
reed@google.com0c219b62011-02-16 21:31:18 +00001199 if (doFill) {
1200 switch (pathPtr->getFillType()) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001201 case SkPath::kWinding_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001202 fill = kWinding_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001203 break;
1204 case SkPath::kEvenOdd_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001205 fill = kEvenOdd_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001206 break;
1207 case SkPath::kInverseWinding_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001208 fill = kInverseWinding_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001209 break;
1210 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001211 fill = kInverseEvenOdd_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001212 break;
1213 default:
bsalomon@google.com5782d712011-01-21 21:03:59 +00001214 SkDebugf("Unsupported path fill type\n");
reed@google.comac10a2d2010-12-22 21:39:39 +00001215 return;
1216 }
1217 }
1218
reed@google.com07f3ee12011-05-16 17:21:57 +00001219 fContext->drawPath(grPaint, *pathPtr, fill);
reed@google.comac10a2d2010-12-22 21:39:39 +00001220}
1221
reed@google.comac10a2d2010-12-22 21:39:39 +00001222void SkGpuDevice::drawBitmap(const SkDraw& draw,
1223 const SkBitmap& bitmap,
1224 const SkIRect* srcRectPtr,
1225 const SkMatrix& m,
1226 const SkPaint& paint) {
1227 CHECK_SHOULD_DRAW(draw);
1228
1229 SkIRect srcRect;
1230 if (NULL == srcRectPtr) {
1231 srcRect.set(0, 0, bitmap.width(), bitmap.height());
1232 } else {
1233 srcRect = *srcRectPtr;
1234 }
1235
junov@google.comd935cfb2011-06-27 20:48:23 +00001236 if (paint.getMaskFilter()){
epoger@google.com9ef2d832011-07-01 21:12:20 +00001237 SkBitmap tmp; // storage if we need a subset of bitmap
junov@google.comd935cfb2011-06-27 20:48:23 +00001238 const SkBitmap* bitmapPtr = &bitmap;
epoger@google.com9ef2d832011-07-01 21:12:20 +00001239 if (srcRectPtr) {
1240 if (!bitmap.extractSubset(&tmp, srcRect)) {
1241 return; // extraction failed
1242 }
1243 bitmapPtr = &tmp;
junov@google.comd935cfb2011-06-27 20:48:23 +00001244 }
1245 SkPaint paintWithTexture(paint);
1246 paintWithTexture.setShader(SkShader::CreateBitmapShader( *bitmapPtr,
1247 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
1248 paintWithTexture.getShader()->setLocalMatrix(m);
1249
1250 SkRect ScalarRect;
epoger@google.com9ef2d832011-07-01 21:12:20 +00001251 ScalarRect.set(srcRect);
junov@google.comd935cfb2011-06-27 20:48:23 +00001252
epoger@google.com9ef2d832011-07-01 21:12:20 +00001253 if (m.rectStaysRect()) {
1254 // Preferred drawing method, optimized for rectangles
1255 m.mapRect(&ScalarRect);
1256 this->drawRect(draw, ScalarRect, paintWithTexture);
1257 } else {
1258 // Slower drawing method, for warped or rotated rectangles
1259 SkPath path;
1260 path.addRect(ScalarRect);
1261 path.transform(m);
1262 this->drawPath(draw, path, paintWithTexture, NULL, true);
1263 }
junov@google.comd935cfb2011-06-27 20:48:23 +00001264 return;
1265 }
1266
bsalomon@google.com5782d712011-01-21 21:03:59 +00001267 GrPaint grPaint;
Scroggod757df22011-05-16 13:11:16 +00001268 if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001269 return;
1270 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001271 GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001272 if (paint.isFilterBitmap()) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001273 sampler->setFilter(GrSamplerState::kBilinear_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001274 } else {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001275 sampler->setFilter(GrSamplerState::kNearest_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001276 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001277
bsalomon@google.com91958362011-06-13 17:58:13 +00001278 const int maxTextureSize = fContext->getMaxTextureSize();
1279 if (bitmap.getTexture() || (bitmap.width() <= maxTextureSize &&
1280 bitmap.height() <= maxTextureSize)) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001281 // take the fast case
bsalomon@google.com5782d712011-01-21 21:03:59 +00001282 this->internalDrawBitmap(draw, bitmap, srcRect, m, &grPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +00001283 return;
1284 }
1285
1286 // undo the translate done by SkCanvas
1287 int DX = SkMax32(0, srcRect.fLeft);
1288 int DY = SkMax32(0, srcRect.fTop);
1289 // compute clip bounds in local coordinates
1290 SkIRect clipRect;
1291 {
1292 SkRect r;
1293 r.set(draw.fClip->getBounds());
1294 SkMatrix matrix, inverse;
1295 matrix.setConcat(*draw.fMatrix, m);
1296 if (!matrix.invert(&inverse)) {
1297 return;
1298 }
1299 inverse.mapRect(&r);
1300 r.roundOut(&clipRect);
1301 // apply the canvas' translate to our local clip
1302 clipRect.offset(DX, DY);
1303 }
1304
bsalomon@google.com91958362011-06-13 17:58:13 +00001305 int nx = bitmap.width() / maxTextureSize;
1306 int ny = bitmap.height() / maxTextureSize;
reed@google.comac10a2d2010-12-22 21:39:39 +00001307 for (int x = 0; x <= nx; x++) {
1308 for (int y = 0; y <= ny; y++) {
1309 SkIRect tileR;
bsalomon@google.com91958362011-06-13 17:58:13 +00001310 tileR.set(x * maxTextureSize, y * maxTextureSize,
1311 (x + 1) * maxTextureSize, (y + 1) * maxTextureSize);
reed@google.comac10a2d2010-12-22 21:39:39 +00001312 if (!SkIRect::Intersects(tileR, clipRect)) {
1313 continue;
1314 }
1315
1316 SkIRect srcR = tileR;
1317 if (!srcR.intersect(srcRect)) {
1318 continue;
1319 }
1320
1321 SkBitmap tmpB;
1322 if (bitmap.extractSubset(&tmpB, tileR)) {
1323 // now offset it to make it "local" to our tmp bitmap
1324 srcR.offset(-tileR.fLeft, -tileR.fTop);
1325
1326 SkMatrix tmpM(m);
1327 {
1328 int dx = tileR.fLeft - DX + SkMax32(0, srcR.fLeft);
1329 int dy = tileR.fTop - DY + SkMax32(0, srcR.fTop);
1330 tmpM.preTranslate(SkIntToScalar(dx), SkIntToScalar(dy));
1331 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001332 this->internalDrawBitmap(draw, tmpB, srcR, tmpM, &grPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +00001333 }
1334 }
1335 }
1336}
1337
1338/*
1339 * This is called by drawBitmap(), which has to handle images that may be too
1340 * large to be represented by a single texture.
1341 *
bsalomon@google.com5782d712011-01-21 21:03:59 +00001342 * internalDrawBitmap assumes that the specified bitmap will fit in a texture
1343 * and that non-texture portion of the GrPaint has already been setup.
reed@google.comac10a2d2010-12-22 21:39:39 +00001344 */
1345void SkGpuDevice::internalDrawBitmap(const SkDraw& draw,
1346 const SkBitmap& bitmap,
1347 const SkIRect& srcRect,
1348 const SkMatrix& m,
bsalomon@google.com5782d712011-01-21 21:03:59 +00001349 GrPaint* grPaint) {
bsalomon@google.com91958362011-06-13 17:58:13 +00001350 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
1351 bitmap.height() <= fContext->getMaxTextureSize());
reed@google.comac10a2d2010-12-22 21:39:39 +00001352
reed@google.com9c49bc32011-07-07 13:42:37 +00001353 SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
reed@google.comac10a2d2010-12-22 21:39:39 +00001354 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
reed@google.com9c49bc32011-07-07 13:42:37 +00001355 SkDebugf("nothing to draw\n");
reed@google.comac10a2d2010-12-22 21:39:39 +00001356 return;
1357 }
1358
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001359 GrSamplerState* sampler = grPaint->getTextureSampler(kBitmapTextureIdx);
1360
1361 sampler->setWrapX(GrSamplerState::kClamp_WrapMode);
1362 sampler->setWrapY(GrSamplerState::kClamp_WrapMode);
1363 sampler->setSampleMode(GrSamplerState::kNormal_SampleMode);
1364 sampler->setMatrix(GrMatrix::I());
reed@google.comac10a2d2010-12-22 21:39:39 +00001365
1366 GrTexture* texture;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001367 SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
reed@google.comac10a2d2010-12-22 21:39:39 +00001368 if (NULL == texture) {
1369 return;
1370 }
1371
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001372 grPaint->setTexture(kShaderTextureIdx, texture);
reed@google.com46799cd2011-02-22 20:56:26 +00001373
reed@google.com20efde72011-05-09 17:00:02 +00001374 GrRect dstRect = SkRect::MakeWH(GrIntToScalar(srcRect.width()),
1375 GrIntToScalar(srcRect.height()));
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001376 GrRect paintRect;
junov@google.com6acc9b32011-05-16 18:32:07 +00001377 paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()),
1378 GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()),
1379 GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()),
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001380 GrFixedToScalar((srcRect.fBottom << 16) / bitmap.height()));
reed@google.comac10a2d2010-12-22 21:39:39 +00001381
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001382 if (GrSamplerState::kNearest_Filter != sampler->getFilter() &&
junov@google.com6acc9b32011-05-16 18:32:07 +00001383 (srcRect.width() < bitmap.width() ||
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001384 srcRect.height() < bitmap.height())) {
junov@google.com6acc9b32011-05-16 18:32:07 +00001385 // If drawing a subrect of the bitmap and filtering is enabled,
1386 // use a constrained texture domain to avoid color bleeding
1387 GrScalar left, top, right, bottom;
1388 if (srcRect.width() > 1) {
1389 GrScalar border = GR_ScalarHalf / bitmap.width();
1390 left = paintRect.left() + border;
1391 right = paintRect.right() - border;
1392 } else {
1393 left = right = GrScalarHalf(paintRect.left() + paintRect.right());
1394 }
1395 if (srcRect.height() > 1) {
1396 GrScalar border = GR_ScalarHalf / bitmap.height();
1397 top = paintRect.top() + border;
1398 bottom = paintRect.bottom() - border;
1399 } else {
1400 top = bottom = GrScalarHalf(paintRect.top() + paintRect.bottom());
1401 }
1402 GrRect textureDomain;
1403 textureDomain.setLTRB(left, top, right, bottom);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001404 sampler->setTextureDomain(textureDomain);
junov@google.com6acc9b32011-05-16 18:32:07 +00001405 }
1406
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001407 fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
reed@google.comac10a2d2010-12-22 21:39:39 +00001408}
1409
1410void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
1411 int left, int top, const SkPaint& paint) {
1412 CHECK_SHOULD_DRAW(draw);
1413
1414 SkAutoLockPixels alp(bitmap);
1415 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
1416 return;
1417 }
1418
bsalomon@google.com5782d712011-01-21 21:03:59 +00001419 GrPaint grPaint;
Scroggod757df22011-05-16 13:11:16 +00001420 if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001421 return;
1422 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001423
bsalomon@google.com5782d712011-01-21 21:03:59 +00001424 GrAutoMatrix avm(fContext, GrMatrix::I());
1425
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001426 GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001427
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001428 GrTexture* texture;
1429 sampler->setClampNoFilter();
1430 SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
1431
1432 grPaint.setTexture(kBitmapTextureIdx, texture);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001433
bsalomon@google.com5782d712011-01-21 21:03:59 +00001434 fContext->drawRectToRect(grPaint,
reed@google.com20efde72011-05-09 17:00:02 +00001435 GrRect::MakeXYWH(GrIntToScalar(left),
1436 GrIntToScalar(top),
1437 GrIntToScalar(bitmap.width()),
1438 GrIntToScalar(bitmap.height())),
1439 GrRect::MakeWH(GR_Scalar1, GR_Scalar1));
reed@google.comac10a2d2010-12-22 21:39:39 +00001440}
1441
1442void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
1443 int x, int y, const SkPaint& paint) {
1444 CHECK_SHOULD_DRAW(draw);
1445
bsalomon@google.com5782d712011-01-21 21:03:59 +00001446 GrPaint grPaint;
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001447 if (!((SkGpuDevice*)dev)->bindDeviceAsTexture(&grPaint) ||
Scroggod757df22011-05-16 13:11:16 +00001448 !this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001449 return;
reed@google.comac10a2d2010-12-22 21:39:39 +00001450 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001451
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001452 GrTexture* devTex = grPaint.getTexture(0);
1453 SkASSERT(NULL != devTex);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001454
1455 const SkBitmap& bm = dev->accessBitmap(false);
1456 int w = bm.width();
1457 int h = bm.height();
1458
1459 GrAutoMatrix avm(fContext, GrMatrix::I());
1460
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001461 grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter();
bsalomon@google.com5782d712011-01-21 21:03:59 +00001462
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001463 GrRect dstRect = GrRect::MakeXYWH(GrIntToScalar(x),
1464 GrIntToScalar(y),
1465 GrIntToScalar(w),
1466 GrIntToScalar(h));
1467 // The device being drawn may not fill up its texture (saveLayer uses
1468 // the approximate ).
1469 GrRect srcRect = GrRect::MakeWH(GR_Scalar1 * w / devTex->width(),
1470 GR_Scalar1 * h / devTex->height());
1471
1472 fContext->drawRectToRect(grPaint, dstRect, srcRect);
reed@google.comac10a2d2010-12-22 21:39:39 +00001473}
1474
1475///////////////////////////////////////////////////////////////////////////////
1476
1477// must be in SkCanvas::VertexMode order
bsalomon@google.comffca4002011-02-22 20:34:01 +00001478static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
1479 kTriangles_PrimitiveType,
1480 kTriangleStrip_PrimitiveType,
1481 kTriangleFan_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +00001482};
1483
1484void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
1485 int vertexCount, const SkPoint vertices[],
1486 const SkPoint texs[], const SkColor colors[],
1487 SkXfermode* xmode,
1488 const uint16_t indices[], int indexCount,
1489 const SkPaint& paint) {
1490 CHECK_SHOULD_DRAW(draw);
1491
bsalomon@google.com5782d712011-01-21 21:03:59 +00001492 GrPaint grPaint;
1493 SkAutoCachedTexture act;
1494 // we ignore the shader if texs is null.
1495 if (NULL == texs) {
Scroggod757df22011-05-16 13:11:16 +00001496 if (!this->skPaint2GrPaintNoShader(paint,
1497 false,
1498 &grPaint,
1499 NULL == colors)) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001500 return;
1501 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001502 } else {
reed@google.com1a2e8d22011-01-21 22:08:29 +00001503 if (!this->skPaint2GrPaintShader(paint, &act,
1504 *draw.fMatrix,
Scroggod757df22011-05-16 13:11:16 +00001505 &grPaint,
1506 NULL == colors)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001507 return;
1508 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001509 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001510
1511 if (NULL != xmode && NULL != texs && NULL != colors) {
1512 SkXfermode::Mode mode;
1513 if (!SkXfermode::IsMode(xmode, &mode) ||
1514 SkXfermode::kMultiply_Mode != mode) {
1515 SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
1516#if 0
1517 return
1518#endif
1519 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001520 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001521
1522#if SK_SCALAR_IS_GR_SCALAR
1523 // even if GrColor and SkColor byte offsets match we need
1524 // to perform pre-multiply.
1525 if (NULL == colors) {
1526 fContext->drawVertices(grPaint,
1527 gVertexMode2PrimitiveType[vmode],
1528 vertexCount,
1529 (GrPoint*) vertices,
1530 (GrPoint*) texs,
1531 NULL,
1532 indices,
1533 indexCount);
1534 } else
1535#endif
1536 {
1537 SkTexCoordSource texSrc(texs);
1538 SkColorSource colSrc(colors);
1539 SkIndexSource idxSrc(indices, indexCount);
1540
1541 fContext->drawCustomVertices(grPaint,
1542 gVertexMode2PrimitiveType[vmode],
1543 SkPositionSource(vertices, vertexCount),
1544 (NULL == texs) ? NULL : &texSrc,
1545 (NULL == colors) ? NULL : &colSrc,
1546 (NULL == indices) ? NULL : &idxSrc);
reed@google.comac10a2d2010-12-22 21:39:39 +00001547 }
1548}
1549
1550///////////////////////////////////////////////////////////////////////////////
1551
1552static void GlyphCacheAuxProc(void* data) {
1553 delete (GrFontScaler*)data;
1554}
1555
1556static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
1557 void* auxData;
1558 GrFontScaler* scaler = NULL;
1559 if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
1560 scaler = (GrFontScaler*)auxData;
1561 }
1562 if (NULL == scaler) {
1563 scaler = new SkGrFontScaler(cache);
1564 cache->setAuxProc(GlyphCacheAuxProc, scaler);
1565 }
1566 return scaler;
1567}
1568
1569static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
1570 SkFixed fx, SkFixed fy,
1571 const SkGlyph& glyph) {
1572 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1573
1574 GrSkDrawProcs* procs = (GrSkDrawProcs*)state.fDraw->fProcs;
1575
1576 if (NULL == procs->fFontScaler) {
1577 procs->fFontScaler = get_gr_font_scaler(state.fCache);
1578 }
reed@google.com39ce0ac2011-04-08 15:42:19 +00001579
1580 /*
reed@google.com3b139f52011-06-07 17:56:25 +00001581 * What should we do with fy? (assuming horizontal/latin text)
reed@google.com39ce0ac2011-04-08 15:42:19 +00001582 *
reed@google.com3b139f52011-06-07 17:56:25 +00001583 * The raster code calls SkFixedFloorToFixed on it, as it does with fx.
1584 * It calls that rather than round, because our caller has already added
1585 * SK_FixedHalf, so that calling floor gives us the rounded integer.
1586 *
1587 * Test code between raster and gpu (they should draw the same)
1588 *
1589 * canvas->drawText("Hamburgefons", 12, 0, 16.5f, paint);
1590 *
1591 * Perhaps we should only perform this integralization if there is no
1592 * fExtMatrix...
reed@google.com39ce0ac2011-04-08 15:42:19 +00001593 */
reed@google.com3b139f52011-06-07 17:56:25 +00001594 fy = SkFixedFloorToFixed(fy);
1595
reed@google.comac10a2d2010-12-22 21:39:39 +00001596 procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), fx, 0),
reed@google.com3b139f52011-06-07 17:56:25 +00001597 SkFixedFloorToFixed(fx), fy,
reed@google.comac10a2d2010-12-22 21:39:39 +00001598 procs->fFontScaler);
1599}
1600
bsalomon@google.com5782d712011-01-21 21:03:59 +00001601SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001602
1603 // deferred allocation
1604 if (NULL == fDrawProcs) {
1605 fDrawProcs = new GrSkDrawProcs;
1606 fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
1607 fDrawProcs->fContext = fContext;
1608 }
1609
1610 // init our (and GL's) state
1611 fDrawProcs->fTextContext = context;
1612 fDrawProcs->fFontScaler = NULL;
1613 return fDrawProcs;
1614}
1615
1616void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
1617 size_t byteLength, SkScalar x, SkScalar y,
1618 const SkPaint& paint) {
1619 CHECK_SHOULD_DRAW(draw);
1620
1621 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1622 // this guy will just call our drawPath()
1623 draw.drawText((const char*)text, byteLength, x, y, paint);
1624 } else {
reed@google.comac10a2d2010-12-22 21:39:39 +00001625 SkDraw myDraw(draw);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001626
1627 GrPaint grPaint;
1628 SkAutoCachedTexture act;
1629
Scroggod757df22011-05-16 13:11:16 +00001630 if (!this->skPaint2GrPaintShader(paint,
1631 &act,
1632 *draw.fMatrix,
1633 &grPaint,
1634 true)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001635 return;
1636 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001637 GrTextContext context(fContext, grPaint, draw.fExtMatrix);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001638 myDraw.fProcs = this->initDrawForText(&context);
reed@google.comac10a2d2010-12-22 21:39:39 +00001639 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
1640 }
1641}
1642
1643void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
1644 size_t byteLength, const SkScalar pos[],
1645 SkScalar constY, int scalarsPerPos,
1646 const SkPaint& paint) {
1647 CHECK_SHOULD_DRAW(draw);
1648
1649 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1650 // this guy will just call our drawPath()
1651 draw.drawPosText((const char*)text, byteLength, pos, constY,
1652 scalarsPerPos, paint);
1653 } else {
reed@google.comac10a2d2010-12-22 21:39:39 +00001654 SkDraw myDraw(draw);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001655
1656 GrPaint grPaint;
1657 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +00001658 if (!this->skPaint2GrPaintShader(paint,
1659 &act,
1660 *draw.fMatrix,
1661 &grPaint,
1662 true)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001663 return;
1664 }
1665
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001666 GrTextContext context(fContext, grPaint, draw.fExtMatrix);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001667 myDraw.fProcs = this->initDrawForText(&context);
reed@google.comac10a2d2010-12-22 21:39:39 +00001668 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
1669 scalarsPerPos, paint);
1670 }
1671}
1672
1673void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
1674 size_t len, const SkPath& path,
1675 const SkMatrix* m, const SkPaint& paint) {
1676 CHECK_SHOULD_DRAW(draw);
1677
1678 SkASSERT(draw.fDevice == this);
1679 draw.drawTextOnPath((const char*)text, len, path, m, paint);
1680}
1681
1682///////////////////////////////////////////////////////////////////////////////
1683
reed@google.comf67e4cf2011-03-15 20:56:58 +00001684bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
1685 if (!paint.isLCDRenderText()) {
1686 // we're cool with the paint as is
1687 return false;
1688 }
1689
1690 if (paint.getShader() ||
1691 paint.getXfermode() || // unless its srcover
1692 paint.getMaskFilter() ||
1693 paint.getRasterizer() ||
1694 paint.getColorFilter() ||
1695 paint.getPathEffect() ||
1696 paint.isFakeBoldText() ||
1697 paint.getStyle() != SkPaint::kFill_Style) {
1698 // turn off lcd
1699 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
1700 flags->fHinting = paint.getHinting();
1701 return true;
1702 }
1703 // we're cool with the paint as is
1704 return false;
1705}
1706
1707///////////////////////////////////////////////////////////////////////////////
1708
reed@google.comac10a2d2010-12-22 21:39:39 +00001709SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001710 const GrSamplerState& sampler,
1711 GrTexture** texture,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001712 TexType type) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001713 GrTexture* newTexture = NULL;
1714 GrTextureEntry* entry = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +00001715 GrContext* ctx = this->context();
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001716
bsalomon@google.come97f0852011-06-17 13:10:25 +00001717 if (kBitmap_TexType != type) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001718 const GrTextureDesc desc = {
1719 kRenderTarget_GrTextureFlagBit,
1720 kNone_GrAALevel,
1721 bitmap.width(),
1722 bitmap.height(),
1723 SkGr::Bitmap2PixelConfig(bitmap)
1724 };
bsalomon@google.come97f0852011-06-17 13:10:25 +00001725 if (kSaveLayerDeviceRenderTarget_TexType == type) {
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001726 // we know layers will only be drawn through drawDevice.
1727 // drawDevice has been made to work with content embedded in a
1728 // larger texture so its okay to use the approximate version.
1729 entry = ctx->findApproximateKeylessTexture(desc);
1730 } else {
bsalomon@google.come97f0852011-06-17 13:10:25 +00001731 SkASSERT(kDeviceRenderTarget_TexType == type);
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001732 entry = ctx->lockKeylessTexture(desc);
1733 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001734 } else {
junov@google.com4ee7ae52011-06-30 17:30:49 +00001735 if (!bitmap.isVolatile()) {
1736 uint32_t p0, p1;
1737 p0 = bitmap.getGenerationID();
1738 p1 = bitmap.pixelRefOffset();
1739 GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
1740
1741 entry = ctx->findAndLockTexture(&key, sampler);
1742 if (NULL == entry)
1743 entry = sk_gr_create_bitmap_texture(ctx, &key, sampler,
1744 bitmap);
1745 } else {
1746 entry = sk_gr_create_bitmap_texture(ctx, NULL, sampler, bitmap);
1747 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001748 if (NULL == entry) {
junov@google.com4ee7ae52011-06-30 17:30:49 +00001749 GrPrintf("---- failed to create texture for cache [%d %d]\n",
1750 bitmap.width(), bitmap.height());
reed@google.comac10a2d2010-12-22 21:39:39 +00001751 }
1752 }
1753
1754 if (NULL != entry) {
1755 newTexture = entry->texture();
reed@google.comac10a2d2010-12-22 21:39:39 +00001756 if (texture) {
1757 *texture = newTexture;
1758 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001759 }
1760 return (TexCache*)entry;
1761}
1762
1763void SkGpuDevice::unlockCachedTexture(TexCache* cache) {
1764 this->context()->unlockTexture((GrTextureEntry*)cache);
1765}
1766
bsalomon@google.come97f0852011-06-17 13:10:25 +00001767SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
1768 int width, int height,
1769 bool isOpaque,
1770 Usage usage) {
1771 return SkNEW_ARGS(SkGpuDevice,(this->context(), config,
1772 width, height, usage));
1773}
1774