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 84 if (!is_forced_not_solid_ && SkColorGetA(color) == 255) { 85 is_solid_color_ = true; 86 color_ = color; 87 } else { 88 is_solid_color_ = false; 89 } 90 } 91 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; 97 98 is_solid_color_ = false; 99 is_transparent_ = false; 100 ++draw_op_count_; 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 ++draw_op_count_; 110 } 111 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; 118 } 119 120 // An extra no-op check SkCanvas.cpp doesn't do. 121 if (paint.nothingToDraw()) 122 return; 123 124 bool does_cover_canvas = IsFullQuad(this, rect); 125 126 SkXfermode::Mode xfermode; 127 SkXfermode::AsMode(paint.getXfermode(), &xfermode); 128 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) 133 // 134 // If the paint alpha is not 0, or if the transfrer mode is 135 // not src, then this canvas will not be transparent. 136 // 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; 144 } 145 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; 156 } 157 ++draw_op_count_; 158 } 159 160 void AnalysisCanvas::drawOval(const SkRect& oval, const SkPaint& paint) { 161 is_solid_color_ = false; 162 is_transparent_ = false; 163 ++draw_op_count_; 164 } 165 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_; 173 } 174 175 void AnalysisCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 176 is_solid_color_ = false; 177 is_transparent_ = false; 178 ++draw_op_count_; 179 } 180 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_; 188 } 189 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_; 203 } 204 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_; 211 } 212 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_; 220 } 221 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_; 229 } 230 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_; 239 } 240 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_; 248 } 249 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_; 258 } 259 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_; 268 } 269 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_; 277 } 278 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_; 285 } 286 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_; 299 } 300 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; 307 } 308 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) { 320 } 321 322 AnalysisCanvas::~AnalysisCanvas() {} 323 324 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const { 325 if (is_transparent_) { 326 *color = SK_ColorTRANSPARENT; 327 return true; 328 } 329 if (is_solid_color_) { 330 *color = color_; 331 return true; 332 } 333 return false; 334 } 335 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; 347 } 348 return false; 349 } 350 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); 358 } 359 if (force_not_transparent_stack_level_ == kNoLayer) { 360 force_not_transparent_stack_level_ = saved_stack_size_; 361 SetForceNotTransparent(true); 362 } 363 } 364 365 void AnalysisCanvas::onClipRect(const SkRect& rect, 366 SkRegion::Op op, 367 ClipEdgeStyle edge_style) { 368 INHERITED::onClipRect(rect, op, edge_style); 369 } 370 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); 376 } 377 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); 383 } 384 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; 390 } 391 OnComplexClip(); 392 INHERITED::onClipRect( 393 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style); 394 } 395 396 void AnalysisCanvas::willSave() { 397 ++saved_stack_size_; 398 INHERITED::willSave(); 399 } 400 401 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer( 402 const SkRect* bounds, 403 const SkPaint* paint, 404 SkCanvas::SaveFlags flags) { 405 406 ++saved_stack_size_; 407 408 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize()); 409 SkRect canvas_bounds; 410 canvas_bounds.set(canvas_ibounds); 411 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); 420 } 421 } 422 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); 433 } 434 } 435 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; 440 } 441 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; 449 } 450 if (saved_stack_size_ < force_not_transparent_stack_level_) { 451 SetForceNotTransparent(false); 452 force_not_transparent_stack_level_ = kNoLayer; 453 } 454 } 455 456 INHERITED::willRestore(); 457 } 458 459 } // namespace skia 460 461 462