1 /* 2 * Copyright (c) 2012, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 33 #include "core/platform/graphics/skia/OpaqueRegionSkia.h" 34 35 #include "core/platform/graphics/GraphicsContext.h" 36 37 #include "SkCanvas.h" 38 #include "SkColorFilter.h" 39 #include "SkShader.h" 40 41 namespace WebCore { 42 43 OpaqueRegionSkia::OpaqueRegionSkia() 44 : m_opaqueRect(SkRect::MakeEmpty()) 45 { 46 } 47 48 OpaqueRegionSkia::~OpaqueRegionSkia() 49 { 50 } 51 52 IntRect OpaqueRegionSkia::asRect() const 53 { 54 // Returns the largest enclosed rect. 55 int left = SkScalarCeil(m_opaqueRect.fLeft); 56 int top = SkScalarCeil(m_opaqueRect.fTop); 57 int right = SkScalarFloor(m_opaqueRect.fRight); 58 int bottom = SkScalarFloor(m_opaqueRect.fBottom); 59 return IntRect(left, top, right-left, bottom-top); 60 } 61 62 // Returns true if the xfermode will force the dst to be opaque, regardless of the current dst. 63 static inline bool xfermodeIsOpaque(const SkPaint& paint, bool srcIsOpaque) 64 { 65 if (!srcIsOpaque) 66 return false; 67 68 SkXfermode* xfermode = paint.getXfermode(); 69 if (!xfermode) 70 return true; // default to kSrcOver_Mode 71 SkXfermode::Mode mode; 72 if (!xfermode->asMode(&mode)) 73 return false; 74 75 switch (mode) { 76 case SkXfermode::kSrc_Mode: // source 77 case SkXfermode::kSrcOver_Mode: // source + dest - source*dest 78 case SkXfermode::kDstOver_Mode: // source + dest - source*dest 79 case SkXfermode::kDstATop_Mode: // source 80 case SkXfermode::kPlus_Mode: // source+dest 81 default: // the rest are all source + dest - source*dest 82 return true; 83 case SkXfermode::kClear_Mode: // 0 84 case SkXfermode::kDst_Mode: // dest 85 case SkXfermode::kSrcIn_Mode: // source * dest 86 case SkXfermode::kDstIn_Mode: // dest * source 87 case SkXfermode::kSrcOut_Mode: // source * (1-dest) 88 case SkXfermode::kDstOut_Mode: // dest * (1-source) 89 case SkXfermode::kSrcATop_Mode: // dest 90 case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest) 91 return false; 92 } 93 } 94 95 // Returns true if the xfermode will keep the dst opaque, assuming the dst is already opaque. 96 static inline bool xfermodePreservesOpaque(const SkPaint& paint, bool srcIsOpaque) 97 { 98 SkXfermode* xfermode = paint.getXfermode(); 99 if (!xfermode) 100 return true; // default to kSrcOver_Mode 101 SkXfermode::Mode mode; 102 if (!xfermode->asMode(&mode)) 103 return false; 104 105 switch (mode) { 106 case SkXfermode::kDst_Mode: // dest 107 case SkXfermode::kSrcOver_Mode: // source + dest - source*dest 108 case SkXfermode::kDstOver_Mode: // source + dest - source*dest 109 case SkXfermode::kSrcATop_Mode: // dest 110 case SkXfermode::kPlus_Mode: // source+dest 111 default: // the rest are all source + dest - source*dest 112 return true; 113 case SkXfermode::kClear_Mode: // 0 114 case SkXfermode::kSrcOut_Mode: // source * (1-dest) 115 case SkXfermode::kDstOut_Mode: // dest * (1-source) 116 case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest) 117 return false; 118 case SkXfermode::kSrc_Mode: // source 119 case SkXfermode::kSrcIn_Mode: // source * dest 120 case SkXfermode::kDstIn_Mode: // dest * source 121 case SkXfermode::kDstATop_Mode: // source 122 return srcIsOpaque; 123 } 124 } 125 126 // Returns true if all pixels painted will be opaque. 127 static inline bool paintIsOpaque(const SkPaint& paint, OpaqueRegionSkia::DrawType drawType, const SkBitmap* bitmap) 128 { 129 if (paint.getAlpha() < 0xFF) 130 return false; 131 bool checkFillOnly = drawType != OpaqueRegionSkia::FillOrStroke; 132 if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias()) 133 return false; 134 SkShader* shader = paint.getShader(); 135 if (shader && !shader->isOpaque()) 136 return false; 137 if (bitmap && !bitmap->isOpaque()) 138 return false; 139 if (paint.getLooper()) 140 return false; 141 if (paint.getImageFilter()) 142 return false; 143 if (paint.getMaskFilter()) 144 return false; 145 SkColorFilter* colorFilter = paint.getColorFilter(); 146 if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag)) 147 return false; 148 return true; 149 } 150 151 // Returns true if there is a rectangular clip, with the result in |deviceClipRect|. 152 static inline bool getDeviceClipAsRect(const GraphicsContext* context, SkRect& deviceClipRect) 153 { 154 // Get the current clip in device coordinate space. 155 if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType) 156 return false; 157 158 SkIRect deviceClipIRect; 159 if (context->canvas()->getClipDeviceBounds(&deviceClipIRect)) 160 deviceClipRect.set(deviceClipIRect); 161 else 162 deviceClipRect.setEmpty(); 163 164 return true; 165 } 166 167 void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint) 168 { 169 CanvasLayerState state; 170 if (paint) 171 state.paint = *paint; 172 m_canvasLayerStack.append(state); 173 } 174 175 void OpaqueRegionSkia::popCanvasLayer(const GraphicsContext* context) 176 { 177 ASSERT(!m_canvasLayerStack.isEmpty()); 178 if (m_canvasLayerStack.isEmpty()) 179 return; 180 181 const CanvasLayerState& canvasLayer = m_canvasLayerStack.last(); 182 SkRect layerOpaqueRect = canvasLayer.opaqueRect; 183 SkPaint layerPaint = canvasLayer.paint; 184 185 // Apply the image mask. 186 if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect)) 187 layerOpaqueRect.setEmpty(); 188 189 m_canvasLayerStack.removeLast(); 190 191 applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint); 192 } 193 194 void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect) 195 { 196 ASSERT(!m_canvasLayerStack.isEmpty()); 197 m_canvasLayerStack.last().hasImageMask = true; 198 m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect; 199 } 200 201 void OpaqueRegionSkia::didDrawRect(const GraphicsContext* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap) 202 { 203 // Any stroking may put alpha in pixels even if the filling part does not. 204 if (paint.getStyle() != SkPaint::kFill_Style) { 205 bool fillsBounds = false; 206 207 if (!paint.canComputeFastBounds()) 208 didDrawUnbounded(context, paint, FillOrStroke); 209 else { 210 SkRect strokeRect; 211 strokeRect = paint.computeFastBounds(fillRect, &strokeRect); 212 didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, FillOrStroke); 213 } 214 } 215 216 bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style; 217 didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly); 218 } 219 220 void OpaqueRegionSkia::didDrawPath(const GraphicsContext* context, const SkPath& path, const SkPaint& paint) 221 { 222 SkRect rect; 223 if (path.isRect(&rect)) { 224 didDrawRect(context, rect, paint, 0); 225 return; 226 } 227 228 bool fillsBounds = false; 229 230 if (!paint.canComputeFastBounds()) 231 didDrawUnbounded(context, paint, FillOrStroke); 232 else { 233 rect = paint.computeFastBounds(path.getBounds(), &rect); 234 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); 235 } 236 } 237 238 void OpaqueRegionSkia::didDrawPoints(const GraphicsContext* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint) 239 { 240 if (!numPoints) 241 return; 242 243 SkRect rect; 244 rect.fLeft = points[0].fX; 245 rect.fRight = points[0].fX + 1; 246 rect.fTop = points[0].fY; 247 rect.fBottom = points[0].fY + 1; 248 249 for (int i = 1; i < numPoints; ++i) { 250 rect.fLeft = std::min(rect.fLeft, points[i].fX); 251 rect.fRight = std::max(rect.fRight, points[i].fX + 1); 252 rect.fTop = std::min(rect.fTop, points[i].fY); 253 rect.fBottom = std::max(rect.fBottom, points[i].fY + 1); 254 } 255 256 bool fillsBounds = false; 257 258 if (!paint.canComputeFastBounds()) 259 didDrawUnbounded(context, paint, FillOrStroke); 260 else { 261 rect = paint.computeFastBounds(rect, &rect); 262 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); 263 } 264 } 265 266 void OpaqueRegionSkia::didDrawBounded(const GraphicsContext* context, const SkRect& bounds, const SkPaint& paint) 267 { 268 bool fillsBounds = false; 269 270 if (!paint.canComputeFastBounds()) 271 didDrawUnbounded(context, paint, FillOrStroke); 272 else { 273 SkRect rect; 274 rect = paint.computeFastBounds(bounds, &rect); 275 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); 276 } 277 } 278 279 void OpaqueRegionSkia::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType) 280 { 281 SkRect targetRect = rect; 282 283 // Apply the transform to device coordinate space. 284 SkMatrix canvasTransform = context->canvas()->getTotalMatrix(); 285 if (!canvasTransform.mapRect(&targetRect)) 286 fillsBounds = false; 287 288 // Apply the current clip. 289 SkRect deviceClipRect; 290 if (!getDeviceClipAsRect(context, deviceClipRect)) 291 fillsBounds = false; 292 else if (!targetRect.intersect(deviceClipRect)) 293 return; 294 295 bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap); 296 bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque); 297 bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque); 298 299 if (fillsBounds && xfersOpaque) 300 markRectAsOpaque(targetRect); 301 else if (!preservesOpaque) 302 markRectAsNonOpaque(targetRect); 303 } 304 305 void OpaqueRegionSkia::didDrawUnbounded(const GraphicsContext* context, const SkPaint& paint, DrawType drawType) 306 { 307 bool drawsOpaque = paintIsOpaque(paint, drawType, 0); 308 bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque); 309 310 if (preservesOpaque) 311 return; 312 313 SkRect deviceClipRect; 314 getDeviceClipAsRect(context, deviceClipRect); 315 markRectAsNonOpaque(deviceClipRect); 316 } 317 318 void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint) 319 { 320 SkRect deviceClipRect; 321 bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect); 322 323 if (deviceClipRect.isEmpty()) 324 return; 325 326 SkRect sourceOpaqueRect = layerOpaqueRect; 327 // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible. 328 SkRect destinationOpaqueRect = currentTrackingOpaqueRect(); 329 330 bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false); 331 if (!outsideSourceOpaqueRectPreservesOpaque) 332 markRectAsNonOpaque(deviceClipRect); 333 334 if (!deviceClipIsARect) 335 return; 336 if (!sourceOpaqueRect.intersect(deviceClipRect)) 337 return; 338 339 bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0); 340 bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque); 341 bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque); 342 343 // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise, 344 // if it preserves opaque then keep the intersection of the two. 345 if (sourceOpaqueRectXfersOpaque) 346 markRectAsOpaque(sourceOpaqueRect); 347 else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect)) 348 markRectAsOpaque(sourceOpaqueRect); 349 } 350 351 void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect) 352 { 353 // We want to keep track of an opaque region but bound its complexity at a constant size. 354 // We keep track of the largest rectangle seen by area. If we can add the new rect to this 355 // rectangle then we do that, as that is the cheapest way to increase the area returned 356 // without increasing the complexity. 357 358 SkRect& opaqueRect = currentTrackingOpaqueRect(); 359 360 if (rect.isEmpty()) 361 return; 362 if (opaqueRect.contains(rect)) 363 return; 364 if (rect.contains(opaqueRect)) { 365 opaqueRect = rect; 366 return; 367 } 368 369 if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) { 370 if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft) 371 opaqueRect.fLeft = rect.fLeft; 372 if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight) 373 opaqueRect.fRight = rect.fRight; 374 } else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) { 375 if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop) 376 opaqueRect.fTop = rect.fTop; 377 if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom) 378 opaqueRect.fBottom = rect.fBottom; 379 } 380 381 long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height(); 382 long area = (long)rect.width() * (long)rect.height(); 383 if (area > opaqueArea) 384 opaqueRect = rect; 385 } 386 387 void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect) 388 { 389 // We want to keep as much of the current opaque rectangle as we can, so find the one largest 390 // rectangle inside m_opaqueRect that does not intersect with |rect|. 391 392 SkRect& opaqueRect = currentTrackingOpaqueRect(); 393 394 if (!SkRect::Intersects(rect, opaqueRect)) 395 return; 396 if (rect.contains(opaqueRect)) { 397 markAllAsNonOpaque(); 398 return; 399 } 400 401 int deltaLeft = rect.fLeft - opaqueRect.fLeft; 402 int deltaRight = opaqueRect.fRight - rect.fRight; 403 int deltaTop = rect.fTop - opaqueRect.fTop; 404 int deltaBottom = opaqueRect.fBottom - rect.fBottom; 405 406 // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside opaqueRect. 407 // vertical is the larger of the two rectangles above or below |rect| and inside opaqueRect. 408 SkRect horizontal = opaqueRect; 409 if (deltaTop > deltaBottom) 410 horizontal.fBottom = rect.fTop; 411 else 412 horizontal.fTop = rect.fBottom; 413 SkRect vertical = opaqueRect; 414 if (deltaLeft > deltaRight) 415 vertical.fRight = rect.fLeft; 416 else 417 vertical.fLeft = rect.fRight; 418 419 if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height()) 420 opaqueRect = horizontal; 421 else 422 opaqueRect = vertical; 423 } 424 425 void OpaqueRegionSkia::markAllAsNonOpaque() 426 { 427 SkRect& opaqueRect = currentTrackingOpaqueRect(); 428 opaqueRect.setEmpty(); 429 } 430 431 SkRect& OpaqueRegionSkia::currentTrackingOpaqueRect() 432 { 433 // If we are drawing into a canvas layer, then track the opaque rect in that layer. 434 return m_canvasLayerStack.isEmpty() ? m_opaqueRect : m_canvasLayerStack.last().opaqueRect; 435 } 436 437 } // namespace WebCore 438