Add a string for translation.
[chromium-blink-merge.git] / skia / ext / analysis_canvas.cc
blob15ec6d880fc0d5164d1942faf7e86614147e1299
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);
83 has_text_ = false;
85 if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
86 is_solid_color_ = true;
87 color_ = color;
88 } else {
89 is_solid_color_ = false;
93 void AnalysisCanvas::drawPaint(const SkPaint& paint) {
94 // This check is in SkCanvas::drawPaint(), and some of our unittests rely on
95 // on this, so we reproduce it here.
96 if (isClipEmpty())
97 return;
99 is_solid_color_ = false;
100 is_transparent_ = false;
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;
111 void AnalysisCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
112 // This recreates the early-exit logic in SkCanvas.cpp, which aborts early
113 // if the paint will "draw nothing".
114 if (paint.nothingToDraw())
115 return;
117 bool does_cover_canvas = IsFullQuad(this, rect);
119 SkXfermode::Mode xfermode;
120 SkXfermode::AsMode(paint.getXfermode(), &xfermode);
122 // This canvas will become transparent if the following holds:
123 // - The quad is a full tile quad
124 // - We're not in "forced not transparent" mode
125 // - Transfer mode is clear (0 color, 0 alpha)
127 // If the paint alpha is not 0, or if the transfrer mode is
128 // not src, then this canvas will not be transparent.
130 // In all other cases, we keep the current transparent value
131 if (does_cover_canvas &&
132 !is_forced_not_transparent_ &&
133 xfermode == SkXfermode::kClear_Mode) {
134 is_transparent_ = true;
135 has_text_ = false;
136 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
137 is_transparent_ = false;
140 // This bitmap is solid if and only if the following holds.
141 // Note that this might be overly conservative:
142 // - We're not in "forced not solid" mode
143 // - Paint is solid color
144 // - The quad is a full tile quad
145 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
146 is_solid_color_ = true;
147 color_ = paint.getColor();
148 has_text_ = false;
149 } else {
150 is_solid_color_ = false;
154 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
155 is_solid_color_ = false;
156 is_transparent_ = false;
159 void AnalysisCanvas::drawRRect(const SkRRect& rr, const SkPaint& paint) {
160 // This should add the SkRRect to an SkPath, and call
161 // drawPath, but since drawPath ignores the SkPath, just
162 // do the same work here.
163 is_solid_color_ = false;
164 is_transparent_ = false;
167 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
168 is_solid_color_ = false;
169 is_transparent_ = false;
172 void AnalysisCanvas::drawBitmap(const SkBitmap& bitmap,
173 SkScalar left,
174 SkScalar top,
175 const SkPaint*) {
176 is_solid_color_ = false;
177 is_transparent_ = false;
180 void AnalysisCanvas::drawBitmapRectToRect(const SkBitmap&,
181 const SkRect* src,
182 const SkRect& dst,
183 const SkPaint* paint,
184 DrawBitmapRectFlags flags) {
185 // Call drawRect to determine transparency,
186 // but reset solid color to false.
187 SkPaint tmpPaint;
188 if (!paint)
189 paint = &tmpPaint;
190 drawRect(dst, *paint);
191 is_solid_color_ = false;
194 void AnalysisCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
195 const SkMatrix& matrix,
196 const SkPaint* paint) {
197 is_solid_color_ = false;
198 is_transparent_ = false;
201 void AnalysisCanvas::drawBitmapNine(const SkBitmap& bitmap,
202 const SkIRect& center,
203 const SkRect& dst,
204 const SkPaint* paint) {
205 is_solid_color_ = false;
206 is_transparent_ = false;
209 void AnalysisCanvas::drawSprite(const SkBitmap& bitmap,
210 int left,
211 int top,
212 const SkPaint* paint) {
213 is_solid_color_ = false;
214 is_transparent_ = false;
217 void AnalysisCanvas::onDrawText(const void* text,
218 size_t len,
219 SkScalar x,
220 SkScalar y,
221 const SkPaint& paint) {
222 is_solid_color_ = false;
223 is_transparent_ = false;
224 has_text_ = true;
227 void AnalysisCanvas::onDrawPosText(const void* text,
228 size_t byteLength,
229 const SkPoint pos[],
230 const SkPaint& paint) {
231 is_solid_color_ = false;
232 is_transparent_ = false;
233 has_text_ = true;
236 void AnalysisCanvas::onDrawPosTextH(const void* text,
237 size_t byteLength,
238 const SkScalar xpos[],
239 SkScalar constY,
240 const SkPaint& paint) {
241 is_solid_color_ = false;
242 is_transparent_ = false;
243 has_text_ = true;
246 void AnalysisCanvas::onDrawTextOnPath(const void* text,
247 size_t len,
248 const SkPath& path,
249 const SkMatrix* matrix,
250 const SkPaint& paint) {
251 is_solid_color_ = false;
252 is_transparent_ = false;
253 has_text_ = true;
256 void AnalysisCanvas::drawVertices(SkCanvas::VertexMode,
257 int vertex_count,
258 const SkPoint verts[],
259 const SkPoint texs[],
260 const SkColor colors[],
261 SkXfermode* xmode,
262 const uint16_t indices[],
263 int index_count,
264 const SkPaint& paint) {
265 is_solid_color_ = false;
266 is_transparent_ = false;
269 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed
270 // by any pixels
271 static SkBitmap MakeEmptyBitmap(int width, int height) {
272 SkBitmap bitmap;
273 bitmap.setConfig(SkBitmap::kNo_Config, width, height);
274 return bitmap;
277 AnalysisCanvas::AnalysisCanvas(int width, int height)
278 : INHERITED(MakeEmptyBitmap(width, height)),
279 saved_stack_size_(0),
280 force_not_solid_stack_level_(kNoLayer),
281 force_not_transparent_stack_level_(kNoLayer),
282 is_forced_not_solid_(false),
283 is_forced_not_transparent_(false),
284 is_solid_color_(true),
285 is_transparent_(true),
286 has_text_(false) {}
288 AnalysisCanvas::~AnalysisCanvas() {}
290 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
291 if (is_transparent_) {
292 *color = SK_ColorTRANSPARENT;
293 return true;
295 if (is_solid_color_) {
296 *color = color_;
297 return true;
299 return false;
302 bool AnalysisCanvas::HasText() const { return has_text_; }
304 bool AnalysisCanvas::abortDrawing() {
305 // Early out as soon as we have detected that the tile has text.
306 return HasText();
309 void AnalysisCanvas::onClipRect(const SkRect& rect, SkRegion::Op op,
310 ClipEdgeStyle edge_style) {
312 INHERITED::onClipRect(rect, op, edge_style);
315 void AnalysisCanvas::onClipPath(const SkPath& path, SkRegion::Op op,
316 ClipEdgeStyle edge_style) {
317 // clipPaths can make our calls to IsFullQuad invalid (ie have false
318 // positives). As a precaution, force the setting to be non-solid
319 // and non-transparent until we pop this
320 if (force_not_solid_stack_level_ == kNoLayer) {
321 force_not_solid_stack_level_ = saved_stack_size_;
322 SetForceNotSolid(true);
324 if (force_not_transparent_stack_level_ == kNoLayer) {
325 force_not_transparent_stack_level_ = saved_stack_size_;
326 SetForceNotTransparent(true);
329 INHERITED::onClipRect(path.getBounds(), op, edge_style);
332 void AnalysisCanvas::onClipRRect(const SkRRect& rrect,
333 SkRegion::Op op,
334 ClipEdgeStyle edge_style) {
335 // clipRRect can make our calls to IsFullQuad invalid (ie have false
336 // positives). As a precaution, force the setting to be non-solid
337 // and non-transparent until we pop this
338 if (force_not_solid_stack_level_ == kNoLayer) {
339 force_not_solid_stack_level_ = saved_stack_size_;
340 SetForceNotSolid(true);
342 if (force_not_transparent_stack_level_ == kNoLayer) {
343 force_not_transparent_stack_level_ = saved_stack_size_;
344 SetForceNotTransparent(true);
347 INHERITED::onClipRect(rrect.getBounds(), op, edge_style);
350 void AnalysisCanvas::willSave(SkCanvas::SaveFlags flags) {
351 ++saved_stack_size_;
352 INHERITED::willSave(flags);
355 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer(
356 const SkRect* bounds,
357 const SkPaint* paint,
358 SkCanvas::SaveFlags flags) {
360 ++saved_stack_size_;
362 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize());
363 SkRect canvas_bounds;
364 canvas_bounds.set(canvas_ibounds);
366 // If after we draw to the saved layer, we have to blend with the current
367 // layer, then we can conservatively say that the canvas will not be of
368 // solid color.
369 if ((paint && !IsSolidColorPaint(*paint)) ||
370 (bounds && !bounds->contains(canvas_bounds))) {
371 if (force_not_solid_stack_level_ == kNoLayer) {
372 force_not_solid_stack_level_ = saved_stack_size_;
373 SetForceNotSolid(true);
377 // If after we draw to the save layer, we have to blend with the current
378 // layer using any part of the current layer's alpha, then we can
379 // conservatively say that the canvas will not be transparent.
380 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
381 if (paint)
382 SkXfermode::AsMode(paint->getXfermode(), &xfermode);
383 if (xfermode != SkXfermode::kSrc_Mode) {
384 if (force_not_transparent_stack_level_ == kNoLayer) {
385 force_not_transparent_stack_level_ = saved_stack_size_;
386 SetForceNotTransparent(true);
390 INHERITED::willSaveLayer(bounds, paint, flags);
391 // Actually saving a layer here could cause a new bitmap to be created
392 // and real rendering to occur.
393 return kNoLayer_SaveLayerStrategy;
396 void AnalysisCanvas::willRestore() {
397 DCHECK(saved_stack_size_);
398 if (saved_stack_size_) {
399 --saved_stack_size_;
400 if (saved_stack_size_ < force_not_solid_stack_level_) {
401 SetForceNotSolid(false);
402 force_not_solid_stack_level_ = kNoLayer;
404 if (saved_stack_size_ < force_not_transparent_stack_level_) {
405 SetForceNotTransparent(false);
406 force_not_transparent_stack_level_ = kNoLayer;
410 INHERITED::willRestore();
413 } // namespace skia