blob: 494b50c39fafa6bedb57b503452237ca23fc2868 [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
reed@google.com563a3b42012-06-26 19:24:50 +000016SK_DEFINE_INST_COUNT(SkDeferredCanvas::DeviceContext)
17
junov@chromium.orgbfeddae2012-07-23 13:35:14 +000018enum {
19 // Deferred canvas will auto-flush when recording reaches this limit
20 kDefaultMaxRecordingStorageBytes = 64*1024*1024,
21};
22
junov@google.com4370aed2012-01-18 16:21:08 +000023namespace {
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +000024bool shouldDrawImmediately(const SkBitmap& bitmap) {
25 return bitmap.getTexture() && !bitmap.isImmutable();
26}
27}
28
29class AutoImmediateDrawIfNeeded {
30public:
31 AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap& bitmap) {
32 if (canvas.isDeferredDrawing() && shouldDrawImmediately(bitmap)) {
33 canvas.setDeferredDrawing(false);
34 fCanvas = &canvas;
35 } else {
36 fCanvas = NULL;
37 }
38 // FIXME: Temporary solution for tracking memory usage, pending
39 // resolution of http://code.google.com/p/skia/issues/detail?id=738
40#if SK_DEFERRED_CANVAS_USES_GPIPE
41 if (canvas.isDeferredDrawing()) {
42 canvas.accountForTempBitmapStorage(bitmap);
43 }
44#endif
45 }
46
47 ~AutoImmediateDrawIfNeeded() {
48 if (fCanvas) {
49 fCanvas->setDeferredDrawing(true);
50 }
51 }
52private:
53 SkDeferredCanvas* fCanvas;
54};
55
56namespace {
junov@google.com4370aed2012-01-18 16:21:08 +000057
junov@chromium.orgc16ca922012-02-24 22:06:27 +000058bool isPaintOpaque(const SkPaint* paint,
59 const SkBitmap* bmpReplacesShader = NULL) {
junov@google.com4370aed2012-01-18 16:21:08 +000060 // TODO: SkXfermode should have a virtual isOpaque method, which would
61 // make it possible to test modes that do not have a Coeff representation.
junov@chromium.org87f982c2012-02-23 21:34:34 +000062
63 if (!paint) {
64 return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
65 }
66
junov@google.com4370aed2012-01-18 16:21:08 +000067 SkXfermode::Coeff srcCoeff, dstCoeff;
junov@chromium.org87f982c2012-02-23 21:34:34 +000068 if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
junov@google.com4370aed2012-01-18 16:21:08 +000069 switch (dstCoeff) {
70 case SkXfermode::kZero_Coeff:
71 return true;
72 case SkXfermode::kISA_Coeff:
junov@chromium.org87f982c2012-02-23 21:34:34 +000073 if (paint->getAlpha() != 255) {
junov@google.com4370aed2012-01-18 16:21:08 +000074 break;
75 }
76 if (bmpReplacesShader) {
77 if (!bmpReplacesShader->isOpaque()) {
78 break;
79 }
junov@chromium.org87f982c2012-02-23 21:34:34 +000080 } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
junov@google.com4370aed2012-01-18 16:21:08 +000081 break;
82 }
junov@chromium.orgc16ca922012-02-24 22:06:27 +000083 if (paint->getColorFilter() &&
84 ((paint->getColorFilter()->getFlags() &
junov@google.com4370aed2012-01-18 16:21:08 +000085 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
86 break;
87 }
88 return true;
89 case SkXfermode::kSA_Coeff:
junov@chromium.org87f982c2012-02-23 21:34:34 +000090 if (paint->getAlpha() != 0) {
junov@google.com4370aed2012-01-18 16:21:08 +000091 break;
92 }
junov@chromium.orgc16ca922012-02-24 22:06:27 +000093 if (paint->getColorFilter() &&
94 ((paint->getColorFilter()->getFlags() &
junov@google.com4370aed2012-01-18 16:21:08 +000095 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
96 break;
97 }
98 return true;
99 case SkXfermode::kSC_Coeff:
junov@chromium.org87f982c2012-02-23 21:34:34 +0000100 if (paint->getColor() != 0) { // all components must be 0
junov@google.com4370aed2012-01-18 16:21:08 +0000101 break;
102 }
junov@chromium.org87f982c2012-02-23 21:34:34 +0000103 if (bmpReplacesShader || paint->getShader()) {
junov@google.com4370aed2012-01-18 16:21:08 +0000104 break;
105 }
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000106 if (paint->getColorFilter() && (
107 (paint->getColorFilter()->getFlags() &
junov@google.com4370aed2012-01-18 16:21:08 +0000108 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
109 break;
110 }
111 return true;
112 default:
113 break;
114 }
115 }
116 return false;
117}
118
119} // unnamed namespace
120
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000121SkDeferredCanvas::SkDeferredCanvas() {
junov@google.com4370aed2012-01-18 16:21:08 +0000122 init();
123}
124
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000125SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
junov@google.com4370aed2012-01-18 16:21:08 +0000126 init();
127 setDevice(device);
128}
129
130SkDeferredCanvas::SkDeferredCanvas(SkDevice* device,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000131 DeviceContext* deviceContext) {
junov@google.com4370aed2012-01-18 16:21:08 +0000132 init();
133 setDevice(device);
134 setDeviceContext(deviceContext);
135}
136
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000137void SkDeferredCanvas::init() {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000138 fDeferredDrawing = true; // On by default
junov@google.com4370aed2012-01-18 16:21:08 +0000139}
140
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000141void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) {
142 validate();
143 this->getDeferredDevice()->setMaxRecordingStorage(maxStorage);
144}
145
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000146// FIXME: Temporary solution for tracking memory usage, pending
147// resolution of http://code.google.com/p/skia/issues/detail?id=738
148#if SK_DEFERRED_CANVAS_USES_GPIPE
149void SkDeferredCanvas::accountForTempBitmapStorage(const SkBitmap& bitmap) const {
150 if (fDeferredDrawing) {
151 this->getDeferredDevice()->accountForTempBitmapStorage(bitmap);
152 }
153}
154#endif
155
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000156void SkDeferredCanvas::validate() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000157 SkASSERT(getDevice());
junov@google.com4370aed2012-01-18 16:21:08 +0000158}
159
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000160SkCanvas* SkDeferredCanvas::drawingCanvas() const {
161 validate();
162 return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
163 getDeferredDevice()->immediateCanvas();
164}
165
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000166SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000167 return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
168}
169
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000170void SkDeferredCanvas::setDeferredDrawing(bool val) {
junov@google.com4370aed2012-01-18 16:21:08 +0000171 validate(); // Must set device before calling this method
junov@google.com4370aed2012-01-18 16:21:08 +0000172 if (val != fDeferredDrawing) {
173 if (fDeferredDrawing) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000174 // Going live.
junov@google.com4370aed2012-01-18 16:21:08 +0000175 getDeferredDevice()->flushPending();
176 }
177 fDeferredDrawing = val;
178 }
179}
180
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000181bool SkDeferredCanvas::isDeferredDrawing() {
182 return fDeferredDrawing;
183}
184
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000185SkDeferredCanvas::~SkDeferredCanvas() {
junov@google.com4370aed2012-01-18 16:21:08 +0000186}
187
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000188SkDevice* SkDeferredCanvas::setDevice(SkDevice* device) {
junov@google.com4370aed2012-01-18 16:21:08 +0000189 INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
190 return device;
191}
192
193SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000194 DeviceContext* deviceContext) {
195
junov@google.com4370aed2012-01-18 16:21:08 +0000196 DeferredDevice* deferredDevice = getDeferredDevice();
197 SkASSERT(deferredDevice);
198 if (deferredDevice) {
199 deferredDevice->setDeviceContext(deviceContext);
200 }
201 return deviceContext;
202}
203
204bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000205 const SkPaint* paint) const {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000206 SkCanvas* canvas = drawingCanvas();
junov@google.com4370aed2012-01-18 16:21:08 +0000207 SkISize canvasSize = getDeviceSize();
208 if (rect) {
209 if (!canvas->getTotalMatrix().rectStaysRect()) {
210 return false; // conservative
211 }
212
213 SkRect transformedRect;
214 canvas->getTotalMatrix().mapRect(&transformedRect, *rect);
215
216 if (paint) {
217 SkPaint::Style paintStyle = paint->getStyle();
218 if (!(paintStyle == SkPaint::kFill_Style ||
219 paintStyle == SkPaint::kStrokeAndFill_Style)) {
220 return false;
221 }
222 if (paint->getMaskFilter() || paint->getLooper()
223 || paint->getPathEffect() || paint->getImageFilter()) {
224 return false; // conservative
225 }
226 }
227
228 // The following test holds with AA enabled, and is conservative
229 // by a 0.5 pixel margin with AA disabled
junov@chromium.orgb1e218e2012-02-13 22:27:58 +0000230 if (transformedRect.fLeft > SkIntToScalar(0) ||
231 transformedRect.fTop > SkIntToScalar(0) ||
232 transformedRect.fRight < SkIntToScalar(canvasSize.fWidth) ||
233 transformedRect.fBottom < SkIntToScalar(canvasSize.fHeight)) {
junov@google.com4370aed2012-01-18 16:21:08 +0000234 return false;
235 }
236 }
237
238 switch (canvas->getClipType()) {
239 case SkCanvas::kRect_ClipType :
240 {
241 SkIRect bounds;
242 canvas->getClipDeviceBounds(&bounds);
243 if (bounds.fLeft > 0 || bounds.fTop > 0 ||
244 bounds.fRight < canvasSize.fWidth ||
245 bounds.fBottom < canvasSize.fHeight)
246 return false;
247 }
248 break;
249 case SkCanvas::kComplex_ClipType :
250 return false; // conservative
251 case SkCanvas::kEmpty_ClipType:
252 default:
253 break;
254 };
255
256 return true;
257}
258
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000259int SkDeferredCanvas::save(SaveFlags flags) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000260 drawingCanvas()->save(flags);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000261 return this->INHERITED::save(flags);
junov@google.com4370aed2012-01-18 16:21:08 +0000262}
263
264int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000265 SaveFlags flags) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000266 drawingCanvas()->saveLayer(bounds, paint, flags);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000267 int count = this->INHERITED::save(flags);
268 this->clipRectBounds(bounds, flags, NULL);
269 return count;
junov@google.com4370aed2012-01-18 16:21:08 +0000270}
271
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000272void SkDeferredCanvas::restore() {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000273 drawingCanvas()->restore();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000274 this->INHERITED::restore();
junov@google.com4370aed2012-01-18 16:21:08 +0000275}
276
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000277bool SkDeferredCanvas::isDrawingToLayer() const {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000278 return drawingCanvas()->isDrawingToLayer();
junov@google.com4370aed2012-01-18 16:21:08 +0000279}
280
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000281bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000282 drawingCanvas()->translate(dx, dy);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000283 return this->INHERITED::translate(dx, dy);
junov@google.com4370aed2012-01-18 16:21:08 +0000284}
285
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000286bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000287 drawingCanvas()->scale(sx, sy);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000288 return this->INHERITED::scale(sx, sy);
junov@google.com4370aed2012-01-18 16:21:08 +0000289}
290
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000291bool SkDeferredCanvas::rotate(SkScalar degrees) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000292 drawingCanvas()->rotate(degrees);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000293 return this->INHERITED::rotate(degrees);
junov@google.com4370aed2012-01-18 16:21:08 +0000294}
295
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000296bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000297 drawingCanvas()->skew(sx, sy);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000298 return this->INHERITED::skew(sx, sy);
junov@google.com4370aed2012-01-18 16:21:08 +0000299}
300
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000301bool SkDeferredCanvas::concat(const SkMatrix& matrix) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000302 drawingCanvas()->concat(matrix);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000303 return this->INHERITED::concat(matrix);
junov@google.com4370aed2012-01-18 16:21:08 +0000304}
305
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000306void SkDeferredCanvas::setMatrix(const SkMatrix& matrix) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000307 drawingCanvas()->setMatrix(matrix);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000308 this->INHERITED::setMatrix(matrix);
junov@google.com4370aed2012-01-18 16:21:08 +0000309}
310
311bool SkDeferredCanvas::clipRect(const SkRect& rect,
312 SkRegion::Op op,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000313 bool doAntiAlias) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000314 drawingCanvas()->clipRect(rect, op, doAntiAlias);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000315 return this->INHERITED::clipRect(rect, op, doAntiAlias);
junov@google.com4370aed2012-01-18 16:21:08 +0000316}
317
318bool SkDeferredCanvas::clipPath(const SkPath& path,
319 SkRegion::Op op,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000320 bool doAntiAlias) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000321 drawingCanvas()->clipPath(path, op, doAntiAlias);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000322 return this->INHERITED::clipPath(path, op, doAntiAlias);
junov@google.com4370aed2012-01-18 16:21:08 +0000323}
324
325bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000326 SkRegion::Op op) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000327 drawingCanvas()->clipRegion(deviceRgn, op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000328 return this->INHERITED::clipRegion(deviceRgn, op);
junov@google.com4370aed2012-01-18 16:21:08 +0000329}
330
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000331void SkDeferredCanvas::clear(SkColor color) {
junov@google.com4370aed2012-01-18 16:21:08 +0000332 // purge pending commands
333 if (fDeferredDrawing) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000334 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000335 }
336
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000337 drawingCanvas()->clear(color);
junov@google.com4370aed2012-01-18 16:21:08 +0000338}
339
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000340void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
341 if (fDeferredDrawing && isFullFrame(NULL, &paint) &&
342 isPaintOpaque(&paint)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000343 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000344 }
345
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000346 drawingCanvas()->drawPaint(paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000347}
348
349void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000350 const SkPoint pts[], const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000351 drawingCanvas()->drawPoints(mode, count, pts, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000352}
353
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000354void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
355 if (fDeferredDrawing && isFullFrame(&rect, &paint) &&
356 isPaintOpaque(&paint)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000357 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000358 }
359
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000360 drawingCanvas()->drawRect(rect, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000361}
362
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000363void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000364 drawingCanvas()->drawPath(path, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000365}
366
367void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000368 SkScalar top, const SkPaint* paint) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000369 SkRect bitmapRect = SkRect::MakeXYWH(left, top,
370 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
junov@google.com4370aed2012-01-18 16:21:08 +0000371 if (fDeferredDrawing &&
372 isFullFrame(&bitmapRect, paint) &&
junov@chromium.org87f982c2012-02-23 21:34:34 +0000373 isPaintOpaque(paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000374 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000375 }
376
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000377 AutoImmediateDrawIfNeeded autoDraw(*this, bitmap);
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000378 drawingCanvas()->drawBitmap(bitmap, left, top, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000379}
380
381void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap,
382 const SkIRect* src,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000383 const SkRect& dst,
384 const SkPaint* paint) {
junov@google.com4370aed2012-01-18 16:21:08 +0000385 if (fDeferredDrawing &&
386 isFullFrame(&dst, paint) &&
junov@chromium.org87f982c2012-02-23 21:34:34 +0000387 isPaintOpaque(paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000388 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000389 }
390
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000391 AutoImmediateDrawIfNeeded autoDraw(*this, bitmap);
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000392 drawingCanvas()->drawBitmapRect(bitmap, src,
junov@google.com4370aed2012-01-18 16:21:08 +0000393 dst, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000394}
395
396
397void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
398 const SkMatrix& m,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000399 const SkPaint* paint) {
junov@google.com4370aed2012-01-18 16:21:08 +0000400 // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
401 // covers canvas entirely and transformed bitmap covers canvas entirely
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000402 AutoImmediateDrawIfNeeded autoDraw(*this, bitmap);
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000403 drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000404}
405
406void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
407 const SkIRect& center, const SkRect& dst,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000408 const SkPaint* paint) {
junov@google.com4370aed2012-01-18 16:21:08 +0000409 // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
410 // covers canvas entirely and dst covers canvas entirely
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000411 AutoImmediateDrawIfNeeded autoDraw(*this, bitmap);
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000412 drawingCanvas()->drawBitmapNine(bitmap, center,
junov@google.com4370aed2012-01-18 16:21:08 +0000413 dst, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000414}
415
416void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000417 const SkPaint* paint) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000418 SkRect bitmapRect = SkRect::MakeXYWH(
419 SkIntToScalar(left),
420 SkIntToScalar(top),
421 SkIntToScalar(bitmap.width()),
422 SkIntToScalar(bitmap.height()));
junov@google.com4370aed2012-01-18 16:21:08 +0000423 if (fDeferredDrawing &&
424 isFullFrame(&bitmapRect, paint) &&
junov@chromium.org87f982c2012-02-23 21:34:34 +0000425 isPaintOpaque(paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000426 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000427 }
428
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000429 AutoImmediateDrawIfNeeded autoDraw(*this, bitmap);
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000430 drawingCanvas()->drawSprite(bitmap, left, top,
junov@google.com4370aed2012-01-18 16:21:08 +0000431 paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000432}
433
434void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000435 SkScalar x, SkScalar y, const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000436 drawingCanvas()->drawText(text, byteLength, x, y, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000437}
438
439void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000440 const SkPoint pos[], const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000441 drawingCanvas()->drawPosText(text, byteLength, pos, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000442}
443
444void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
445 const SkScalar xpos[], SkScalar constY,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000446 const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000447 drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000448}
449
450void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
451 const SkPath& path,
452 const SkMatrix* matrix,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000453 const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000454 drawingCanvas()->drawTextOnPath(text, byteLength,
junov@google.com4370aed2012-01-18 16:21:08 +0000455 path, matrix,
456 paint);
457}
458
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000459void SkDeferredCanvas::drawPicture(SkPicture& picture) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000460 drawingCanvas()->drawPicture(picture);
junov@google.com4370aed2012-01-18 16:21:08 +0000461}
462
463void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
464 const SkPoint vertices[],
465 const SkPoint texs[],
466 const SkColor colors[], SkXfermode* xmode,
467 const uint16_t indices[], int indexCount,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000468 const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000469 drawingCanvas()->drawVertices(vmode, vertexCount,
junov@google.com4370aed2012-01-18 16:21:08 +0000470 vertices, texs,
471 colors, xmode,
472 indices, indexCount,
473 paint);
474}
475
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000476SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000477 drawingCanvas()->setBounder(bounder);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000478 return INHERITED::setBounder(bounder);
junov@google.com4370aed2012-01-18 16:21:08 +0000479}
480
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000481SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000482 drawingCanvas()->setDrawFilter(filter);
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000483 return INHERITED::setDrawFilter(filter);
junov@google.com4370aed2012-01-18 16:21:08 +0000484}
485
486SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000487 return drawingCanvas();
junov@google.com4370aed2012-01-18 16:21:08 +0000488}
489
junov@chromium.org77eec242012-07-18 17:54:45 +0000490#if SK_DEFERRED_CANVAS_USES_GPIPE
491
492// SkDeferredCanvas::DeferredPipeController
493//-------------------------------------------
494
495SkDeferredCanvas::DeferredPipeController::DeferredPipeController() :
496 fAllocator(kMinBlockSize) {
497 fBlock = NULL;
498 fBytesWritten = 0;
499}
500
501SkDeferredCanvas::DeferredPipeController::~DeferredPipeController() {
502 fAllocator.reset();
503}
504
505void SkDeferredCanvas::DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) {
506 fReader.setCanvas(canvas);
507}
508
509void* SkDeferredCanvas::DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) {
510 if (fBlock) {
511 // Save the previous block for later
512 PipeBlock previousBloc(fBlock, fBytesWritten);
513 fBlockList.push(previousBloc);
514 }
515 int32_t blockSize = SkMax32(minRequest, kMinBlockSize);
516 fBlock = fAllocator.allocThrow(blockSize);
517 fBytesWritten = 0;
518 *actual = blockSize;
519 return fBlock;
520}
521
522void SkDeferredCanvas::DeferredPipeController::notifyWritten(size_t bytes) {
523 fBytesWritten += bytes;
524}
525
526void SkDeferredCanvas::DeferredPipeController::playback() {
527
528 for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
529 fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize);
530 }
531 fBlockList.reset();
532
533 if (fBlock) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000534 fReader.playback(fBlock, fBytesWritten);
junov@chromium.org77eec242012-07-18 17:54:45 +0000535 fBlock = NULL;
536 }
537
538 // Release all allocated blocks
539 fAllocator.reset();
540}
541
542void SkDeferredCanvas::DeferredPipeController::reset() {
543 fBlockList.reset();
544 fBlock = NULL;
545 fAllocator.reset();
546}
547
548#endif // SK_DEFERRED_CANVAS_USES_GPIPE
549
junov@google.com4370aed2012-01-18 16:21:08 +0000550// SkDeferredCanvas::DeferredDevice
551//------------------------------------
552
553SkDeferredCanvas::DeferredDevice::DeferredDevice(
554 SkDevice* immediateDevice, DeviceContext* deviceContext) :
555 SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
556 immediateDevice->height(), immediateDevice->isOpaque())
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000557 , fFreshFrame(true) {
558
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000559 fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
junov@google.com4370aed2012-01-18 16:21:08 +0000560 fDeviceContext = deviceContext;
561 SkSafeRef(fDeviceContext);
562 fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
563 fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
junov@chromium.org77eec242012-07-18 17:54:45 +0000564#if SK_DEFERRED_CANVAS_USES_GPIPE
565 fPipeController.setPlaybackCanvas(fImmediateCanvas);
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000566 fTempBitmapStorage = 0;
junov@chromium.org77eec242012-07-18 17:54:45 +0000567#endif
568 beginRecording();
junov@google.com4370aed2012-01-18 16:21:08 +0000569}
570
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000571SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000572 flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000573 SkSafeUnref(fImmediateCanvas);
574 SkSafeUnref(fDeviceContext);
575}
junov@chromium.org77eec242012-07-18 17:54:45 +0000576
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000577void SkDeferredCanvas::DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
578 fMaxRecordingStorageBytes = maxStorage;
579 recordingCanvas(); // Accessing the recording canvas applies the new limit.
580}
junov@chromium.org77eec242012-07-18 17:54:45 +0000581
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000582#if SK_DEFERRED_CANVAS_USES_GPIPE
583void SkDeferredCanvas::DeferredDevice::accountForTempBitmapStorage(const SkBitmap& bitmap) {
584 // SkGPipe will store copies of mutable bitmaps. The memory allocations
585 // and deallocations for these bitmaps are not tracked by the writer or
586 // the controller, so we do as best we can to track consumption here
587 if (!bitmap.isImmutable()) {
588 // FIXME: Temporary solution for tracking memory usage, pending
589 // resolution of http://code.google.com/p/skia/issues/detail?id=738
590 // This does not take into account duplicates of previously
591 // copied bitmaps that will not get copied again.
592 fTempBitmapStorage += bitmap.getSize();
593 }
594}
595#endif
596
junov@chromium.org77eec242012-07-18 17:54:45 +0000597void SkDeferredCanvas::DeferredDevice::endRecording() {
598#if SK_DEFERRED_CANVAS_USES_GPIPE
599 fPipeWriter.endRecording();
600 fPipeController.reset();
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000601 fTempBitmapStorage = 0;
junov@chromium.org77eec242012-07-18 17:54:45 +0000602#else
603 fPicture.endRecording();
604#endif
605 fRecordingCanvas = NULL;
606}
607
608void SkDeferredCanvas::DeferredDevice::beginRecording() {
609#if SK_DEFERRED_CANVAS_USES_GPIPE
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000610 SkASSERT(0 == fTempBitmapStorage);
junov@chromium.org77eec242012-07-18 17:54:45 +0000611 fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0);
612#else
613 fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
614 fImmediateDevice->height(),
615 SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
616#endif
617}
junov@google.com4370aed2012-01-18 16:21:08 +0000618
619void SkDeferredCanvas::DeferredDevice::setDeviceContext(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000620 DeviceContext* deviceContext) {
junov@google.com4370aed2012-01-18 16:21:08 +0000621 SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
622}
623
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000624void SkDeferredCanvas::DeferredDevice::contentsCleared() {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000625 if (!fRecordingCanvas->isDrawingToLayer()) {
626 fFreshFrame = true;
junov@google.com4370aed2012-01-18 16:21:08 +0000627
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000628 // TODO: find a way to transfer the state stack and layers
629 // to the new recording canvas. For now, purging only works
630 // with an empty stack.
631 if (fRecordingCanvas->getSaveCount() == 0) {
junov@google.com4370aed2012-01-18 16:21:08 +0000632
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000633 // Save state that is trashed by the purge
634 SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
635 SkSafeRef(drawFilter); // So that it survives the purge
636 SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
637 SkRegion clipRegion = fRecordingCanvas->getTotalClip();
junov@google.com4370aed2012-01-18 16:21:08 +0000638
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000639 // beginRecording creates a new recording canvas and discards the
640 // old one, hence purging deferred draw ops.
junov@chromium.org77eec242012-07-18 17:54:45 +0000641 this->endRecording();
642 this->beginRecording();
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000643
644 // Restore pre-purge state
645 if (!clipRegion.isEmpty()) {
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000646 fRecordingCanvas->clipRegion(clipRegion,
647 SkRegion::kReplace_Op);
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000648 }
649 if (!matrix.isIdentity()) {
650 fRecordingCanvas->setMatrix(matrix);
651 }
652 if (drawFilter) {
653 fRecordingCanvas->setDrawFilter(drawFilter)->unref();
654 }
655 }
junov@google.com4370aed2012-01-18 16:21:08 +0000656 }
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000657}
658
659bool SkDeferredCanvas::DeferredDevice::isFreshFrame() {
660 bool ret = fFreshFrame;
661 fFreshFrame = false;
662 return ret;
junov@google.com4370aed2012-01-18 16:21:08 +0000663}
664
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000665void SkDeferredCanvas::DeferredDevice::flushPending() {
junov@chromium.org77eec242012-07-18 17:54:45 +0000666#if SK_DEFERRED_CANVAS_USES_GPIPE
667 if (!fPipeController.hasRecorded()) {
668 return;
669 }
670#else
junov@chromium.org4866cc02012-06-01 21:23:07 +0000671 if (!fPicture.hasRecorded()) {
672 return;
673 }
junov@chromium.org77eec242012-07-18 17:54:45 +0000674#endif
junov@google.com4370aed2012-01-18 16:21:08 +0000675 if (fDeviceContext) {
676 fDeviceContext->prepareForDraw();
677 }
junov@chromium.org77eec242012-07-18 17:54:45 +0000678
679#if SK_DEFERRED_CANVAS_USES_GPIPE
680 fPipeWriter.flushRecording(true);
681 fPipeController.playback();
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000682 fTempBitmapStorage = 0;
junov@chromium.org77eec242012-07-18 17:54:45 +0000683#else
junov@google.com4370aed2012-01-18 16:21:08 +0000684 fPicture.draw(fImmediateCanvas);
junov@chromium.org77eec242012-07-18 17:54:45 +0000685 this->beginRecording();
686#endif
junov@google.com4370aed2012-01-18 16:21:08 +0000687}
688
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000689void SkDeferredCanvas::DeferredDevice::flush() {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000690 this->flushPending();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000691 fImmediateCanvas->flush();
junov@google.com4370aed2012-01-18 16:21:08 +0000692}
693
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000694SkCanvas* SkDeferredCanvas::DeferredDevice::recordingCanvas() {
695#if SK_DEFERRED_CANVAS_USES_GPIPE
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000696 if (fPipeController.storageAllocatedForRecording() + fTempBitmapStorage >
697 fMaxRecordingStorageBytes) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000698 this->flushPending();
699 }
700#endif
701 return fRecordingCanvas;
702}
703
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000704uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() {
junov@google.com4370aed2012-01-18 16:21:08 +0000705 return fImmediateDevice->getDeviceCapabilities();
706}
707
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000708int SkDeferredCanvas::DeferredDevice::width() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000709 return fImmediateDevice->width();
710}
711
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000712int SkDeferredCanvas::DeferredDevice::height() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000713 return fImmediateDevice->height();
714}
715
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000716SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000717 this->flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000718 return fImmediateDevice->accessRenderTarget();
719}
720
721void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000722 int x, int y, SkCanvas::Config8888 config8888) {
723
junov@google.com4370aed2012-01-18 16:21:08 +0000724 if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
725 (y + bitmap.height()) >= height()) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000726 contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000727 }
728
729 if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
730 SkCanvas::kNative_Premul_Config8888 != config8888 &&
731 kPMColorAlias != config8888) {
732 //Special case config: no deferral
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000733 this->flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000734 fImmediateDevice->writePixels(bitmap, x, y, config8888);
735 }
736
737 SkPaint paint;
738 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
junov@chromium.orgb10a6bd2012-07-25 17:27:13 +0000739 if (shouldDrawImmediately(bitmap)) {
740 this->flushPending();
741 fImmediateCanvas->drawSprite(bitmap, x, y, &paint);
742 } else {
743#if SK_DEFERRED_CANVAS_USES_GPIPE
744 // FIXME: Temporary solution for tracking memory usage, pending
745 // resolution of http://code.google.com/p/skia/issues/detail?id=738
746 this->accountForTempBitmapStorage(bitmap);
747#endif
748 recordingCanvas()->drawSprite(bitmap, x, y, &paint);
749 }
junov@google.com4370aed2012-01-18 16:21:08 +0000750}
751
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000752const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000753 this->flushPending();
junov@chromium.org1f9767c2012-02-07 16:27:57 +0000754 return fImmediateDevice->accessBitmap(false);
junov@google.com4370aed2012-01-18 16:21:08 +0000755}
756
757SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000758 SkBitmap::Config config, int width, int height, bool isOpaque,
759 Usage usage) {
760
junov@google.com4370aed2012-01-18 16:21:08 +0000761 // Save layer usage not supported, and not required by SkDeferredCanvas.
762 SkASSERT(usage != kSaveLayer_Usage);
763 // Create a compatible non-deferred device.
scroggo@google.comd7dbd422012-07-03 15:16:30 +0000764 SkAutoTUnref<SkDevice> compatibleDevice
765 (fImmediateDevice->createCompatibleDevice(config, width, height,
766 isOpaque));
junov@google.com4370aed2012-01-18 16:21:08 +0000767 return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
768}
769
770bool SkDeferredCanvas::DeferredDevice::onReadPixels(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000771 const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000772 this->flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000773 return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
774 x, y, config8888);
775}