|
1 /* |
|
2 * Copyright 2011 Google Inc. |
|
3 * |
|
4 * Use of this source code is governed by a BSD-style license that can be |
|
5 * found in the LICENSE file. |
|
6 */ |
|
7 |
|
8 #include "SkGpuDevice.h" |
|
9 |
|
10 #include "effects/GrBicubicEffect.h" |
|
11 #include "effects/GrTextureDomain.h" |
|
12 #include "effects/GrSimpleTextureEffect.h" |
|
13 |
|
14 #include "GrContext.h" |
|
15 #include "GrBitmapTextContext.h" |
|
16 #include "GrDistanceFieldTextContext.h" |
|
17 |
|
18 #include "SkGrTexturePixelRef.h" |
|
19 |
|
20 #include "SkBounder.h" |
|
21 #include "SkColorFilter.h" |
|
22 #include "SkDeviceImageFilterProxy.h" |
|
23 #include "SkDrawProcs.h" |
|
24 #include "SkGlyphCache.h" |
|
25 #include "SkImageFilter.h" |
|
26 #include "SkMaskFilter.h" |
|
27 #include "SkPathEffect.h" |
|
28 #include "SkPicture.h" |
|
29 #include "SkRRect.h" |
|
30 #include "SkStroke.h" |
|
31 #include "SkSurface.h" |
|
32 #include "SkTLazy.h" |
|
33 #include "SkUtils.h" |
|
34 #include "SkErrorInternals.h" |
|
35 |
|
36 #define CACHE_COMPATIBLE_DEVICE_TEXTURES 1 |
|
37 |
|
38 #if 0 |
|
39 extern bool (*gShouldDrawProc)(); |
|
40 #define CHECK_SHOULD_DRAW(draw, forceI) \ |
|
41 do { \ |
|
42 if (gShouldDrawProc && !gShouldDrawProc()) return; \ |
|
43 this->prepareDraw(draw, forceI); \ |
|
44 } while (0) |
|
45 #else |
|
46 #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI) |
|
47 #endif |
|
48 |
|
49 // This constant represents the screen alignment criterion in texels for |
|
50 // requiring texture domain clamping to prevent color bleeding when drawing |
|
51 // a sub region of a larger source image. |
|
52 #define COLOR_BLEED_TOLERANCE 0.001f |
|
53 |
|
54 #define DO_DEFERRED_CLEAR() \ |
|
55 do { \ |
|
56 if (fNeedClear) { \ |
|
57 this->clear(SK_ColorTRANSPARENT); \ |
|
58 } \ |
|
59 } while (false) \ |
|
60 |
|
61 /////////////////////////////////////////////////////////////////////////////// |
|
62 |
|
63 #define CHECK_FOR_ANNOTATION(paint) \ |
|
64 do { if (paint.getAnnotation()) { return; } } while (0) |
|
65 |
|
66 /////////////////////////////////////////////////////////////////////////////// |
|
67 |
|
68 |
|
69 class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable { |
|
70 public: |
|
71 SkAutoCachedTexture() |
|
72 : fDevice(NULL) |
|
73 , fTexture(NULL) { |
|
74 } |
|
75 |
|
76 SkAutoCachedTexture(SkGpuDevice* device, |
|
77 const SkBitmap& bitmap, |
|
78 const GrTextureParams* params, |
|
79 GrTexture** texture) |
|
80 : fDevice(NULL) |
|
81 , fTexture(NULL) { |
|
82 SkASSERT(NULL != texture); |
|
83 *texture = this->set(device, bitmap, params); |
|
84 } |
|
85 |
|
86 ~SkAutoCachedTexture() { |
|
87 if (NULL != fTexture) { |
|
88 GrUnlockAndUnrefCachedBitmapTexture(fTexture); |
|
89 } |
|
90 } |
|
91 |
|
92 GrTexture* set(SkGpuDevice* device, |
|
93 const SkBitmap& bitmap, |
|
94 const GrTextureParams* params) { |
|
95 if (NULL != fTexture) { |
|
96 GrUnlockAndUnrefCachedBitmapTexture(fTexture); |
|
97 fTexture = NULL; |
|
98 } |
|
99 fDevice = device; |
|
100 GrTexture* result = (GrTexture*)bitmap.getTexture(); |
|
101 if (NULL == result) { |
|
102 // Cannot return the native texture so look it up in our cache |
|
103 fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params); |
|
104 result = fTexture; |
|
105 } |
|
106 return result; |
|
107 } |
|
108 |
|
109 private: |
|
110 SkGpuDevice* fDevice; |
|
111 GrTexture* fTexture; |
|
112 }; |
|
113 |
|
114 /////////////////////////////////////////////////////////////////////////////// |
|
115 |
|
116 struct GrSkDrawProcs : public SkDrawProcs { |
|
117 public: |
|
118 GrContext* fContext; |
|
119 GrTextContext* fTextContext; |
|
120 GrFontScaler* fFontScaler; // cached in the skia glyphcache |
|
121 }; |
|
122 |
|
123 /////////////////////////////////////////////////////////////////////////////// |
|
124 |
|
125 static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) { |
|
126 switch (config) { |
|
127 case kAlpha_8_GrPixelConfig: |
|
128 *isOpaque = false; |
|
129 return SkBitmap::kA8_Config; |
|
130 case kRGB_565_GrPixelConfig: |
|
131 *isOpaque = true; |
|
132 return SkBitmap::kRGB_565_Config; |
|
133 case kRGBA_4444_GrPixelConfig: |
|
134 *isOpaque = false; |
|
135 return SkBitmap::kARGB_4444_Config; |
|
136 case kSkia8888_GrPixelConfig: |
|
137 // we don't currently have a way of knowing whether |
|
138 // a 8888 is opaque based on the config. |
|
139 *isOpaque = false; |
|
140 return SkBitmap::kARGB_8888_Config; |
|
141 default: |
|
142 *isOpaque = false; |
|
143 return SkBitmap::kNo_Config; |
|
144 } |
|
145 } |
|
146 |
|
147 /* |
|
148 * GrRenderTarget does not know its opaqueness, only its config, so we have |
|
149 * to make conservative guesses when we return an "equivalent" bitmap. |
|
150 */ |
|
151 static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) { |
|
152 bool isOpaque; |
|
153 SkBitmap::Config config = grConfig2skConfig(renderTarget->config(), &isOpaque); |
|
154 |
|
155 SkBitmap bitmap; |
|
156 bitmap.setConfig(config, renderTarget->width(), renderTarget->height(), 0, |
|
157 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); |
|
158 return bitmap; |
|
159 } |
|
160 |
|
161 SkGpuDevice* SkGpuDevice::Create(GrSurface* surface) { |
|
162 SkASSERT(NULL != surface); |
|
163 if (NULL == surface->asRenderTarget() || NULL == surface->getContext()) { |
|
164 return NULL; |
|
165 } |
|
166 if (surface->asTexture()) { |
|
167 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asTexture())); |
|
168 } else { |
|
169 return SkNEW_ARGS(SkGpuDevice, (surface->getContext(), surface->asRenderTarget())); |
|
170 } |
|
171 } |
|
172 |
|
173 SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture) |
|
174 : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { |
|
175 this->initFromRenderTarget(context, texture->asRenderTarget(), false); |
|
176 } |
|
177 |
|
178 SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget) |
|
179 : SkBitmapDevice(make_bitmap(context, renderTarget)) { |
|
180 this->initFromRenderTarget(context, renderTarget, false); |
|
181 } |
|
182 |
|
183 void SkGpuDevice::initFromRenderTarget(GrContext* context, |
|
184 GrRenderTarget* renderTarget, |
|
185 bool cached) { |
|
186 fDrawProcs = NULL; |
|
187 |
|
188 fContext = context; |
|
189 fContext->ref(); |
|
190 |
|
191 fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties)); |
|
192 fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); |
|
193 |
|
194 fRenderTarget = NULL; |
|
195 fNeedClear = false; |
|
196 |
|
197 SkASSERT(NULL != renderTarget); |
|
198 fRenderTarget = renderTarget; |
|
199 fRenderTarget->ref(); |
|
200 |
|
201 // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref |
|
202 // on the RT but not vice-versa. |
|
203 // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without |
|
204 // busting chrome (for a currently unknown reason). |
|
205 GrSurface* surface = fRenderTarget->asTexture(); |
|
206 if (NULL == surface) { |
|
207 surface = fRenderTarget; |
|
208 } |
|
209 |
|
210 SkImageInfo info; |
|
211 surface->asImageInfo(&info); |
|
212 SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface, cached)); |
|
213 |
|
214 this->setPixelRef(pr)->unref(); |
|
215 } |
|
216 |
|
217 SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo, |
|
218 int sampleCount) { |
|
219 if (kUnknown_SkColorType == origInfo.colorType() || |
|
220 origInfo.width() < 0 || origInfo.height() < 0) { |
|
221 return NULL; |
|
222 } |
|
223 |
|
224 SkImageInfo info = origInfo; |
|
225 // TODO: perhas we can loosen this check now that colortype is more detailed |
|
226 // e.g. can we support both RGBA and BGRA here? |
|
227 if (kRGB_565_SkColorType == info.colorType()) { |
|
228 info.fAlphaType = kOpaque_SkAlphaType; // force this setting |
|
229 } else { |
|
230 info.fColorType = kPMColor_SkColorType; |
|
231 if (kOpaque_SkAlphaType != info.alphaType()) { |
|
232 info.fAlphaType = kPremul_SkAlphaType; // force this setting |
|
233 } |
|
234 } |
|
235 |
|
236 GrTextureDesc desc; |
|
237 desc.fFlags = kRenderTarget_GrTextureFlagBit; |
|
238 desc.fWidth = info.width(); |
|
239 desc.fHeight = info.height(); |
|
240 desc.fConfig = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType()); |
|
241 desc.fSampleCnt = sampleCount; |
|
242 |
|
243 SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0)); |
|
244 if (!texture.get()) { |
|
245 return NULL; |
|
246 } |
|
247 |
|
248 return SkNEW_ARGS(SkGpuDevice, (context, texture.get())); |
|
249 } |
|
250 |
|
251 #ifdef SK_SUPPORT_LEGACY_COMPATIBLEDEVICE_CONFIG |
|
252 static SkBitmap make_bitmap(SkBitmap::Config config, int width, int height) { |
|
253 SkBitmap bm; |
|
254 bm.setConfig(SkImageInfo::Make(width, height, |
|
255 SkBitmapConfigToColorType(config), |
|
256 kPremul_SkAlphaType)); |
|
257 return bm; |
|
258 } |
|
259 SkGpuDevice::SkGpuDevice(GrContext* context, |
|
260 SkBitmap::Config config, |
|
261 int width, |
|
262 int height, |
|
263 int sampleCount) |
|
264 : SkBitmapDevice(make_bitmap(config, width, height)) |
|
265 { |
|
266 fDrawProcs = NULL; |
|
267 |
|
268 fContext = context; |
|
269 fContext->ref(); |
|
270 |
|
271 fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties)); |
|
272 fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); |
|
273 |
|
274 fRenderTarget = NULL; |
|
275 fNeedClear = false; |
|
276 |
|
277 if (config != SkBitmap::kRGB_565_Config) { |
|
278 config = SkBitmap::kARGB_8888_Config; |
|
279 } |
|
280 |
|
281 GrTextureDesc desc; |
|
282 desc.fFlags = kRenderTarget_GrTextureFlagBit; |
|
283 desc.fWidth = width; |
|
284 desc.fHeight = height; |
|
285 desc.fConfig = SkBitmapConfig2GrPixelConfig(config); |
|
286 desc.fSampleCnt = sampleCount; |
|
287 |
|
288 SkImageInfo info; |
|
289 if (!GrPixelConfig2ColorType(desc.fConfig, &info.fColorType)) { |
|
290 sk_throw(); |
|
291 } |
|
292 info.fWidth = width; |
|
293 info.fHeight = height; |
|
294 info.fAlphaType = kPremul_SkAlphaType; |
|
295 |
|
296 SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0)); |
|
297 |
|
298 if (NULL != texture) { |
|
299 fRenderTarget = texture->asRenderTarget(); |
|
300 fRenderTarget->ref(); |
|
301 |
|
302 SkASSERT(NULL != fRenderTarget); |
|
303 |
|
304 // wrap the bitmap with a pixelref to expose our texture |
|
305 SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, texture)); |
|
306 this->setPixelRef(pr)->unref(); |
|
307 } else { |
|
308 GrPrintf("--- failed to create gpu-offscreen [%d %d]\n", |
|
309 width, height); |
|
310 SkASSERT(false); |
|
311 } |
|
312 } |
|
313 #endif |
|
314 |
|
315 SkGpuDevice::~SkGpuDevice() { |
|
316 if (fDrawProcs) { |
|
317 delete fDrawProcs; |
|
318 } |
|
319 |
|
320 delete fMainTextContext; |
|
321 delete fFallbackTextContext; |
|
322 |
|
323 // The GrContext takes a ref on the target. We don't want to cause the render |
|
324 // target to be unnecessarily kept alive. |
|
325 if (fContext->getRenderTarget() == fRenderTarget) { |
|
326 fContext->setRenderTarget(NULL); |
|
327 } |
|
328 |
|
329 if (fContext->getClip() == &fClipData) { |
|
330 fContext->setClip(NULL); |
|
331 } |
|
332 |
|
333 SkSafeUnref(fRenderTarget); |
|
334 fContext->unref(); |
|
335 } |
|
336 |
|
337 /////////////////////////////////////////////////////////////////////////////// |
|
338 |
|
339 void SkGpuDevice::makeRenderTargetCurrent() { |
|
340 DO_DEFERRED_CLEAR(); |
|
341 fContext->setRenderTarget(fRenderTarget); |
|
342 } |
|
343 |
|
344 /////////////////////////////////////////////////////////////////////////////// |
|
345 |
|
346 namespace { |
|
347 GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) { |
|
348 switch (config8888) { |
|
349 case SkCanvas::kNative_Premul_Config8888: |
|
350 *flags = 0; |
|
351 return kSkia8888_GrPixelConfig; |
|
352 case SkCanvas::kNative_Unpremul_Config8888: |
|
353 *flags = GrContext::kUnpremul_PixelOpsFlag; |
|
354 return kSkia8888_GrPixelConfig; |
|
355 case SkCanvas::kBGRA_Premul_Config8888: |
|
356 *flags = 0; |
|
357 return kBGRA_8888_GrPixelConfig; |
|
358 case SkCanvas::kBGRA_Unpremul_Config8888: |
|
359 *flags = GrContext::kUnpremul_PixelOpsFlag; |
|
360 return kBGRA_8888_GrPixelConfig; |
|
361 case SkCanvas::kRGBA_Premul_Config8888: |
|
362 *flags = 0; |
|
363 return kRGBA_8888_GrPixelConfig; |
|
364 case SkCanvas::kRGBA_Unpremul_Config8888: |
|
365 *flags = GrContext::kUnpremul_PixelOpsFlag; |
|
366 return kRGBA_8888_GrPixelConfig; |
|
367 default: |
|
368 GrCrash("Unexpected Config8888."); |
|
369 *flags = 0; // suppress warning |
|
370 return kSkia8888_GrPixelConfig; |
|
371 } |
|
372 } |
|
373 } |
|
374 |
|
375 bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap, |
|
376 int x, int y, |
|
377 SkCanvas::Config8888 config8888) { |
|
378 DO_DEFERRED_CLEAR(); |
|
379 SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config()); |
|
380 SkASSERT(!bitmap.isNull()); |
|
381 SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))); |
|
382 |
|
383 SkAutoLockPixels alp(bitmap); |
|
384 GrPixelConfig config; |
|
385 uint32_t flags; |
|
386 config = config8888_to_grconfig_and_flags(config8888, &flags); |
|
387 return fContext->readRenderTargetPixels(fRenderTarget, |
|
388 x, y, |
|
389 bitmap.width(), |
|
390 bitmap.height(), |
|
391 config, |
|
392 bitmap.getPixels(), |
|
393 bitmap.rowBytes(), |
|
394 flags); |
|
395 } |
|
396 |
|
397 #ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG |
|
398 void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y, |
|
399 SkCanvas::Config8888 config8888) { |
|
400 SkAutoLockPixels alp(bitmap); |
|
401 if (!bitmap.readyToDraw()) { |
|
402 return; |
|
403 } |
|
404 |
|
405 GrPixelConfig config; |
|
406 uint32_t flags; |
|
407 if (SkBitmap::kARGB_8888_Config == bitmap.config()) { |
|
408 config = config8888_to_grconfig_and_flags(config8888, &flags); |
|
409 } else { |
|
410 flags = 0; |
|
411 config= SkBitmapConfig2GrPixelConfig(bitmap.config()); |
|
412 } |
|
413 |
|
414 fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(), |
|
415 config, bitmap.getPixels(), bitmap.rowBytes(), flags); |
|
416 } |
|
417 #endif |
|
418 |
|
419 bool SkGpuDevice::onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, |
|
420 int x, int y) { |
|
421 // TODO: teach fRenderTarget to take ImageInfo directly to specify the src pixels |
|
422 GrPixelConfig config = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType()); |
|
423 if (kUnknown_GrPixelConfig == config) { |
|
424 return false; |
|
425 } |
|
426 uint32_t flags = 0; |
|
427 if (kUnpremul_SkAlphaType == info.alphaType()) { |
|
428 flags = GrContext::kUnpremul_PixelOpsFlag; |
|
429 } |
|
430 fRenderTarget->writePixels(x, y, info.width(), info.height(), config, pixels, rowBytes, flags); |
|
431 |
|
432 // need to bump our genID for compatibility with clients that "know" we have a bitmap |
|
433 this->onAccessBitmap().notifyPixelsChanged(); |
|
434 |
|
435 return true; |
|
436 } |
|
437 |
|
438 void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) { |
|
439 INHERITED::onAttachToCanvas(canvas); |
|
440 |
|
441 // Canvas promises that this ptr is valid until onDetachFromCanvas is called |
|
442 fClipData.fClipStack = canvas->getClipStack(); |
|
443 } |
|
444 |
|
445 void SkGpuDevice::onDetachFromCanvas() { |
|
446 INHERITED::onDetachFromCanvas(); |
|
447 fClipData.fClipStack = NULL; |
|
448 } |
|
449 |
|
450 // call this every draw call, to ensure that the context reflects our state, |
|
451 // and not the state from some other canvas/device |
|
452 void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) { |
|
453 SkASSERT(NULL != fClipData.fClipStack); |
|
454 |
|
455 fContext->setRenderTarget(fRenderTarget); |
|
456 |
|
457 SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack); |
|
458 |
|
459 if (forceIdentity) { |
|
460 fContext->setIdentityMatrix(); |
|
461 } else { |
|
462 fContext->setMatrix(*draw.fMatrix); |
|
463 } |
|
464 fClipData.fOrigin = this->getOrigin(); |
|
465 |
|
466 fContext->setClip(&fClipData); |
|
467 |
|
468 DO_DEFERRED_CLEAR(); |
|
469 } |
|
470 |
|
471 GrRenderTarget* SkGpuDevice::accessRenderTarget() { |
|
472 DO_DEFERRED_CLEAR(); |
|
473 return fRenderTarget; |
|
474 } |
|
475 |
|
476 /////////////////////////////////////////////////////////////////////////////// |
|
477 |
|
478 SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch); |
|
479 SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch); |
|
480 SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch); |
|
481 SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch); |
|
482 SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4, |
|
483 shader_type_mismatch); |
|
484 SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5, |
|
485 shader_type_mismatch); |
|
486 SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch); |
|
487 SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch); |
|
488 |
|
489 namespace { |
|
490 |
|
491 // converts a SkPaint to a GrPaint, ignoring the skPaint's shader |
|
492 // justAlpha indicates that skPaint's alpha should be used rather than the color |
|
493 // Callers may subsequently modify the GrPaint. Setting constantColor indicates |
|
494 // that the final paint will draw the same color at every pixel. This allows |
|
495 // an optimization where the the color filter can be applied to the skPaint's |
|
496 // color once while converting to GrPaint and then ignored. |
|
497 inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev, |
|
498 const SkPaint& skPaint, |
|
499 bool justAlpha, |
|
500 bool constantColor, |
|
501 GrPaint* grPaint) { |
|
502 |
|
503 grPaint->setDither(skPaint.isDither()); |
|
504 grPaint->setAntiAlias(skPaint.isAntiAlias()); |
|
505 |
|
506 SkXfermode::Coeff sm; |
|
507 SkXfermode::Coeff dm; |
|
508 |
|
509 SkXfermode* mode = skPaint.getXfermode(); |
|
510 GrEffectRef* xferEffect = NULL; |
|
511 if (SkXfermode::AsNewEffectOrCoeff(mode, &xferEffect, &sm, &dm)) { |
|
512 if (NULL != xferEffect) { |
|
513 grPaint->addColorEffect(xferEffect)->unref(); |
|
514 sm = SkXfermode::kOne_Coeff; |
|
515 dm = SkXfermode::kZero_Coeff; |
|
516 } |
|
517 } else { |
|
518 //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");) |
|
519 #if 0 |
|
520 return false; |
|
521 #else |
|
522 // Fall back to src-over |
|
523 sm = SkXfermode::kOne_Coeff; |
|
524 dm = SkXfermode::kISA_Coeff; |
|
525 #endif |
|
526 } |
|
527 grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm)); |
|
528 |
|
529 if (justAlpha) { |
|
530 uint8_t alpha = skPaint.getAlpha(); |
|
531 grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha)); |
|
532 // justAlpha is currently set to true only if there is a texture, |
|
533 // so constantColor should not also be true. |
|
534 SkASSERT(!constantColor); |
|
535 } else { |
|
536 grPaint->setColor(SkColor2GrColor(skPaint.getColor())); |
|
537 } |
|
538 |
|
539 SkColorFilter* colorFilter = skPaint.getColorFilter(); |
|
540 if (NULL != colorFilter) { |
|
541 // if the source color is a constant then apply the filter here once rather than per pixel |
|
542 // in a shader. |
|
543 if (constantColor) { |
|
544 SkColor filtered = colorFilter->filterColor(skPaint.getColor()); |
|
545 grPaint->setColor(SkColor2GrColor(filtered)); |
|
546 } else { |
|
547 SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context())); |
|
548 if (NULL != effect.get()) { |
|
549 grPaint->addColorEffect(effect); |
|
550 } |
|
551 } |
|
552 } |
|
553 |
|
554 return true; |
|
555 } |
|
556 |
|
557 // This function is similar to skPaint2GrPaintNoShader but also converts |
|
558 // skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to |
|
559 // be used is set on grPaint and returned in param act. constantColor has the |
|
560 // same meaning as in skPaint2GrPaintNoShader. |
|
561 inline bool skPaint2GrPaintShader(SkGpuDevice* dev, |
|
562 const SkPaint& skPaint, |
|
563 bool constantColor, |
|
564 GrPaint* grPaint) { |
|
565 SkShader* shader = skPaint.getShader(); |
|
566 if (NULL == shader) { |
|
567 return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint); |
|
568 } |
|
569 |
|
570 // SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state and require |
|
571 // the shader to set a render target . |
|
572 GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL); |
|
573 |
|
574 // setup the shader as the first color effect on the paint |
|
575 SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint)); |
|
576 if (NULL != effect.get()) { |
|
577 grPaint->addColorEffect(effect); |
|
578 // Now setup the rest of the paint. |
|
579 return skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint); |
|
580 } else { |
|
581 // We still don't have SkColorShader::asNewEffect() implemented. |
|
582 SkShader::GradientInfo info; |
|
583 SkColor color; |
|
584 |
|
585 info.fColors = &color; |
|
586 info.fColorOffsets = NULL; |
|
587 info.fColorCount = 1; |
|
588 if (SkShader::kColor_GradientType == shader->asAGradient(&info)) { |
|
589 SkPaint copy(skPaint); |
|
590 copy.setShader(NULL); |
|
591 // modulate the paint alpha by the shader's solid color alpha |
|
592 U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha()); |
|
593 copy.setColor(SkColorSetA(color, newA)); |
|
594 return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint); |
|
595 } else { |
|
596 return false; |
|
597 } |
|
598 } |
|
599 } |
|
600 } |
|
601 |
|
602 /////////////////////////////////////////////////////////////////////////////// |
|
603 |
|
604 SkBitmap::Config SkGpuDevice::config() const { |
|
605 if (NULL == fRenderTarget) { |
|
606 return SkBitmap::kNo_Config; |
|
607 } |
|
608 |
|
609 bool isOpaque; |
|
610 return grConfig2skConfig(fRenderTarget->config(), &isOpaque); |
|
611 } |
|
612 |
|
613 void SkGpuDevice::clear(SkColor color) { |
|
614 SkIRect rect = SkIRect::MakeWH(this->width(), this->height()); |
|
615 fContext->clear(&rect, SkColor2GrColor(color), true, fRenderTarget); |
|
616 fNeedClear = false; |
|
617 } |
|
618 |
|
619 void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { |
|
620 CHECK_SHOULD_DRAW(draw, false); |
|
621 |
|
622 GrPaint grPaint; |
|
623 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
624 return; |
|
625 } |
|
626 |
|
627 fContext->drawPaint(grPaint); |
|
628 } |
|
629 |
|
630 // must be in SkCanvas::PointMode order |
|
631 static const GrPrimitiveType gPointMode2PrimtiveType[] = { |
|
632 kPoints_GrPrimitiveType, |
|
633 kLines_GrPrimitiveType, |
|
634 kLineStrip_GrPrimitiveType |
|
635 }; |
|
636 |
|
637 void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, |
|
638 size_t count, const SkPoint pts[], const SkPaint& paint) { |
|
639 CHECK_FOR_ANNOTATION(paint); |
|
640 CHECK_SHOULD_DRAW(draw, false); |
|
641 |
|
642 SkScalar width = paint.getStrokeWidth(); |
|
643 if (width < 0) { |
|
644 return; |
|
645 } |
|
646 |
|
647 // we only handle hairlines and paints without path effects or mask filters, |
|
648 // else we let the SkDraw call our drawPath() |
|
649 if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) { |
|
650 draw.drawPoints(mode, count, pts, paint, true); |
|
651 return; |
|
652 } |
|
653 |
|
654 GrPaint grPaint; |
|
655 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
656 return; |
|
657 } |
|
658 |
|
659 fContext->drawVertices(grPaint, |
|
660 gPointMode2PrimtiveType[mode], |
|
661 SkToS32(count), |
|
662 (GrPoint*)pts, |
|
663 NULL, |
|
664 NULL, |
|
665 NULL, |
|
666 0); |
|
667 } |
|
668 |
|
669 /////////////////////////////////////////////////////////////////////////////// |
|
670 |
|
671 void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, |
|
672 const SkPaint& paint) { |
|
673 CHECK_FOR_ANNOTATION(paint); |
|
674 CHECK_SHOULD_DRAW(draw, false); |
|
675 |
|
676 bool doStroke = paint.getStyle() != SkPaint::kFill_Style; |
|
677 SkScalar width = paint.getStrokeWidth(); |
|
678 |
|
679 /* |
|
680 We have special code for hairline strokes, miter-strokes, bevel-stroke |
|
681 and fills. Anything else we just call our path code. |
|
682 */ |
|
683 bool usePath = doStroke && width > 0 && |
|
684 (paint.getStrokeJoin() == SkPaint::kRound_Join || |
|
685 (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty())); |
|
686 // another two reasons we might need to call drawPath... |
|
687 if (paint.getMaskFilter() || paint.getPathEffect()) { |
|
688 usePath = true; |
|
689 } |
|
690 if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) { |
|
691 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) |
|
692 if (doStroke) { |
|
693 #endif |
|
694 usePath = true; |
|
695 #if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT) |
|
696 } else { |
|
697 usePath = !fContext->getMatrix().preservesRightAngles(); |
|
698 } |
|
699 #endif |
|
700 } |
|
701 // until we can both stroke and fill rectangles |
|
702 if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) { |
|
703 usePath = true; |
|
704 } |
|
705 |
|
706 if (usePath) { |
|
707 SkPath path; |
|
708 path.addRect(rect); |
|
709 this->drawPath(draw, path, paint, NULL, true); |
|
710 return; |
|
711 } |
|
712 |
|
713 GrPaint grPaint; |
|
714 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
715 return; |
|
716 } |
|
717 |
|
718 if (!doStroke) { |
|
719 fContext->drawRect(grPaint, rect); |
|
720 } else { |
|
721 SkStrokeRec stroke(paint); |
|
722 fContext->drawRect(grPaint, rect, &stroke); |
|
723 } |
|
724 } |
|
725 |
|
726 /////////////////////////////////////////////////////////////////////////////// |
|
727 |
|
728 void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect, |
|
729 const SkPaint& paint) { |
|
730 CHECK_FOR_ANNOTATION(paint); |
|
731 CHECK_SHOULD_DRAW(draw, false); |
|
732 |
|
733 GrPaint grPaint; |
|
734 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
735 return; |
|
736 } |
|
737 |
|
738 SkStrokeRec stroke(paint); |
|
739 if (paint.getMaskFilter()) { |
|
740 // try to hit the fast path for drawing filtered round rects |
|
741 |
|
742 SkRRect devRRect; |
|
743 if (rect.transform(fContext->getMatrix(), &devRRect)) { |
|
744 if (devRRect.allCornersCircular()) { |
|
745 SkRect maskRect; |
|
746 if (paint.getMaskFilter()->canFilterMaskGPU(devRRect.rect(), |
|
747 draw.fClip->getBounds(), |
|
748 fContext->getMatrix(), |
|
749 &maskRect)) { |
|
750 SkIRect finalIRect; |
|
751 maskRect.roundOut(&finalIRect); |
|
752 if (draw.fClip->quickReject(finalIRect)) { |
|
753 // clipped out |
|
754 return; |
|
755 } |
|
756 if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) { |
|
757 // nothing to draw |
|
758 return; |
|
759 } |
|
760 if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext, &grPaint, |
|
761 stroke, devRRect)) { |
|
762 return; |
|
763 } |
|
764 } |
|
765 |
|
766 } |
|
767 } |
|
768 |
|
769 } |
|
770 |
|
771 bool usePath = !rect.isSimple(); |
|
772 // another two reasons we might need to call drawPath... |
|
773 if (paint.getMaskFilter() || paint.getPathEffect()) { |
|
774 usePath = true; |
|
775 } |
|
776 // until we can rotate rrects... |
|
777 if (!usePath && !fContext->getMatrix().rectStaysRect()) { |
|
778 usePath = true; |
|
779 } |
|
780 |
|
781 if (usePath) { |
|
782 SkPath path; |
|
783 path.addRRect(rect); |
|
784 this->drawPath(draw, path, paint, NULL, true); |
|
785 return; |
|
786 } |
|
787 |
|
788 fContext->drawRRect(grPaint, rect, stroke); |
|
789 } |
|
790 |
|
791 ///////////////////////////////////////////////////////////////////////////// |
|
792 |
|
793 void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval, |
|
794 const SkPaint& paint) { |
|
795 CHECK_FOR_ANNOTATION(paint); |
|
796 CHECK_SHOULD_DRAW(draw, false); |
|
797 |
|
798 bool usePath = false; |
|
799 // some basic reasons we might need to call drawPath... |
|
800 if (paint.getMaskFilter() || paint.getPathEffect()) { |
|
801 usePath = true; |
|
802 } |
|
803 |
|
804 if (usePath) { |
|
805 SkPath path; |
|
806 path.addOval(oval); |
|
807 this->drawPath(draw, path, paint, NULL, true); |
|
808 return; |
|
809 } |
|
810 |
|
811 GrPaint grPaint; |
|
812 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
813 return; |
|
814 } |
|
815 SkStrokeRec stroke(paint); |
|
816 |
|
817 fContext->drawOval(grPaint, oval, stroke); |
|
818 } |
|
819 |
|
820 #include "SkMaskFilter.h" |
|
821 #include "SkBounder.h" |
|
822 |
|
823 /////////////////////////////////////////////////////////////////////////////// |
|
824 |
|
825 // helpers for applying mask filters |
|
826 namespace { |
|
827 |
|
828 // Draw a mask using the supplied paint. Since the coverage/geometry |
|
829 // is already burnt into the mask this boils down to a rect draw. |
|
830 // Return true if the mask was successfully drawn. |
|
831 bool draw_mask(GrContext* context, const SkRect& maskRect, |
|
832 GrPaint* grp, GrTexture* mask) { |
|
833 GrContext::AutoMatrix am; |
|
834 if (!am.setIdentity(context, grp)) { |
|
835 return false; |
|
836 } |
|
837 |
|
838 SkMatrix matrix; |
|
839 matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop); |
|
840 matrix.postIDiv(mask->width(), mask->height()); |
|
841 |
|
842 grp->addCoverageEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref(); |
|
843 context->drawRect(*grp, maskRect); |
|
844 return true; |
|
845 } |
|
846 |
|
847 bool draw_with_mask_filter(GrContext* context, const SkPath& devPath, |
|
848 SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder, |
|
849 GrPaint* grp, SkPaint::Style style) { |
|
850 SkMask srcM, dstM; |
|
851 |
|
852 if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM, |
|
853 SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) { |
|
854 return false; |
|
855 } |
|
856 SkAutoMaskFreeImage autoSrc(srcM.fImage); |
|
857 |
|
858 if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) { |
|
859 return false; |
|
860 } |
|
861 // this will free-up dstM when we're done (allocated in filterMask()) |
|
862 SkAutoMaskFreeImage autoDst(dstM.fImage); |
|
863 |
|
864 if (clip.quickReject(dstM.fBounds)) { |
|
865 return false; |
|
866 } |
|
867 if (bounder && !bounder->doIRect(dstM.fBounds)) { |
|
868 return false; |
|
869 } |
|
870 |
|
871 // we now have a device-aligned 8bit mask in dstM, ready to be drawn using |
|
872 // the current clip (and identity matrix) and GrPaint settings |
|
873 GrTextureDesc desc; |
|
874 desc.fWidth = dstM.fBounds.width(); |
|
875 desc.fHeight = dstM.fBounds.height(); |
|
876 desc.fConfig = kAlpha_8_GrPixelConfig; |
|
877 |
|
878 GrAutoScratchTexture ast(context, desc); |
|
879 GrTexture* texture = ast.texture(); |
|
880 |
|
881 if (NULL == texture) { |
|
882 return false; |
|
883 } |
|
884 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, |
|
885 dstM.fImage, dstM.fRowBytes); |
|
886 |
|
887 SkRect maskRect = SkRect::Make(dstM.fBounds); |
|
888 |
|
889 return draw_mask(context, maskRect, grp, texture); |
|
890 } |
|
891 |
|
892 // Create a mask of 'devPath' and place the result in 'mask'. Return true on |
|
893 // success; false otherwise. |
|
894 bool create_mask_GPU(GrContext* context, |
|
895 const SkRect& maskRect, |
|
896 const SkPath& devPath, |
|
897 const SkStrokeRec& stroke, |
|
898 bool doAA, |
|
899 GrAutoScratchTexture* mask) { |
|
900 GrTextureDesc desc; |
|
901 desc.fFlags = kRenderTarget_GrTextureFlagBit; |
|
902 desc.fWidth = SkScalarCeilToInt(maskRect.width()); |
|
903 desc.fHeight = SkScalarCeilToInt(maskRect.height()); |
|
904 // We actually only need A8, but it often isn't supported as a |
|
905 // render target so default to RGBA_8888 |
|
906 desc.fConfig = kRGBA_8888_GrPixelConfig; |
|
907 if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { |
|
908 desc.fConfig = kAlpha_8_GrPixelConfig; |
|
909 } |
|
910 |
|
911 mask->set(context, desc); |
|
912 if (NULL == mask->texture()) { |
|
913 return false; |
|
914 } |
|
915 |
|
916 GrTexture* maskTexture = mask->texture(); |
|
917 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height()); |
|
918 |
|
919 GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget()); |
|
920 GrContext::AutoClip ac(context, clipRect); |
|
921 |
|
922 context->clear(NULL, 0x0, true); |
|
923 |
|
924 GrPaint tempPaint; |
|
925 if (doAA) { |
|
926 tempPaint.setAntiAlias(true); |
|
927 // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst |
|
928 // blend coeff of zero requires dual source blending support in order |
|
929 // to properly blend partially covered pixels. This means the AA |
|
930 // code path may not be taken. So we use a dst blend coeff of ISA. We |
|
931 // could special case AA draws to a dst surface with known alpha=0 to |
|
932 // use a zero dst coeff when dual source blending isn't available. |
|
933 tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); |
|
934 } |
|
935 |
|
936 GrContext::AutoMatrix am; |
|
937 |
|
938 // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint. |
|
939 SkMatrix translate; |
|
940 translate.setTranslate(-maskRect.fLeft, -maskRect.fTop); |
|
941 am.set(context, translate); |
|
942 context->drawPath(tempPaint, devPath, stroke); |
|
943 return true; |
|
944 } |
|
945 |
|
946 SkBitmap wrap_texture(GrTexture* texture) { |
|
947 SkImageInfo info; |
|
948 texture->asImageInfo(&info); |
|
949 |
|
950 SkBitmap result; |
|
951 result.setConfig(info); |
|
952 result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); |
|
953 return result; |
|
954 } |
|
955 |
|
956 }; |
|
957 |
|
958 void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, |
|
959 const SkPaint& paint, const SkMatrix* prePathMatrix, |
|
960 bool pathIsMutable) { |
|
961 CHECK_FOR_ANNOTATION(paint); |
|
962 CHECK_SHOULD_DRAW(draw, false); |
|
963 |
|
964 GrPaint grPaint; |
|
965 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
966 return; |
|
967 } |
|
968 |
|
969 // If we have a prematrix, apply it to the path, optimizing for the case |
|
970 // where the original path can in fact be modified in place (even though |
|
971 // its parameter type is const). |
|
972 SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath); |
|
973 SkTLazy<SkPath> tmpPath; |
|
974 SkTLazy<SkPath> effectPath; |
|
975 |
|
976 if (prePathMatrix) { |
|
977 SkPath* result = pathPtr; |
|
978 |
|
979 if (!pathIsMutable) { |
|
980 result = tmpPath.init(); |
|
981 pathIsMutable = true; |
|
982 } |
|
983 // should I push prePathMatrix on our MV stack temporarily, instead |
|
984 // of applying it here? See SkDraw.cpp |
|
985 pathPtr->transform(*prePathMatrix, result); |
|
986 pathPtr = result; |
|
987 } |
|
988 // at this point we're done with prePathMatrix |
|
989 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;) |
|
990 |
|
991 SkStrokeRec stroke(paint); |
|
992 SkPathEffect* pathEffect = paint.getPathEffect(); |
|
993 const SkRect* cullRect = NULL; // TODO: what is our bounds? |
|
994 if (pathEffect && pathEffect->filterPath(effectPath.init(), *pathPtr, &stroke, |
|
995 cullRect)) { |
|
996 pathPtr = effectPath.get(); |
|
997 pathIsMutable = true; |
|
998 } |
|
999 |
|
1000 if (paint.getMaskFilter()) { |
|
1001 if (!stroke.isHairlineStyle()) { |
|
1002 SkPath* strokedPath = pathIsMutable ? pathPtr : tmpPath.init(); |
|
1003 if (stroke.applyToPath(strokedPath, *pathPtr)) { |
|
1004 pathPtr = strokedPath; |
|
1005 pathIsMutable = true; |
|
1006 stroke.setFillStyle(); |
|
1007 } |
|
1008 } |
|
1009 |
|
1010 // avoid possibly allocating a new path in transform if we can |
|
1011 SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath.init(); |
|
1012 |
|
1013 // transform the path into device space |
|
1014 pathPtr->transform(fContext->getMatrix(), devPathPtr); |
|
1015 |
|
1016 SkRect maskRect; |
|
1017 if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(), |
|
1018 draw.fClip->getBounds(), |
|
1019 fContext->getMatrix(), |
|
1020 &maskRect)) { |
|
1021 // The context's matrix may change while creating the mask, so save the CTM here to |
|
1022 // pass to filterMaskGPU. |
|
1023 const SkMatrix ctm = fContext->getMatrix(); |
|
1024 |
|
1025 SkIRect finalIRect; |
|
1026 maskRect.roundOut(&finalIRect); |
|
1027 if (draw.fClip->quickReject(finalIRect)) { |
|
1028 // clipped out |
|
1029 return; |
|
1030 } |
|
1031 if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) { |
|
1032 // nothing to draw |
|
1033 return; |
|
1034 } |
|
1035 |
|
1036 if (paint.getMaskFilter()->directFilterMaskGPU(fContext, &grPaint, |
|
1037 stroke, *devPathPtr)) { |
|
1038 // the mask filter was able to draw itself directly, so there's nothing |
|
1039 // left to do. |
|
1040 return; |
|
1041 } |
|
1042 |
|
1043 GrAutoScratchTexture mask; |
|
1044 |
|
1045 if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke, |
|
1046 grPaint.isAntiAlias(), &mask)) { |
|
1047 GrTexture* filtered; |
|
1048 |
|
1049 if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), |
|
1050 ctm, maskRect, &filtered, true)) { |
|
1051 // filterMaskGPU gives us ownership of a ref to the result |
|
1052 SkAutoTUnref<GrTexture> atu(filtered); |
|
1053 |
|
1054 // If the scratch texture that we used as the filter src also holds the filter |
|
1055 // result then we must detach so that this texture isn't recycled for a later |
|
1056 // draw. |
|
1057 if (filtered == mask.texture()) { |
|
1058 mask.detach(); |
|
1059 filtered->unref(); // detach transfers GrAutoScratchTexture's ref to us. |
|
1060 } |
|
1061 |
|
1062 if (draw_mask(fContext, maskRect, &grPaint, filtered)) { |
|
1063 // This path is completely drawn |
|
1064 return; |
|
1065 } |
|
1066 } |
|
1067 } |
|
1068 } |
|
1069 |
|
1070 // draw the mask on the CPU - this is a fallthrough path in case the |
|
1071 // GPU path fails |
|
1072 SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style : |
|
1073 SkPaint::kFill_Style; |
|
1074 draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(), |
|
1075 *draw.fClip, draw.fBounder, &grPaint, style); |
|
1076 return; |
|
1077 } |
|
1078 |
|
1079 fContext->drawPath(grPaint, *pathPtr, stroke); |
|
1080 } |
|
1081 |
|
1082 static const int kBmpSmallTileSize = 1 << 10; |
|
1083 |
|
1084 static inline int get_tile_count(const SkIRect& srcRect, int tileSize) { |
|
1085 int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1; |
|
1086 int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1; |
|
1087 return tilesX * tilesY; |
|
1088 } |
|
1089 |
|
1090 static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) { |
|
1091 if (maxTileSize <= kBmpSmallTileSize) { |
|
1092 return maxTileSize; |
|
1093 } |
|
1094 |
|
1095 size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize); |
|
1096 size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize); |
|
1097 |
|
1098 maxTileTotalTileSize *= maxTileSize * maxTileSize; |
|
1099 smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize; |
|
1100 |
|
1101 if (maxTileTotalTileSize > 2 * smallTotalTileSize) { |
|
1102 return kBmpSmallTileSize; |
|
1103 } else { |
|
1104 return maxTileSize; |
|
1105 } |
|
1106 } |
|
1107 |
|
1108 // Given a bitmap, an optional src rect, and a context with a clip and matrix determine what |
|
1109 // pixels from the bitmap are necessary. |
|
1110 static void determine_clipped_src_rect(const GrContext* context, |
|
1111 const SkBitmap& bitmap, |
|
1112 const SkRect* srcRectPtr, |
|
1113 SkIRect* clippedSrcIRect) { |
|
1114 const GrClipData* clip = context->getClip(); |
|
1115 clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL); |
|
1116 SkMatrix inv; |
|
1117 if (!context->getMatrix().invert(&inv)) { |
|
1118 clippedSrcIRect->setEmpty(); |
|
1119 return; |
|
1120 } |
|
1121 SkRect clippedSrcRect = SkRect::Make(*clippedSrcIRect); |
|
1122 inv.mapRect(&clippedSrcRect); |
|
1123 if (NULL != srcRectPtr) { |
|
1124 // we've setup src space 0,0 to map to the top left of the src rect. |
|
1125 clippedSrcRect.offset(srcRectPtr->fLeft, srcRectPtr->fTop); |
|
1126 if (!clippedSrcRect.intersect(*srcRectPtr)) { |
|
1127 clippedSrcIRect->setEmpty(); |
|
1128 return; |
|
1129 } |
|
1130 } |
|
1131 clippedSrcRect.roundOut(clippedSrcIRect); |
|
1132 SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); |
|
1133 if (!clippedSrcIRect->intersect(bmpBounds)) { |
|
1134 clippedSrcIRect->setEmpty(); |
|
1135 } |
|
1136 } |
|
1137 |
|
1138 bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap, |
|
1139 const GrTextureParams& params, |
|
1140 const SkRect* srcRectPtr, |
|
1141 int maxTileSize, |
|
1142 int* tileSize, |
|
1143 SkIRect* clippedSrcRect) const { |
|
1144 // if bitmap is explictly texture backed then just use the texture |
|
1145 if (NULL != bitmap.getTexture()) { |
|
1146 return false; |
|
1147 } |
|
1148 |
|
1149 // if it's larger than the max tile size, then we have no choice but tiling. |
|
1150 if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) { |
|
1151 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); |
|
1152 *tileSize = determine_tile_size(bitmap, *clippedSrcRect, maxTileSize); |
|
1153 return true; |
|
1154 } |
|
1155 |
|
1156 if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) { |
|
1157 return false; |
|
1158 } |
|
1159 |
|
1160 // if the entire texture is already in our cache then no reason to tile it |
|
1161 if (GrIsBitmapInCache(fContext, bitmap, ¶ms)) { |
|
1162 return false; |
|
1163 } |
|
1164 |
|
1165 // At this point we know we could do the draw by uploading the entire bitmap |
|
1166 // as a texture. However, if the texture would be large compared to the |
|
1167 // cache size and we don't require most of it for this draw then tile to |
|
1168 // reduce the amount of upload and cache spill. |
|
1169 |
|
1170 // assumption here is that sw bitmap size is a good proxy for its size as |
|
1171 // a texture |
|
1172 size_t bmpSize = bitmap.getSize(); |
|
1173 size_t cacheSize; |
|
1174 fContext->getTextureCacheLimits(NULL, &cacheSize); |
|
1175 if (bmpSize < cacheSize / 2) { |
|
1176 return false; |
|
1177 } |
|
1178 |
|
1179 // Figure out how much of the src we will need based on the src rect and clipping. |
|
1180 determine_clipped_src_rect(fContext, bitmap, srcRectPtr, clippedSrcRect); |
|
1181 *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile. |
|
1182 size_t usedTileBytes = get_tile_count(*clippedSrcRect, kBmpSmallTileSize) * |
|
1183 kBmpSmallTileSize * kBmpSmallTileSize; |
|
1184 |
|
1185 return usedTileBytes < 2 * bmpSize; |
|
1186 } |
|
1187 |
|
1188 void SkGpuDevice::drawBitmap(const SkDraw& origDraw, |
|
1189 const SkBitmap& bitmap, |
|
1190 const SkMatrix& m, |
|
1191 const SkPaint& paint) { |
|
1192 SkMatrix concat; |
|
1193 SkTCopyOnFirstWrite<SkDraw> draw(origDraw); |
|
1194 if (!m.isIdentity()) { |
|
1195 concat.setConcat(*draw->fMatrix, m); |
|
1196 draw.writable()->fMatrix = &concat; |
|
1197 } |
|
1198 this->drawBitmapCommon(*draw, bitmap, NULL, NULL, paint, SkCanvas::kNone_DrawBitmapRectFlag); |
|
1199 } |
|
1200 |
|
1201 // This method outsets 'iRect' by 'outset' all around and then clamps its extents to |
|
1202 // 'clamp'. 'offset' is adjusted to remain positioned over the top-left corner |
|
1203 // of 'iRect' for all possible outsets/clamps. |
|
1204 static inline void clamped_outset_with_offset(SkIRect* iRect, |
|
1205 int outset, |
|
1206 SkPoint* offset, |
|
1207 const SkIRect& clamp) { |
|
1208 iRect->outset(outset, outset); |
|
1209 |
|
1210 int leftClampDelta = clamp.fLeft - iRect->fLeft; |
|
1211 if (leftClampDelta > 0) { |
|
1212 offset->fX -= outset - leftClampDelta; |
|
1213 iRect->fLeft = clamp.fLeft; |
|
1214 } else { |
|
1215 offset->fX -= outset; |
|
1216 } |
|
1217 |
|
1218 int topClampDelta = clamp.fTop - iRect->fTop; |
|
1219 if (topClampDelta > 0) { |
|
1220 offset->fY -= outset - topClampDelta; |
|
1221 iRect->fTop = clamp.fTop; |
|
1222 } else { |
|
1223 offset->fY -= outset; |
|
1224 } |
|
1225 |
|
1226 if (iRect->fRight > clamp.fRight) { |
|
1227 iRect->fRight = clamp.fRight; |
|
1228 } |
|
1229 if (iRect->fBottom > clamp.fBottom) { |
|
1230 iRect->fBottom = clamp.fBottom; |
|
1231 } |
|
1232 } |
|
1233 |
|
1234 void SkGpuDevice::drawBitmapCommon(const SkDraw& draw, |
|
1235 const SkBitmap& bitmap, |
|
1236 const SkRect* srcRectPtr, |
|
1237 const SkSize* dstSizePtr, |
|
1238 const SkPaint& paint, |
|
1239 SkCanvas::DrawBitmapRectFlags flags) { |
|
1240 CHECK_SHOULD_DRAW(draw, false); |
|
1241 |
|
1242 SkRect srcRect; |
|
1243 SkSize dstSize; |
|
1244 // If there is no src rect, or the src rect contains the entire bitmap then we're effectively |
|
1245 // in the (easier) bleed case, so update flags. |
|
1246 if (NULL == srcRectPtr) { |
|
1247 SkScalar w = SkIntToScalar(bitmap.width()); |
|
1248 SkScalar h = SkIntToScalar(bitmap.height()); |
|
1249 dstSize.fWidth = w; |
|
1250 dstSize.fHeight = h; |
|
1251 srcRect.set(0, 0, w, h); |
|
1252 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag); |
|
1253 } else { |
|
1254 SkASSERT(NULL != dstSizePtr); |
|
1255 srcRect = *srcRectPtr; |
|
1256 dstSize = *dstSizePtr; |
|
1257 if (srcRect.fLeft <= 0 && srcRect.fTop <= 0 && |
|
1258 srcRect.fRight >= bitmap.width() && srcRect.fBottom >= bitmap.height()) { |
|
1259 flags = (SkCanvas::DrawBitmapRectFlags) (flags | SkCanvas::kBleed_DrawBitmapRectFlag); |
|
1260 } |
|
1261 } |
|
1262 |
|
1263 if (paint.getMaskFilter()){ |
|
1264 // Convert the bitmap to a shader so that the rect can be drawn |
|
1265 // through drawRect, which supports mask filters. |
|
1266 SkBitmap tmp; // subset of bitmap, if necessary |
|
1267 const SkBitmap* bitmapPtr = &bitmap; |
|
1268 SkMatrix localM; |
|
1269 if (NULL != srcRectPtr) { |
|
1270 localM.setTranslate(-srcRectPtr->fLeft, -srcRectPtr->fTop); |
|
1271 localM.postScale(dstSize.fWidth / srcRectPtr->width(), |
|
1272 dstSize.fHeight / srcRectPtr->height()); |
|
1273 // In bleed mode we position and trim the bitmap based on the src rect which is |
|
1274 // already accounted for in 'm' and 'srcRect'. In clamp mode we need to chop out |
|
1275 // the desired portion of the bitmap and then update 'm' and 'srcRect' to |
|
1276 // compensate. |
|
1277 if (!(SkCanvas::kBleed_DrawBitmapRectFlag & flags)) { |
|
1278 SkIRect iSrc; |
|
1279 srcRect.roundOut(&iSrc); |
|
1280 |
|
1281 SkPoint offset = SkPoint::Make(SkIntToScalar(iSrc.fLeft), |
|
1282 SkIntToScalar(iSrc.fTop)); |
|
1283 |
|
1284 if (!bitmap.extractSubset(&tmp, iSrc)) { |
|
1285 return; // extraction failed |
|
1286 } |
|
1287 bitmapPtr = &tmp; |
|
1288 srcRect.offset(-offset.fX, -offset.fY); |
|
1289 |
|
1290 // The source rect has changed so update the matrix |
|
1291 localM.preTranslate(offset.fX, offset.fY); |
|
1292 } |
|
1293 } else { |
|
1294 localM.reset(); |
|
1295 } |
|
1296 |
|
1297 SkPaint paintWithShader(paint); |
|
1298 paintWithShader.setShader(SkShader::CreateBitmapShader(*bitmapPtr, |
|
1299 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref(); |
|
1300 paintWithShader.getShader()->setLocalMatrix(localM); |
|
1301 SkRect dstRect = {0, 0, dstSize.fWidth, dstSize.fHeight}; |
|
1302 this->drawRect(draw, dstRect, paintWithShader); |
|
1303 |
|
1304 return; |
|
1305 } |
|
1306 |
|
1307 // If there is no mask filter than it is OK to handle the src rect -> dst rect scaling using |
|
1308 // the view matrix rather than a local matrix. |
|
1309 SkMatrix m; |
|
1310 m.setScale(dstSize.fWidth / srcRect.width(), |
|
1311 dstSize.fHeight / srcRect.height()); |
|
1312 fContext->concatMatrix(m); |
|
1313 |
|
1314 GrTextureParams params; |
|
1315 SkPaint::FilterLevel paintFilterLevel = paint.getFilterLevel(); |
|
1316 GrTextureParams::FilterMode textureFilterMode; |
|
1317 |
|
1318 int tileFilterPad; |
|
1319 bool doBicubic = false; |
|
1320 |
|
1321 switch(paintFilterLevel) { |
|
1322 case SkPaint::kNone_FilterLevel: |
|
1323 tileFilterPad = 0; |
|
1324 textureFilterMode = GrTextureParams::kNone_FilterMode; |
|
1325 break; |
|
1326 case SkPaint::kLow_FilterLevel: |
|
1327 tileFilterPad = 1; |
|
1328 textureFilterMode = GrTextureParams::kBilerp_FilterMode; |
|
1329 break; |
|
1330 case SkPaint::kMedium_FilterLevel: |
|
1331 tileFilterPad = 1; |
|
1332 if (fContext->getMatrix().getMinStretch() < SK_Scalar1) { |
|
1333 textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
|
1334 } else { |
|
1335 // Don't trigger MIP level generation unnecessarily. |
|
1336 textureFilterMode = GrTextureParams::kBilerp_FilterMode; |
|
1337 } |
|
1338 break; |
|
1339 case SkPaint::kHigh_FilterLevel: |
|
1340 // Minification can look bad with the bicubic effect. |
|
1341 if (fContext->getMatrix().getMinStretch() >= SK_Scalar1) { |
|
1342 // We will install an effect that does the filtering in the shader. |
|
1343 textureFilterMode = GrTextureParams::kNone_FilterMode; |
|
1344 tileFilterPad = GrBicubicEffect::kFilterTexelPad; |
|
1345 doBicubic = true; |
|
1346 } else { |
|
1347 textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
|
1348 tileFilterPad = 1; |
|
1349 } |
|
1350 break; |
|
1351 default: |
|
1352 SkErrorInternals::SetError( kInvalidPaint_SkError, |
|
1353 "Sorry, I don't understand the filtering " |
|
1354 "mode you asked for. Falling back to " |
|
1355 "MIPMaps."); |
|
1356 tileFilterPad = 1; |
|
1357 textureFilterMode = GrTextureParams::kMipMap_FilterMode; |
|
1358 break; |
|
1359 } |
|
1360 |
|
1361 params.setFilterMode(textureFilterMode); |
|
1362 |
|
1363 int maxTileSize = fContext->getMaxTextureSize() - 2 * tileFilterPad; |
|
1364 int tileSize; |
|
1365 |
|
1366 SkIRect clippedSrcRect; |
|
1367 if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize, |
|
1368 &clippedSrcRect)) { |
|
1369 this->drawTiledBitmap(bitmap, srcRect, clippedSrcRect, params, paint, flags, tileSize, |
|
1370 doBicubic); |
|
1371 } else { |
|
1372 // take the simple case |
|
1373 this->internalDrawBitmap(bitmap, srcRect, params, paint, flags, doBicubic); |
|
1374 } |
|
1375 } |
|
1376 |
|
1377 // Break 'bitmap' into several tiles to draw it since it has already |
|
1378 // been determined to be too large to fit in VRAM |
|
1379 void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap, |
|
1380 const SkRect& srcRect, |
|
1381 const SkIRect& clippedSrcIRect, |
|
1382 const GrTextureParams& params, |
|
1383 const SkPaint& paint, |
|
1384 SkCanvas::DrawBitmapRectFlags flags, |
|
1385 int tileSize, |
|
1386 bool bicubic) { |
|
1387 SkRect clippedSrcRect = SkRect::Make(clippedSrcIRect); |
|
1388 |
|
1389 int nx = bitmap.width() / tileSize; |
|
1390 int ny = bitmap.height() / tileSize; |
|
1391 for (int x = 0; x <= nx; x++) { |
|
1392 for (int y = 0; y <= ny; y++) { |
|
1393 SkRect tileR; |
|
1394 tileR.set(SkIntToScalar(x * tileSize), |
|
1395 SkIntToScalar(y * tileSize), |
|
1396 SkIntToScalar((x + 1) * tileSize), |
|
1397 SkIntToScalar((y + 1) * tileSize)); |
|
1398 |
|
1399 if (!SkRect::Intersects(tileR, clippedSrcRect)) { |
|
1400 continue; |
|
1401 } |
|
1402 |
|
1403 if (!tileR.intersect(srcRect)) { |
|
1404 continue; |
|
1405 } |
|
1406 |
|
1407 SkBitmap tmpB; |
|
1408 SkIRect iTileR; |
|
1409 tileR.roundOut(&iTileR); |
|
1410 SkPoint offset = SkPoint::Make(SkIntToScalar(iTileR.fLeft), |
|
1411 SkIntToScalar(iTileR.fTop)); |
|
1412 |
|
1413 // Adjust the context matrix to draw at the right x,y in device space |
|
1414 SkMatrix tmpM; |
|
1415 GrContext::AutoMatrix am; |
|
1416 tmpM.setTranslate(offset.fX - srcRect.fLeft, offset.fY - srcRect.fTop); |
|
1417 am.setPreConcat(fContext, tmpM); |
|
1418 |
|
1419 if (SkPaint::kNone_FilterLevel != paint.getFilterLevel() || bicubic) { |
|
1420 SkIRect iClampRect; |
|
1421 |
|
1422 if (SkCanvas::kBleed_DrawBitmapRectFlag & flags) { |
|
1423 // In bleed mode we want to always expand the tile on all edges |
|
1424 // but stay within the bitmap bounds |
|
1425 iClampRect = SkIRect::MakeWH(bitmap.width(), bitmap.height()); |
|
1426 } else { |
|
1427 // In texture-domain/clamp mode we only want to expand the |
|
1428 // tile on edges interior to "srcRect" (i.e., we want to |
|
1429 // not bleed across the original clamped edges) |
|
1430 srcRect.roundOut(&iClampRect); |
|
1431 } |
|
1432 int outset = bicubic ? GrBicubicEffect::kFilterTexelPad : 1; |
|
1433 clamped_outset_with_offset(&iTileR, outset, &offset, iClampRect); |
|
1434 } |
|
1435 |
|
1436 if (bitmap.extractSubset(&tmpB, iTileR)) { |
|
1437 // now offset it to make it "local" to our tmp bitmap |
|
1438 tileR.offset(-offset.fX, -offset.fY); |
|
1439 |
|
1440 this->internalDrawBitmap(tmpB, tileR, params, paint, flags, bicubic); |
|
1441 } |
|
1442 } |
|
1443 } |
|
1444 } |
|
1445 |
|
1446 static bool has_aligned_samples(const SkRect& srcRect, |
|
1447 const SkRect& transformedRect) { |
|
1448 // detect pixel disalignment |
|
1449 if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - |
|
1450 transformedRect.left()) < COLOR_BLEED_TOLERANCE && |
|
1451 SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) - |
|
1452 transformedRect.top()) < COLOR_BLEED_TOLERANCE && |
|
1453 SkScalarAbs(transformedRect.width() - srcRect.width()) < |
|
1454 COLOR_BLEED_TOLERANCE && |
|
1455 SkScalarAbs(transformedRect.height() - srcRect.height()) < |
|
1456 COLOR_BLEED_TOLERANCE) { |
|
1457 return true; |
|
1458 } |
|
1459 return false; |
|
1460 } |
|
1461 |
|
1462 static bool may_color_bleed(const SkRect& srcRect, |
|
1463 const SkRect& transformedRect, |
|
1464 const SkMatrix& m) { |
|
1465 // Only gets called if has_aligned_samples returned false. |
|
1466 // So we can assume that sampling is axis aligned but not texel aligned. |
|
1467 SkASSERT(!has_aligned_samples(srcRect, transformedRect)); |
|
1468 SkRect innerSrcRect(srcRect), innerTransformedRect, |
|
1469 outerTransformedRect(transformedRect); |
|
1470 innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf); |
|
1471 m.mapRect(&innerTransformedRect, innerSrcRect); |
|
1472 |
|
1473 // The gap between outerTransformedRect and innerTransformedRect |
|
1474 // represents the projection of the source border area, which is |
|
1475 // problematic for color bleeding. We must check whether any |
|
1476 // destination pixels sample the border area. |
|
1477 outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
|
1478 innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE); |
|
1479 SkIRect outer, inner; |
|
1480 outerTransformedRect.round(&outer); |
|
1481 innerTransformedRect.round(&inner); |
|
1482 // If the inner and outer rects round to the same result, it means the |
|
1483 // border does not overlap any pixel centers. Yay! |
|
1484 return inner != outer; |
|
1485 } |
|
1486 |
|
1487 |
|
1488 /* |
|
1489 * This is called by drawBitmap(), which has to handle images that may be too |
|
1490 * large to be represented by a single texture. |
|
1491 * |
|
1492 * internalDrawBitmap assumes that the specified bitmap will fit in a texture |
|
1493 * and that non-texture portion of the GrPaint has already been setup. |
|
1494 */ |
|
1495 void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap, |
|
1496 const SkRect& srcRect, |
|
1497 const GrTextureParams& params, |
|
1498 const SkPaint& paint, |
|
1499 SkCanvas::DrawBitmapRectFlags flags, |
|
1500 bool bicubic) { |
|
1501 SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() && |
|
1502 bitmap.height() <= fContext->getMaxTextureSize()); |
|
1503 |
|
1504 GrTexture* texture; |
|
1505 SkAutoCachedTexture act(this, bitmap, ¶ms, &texture); |
|
1506 if (NULL == texture) { |
|
1507 return; |
|
1508 } |
|
1509 |
|
1510 SkRect dstRect = {0, 0, srcRect.width(), srcRect.height() }; |
|
1511 SkRect paintRect; |
|
1512 SkScalar wInv = SkScalarInvert(SkIntToScalar(texture->width())); |
|
1513 SkScalar hInv = SkScalarInvert(SkIntToScalar(texture->height())); |
|
1514 paintRect.setLTRB(SkScalarMul(srcRect.fLeft, wInv), |
|
1515 SkScalarMul(srcRect.fTop, hInv), |
|
1516 SkScalarMul(srcRect.fRight, wInv), |
|
1517 SkScalarMul(srcRect.fBottom, hInv)); |
|
1518 |
|
1519 bool needsTextureDomain = false; |
|
1520 if (!(flags & SkCanvas::kBleed_DrawBitmapRectFlag) && |
|
1521 (bicubic || params.filterMode() != GrTextureParams::kNone_FilterMode)) { |
|
1522 // Need texture domain if drawing a sub rect |
|
1523 needsTextureDomain = srcRect.width() < bitmap.width() || |
|
1524 srcRect.height() < bitmap.height(); |
|
1525 if (!bicubic && needsTextureDomain && fContext->getMatrix().rectStaysRect()) { |
|
1526 const SkMatrix& matrix = fContext->getMatrix(); |
|
1527 // sampling is axis-aligned |
|
1528 SkRect transformedRect; |
|
1529 matrix.mapRect(&transformedRect, srcRect); |
|
1530 |
|
1531 if (has_aligned_samples(srcRect, transformedRect)) { |
|
1532 // We could also turn off filtering here (but we already did a cache lookup with |
|
1533 // params). |
|
1534 needsTextureDomain = false; |
|
1535 } else { |
|
1536 needsTextureDomain = may_color_bleed(srcRect, transformedRect, matrix); |
|
1537 } |
|
1538 } |
|
1539 } |
|
1540 |
|
1541 SkRect textureDomain = SkRect::MakeEmpty(); |
|
1542 SkAutoTUnref<GrEffectRef> effect; |
|
1543 if (needsTextureDomain) { |
|
1544 // Use a constrained texture domain to avoid color bleeding |
|
1545 SkScalar left, top, right, bottom; |
|
1546 if (srcRect.width() > SK_Scalar1) { |
|
1547 SkScalar border = SK_ScalarHalf / texture->width(); |
|
1548 left = paintRect.left() + border; |
|
1549 right = paintRect.right() - border; |
|
1550 } else { |
|
1551 left = right = SkScalarHalf(paintRect.left() + paintRect.right()); |
|
1552 } |
|
1553 if (srcRect.height() > SK_Scalar1) { |
|
1554 SkScalar border = SK_ScalarHalf / texture->height(); |
|
1555 top = paintRect.top() + border; |
|
1556 bottom = paintRect.bottom() - border; |
|
1557 } else { |
|
1558 top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom()); |
|
1559 } |
|
1560 textureDomain.setLTRB(left, top, right, bottom); |
|
1561 if (bicubic) { |
|
1562 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), textureDomain)); |
|
1563 } else { |
|
1564 effect.reset(GrTextureDomainEffect::Create(texture, |
|
1565 SkMatrix::I(), |
|
1566 textureDomain, |
|
1567 GrTextureDomain::kClamp_Mode, |
|
1568 params.filterMode())); |
|
1569 } |
|
1570 } else if (bicubic) { |
|
1571 SkASSERT(GrTextureParams::kNone_FilterMode == params.filterMode()); |
|
1572 SkShader::TileMode tileModes[2] = { params.getTileModeX(), params.getTileModeY() }; |
|
1573 effect.reset(GrBicubicEffect::Create(texture, SkMatrix::I(), tileModes)); |
|
1574 } else { |
|
1575 effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params)); |
|
1576 } |
|
1577 |
|
1578 // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring |
|
1579 // the rest from the SkPaint. |
|
1580 GrPaint grPaint; |
|
1581 grPaint.addColorEffect(effect); |
|
1582 bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config()); |
|
1583 if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) { |
|
1584 return; |
|
1585 } |
|
1586 |
|
1587 fContext->drawRectToRect(grPaint, dstRect, paintRect, NULL); |
|
1588 } |
|
1589 |
|
1590 static bool filter_texture(SkBaseDevice* device, GrContext* context, |
|
1591 GrTexture* texture, const SkImageFilter* filter, |
|
1592 int w, int h, const SkImageFilter::Context& ctx, |
|
1593 SkBitmap* result, SkIPoint* offset) { |
|
1594 SkASSERT(filter); |
|
1595 SkDeviceImageFilterProxy proxy(device); |
|
1596 |
|
1597 if (filter->canFilterImageGPU()) { |
|
1598 // Save the render target and set it to NULL, so we don't accidentally draw to it in the |
|
1599 // filter. Also set the clip wide open and the matrix to identity. |
|
1600 GrContext::AutoWideOpenIdentityDraw awo(context, NULL); |
|
1601 return filter->filterImageGPU(&proxy, wrap_texture(texture), ctx, result, offset); |
|
1602 } else { |
|
1603 return false; |
|
1604 } |
|
1605 } |
|
1606 |
|
1607 void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, |
|
1608 int left, int top, const SkPaint& paint) { |
|
1609 // drawSprite is defined to be in device coords. |
|
1610 CHECK_SHOULD_DRAW(draw, true); |
|
1611 |
|
1612 SkAutoLockPixels alp(bitmap, !bitmap.getTexture()); |
|
1613 if (!bitmap.getTexture() && !bitmap.readyToDraw()) { |
|
1614 return; |
|
1615 } |
|
1616 |
|
1617 int w = bitmap.width(); |
|
1618 int h = bitmap.height(); |
|
1619 |
|
1620 GrTexture* texture; |
|
1621 // draw sprite uses the default texture params |
|
1622 SkAutoCachedTexture act(this, bitmap, NULL, &texture); |
|
1623 |
|
1624 SkImageFilter* filter = paint.getImageFilter(); |
|
1625 // This bitmap will own the filtered result as a texture. |
|
1626 SkBitmap filteredBitmap; |
|
1627 |
|
1628 if (NULL != filter) { |
|
1629 SkIPoint offset = SkIPoint::Make(0, 0); |
|
1630 SkMatrix matrix(*draw.fMatrix); |
|
1631 matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top)); |
|
1632 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height()); |
|
1633 SkImageFilter::Context ctx(matrix, clipBounds); |
|
1634 if (filter_texture(this, fContext, texture, filter, w, h, ctx, &filteredBitmap, |
|
1635 &offset)) { |
|
1636 texture = (GrTexture*) filteredBitmap.getTexture(); |
|
1637 w = filteredBitmap.width(); |
|
1638 h = filteredBitmap.height(); |
|
1639 left += offset.x(); |
|
1640 top += offset.y(); |
|
1641 } else { |
|
1642 return; |
|
1643 } |
|
1644 } |
|
1645 |
|
1646 GrPaint grPaint; |
|
1647 grPaint.addColorTextureEffect(texture, SkMatrix::I()); |
|
1648 |
|
1649 if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { |
|
1650 return; |
|
1651 } |
|
1652 |
|
1653 fContext->drawRectToRect(grPaint, |
|
1654 SkRect::MakeXYWH(SkIntToScalar(left), |
|
1655 SkIntToScalar(top), |
|
1656 SkIntToScalar(w), |
|
1657 SkIntToScalar(h)), |
|
1658 SkRect::MakeXYWH(0, |
|
1659 0, |
|
1660 SK_Scalar1 * w / texture->width(), |
|
1661 SK_Scalar1 * h / texture->height())); |
|
1662 } |
|
1663 |
|
1664 void SkGpuDevice::drawBitmapRect(const SkDraw& origDraw, const SkBitmap& bitmap, |
|
1665 const SkRect* src, const SkRect& dst, |
|
1666 const SkPaint& paint, |
|
1667 SkCanvas::DrawBitmapRectFlags flags) { |
|
1668 SkMatrix matrix; |
|
1669 SkRect bitmapBounds, tmpSrc; |
|
1670 |
|
1671 bitmapBounds.set(0, 0, |
|
1672 SkIntToScalar(bitmap.width()), |
|
1673 SkIntToScalar(bitmap.height())); |
|
1674 |
|
1675 // Compute matrix from the two rectangles |
|
1676 if (NULL != src) { |
|
1677 tmpSrc = *src; |
|
1678 } else { |
|
1679 tmpSrc = bitmapBounds; |
|
1680 } |
|
1681 |
|
1682 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); |
|
1683 |
|
1684 // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null. |
|
1685 if (NULL != src) { |
|
1686 if (!bitmapBounds.contains(tmpSrc)) { |
|
1687 if (!tmpSrc.intersect(bitmapBounds)) { |
|
1688 return; // nothing to draw |
|
1689 } |
|
1690 } |
|
1691 } |
|
1692 |
|
1693 SkRect tmpDst; |
|
1694 matrix.mapRect(&tmpDst, tmpSrc); |
|
1695 |
|
1696 SkTCopyOnFirstWrite<SkDraw> draw(origDraw); |
|
1697 if (0 != tmpDst.fLeft || 0 != tmpDst.fTop) { |
|
1698 // Translate so that tempDst's top left is at the origin. |
|
1699 matrix = *origDraw.fMatrix; |
|
1700 matrix.preTranslate(tmpDst.fLeft, tmpDst.fTop); |
|
1701 draw.writable()->fMatrix = &matrix; |
|
1702 } |
|
1703 SkSize dstSize; |
|
1704 dstSize.fWidth = tmpDst.width(); |
|
1705 dstSize.fHeight = tmpDst.height(); |
|
1706 |
|
1707 this->drawBitmapCommon(*draw, bitmap, &tmpSrc, &dstSize, paint, flags); |
|
1708 } |
|
1709 |
|
1710 void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, |
|
1711 int x, int y, const SkPaint& paint) { |
|
1712 // clear of the source device must occur before CHECK_SHOULD_DRAW |
|
1713 SkGpuDevice* dev = static_cast<SkGpuDevice*>(device); |
|
1714 if (dev->fNeedClear) { |
|
1715 // TODO: could check here whether we really need to draw at all |
|
1716 dev->clear(0x0); |
|
1717 } |
|
1718 |
|
1719 // drawDevice is defined to be in device coords. |
|
1720 CHECK_SHOULD_DRAW(draw, true); |
|
1721 |
|
1722 GrRenderTarget* devRT = dev->accessRenderTarget(); |
|
1723 GrTexture* devTex; |
|
1724 if (NULL == (devTex = devRT->asTexture())) { |
|
1725 return; |
|
1726 } |
|
1727 |
|
1728 const SkBitmap& bm = dev->accessBitmap(false); |
|
1729 int w = bm.width(); |
|
1730 int h = bm.height(); |
|
1731 |
|
1732 SkImageFilter* filter = paint.getImageFilter(); |
|
1733 // This bitmap will own the filtered result as a texture. |
|
1734 SkBitmap filteredBitmap; |
|
1735 |
|
1736 if (NULL != filter) { |
|
1737 SkIPoint offset = SkIPoint::Make(0, 0); |
|
1738 SkMatrix matrix(*draw.fMatrix); |
|
1739 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); |
|
1740 SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height()); |
|
1741 SkImageFilter::Context ctx(matrix, clipBounds); |
|
1742 if (filter_texture(this, fContext, devTex, filter, w, h, ctx, &filteredBitmap, |
|
1743 &offset)) { |
|
1744 devTex = filteredBitmap.getTexture(); |
|
1745 w = filteredBitmap.width(); |
|
1746 h = filteredBitmap.height(); |
|
1747 x += offset.fX; |
|
1748 y += offset.fY; |
|
1749 } else { |
|
1750 return; |
|
1751 } |
|
1752 } |
|
1753 |
|
1754 GrPaint grPaint; |
|
1755 grPaint.addColorTextureEffect(devTex, SkMatrix::I()); |
|
1756 |
|
1757 if (!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) { |
|
1758 return; |
|
1759 } |
|
1760 |
|
1761 SkRect dstRect = SkRect::MakeXYWH(SkIntToScalar(x), |
|
1762 SkIntToScalar(y), |
|
1763 SkIntToScalar(w), |
|
1764 SkIntToScalar(h)); |
|
1765 |
|
1766 // The device being drawn may not fill up its texture (e.g. saveLayer uses approximate |
|
1767 // scratch texture). |
|
1768 SkRect srcRect = SkRect::MakeWH(SK_Scalar1 * w / devTex->width(), |
|
1769 SK_Scalar1 * h / devTex->height()); |
|
1770 |
|
1771 fContext->drawRectToRect(grPaint, dstRect, srcRect); |
|
1772 } |
|
1773 |
|
1774 bool SkGpuDevice::canHandleImageFilter(const SkImageFilter* filter) { |
|
1775 return filter->canFilterImageGPU(); |
|
1776 } |
|
1777 |
|
1778 bool SkGpuDevice::filterImage(const SkImageFilter* filter, const SkBitmap& src, |
|
1779 const SkImageFilter::Context& ctx, |
|
1780 SkBitmap* result, SkIPoint* offset) { |
|
1781 // want explicitly our impl, so guard against a subclass of us overriding it |
|
1782 if (!this->SkGpuDevice::canHandleImageFilter(filter)) { |
|
1783 return false; |
|
1784 } |
|
1785 |
|
1786 SkAutoLockPixels alp(src, !src.getTexture()); |
|
1787 if (!src.getTexture() && !src.readyToDraw()) { |
|
1788 return false; |
|
1789 } |
|
1790 |
|
1791 GrTexture* texture; |
|
1792 // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup |
|
1793 // must be pushed upstack. |
|
1794 SkAutoCachedTexture act(this, src, NULL, &texture); |
|
1795 |
|
1796 return filter_texture(this, fContext, texture, filter, src.width(), src.height(), ctx, |
|
1797 result, offset); |
|
1798 } |
|
1799 |
|
1800 /////////////////////////////////////////////////////////////////////////////// |
|
1801 |
|
1802 // must be in SkCanvas::VertexMode order |
|
1803 static const GrPrimitiveType gVertexMode2PrimitiveType[] = { |
|
1804 kTriangles_GrPrimitiveType, |
|
1805 kTriangleStrip_GrPrimitiveType, |
|
1806 kTriangleFan_GrPrimitiveType, |
|
1807 }; |
|
1808 |
|
1809 void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, |
|
1810 int vertexCount, const SkPoint vertices[], |
|
1811 const SkPoint texs[], const SkColor colors[], |
|
1812 SkXfermode* xmode, |
|
1813 const uint16_t indices[], int indexCount, |
|
1814 const SkPaint& paint) { |
|
1815 CHECK_SHOULD_DRAW(draw, false); |
|
1816 |
|
1817 GrPaint grPaint; |
|
1818 // we ignore the shader if texs is null. |
|
1819 if (NULL == texs) { |
|
1820 if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) { |
|
1821 return; |
|
1822 } |
|
1823 } else { |
|
1824 if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) { |
|
1825 return; |
|
1826 } |
|
1827 } |
|
1828 |
|
1829 if (NULL != xmode && NULL != texs && NULL != colors) { |
|
1830 if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) { |
|
1831 SkDebugf("Unsupported vertex-color/texture xfer mode.\n"); |
|
1832 #if 0 |
|
1833 return |
|
1834 #endif |
|
1835 } |
|
1836 } |
|
1837 |
|
1838 SkAutoSTMalloc<128, GrColor> convertedColors(0); |
|
1839 if (NULL != colors) { |
|
1840 // need to convert byte order and from non-PM to PM |
|
1841 convertedColors.reset(vertexCount); |
|
1842 for (int i = 0; i < vertexCount; ++i) { |
|
1843 convertedColors[i] = SkColor2GrColor(colors[i]); |
|
1844 } |
|
1845 colors = convertedColors.get(); |
|
1846 } |
|
1847 fContext->drawVertices(grPaint, |
|
1848 gVertexMode2PrimitiveType[vmode], |
|
1849 vertexCount, |
|
1850 (GrPoint*) vertices, |
|
1851 (GrPoint*) texs, |
|
1852 colors, |
|
1853 indices, |
|
1854 indexCount); |
|
1855 } |
|
1856 |
|
1857 /////////////////////////////////////////////////////////////////////////////// |
|
1858 |
|
1859 void SkGpuDevice::drawText(const SkDraw& draw, const void* text, |
|
1860 size_t byteLength, SkScalar x, SkScalar y, |
|
1861 const SkPaint& paint) { |
|
1862 CHECK_SHOULD_DRAW(draw, false); |
|
1863 |
|
1864 if (fMainTextContext->canDraw(paint)) { |
|
1865 GrPaint grPaint; |
|
1866 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
1867 return; |
|
1868 } |
|
1869 |
|
1870 SkDEBUGCODE(this->validate();) |
|
1871 |
|
1872 fMainTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y); |
|
1873 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) { |
|
1874 GrPaint grPaint; |
|
1875 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
1876 return; |
|
1877 } |
|
1878 |
|
1879 SkDEBUGCODE(this->validate();) |
|
1880 |
|
1881 fFallbackTextContext->drawText(grPaint, paint, (const char *)text, byteLength, x, y); |
|
1882 } else { |
|
1883 // this guy will just call our drawPath() |
|
1884 draw.drawText_asPaths((const char*)text, byteLength, x, y, paint); |
|
1885 } |
|
1886 } |
|
1887 |
|
1888 void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text, |
|
1889 size_t byteLength, const SkScalar pos[], |
|
1890 SkScalar constY, int scalarsPerPos, |
|
1891 const SkPaint& paint) { |
|
1892 CHECK_SHOULD_DRAW(draw, false); |
|
1893 |
|
1894 if (fMainTextContext->canDraw(paint)) { |
|
1895 GrPaint grPaint; |
|
1896 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
1897 return; |
|
1898 } |
|
1899 |
|
1900 SkDEBUGCODE(this->validate();) |
|
1901 |
|
1902 fMainTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos, |
|
1903 constY, scalarsPerPos); |
|
1904 } else if (fFallbackTextContext && fFallbackTextContext->canDraw(paint)) { |
|
1905 GrPaint grPaint; |
|
1906 if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) { |
|
1907 return; |
|
1908 } |
|
1909 |
|
1910 SkDEBUGCODE(this->validate();) |
|
1911 |
|
1912 fFallbackTextContext->drawPosText(grPaint, paint, (const char *)text, byteLength, pos, |
|
1913 constY, scalarsPerPos); |
|
1914 } else { |
|
1915 draw.drawPosText_asPaths((const char*)text, byteLength, pos, constY, |
|
1916 scalarsPerPos, paint); |
|
1917 } |
|
1918 } |
|
1919 |
|
1920 void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text, |
|
1921 size_t len, const SkPath& path, |
|
1922 const SkMatrix* m, const SkPaint& paint) { |
|
1923 CHECK_SHOULD_DRAW(draw, false); |
|
1924 |
|
1925 SkASSERT(draw.fDevice == this); |
|
1926 draw.drawTextOnPath((const char*)text, len, path, m, paint); |
|
1927 } |
|
1928 |
|
1929 /////////////////////////////////////////////////////////////////////////////// |
|
1930 |
|
1931 bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) { |
|
1932 if (!paint.isLCDRenderText()) { |
|
1933 // we're cool with the paint as is |
|
1934 return false; |
|
1935 } |
|
1936 |
|
1937 if (paint.getShader() || |
|
1938 paint.getXfermode() || // unless its srcover |
|
1939 paint.getMaskFilter() || |
|
1940 paint.getRasterizer() || |
|
1941 paint.getColorFilter() || |
|
1942 paint.getPathEffect() || |
|
1943 paint.isFakeBoldText() || |
|
1944 paint.getStyle() != SkPaint::kFill_Style) { |
|
1945 // turn off lcd |
|
1946 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag; |
|
1947 flags->fHinting = paint.getHinting(); |
|
1948 return true; |
|
1949 } |
|
1950 // we're cool with the paint as is |
|
1951 return false; |
|
1952 } |
|
1953 |
|
1954 void SkGpuDevice::flush() { |
|
1955 DO_DEFERRED_CLEAR(); |
|
1956 fContext->resolveRenderTarget(fRenderTarget); |
|
1957 } |
|
1958 |
|
1959 /////////////////////////////////////////////////////////////////////////////// |
|
1960 |
|
1961 SkBaseDevice* SkGpuDevice::onCreateDevice(const SkImageInfo& info, Usage usage) { |
|
1962 GrTextureDesc desc; |
|
1963 desc.fConfig = fRenderTarget->config(); |
|
1964 desc.fFlags = kRenderTarget_GrTextureFlagBit; |
|
1965 desc.fWidth = info.width(); |
|
1966 desc.fHeight = info.height(); |
|
1967 desc.fSampleCnt = fRenderTarget->numSamples(); |
|
1968 |
|
1969 SkAutoTUnref<GrTexture> texture; |
|
1970 // Skia's convention is to only clear a device if it is non-opaque. |
|
1971 bool needClear = !info.isOpaque(); |
|
1972 |
|
1973 #if CACHE_COMPATIBLE_DEVICE_TEXTURES |
|
1974 // layers are never draw in repeat modes, so we can request an approx |
|
1975 // match and ignore any padding. |
|
1976 const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ? |
|
1977 GrContext::kApprox_ScratchTexMatch : |
|
1978 GrContext::kExact_ScratchTexMatch; |
|
1979 texture.reset(fContext->lockAndRefScratchTexture(desc, match)); |
|
1980 #else |
|
1981 texture.reset(fContext->createUncachedTexture(desc, NULL, 0)); |
|
1982 #endif |
|
1983 if (NULL != texture.get()) { |
|
1984 return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear)); |
|
1985 } else { |
|
1986 GrPrintf("---- failed to create compatible device texture [%d %d]\n", |
|
1987 info.width(), info.height()); |
|
1988 return NULL; |
|
1989 } |
|
1990 } |
|
1991 |
|
1992 SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info) { |
|
1993 return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples()); |
|
1994 } |
|
1995 |
|
1996 SkGpuDevice::SkGpuDevice(GrContext* context, |
|
1997 GrTexture* texture, |
|
1998 bool needClear) |
|
1999 : SkBitmapDevice(make_bitmap(context, texture->asRenderTarget())) { |
|
2000 |
|
2001 SkASSERT(texture && texture->asRenderTarget()); |
|
2002 // This constructor is called from onCreateDevice. It has locked the RT in the texture |
|
2003 // cache. We pass true for the third argument so that it will get unlocked. |
|
2004 this->initFromRenderTarget(context, texture->asRenderTarget(), true); |
|
2005 fNeedClear = needClear; |
|
2006 } |
|
2007 |
|
2008 class GPUAccelData : public SkPicture::AccelData { |
|
2009 public: |
|
2010 GPUAccelData(Key key) : INHERITED(key) { } |
|
2011 |
|
2012 protected: |
|
2013 |
|
2014 private: |
|
2015 typedef SkPicture::AccelData INHERITED; |
|
2016 }; |
|
2017 |
|
2018 // In the future this may not be a static method if we need to incorporate the |
|
2019 // clip and matrix state into the key |
|
2020 SkPicture::AccelData::Key SkGpuDevice::ComputeAccelDataKey() { |
|
2021 static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain(); |
|
2022 |
|
2023 return gGPUID; |
|
2024 } |
|
2025 |
|
2026 void SkGpuDevice::EXPERIMENTAL_optimize(SkPicture* picture) { |
|
2027 SkPicture::AccelData::Key key = ComputeAccelDataKey(); |
|
2028 |
|
2029 GPUAccelData* data = SkNEW_ARGS(GPUAccelData, (key)); |
|
2030 |
|
2031 picture->EXPERIMENTAL_addAccelData(data); |
|
2032 } |
|
2033 |
|
2034 bool SkGpuDevice::EXPERIMENTAL_drawPicture(const SkPicture& picture) { |
|
2035 SkPicture::AccelData::Key key = ComputeAccelDataKey(); |
|
2036 |
|
2037 const SkPicture::AccelData* data = picture.EXPERIMENTAL_getAccelData(key); |
|
2038 if (NULL == data) { |
|
2039 return false; |
|
2040 } |
|
2041 |
|
2042 #if 0 |
|
2043 const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data); |
|
2044 #endif |
|
2045 |
|
2046 return false; |
|
2047 } |