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. 4 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" 13 14 namespace { 15 16 const int kNoLayer = -1; 17 18 bool IsSolidColorPaint(const SkPaint& paint) { 19 SkXfermode::Mode xfermode; 20 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); 24 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)); 38 } 39 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; 45 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; 51 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; 57 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); 63 } 64 65 } // namespace 66 67 namespace skia { 68 69 void AnalysisCanvas::SetForceNotSolid(bool flag) { 70 is_forced_not_solid_ = flag; 71 if (is_forced_not_solid_) 72 is_solid_color_ = false; 73 } 74 75 void AnalysisCanvas::SetForceNotTransparent(bool flag) { 76 is_forced_not_transparent_ = flag; 77 if (is_forced_not_transparent_) 78 is_transparent_ = false; 79 } 80 81 void AnalysisCanvas::clear(SkColor color) { 82 is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0); 83 has_text_ = false; 84 85 if (!is_forced_not_solid_ && SkColorGetA(color) == 255) { 86 is_solid_color_ = true; 87 color_ = color; 88 } else { 89 is_solid_color_ = false; 90 } 91 } 92 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; 98 99 is_solid_color_ = false; 100 is_transparent_ = false; 101 } 102 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 } 110 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; 116 117 bool does_cover_canvas = IsFullQuad(this, rect); 118 119 SkXfermode::Mode xfermode; 120 SkXfermode::AsMode(paint.getXfermode(), &xfermode); 121 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) 126 // 127 // If the paint alpha is not 0, or if the transfrer mode is 128 // not src, then this canvas will not be transparent. 129 // 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; 138 } 139 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; 151 } 152 } 153 154 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) { 155 is_solid_color_ = false; 156 is_transparent_ = false; 157 } 158 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; 165 } 166 167 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 168 is_solid_color_ = false; 169 is_transparent_ = false; 170 } 171 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; 178 } 179 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; 192 } 193 194 void AnalysisCanvas::drawBitmapMatrix(const SkBitmap& bitmap, 195 const SkMatrix& matrix, 196 const SkPaint* paint) { 197 is_solid_color_ = false; 198 is_transparent_ = false; 199 } 200 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; 207 } 208 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; 215 } 216 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; 225 } 226 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; 234 } 235 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; 244 } 245 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; 254 } 255 256 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer, 257 const SkRRect& inner, 258 const SkPaint& paint) { 259 is_solid_color_ = false; 260 is_transparent_ = false; 261 } 262 263 void AnalysisCanvas::drawVertices(SkCanvas::VertexMode, 264 int vertex_count, 265 const SkPoint verts[], 266 const SkPoint texs[], 267 const SkColor colors[], 268 SkXfermode* xmode, 269 const uint16_t indices[], 270 int index_count, 271 const SkPaint& paint) { 272 is_solid_color_ = false; 273 is_transparent_ = false; 274 } 275 276 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed 277 // by any pixels 278 static SkBitmap MakeEmptyBitmap(int width, int height) { 279 SkBitmap bitmap; 280 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height)); 281 return bitmap; 282 } 283 284 AnalysisCanvas::AnalysisCanvas(int width, int height) 285 : INHERITED(MakeEmptyBitmap(width, height)), 286 saved_stack_size_(0), 287 force_not_solid_stack_level_(kNoLayer), 288 force_not_transparent_stack_level_(kNoLayer), 289 is_forced_not_solid_(false), 290 is_forced_not_transparent_(false), 291 is_solid_color_(true), 292 is_transparent_(true), 293 has_text_(false) {} 294 295 AnalysisCanvas::~AnalysisCanvas() {} 296 297 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const { 298 if (is_transparent_) { 299 *color = SK_ColorTRANSPARENT; 300 return true; 301 } 302 if (is_solid_color_) { 303 *color = color_; 304 return true; 305 } 306 return false; 307 } 308 309 bool AnalysisCanvas::HasText() const { return has_text_; } 310 311 bool AnalysisCanvas::abortDrawing() { 312 // Early out as soon as we have detected that the tile has text. 313 return HasText(); 314 } 315 316 void AnalysisCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, 317 ClipEdgeStyle edge_style) { 318 319 INHERITED::onClipRect(rect, op, edge_style); 320 } 321 322 void AnalysisCanvas::onClipPath(const SkPath& path, SkRegion::Op op, 323 ClipEdgeStyle edge_style) { 324 // clipPaths can make our calls to IsFullQuad invalid (ie have false 325 // positives). As a precaution, force the setting to be non-solid 326 // and non-transparent until we pop this 327 if (force_not_solid_stack_level_ == kNoLayer) { 328 force_not_solid_stack_level_ = saved_stack_size_; 329 SetForceNotSolid(true); 330 } 331 if (force_not_transparent_stack_level_ == kNoLayer) { 332 force_not_transparent_stack_level_ = saved_stack_size_; 333 SetForceNotTransparent(true); 334 } 335 336 INHERITED::onClipRect(path.getBounds(), op, edge_style); 337 } 338 339 void AnalysisCanvas::onClipRRect(const SkRRect& rrect, 340 SkRegion::Op op, 341 ClipEdgeStyle edge_style) { 342 // clipRRect can make our calls to IsFullQuad invalid (ie have false 343 // positives). As a precaution, force the setting to be non-solid 344 // and non-transparent until we pop this 345 if (force_not_solid_stack_level_ == kNoLayer) { 346 force_not_solid_stack_level_ = saved_stack_size_; 347 SetForceNotSolid(true); 348 } 349 if (force_not_transparent_stack_level_ == kNoLayer) { 350 force_not_transparent_stack_level_ = saved_stack_size_; 351 SetForceNotTransparent(true); 352 } 353 354 INHERITED::onClipRect(rrect.getBounds(), op, edge_style); 355 } 356 357 void AnalysisCanvas::willSave() { 358 ++saved_stack_size_; 359 INHERITED::willSave(); 360 } 361 362 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer( 363 const SkRect* bounds, 364 const SkPaint* paint, 365 SkCanvas::SaveFlags flags) { 366 367 ++saved_stack_size_; 368 369 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize()); 370 SkRect canvas_bounds; 371 canvas_bounds.set(canvas_ibounds); 372 373 // If after we draw to the saved layer, we have to blend with the current 374 // layer, then we can conservatively say that the canvas will not be of 375 // solid color. 376 if ((paint && !IsSolidColorPaint(*paint)) || 377 (bounds && !bounds->contains(canvas_bounds))) { 378 if (force_not_solid_stack_level_ == kNoLayer) { 379 force_not_solid_stack_level_ = saved_stack_size_; 380 SetForceNotSolid(true); 381 } 382 } 383 384 // If after we draw to the save layer, we have to blend with the current 385 // layer using any part of the current layer's alpha, then we can 386 // conservatively say that the canvas will not be transparent. 387 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode; 388 if (paint) 389 SkXfermode::AsMode(paint->getXfermode(), &xfermode); 390 if (xfermode != SkXfermode::kSrc_Mode) { 391 if (force_not_transparent_stack_level_ == kNoLayer) { 392 force_not_transparent_stack_level_ = saved_stack_size_; 393 SetForceNotTransparent(true); 394 } 395 } 396 397 INHERITED::willSaveLayer(bounds, paint, flags); 398 // Actually saving a layer here could cause a new bitmap to be created 399 // and real rendering to occur. 400 return kNoLayer_SaveLayerStrategy; 401 } 402 403 void AnalysisCanvas::willRestore() { 404 DCHECK(saved_stack_size_); 405 if (saved_stack_size_) { 406 --saved_stack_size_; 407 if (saved_stack_size_ < force_not_solid_stack_level_) { 408 SetForceNotSolid(false); 409 force_not_solid_stack_level_ = kNoLayer; 410 } 411 if (saved_stack_size_ < force_not_transparent_stack_level_) { 412 SetForceNotTransparent(false); 413 force_not_transparent_stack_level_ = kNoLayer; 414 } 415 } 416 417 INHERITED::willRestore(); 418 } 419 420 } // namespace skia 421 422 423