Fix uninitialized value in AnalysisCanvas
[chromium-blink-merge.git] / skia / ext / analysis_canvas.cc
blob1b8694a1ccccf507552df2f266f416d9d618aec7
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/logging.h"
6 #include "base/trace_event/trace_event.h"
7 #include "skia/ext/analysis_canvas.h"
8 #include "third_party/skia/include/core/SkDraw.h"
9 #include "third_party/skia/include/core/SkRRect.h"
10 #include "third_party/skia/include/core/SkShader.h"
11 #include "third_party/skia/src/core/SkRasterClip.h"
13 namespace {
15 const int kNoLayer = -1;
17 bool ActsLikeClear(SkXfermode::Mode mode, unsigned src_alpha) {
18 switch (mode) {
19 case SkXfermode::kClear_Mode:
20 return true;
21 case SkXfermode::kSrc_Mode:
22 case SkXfermode::kSrcIn_Mode:
23 case SkXfermode::kDstIn_Mode:
24 case SkXfermode::kSrcOut_Mode:
25 case SkXfermode::kDstATop_Mode:
26 return src_alpha == 0;
27 case SkXfermode::kDstOut_Mode:
28 return src_alpha == 0xFF;
29 default:
30 return false;
34 bool IsSolidColorPaint(const SkPaint& paint) {
35 SkXfermode::Mode xfermode;
37 // getXfermode can return a NULL, but that is handled
38 // gracefully by AsMode (NULL turns into kSrcOver mode).
39 if (!SkXfermode::AsMode(paint.getXfermode(), &xfermode))
40 return false;
42 // Paint is solid color if the following holds:
43 // - Alpha is 1.0, style is fill, and there are no special effects
44 // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
45 // to kSrc if source alpha is 1.0, which is already checked).
46 return (paint.getAlpha() == 255 &&
47 !paint.getShader() &&
48 !paint.getLooper() &&
49 !paint.getMaskFilter() &&
50 !paint.getColorFilter() &&
51 !paint.getImageFilter() &&
52 paint.getStyle() == SkPaint::kFill_Style &&
53 (xfermode == SkXfermode::kSrc_Mode ||
54 xfermode == SkXfermode::kSrcOver_Mode));
57 // Returns true if the specified drawn_rect will cover the entire canvas, and
58 // that the canvas is not clipped (i.e. it covers ALL of the canvas).
59 bool IsFullQuad(SkCanvas* canvas, const SkRect& drawn_rect) {
60 if (!canvas->isClipRect())
61 return false;
63 SkIRect clip_irect;
64 if (!canvas->getClipDeviceBounds(&clip_irect))
65 return false;
67 // if the clip is smaller than the canvas, we're partly clipped, so abort.
68 if (!clip_irect.contains(SkIRect::MakeSize(canvas->getDeviceSize())))
69 return false;
71 const SkMatrix& matrix = canvas->getTotalMatrix();
72 // If the transform results in a non-axis aligned
73 // rect, then be conservative and return false.
74 if (!matrix.rectStaysRect())
75 return false;
77 SkRect device_rect;
78 matrix.mapRect(&device_rect, drawn_rect);
79 SkRect clip_rect;
80 clip_rect.set(clip_irect);
81 return device_rect.contains(clip_rect);
84 } // namespace
86 namespace skia {
88 void AnalysisCanvas::SetForceNotSolid(bool flag) {
89 is_forced_not_solid_ = flag;
90 if (is_forced_not_solid_)
91 is_solid_color_ = false;
94 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
95 is_forced_not_transparent_ = flag;
96 if (is_forced_not_transparent_)
97 is_transparent_ = false;
100 void AnalysisCanvas::onDrawPaint(const SkPaint& paint) {
101 SkRect rect;
102 if (getClipBounds(&rect))
103 drawRect(rect, paint);
106 void AnalysisCanvas::onDrawPoints(SkCanvas::PointMode mode,
107 size_t count,
108 const SkPoint points[],
109 const SkPaint& paint) {
110 is_solid_color_ = false;
111 is_transparent_ = false;
112 ++draw_op_count_;
115 void AnalysisCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
116 // This recreates the early-exit logic in SkCanvas.cpp.
117 SkRect scratch;
118 if (paint.canComputeFastBounds() &&
119 quickReject(paint.computeFastBounds(rect, &scratch))) {
120 return;
123 // An extra no-op check SkCanvas.cpp doesn't do.
124 if (paint.nothingToDraw())
125 return;
127 bool does_cover_canvas = IsFullQuad(this, rect);
129 SkXfermode::Mode xfermode;
130 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
132 // This canvas will become transparent if the following holds:
133 // - The quad is a full tile quad
134 // - We're not in "forced not transparent" mode
135 // - Transfer mode is clear (0 color, 0 alpha)
137 // If the paint alpha is not 0, or if the transfrer mode is
138 // not src, then this canvas will not be transparent.
140 // In all other cases, we keep the current transparent value
141 if (does_cover_canvas &&
142 !is_forced_not_transparent_ &&
143 ActsLikeClear(xfermode, paint.getAlpha())) {
144 is_transparent_ = true;
145 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
146 is_transparent_ = false;
149 // This bitmap is solid if and only if the following holds.
150 // Note that this might be overly conservative:
151 // - We're not in "forced not solid" mode
152 // - Paint is solid color
153 // - The quad is a full tile quad
154 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
155 is_solid_color_ = true;
156 color_ = paint.getColor();
157 } else {
158 is_solid_color_ = false;
160 ++draw_op_count_;
163 void AnalysisCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
164 is_solid_color_ = false;
165 is_transparent_ = false;
166 ++draw_op_count_;
169 void AnalysisCanvas::onDrawRRect(const SkRRect& rr, const SkPaint& paint) {
170 // This should add the SkRRect to an SkPath, and call
171 // drawPath, but since drawPath ignores the SkPath, just
172 // do the same work here.
173 is_solid_color_ = false;
174 is_transparent_ = false;
175 ++draw_op_count_;
178 void AnalysisCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
179 is_solid_color_ = false;
180 is_transparent_ = false;
181 ++draw_op_count_;
184 void AnalysisCanvas::onDrawBitmap(const SkBitmap& bitmap,
185 SkScalar left,
186 SkScalar top,
187 const SkPaint*) {
188 is_solid_color_ = false;
189 is_transparent_ = false;
190 ++draw_op_count_;
193 void AnalysisCanvas::onDrawBitmapRect(const SkBitmap&,
194 const SkRect* src,
195 const SkRect& dst,
196 const SkPaint* paint,
197 DrawBitmapRectFlags flags) {
198 // Call drawRect to determine transparency,
199 // but reset solid color to false.
200 SkPaint tmpPaint;
201 if (!paint)
202 paint = &tmpPaint;
203 drawRect(dst, *paint);
204 is_solid_color_ = false;
205 ++draw_op_count_;
208 void AnalysisCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
209 const SkIRect& center,
210 const SkRect& dst,
211 const SkPaint* paint) {
212 is_solid_color_ = false;
213 is_transparent_ = false;
214 ++draw_op_count_;
217 void AnalysisCanvas::onDrawImage(const SkImage*,
218 SkScalar left,
219 SkScalar top,
220 const SkPaint*) {
221 is_solid_color_ = false;
222 is_transparent_ = false;
223 ++draw_op_count_;
226 void AnalysisCanvas::onDrawImageRect(const SkImage*,
227 const SkRect* src,
228 const SkRect& dst,
229 const SkPaint* paint) {
230 // Call drawRect to determine transparency,
231 // but reset solid color to false.
232 SkPaint tmpPaint;
233 if (!paint)
234 paint = &tmpPaint;
235 drawRect(dst, *paint);
236 is_solid_color_ = false;
237 ++draw_op_count_;
240 void AnalysisCanvas::onDrawSprite(const SkBitmap& bitmap,
241 int left,
242 int top,
243 const SkPaint* paint) {
244 is_solid_color_ = false;
245 is_transparent_ = false;
246 ++draw_op_count_;
249 void AnalysisCanvas::onDrawText(const void* text,
250 size_t len,
251 SkScalar x,
252 SkScalar y,
253 const SkPaint& paint) {
254 is_solid_color_ = false;
255 is_transparent_ = false;
256 ++draw_op_count_;
259 void AnalysisCanvas::onDrawPosText(const void* text,
260 size_t byteLength,
261 const SkPoint pos[],
262 const SkPaint& paint) {
263 is_solid_color_ = false;
264 is_transparent_ = false;
265 ++draw_op_count_;
268 void AnalysisCanvas::onDrawPosTextH(const void* text,
269 size_t byteLength,
270 const SkScalar xpos[],
271 SkScalar constY,
272 const SkPaint& paint) {
273 is_solid_color_ = false;
274 is_transparent_ = false;
275 ++draw_op_count_;
278 void AnalysisCanvas::onDrawTextOnPath(const void* text,
279 size_t len,
280 const SkPath& path,
281 const SkMatrix* matrix,
282 const SkPaint& paint) {
283 is_solid_color_ = false;
284 is_transparent_ = false;
285 ++draw_op_count_;
288 void AnalysisCanvas::onDrawTextBlob(const SkTextBlob* blob,
289 SkScalar x,
290 SkScalar y,
291 const SkPaint &paint) {
292 is_solid_color_ = false;
293 is_transparent_ = false;
294 ++draw_op_count_;
297 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
298 const SkRRect& inner,
299 const SkPaint& paint) {
300 is_solid_color_ = false;
301 is_transparent_ = false;
302 ++draw_op_count_;
305 void AnalysisCanvas::onDrawVertices(SkCanvas::VertexMode,
306 int vertex_count,
307 const SkPoint verts[],
308 const SkPoint texs[],
309 const SkColor colors[],
310 SkXfermode* xmode,
311 const uint16_t indices[],
312 int index_count,
313 const SkPaint& paint) {
314 is_solid_color_ = false;
315 is_transparent_ = false;
316 ++draw_op_count_;
319 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
320 // by any pixels
321 static SkBitmap MakeEmptyBitmap(int width, int height) {
322 SkBitmap bitmap;
323 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
324 return bitmap;
327 AnalysisCanvas::AnalysisCanvas(int width, int height)
328 : INHERITED(MakeEmptyBitmap(width, height)),
329 saved_stack_size_(0),
330 force_not_solid_stack_level_(kNoLayer),
331 force_not_transparent_stack_level_(kNoLayer),
332 is_forced_not_solid_(false),
333 is_forced_not_transparent_(false),
334 is_solid_color_(true),
335 color_(SK_ColorTRANSPARENT),
336 is_transparent_(true),
337 draw_op_count_(0) {
340 AnalysisCanvas::~AnalysisCanvas() {}
342 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
343 if (is_transparent_) {
344 *color = SK_ColorTRANSPARENT;
345 return true;
347 if (is_solid_color_) {
348 *color = color_;
349 return true;
351 return false;
354 bool AnalysisCanvas::abortDrawing() {
355 // Early out as soon as we have more than one draw op.
356 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
357 // balance the amount of time we spend analyzing vs how many tiles would be
358 // solid if the number was higher.
359 if (draw_op_count_ > 1) {
360 // We have to reset solid/transparent state to false since we don't
361 // know whether consequent operations will make this false.
362 is_solid_color_ = false;
363 is_transparent_ = false;
364 return true;
366 return false;
369 void AnalysisCanvas::OnComplexClip() {
370 // complex clips can make our calls to IsFullQuad invalid (ie have false
371 // positives). As a precaution, force the setting to be non-solid
372 // and non-transparent until we pop this
373 if (force_not_solid_stack_level_ == kNoLayer) {
374 force_not_solid_stack_level_ = saved_stack_size_;
375 SetForceNotSolid(true);
377 if (force_not_transparent_stack_level_ == kNoLayer) {
378 force_not_transparent_stack_level_ = saved_stack_size_;
379 SetForceNotTransparent(true);
383 void AnalysisCanvas::onClipRect(const SkRect& rect,
384 SkRegion::Op op,
385 ClipEdgeStyle edge_style) {
386 INHERITED::onClipRect(rect, op, edge_style);
389 void AnalysisCanvas::onClipPath(const SkPath& path,
390 SkRegion::Op op,
391 ClipEdgeStyle edge_style) {
392 OnComplexClip();
393 INHERITED::onClipRect(path.getBounds(), op, edge_style);
396 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
397 SkRegion::Op op,
398 ClipEdgeStyle edge_style) {
399 OnComplexClip();
400 INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
403 void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
404 const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle;
405 if (deviceRgn.isRect()) {
406 onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
407 return;
409 OnComplexClip();
410 INHERITED::onClipRect(
411 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
414 void AnalysisCanvas::willSave() {
415 ++saved_stack_size_;
416 INHERITED::willSave();
419 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
420 const SkRect* bounds,
421 const SkPaint* paint,
422 SkCanvas::SaveFlags flags) {
424 ++saved_stack_size_;
426 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
427 SkRect canvas_bounds;
428 canvas_bounds.set(canvas_ibounds);
430 // If after we draw to the saved layer, we have to blend with the current
431 // layer, then we can conservatively say that the canvas will not be of
432 // solid color.
433 if ((paint && !IsSolidColorPaint(*paint)) ||
434 (bounds && !bounds->contains(canvas_bounds))) {
435 if (force_not_solid_stack_level_ == kNoLayer) {
436 force_not_solid_stack_level_ = saved_stack_size_;
437 SetForceNotSolid(true);
441 // If after we draw to the save layer, we have to blend with the current
442 // layer using any part of the current layer's alpha, then we can
443 // conservatively say that the canvas will not be transparent.
444 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
445 if (paint)
446 SkXfermode::AsMode(paint->getXfermode(), &xfermode);
447 if (xfermode != SkXfermode::kDst_Mode) {
448 if (force_not_transparent_stack_level_ == kNoLayer) {
449 force_not_transparent_stack_level_ = saved_stack_size_;
450 SetForceNotTransparent(true);
454 INHERITED::willSaveLayer(bounds, paint, flags);
455 // Actually saving a layer here could cause a new bitmap to be created
456 // and real rendering to occur.
457 return kNoLayer_SaveLayerStrategy;
460 void AnalysisCanvas::willRestore() {
461 DCHECK(saved_stack_size_);
462 if (saved_stack_size_) {
463 --saved_stack_size_;
464 if (saved_stack_size_ < force_not_solid_stack_level_) {
465 SetForceNotSolid(false);
466 force_not_solid_stack_level_ = kNoLayer;
468 if (saved_stack_size_ < force_not_transparent_stack_level_) {
469 SetForceNotTransparent(false);
470 force_not_transparent_stack_level_ = kNoLayer;
474 INHERITED::willRestore();
477 } // namespace skia