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 "platform/graphics/skia/OpaqueRegionSkia.h" 34 35 #include "platform/graphics/GraphicsContext.h" 36 37 #include "SkColorFilter.h" 38 #include "SkShader.h" 39 40 namespace WebCore { 41 42 OpaqueRegionSkia::OpaqueRegionSkia() 43 : m_opaqueRect(SkRect::MakeEmpty()) 44 { 45 } 46 47 IntRect OpaqueRegionSkia::asRect() const 48 { 49 // Returns the largest enclosed rect. 50 // TODO: actually, this logic looks like its returning the smallest. 51 // to return largest, shouldn't we take floor of left/top 52 // and the ceil of right/bottom? 53 int left = SkScalarCeilToInt(m_opaqueRect.fLeft); 54 int top = SkScalarCeilToInt(m_opaqueRect.fTop); 55 int right = SkScalarFloorToInt(m_opaqueRect.fRight); 56 int bottom = SkScalarFloorToInt(m_opaqueRect.fBottom); 57 return IntRect(left, top, right-left, bottom-top); 58 } 59 60 // Returns true if the xfermode will force the dst to be opaque, regardless of the current dst. 61 static inline bool xfermodeIsOpaque(const SkPaint& paint, bool srcIsOpaque) 62 { 63 if (!srcIsOpaque) 64 return false; 65 66 SkXfermode* xfermode = paint.getXfermode(); 67 if (!xfermode) 68 return true; // default to kSrcOver_Mode 69 SkXfermode::Mode mode; 70 if (!xfermode->asMode(&mode)) 71 return false; 72 73 switch (mode) { 74 case SkXfermode::kSrc_Mode: // source 75 case SkXfermode::kSrcOver_Mode: // source + dest - source*dest 76 case SkXfermode::kDstOver_Mode: // source + dest - source*dest 77 case SkXfermode::kDstATop_Mode: // source 78 case SkXfermode::kPlus_Mode: // source+dest 79 default: // the rest are all source + dest - source*dest 80 return true; 81 case SkXfermode::kClear_Mode: // 0 82 case SkXfermode::kDst_Mode: // dest 83 case SkXfermode::kSrcIn_Mode: // source * dest 84 case SkXfermode::kDstIn_Mode: // dest * source 85 case SkXfermode::kSrcOut_Mode: // source * (1-dest) 86 case SkXfermode::kDstOut_Mode: // dest * (1-source) 87 case SkXfermode::kSrcATop_Mode: // dest 88 case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest) 89 return false; 90 } 91 } 92 93 // Returns true if the xfermode will keep the dst opaque, assuming the dst is already opaque. 94 static inline bool xfermodePreservesOpaque(const SkPaint& paint, bool srcIsOpaque) 95 { 96 SkXfermode* xfermode = paint.getXfermode(); 97 if (!xfermode) 98 return true; // default to kSrcOver_Mode 99 SkXfermode::Mode mode; 100 if (!xfermode->asMode(&mode)) 101 return false; 102 103 switch (mode) { 104 case SkXfermode::kDst_Mode: // dest 105 case SkXfermode::kSrcOver_Mode: // source + dest - source*dest 106 case SkXfermode::kDstOver_Mode: // source + dest - source*dest 107 case SkXfermode::kSrcATop_Mode: // dest 108 case SkXfermode::kPlus_Mode: // source+dest 109 default: // the rest are all source + dest - source*dest 110 return true; 111 case SkXfermode::kClear_Mode: // 0 112 case SkXfermode::kSrcOut_Mode: // source * (1-dest) 113 case SkXfermode::kDstOut_Mode: // dest * (1-source) 114 case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest) 115 return false; 116 case SkXfermode::kSrc_Mode: // source 117 case SkXfermode::kSrcIn_Mode: // source * dest 118 case SkXfermode::kDstIn_Mode: // dest * source 119 case SkXfermode::kDstATop_Mode: // source 120 return srcIsOpaque; 121 } 122 } 123 124 // Returns true if all pixels painted will be opaque. 125 static inline bool paintIsOpaque(const SkPaint& paint, OpaqueRegionSkia::DrawType drawType, const SkBitmap* bitmap) 126 { 127 if (paint.getAlpha() < 0xFF) 128 return false; 129 bool checkFillOnly = drawType != OpaqueRegionSkia::FillOrStroke; 130 if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias()) 131 return false; 132 SkShader* shader = paint.getShader(); 133 if (shader && !shader->isOpaque()) 134 return false; 135 if (bitmap && !bitmap->isOpaque()) 136 return false; 137 if (paint.getLooper()) 138 return false; 139 if (paint.getImageFilter()) 140 return false; 141 if (paint.getMaskFilter()) 142 return false; 143 SkColorFilter* colorFilter = paint.getColorFilter(); 144 if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag)) 145 return false; 146 return true; 147 } 148 149 // Returns true if there is a rectangular clip, with the result in |deviceClipRect|. 150 static inline bool getDeviceClipAsRect(const GraphicsContext* context, SkRect& deviceClipRect) 151 { 152 // Get the current clip in device coordinate space. 153 if (!context->canvas()->isClipRect()) 154 return false; 155 156 SkIRect deviceClipIRect; 157 if (context->canvas()->getClipDeviceBounds(&deviceClipIRect)) 158 deviceClipRect.set(deviceClipIRect); 159 else 160 deviceClipRect.setEmpty(); 161 162 return true; 163 } 164 165 void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint) 166 { 167 CanvasLayerState state; 168 if (paint) 169 state.paint = *paint; 170 m_canvasLayerStack.append(state); 171 } 172 173 void OpaqueRegionSkia::popCanvasLayer(const GraphicsContext* context) 174 { 175 ASSERT(!context->paintingDisabled()); 176 ASSERT(!m_canvasLayerStack.isEmpty()); 177 if (m_canvasLayerStack.isEmpty()) 178 return; 179 180 const CanvasLayerState& canvasLayer = m_canvasLayerStack.last(); 181 SkRect layerOpaqueRect = canvasLayer.opaqueRect; 182 SkPaint layerPaint = canvasLayer.paint; 183 184 // Apply the image mask. 185 if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect)) 186 layerOpaqueRect.setEmpty(); 187 188 m_canvasLayerStack.removeLast(); 189 190 applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint); 191 } 192 193 void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect) 194 { 195 ASSERT(!m_canvasLayerStack.isEmpty()); 196 m_canvasLayerStack.last().hasImageMask = true; 197 m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect; 198 } 199 200 void OpaqueRegionSkia::didDrawRect(const GraphicsContext* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap) 201 { 202 ASSERT(!context->paintingDisabled()); 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 ASSERT(!context->paintingDisabled()); 223 SkRect rect; 224 if (path.isRect(&rect)) { 225 didDrawRect(context, rect, paint, 0); 226 return; 227 } 228 229 bool fillsBounds = false; 230 231 if (!paint.canComputeFastBounds()) 232 didDrawUnbounded(context, paint, FillOrStroke); 233 else { 234 rect = paint.computeFastBounds(path.getBounds(), &rect); 235 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); 236 } 237 } 238 239 void OpaqueRegionSkia::didDrawPoints(const GraphicsContext* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint) 240 { 241 ASSERT(!context->paintingDisabled()); 242 if (!numPoints) 243 return; 244 245 SkRect rect; 246 rect.fLeft = points[0].fX; 247 rect.fRight = points[0].fX + 1; 248 rect.fTop = points[0].fY; 249 rect.fBottom = points[0].fY + 1; 250 251 for (int i = 1; i < numPoints; ++i) { 252 rect.fLeft = std::min(rect.fLeft, points[i].fX); 253 rect.fRight = std::max(rect.fRight, points[i].fX + 1); 254 rect.fTop = std::min(rect.fTop, points[i].fY); 255 rect.fBottom = std::max(rect.fBottom, points[i].fY + 1); 256 } 257 258 bool fillsBounds = false; 259 260 if (!paint.canComputeFastBounds()) 261 didDrawUnbounded(context, paint, FillOrStroke); 262 else { 263 rect = paint.computeFastBounds(rect, &rect); 264 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); 265 } 266 } 267 268 void OpaqueRegionSkia::didDrawBounded(const GraphicsContext* context, const SkRect& bounds, const SkPaint& paint) 269 { 270 ASSERT(!context->paintingDisabled()); 271 bool fillsBounds = false; 272 273 if (!paint.canComputeFastBounds()) 274 didDrawUnbounded(context, paint, FillOrStroke); 275 else { 276 SkRect rect; 277 rect = paint.computeFastBounds(bounds, &rect); 278 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke); 279 } 280 } 281 282 void OpaqueRegionSkia::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType) 283 { 284 ASSERT(!context->paintingDisabled()); 285 SkRect targetRect = rect; 286 287 // Apply the transform to device coordinate space. 288 SkMatrix canvasTransform = context->canvas()->getTotalMatrix(); 289 if (!canvasTransform.mapRect(&targetRect)) 290 fillsBounds = false; 291 292 // Apply the current clip. 293 SkRect deviceClipRect; 294 if (!getDeviceClipAsRect(context, deviceClipRect)) 295 fillsBounds = false; 296 else if (!targetRect.intersect(deviceClipRect)) 297 return; 298 299 bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap); 300 bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque); 301 bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque); 302 303 if (fillsBounds && xfersOpaque) 304 markRectAsOpaque(targetRect); 305 else if (!preservesOpaque) 306 markRectAsNonOpaque(targetRect); 307 } 308 309 void OpaqueRegionSkia::didDrawUnbounded(const GraphicsContext* context, const SkPaint& paint, DrawType drawType) 310 { 311 ASSERT(!context->paintingDisabled()); 312 bool drawsOpaque = paintIsOpaque(paint, drawType, 0); 313 bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque); 314 315 if (preservesOpaque) 316 return; 317 318 SkRect deviceClipRect; 319 getDeviceClipAsRect(context, deviceClipRect); 320 markRectAsNonOpaque(deviceClipRect); 321 } 322 323 void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint) 324 { 325 SkRect deviceClipRect; 326 bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect); 327 328 if (deviceClipRect.isEmpty()) 329 return; 330 331 SkRect sourceOpaqueRect = layerOpaqueRect; 332 // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible. 333 SkRect destinationOpaqueRect = currentTrackingOpaqueRect(); 334 335 bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false); 336 if (!outsideSourceOpaqueRectPreservesOpaque) 337 markRectAsNonOpaque(deviceClipRect); 338 339 if (!deviceClipIsARect) 340 return; 341 if (!sourceOpaqueRect.intersect(deviceClipRect)) 342 return; 343 344 bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0); 345 bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque); 346 bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque); 347 348 // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise, 349 // if it preserves opaque then keep the intersection of the two. 350 if (sourceOpaqueRectXfersOpaque) 351 markRectAsOpaque(sourceOpaqueRect); 352 else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect)) 353 markRectAsOpaque(sourceOpaqueRect); 354 } 355 356 void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect) 357 { 358 // We want to keep track of an opaque region but bound its complexity at a constant size. 359 // We keep track of the largest rectangle seen by area. If we can add the new rect to this 360 // rectangle then we do that, as that is the cheapest way to increase the area returned 361 // without increasing the complexity. 362 363 SkRect& opaqueRect = currentTrackingOpaqueRect(); 364 365 if (rect.isEmpty()) 366 return; 367 if (opaqueRect.contains(rect)) 368 return; 369 if (rect.contains(opaqueRect)) { 370 opaqueRect = rect; 371 return; 372 } 373 374 if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) { 375 if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft) 376 opaqueRect.fLeft = rect.fLeft; 377 if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight) 378 opaqueRect.fRight = rect.fRight; 379 } else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) { 380 if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop) 381 opaqueRect.fTop = rect.fTop; 382 if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom) 383 opaqueRect.fBottom = rect.fBottom; 384 } 385 386 long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height(); 387 long area = (long)rect.width() * (long)rect.height(); 388 if (area > opaqueArea) 389 opaqueRect = rect; 390 } 391 392 void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect) 393 { 394 // We want to keep as much of the current opaque rectangle as we can, so find the one largest 395 // rectangle inside m_opaqueRect that does not intersect with |rect|. 396 397 SkRect& opaqueRect = currentTrackingOpaqueRect(); 398 399 if (!SkRect::Intersects(rect, opaqueRect)) 400 return; 401 if (rect.contains(opaqueRect)) { 402 markAllAsNonOpaque(); 403 return; 404 } 405 406 int deltaLeft = rect.fLeft - opaqueRect.fLeft; 407 int deltaRight = opaqueRect.fRight - rect.fRight; 408 int deltaTop = rect.fTop - opaqueRect.fTop; 409 int deltaBottom = opaqueRect.fBottom - rect.fBottom; 410 411 // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside opaqueRect. 412 // vertical is the larger of the two rectangles above or below |rect| and inside opaqueRect. 413 SkRect horizontal = opaqueRect; 414 if (deltaTop > deltaBottom) 415 horizontal.fBottom = rect.fTop; 416 else 417 horizontal.fTop = rect.fBottom; 418 SkRect vertical = opaqueRect; 419 if (deltaLeft > deltaRight) 420 vertical.fRight = rect.fLeft; 421 else 422 vertical.fLeft = rect.fRight; 423 424 if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height()) 425 opaqueRect = horizontal; 426 else 427 opaqueRect = vertical; 428 } 429 430 void OpaqueRegionSkia::markAllAsNonOpaque() 431 { 432 SkRect& opaqueRect = currentTrackingOpaqueRect(); 433 opaqueRect.setEmpty(); 434 } 435 436 SkRect& OpaqueRegionSkia::currentTrackingOpaqueRect() 437 { 438 // If we are drawing into a canvas layer, then track the opaque rect in that layer. 439 return m_canvasLayerStack.isEmpty() ? m_opaqueRect : m_canvasLayerStack.last().opaqueRect; 440 } 441 442 } // namespace WebCore 443