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 // Call drawRect to determine transparency, 227 // but reset solid color to false. 228 drawRect(draw, dst, paint); 229 is_solid_color_ = false; 230 } 231 232 void AnalysisDevice::drawText(const SkDraw& draw, 233 const void* text, 234 size_t len, 235 SkScalar x, 236 SkScalar y, 237 const SkPaint& paint) { 238 is_solid_color_ = false; 239 is_transparent_ = false; 240 has_text_ = true; 241 } 242 243 void AnalysisDevice::drawPosText(const SkDraw& draw, 244 const void* text, 245 size_t len, 246 const SkScalar pos[], 247 SkScalar const_y, 248 int scalars_per_pos, 249 const SkPaint& paint) { 250 is_solid_color_ = false; 251 is_transparent_ = false; 252 has_text_ = true; 253 } 254 255 void AnalysisDevice::drawTextOnPath(const SkDraw& draw, 256 const void* text, 257 size_t len, 258 const SkPath& path, 259 const SkMatrix* matrix, 260 const SkPaint& paint) { 261 is_solid_color_ = false; 262 is_transparent_ = false; 263 has_text_ = true; 264 } 265 266 #ifdef SK_BUILD_FOR_ANDROID 267 void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw, 268 const void* text, 269 size_t len, 270 const SkPoint pos[], 271 const SkPaint& paint, 272 const SkPath& path, 273 const SkMatrix* matrix) { 274 is_solid_color_ = false; 275 is_transparent_ = false; 276 has_text_ = true; 277 } 278 #endif 279 280 void AnalysisDevice::drawVertices(const SkDraw& draw, 281 SkCanvas::VertexMode, 282 int vertex_count, 283 const SkPoint verts[], 284 const SkPoint texs[], 285 const SkColor colors[], 286 SkXfermode* xmode, 287 const uint16_t indices[], 288 int index_count, 289 const SkPaint& paint) { 290 is_solid_color_ = false; 291 is_transparent_ = false; 292 } 293 294 void AnalysisDevice::drawDevice(const SkDraw& draw, 295 SkDevice* device, 296 int x, 297 int y, 298 const SkPaint& paint) { 299 is_solid_color_ = false; 300 is_transparent_ = false; 301 } 302 303 AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device) 304 : INHERITED(device), 305 saved_stack_size_(0), 306 force_not_solid_stack_level_(kNoLayer), 307 force_not_transparent_stack_level_(kNoLayer) {} 308 309 AnalysisCanvas::~AnalysisCanvas() {} 310 311 bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const { 312 return (static_cast<AnalysisDevice*>(getDevice()))->GetColorIfSolid(color); 313 } 314 315 bool AnalysisCanvas::HasText() const { 316 return (static_cast<AnalysisDevice*>(getDevice()))->HasText(); 317 } 318 319 bool AnalysisCanvas::abortDrawing() { 320 // Early out as soon as we have detected that the tile has text. 321 return HasText(); 322 } 323 324 bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool do_aa) { 325 return INHERITED::clipRect(rect, op, do_aa); 326 } 327 328 bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool do_aa) { 329 // clipPaths can make our calls to IsFullQuad invalid (ie have false 330 // positives). As a precaution, force the setting to be non-solid 331 // and non-transparent until we pop this 332 if (force_not_solid_stack_level_ == kNoLayer) { 333 force_not_solid_stack_level_ = saved_stack_size_; 334 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true); 335 } 336 if (force_not_transparent_stack_level_ == kNoLayer) { 337 force_not_transparent_stack_level_ = saved_stack_size_; 338 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true); 339 } 340 341 return INHERITED::clipRect(path.getBounds(), op, do_aa); 342 } 343 344 bool AnalysisCanvas::clipRRect(const SkRRect& rrect, 345 SkRegion::Op op, 346 bool do_aa) { 347 // clipRRect can make our calls to IsFullQuad invalid (ie have false 348 // positives). As a precaution, force the setting to be non-solid 349 // and non-transparent until we pop this 350 if (force_not_solid_stack_level_ == kNoLayer) { 351 force_not_solid_stack_level_ = saved_stack_size_; 352 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true); 353 } 354 if (force_not_transparent_stack_level_ == kNoLayer) { 355 force_not_transparent_stack_level_ = saved_stack_size_; 356 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true); 357 } 358 359 return INHERITED::clipRect(rrect.getBounds(), op, do_aa); 360 } 361 362 int AnalysisCanvas::save(SkCanvas::SaveFlags flags) { 363 ++saved_stack_size_; 364 return INHERITED::save(flags); 365 } 366 367 int AnalysisCanvas::saveLayer(const SkRect* bounds, 368 const SkPaint* paint, 369 SkCanvas::SaveFlags flags) { 370 ++saved_stack_size_; 371 372 // If after we draw to the saved layer, we have to blend with the current 373 // layer, then we can conservatively say that the canvas will not be of 374 // solid color. 375 if ((paint && !IsSolidColorPaint(*paint)) || 376 (bounds && !bounds->contains(SkRect::MakeWH(getDevice()->width(), 377 getDevice()->height())))) { 378 if (force_not_solid_stack_level_ == kNoLayer) { 379 force_not_solid_stack_level_ = saved_stack_size_; 380 (static_cast<AnalysisDevice*>(getDevice()))->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 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true); 394 } 395 } 396 397 // Actually saving a layer here could cause a new bitmap to be created 398 // and real rendering to occur. 399 int count = INHERITED::save(flags); 400 if (bounds) { 401 INHERITED::clipRectBounds(bounds, flags, NULL); 402 } 403 return count; 404 } 405 406 void AnalysisCanvas::restore() { 407 INHERITED::restore(); 408 409 DCHECK(saved_stack_size_); 410 if (saved_stack_size_) { 411 --saved_stack_size_; 412 if (saved_stack_size_ < force_not_solid_stack_level_) { 413 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(false); 414 force_not_solid_stack_level_ = kNoLayer; 415 } 416 if (saved_stack_size_ < force_not_transparent_stack_level_) { 417 (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent( 418 false); 419 force_not_transparent_stack_level_ = kNoLayer; 420 } 421 } 422 } 423 424 } // namespace skia 425 426 427