blob: 19b98a8f63b1f1f278fe5106b7be101a5b4383ef [file] [log] [blame]
junov@google.com4370aed2012-01-18 16:21:08 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "SkDeferredCanvas.h"
10
11#include "SkPaint.h"
12#include "SkShader.h"
13#include "SkColorFilter.h"
14#include "SkDrawFilter.h"
15
16namespace {
17
18bool isPaintOpaque(const SkPaint& paint, const SkBitmap* bmpReplacesShader = NULL) {
19 // TODO: SkXfermode should have a virtual isOpaque method, which would
20 // make it possible to test modes that do not have a Coeff representation.
21 SkXfermode::Coeff srcCoeff, dstCoeff;
22 if (SkXfermode::AsCoeff(paint.getXfermode(), &srcCoeff, &dstCoeff)){
23 switch (dstCoeff) {
24 case SkXfermode::kZero_Coeff:
25 return true;
26 case SkXfermode::kISA_Coeff:
27 if (paint.getAlpha() != 255) {
28 break;
29 }
30 if (bmpReplacesShader) {
31 if (!bmpReplacesShader->isOpaque()) {
32 break;
33 }
34 } else if (paint.getShader() && !paint.getShader()->isOpaque()) {
35 break;
36 }
37 if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
38 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
39 break;
40 }
41 return true;
42 case SkXfermode::kSA_Coeff:
43 if (paint.getAlpha() != 0) {
44 break;
45 }
46 if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
47 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
48 break;
49 }
50 return true;
51 case SkXfermode::kSC_Coeff:
52 if (paint.getColor() != 0) { // all components must be 0
53 break;
54 }
55 if (bmpReplacesShader || paint.getShader()) {
56 break;
57 }
58 if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
59 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
60 break;
61 }
62 return true;
63 default:
64 break;
65 }
66 }
67 return false;
68}
69
70} // unnamed namespace
71
72SkDeferredCanvas::SkDeferredCanvas()
73{
74 init();
75}
76
77SkDeferredCanvas::SkDeferredCanvas(SkDevice* device)
78{
79 init();
80 setDevice(device);
81}
82
83SkDeferredCanvas::SkDeferredCanvas(SkDevice* device,
84 DeviceContext* deviceContext)
85{
86 init();
87 setDevice(device);
88 setDeviceContext(deviceContext);
89}
90
91void SkDeferredCanvas::init()
92{
93 fDeferredDrawing = true; // On by default
94}
95
96void SkDeferredCanvas::validate() const
97{
98 SkASSERT(getDevice());
99 SkASSERT(INHERITED::getTotalMatrix().isIdentity());
100}
101
102SkCanvas* SkDeferredCanvas::drawingCanvas() const
103{
104 validate();
105 return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
106 getDeferredDevice()->immediateCanvas();
107}
108
109void SkDeferredCanvas::flushIfNeeded(const SkBitmap& bitmap)
110{
111 validate();
112 if (fDeferredDrawing) {
113 getDeferredDevice()->flushIfNeeded(bitmap);
114 }
115}
116
117SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const
118{
119 return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
120}
121
122void SkDeferredCanvas::setDeferredDrawing(bool val)
123{
124 validate(); // Must set device before calling this method
125 SkASSERT(drawingCanvas()->getSaveCount() == 1);
126 if (val != fDeferredDrawing) {
127 if (fDeferredDrawing) {
128 // Going live.
129 getDeferredDevice()->flushPending();
130 }
131 fDeferredDrawing = val;
132 }
133}
134
135SkDeferredCanvas::~SkDeferredCanvas()
136{
137}
138
139SkDevice* SkDeferredCanvas::setDevice(SkDevice* device)
140{
141 INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
142 return device;
143}
144
145SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
146 DeviceContext* deviceContext)
147{
148 DeferredDevice* deferredDevice = getDeferredDevice();
149 SkASSERT(deferredDevice);
150 if (deferredDevice) {
151 deferredDevice->setDeviceContext(deviceContext);
152 }
153 return deviceContext;
154}
155
156bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
157 const SkPaint* paint) const
158{
159 SkCanvas* canvas = drawingCanvas();
160 SkISize canvasSize = getDeviceSize();
161 if (rect) {
162 if (!canvas->getTotalMatrix().rectStaysRect()) {
163 return false; // conservative
164 }
165
166 SkRect transformedRect;
167 canvas->getTotalMatrix().mapRect(&transformedRect, *rect);
168
169 if (paint) {
170 SkPaint::Style paintStyle = paint->getStyle();
171 if (!(paintStyle == SkPaint::kFill_Style ||
172 paintStyle == SkPaint::kStrokeAndFill_Style)) {
173 return false;
174 }
175 if (paint->getMaskFilter() || paint->getLooper()
176 || paint->getPathEffect() || paint->getImageFilter()) {
177 return false; // conservative
178 }
179 }
180
181 // The following test holds with AA enabled, and is conservative
182 // by a 0.5 pixel margin with AA disabled
junov@chromium.orgb1e218e2012-02-13 22:27:58 +0000183 if (transformedRect.fLeft > SkIntToScalar(0) ||
184 transformedRect.fTop > SkIntToScalar(0) ||
185 transformedRect.fRight < SkIntToScalar(canvasSize.fWidth) ||
186 transformedRect.fBottom < SkIntToScalar(canvasSize.fHeight)) {
junov@google.com4370aed2012-01-18 16:21:08 +0000187 return false;
188 }
189 }
190
191 switch (canvas->getClipType()) {
192 case SkCanvas::kRect_ClipType :
193 {
194 SkIRect bounds;
195 canvas->getClipDeviceBounds(&bounds);
196 if (bounds.fLeft > 0 || bounds.fTop > 0 ||
197 bounds.fRight < canvasSize.fWidth ||
198 bounds.fBottom < canvasSize.fHeight)
199 return false;
200 }
201 break;
202 case SkCanvas::kComplex_ClipType :
203 return false; // conservative
204 case SkCanvas::kEmpty_ClipType:
205 default:
206 break;
207 };
208
209 return true;
210}
211
212int SkDeferredCanvas::save(SaveFlags flags)
213{
214 return drawingCanvas()->save(flags);
215}
216
217int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
218 SaveFlags flags)
219{
220 return drawingCanvas()->saveLayer(bounds, paint, flags);
221}
222
223void SkDeferredCanvas::restore()
224{
225 drawingCanvas()->restore();
226}
227
228int SkDeferredCanvas::getSaveCount() const
229{
230 return drawingCanvas()->getSaveCount();
231}
232
233bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy)
234{
235 return drawingCanvas()->translate(dx, dy);
236}
237
238bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy)
239{
240 return drawingCanvas()->scale(sx, sy);
241}
242
243bool SkDeferredCanvas::rotate(SkScalar degrees)
244{
245 return drawingCanvas()->rotate(degrees);
246}
247
248bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy)
249{
250 return drawingCanvas()->skew(sx, sy);
251}
252
253bool SkDeferredCanvas::concat(const SkMatrix& matrix)
254{
255 return drawingCanvas()->concat(matrix);
256}
257
258void SkDeferredCanvas::setMatrix(const SkMatrix& matrix)
259{
260 drawingCanvas()->setMatrix(matrix);
261}
262
263const SkMatrix& SkDeferredCanvas::getTotalMatrix() const
264{
265 return drawingCanvas()->getTotalMatrix();
266}
267
268bool SkDeferredCanvas::clipRect(const SkRect& rect,
269 SkRegion::Op op,
270 bool doAntiAlias)
271{
272 return drawingCanvas()->clipRect(rect, op, doAntiAlias);
273}
274
275bool SkDeferredCanvas::clipPath(const SkPath& path,
276 SkRegion::Op op,
277 bool doAntiAlias)
278{
279 return drawingCanvas()->clipPath(path, op, doAntiAlias);
280}
281
282bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
283 SkRegion::Op op)
284{
285 return drawingCanvas()->clipRegion(deviceRgn, op);
286}
287
288void SkDeferredCanvas::clear(SkColor color)
289{
290 // purge pending commands
291 if (fDeferredDrawing) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000292 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000293 }
294
295 drawingCanvas()->clear(color);
296}
297
298void SkDeferredCanvas::drawPaint(const SkPaint& paint)
299{
300 if (fDeferredDrawing && isFullFrame(NULL, &paint) && isPaintOpaque(paint)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000301 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000302 }
303
304 drawingCanvas()->drawPaint(paint);
305}
306
307void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
308 const SkPoint pts[], const SkPaint& paint)
309{
310 drawingCanvas()->drawPoints(mode, count, pts, paint);
311}
312
313void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint)
314{
315 if (fDeferredDrawing && isFullFrame(&rect, &paint) && isPaintOpaque(paint)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000316 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000317 }
318
319 drawingCanvas()->drawRect(rect, paint);
320}
321
322void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint)
323{
324 drawingCanvas()->drawPath(path, paint);
325}
326
327void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
328 SkScalar top, const SkPaint* paint)
329{
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000330 SkRect bitmapRect = SkRect::MakeXYWH(left, top,
331 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
junov@google.com4370aed2012-01-18 16:21:08 +0000332 if (fDeferredDrawing &&
333 isFullFrame(&bitmapRect, paint) &&
334 isPaintOpaque(*paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000335 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000336 }
337
338 drawingCanvas()->drawBitmap(bitmap, left, top, paint);
339 flushIfNeeded(bitmap);
340}
341
342void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap,
343 const SkIRect* src,
344 const SkRect& dst, const SkPaint* paint)
345{
346 if (fDeferredDrawing &&
347 isFullFrame(&dst, paint) &&
348 isPaintOpaque(*paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000349 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000350 }
351
352 drawingCanvas()->drawBitmapRect(bitmap, src,
353 dst, paint);
354 flushIfNeeded(bitmap);
355}
356
357
358void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
359 const SkMatrix& m,
360 const SkPaint* paint)
361{
362 // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
363 // covers canvas entirely and transformed bitmap covers canvas entirely
364 drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
365 flushIfNeeded(bitmap);
366}
367
368void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
369 const SkIRect& center, const SkRect& dst,
370 const SkPaint* paint)
371{
372 // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
373 // covers canvas entirely and dst covers canvas entirely
374 drawingCanvas()->drawBitmapNine(bitmap, center,
375 dst, paint);
376 flushIfNeeded(bitmap);
377}
378
379void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
380 const SkPaint* paint)
381{
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000382 SkRect bitmapRect = SkRect::MakeXYWH(
383 SkIntToScalar(left),
384 SkIntToScalar(top),
385 SkIntToScalar(bitmap.width()),
386 SkIntToScalar(bitmap.height()));
junov@google.com4370aed2012-01-18 16:21:08 +0000387 if (fDeferredDrawing &&
388 isFullFrame(&bitmapRect, paint) &&
389 isPaintOpaque(*paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000390 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000391 }
392
393 drawingCanvas()->drawSprite(bitmap, left, top,
394 paint);
395 flushIfNeeded(bitmap);
396}
397
398void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
399 SkScalar x, SkScalar y, const SkPaint& paint)
400{
401 drawingCanvas()->drawText(text, byteLength, x,
402 y, paint);
403}
404
405void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
406 const SkPoint pos[], const SkPaint& paint)
407{
408 drawingCanvas()->drawPosText(text, byteLength,
409 pos, paint);
410}
411
412void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
413 const SkScalar xpos[], SkScalar constY,
414 const SkPaint& paint)
415{
416 drawingCanvas()->drawPosTextH(text, byteLength,
417 xpos, constY,
418 paint);
419}
420
421void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
422 const SkPath& path,
423 const SkMatrix* matrix,
424 const SkPaint& paint)
425{
426 drawingCanvas()->drawTextOnPath(text, byteLength,
427 path, matrix,
428 paint);
429}
430
431void SkDeferredCanvas::drawPicture(SkPicture& picture)
432{
433 drawingCanvas()->drawPicture(picture);
434}
435
436void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
437 const SkPoint vertices[],
438 const SkPoint texs[],
439 const SkColor colors[], SkXfermode* xmode,
440 const uint16_t indices[], int indexCount,
441 const SkPaint& paint)
442{
443 drawingCanvas()->drawVertices(vmode, vertexCount,
444 vertices, texs,
445 colors, xmode,
446 indices, indexCount,
447 paint);
448}
449
450SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder)
451{
452 INHERITED::setBounder(bounder); // So non-virtual getBounder works
453 return drawingCanvas()->setBounder(bounder);
454}
455
456SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter)
457{
458 INHERITED::setDrawFilter(filter); // So non-virtual getDrawFilter works
459 return drawingCanvas()->setDrawFilter(filter);
460}
461
462SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
463 return drawingCanvas();
464}
465
466// SkDeferredCanvas::DeferredDevice
467//------------------------------------
468
469SkDeferredCanvas::DeferredDevice::DeferredDevice(
470 SkDevice* immediateDevice, DeviceContext* deviceContext) :
471 SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
472 immediateDevice->height(), immediateDevice->isOpaque())
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000473 , fFreshFrame(true)
junov@google.com4370aed2012-01-18 16:21:08 +0000474{
475 fDeviceContext = deviceContext;
476 SkSafeRef(fDeviceContext);
477 fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
478 fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
junov@google.com4370aed2012-01-18 16:21:08 +0000479 fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
480 fImmediateDevice->height(),
481 SkPicture::kUsePathBoundsForClip_RecordingFlag);
482}
483
484SkDeferredCanvas::DeferredDevice::~DeferredDevice()
485{
486 SkSafeUnref(fImmediateCanvas);
487 SkSafeUnref(fDeviceContext);
488}
489
490void SkDeferredCanvas::DeferredDevice::setDeviceContext(
491 DeviceContext* deviceContext)
492{
493 SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
494}
495
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000496void SkDeferredCanvas::DeferredDevice::contentsCleared()
junov@google.com4370aed2012-01-18 16:21:08 +0000497{
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000498 if (!fRecordingCanvas->isDrawingToLayer()) {
499 fFreshFrame = true;
junov@google.com4370aed2012-01-18 16:21:08 +0000500
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000501 // TODO: find a way to transfer the state stack and layers
502 // to the new recording canvas. For now, purging only works
503 // with an empty stack.
504 if (fRecordingCanvas->getSaveCount() == 0) {
junov@google.com4370aed2012-01-18 16:21:08 +0000505
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000506 // Save state that is trashed by the purge
507 SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
508 SkSafeRef(drawFilter); // So that it survives the purge
509 SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
510 SkRegion clipRegion = fRecordingCanvas->getTotalClip();
junov@google.com4370aed2012-01-18 16:21:08 +0000511
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000512 // beginRecording creates a new recording canvas and discards the
513 // old one, hence purging deferred draw ops.
514 fRecordingCanvas = fPicture.beginRecording(
515 fImmediateDevice->width(),
516 fImmediateDevice->height(),
517 SkPicture::kUsePathBoundsForClip_RecordingFlag);
518
519 // Restore pre-purge state
520 if (!clipRegion.isEmpty()) {
521 fRecordingCanvas->clipRegion(clipRegion, SkRegion::kReplace_Op);
522 }
523 if (!matrix.isIdentity()) {
524 fRecordingCanvas->setMatrix(matrix);
525 }
526 if (drawFilter) {
527 fRecordingCanvas->setDrawFilter(drawFilter)->unref();
528 }
529 }
junov@google.com4370aed2012-01-18 16:21:08 +0000530 }
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000531}
532
533bool SkDeferredCanvas::DeferredDevice::isFreshFrame() {
534 bool ret = fFreshFrame;
535 fFreshFrame = false;
536 return ret;
junov@google.com4370aed2012-01-18 16:21:08 +0000537}
538
539void SkDeferredCanvas::DeferredDevice::flushPending()
540{
541 if (fDeviceContext) {
542 fDeviceContext->prepareForDraw();
543 }
544 fPicture.draw(fImmediateCanvas);
545 fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
546 fImmediateDevice->height(),
547 SkPicture::kUsePathBoundsForClip_RecordingFlag);
548}
549
550void SkDeferredCanvas::DeferredDevice::flush()
551{
552 flushPending();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000553 fImmediateCanvas->flush();
junov@google.com4370aed2012-01-18 16:21:08 +0000554}
555
junov@google.com4370aed2012-01-18 16:21:08 +0000556void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap)
557{
558 if (bitmap.isImmutable()) {
559 return; // safe to deffer without registering a dependency
560 }
561
562 // For now, drawing a writable bitmap triggers a flush
563 // TODO: implement read-only semantics and auto buffer duplication on write
564 // in SkBitmap/SkPixelRef, which will make deferral possible in this case.
565 flushPending();
566}
567
568uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities()
569{
570 return fImmediateDevice->getDeviceCapabilities();
571}
572
573int SkDeferredCanvas::DeferredDevice::width() const
574{
575 return fImmediateDevice->width();
576}
577
578int SkDeferredCanvas::DeferredDevice::height() const
579{
580 return fImmediateDevice->height();
581}
582
583SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget()
584{
585 flushPending();
586 return fImmediateDevice->accessRenderTarget();
587}
588
589void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
590 int x, int y, SkCanvas::Config8888 config8888)
591{
592 if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
593 (y + bitmap.height()) >= height()) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000594 contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000595 }
596
597 if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
598 SkCanvas::kNative_Premul_Config8888 != config8888 &&
599 kPMColorAlias != config8888) {
600 //Special case config: no deferral
601 flushPending();
602 fImmediateDevice->writePixels(bitmap, x, y, config8888);
603 }
604
605 SkPaint paint;
606 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
607 fRecordingCanvas->drawSprite(bitmap, x, y, &paint);
608 flushIfNeeded(bitmap);
609}
610
junov@chromium.org1f9767c2012-02-07 16:27:57 +0000611const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*)
junov@google.com4370aed2012-01-18 16:21:08 +0000612{
junov@google.com4370aed2012-01-18 16:21:08 +0000613 flushPending();
junov@chromium.org1f9767c2012-02-07 16:27:57 +0000614 return fImmediateDevice->accessBitmap(false);
junov@google.com4370aed2012-01-18 16:21:08 +0000615}
616
617SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
618 SkBitmap::Config config, int width, int height, bool isOpaque, Usage usage)
619{
620 // Save layer usage not supported, and not required by SkDeferredCanvas.
621 SkASSERT(usage != kSaveLayer_Usage);
622 // Create a compatible non-deferred device.
623 SkDevice* compatibleDevice = fImmediateDevice->createCompatibleDevice(config, width,
624 height, isOpaque);
625 return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
626}
627
628bool SkDeferredCanvas::DeferredDevice::onReadPixels(
629 const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888)
630{
631 flushPending();
632 return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
633 x, y, config8888);
634}