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/SkDevice.h" 9 #include "third_party/skia/include/core/SkDraw.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 #include "ui/gfx/rect_conversions.h" 14 15 namespace { 16 17 const int kNoLayer = -1; 18 19 bool IsSolidColorPaint(const SkPaint& paint) { 20 SkXfermode::Mode xfermode; 21 22 // getXfermode can return a NULL, but that is handled 23 // gracefully by AsMode (NULL turns into kSrcOver mode). 24 SkXfermode::AsMode(paint.getXfermode(), &xfermode); 25 26 // Paint is solid color if the following holds: 27 // - Alpha is 1.0, style is fill, and there are no special effects 28 // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent 29 // to kSrc if source alpha is 1.0, which is already checked). 30 return (paint.getAlpha() == 255 && 31 !paint.getShader() && 32 !paint.getLooper() && 33 !paint.getMaskFilter() && 34 !paint.getColorFilter() && 35 paint.getStyle() == SkPaint::kFill_Style && 36 (xfermode == SkXfermode::kSrc_Mode || 37 xfermode == SkXfermode::kSrcOver_Mode)); 38 } 39 40 bool IsFullQuad(const SkDraw& draw, 41 const SkRect& canvas_rect, 42 const SkRect& drawn_rect) { 43 44 // If the transform results in a non-axis aligned 45 // rect, then be conservative and return false. 46 if (!draw.fMatrix->rectStaysRect()) 47 return false; 48 49 SkRect draw_bitmap_rect; 50 draw.fBitmap->getBounds(&draw_bitmap_rect); 51 SkRect clip_rect = SkRect::Make(draw.fRC->getBounds()); 52 SkRect device_rect; 53 draw.fMatrix->mapRect(&device_rect, drawn_rect); 54 55 // The drawn rect covers the full canvas, if the following conditions hold: 56 // - Clip rect is an actual rectangle. 57 // - The rect we're drawing (post-transform) contains the clip rect. 58 // That is, all of clip rect will be colored by the rect. 59 // - Clip rect contains the canvas rect. 60 // That is, we're not clipping to a portion of this canvas. 61 // - The bitmap into which the draw call happens is at least as 62 // big as the canvas rect 63 return draw.fRC->isRect() && 64 device_rect.contains(clip_rect) && 65 clip_rect.contains(canvas_rect) && 66 draw_bitmap_rect.contains(canvas_rect); 67 } 68 69 } // namespace 70 71 namespace skia { 72 73 AnalysisDevice::AnalysisDevice(const SkBitmap& bitmap) 74 : INHERITED(bitmap), 75 is_forced_not_solid_(false), 76 is_forced_not_transparent_(false), 77 is_solid_color_(true), 78 is_transparent_(true), 79 has_text_(false) {} 80 81 AnalysisDevice::~AnalysisDevice() {} 82 83 bool AnalysisDevice::GetColorIfSolid(SkColor* color) const { 84 if (is_transparent_) { 85 *color = SK_ColorTRANSPARENT; 86 return true; 87 } 88 if (is_solid_color_) { 89 *color = color_; 90 return true; 91 } 92 return false; 93 } 94 95 bool AnalysisDevice::HasText() const { 96 return has_text_; 97 } 98 99 void AnalysisDevice::SetForceNotSolid(bool flag) { 100 is_forced_not_solid_ = flag; 101 if (is_forced_not_solid_) 102 is_solid_color_ = false; 103 } 104 105 void AnalysisDevice::SetForceNotTransparent(bool flag) { 106 is_forced_not_transparent_ = flag; 107 if (is_forced_not_transparent_) 108 is_transparent_ = false; 109 } 110 111 void AnalysisDevice::clear(SkColor color) { 112 is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0); 113 has_text_ = false; 114 115 if (!is_forced_not_solid_ && SkColorGetA(color) == 255) { 116 is_solid_color_ = true; 117 color_ = color; 118 } else { 119 is_solid_color_ = false; 120 } 121 } 122 123 void AnalysisDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { 124 is_solid_color_ = false; 125 is_transparent_ = false; 126 } 127 128 void AnalysisDevice::drawPoints(const SkDraw& draw, 129 SkCanvas::PointMode mode, 130 size_t count, 131 const SkPoint points[], 132 const SkPaint& paint) { 133 is_solid_color_ = false; 134 is_transparent_ = false; 135 } 136 137 void AnalysisDevice::drawRect(const SkDraw& draw, 138 const SkRect& rect, 139 const SkPaint& paint) { 140 bool does_cover_canvas = 141 IsFullQuad(draw, SkRect::MakeWH(width(), height()), rect); 142 143 SkXfermode::Mode xfermode; 144 SkXfermode::AsMode(paint.getXfermode(), &xfermode); 145 146 // This canvas will become transparent if the following holds: 147 // - The quad is a full tile quad 148 // - We're not in "forced not transparent" mode 149 // - Transfer mode is clear (0 color, 0 alpha) 150 // 151 // If the paint alpha is not 0, or if the transfrer mode is 152 // not src, then this canvas will not be transparent. 153 // 154 // In all other cases, we keep the current transparent value 155 if (does_cover_canvas && 156 !is_forced_not_transparent_ && 157 xfermode == SkXfermode::kClear_Mode) { 158 is_transparent_ = true; 159 has_text_ = false; 160 } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) { 161 is_transparent_ = false; 162 } 163 164 // This bitmap is solid if and only if the following holds. 165 // Note that this might be overly conservative: 166 // - We're not in "forced not solid" mode 167 // - Paint is solid color 168 // - The quad is a full tile quad 169 if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) { 170 is_solid_color_ = true; 171 color_ = paint.getColor(); 172 has_text_ = false; 173 } else { 174 is_solid_color_ = false; 175 } 176 } 177 178 void AnalysisDevice::drawOval(const SkDraw& draw, 179 const SkRect& oval, 180 const SkPaint& paint) { 181 is_solid_color_ = false; 182 is_transparent_ = false; 183 } 184 185 void AnalysisDevice::drawRRect(const SkDraw& draw, 186 const SkRRect& rr, 187 const SkPaint& paint) { 188 // This should add the SkRRect to an SkPath, and call 189 // drawPath, but since drawPath ignores the SkPath, just 190 // do the same work here. 191 is_solid_color_ = false; 192 is_transparent_ = false; 193 } 194 195 void AnalysisDevice::drawPath(const SkDraw& draw, 196 const SkPath& path, 197 const SkPaint& paint, 198 const SkMatrix* pre_path_matrix, 199 bool path_is_mutable) { 200 is_solid_color_ = false; 201 is_transparent_ = false; 202 } 203 204 void AnalysisDevice::drawBitmap(const SkDraw& draw, 205 const SkBitmap& bitmap, 206 const SkMatrix& matrix, 207 const SkPaint& paint) { 208 is_solid_color_ = false; 209 is_transparent_ = false; 210 } 211 212 void AnalysisDevice::drawSprite(const SkDraw& draw, 213 const SkBitmap& bitmap, 214 int x, 215 int y, 216 const SkPaint& paint) { 217 is_solid_color_ = false; 218 is_transparent_ = false; 219 } 220 221 void AnalysisDevice::drawBitmapRect(const SkDraw& draw, 222 const SkBitmap& bitmap, 223 const SkRect* src_or_null, 224 const SkRect& dst, 225 const SkPaint& paint, 226 SkCanvas::DrawBitmapRectFlags flags) { 227 // Call drawRect to determine transparency, 228 // but reset solid color to false. 229 drawRect(draw, dst, paint); 230 is_solid_color_ = false; 231 } 232 233 void AnalysisDevice::drawText(const SkDraw& draw, 234 const void* text, 235 size_t len, 236 SkScalar x, 237 SkScalar y, 238 const SkPaint& paint) { 239 is_solid_color_ = false; 240 is_transparent_ = false; 241 has_text_ = true; 242 } 243 244 void AnalysisDevice::drawPosText(const SkDraw& draw, 245 const void* text, 246 size_t len, 247 const SkScalar pos[], 248 SkScalar const_y, 249 int scalars_per_pos, 250 const SkPaint& paint) { 251 is_solid_color_ = false; 252 is_transparent_ = false; 253 has_text_ = true; 254 } 255 256 void AnalysisDevice::drawTextOnPath(const SkDraw& draw, 257 const void* text, 258 size_t len, 259 const SkPath& path, 260 const SkMatrix* matrix, 261 const SkPaint& paint) { 262 is_solid_color_ = false; 263 is_transparent_ = false; 264 has_text_ = true; 265 } 266 267 #ifdef SK_BUILD_FOR_ANDROID 268 void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw, 269 const void* text, 270 size_t len, 271 const SkPoint pos[], 272 const SkPaint& paint, 273 const SkPath& path, 274 const SkMatrix* matrix) { 275 is_solid_color_ = false; 276 is_transparent_ = false; 277 has_text_ = true; 278 } 279 #endif 280 281 void AnalysisDevice::drawVertices(const SkDraw& draw, 282 SkCanvas::VertexMode, 283 int vertex_count, 284 const SkPoint verts[], 285 const SkPoint texs[], 286 const SkColor colors[], 287 SkXfermode* xmode, 288 const uint16_t indices[], 289 int index_count, 290 const SkPaint& paint) { 291 is_solid_color_ = false; 292 is_transparent_ = false; 293 } 294 295 void AnalysisDevice::drawDevice(const SkDraw& draw, 296 SkBaseDevice* device, 297 int x, 298 int y, 299 const SkPaint& paint) { 300 is_solid_color_ = false; 301 is_transparent_ = false; 302 } 303 304 AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device) 305 : INHERITED(device), 306 saved_stack_size_(0), 307 force_not_solid_stack_level_(kNoLayer), 308 force_not_transparent_stack_level_(kNoLayer) {} 309 310 AnalysisCanvas::~AnalysisCanvas() {} 311 312 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const { 313 return (static_cast<AnalysisDevice*>(getDevice()))->GetColorIfSolid(color); 314 } 315 316 bool AnalysisCanvas::HasText() const { 317 return (static_cast<AnalysisDevice*>(getDevice()))->HasText(); 318 } 319 320 bool AnalysisCanvas::abortDrawing() { 321 // Early out as soon as we have detected that the tile has text. 322 return HasText(); 323 } 324 325 bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool do_aa) { 326 return INHERITED::clipRect(rect, op, do_aa); 327 } 328 329 bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool do_aa) { 330 // clipPaths can make our calls to IsFullQuad invalid (ie have false 331 // positives). As a precaution, force the setting to be non-solid 332 // and non-transparent until we pop this 333 if (force_not_solid_stack_level_ == kNoLayer) { 334 force_not_solid_stack_level_ = saved_stack_size_; 335 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true); 336 } 337 if (force_not_transparent_stack_level_ == kNoLayer) { 338 force_not_transparent_stack_level_ = saved_stack_size_; 339 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true); 340 } 341 342 return INHERITED::clipRect(path.getBounds(), op, do_aa); 343 } 344 345 bool AnalysisCanvas::clipRRect(const SkRRect& rrect, 346 SkRegion::Op op, 347 bool do_aa) { 348 // clipRRect can make our calls to IsFullQuad invalid (ie have false 349 // positives). As a precaution, force the setting to be non-solid 350 // and non-transparent until we pop this 351 if (force_not_solid_stack_level_ == kNoLayer) { 352 force_not_solid_stack_level_ = saved_stack_size_; 353 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true); 354 } 355 if (force_not_transparent_stack_level_ == kNoLayer) { 356 force_not_transparent_stack_level_ = saved_stack_size_; 357 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true); 358 } 359 360 return INHERITED::clipRect(rrect.getBounds(), op, do_aa); 361 } 362 363 int AnalysisCanvas::save(SkCanvas::SaveFlags flags) { 364 ++saved_stack_size_; 365 return INHERITED::save(flags); 366 } 367 368 int AnalysisCanvas::saveLayer(const SkRect* bounds, 369 const SkPaint* paint, 370 SkCanvas::SaveFlags flags) { 371 ++saved_stack_size_; 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(SkRect::MakeWH(getDevice()->width(), 378 getDevice()->height())))) { 379 if (force_not_solid_stack_level_ == kNoLayer) { 380 force_not_solid_stack_level_ = saved_stack_size_; 381 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true); 382 } 383 } 384 385 // If after we draw to the save layer, we have to blend with the current 386 // layer using any part of the current layer's alpha, then we can 387 // conservatively say that the canvas will not be transparent. 388 SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode; 389 if (paint) 390 SkXfermode::AsMode(paint->getXfermode(), &xfermode); 391 if (xfermode != SkXfermode::kSrc_Mode) { 392 if (force_not_transparent_stack_level_ == kNoLayer) { 393 force_not_transparent_stack_level_ = saved_stack_size_; 394 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true); 395 } 396 } 397 398 // Actually saving a layer here could cause a new bitmap to be created 399 // and real rendering to occur. 400 int count = INHERITED::save(flags); 401 if (bounds) { 402 INHERITED::clipRectBounds(bounds, flags, NULL); 403 } 404 return count; 405 } 406 407 void AnalysisCanvas::restore() { 408 INHERITED::restore(); 409 410 DCHECK(saved_stack_size_); 411 if (saved_stack_size_) { 412 --saved_stack_size_; 413 if (saved_stack_size_ < force_not_solid_stack_level_) { 414 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(false); 415 force_not_solid_stack_level_ = kNoLayer; 416 } 417 if (saved_stack_size_ < force_not_transparent_stack_level_) { 418 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent( 419 false); 420 force_not_transparent_stack_level_ = kNoLayer; 421 } 422 } 423 } 424 425 } // namespace skia 426 427 428