Implement listing tests to a JSON file for iOS gtest test launcher
[chromium-blink-merge.git] / skia / ext / analysis_canvas.cc
blobca19170339fdede31acb2af07dd4cc30c6ab89ca
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/debug/trace_event.h"
6 #include "base/logging.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"
12 #include "ui/gfx/rect_conversions.h"
14 namespace {
16 const int kNoLayer = -1;
18 bool IsSolidColorPaint(const SkPaint& paint) {
19 SkXfermode::Mode xfermode;
21 // getXfermode can return a NULL, but that is handled
22 // gracefully by AsMode (NULL turns into kSrcOver mode).
23 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
25 // Paint is solid color if the following holds:
26 // - Alpha is 1.0, style is fill, and there are no special effects
27 // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
28 // to kSrc if source alpha is 1.0, which is already checked).
29 return (paint.getAlpha() == 255 &&
30 !paint.getShader() &&
31 !paint.getLooper() &&
32 !paint.getMaskFilter() &&
33 !paint.getColorFilter() &&
34 !paint.getImageFilter() &&
35 paint.getStyle() == SkPaint::kFill_Style &&
36 (xfermode == SkXfermode::kSrc_Mode ||
37 xfermode == SkXfermode::kSrcOver_Mode));
40 // Returns true if the specified drawn_rect will cover the entire canvas, and
41 // that the canvas is not clipped (i.e. it covers ALL of the canvas).
42 bool IsFullQuad(SkCanvas* canvas, const SkRect& drawn_rect) {
43 if (!canvas->isClipRect())
44 return false;
46 SkIRect clip_irect;
47 canvas->getClipDeviceBounds(&clip_irect);
48 // if the clip is smaller than the canvas, we're partly clipped, so abort.
49 if (!clip_irect.contains(SkIRect::MakeSize(canvas->getDeviceSize())))
50 return false;
52 const SkMatrix& matrix = canvas->getTotalMatrix();
53 // If the transform results in a non-axis aligned
54 // rect, then be conservative and return false.
55 if (!matrix.rectStaysRect())
56 return false;
58 SkRect device_rect;
59 matrix.mapRect(&device_rect, drawn_rect);
60 SkRect clip_rect;
61 clip_rect.set(clip_irect);
62 return device_rect.contains(clip_rect);
65 } // namespace
67 namespace skia {
69 void AnalysisCanvas::SetForceNotSolid(bool flag) {
70 is_forced_not_solid_ = flag;
71 if (is_forced_not_solid_)
72 is_solid_color_ = false;
75 void AnalysisCanvas::SetForceNotTransparent(bool flag) {
76 is_forced_not_transparent_ = flag;
77 if (is_forced_not_transparent_)
78 is_transparent_ = false;
81 void AnalysisCanvas::clear(SkColor color) {
82 is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0);
84 if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
85 is_solid_color_ = true;
86 color_ = color;
87 } else {
88 is_solid_color_ = false;
92 void AnalysisCanvas::drawPaint(const SkPaint& paint) {
93 // This check is in SkCanvas::drawPaint(), and some of our unittests rely on
94 // on this, so we reproduce it here.
95 if (isClipEmpty())
96 return;
98 is_solid_color_ = false;
99 is_transparent_ = false;
100 ++draw_op_count_;
103 void AnalysisCanvas::drawPoints(SkCanvas::PointMode mode,
104 size_t count,
105 const SkPoint points[],
106 const SkPaint& paint) {
107 is_solid_color_ = false;
108 is_transparent_ = false;
109 ++draw_op_count_;
112 void AnalysisCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
113 // This recreates the early-exit logic in SkCanvas.cpp.
114 SkRect scratch;
115 if (paint.canComputeFastBounds() &&
116 quickReject(paint.computeFastBounds(rect, &scratch))) {
117 return;
120 // An extra no-op check SkCanvas.cpp doesn't do.
121 if (paint.nothingToDraw())
122 return;
124 bool does_cover_canvas = IsFullQuad(this, rect);
126 SkXfermode::Mode xfermode;
127 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
129 // This canvas will become transparent if the following holds:
130 // - The quad is a full tile quad
131 // - We're not in "forced not transparent" mode
132 // - Transfer mode is clear (0 color, 0 alpha)
134 // If the paint alpha is not 0, or if the transfrer mode is
135 // not src, then this canvas will not be transparent.
137 // In all other cases, we keep the current transparent value
138 if (does_cover_canvas &&
139 !is_forced_not_transparent_ &&
140 xfermode == SkXfermode::kClear_Mode) {
141 is_transparent_ = true;
142 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
143 is_transparent_ = false;
146 // This bitmap is solid if and only if the following holds.
147 // Note that this might be overly conservative:
148 // - We're not in "forced not solid" mode
149 // - Paint is solid color
150 // - The quad is a full tile quad
151 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
152 is_solid_color_ = true;
153 color_ = paint.getColor();
154 } else {
155 is_solid_color_ = false;
157 ++draw_op_count_;
160 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
161 is_solid_color_ = false;
162 is_transparent_ = false;
163 ++draw_op_count_;
166 void AnalysisCanvas::drawRRect(const SkRRect& rr, const SkPaint& paint) {
167 // This should add the SkRRect to an SkPath, and call
168 // drawPath, but since drawPath ignores the SkPath, just
169 // do the same work here.
170 is_solid_color_ = false;
171 is_transparent_ = false;
172 ++draw_op_count_;
175 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
176 is_solid_color_ = false;
177 is_transparent_ = false;
178 ++draw_op_count_;
181 void AnalysisCanvas::drawBitmap(const SkBitmap& bitmap,
182 SkScalar left,
183 SkScalar top,
184 const SkPaint*) {
185 is_solid_color_ = false;
186 is_transparent_ = false;
187 ++draw_op_count_;
190 void AnalysisCanvas::drawBitmapRectToRect(const SkBitmap&,
191 const SkRect* src,
192 const SkRect& dst,
193 const SkPaint* paint,
194 DrawBitmapRectFlags flags) {
195 // Call drawRect to determine transparency,
196 // but reset solid color to false.
197 SkPaint tmpPaint;
198 if (!paint)
199 paint = &tmpPaint;
200 drawRect(dst, *paint);
201 is_solid_color_ = false;
202 ++draw_op_count_;
205 void AnalysisCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
206 const SkMatrix& matrix,
207 const SkPaint* paint) {
208 is_solid_color_ = false;
209 is_transparent_ = false;
210 ++draw_op_count_;
213 void AnalysisCanvas::drawBitmapNine(const SkBitmap& bitmap,
214 const SkIRect& center,
215 const SkRect& dst,
216 const SkPaint* paint) {
217 is_solid_color_ = false;
218 is_transparent_ = false;
219 ++draw_op_count_;
222 void AnalysisCanvas::drawSprite(const SkBitmap& bitmap,
223 int left,
224 int top,
225 const SkPaint* paint) {
226 is_solid_color_ = false;
227 is_transparent_ = false;
228 ++draw_op_count_;
231 void AnalysisCanvas::onDrawText(const void* text,
232 size_t len,
233 SkScalar x,
234 SkScalar y,
235 const SkPaint& paint) {
236 is_solid_color_ = false;
237 is_transparent_ = false;
238 ++draw_op_count_;
241 void AnalysisCanvas::onDrawPosText(const void* text,
242 size_t byteLength,
243 const SkPoint pos[],
244 const SkPaint& paint) {
245 is_solid_color_ = false;
246 is_transparent_ = false;
247 ++draw_op_count_;
250 void AnalysisCanvas::onDrawPosTextH(const void* text,
251 size_t byteLength,
252 const SkScalar xpos[],
253 SkScalar constY,
254 const SkPaint& paint) {
255 is_solid_color_ = false;
256 is_transparent_ = false;
257 ++draw_op_count_;
260 void AnalysisCanvas::onDrawTextOnPath(const void* text,
261 size_t len,
262 const SkPath& path,
263 const SkMatrix* matrix,
264 const SkPaint& paint) {
265 is_solid_color_ = false;
266 is_transparent_ = false;
267 ++draw_op_count_;
270 void AnalysisCanvas::onDrawTextBlob(const SkTextBlob* blob,
271 SkScalar x,
272 SkScalar y,
273 const SkPaint &paint) {
274 is_solid_color_ = false;
275 is_transparent_ = false;
276 ++draw_op_count_;
279 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer,
280 const SkRRect& inner,
281 const SkPaint& paint) {
282 is_solid_color_ = false;
283 is_transparent_ = false;
284 ++draw_op_count_;
287 void AnalysisCanvas::drawVertices(SkCanvas::VertexMode,
288 int vertex_count,
289 const SkPoint verts[],
290 const SkPoint texs[],
291 const SkColor colors[],
292 SkXfermode* xmode,
293 const uint16_t indices[],
294 int index_count,
295 const SkPaint& paint) {
296 is_solid_color_ = false;
297 is_transparent_ = false;
298 ++draw_op_count_;
301 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
302 // by any pixels
303 static SkBitmap MakeEmptyBitmap(int width, int height) {
304 SkBitmap bitmap;
305 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
306 return bitmap;
309 AnalysisCanvas::AnalysisCanvas(int width, int height)
310 : INHERITED(MakeEmptyBitmap(width, height)),
311 saved_stack_size_(0),
312 force_not_solid_stack_level_(kNoLayer),
313 force_not_transparent_stack_level_(kNoLayer),
314 is_forced_not_solid_(false),
315 is_forced_not_transparent_(false),
316 is_solid_color_(true),
317 color_(SK_ColorTRANSPARENT),
318 is_transparent_(true),
319 draw_op_count_(0) {
322 AnalysisCanvas::~AnalysisCanvas() {}
324 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
325 if (is_transparent_) {
326 *color = SK_ColorTRANSPARENT;
327 return true;
329 if (is_solid_color_) {
330 *color = color_;
331 return true;
333 return false;
336 bool AnalysisCanvas::abortDrawing() {
337 // Early out as soon as we have more than one draw op.
338 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to
339 // balance the amount of time we spend analyzing vs how many tiles would be
340 // solid if the number was higher.
341 if (draw_op_count_ > 1) {
342 // We have to reset solid/transparent state to false since we don't
343 // know whether consequent operations will make this false.
344 is_solid_color_ = false;
345 is_transparent_ = false;
346 return true;
348 return false;
351 void AnalysisCanvas::OnComplexClip() {
352 // complex clips can make our calls to IsFullQuad invalid (ie have false
353 // positives). As a precaution, force the setting to be non-solid
354 // and non-transparent until we pop this
355 if (force_not_solid_stack_level_ == kNoLayer) {
356 force_not_solid_stack_level_ = saved_stack_size_;
357 SetForceNotSolid(true);
359 if (force_not_transparent_stack_level_ == kNoLayer) {
360 force_not_transparent_stack_level_ = saved_stack_size_;
361 SetForceNotTransparent(true);
365 void AnalysisCanvas::onClipRect(const SkRect& rect,
366 SkRegion::Op op,
367 ClipEdgeStyle edge_style) {
368 INHERITED::onClipRect(rect, op, edge_style);
371 void AnalysisCanvas::onClipPath(const SkPath& path,
372 SkRegion::Op op,
373 ClipEdgeStyle edge_style) {
374 OnComplexClip();
375 INHERITED::onClipRect(path.getBounds(), op, edge_style);
378 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
379 SkRegion::Op op,
380 ClipEdgeStyle edge_style) {
381 OnComplexClip();
382 INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
385 void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
386 const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle;
387 if (deviceRgn.isRect()) {
388 onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
389 return;
391 OnComplexClip();
392 INHERITED::onClipRect(
393 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style);
396 void AnalysisCanvas::willSave() {
397 ++saved_stack_size_;
398 INHERITED::willSave();
401 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
402 const SkRect* bounds,
403 const SkPaint* paint,
404 SkCanvas::SaveFlags flags) {
406 ++saved_stack_size_;
408 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
409 SkRect canvas_bounds;
410 canvas_bounds.set(canvas_ibounds);
412 // If after we draw to the saved layer, we have to blend with the current
413 // layer, then we can conservatively say that the canvas will not be of
414 // solid color.
415 if ((paint && !IsSolidColorPaint(*paint)) ||
416 (bounds && !bounds->contains(canvas_bounds))) {
417 if (force_not_solid_stack_level_ == kNoLayer) {
418 force_not_solid_stack_level_ = saved_stack_size_;
419 SetForceNotSolid(true);
423 // If after we draw to the save layer, we have to blend with the current
424 // layer using any part of the current layer's alpha, then we can
425 // conservatively say that the canvas will not be transparent.
426 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
427 if (paint)
428 SkXfermode::AsMode(paint->getXfermode(), &xfermode);
429 if (xfermode != SkXfermode::kSrc_Mode) {
430 if (force_not_transparent_stack_level_ == kNoLayer) {
431 force_not_transparent_stack_level_ = saved_stack_size_;
432 SetForceNotTransparent(true);
436 INHERITED::willSaveLayer(bounds, paint, flags);
437 // Actually saving a layer here could cause a new bitmap to be created
438 // and real rendering to occur.
439 return kNoLayer_SaveLayerStrategy;
442 void AnalysisCanvas::willRestore() {
443 DCHECK(saved_stack_size_);
444 if (saved_stack_size_) {
445 --saved_stack_size_;
446 if (saved_stack_size_ < force_not_solid_stack_level_) {
447 SetForceNotSolid(false);
448 force_not_solid_stack_level_ = kNoLayer;
450 if (saved_stack_size_ < force_not_transparent_stack_level_) {
451 SetForceNotTransparent(false);
452 force_not_transparent_stack_level_ = kNoLayer;
456 INHERITED::willRestore();
459 } // namespace skia