blob: 728e3d39d3fa0e32ed07ba6fecfe375b3a469b1e [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 {
24
junov@chromium.orgc16ca922012-02-24 22:06:27 +000025bool isPaintOpaque(const SkPaint* paint,
26 const SkBitmap* bmpReplacesShader = NULL) {
junov@google.com4370aed2012-01-18 16:21:08 +000027 // TODO: SkXfermode should have a virtual isOpaque method, which would
28 // make it possible to test modes that do not have a Coeff representation.
junov@chromium.org87f982c2012-02-23 21:34:34 +000029
30 if (!paint) {
31 return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
32 }
33
junov@google.com4370aed2012-01-18 16:21:08 +000034 SkXfermode::Coeff srcCoeff, dstCoeff;
junov@chromium.org87f982c2012-02-23 21:34:34 +000035 if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
junov@google.com4370aed2012-01-18 16:21:08 +000036 switch (dstCoeff) {
37 case SkXfermode::kZero_Coeff:
38 return true;
39 case SkXfermode::kISA_Coeff:
junov@chromium.org87f982c2012-02-23 21:34:34 +000040 if (paint->getAlpha() != 255) {
junov@google.com4370aed2012-01-18 16:21:08 +000041 break;
42 }
43 if (bmpReplacesShader) {
44 if (!bmpReplacesShader->isOpaque()) {
45 break;
46 }
junov@chromium.org87f982c2012-02-23 21:34:34 +000047 } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
junov@google.com4370aed2012-01-18 16:21:08 +000048 break;
49 }
junov@chromium.orgc16ca922012-02-24 22:06:27 +000050 if (paint->getColorFilter() &&
51 ((paint->getColorFilter()->getFlags() &
junov@google.com4370aed2012-01-18 16:21:08 +000052 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
53 break;
54 }
55 return true;
56 case SkXfermode::kSA_Coeff:
junov@chromium.org87f982c2012-02-23 21:34:34 +000057 if (paint->getAlpha() != 0) {
junov@google.com4370aed2012-01-18 16:21:08 +000058 break;
59 }
junov@chromium.orgc16ca922012-02-24 22:06:27 +000060 if (paint->getColorFilter() &&
61 ((paint->getColorFilter()->getFlags() &
junov@google.com4370aed2012-01-18 16:21:08 +000062 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
63 break;
64 }
65 return true;
66 case SkXfermode::kSC_Coeff:
junov@chromium.org87f982c2012-02-23 21:34:34 +000067 if (paint->getColor() != 0) { // all components must be 0
junov@google.com4370aed2012-01-18 16:21:08 +000068 break;
69 }
junov@chromium.org87f982c2012-02-23 21:34:34 +000070 if (bmpReplacesShader || paint->getShader()) {
junov@google.com4370aed2012-01-18 16:21:08 +000071 break;
72 }
junov@chromium.orgc16ca922012-02-24 22:06:27 +000073 if (paint->getColorFilter() && (
74 (paint->getColorFilter()->getFlags() &
junov@google.com4370aed2012-01-18 16:21:08 +000075 SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
76 break;
77 }
78 return true;
79 default:
80 break;
81 }
82 }
83 return false;
84}
85
86} // unnamed namespace
87
junov@chromium.orgc16ca922012-02-24 22:06:27 +000088SkDeferredCanvas::SkDeferredCanvas() {
junov@google.com4370aed2012-01-18 16:21:08 +000089 init();
90}
91
junov@chromium.orgc16ca922012-02-24 22:06:27 +000092SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
junov@google.com4370aed2012-01-18 16:21:08 +000093 init();
94 setDevice(device);
95}
96
97SkDeferredCanvas::SkDeferredCanvas(SkDevice* device,
junov@chromium.orgc16ca922012-02-24 22:06:27 +000098 DeviceContext* deviceContext) {
junov@google.com4370aed2012-01-18 16:21:08 +000099 init();
100 setDevice(device);
101 setDeviceContext(deviceContext);
102}
103
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000104void SkDeferredCanvas::init() {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000105 fDeferredDrawing = true; // On by default
junov@google.com4370aed2012-01-18 16:21:08 +0000106}
107
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000108void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) {
109 validate();
110 this->getDeferredDevice()->setMaxRecordingStorage(maxStorage);
111}
112
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000113void SkDeferredCanvas::validate() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000114 SkASSERT(getDevice());
junov@google.com4370aed2012-01-18 16:21:08 +0000115}
116
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000117SkCanvas* SkDeferredCanvas::drawingCanvas() const {
118 validate();
119 return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
120 getDeferredDevice()->immediateCanvas();
121}
122
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000123void SkDeferredCanvas::flushIfNeeded(const SkBitmap& bitmap) {
junov@google.com4370aed2012-01-18 16:21:08 +0000124 validate();
125 if (fDeferredDrawing) {
126 getDeferredDevice()->flushIfNeeded(bitmap);
127 }
128}
129
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000130SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000131 return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
132}
133
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000134void SkDeferredCanvas::setDeferredDrawing(bool val) {
junov@google.com4370aed2012-01-18 16:21:08 +0000135 validate(); // Must set device before calling this method
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000136 SkASSERT(drawingCanvas()->getSaveCount() == 1);
junov@google.com4370aed2012-01-18 16:21:08 +0000137 if (val != fDeferredDrawing) {
138 if (fDeferredDrawing) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000139 // Going live.
junov@google.com4370aed2012-01-18 16:21:08 +0000140 getDeferredDevice()->flushPending();
141 }
142 fDeferredDrawing = val;
143 }
144}
145
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000146SkDeferredCanvas::~SkDeferredCanvas() {
junov@google.com4370aed2012-01-18 16:21:08 +0000147}
148
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000149SkDevice* SkDeferredCanvas::setDevice(SkDevice* device) {
junov@google.com4370aed2012-01-18 16:21:08 +0000150 INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
151 return device;
152}
153
154SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000155 DeviceContext* deviceContext) {
156
junov@google.com4370aed2012-01-18 16:21:08 +0000157 DeferredDevice* deferredDevice = getDeferredDevice();
158 SkASSERT(deferredDevice);
159 if (deferredDevice) {
160 deferredDevice->setDeviceContext(deviceContext);
161 }
162 return deviceContext;
163}
164
165bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000166 const SkPaint* paint) const {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000167 SkCanvas* canvas = drawingCanvas();
junov@google.com4370aed2012-01-18 16:21:08 +0000168 SkISize canvasSize = getDeviceSize();
169 if (rect) {
170 if (!canvas->getTotalMatrix().rectStaysRect()) {
171 return false; // conservative
172 }
173
174 SkRect transformedRect;
175 canvas->getTotalMatrix().mapRect(&transformedRect, *rect);
176
177 if (paint) {
178 SkPaint::Style paintStyle = paint->getStyle();
179 if (!(paintStyle == SkPaint::kFill_Style ||
180 paintStyle == SkPaint::kStrokeAndFill_Style)) {
181 return false;
182 }
183 if (paint->getMaskFilter() || paint->getLooper()
184 || paint->getPathEffect() || paint->getImageFilter()) {
185 return false; // conservative
186 }
187 }
188
189 // The following test holds with AA enabled, and is conservative
190 // by a 0.5 pixel margin with AA disabled
junov@chromium.orgb1e218e2012-02-13 22:27:58 +0000191 if (transformedRect.fLeft > SkIntToScalar(0) ||
192 transformedRect.fTop > SkIntToScalar(0) ||
193 transformedRect.fRight < SkIntToScalar(canvasSize.fWidth) ||
194 transformedRect.fBottom < SkIntToScalar(canvasSize.fHeight)) {
junov@google.com4370aed2012-01-18 16:21:08 +0000195 return false;
196 }
197 }
198
199 switch (canvas->getClipType()) {
200 case SkCanvas::kRect_ClipType :
201 {
202 SkIRect bounds;
203 canvas->getClipDeviceBounds(&bounds);
204 if (bounds.fLeft > 0 || bounds.fTop > 0 ||
205 bounds.fRight < canvasSize.fWidth ||
206 bounds.fBottom < canvasSize.fHeight)
207 return false;
208 }
209 break;
210 case SkCanvas::kComplex_ClipType :
211 return false; // conservative
212 case SkCanvas::kEmpty_ClipType:
213 default:
214 break;
215 };
216
217 return true;
218}
219
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000220int SkDeferredCanvas::save(SaveFlags flags) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000221 drawingCanvas()->save(flags);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000222 return this->INHERITED::save(flags);
junov@google.com4370aed2012-01-18 16:21:08 +0000223}
224
225int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000226 SaveFlags flags) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000227 drawingCanvas()->saveLayer(bounds, paint, flags);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000228 int count = this->INHERITED::save(flags);
229 this->clipRectBounds(bounds, flags, NULL);
230 return count;
junov@google.com4370aed2012-01-18 16:21:08 +0000231}
232
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000233void SkDeferredCanvas::restore() {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000234 drawingCanvas()->restore();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000235 this->INHERITED::restore();
junov@google.com4370aed2012-01-18 16:21:08 +0000236}
237
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000238bool SkDeferredCanvas::isDrawingToLayer() const {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000239 return drawingCanvas()->isDrawingToLayer();
junov@google.com4370aed2012-01-18 16:21:08 +0000240}
241
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000242bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000243 drawingCanvas()->translate(dx, dy);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000244 return this->INHERITED::translate(dx, dy);
junov@google.com4370aed2012-01-18 16:21:08 +0000245}
246
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000247bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000248 drawingCanvas()->scale(sx, sy);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000249 return this->INHERITED::scale(sx, sy);
junov@google.com4370aed2012-01-18 16:21:08 +0000250}
251
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000252bool SkDeferredCanvas::rotate(SkScalar degrees) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000253 drawingCanvas()->rotate(degrees);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000254 return this->INHERITED::rotate(degrees);
junov@google.com4370aed2012-01-18 16:21:08 +0000255}
256
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000257bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000258 drawingCanvas()->skew(sx, sy);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000259 return this->INHERITED::skew(sx, sy);
junov@google.com4370aed2012-01-18 16:21:08 +0000260}
261
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000262bool SkDeferredCanvas::concat(const SkMatrix& matrix) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000263 drawingCanvas()->concat(matrix);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000264 return this->INHERITED::concat(matrix);
junov@google.com4370aed2012-01-18 16:21:08 +0000265}
266
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000267void SkDeferredCanvas::setMatrix(const SkMatrix& matrix) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000268 drawingCanvas()->setMatrix(matrix);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000269 this->INHERITED::setMatrix(matrix);
junov@google.com4370aed2012-01-18 16:21:08 +0000270}
271
272bool SkDeferredCanvas::clipRect(const SkRect& rect,
273 SkRegion::Op op,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000274 bool doAntiAlias) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000275 drawingCanvas()->clipRect(rect, op, doAntiAlias);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000276 return this->INHERITED::clipRect(rect, op, doAntiAlias);
junov@google.com4370aed2012-01-18 16:21:08 +0000277}
278
279bool SkDeferredCanvas::clipPath(const SkPath& path,
280 SkRegion::Op op,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000281 bool doAntiAlias) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000282 drawingCanvas()->clipPath(path, op, doAntiAlias);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000283 return this->INHERITED::clipPath(path, op, doAntiAlias);
junov@google.com4370aed2012-01-18 16:21:08 +0000284}
285
286bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000287 SkRegion::Op op) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000288 drawingCanvas()->clipRegion(deviceRgn, op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000289 return this->INHERITED::clipRegion(deviceRgn, op);
junov@google.com4370aed2012-01-18 16:21:08 +0000290}
291
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000292void SkDeferredCanvas::clear(SkColor color) {
junov@google.com4370aed2012-01-18 16:21:08 +0000293 // purge pending commands
294 if (fDeferredDrawing) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000295 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000296 }
297
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000298 drawingCanvas()->clear(color);
junov@google.com4370aed2012-01-18 16:21:08 +0000299}
300
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000301void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
302 if (fDeferredDrawing && isFullFrame(NULL, &paint) &&
303 isPaintOpaque(&paint)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000304 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000305 }
306
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000307 drawingCanvas()->drawPaint(paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000308}
309
310void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000311 const SkPoint pts[], const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000312 drawingCanvas()->drawPoints(mode, count, pts, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000313}
314
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000315void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
316 if (fDeferredDrawing && isFullFrame(&rect, &paint) &&
317 isPaintOpaque(&paint)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000318 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000319 }
320
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000321 drawingCanvas()->drawRect(rect, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000322}
323
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000324void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000325 drawingCanvas()->drawPath(path, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000326}
327
328void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000329 SkScalar top, const SkPaint* paint) {
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) &&
junov@chromium.org87f982c2012-02-23 21:34:34 +0000334 isPaintOpaque(paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000335 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000336 }
337
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000338 drawingCanvas()->drawBitmap(bitmap, left, top, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000339 flushIfNeeded(bitmap);
340}
341
342void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap,
343 const SkIRect* src,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000344 const SkRect& dst,
345 const SkPaint* paint) {
junov@google.com4370aed2012-01-18 16:21:08 +0000346 if (fDeferredDrawing &&
347 isFullFrame(&dst, paint) &&
junov@chromium.org87f982c2012-02-23 21:34:34 +0000348 isPaintOpaque(paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000349 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000350 }
351
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000352 drawingCanvas()->drawBitmapRect(bitmap, src,
junov@google.com4370aed2012-01-18 16:21:08 +0000353 dst, paint);
354 flushIfNeeded(bitmap);
355}
356
357
358void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
359 const SkMatrix& m,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000360 const SkPaint* paint) {
junov@google.com4370aed2012-01-18 16:21:08 +0000361 // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
362 // covers canvas entirely and transformed bitmap covers canvas entirely
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000363 drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000364 flushIfNeeded(bitmap);
365}
366
367void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
368 const SkIRect& center, const SkRect& dst,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000369 const SkPaint* paint) {
junov@google.com4370aed2012-01-18 16:21:08 +0000370 // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
371 // covers canvas entirely and dst covers canvas entirely
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000372 drawingCanvas()->drawBitmapNine(bitmap, center,
junov@google.com4370aed2012-01-18 16:21:08 +0000373 dst, paint);
374 flushIfNeeded(bitmap);
375}
376
377void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000378 const SkPaint* paint) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000379 SkRect bitmapRect = SkRect::MakeXYWH(
380 SkIntToScalar(left),
381 SkIntToScalar(top),
382 SkIntToScalar(bitmap.width()),
383 SkIntToScalar(bitmap.height()));
junov@google.com4370aed2012-01-18 16:21:08 +0000384 if (fDeferredDrawing &&
385 isFullFrame(&bitmapRect, paint) &&
junov@chromium.org87f982c2012-02-23 21:34:34 +0000386 isPaintOpaque(paint, &bitmap)) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000387 getDeferredDevice()->contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000388 }
389
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000390 drawingCanvas()->drawSprite(bitmap, left, top,
junov@google.com4370aed2012-01-18 16:21:08 +0000391 paint);
392 flushIfNeeded(bitmap);
393}
394
395void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000396 SkScalar x, SkScalar y, const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000397 drawingCanvas()->drawText(text, byteLength, x, y, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000398}
399
400void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000401 const SkPoint pos[], const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000402 drawingCanvas()->drawPosText(text, byteLength, pos, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000403}
404
405void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
406 const SkScalar xpos[], SkScalar constY,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000407 const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000408 drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint);
junov@google.com4370aed2012-01-18 16:21:08 +0000409}
410
411void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
412 const SkPath& path,
413 const SkMatrix* matrix,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000414 const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000415 drawingCanvas()->drawTextOnPath(text, byteLength,
junov@google.com4370aed2012-01-18 16:21:08 +0000416 path, matrix,
417 paint);
418}
419
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000420void SkDeferredCanvas::drawPicture(SkPicture& picture) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000421 drawingCanvas()->drawPicture(picture);
junov@google.com4370aed2012-01-18 16:21:08 +0000422}
423
424void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
425 const SkPoint vertices[],
426 const SkPoint texs[],
427 const SkColor colors[], SkXfermode* xmode,
428 const uint16_t indices[], int indexCount,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000429 const SkPaint& paint) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000430 drawingCanvas()->drawVertices(vmode, vertexCount,
junov@google.com4370aed2012-01-18 16:21:08 +0000431 vertices, texs,
432 colors, xmode,
433 indices, indexCount,
434 paint);
435}
436
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000437SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000438 drawingCanvas()->setBounder(bounder);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000439 return INHERITED::setBounder(bounder);
junov@google.com4370aed2012-01-18 16:21:08 +0000440}
441
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000442SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000443 drawingCanvas()->setDrawFilter(filter);
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000444 return INHERITED::setDrawFilter(filter);
junov@google.com4370aed2012-01-18 16:21:08 +0000445}
446
447SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
junov@chromium.org5e5a0952012-02-28 15:27:59 +0000448 return drawingCanvas();
junov@google.com4370aed2012-01-18 16:21:08 +0000449}
450
junov@chromium.org77eec242012-07-18 17:54:45 +0000451#if SK_DEFERRED_CANVAS_USES_GPIPE
452
453// SkDeferredCanvas::DeferredPipeController
454//-------------------------------------------
455
456SkDeferredCanvas::DeferredPipeController::DeferredPipeController() :
457 fAllocator(kMinBlockSize) {
458 fBlock = NULL;
459 fBytesWritten = 0;
460}
461
462SkDeferredCanvas::DeferredPipeController::~DeferredPipeController() {
463 fAllocator.reset();
464}
465
466void SkDeferredCanvas::DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) {
467 fReader.setCanvas(canvas);
468}
469
470void* SkDeferredCanvas::DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) {
471 if (fBlock) {
472 // Save the previous block for later
473 PipeBlock previousBloc(fBlock, fBytesWritten);
474 fBlockList.push(previousBloc);
475 }
476 int32_t blockSize = SkMax32(minRequest, kMinBlockSize);
477 fBlock = fAllocator.allocThrow(blockSize);
478 fBytesWritten = 0;
479 *actual = blockSize;
480 return fBlock;
481}
482
483void SkDeferredCanvas::DeferredPipeController::notifyWritten(size_t bytes) {
484 fBytesWritten += bytes;
485}
486
487void SkDeferredCanvas::DeferredPipeController::playback() {
488
489 for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
490 fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize);
491 }
492 fBlockList.reset();
493
494 if (fBlock) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000495 fReader.playback(fBlock, fBytesWritten);
junov@chromium.org77eec242012-07-18 17:54:45 +0000496 fBlock = NULL;
497 }
498
499 // Release all allocated blocks
500 fAllocator.reset();
501}
502
503void SkDeferredCanvas::DeferredPipeController::reset() {
504 fBlockList.reset();
505 fBlock = NULL;
506 fAllocator.reset();
507}
508
509#endif // SK_DEFERRED_CANVAS_USES_GPIPE
510
junov@google.com4370aed2012-01-18 16:21:08 +0000511// SkDeferredCanvas::DeferredDevice
512//------------------------------------
513
514SkDeferredCanvas::DeferredDevice::DeferredDevice(
515 SkDevice* immediateDevice, DeviceContext* deviceContext) :
516 SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
517 immediateDevice->height(), immediateDevice->isOpaque())
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000518 , fFreshFrame(true) {
519
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000520 fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
junov@google.com4370aed2012-01-18 16:21:08 +0000521 fDeviceContext = deviceContext;
522 SkSafeRef(fDeviceContext);
523 fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
524 fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
junov@chromium.org77eec242012-07-18 17:54:45 +0000525#if SK_DEFERRED_CANVAS_USES_GPIPE
526 fPipeController.setPlaybackCanvas(fImmediateCanvas);
527#endif
528 beginRecording();
junov@google.com4370aed2012-01-18 16:21:08 +0000529}
530
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000531SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
junov@google.com4370aed2012-01-18 16:21:08 +0000532 SkSafeUnref(fImmediateCanvas);
533 SkSafeUnref(fDeviceContext);
534}
junov@chromium.org77eec242012-07-18 17:54:45 +0000535
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000536void SkDeferredCanvas::DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
537 fMaxRecordingStorageBytes = maxStorage;
538 recordingCanvas(); // Accessing the recording canvas applies the new limit.
539}
junov@chromium.org77eec242012-07-18 17:54:45 +0000540
541void SkDeferredCanvas::DeferredDevice::endRecording() {
542#if SK_DEFERRED_CANVAS_USES_GPIPE
543 fPipeWriter.endRecording();
544 fPipeController.reset();
545#else
546 fPicture.endRecording();
547#endif
548 fRecordingCanvas = NULL;
549}
550
551void SkDeferredCanvas::DeferredDevice::beginRecording() {
552#if SK_DEFERRED_CANVAS_USES_GPIPE
553 fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0);
554#else
555 fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
556 fImmediateDevice->height(),
557 SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
558#endif
559}
junov@google.com4370aed2012-01-18 16:21:08 +0000560
561void SkDeferredCanvas::DeferredDevice::setDeviceContext(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000562 DeviceContext* deviceContext) {
junov@google.com4370aed2012-01-18 16:21:08 +0000563 SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
564}
565
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000566void SkDeferredCanvas::DeferredDevice::contentsCleared() {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000567 if (!fRecordingCanvas->isDrawingToLayer()) {
568 fFreshFrame = true;
junov@google.com4370aed2012-01-18 16:21:08 +0000569
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000570 // TODO: find a way to transfer the state stack and layers
571 // to the new recording canvas. For now, purging only works
572 // with an empty stack.
573 if (fRecordingCanvas->getSaveCount() == 0) {
junov@google.com4370aed2012-01-18 16:21:08 +0000574
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000575 // Save state that is trashed by the purge
576 SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
577 SkSafeRef(drawFilter); // So that it survives the purge
578 SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
579 SkRegion clipRegion = fRecordingCanvas->getTotalClip();
junov@google.com4370aed2012-01-18 16:21:08 +0000580
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000581 // beginRecording creates a new recording canvas and discards the
582 // old one, hence purging deferred draw ops.
junov@chromium.org77eec242012-07-18 17:54:45 +0000583 this->endRecording();
584 this->beginRecording();
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000585
586 // Restore pre-purge state
587 if (!clipRegion.isEmpty()) {
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000588 fRecordingCanvas->clipRegion(clipRegion,
589 SkRegion::kReplace_Op);
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000590 }
591 if (!matrix.isIdentity()) {
592 fRecordingCanvas->setMatrix(matrix);
593 }
594 if (drawFilter) {
595 fRecordingCanvas->setDrawFilter(drawFilter)->unref();
596 }
597 }
junov@google.com4370aed2012-01-18 16:21:08 +0000598 }
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000599}
600
601bool SkDeferredCanvas::DeferredDevice::isFreshFrame() {
602 bool ret = fFreshFrame;
603 fFreshFrame = false;
604 return ret;
junov@google.com4370aed2012-01-18 16:21:08 +0000605}
606
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000607void SkDeferredCanvas::DeferredDevice::flushPending() {
junov@chromium.org77eec242012-07-18 17:54:45 +0000608#if SK_DEFERRED_CANVAS_USES_GPIPE
609 if (!fPipeController.hasRecorded()) {
610 return;
611 }
612#else
junov@chromium.org4866cc02012-06-01 21:23:07 +0000613 if (!fPicture.hasRecorded()) {
614 return;
615 }
junov@chromium.org77eec242012-07-18 17:54:45 +0000616#endif
junov@google.com4370aed2012-01-18 16:21:08 +0000617 if (fDeviceContext) {
618 fDeviceContext->prepareForDraw();
619 }
junov@chromium.org77eec242012-07-18 17:54:45 +0000620
621#if SK_DEFERRED_CANVAS_USES_GPIPE
622 fPipeWriter.flushRecording(true);
623 fPipeController.playback();
624#else
junov@google.com4370aed2012-01-18 16:21:08 +0000625 fPicture.draw(fImmediateCanvas);
junov@chromium.org77eec242012-07-18 17:54:45 +0000626 this->beginRecording();
627#endif
junov@google.com4370aed2012-01-18 16:21:08 +0000628}
629
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000630void SkDeferredCanvas::DeferredDevice::flush() {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000631 this->flushPending();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000632 fImmediateCanvas->flush();
junov@google.com4370aed2012-01-18 16:21:08 +0000633}
634
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000635SkCanvas* SkDeferredCanvas::DeferredDevice::recordingCanvas() {
636#if SK_DEFERRED_CANVAS_USES_GPIPE
637 if (fPipeController.storageAllocatedForRecording() > fMaxRecordingStorageBytes) {
638 this->flushPending();
639 }
640#endif
641 return fRecordingCanvas;
642}
643
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000644void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) {
junov@chromium.org77eec242012-07-18 17:54:45 +0000645#if SK_DEFERRED_CANVAS_USES_GPIPE
646 if (bitmap.isImmutable()) {
647 // FIXME: Make SkGPipe flatten software-backed non-immutable bitmaps
648 return;
649 }
650#else
junov@chromium.org4866cc02012-06-01 21:23:07 +0000651 if (bitmap.isImmutable() || fPicture.willFlattenPixelsOnRecord(bitmap)) {
652 return; // safe to defer.
junov@google.com4370aed2012-01-18 16:21:08 +0000653 }
junov@chromium.org77eec242012-07-18 17:54:45 +0000654#endif
junov@google.com4370aed2012-01-18 16:21:08 +0000655
656 // For now, drawing a writable bitmap triggers a flush
657 // TODO: implement read-only semantics and auto buffer duplication on write
658 // in SkBitmap/SkPixelRef, which will make deferral possible in this case.
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000659 this->flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000660}
661
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000662uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() {
junov@google.com4370aed2012-01-18 16:21:08 +0000663 return fImmediateDevice->getDeviceCapabilities();
664}
665
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000666int SkDeferredCanvas::DeferredDevice::width() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000667 return fImmediateDevice->width();
668}
669
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000670int SkDeferredCanvas::DeferredDevice::height() const {
junov@google.com4370aed2012-01-18 16:21:08 +0000671 return fImmediateDevice->height();
672}
673
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000674SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000675 this->flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000676 return fImmediateDevice->accessRenderTarget();
677}
678
679void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000680 int x, int y, SkCanvas::Config8888 config8888) {
681
junov@google.com4370aed2012-01-18 16:21:08 +0000682 if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
683 (y + bitmap.height()) >= height()) {
junov@chromium.org8f9ecbd2012-02-13 21:53:45 +0000684 contentsCleared();
junov@google.com4370aed2012-01-18 16:21:08 +0000685 }
686
687 if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
688 SkCanvas::kNative_Premul_Config8888 != config8888 &&
689 kPMColorAlias != config8888) {
690 //Special case config: no deferral
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000691 this->flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000692 fImmediateDevice->writePixels(bitmap, x, y, config8888);
693 }
694
695 SkPaint paint;
696 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
697 fRecordingCanvas->drawSprite(bitmap, x, y, &paint);
698 flushIfNeeded(bitmap);
699}
700
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000701const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000702 this->flushPending();
junov@chromium.org1f9767c2012-02-07 16:27:57 +0000703 return fImmediateDevice->accessBitmap(false);
junov@google.com4370aed2012-01-18 16:21:08 +0000704}
705
706SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000707 SkBitmap::Config config, int width, int height, bool isOpaque,
708 Usage usage) {
709
junov@google.com4370aed2012-01-18 16:21:08 +0000710 // Save layer usage not supported, and not required by SkDeferredCanvas.
711 SkASSERT(usage != kSaveLayer_Usage);
712 // Create a compatible non-deferred device.
scroggo@google.comd7dbd422012-07-03 15:16:30 +0000713 SkAutoTUnref<SkDevice> compatibleDevice
714 (fImmediateDevice->createCompatibleDevice(config, width, height,
715 isOpaque));
junov@google.com4370aed2012-01-18 16:21:08 +0000716 return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
717}
718
719bool SkDeferredCanvas::DeferredDevice::onReadPixels(
junov@chromium.orgc16ca922012-02-24 22:06:27 +0000720 const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
junov@chromium.orgbfeddae2012-07-23 13:35:14 +0000721 this->flushPending();
junov@google.com4370aed2012-01-18 16:21:08 +0000722 return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
723 x, y, config8888);
724}