| OLD | NEW |
| (Empty) |
| 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/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/SkPath.h" | |
| 10 #include "third_party/skia/include/core/SkRRect.h" | |
| 11 #include "third_party/skia/include/core/SkShader.h" | |
| 12 #include "third_party/skia/src/core/SkRasterClip.h" | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 const int kNoLayer = -1; | |
| 17 | |
| 18 bool ActsLikeClear(SkXfermode::Mode mode, unsigned src_alpha) { | |
| 19 switch (mode) { | |
| 20 case SkXfermode::kClear_Mode: | |
| 21 return true; | |
| 22 case SkXfermode::kSrc_Mode: | |
| 23 case SkXfermode::kSrcIn_Mode: | |
| 24 case SkXfermode::kDstIn_Mode: | |
| 25 case SkXfermode::kSrcOut_Mode: | |
| 26 case SkXfermode::kDstATop_Mode: | |
| 27 return src_alpha == 0; | |
| 28 case SkXfermode::kDstOut_Mode: | |
| 29 return src_alpha == 0xFF; | |
| 30 default: | |
| 31 return false; | |
| 32 } | |
| 33 } | |
| 34 | |
| 35 bool IsSolidColorPaint(const SkPaint& paint) { | |
| 36 SkXfermode::Mode xfermode; | |
| 37 | |
| 38 // getXfermode can return a NULL, but that is handled | |
| 39 // gracefully by AsMode (NULL turns into kSrcOver mode). | |
| 40 SkXfermode::AsMode(paint.getXfermode(), &xfermode); | |
| 41 | |
| 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)); | |
| 55 } | |
| 56 | |
| 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; | |
| 62 | |
| 63 SkIRect clip_irect; | |
| 64 canvas->getClipDeviceBounds(&clip_irect); | |
| 65 // if the clip is smaller than the canvas, we're partly clipped, so abort. | |
| 66 if (!clip_irect.contains(SkIRect::MakeSize(canvas->getDeviceSize()))) | |
| 67 return false; | |
| 68 | |
| 69 const SkMatrix& matrix = canvas->getTotalMatrix(); | |
| 70 // If the transform results in a non-axis aligned | |
| 71 // rect, then be conservative and return false. | |
| 72 if (!matrix.rectStaysRect()) | |
| 73 return false; | |
| 74 | |
| 75 SkRect device_rect; | |
| 76 matrix.mapRect(&device_rect, drawn_rect); | |
| 77 SkRect clip_rect; | |
| 78 clip_rect.set(clip_irect); | |
| 79 return device_rect.contains(clip_rect); | |
| 80 } | |
| 81 | |
| 82 } // namespace | |
| 83 | |
| 84 namespace skia { | |
| 85 | |
| 86 void AnalysisCanvas::SetForceNotSolid(bool flag) { | |
| 87 is_forced_not_solid_ = flag; | |
| 88 if (is_forced_not_solid_) | |
| 89 is_solid_color_ = false; | |
| 90 } | |
| 91 | |
| 92 void AnalysisCanvas::SetForceNotTransparent(bool flag) { | |
| 93 is_forced_not_transparent_ = flag; | |
| 94 if (is_forced_not_transparent_) | |
| 95 is_transparent_ = false; | |
| 96 } | |
| 97 | |
| 98 void AnalysisCanvas::onDrawPaint(const SkPaint& paint) { | |
| 99 SkRect rect; | |
| 100 getClipBounds(&rect); | |
| 101 drawRect(rect, paint); | |
| 102 } | |
| 103 | |
| 104 void AnalysisCanvas::onDrawPoints(SkCanvas::PointMode mode, | |
| 105 size_t count, | |
| 106 const SkPoint points[], | |
| 107 const SkPaint& paint) { | |
| 108 is_solid_color_ = false; | |
| 109 is_transparent_ = false; | |
| 110 ++draw_op_count_; | |
| 111 } | |
| 112 | |
| 113 void AnalysisCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { | |
| 114 // This recreates the early-exit logic in SkCanvas.cpp. | |
| 115 SkRect scratch; | |
| 116 if (paint.canComputeFastBounds() && | |
| 117 quickReject(paint.computeFastBounds(rect, &scratch))) { | |
| 118 return; | |
| 119 } | |
| 120 | |
| 121 // An extra no-op check SkCanvas.cpp doesn't do. | |
| 122 if (paint.nothingToDraw()) | |
| 123 return; | |
| 124 | |
| 125 bool does_cover_canvas = IsFullQuad(this, rect); | |
| 126 | |
| 127 SkXfermode::Mode xfermode; | |
| 128 SkXfermode::AsMode(paint.getXfermode(), &xfermode); | |
| 129 | |
| 130 // This canvas will become transparent if the following holds: | |
| 131 // - The quad is a full tile quad | |
| 132 // - We're not in "forced not transparent" mode | |
| 133 // - Transfer mode is clear (0 color, 0 alpha) | |
| 134 // | |
| 135 // If the paint alpha is not 0, or if the transfrer mode is | |
| 136 // not src, then this canvas will not be transparent. | |
| 137 // | |
| 138 // In all other cases, we keep the current transparent value | |
| 139 if (does_cover_canvas && | |
| 140 !is_forced_not_transparent_ && | |
| 141 ActsLikeClear(xfermode, paint.getAlpha())) { | |
| 142 is_transparent_ = true; | |
| 143 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) { | |
| 144 is_transparent_ = false; | |
| 145 } | |
| 146 | |
| 147 // This bitmap is solid if and only if the following holds. | |
| 148 // Note that this might be overly conservative: | |
| 149 // - We're not in "forced not solid" mode | |
| 150 // - Paint is solid color | |
| 151 // - The quad is a full tile quad | |
| 152 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) { | |
| 153 is_solid_color_ = true; | |
| 154 color_ = paint.getColor(); | |
| 155 } else { | |
| 156 is_solid_color_ = false; | |
| 157 } | |
| 158 ++draw_op_count_; | |
| 159 } | |
| 160 | |
| 161 void AnalysisCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { | |
| 162 is_solid_color_ = false; | |
| 163 is_transparent_ = false; | |
| 164 ++draw_op_count_; | |
| 165 } | |
| 166 | |
| 167 void AnalysisCanvas::onDrawRRect(const SkRRect& rr, const SkPaint& paint) { | |
| 168 // This should add the SkRRect to an SkPath, and call | |
| 169 // drawPath, but since drawPath ignores the SkPath, just | |
| 170 // do the same work here. | |
| 171 is_solid_color_ = false; | |
| 172 is_transparent_ = false; | |
| 173 ++draw_op_count_; | |
| 174 } | |
| 175 | |
| 176 void AnalysisCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { | |
| 177 is_solid_color_ = false; | |
| 178 is_transparent_ = false; | |
| 179 ++draw_op_count_; | |
| 180 } | |
| 181 | |
| 182 void AnalysisCanvas::onDrawBitmap(const SkBitmap& bitmap, | |
| 183 SkScalar left, | |
| 184 SkScalar top, | |
| 185 const SkPaint*) { | |
| 186 is_solid_color_ = false; | |
| 187 is_transparent_ = false; | |
| 188 ++draw_op_count_; | |
| 189 } | |
| 190 | |
| 191 void AnalysisCanvas::onDrawBitmapRect(const SkBitmap&, | |
| 192 const SkRect* src, | |
| 193 const SkRect& dst, | |
| 194 const SkPaint* paint, | |
| 195 SrcRectConstraint flags) { | |
| 196 // Call drawRect to determine transparency, | |
| 197 // but reset solid color to false. | |
| 198 SkPaint tmpPaint; | |
| 199 if (!paint) | |
| 200 paint = &tmpPaint; | |
| 201 drawRect(dst, *paint); | |
| 202 is_solid_color_ = false; | |
| 203 ++draw_op_count_; | |
| 204 } | |
| 205 | |
| 206 void AnalysisCanvas::onDrawBitmapNine(const SkBitmap& bitmap, | |
| 207 const SkIRect& center, | |
| 208 const SkRect& dst, | |
| 209 const SkPaint* paint) { | |
| 210 is_solid_color_ = false; | |
| 211 is_transparent_ = false; | |
| 212 ++draw_op_count_; | |
| 213 } | |
| 214 | |
| 215 void AnalysisCanvas::onDrawSprite(const SkBitmap& bitmap, | |
| 216 int left, | |
| 217 int top, | |
| 218 const SkPaint* paint) { | |
| 219 is_solid_color_ = false; | |
| 220 is_transparent_ = false; | |
| 221 ++draw_op_count_; | |
| 222 } | |
| 223 | |
| 224 void AnalysisCanvas::onDrawText(const void* text, | |
| 225 size_t len, | |
| 226 SkScalar x, | |
| 227 SkScalar y, | |
| 228 const SkPaint& paint) { | |
| 229 is_solid_color_ = false; | |
| 230 is_transparent_ = false; | |
| 231 ++draw_op_count_; | |
| 232 } | |
| 233 | |
| 234 void AnalysisCanvas::onDrawPosText(const void* text, | |
| 235 size_t byteLength, | |
| 236 const SkPoint pos[], | |
| 237 const SkPaint& paint) { | |
| 238 is_solid_color_ = false; | |
| 239 is_transparent_ = false; | |
| 240 ++draw_op_count_; | |
| 241 } | |
| 242 | |
| 243 void AnalysisCanvas::onDrawPosTextH(const void* text, | |
| 244 size_t byteLength, | |
| 245 const SkScalar xpos[], | |
| 246 SkScalar constY, | |
| 247 const SkPaint& paint) { | |
| 248 is_solid_color_ = false; | |
| 249 is_transparent_ = false; | |
| 250 ++draw_op_count_; | |
| 251 } | |
| 252 | |
| 253 void AnalysisCanvas::onDrawTextOnPath(const void* text, | |
| 254 size_t len, | |
| 255 const SkPath& path, | |
| 256 const SkMatrix* matrix, | |
| 257 const SkPaint& paint) { | |
| 258 is_solid_color_ = false; | |
| 259 is_transparent_ = false; | |
| 260 ++draw_op_count_; | |
| 261 } | |
| 262 | |
| 263 void AnalysisCanvas::onDrawTextBlob(const SkTextBlob* blob, | |
| 264 SkScalar x, | |
| 265 SkScalar y, | |
| 266 const SkPaint &paint) { | |
| 267 is_solid_color_ = false; | |
| 268 is_transparent_ = false; | |
| 269 ++draw_op_count_; | |
| 270 } | |
| 271 | |
| 272 void AnalysisCanvas::onDrawDRRect(const SkRRect& outer, | |
| 273 const SkRRect& inner, | |
| 274 const SkPaint& paint) { | |
| 275 is_solid_color_ = false; | |
| 276 is_transparent_ = false; | |
| 277 ++draw_op_count_; | |
| 278 } | |
| 279 | |
| 280 void AnalysisCanvas::onDrawVertices(SkCanvas::VertexMode, | |
| 281 int vertex_count, | |
| 282 const SkPoint verts[], | |
| 283 const SkPoint texs[], | |
| 284 const SkColor colors[], | |
| 285 SkXfermode* xmode, | |
| 286 const uint16_t indices[], | |
| 287 int index_count, | |
| 288 const SkPaint& paint) { | |
| 289 is_solid_color_ = false; | |
| 290 is_transparent_ = false; | |
| 291 ++draw_op_count_; | |
| 292 } | |
| 293 | |
| 294 // Needed for now, since SkCanvas requires a bitmap, even if it is not backed | |
| 295 // by any pixels | |
| 296 static SkBitmap MakeEmptyBitmap(int width, int height) { | |
| 297 SkBitmap bitmap; | |
| 298 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height)); | |
| 299 return bitmap; | |
| 300 } | |
| 301 | |
| 302 AnalysisCanvas::AnalysisCanvas(int width, int height) | |
| 303 : INHERITED(MakeEmptyBitmap(width, height)), | |
| 304 saved_stack_size_(0), | |
| 305 force_not_solid_stack_level_(kNoLayer), | |
| 306 force_not_transparent_stack_level_(kNoLayer), | |
| 307 is_forced_not_solid_(false), | |
| 308 is_forced_not_transparent_(false), | |
| 309 is_solid_color_(true), | |
| 310 color_(SK_ColorTRANSPARENT), | |
| 311 is_transparent_(true), | |
| 312 draw_op_count_(0) { | |
| 313 } | |
| 314 | |
| 315 AnalysisCanvas::~AnalysisCanvas() {} | |
| 316 | |
| 317 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const { | |
| 318 if (is_transparent_) { | |
| 319 *color = SK_ColorTRANSPARENT; | |
| 320 return true; | |
| 321 } | |
| 322 if (is_solid_color_) { | |
| 323 *color = color_; | |
| 324 return true; | |
| 325 } | |
| 326 return false; | |
| 327 } | |
| 328 | |
| 329 bool AnalysisCanvas::abort() { | |
| 330 // Early out as soon as we have more than one draw op. | |
| 331 // TODO(vmpstr): Investigate if 1 is the correct metric here. We need to | |
| 332 // balance the amount of time we spend analyzing vs how many tiles would be | |
| 333 // solid if the number was higher. | |
| 334 if (draw_op_count_ > 1) { | |
| 335 // We have to reset solid/transparent state to false since we don't | |
| 336 // know whether consequent operations will make this false. | |
| 337 is_solid_color_ = false; | |
| 338 is_transparent_ = false; | |
| 339 return true; | |
| 340 } | |
| 341 return false; | |
| 342 } | |
| 343 | |
| 344 void AnalysisCanvas::OnComplexClip() { | |
| 345 // complex clips can make our calls to IsFullQuad invalid (ie have false | |
| 346 // positives). As a precaution, force the setting to be non-solid | |
| 347 // and non-transparent until we pop this | |
| 348 if (force_not_solid_stack_level_ == kNoLayer) { | |
| 349 force_not_solid_stack_level_ = saved_stack_size_; | |
| 350 SetForceNotSolid(true); | |
| 351 } | |
| 352 if (force_not_transparent_stack_level_ == kNoLayer) { | |
| 353 force_not_transparent_stack_level_ = saved_stack_size_; | |
| 354 SetForceNotTransparent(true); | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 void AnalysisCanvas::onClipRect(const SkRect& rect, | |
| 359 SkRegion::Op op, | |
| 360 ClipEdgeStyle edge_style) { | |
| 361 INHERITED::onClipRect(rect, op, edge_style); | |
| 362 } | |
| 363 | |
| 364 void AnalysisCanvas::onClipPath(const SkPath& path, | |
| 365 SkRegion::Op op, | |
| 366 ClipEdgeStyle edge_style) { | |
| 367 OnComplexClip(); | |
| 368 INHERITED::onClipRect(path.getBounds(), op, edge_style); | |
| 369 } | |
| 370 | |
| 371 void AnalysisCanvas::onClipRRect(const SkRRect& rrect, | |
| 372 SkRegion::Op op, | |
| 373 ClipEdgeStyle edge_style) { | |
| 374 OnComplexClip(); | |
| 375 INHERITED::onClipRect(rrect.getBounds(), op, edge_style); | |
| 376 } | |
| 377 | |
| 378 void AnalysisCanvas::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) { | |
| 379 const ClipEdgeStyle edge_style = kHard_ClipEdgeStyle; | |
| 380 if (deviceRgn.isRect()) { | |
| 381 onClipRect(SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style); | |
| 382 return; | |
| 383 } | |
| 384 OnComplexClip(); | |
| 385 INHERITED::onClipRect( | |
| 386 SkRect::MakeFromIRect(deviceRgn.getBounds()), op, edge_style); | |
| 387 } | |
| 388 | |
| 389 void AnalysisCanvas::willSave() { | |
| 390 ++saved_stack_size_; | |
| 391 INHERITED::willSave(); | |
| 392 } | |
| 393 | |
| 394 SkCanvas::SaveLayerStrategy AnalysisCanvas::willSaveLayer( | |
| 395 const SkRect* bounds, | |
| 396 const SkPaint* paint, | |
| 397 SkCanvas::SaveFlags flags) { | |
| 398 | |
| 399 ++saved_stack_size_; | |
| 400 | |
| 401 SkIRect canvas_ibounds = SkIRect::MakeSize(this->getDeviceSize()); | |
| 402 SkRect canvas_bounds; | |
| 403 canvas_bounds.set(canvas_ibounds); | |
| 404 | |
| 405 // If after we draw to the saved layer, we have to blend with the current | |
| 406 // layer, then we can conservatively say that the canvas will not be of | |
| 407 // solid color. | |
| 408 if ((paint && !IsSolidColorPaint(*paint)) || | |
| 409 (bounds && !bounds->contains(canvas_bounds))) { | |
| 410 if (force_not_solid_stack_level_ == kNoLayer) { | |
| 411 force_not_solid_stack_level_ = saved_stack_size_; | |
| 412 SetForceNotSolid(true); | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 // If after we draw to the save layer, we have to blend with the current | |
| 417 // layer using any part of the current layer's alpha, then we can | |
| 418 // conservatively say that the canvas will not be transparent. | |
| 419 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode; | |
| 420 if (paint) | |
| 421 SkXfermode::AsMode(paint->getXfermode(), &xfermode); | |
| 422 if (xfermode != SkXfermode::kDst_Mode) { | |
| 423 if (force_not_transparent_stack_level_ == kNoLayer) { | |
| 424 force_not_transparent_stack_level_ = saved_stack_size_; | |
| 425 SetForceNotTransparent(true); | |
| 426 } | |
| 427 } | |
| 428 | |
| 429 INHERITED::willSaveLayer(bounds, paint, flags); | |
| 430 // Actually saving a layer here could cause a new bitmap to be created | |
| 431 // and real rendering to occur. | |
| 432 return kNoLayer_SaveLayerStrategy; | |
| 433 } | |
| 434 | |
| 435 void AnalysisCanvas::willRestore() { | |
| 436 DCHECK(saved_stack_size_); | |
| 437 if (saved_stack_size_) { | |
| 438 --saved_stack_size_; | |
| 439 if (saved_stack_size_ < force_not_solid_stack_level_) { | |
| 440 SetForceNotSolid(false); | |
| 441 force_not_solid_stack_level_ = kNoLayer; | |
| 442 } | |
| 443 if (saved_stack_size_ < force_not_transparent_stack_level_) { | |
| 444 SetForceNotTransparent(false); | |
| 445 force_not_transparent_stack_level_ = kNoLayer; | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 INHERITED::willRestore(); | |
| 450 } | |
| 451 | |
| 452 } // namespace skia | |
| 453 | |
| 454 | |
| OLD | NEW |