blob: 22cbaf020ad9eaef620c18aa09278af134025ac0 [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.orgadec4462011-07-12 17:02:06 +000050#define USE_GPU_BLUR 0
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +000051#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 +0000261///////////////////////////////////////////////////////////////////////////////
262
263void SkGpuDevice::makeRenderTargetCurrent() {
264 fContext->setRenderTarget(fRenderTarget);
265 fContext->flush(true);
266 fNeedPrepareRenderTarget = true;
267}
268
269///////////////////////////////////////////////////////////////////////////////
270
271bool SkGpuDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
272 SkIRect bounds;
273 bounds.set(0, 0, this->width(), this->height());
274 if (!bounds.intersect(srcRect)) {
275 return false;
276 }
277
278 const int w = bounds.width();
279 const int h = bounds.height();
280 SkBitmap tmp;
281 // note we explicitly specify our rowBytes to be snug (no gap between rows)
282 tmp.setConfig(SkBitmap::kARGB_8888_Config, w, h, w * 4);
283 if (!tmp.allocPixels()) {
284 return false;
285 }
286
Scroggo813c33c2011-04-07 20:56:21 +0000287 tmp.lockPixels();
reed@google.comac10a2d2010-12-22 21:39:39 +0000288
Scroggoeb176032011-04-07 21:11:49 +0000289 bool read = fContext->readRenderTargetPixels(fRenderTarget,
290 bounds.fLeft, bounds.fTop,
291 bounds.width(), bounds.height(),
292 kRGBA_8888_GrPixelConfig,
293 tmp.getPixels());
Scroggo813c33c2011-04-07 20:56:21 +0000294 tmp.unlockPixels();
295 if (!read) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000296 return false;
297 }
298
299 tmp.swap(*bitmap);
300 return true;
301}
302
303void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) {
304 SkAutoLockPixels alp(bitmap);
305 if (!bitmap.readyToDraw()) {
306 return;
307 }
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000308 GrPixelConfig config = SkGr::BitmapConfig2PixelConfig(bitmap.config(),
309 bitmap.isOpaque());
reed@google.comac10a2d2010-12-22 21:39:39 +0000310 fContext->setRenderTarget(fRenderTarget);
311 // we aren't setting the clip or matrix, so mark as dirty
312 // we don't need to set them for this call and don't have them anyway
313 fNeedPrepareRenderTarget = true;
314
315 fContext->writePixels(x, y, bitmap.width(), bitmap.height(),
316 config, bitmap.getPixels(), bitmap.rowBytes());
317}
318
319///////////////////////////////////////////////////////////////////////////////
320
321static void convert_matrixclip(GrContext* context, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000322 const SkClipStack& clipStack,
reed@google.com6f8f2922011-03-04 22:27:10 +0000323 const SkRegion& clipRegion,
324 const SkIPoint& origin) {
bsalomon@google.comcc4dac32011-05-10 13:52:42 +0000325 context->setMatrix(matrix);
reed@google.comac10a2d2010-12-22 21:39:39 +0000326
327 SkGrClipIterator iter;
bsalomon@google.comd302f142011-03-03 13:54:13 +0000328 iter.reset(clipStack);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000329 const SkIRect& skBounds = clipRegion.getBounds();
330 GrRect bounds;
331 bounds.setLTRB(GrIntToScalar(skBounds.fLeft),
332 GrIntToScalar(skBounds.fTop),
333 GrIntToScalar(skBounds.fRight),
334 GrIntToScalar(skBounds.fBottom));
reed@google.com6f8f2922011-03-04 22:27:10 +0000335 GrClip grc(&iter, GrIntToScalar(-origin.x()), GrIntToScalar(-origin.y()),
336 &bounds);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000337 context->setClip(grc);
reed@google.comac10a2d2010-12-22 21:39:39 +0000338}
339
340// call this ever each draw call, to ensure that the context reflects our state,
341// and not the state from some other canvas/device
342void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) {
343 if (fNeedPrepareRenderTarget ||
bsalomon@google.com5782d712011-01-21 21:03:59 +0000344 fContext->getRenderTarget() != fRenderTarget) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000345
346 fContext->setRenderTarget(fRenderTarget);
bsalomon@google.comd302f142011-03-03 13:54:13 +0000347 SkASSERT(draw.fClipStack);
348 convert_matrixclip(fContext, *draw.fMatrix,
reed@google.com6f8f2922011-03-04 22:27:10 +0000349 *draw.fClipStack, *draw.fClip, this->getOrigin());
reed@google.comac10a2d2010-12-22 21:39:39 +0000350 fNeedPrepareRenderTarget = false;
351 }
352}
353
reed@google.com46799cd2011-02-22 20:56:26 +0000354void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip,
355 const SkClipStack& clipStack) {
356 this->INHERITED::setMatrixClip(matrix, clip, clipStack);
bsalomon@google.coma7bf6e22011-04-11 19:20:46 +0000357 // We don't need to set them now because the context may not reflect this device.
358 fNeedPrepareRenderTarget = true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000359}
360
361void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000362 const SkRegion& clip, const SkClipStack& clipStack) {
363
reed@google.comac10a2d2010-12-22 21:39:39 +0000364 fContext->setRenderTarget(fRenderTarget);
365
bsalomon@google.comd302f142011-03-03 13:54:13 +0000366 this->INHERITED::gainFocus(canvas, matrix, clip, clipStack);
reed@google.comac10a2d2010-12-22 21:39:39 +0000367
reed@google.com6f8f2922011-03-04 22:27:10 +0000368 convert_matrixclip(fContext, matrix, clipStack, clip, this->getOrigin());
reed@google.comac10a2d2010-12-22 21:39:39 +0000369
370 if (fNeedClear) {
bsalomon@google.com31a58402011-04-27 21:00:02 +0000371 fContext->clear(NULL, 0x0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000372 fNeedClear = false;
373 }
374}
375
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000376bool SkGpuDevice::bindDeviceAsTexture(GrPaint* paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000377 if (NULL != fTexture) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000378 paint->setTexture(kBitmapTextureIdx, fTexture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000379 return true;
380 }
381 return false;
382}
383
384///////////////////////////////////////////////////////////////////////////////
385
vandebo@chromium.orgd3ae7792011-02-24 00:21:06 +0000386SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
387SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
388SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
389SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
390SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
391 shader_type_mismatch);
392SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 4, shader_type_mismatch);
reed@google.comac10a2d2010-12-22 21:39:39 +0000393
bsalomon@google.com5782d712011-01-21 21:03:59 +0000394static const GrSamplerState::SampleMode sk_bmp_type_to_sample_mode[] = {
395 (GrSamplerState::SampleMode) -1, // kNone_BitmapType
396 GrSamplerState::kNormal_SampleMode, // kDefault_BitmapType
397 GrSamplerState::kRadial_SampleMode, // kRadial_BitmapType
398 GrSamplerState::kSweep_SampleMode, // kSweep_BitmapType
399 GrSamplerState::kRadial2_SampleMode, // kTwoPointRadial_BitmapType
400};
401
402bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint,
403 bool justAlpha,
Scroggod757df22011-05-16 13:11:16 +0000404 GrPaint* grPaint,
405 bool constantColor) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000406
407 grPaint->fDither = skPaint.isDither();
408 grPaint->fAntiAlias = skPaint.isAntiAlias();
409
410 SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
411 SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
412
413 SkXfermode* mode = skPaint.getXfermode();
414 if (mode) {
415 if (!mode->asCoeff(&sm, &dm)) {
reed@google.com1a2e8d22011-01-21 22:08:29 +0000416 SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
bsalomon@google.com5782d712011-01-21 21:03:59 +0000417#if 0
418 return false;
419#endif
420 }
421 }
422 grPaint->fSrcBlendCoeff = sk_blend_to_grblend(sm);
423 grPaint->fDstBlendCoeff = sk_blend_to_grblend(dm);
424
425 if (justAlpha) {
426 uint8_t alpha = skPaint.getAlpha();
427 grPaint->fColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
Scroggod757df22011-05-16 13:11:16 +0000428 // justAlpha is currently set to true only if there is a texture,
429 // so constantColor should not also be true.
430 GrAssert(!constantColor);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000431 } else {
432 grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor());
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000433 grPaint->setTexture(kShaderTextureIdx, NULL);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000434 }
Scroggo97c88c22011-05-11 14:05:25 +0000435 SkColorFilter* colorFilter = skPaint.getColorFilter();
436 SkColor color;
437 SkXfermode::Mode filterMode;
438 if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
Scroggod757df22011-05-16 13:11:16 +0000439 if (!constantColor) {
440 grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
441 grPaint->fColorFilterXfermode = filterMode;
442 return true;
443 }
444 SkColor filtered = colorFilter->filterColor(skPaint.getColor());
445 grPaint->fColor = SkGr::SkColor2GrColor(filtered);
Scroggo97c88c22011-05-11 14:05:25 +0000446 }
Scroggod757df22011-05-16 13:11:16 +0000447 grPaint->resetColorFilter();
bsalomon@google.com5782d712011-01-21 21:03:59 +0000448 return true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000449}
450
bsalomon@google.com5782d712011-01-21 21:03:59 +0000451bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint,
452 SkAutoCachedTexture* act,
453 const SkMatrix& ctm,
Scroggod757df22011-05-16 13:11:16 +0000454 GrPaint* grPaint,
455 bool constantColor) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000456
bsalomon@google.com5782d712011-01-21 21:03:59 +0000457 SkASSERT(NULL != act);
reed@google.comac10a2d2010-12-22 21:39:39 +0000458
bsalomon@google.com5782d712011-01-21 21:03:59 +0000459 SkShader* shader = skPaint.getShader();
reed@google.comac10a2d2010-12-22 21:39:39 +0000460 if (NULL == shader) {
Scroggod757df22011-05-16 13:11:16 +0000461 return this->skPaint2GrPaintNoShader(skPaint,
462 false,
463 grPaint,
464 constantColor);
465 } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000466 return false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000467 }
468
reed@google.comac10a2d2010-12-22 21:39:39 +0000469 SkBitmap bitmap;
470 SkMatrix matrix;
471 SkShader::TileMode tileModes[2];
472 SkScalar twoPointParams[3];
473 SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, &matrix,
474 tileModes, twoPointParams);
475
bsalomon@google.com5782d712011-01-21 21:03:59 +0000476 GrSamplerState::SampleMode sampleMode = sk_bmp_type_to_sample_mode[bmptype];
477 if (-1 == sampleMode) {
reed@google.com2be9e8b2011-07-06 21:18:09 +0000478 SkShader::GradientInfo info;
479 SkColor color;
480
481 info.fColors = &color;
482 info.fColorOffsets = NULL;
483 info.fColorCount = 1;
484 if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
485 SkPaint copy(skPaint);
486 copy.setShader(NULL);
bsalomon@google.comcd9cfd72011-07-08 16:55:04 +0000487 // modulate the paint alpha by the shader's solid color alpha
488 U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
489 copy.setColor(SkColorSetA(color, newA));
reed@google.com2be9e8b2011-07-06 21:18:09 +0000490 return this->skPaint2GrPaintNoShader(copy,
491 false,
492 grPaint,
493 constantColor);
494 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000495 return false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000496 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000497 GrSamplerState* sampler = grPaint->getTextureSampler(kShaderTextureIdx);
498 sampler->setSampleMode(sampleMode);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000499 if (skPaint.isFilterBitmap()) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000500 sampler->setFilter(GrSamplerState::kBilinear_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000501 } else {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000502 sampler->setFilter(GrSamplerState::kNearest_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +0000503 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000504 sampler->setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
505 sampler->setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
reed@google.comac10a2d2010-12-22 21:39:39 +0000506 if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000507 sampler->setRadial2Params(twoPointParams[0],
508 twoPointParams[1],
509 twoPointParams[2] < 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000510 }
511
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000512 GrTexture* texture = act->set(this, bitmap, *sampler);
reed@google.comac10a2d2010-12-22 21:39:39 +0000513 if (NULL == texture) {
bsalomon@google.com5782d712011-01-21 21:03:59 +0000514 SkDebugf("Couldn't convert bitmap to texture.\n");
515 return false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000516 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000517 grPaint->setTexture(kShaderTextureIdx, texture);
reed@google.comac10a2d2010-12-22 21:39:39 +0000518
519 // since our texture coords will be in local space, we wack the texture
520 // matrix to map them back into 0...1 before we load it
521 SkMatrix localM;
522 if (shader->getLocalMatrix(&localM)) {
523 SkMatrix inverse;
524 if (localM.invert(&inverse)) {
525 matrix.preConcat(inverse);
526 }
527 }
528 if (SkShader::kDefault_BitmapType == bmptype) {
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000529 GrScalar sx = GrFixedToScalar(GR_Fixed1 / bitmap.width());
530 GrScalar sy = GrFixedToScalar(GR_Fixed1 / bitmap.height());
reed@google.comac10a2d2010-12-22 21:39:39 +0000531 matrix.postScale(sx, sy);
reed@google.comac10a2d2010-12-22 21:39:39 +0000532 } else if (SkShader::kRadial_BitmapType == bmptype) {
bsalomon@google.comc6cf7232011-02-17 16:43:10 +0000533 GrScalar s = GrFixedToScalar(GR_Fixed1 / bitmap.width());
reed@google.comac10a2d2010-12-22 21:39:39 +0000534 matrix.postScale(s, s);
535 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +0000536 sampler->setMatrix(matrix);
bsalomon@google.com5782d712011-01-21 21:03:59 +0000537
538 return true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000539}
540
541///////////////////////////////////////////////////////////////////////////////
bsalomon@google.com5782d712011-01-21 21:03:59 +0000542
543class SkPositionSource {
544public:
545 SkPositionSource(const SkPoint* points, int count)
546 : fPoints(points), fCount(count) {}
547
548 int count() const { return fCount; }
549
550 void writeValue(int i, GrPoint* dstPosition) const {
551 SkASSERT(i < fCount);
552 dstPosition->fX = SkScalarToGrScalar(fPoints[i].fX);
553 dstPosition->fY = SkScalarToGrScalar(fPoints[i].fY);
554 }
555private:
bsalomon@google.com5782d712011-01-21 21:03:59 +0000556 const SkPoint* fPoints;
bsalomon@google.com19628322011-02-03 21:30:17 +0000557 int fCount;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000558};
559
560class SkTexCoordSource {
561public:
562 SkTexCoordSource(const SkPoint* coords)
563 : fCoords(coords) {}
564
565 void writeValue(int i, GrPoint* dstCoord) const {
566 dstCoord->fX = SkScalarToGrScalar(fCoords[i].fX);
567 dstCoord->fY = SkScalarToGrScalar(fCoords[i].fY);
568 }
569private:
570 const SkPoint* fCoords;
571};
572
573class SkColorSource {
574public:
575 SkColorSource(const SkColor* colors) : fColors(colors) {}
576
577 void writeValue(int i, GrColor* dstColor) const {
578 *dstColor = SkGr::SkColor2GrColor(fColors[i]);
579 }
580private:
581 const SkColor* fColors;
582};
583
584class SkIndexSource {
585public:
586 SkIndexSource(const uint16_t* indices, int count)
587 : fIndices(indices), fCount(count) {
588 }
589
590 int count() const { return fCount; }
591
592 void writeValue(int i, uint16_t* dstIndex) const {
593 *dstIndex = fIndices[i];
594 }
595
596private:
bsalomon@google.com5782d712011-01-21 21:03:59 +0000597 const uint16_t* fIndices;
bsalomon@google.com19628322011-02-03 21:30:17 +0000598 int fCount;
bsalomon@google.com5782d712011-01-21 21:03:59 +0000599};
600
601///////////////////////////////////////////////////////////////////////////////
602
bsalomon@google.com6f7fbc92011-02-01 19:12:40 +0000603#if 0 // not currently being used so don't compile,
604
bsalomon@google.com5782d712011-01-21 21:03:59 +0000605// can be used for positions or texture coordinates
bsalomon@google.com6f7fbc92011-02-01 19:12:40 +0000606
bsalomon@google.com5782d712011-01-21 21:03:59 +0000607class SkRectFanSource {
608public:
609 SkRectFanSource(const SkRect& rect) : fRect(rect) {}
610
611 int count() const { return 4; }
612
613 void writeValue(int i, GrPoint* dstPoint) const {
614 SkASSERT(i < 4);
615 dstPoint->fX = SkScalarToGrScalar((i % 3) ? fRect.fRight :
616 fRect.fLeft);
617 dstPoint->fY = SkScalarToGrScalar((i < 2) ? fRect.fTop :
618 fRect.fBottom);
619 }
620private:
621 const SkRect& fRect;
622};
623
624class SkIRectFanSource {
625public:
626 SkIRectFanSource(const SkIRect& rect) : fRect(rect) {}
627
628 int count() const { return 4; }
629
630 void writeValue(int i, GrPoint* dstPoint) const {
631 SkASSERT(i < 4);
632 dstPoint->fX = (i % 3) ? GrIntToScalar(fRect.fRight) :
633 GrIntToScalar(fRect.fLeft);
634 dstPoint->fY = (i < 2) ? GrIntToScalar(fRect.fTop) :
635 GrIntToScalar(fRect.fBottom);
636 }
637private:
638 const SkIRect& fRect;
639};
640
641class SkMatRectFanSource {
642public:
643 SkMatRectFanSource(const SkRect& rect, const SkMatrix& matrix)
644 : fRect(rect), fMatrix(matrix) {}
645
646 int count() const { return 4; }
647
648 void writeValue(int i, GrPoint* dstPoint) const {
649 SkASSERT(i < 4);
650
651#if SK_SCALAR_IS_GR_SCALAR
652 fMatrix.mapXY((i % 3) ? fRect.fRight : fRect.fLeft,
653 (i < 2) ? fRect.fTop : fRect.fBottom,
654 (SkPoint*)dstPoint);
655#else
656 SkPoint dst;
657 fMatrix.mapXY((i % 3) ? fRect.fRight : fRect.fLeft,
658 (i < 2) ? fRect.fTop : fRect.fBottom,
659 &dst);
660 dstPoint->fX = SkScalarToGrScalar(dst.fX);
661 dstPoint->fY = SkScalarToGrScalar(dst.fY);
662#endif
663 }
664private:
665 const SkRect& fRect;
666 const SkMatrix& fMatrix;
667};
668
bsalomon@google.com6f7fbc92011-02-01 19:12:40 +0000669#endif
670
reed@google.comac10a2d2010-12-22 21:39:39 +0000671///////////////////////////////////////////////////////////////////////////////
672
bsalomon@google.com398109c2011-04-14 18:40:27 +0000673void SkGpuDevice::clear(SkColor color) {
bsalomon@google.com31a58402011-04-27 21:00:02 +0000674 fContext->clear(NULL, color);
bsalomon@google.com398109c2011-04-14 18:40:27 +0000675}
676
reed@google.comac10a2d2010-12-22 21:39:39 +0000677void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
678 CHECK_SHOULD_DRAW(draw);
679
bsalomon@google.com5782d712011-01-21 21:03:59 +0000680 GrPaint grPaint;
681 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +0000682 if (!this->skPaint2GrPaintShader(paint,
683 &act,
684 *draw.fMatrix,
685 &grPaint,
686 true)) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000687 return;
688 }
bsalomon@google.com5782d712011-01-21 21:03:59 +0000689
690 fContext->drawPaint(grPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +0000691}
692
693// must be in SkCanvas::PointMode order
bsalomon@google.comffca4002011-02-22 20:34:01 +0000694static const GrPrimitiveType gPointMode2PrimtiveType[] = {
695 kPoints_PrimitiveType,
696 kLines_PrimitiveType,
697 kLineStrip_PrimitiveType
reed@google.comac10a2d2010-12-22 21:39:39 +0000698};
699
700void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
bsalomon@google.com5782d712011-01-21 21:03:59 +0000701 size_t count, const SkPoint pts[], const SkPaint& paint) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000702 CHECK_SHOULD_DRAW(draw);
703
704 SkScalar width = paint.getStrokeWidth();
705 if (width < 0) {
706 return;
707 }
708
709 // we only handle hairlines here, else we let the SkDraw call our drawPath()
710 if (width > 0) {
711 draw.drawPoints(mode, count, pts, paint, true);
712 return;
713 }
714
bsalomon@google.com5782d712011-01-21 21:03:59 +0000715 GrPaint grPaint;
716 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +0000717 if (!this->skPaint2GrPaintShader(paint,
718 &act,
719 *draw.fMatrix,
720 &grPaint,
721 true)) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000722 return;
723 }
724
reed@google.comac10a2d2010-12-22 21:39:39 +0000725#if SK_SCALAR_IS_GR_SCALAR
bsalomon@google.com5782d712011-01-21 21:03:59 +0000726 fContext->drawVertices(grPaint,
727 gPointMode2PrimtiveType[mode],
728 count,
729 (GrPoint*)pts,
730 NULL,
731 NULL,
732 NULL,
733 0);
reed@google.comac10a2d2010-12-22 21:39:39 +0000734#else
bsalomon@google.com5782d712011-01-21 21:03:59 +0000735 fContext->drawCustomVertices(grPaint,
736 gPointMode2PrimtiveType[mode],
737 SkPositionSource(pts, count));
reed@google.comac10a2d2010-12-22 21:39:39 +0000738#endif
reed@google.comac10a2d2010-12-22 21:39:39 +0000739}
740
reed@google.comc9aa5872011-04-05 21:05:37 +0000741///////////////////////////////////////////////////////////////////////////////
742
reed@google.comac10a2d2010-12-22 21:39:39 +0000743void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
744 const SkPaint& paint) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000745 CHECK_SHOULD_DRAW(draw);
746
747 bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
748 SkScalar width = paint.getStrokeWidth();
749
750 /*
751 We have special code for hairline strokes, miter-strokes, and fills.
752 Anything else we just call our path code.
753 */
754 bool usePath = doStroke && width > 0 &&
755 paint.getStrokeJoin() != SkPaint::kMiter_Join;
756 // another reason we might need to call drawPath...
757 if (paint.getMaskFilter()) {
758 usePath = true;
759 }
reed@google.com67db6642011-05-26 11:46:35 +0000760 // until we aa rotated rects...
761 if (!usePath && paint.isAntiAlias() && !draw.fMatrix->rectStaysRect()) {
762 usePath = true;
763 }
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000764
765 if (usePath) {
766 SkPath path;
767 path.addRect(rect);
768 this->drawPath(draw, path, paint, NULL, true);
769 return;
770 }
771
772 GrPaint grPaint;
773 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +0000774 if (!this->skPaint2GrPaintShader(paint,
775 &act,
776 *draw.fMatrix,
777 &grPaint,
778 true)) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +0000779 return;
780 }
reed@google.com20efde72011-05-09 17:00:02 +0000781 fContext->drawRect(grPaint, rect, doStroke ? width : -1);
reed@google.comac10a2d2010-12-22 21:39:39 +0000782}
783
reed@google.com69302852011-02-16 18:08:07 +0000784#include "SkMaskFilter.h"
785#include "SkBounder.h"
786
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000787static GrPathFill skToGrFillType(SkPath::FillType fillType) {
788 switch (fillType) {
789 case SkPath::kWinding_FillType:
790 return kWinding_PathFill;
791 case SkPath::kEvenOdd_FillType:
792 return kEvenOdd_PathFill;
793 case SkPath::kInverseWinding_FillType:
794 return kInverseWinding_PathFill;
795 case SkPath::kInverseEvenOdd_FillType:
796 return kInverseEvenOdd_PathFill;
797 default:
798 SkDebugf("Unsupported path fill type\n");
799 return kHairLine_PathFill;
800 }
801}
802
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000803static void buildKernel(float sigma, float* kernel, int kernelWidth) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000804 int halfWidth = (kernelWidth - 1) / 2;
805 float sum = 0.0f;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000806 float denom = 1.0f / (2.0f * sigma * sigma);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000807 for (int i = 0; i < kernelWidth; ++i) {
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000808 float x = static_cast<float>(i - halfWidth);
809 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
810 // is dropped here, since we renormalize the kernel below.
811 kernel[i] = sk_float_exp(- x * x * denom);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000812 sum += kernel[i];
813 }
814 // Normalize the kernel
815 float scale = 1.0f / sum;
816 for (int i = 0; i < kernelWidth; ++i)
817 kernel[i] *= scale;
818}
819
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000820static void scaleRect(SkRect* rect, float scale) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000821 rect->fLeft *= scale;
822 rect->fTop *= scale;
823 rect->fRight *= scale;
824 rect->fBottom *= scale;
825}
826
827static bool drawWithGPUMaskFilter(GrContext* context, const SkPath& path,
828 SkMaskFilter* filter, const SkMatrix& matrix,
829 const SkRegion& clip, SkBounder* bounder,
830 GrPaint* grp) {
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000831#if !USE_GPU_BLUR
832 return false;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000833#endif
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000834 SkMaskFilter::BlurInfo info;
835 SkMaskFilter::BlurType blurType = filter->asABlur(&info);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000836 if (SkMaskFilter::kNone_BlurType == blurType) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000837 return false;
838 }
839 float radius = info.fIgnoreTransform ? info.fRadius
840 : matrix.mapRadius(info.fRadius);
841 float sigma = radius * 0.6666f;
842 SkRect srcRect = path.getBounds();
843
844 int scaleFactor = 1;
845
846 while (sigma > MAX_SIGMA) {
847 scaleFactor *= 2;
848 sigma *= 0.5f;
849 }
850 scaleRect(&srcRect, 1.0f / scaleFactor);
851 int halfWidth = static_cast<int>(sigma * 3.0f);
852 int kernelWidth = halfWidth * 2 + 1;
853
854 SkIRect srcIRect;
855 srcRect.roundOut(&srcIRect);
856 srcRect.set(srcIRect);
857 srcRect.inset(-halfWidth, -halfWidth);
858
859 scaleRect(&srcRect, scaleFactor);
860 SkRect finalRect = srcRect;
861
862 SkIRect finalIRect;
863 finalRect.roundOut(&finalIRect);
864 if (clip.quickReject(finalIRect)) {
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000865 return true;
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000866 }
867 if (bounder && !bounder->doIRect(finalIRect)) {
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000868 return true;
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000869 }
870 GrPoint offset = GrPoint::Make(-srcRect.fLeft, -srcRect.fTop);
871 srcRect.offset(-srcRect.fLeft, -srcRect.fTop);
872 const GrTextureDesc desc = {
873 kRenderTarget_GrTextureFlagBit,
874 kNone_GrAALevel,
875 srcRect.width(),
876 srcRect.height(),
877 // We actually only need A8, but it often isn't supported as a
878 // render target
879 kRGBA_8888_GrPixelConfig
880 };
881
882 GrTextureEntry* srcEntry = context->findApproximateKeylessTexture(desc);
883 GrTextureEntry* dstEntry = context->findApproximateKeylessTexture(desc);
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000884 GrAutoUnlockTextureEntry srcLock(context, srcEntry),
885 dstLock(context, dstEntry);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000886 if (NULL == srcEntry || NULL == dstEntry) {
887 return false;
888 }
889 GrTexture* srcTexture = srcEntry->texture();
890 GrTexture* dstTexture = dstEntry->texture();
891 if (NULL == srcTexture || NULL == dstTexture) {
892 return false;
893 }
894 GrRenderTarget* oldRenderTarget = context->getRenderTarget();
senorblanco@chromium.org42dd0f92011-07-14 15:29:57 +0000895 // Once this code moves into GrContext, this should be changed to use
896 // an AutoClipRestore.
897 GrClip oldClip = context->getClip();
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000898 context->setRenderTarget(dstTexture->asRenderTarget());
senorblanco@chromium.org42dd0f92011-07-14 15:29:57 +0000899 context->setClip(srcRect);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000900 // FIXME: could just clear bounds
901 context->clear(NULL, 0);
902 GrMatrix transM;
903 GrPaint tempPaint;
904 tempPaint.reset();
905
906 GrAutoMatrix avm(context, GrMatrix::I());
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000907 tempPaint.fAntiAlias = grp->fAntiAlias;
908 if (tempPaint.fAntiAlias) {
909 // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
910 // blend coeff of zero requires dual source blending support in order
911 // to properly blend partially covered pixels. This means the AA
912 // code path may not be taken. So we use a dst blend coeff of ISA. We
913 // could special case AA draws to a dst surface with known alpha=0 to
914 // use a zero dst coeff when dual source blending isn't available.
915 tempPaint.fSrcBlendCoeff = kOne_BlendCoeff;
916 tempPaint.fDstBlendCoeff = kISC_BlendCoeff;
917 }
918 // Draw hard shadow to dstTexture with path topleft at origin 0,0.
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000919 context->drawPath(tempPaint, path, skToGrFillType(path.getFillType()), &offset);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000920 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000921
922 GrMatrix sampleM;
923 sampleM.setScale(GR_Scalar1 / srcTexture->width(),
924 GR_Scalar1 / srcTexture->height());
925 GrPaint paint;
926 paint.reset();
927 paint.getTextureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter);
928 paint.getTextureSampler(0)->setMatrix(sampleM);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000929 GrTextureEntry* origEntry = NULL;
930 if (blurType != SkMaskFilter::kNormal_BlurType) {
931 // Stash away a copy of the unblurred image.
932 origEntry = context->findApproximateKeylessTexture(desc);
933 if (NULL == origEntry) {
934 return false;
935 }
936 context->setRenderTarget(origEntry->texture()->asRenderTarget());
937 paint.setTexture(0, srcTexture);
938 context->drawRect(paint, srcRect);
939 }
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000940 GrAutoUnlockTextureEntry origLock(context, origEntry);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000941 for (int i = 1; i < scaleFactor; i *= 2) {
942 context->setRenderTarget(dstTexture->asRenderTarget());
943 SkRect dstRect(srcRect);
944 scaleRect(&dstRect, 0.5f);
945 // Clear out 1 pixel border for linear filtering.
946 // FIXME: for now, clear everything
947 context->clear(NULL, 0);
948 paint.setTexture(0, srcTexture);
949 context->drawRectToRect(paint, dstRect, srcRect);
950 srcRect = dstRect;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000951 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000952 }
953
954 SkAutoTMalloc<float> kernelStorage(kernelWidth);
955 float* kernel = kernelStorage.get();
956 buildKernel(sigma, kernel, kernelWidth);
957
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000958 context->setRenderTarget(dstTexture->asRenderTarget());
959 context->clear(NULL, 0);
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000960 context->convolveInX(srcTexture, srcRect, kernel, kernelWidth);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000961 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000962
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000963 context->setRenderTarget(dstTexture->asRenderTarget());
964 context->clear(NULL, 0);
senorblanco@chromium.orgaadd9f82011-07-12 19:44:51 +0000965 context->convolveInY(srcTexture, srcRect, kernel, kernelWidth);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000966 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000967
968 if (scaleFactor > 1) {
969 // FIXME: This should be mitchell, not bilinear.
970 paint.getTextureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter);
971 sampleM.setScale(GR_Scalar1 / srcTexture->width(),
972 GR_Scalar1 / srcTexture->height());
973 paint.getTextureSampler(0)->setMatrix(sampleM);
974 context->setRenderTarget(dstTexture->asRenderTarget());
975 // Clear out 2 pixel border for bicubic filtering.
976 // FIXME: for now, clear everything
977 context->clear(NULL, 0);
978 paint.setTexture(0, srcTexture);
979 SkRect dstRect(srcRect);
980 scaleRect(&dstRect, scaleFactor);
981 context->drawRectToRect(paint, dstRect, srcRect);
982 srcRect = dstRect;
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000983 SkTSwap(srcTexture, dstTexture);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +0000984 }
985
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +0000986 if (blurType != SkMaskFilter::kNormal_BlurType) {
987 GrTexture* origTexture = origEntry->texture();
988 paint.getTextureSampler(0)->setFilter(GrSamplerState::kNearest_Filter);
989 sampleM.setScale(GR_Scalar1 / origTexture->width(),
990 GR_Scalar1 / origTexture->height());
991 paint.getTextureSampler(0)->setMatrix(sampleM);
992 // Blend origTexture over srcTexture.
993 context->setRenderTarget(srcTexture->asRenderTarget());
994 paint.setTexture(0, origTexture);
995 if (SkMaskFilter::kInner_BlurType == blurType) {
996 // inner: dst = dst * src
997 paint.fSrcBlendCoeff = kDC_BlendCoeff;
998 paint.fDstBlendCoeff = kZero_BlendCoeff;
999 } else if (SkMaskFilter::kSolid_BlurType == blurType) {
1000 // solid: dst = src + dst - src * dst
1001 // = (1 - dst) * src + 1 * dst
1002 paint.fSrcBlendCoeff = kIDC_BlendCoeff;
1003 paint.fDstBlendCoeff = kOne_BlendCoeff;
1004 } else if (SkMaskFilter::kOuter_BlurType == blurType) {
1005 // outer: dst = dst * (1 - src)
1006 // = 0 * src + (1 - src) * dst
1007 paint.fSrcBlendCoeff = kZero_BlendCoeff;
1008 paint.fDstBlendCoeff = kISC_BlendCoeff;
1009 }
1010 context->drawRect(paint, srcRect);
1011 }
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001012 context->setRenderTarget(oldRenderTarget);
senorblanco@chromium.org42dd0f92011-07-14 15:29:57 +00001013 context->setClip(oldClip);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001014
1015 if (grp->hasTextureOrMask()) {
1016 GrMatrix inverse;
1017 if (!matrix.invert(&inverse)) {
1018 return false;
1019 }
1020 grp->preConcatActiveSamplerMatrices(inverse);
1021 }
1022
1023 static const int MASK_IDX = GrPaint::kMaxMasks - 1;
1024 // we assume the last mask index is available for use
1025 GrAssert(NULL == grp->getMask(MASK_IDX));
1026 grp->setMask(MASK_IDX, srcTexture);
1027 grp->getMaskSampler(MASK_IDX)->setClampNoFilter();
1028
1029 GrMatrix m;
1030 m.setTranslate(-finalRect.fLeft, -finalRect.fTop);
1031 m.postIDiv(srcTexture->width(), srcTexture->height());
1032 grp->getMaskSampler(MASK_IDX)->setMatrix(m);
1033 context->drawRect(*grp, finalRect);
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001034 return true;
1035}
1036
reed@google.com69302852011-02-16 18:08:07 +00001037static bool drawWithMaskFilter(GrContext* context, const SkPath& path,
1038 SkMaskFilter* filter, const SkMatrix& matrix,
1039 const SkRegion& clip, SkBounder* bounder,
1040 GrPaint* grp) {
1041 SkMask srcM, dstM;
1042
1043 if (!SkDraw::DrawToMask(path, &clip.getBounds(), filter, &matrix, &srcM,
1044 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
1045 return false;
1046 }
1047
1048 SkAutoMaskImage autoSrc(&srcM, false);
1049
1050 if (!filter->filterMask(&dstM, srcM, matrix, NULL)) {
1051 return false;
1052 }
1053 // this will free-up dstM when we're done (allocated in filterMask())
1054 SkAutoMaskImage autoDst(&dstM, false);
1055
1056 if (clip.quickReject(dstM.fBounds)) {
1057 return false;
1058 }
1059 if (bounder && !bounder->doIRect(dstM.fBounds)) {
1060 return false;
1061 }
1062
1063 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
1064 // the current clip (and identity matrix) and grpaint settings
1065
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001066 // used to compute inverse view, if necessary
1067 GrMatrix ivm = context->getMatrix();
1068
reed@google.com0c219b62011-02-16 21:31:18 +00001069 GrAutoMatrix avm(context, GrMatrix::I());
reed@google.com69302852011-02-16 18:08:07 +00001070
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001071 const GrTextureDesc desc = {
1072 kNone_GrTextureFlags,
1073 kNone_GrAALevel,
reed@google.com69302852011-02-16 18:08:07 +00001074 dstM.fBounds.width(),
1075 dstM.fBounds.height(),
bsalomon@google.com669fdc42011-04-05 17:08:27 +00001076 kAlpha_8_GrPixelConfig
reed@google.com69302852011-02-16 18:08:07 +00001077 };
1078
1079 GrTexture* texture = context->createUncachedTexture(desc, dstM.fImage,
1080 dstM.fRowBytes);
1081 if (NULL == texture) {
1082 return false;
1083 }
1084
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001085 if (grp->hasTextureOrMask() && ivm.invert(&ivm)) {
1086 grp->preConcatActiveSamplerMatrices(ivm);
1087 }
1088
1089 static const int MASK_IDX = GrPaint::kMaxMasks - 1;
1090 // we assume the last mask index is available for use
1091 GrAssert(NULL == grp->getMask(MASK_IDX));
1092 grp->setMask(MASK_IDX, texture);
reed@google.com0c219b62011-02-16 21:31:18 +00001093 texture->unref();
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001094 grp->getMaskSampler(MASK_IDX)->setClampNoFilter();
reed@google.com69302852011-02-16 18:08:07 +00001095
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001096 GrRect d;
1097 d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
reed@google.com0c219b62011-02-16 21:31:18 +00001098 GrIntToScalar(dstM.fBounds.fTop),
1099 GrIntToScalar(dstM.fBounds.fRight),
1100 GrIntToScalar(dstM.fBounds.fBottom));
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001101
1102 GrMatrix m;
1103 m.setTranslate(-dstM.fBounds.fLeft, -dstM.fBounds.fTop);
1104 m.postIDiv(dstM.fBounds.width(), dstM.fBounds.height());
1105 grp->getMaskSampler(MASK_IDX)->setMatrix(m);
1106
1107 context->drawRect(*grp, d);
reed@google.com69302852011-02-16 18:08:07 +00001108 return true;
1109}
reed@google.com69302852011-02-16 18:08:07 +00001110
reed@google.com0c219b62011-02-16 21:31:18 +00001111void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
reed@google.comac10a2d2010-12-22 21:39:39 +00001112 const SkPaint& paint, const SkMatrix* prePathMatrix,
1113 bool pathIsMutable) {
1114 CHECK_SHOULD_DRAW(draw);
1115
bsalomon@google.com5782d712011-01-21 21:03:59 +00001116 GrPaint grPaint;
1117 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +00001118 if (!this->skPaint2GrPaintShader(paint,
1119 &act,
1120 *draw.fMatrix,
1121 &grPaint,
1122 true)) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001123 return;
1124 }
1125
reed@google.com0c219b62011-02-16 21:31:18 +00001126 // BEGIN lift from SkDraw::drawPath()
1127
1128 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
1129 bool doFill = true;
1130 SkPath tmpPath;
reed@google.comac10a2d2010-12-22 21:39:39 +00001131
1132 if (prePathMatrix) {
reed@google.come3445642011-02-16 23:20:39 +00001133 SkPath* result = pathPtr;
reed@google.com0c219b62011-02-16 21:31:18 +00001134
reed@google.come3445642011-02-16 23:20:39 +00001135 if (!pathIsMutable) {
1136 result = &tmpPath;
1137 pathIsMutable = true;
reed@google.comac10a2d2010-12-22 21:39:39 +00001138 }
reed@google.come3445642011-02-16 23:20:39 +00001139 // should I push prePathMatrix on our MV stack temporarily, instead
1140 // of applying it here? See SkDraw.cpp
1141 pathPtr->transform(*prePathMatrix, result);
1142 pathPtr = result;
reed@google.comac10a2d2010-12-22 21:39:39 +00001143 }
reed@google.com0c219b62011-02-16 21:31:18 +00001144 // at this point we're done with prePathMatrix
1145 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.comac10a2d2010-12-22 21:39:39 +00001146
bsalomon@google.com04de7822011-03-25 18:04:43 +00001147 // This "if" is not part of the SkDraw::drawPath() lift.
1148 // When we get a 1.0 wide stroke we hairline stroke it instead of creating
1149 // a new stroked-path. This is motivated by canvas2D sites that draw
1150 // lines as 1.0 wide stroked paths. We can consider doing an alpha-modulated-
1151 // hairline for width < 1.0 when AA is enabled.
1152 static const int gMatrixMask = ~(SkMatrix::kIdentity_Mask |
1153 SkMatrix::kTranslate_Mask);
1154 if (!paint.getPathEffect() &&
1155 SkPaint::kStroke_Style == paint.getStyle() &&
1156 !(draw.fMatrix->getType() & gMatrixMask) &&
1157 SK_Scalar1 == paint.getStrokeWidth()) {
1158 doFill = false;
1159 }
1160
1161 if (doFill && (paint.getPathEffect() ||
1162 paint.getStyle() != SkPaint::kFill_Style)) {
reed@google.com0c219b62011-02-16 21:31:18 +00001163 doFill = paint.getFillPath(*pathPtr, &tmpPath);
1164 pathPtr = &tmpPath;
1165 }
1166
1167 // END lift from SkDraw::drawPath()
1168
reed@google.com69302852011-02-16 18:08:07 +00001169 if (paint.getMaskFilter()) {
reed@google.com0c219b62011-02-16 21:31:18 +00001170 // avoid possibly allocating a new path in transform if we can
1171 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
1172
1173 // transform the path into device space
reed@google.come3445642011-02-16 23:20:39 +00001174 pathPtr->transform(*draw.fMatrix, devPathPtr);
senorblanco@chromium.orgb08ea1b2011-07-12 16:54:59 +00001175 if (!drawWithGPUMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
1176 *draw.fMatrix, *draw.fClip, draw.fBounder,
1177 &grPaint)) {
senorblanco@chromium.org027de5f2011-07-08 18:03:33 +00001178 drawWithMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
1179 *draw.fMatrix, *draw.fClip, draw.fBounder,
1180 &grPaint);
1181 }
reed@google.com69302852011-02-16 18:08:07 +00001182 return;
1183 }
reed@google.com69302852011-02-16 18:08:07 +00001184
bsalomon@google.comffca4002011-02-22 20:34:01 +00001185 GrPathFill fill = kHairLine_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001186
reed@google.com0c219b62011-02-16 21:31:18 +00001187 if (doFill) {
1188 switch (pathPtr->getFillType()) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001189 case SkPath::kWinding_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001190 fill = kWinding_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001191 break;
1192 case SkPath::kEvenOdd_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001193 fill = kEvenOdd_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001194 break;
1195 case SkPath::kInverseWinding_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001196 fill = kInverseWinding_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001197 break;
1198 case SkPath::kInverseEvenOdd_FillType:
bsalomon@google.comffca4002011-02-22 20:34:01 +00001199 fill = kInverseEvenOdd_PathFill;
reed@google.comac10a2d2010-12-22 21:39:39 +00001200 break;
1201 default:
bsalomon@google.com5782d712011-01-21 21:03:59 +00001202 SkDebugf("Unsupported path fill type\n");
reed@google.comac10a2d2010-12-22 21:39:39 +00001203 return;
1204 }
1205 }
1206
reed@google.com07f3ee12011-05-16 17:21:57 +00001207 fContext->drawPath(grPaint, *pathPtr, fill);
reed@google.comac10a2d2010-12-22 21:39:39 +00001208}
1209
reed@google.comac10a2d2010-12-22 21:39:39 +00001210void SkGpuDevice::drawBitmap(const SkDraw& draw,
1211 const SkBitmap& bitmap,
1212 const SkIRect* srcRectPtr,
1213 const SkMatrix& m,
1214 const SkPaint& paint) {
1215 CHECK_SHOULD_DRAW(draw);
1216
1217 SkIRect srcRect;
1218 if (NULL == srcRectPtr) {
1219 srcRect.set(0, 0, bitmap.width(), bitmap.height());
1220 } else {
1221 srcRect = *srcRectPtr;
1222 }
1223
junov@google.comd935cfb2011-06-27 20:48:23 +00001224 if (paint.getMaskFilter()){
epoger@google.com9ef2d832011-07-01 21:12:20 +00001225 SkBitmap tmp; // storage if we need a subset of bitmap
junov@google.comd935cfb2011-06-27 20:48:23 +00001226 const SkBitmap* bitmapPtr = &bitmap;
epoger@google.com9ef2d832011-07-01 21:12:20 +00001227 if (srcRectPtr) {
1228 if (!bitmap.extractSubset(&tmp, srcRect)) {
1229 return; // extraction failed
1230 }
1231 bitmapPtr = &tmp;
junov@google.comd935cfb2011-06-27 20:48:23 +00001232 }
1233 SkPaint paintWithTexture(paint);
1234 paintWithTexture.setShader(SkShader::CreateBitmapShader( *bitmapPtr,
1235 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
1236 paintWithTexture.getShader()->setLocalMatrix(m);
1237
1238 SkRect ScalarRect;
epoger@google.com9ef2d832011-07-01 21:12:20 +00001239 ScalarRect.set(srcRect);
junov@google.comd935cfb2011-06-27 20:48:23 +00001240
epoger@google.com9ef2d832011-07-01 21:12:20 +00001241 if (m.rectStaysRect()) {
1242 // Preferred drawing method, optimized for rectangles
1243 m.mapRect(&ScalarRect);
1244 this->drawRect(draw, ScalarRect, paintWithTexture);
1245 } else {
1246 // Slower drawing method, for warped or rotated rectangles
1247 SkPath path;
1248 path.addRect(ScalarRect);
1249 path.transform(m);
1250 this->drawPath(draw, path, paintWithTexture, NULL, true);
1251 }
junov@google.comd935cfb2011-06-27 20:48:23 +00001252 return;
1253 }
1254
bsalomon@google.com5782d712011-01-21 21:03:59 +00001255 GrPaint grPaint;
Scroggod757df22011-05-16 13:11:16 +00001256 if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001257 return;
1258 }
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001259 GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001260 if (paint.isFilterBitmap()) {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001261 sampler->setFilter(GrSamplerState::kBilinear_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001262 } else {
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001263 sampler->setFilter(GrSamplerState::kNearest_Filter);
bsalomon@google.com6aef1fb2011-05-05 12:33:22 +00001264 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001265
bsalomon@google.com91958362011-06-13 17:58:13 +00001266 const int maxTextureSize = fContext->getMaxTextureSize();
1267 if (bitmap.getTexture() || (bitmap.width() <= maxTextureSize &&
1268 bitmap.height() <= maxTextureSize)) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001269 // take the fast case
bsalomon@google.com5782d712011-01-21 21:03:59 +00001270 this->internalDrawBitmap(draw, bitmap, srcRect, m, &grPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +00001271 return;
1272 }
1273
1274 // undo the translate done by SkCanvas
1275 int DX = SkMax32(0, srcRect.fLeft);
1276 int DY = SkMax32(0, srcRect.fTop);
1277 // compute clip bounds in local coordinates
1278 SkIRect clipRect;
1279 {
1280 SkRect r;
1281 r.set(draw.fClip->getBounds());
1282 SkMatrix matrix, inverse;
1283 matrix.setConcat(*draw.fMatrix, m);
1284 if (!matrix.invert(&inverse)) {
1285 return;
1286 }
1287 inverse.mapRect(&r);
1288 r.roundOut(&clipRect);
1289 // apply the canvas' translate to our local clip
1290 clipRect.offset(DX, DY);
1291 }
1292
bsalomon@google.com91958362011-06-13 17:58:13 +00001293 int nx = bitmap.width() / maxTextureSize;
1294 int ny = bitmap.height() / maxTextureSize;
reed@google.comac10a2d2010-12-22 21:39:39 +00001295 for (int x = 0; x <= nx; x++) {
1296 for (int y = 0; y <= ny; y++) {
1297 SkIRect tileR;
bsalomon@google.com91958362011-06-13 17:58:13 +00001298 tileR.set(x * maxTextureSize, y * maxTextureSize,
1299 (x + 1) * maxTextureSize, (y + 1) * maxTextureSize);
reed@google.comac10a2d2010-12-22 21:39:39 +00001300 if (!SkIRect::Intersects(tileR, clipRect)) {
1301 continue;
1302 }
1303
1304 SkIRect srcR = tileR;
1305 if (!srcR.intersect(srcRect)) {
1306 continue;
1307 }
1308
1309 SkBitmap tmpB;
1310 if (bitmap.extractSubset(&tmpB, tileR)) {
1311 // now offset it to make it "local" to our tmp bitmap
1312 srcR.offset(-tileR.fLeft, -tileR.fTop);
1313
1314 SkMatrix tmpM(m);
1315 {
1316 int dx = tileR.fLeft - DX + SkMax32(0, srcR.fLeft);
1317 int dy = tileR.fTop - DY + SkMax32(0, srcR.fTop);
1318 tmpM.preTranslate(SkIntToScalar(dx), SkIntToScalar(dy));
1319 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001320 this->internalDrawBitmap(draw, tmpB, srcR, tmpM, &grPaint);
reed@google.comac10a2d2010-12-22 21:39:39 +00001321 }
1322 }
1323 }
1324}
1325
1326/*
1327 * This is called by drawBitmap(), which has to handle images that may be too
1328 * large to be represented by a single texture.
1329 *
bsalomon@google.com5782d712011-01-21 21:03:59 +00001330 * internalDrawBitmap assumes that the specified bitmap will fit in a texture
1331 * and that non-texture portion of the GrPaint has already been setup.
reed@google.comac10a2d2010-12-22 21:39:39 +00001332 */
1333void SkGpuDevice::internalDrawBitmap(const SkDraw& draw,
1334 const SkBitmap& bitmap,
1335 const SkIRect& srcRect,
1336 const SkMatrix& m,
bsalomon@google.com5782d712011-01-21 21:03:59 +00001337 GrPaint* grPaint) {
bsalomon@google.com91958362011-06-13 17:58:13 +00001338 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
1339 bitmap.height() <= fContext->getMaxTextureSize());
reed@google.comac10a2d2010-12-22 21:39:39 +00001340
reed@google.com9c49bc32011-07-07 13:42:37 +00001341 SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
reed@google.comac10a2d2010-12-22 21:39:39 +00001342 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
reed@google.com9c49bc32011-07-07 13:42:37 +00001343 SkDebugf("nothing to draw\n");
reed@google.comac10a2d2010-12-22 21:39:39 +00001344 return;
1345 }
1346
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001347 GrSamplerState* sampler = grPaint->getTextureSampler(kBitmapTextureIdx);
1348
1349 sampler->setWrapX(GrSamplerState::kClamp_WrapMode);
1350 sampler->setWrapY(GrSamplerState::kClamp_WrapMode);
1351 sampler->setSampleMode(GrSamplerState::kNormal_SampleMode);
1352 sampler->setMatrix(GrMatrix::I());
reed@google.comac10a2d2010-12-22 21:39:39 +00001353
1354 GrTexture* texture;
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001355 SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
reed@google.comac10a2d2010-12-22 21:39:39 +00001356 if (NULL == texture) {
1357 return;
1358 }
1359
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001360 grPaint->setTexture(kShaderTextureIdx, texture);
reed@google.com46799cd2011-02-22 20:56:26 +00001361
reed@google.com20efde72011-05-09 17:00:02 +00001362 GrRect dstRect = SkRect::MakeWH(GrIntToScalar(srcRect.width()),
1363 GrIntToScalar(srcRect.height()));
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001364 GrRect paintRect;
junov@google.com6acc9b32011-05-16 18:32:07 +00001365 paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()),
1366 GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()),
1367 GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()),
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001368 GrFixedToScalar((srcRect.fBottom << 16) / bitmap.height()));
reed@google.comac10a2d2010-12-22 21:39:39 +00001369
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001370 if (GrSamplerState::kNearest_Filter != sampler->getFilter() &&
junov@google.com6acc9b32011-05-16 18:32:07 +00001371 (srcRect.width() < bitmap.width() ||
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001372 srcRect.height() < bitmap.height())) {
junov@google.com6acc9b32011-05-16 18:32:07 +00001373 // If drawing a subrect of the bitmap and filtering is enabled,
1374 // use a constrained texture domain to avoid color bleeding
1375 GrScalar left, top, right, bottom;
1376 if (srcRect.width() > 1) {
1377 GrScalar border = GR_ScalarHalf / bitmap.width();
1378 left = paintRect.left() + border;
1379 right = paintRect.right() - border;
1380 } else {
1381 left = right = GrScalarHalf(paintRect.left() + paintRect.right());
1382 }
1383 if (srcRect.height() > 1) {
1384 GrScalar border = GR_ScalarHalf / bitmap.height();
1385 top = paintRect.top() + border;
1386 bottom = paintRect.bottom() - border;
1387 } else {
1388 top = bottom = GrScalarHalf(paintRect.top() + paintRect.bottom());
1389 }
1390 GrRect textureDomain;
1391 textureDomain.setLTRB(left, top, right, bottom);
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001392 sampler->setTextureDomain(textureDomain);
junov@google.com6acc9b32011-05-16 18:32:07 +00001393 }
1394
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001395 fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
reed@google.comac10a2d2010-12-22 21:39:39 +00001396}
1397
1398void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
1399 int left, int top, const SkPaint& paint) {
1400 CHECK_SHOULD_DRAW(draw);
1401
1402 SkAutoLockPixels alp(bitmap);
1403 if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
1404 return;
1405 }
1406
bsalomon@google.com5782d712011-01-21 21:03:59 +00001407 GrPaint grPaint;
Scroggod757df22011-05-16 13:11:16 +00001408 if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001409 return;
1410 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001411
bsalomon@google.com5782d712011-01-21 21:03:59 +00001412 GrAutoMatrix avm(fContext, GrMatrix::I());
1413
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001414 GrSamplerState* sampler = grPaint.getTextureSampler(kBitmapTextureIdx);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001415
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001416 GrTexture* texture;
1417 sampler->setClampNoFilter();
1418 SkAutoCachedTexture act(this, bitmap, *sampler, &texture);
1419
1420 grPaint.setTexture(kBitmapTextureIdx, texture);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001421
bsalomon@google.com5782d712011-01-21 21:03:59 +00001422 fContext->drawRectToRect(grPaint,
reed@google.com20efde72011-05-09 17:00:02 +00001423 GrRect::MakeXYWH(GrIntToScalar(left),
1424 GrIntToScalar(top),
1425 GrIntToScalar(bitmap.width()),
1426 GrIntToScalar(bitmap.height())),
1427 GrRect::MakeWH(GR_Scalar1, GR_Scalar1));
reed@google.comac10a2d2010-12-22 21:39:39 +00001428}
1429
1430void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
1431 int x, int y, const SkPaint& paint) {
1432 CHECK_SHOULD_DRAW(draw);
1433
bsalomon@google.com5782d712011-01-21 21:03:59 +00001434 GrPaint grPaint;
bsalomon@google.comc6cf7232011-02-17 16:43:10 +00001435 if (!((SkGpuDevice*)dev)->bindDeviceAsTexture(&grPaint) ||
Scroggod757df22011-05-16 13:11:16 +00001436 !this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001437 return;
reed@google.comac10a2d2010-12-22 21:39:39 +00001438 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001439
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001440 GrTexture* devTex = grPaint.getTexture(0);
1441 SkASSERT(NULL != devTex);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001442
1443 const SkBitmap& bm = dev->accessBitmap(false);
1444 int w = bm.width();
1445 int h = bm.height();
1446
1447 GrAutoMatrix avm(fContext, GrMatrix::I());
1448
bsalomon@google.com26c2d0a2011-05-17 20:15:30 +00001449 grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter();
bsalomon@google.com5782d712011-01-21 21:03:59 +00001450
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001451 GrRect dstRect = GrRect::MakeXYWH(GrIntToScalar(x),
1452 GrIntToScalar(y),
1453 GrIntToScalar(w),
1454 GrIntToScalar(h));
1455 // The device being drawn may not fill up its texture (saveLayer uses
1456 // the approximate ).
1457 GrRect srcRect = GrRect::MakeWH(GR_Scalar1 * w / devTex->width(),
1458 GR_Scalar1 * h / devTex->height());
1459
1460 fContext->drawRectToRect(grPaint, dstRect, srcRect);
reed@google.comac10a2d2010-12-22 21:39:39 +00001461}
1462
1463///////////////////////////////////////////////////////////////////////////////
1464
1465// must be in SkCanvas::VertexMode order
bsalomon@google.comffca4002011-02-22 20:34:01 +00001466static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
1467 kTriangles_PrimitiveType,
1468 kTriangleStrip_PrimitiveType,
1469 kTriangleFan_PrimitiveType,
reed@google.comac10a2d2010-12-22 21:39:39 +00001470};
1471
1472void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
1473 int vertexCount, const SkPoint vertices[],
1474 const SkPoint texs[], const SkColor colors[],
1475 SkXfermode* xmode,
1476 const uint16_t indices[], int indexCount,
1477 const SkPaint& paint) {
1478 CHECK_SHOULD_DRAW(draw);
1479
bsalomon@google.com5782d712011-01-21 21:03:59 +00001480 GrPaint grPaint;
1481 SkAutoCachedTexture act;
1482 // we ignore the shader if texs is null.
1483 if (NULL == texs) {
Scroggod757df22011-05-16 13:11:16 +00001484 if (!this->skPaint2GrPaintNoShader(paint,
1485 false,
1486 &grPaint,
1487 NULL == colors)) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001488 return;
1489 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001490 } else {
reed@google.com1a2e8d22011-01-21 22:08:29 +00001491 if (!this->skPaint2GrPaintShader(paint, &act,
1492 *draw.fMatrix,
Scroggod757df22011-05-16 13:11:16 +00001493 &grPaint,
1494 NULL == colors)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001495 return;
1496 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001497 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001498
1499 if (NULL != xmode && NULL != texs && NULL != colors) {
1500 SkXfermode::Mode mode;
1501 if (!SkXfermode::IsMode(xmode, &mode) ||
1502 SkXfermode::kMultiply_Mode != mode) {
1503 SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
1504#if 0
1505 return
1506#endif
1507 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001508 }
bsalomon@google.com5782d712011-01-21 21:03:59 +00001509
1510#if SK_SCALAR_IS_GR_SCALAR
1511 // even if GrColor and SkColor byte offsets match we need
1512 // to perform pre-multiply.
1513 if (NULL == colors) {
1514 fContext->drawVertices(grPaint,
1515 gVertexMode2PrimitiveType[vmode],
1516 vertexCount,
1517 (GrPoint*) vertices,
1518 (GrPoint*) texs,
1519 NULL,
1520 indices,
1521 indexCount);
1522 } else
1523#endif
1524 {
1525 SkTexCoordSource texSrc(texs);
1526 SkColorSource colSrc(colors);
1527 SkIndexSource idxSrc(indices, indexCount);
1528
1529 fContext->drawCustomVertices(grPaint,
1530 gVertexMode2PrimitiveType[vmode],
1531 SkPositionSource(vertices, vertexCount),
1532 (NULL == texs) ? NULL : &texSrc,
1533 (NULL == colors) ? NULL : &colSrc,
1534 (NULL == indices) ? NULL : &idxSrc);
reed@google.comac10a2d2010-12-22 21:39:39 +00001535 }
1536}
1537
1538///////////////////////////////////////////////////////////////////////////////
1539
1540static void GlyphCacheAuxProc(void* data) {
1541 delete (GrFontScaler*)data;
1542}
1543
1544static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
1545 void* auxData;
1546 GrFontScaler* scaler = NULL;
1547 if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
1548 scaler = (GrFontScaler*)auxData;
1549 }
1550 if (NULL == scaler) {
1551 scaler = new SkGrFontScaler(cache);
1552 cache->setAuxProc(GlyphCacheAuxProc, scaler);
1553 }
1554 return scaler;
1555}
1556
1557static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
1558 SkFixed fx, SkFixed fy,
1559 const SkGlyph& glyph) {
1560 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1561
1562 GrSkDrawProcs* procs = (GrSkDrawProcs*)state.fDraw->fProcs;
1563
1564 if (NULL == procs->fFontScaler) {
1565 procs->fFontScaler = get_gr_font_scaler(state.fCache);
1566 }
reed@google.com39ce0ac2011-04-08 15:42:19 +00001567
1568 /*
reed@google.com3b139f52011-06-07 17:56:25 +00001569 * What should we do with fy? (assuming horizontal/latin text)
reed@google.com39ce0ac2011-04-08 15:42:19 +00001570 *
reed@google.com3b139f52011-06-07 17:56:25 +00001571 * The raster code calls SkFixedFloorToFixed on it, as it does with fx.
1572 * It calls that rather than round, because our caller has already added
1573 * SK_FixedHalf, so that calling floor gives us the rounded integer.
1574 *
1575 * Test code between raster and gpu (they should draw the same)
1576 *
1577 * canvas->drawText("Hamburgefons", 12, 0, 16.5f, paint);
1578 *
1579 * Perhaps we should only perform this integralization if there is no
1580 * fExtMatrix...
reed@google.com39ce0ac2011-04-08 15:42:19 +00001581 */
reed@google.com3b139f52011-06-07 17:56:25 +00001582 fy = SkFixedFloorToFixed(fy);
1583
reed@google.comac10a2d2010-12-22 21:39:39 +00001584 procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), fx, 0),
reed@google.com3b139f52011-06-07 17:56:25 +00001585 SkFixedFloorToFixed(fx), fy,
reed@google.comac10a2d2010-12-22 21:39:39 +00001586 procs->fFontScaler);
1587}
1588
bsalomon@google.com5782d712011-01-21 21:03:59 +00001589SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {
reed@google.comac10a2d2010-12-22 21:39:39 +00001590
1591 // deferred allocation
1592 if (NULL == fDrawProcs) {
1593 fDrawProcs = new GrSkDrawProcs;
1594 fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
1595 fDrawProcs->fContext = fContext;
1596 }
1597
1598 // init our (and GL's) state
1599 fDrawProcs->fTextContext = context;
1600 fDrawProcs->fFontScaler = NULL;
1601 return fDrawProcs;
1602}
1603
1604void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
1605 size_t byteLength, SkScalar x, SkScalar y,
1606 const SkPaint& paint) {
1607 CHECK_SHOULD_DRAW(draw);
1608
1609 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1610 // this guy will just call our drawPath()
1611 draw.drawText((const char*)text, byteLength, x, y, paint);
1612 } else {
reed@google.comac10a2d2010-12-22 21:39:39 +00001613 SkDraw myDraw(draw);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001614
1615 GrPaint grPaint;
1616 SkAutoCachedTexture act;
1617
Scroggod757df22011-05-16 13:11:16 +00001618 if (!this->skPaint2GrPaintShader(paint,
1619 &act,
1620 *draw.fMatrix,
1621 &grPaint,
1622 true)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001623 return;
1624 }
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001625 GrTextContext context(fContext, grPaint, draw.fExtMatrix);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001626 myDraw.fProcs = this->initDrawForText(&context);
reed@google.comac10a2d2010-12-22 21:39:39 +00001627 this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
1628 }
1629}
1630
1631void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
1632 size_t byteLength, const SkScalar pos[],
1633 SkScalar constY, int scalarsPerPos,
1634 const SkPaint& paint) {
1635 CHECK_SHOULD_DRAW(draw);
1636
1637 if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1638 // this guy will just call our drawPath()
1639 draw.drawPosText((const char*)text, byteLength, pos, constY,
1640 scalarsPerPos, paint);
1641 } else {
reed@google.comac10a2d2010-12-22 21:39:39 +00001642 SkDraw myDraw(draw);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001643
1644 GrPaint grPaint;
1645 SkAutoCachedTexture act;
Scroggod757df22011-05-16 13:11:16 +00001646 if (!this->skPaint2GrPaintShader(paint,
1647 &act,
1648 *draw.fMatrix,
1649 &grPaint,
1650 true)) {
bsalomon@google.com5782d712011-01-21 21:03:59 +00001651 return;
1652 }
1653
bsalomon@google.comcc4dac32011-05-10 13:52:42 +00001654 GrTextContext context(fContext, grPaint, draw.fExtMatrix);
bsalomon@google.com5782d712011-01-21 21:03:59 +00001655 myDraw.fProcs = this->initDrawForText(&context);
reed@google.comac10a2d2010-12-22 21:39:39 +00001656 this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
1657 scalarsPerPos, paint);
1658 }
1659}
1660
1661void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
1662 size_t len, const SkPath& path,
1663 const SkMatrix* m, const SkPaint& paint) {
1664 CHECK_SHOULD_DRAW(draw);
1665
1666 SkASSERT(draw.fDevice == this);
1667 draw.drawTextOnPath((const char*)text, len, path, m, paint);
1668}
1669
1670///////////////////////////////////////////////////////////////////////////////
1671
reed@google.comf67e4cf2011-03-15 20:56:58 +00001672bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
1673 if (!paint.isLCDRenderText()) {
1674 // we're cool with the paint as is
1675 return false;
1676 }
1677
1678 if (paint.getShader() ||
1679 paint.getXfermode() || // unless its srcover
1680 paint.getMaskFilter() ||
1681 paint.getRasterizer() ||
1682 paint.getColorFilter() ||
1683 paint.getPathEffect() ||
1684 paint.isFakeBoldText() ||
1685 paint.getStyle() != SkPaint::kFill_Style) {
1686 // turn off lcd
1687 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag;
1688 flags->fHinting = paint.getHinting();
1689 return true;
1690 }
1691 // we're cool with the paint as is
1692 return false;
1693}
1694
1695///////////////////////////////////////////////////////////////////////////////
1696
reed@google.comac10a2d2010-12-22 21:39:39 +00001697SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001698 const GrSamplerState& sampler,
1699 GrTexture** texture,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001700 TexType type) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001701 GrTexture* newTexture = NULL;
1702 GrTextureEntry* entry = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +00001703 GrContext* ctx = this->context();
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001704
bsalomon@google.come97f0852011-06-17 13:10:25 +00001705 if (kBitmap_TexType != type) {
bsalomon@google.comfea37b52011-04-25 15:51:06 +00001706 const GrTextureDesc desc = {
1707 kRenderTarget_GrTextureFlagBit,
1708 kNone_GrAALevel,
1709 bitmap.width(),
1710 bitmap.height(),
1711 SkGr::Bitmap2PixelConfig(bitmap)
1712 };
bsalomon@google.come97f0852011-06-17 13:10:25 +00001713 if (kSaveLayerDeviceRenderTarget_TexType == type) {
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001714 // we know layers will only be drawn through drawDevice.
1715 // drawDevice has been made to work with content embedded in a
1716 // larger texture so its okay to use the approximate version.
1717 entry = ctx->findApproximateKeylessTexture(desc);
1718 } else {
bsalomon@google.come97f0852011-06-17 13:10:25 +00001719 SkASSERT(kDeviceRenderTarget_TexType == type);
bsalomon@google.comb5b31682011-06-16 18:05:35 +00001720 entry = ctx->lockKeylessTexture(desc);
1721 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001722 } else {
junov@google.com4ee7ae52011-06-30 17:30:49 +00001723 if (!bitmap.isVolatile()) {
1724 uint32_t p0, p1;
1725 p0 = bitmap.getGenerationID();
1726 p1 = bitmap.pixelRefOffset();
1727 GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
1728
1729 entry = ctx->findAndLockTexture(&key, sampler);
1730 if (NULL == entry)
1731 entry = sk_gr_create_bitmap_texture(ctx, &key, sampler,
1732 bitmap);
1733 } else {
1734 entry = sk_gr_create_bitmap_texture(ctx, NULL, sampler, bitmap);
1735 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001736 if (NULL == entry) {
junov@google.com4ee7ae52011-06-30 17:30:49 +00001737 GrPrintf("---- failed to create texture for cache [%d %d]\n",
1738 bitmap.width(), bitmap.height());
reed@google.comac10a2d2010-12-22 21:39:39 +00001739 }
1740 }
1741
1742 if (NULL != entry) {
1743 newTexture = entry->texture();
reed@google.comac10a2d2010-12-22 21:39:39 +00001744 if (texture) {
1745 *texture = newTexture;
1746 }
reed@google.comac10a2d2010-12-22 21:39:39 +00001747 }
1748 return (TexCache*)entry;
1749}
1750
1751void SkGpuDevice::unlockCachedTexture(TexCache* cache) {
1752 this->context()->unlockTexture((GrTextureEntry*)cache);
1753}
1754
bsalomon@google.come97f0852011-06-17 13:10:25 +00001755SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
1756 int width, int height,
1757 bool isOpaque,
1758 Usage usage) {
1759 return SkNEW_ARGS(SkGpuDevice,(this->context(), config,
1760 width, height, usage));
1761}
1762