2 * Copyright (c) 2012, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "platform/graphics/RegionTracker.h"
35 #include "platform/graphics/GraphicsContext.h"
36 #include "third_party/skia/include/core/SkColorFilter.h"
37 #include "third_party/skia/include/core/SkShader.h"
41 RegionTracker::RegionTracker()
42 : m_opaqueRect(SkRect::MakeEmpty())
43 , m_trackedRegionType(Opaque)
47 void RegionTracker::reset()
49 ASSERT(m_canvasLayerStack.isEmpty());
50 m_opaqueRect = SkRect::MakeEmpty();
53 IntRect RegionTracker::asRect() const
55 // Returns the largest enclosed rect.
57 // epsilon is large enough to accommodate machine precision issues and
58 // small enough to have a negligible effect on rendered results.
59 const SkScalar epsilon = 1.0f / 512.0f;
61 int left = SkScalarCeilToInt(m_opaqueRect.fLeft - epsilon);
62 int top = SkScalarCeilToInt(m_opaqueRect.fTop - epsilon);
63 int right = SkScalarFloorToInt(m_opaqueRect.fRight + epsilon);
64 int bottom = SkScalarFloorToInt(m_opaqueRect.fBottom + epsilon);
65 return IntRect(left, top, right-left, bottom-top);
68 // Returns true if the xfermode will force the dst to be opaque, regardless of the current dst.
69 static inline bool xfermodeIsOpaque(const SkPaint& paint, bool srcIsOpaque)
74 SkXfermode* xfermode = paint.getXfermode();
76 return true; // default to kSrcOver_Mode
77 SkXfermode::Mode mode;
78 if (!xfermode->asMode(&mode))
82 case SkXfermode::kSrc_Mode: // source
83 case SkXfermode::kSrcOver_Mode: // source + dest - source*dest
84 case SkXfermode::kDstOver_Mode: // source + dest - source*dest
85 case SkXfermode::kDstATop_Mode: // source
86 case SkXfermode::kPlus_Mode: // source+dest
87 default: // the rest are all source + dest - source*dest
89 case SkXfermode::kClear_Mode: // 0
90 case SkXfermode::kDst_Mode: // dest
91 case SkXfermode::kSrcIn_Mode: // source * dest
92 case SkXfermode::kDstIn_Mode: // dest * source
93 case SkXfermode::kSrcOut_Mode: // source * (1-dest)
94 case SkXfermode::kDstOut_Mode: // dest * (1-source)
95 case SkXfermode::kSrcATop_Mode: // dest
96 case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest)
101 static inline bool xfermodeIsOverwrite(const SkPaint& paint)
103 SkXfermode* xfermode = paint.getXfermode();
105 return false; // default to kSrcOver_Mode
106 SkXfermode::Mode mode;
107 if (!xfermode->asMode(&mode))
110 case SkXfermode::kSrc_Mode:
111 case SkXfermode::kClear_Mode:
118 // Returns true if the xfermode will keep the dst opaque, assuming the dst is already opaque.
119 static inline bool xfermodePreservesOpaque(const SkPaint& paint, bool srcIsOpaque)
121 SkXfermode* xfermode = paint.getXfermode();
123 return true; // default to kSrcOver_Mode
124 SkXfermode::Mode mode;
125 if (!xfermode->asMode(&mode))
129 case SkXfermode::kDst_Mode: // dest
130 case SkXfermode::kSrcOver_Mode: // source + dest - source*dest
131 case SkXfermode::kDstOver_Mode: // source + dest - source*dest
132 case SkXfermode::kSrcATop_Mode: // dest
133 case SkXfermode::kPlus_Mode: // source+dest
134 default: // the rest are all source + dest - source*dest
136 case SkXfermode::kClear_Mode: // 0
137 case SkXfermode::kSrcOut_Mode: // source * (1-dest)
138 case SkXfermode::kDstOut_Mode: // dest * (1-source)
139 case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest)
141 case SkXfermode::kSrc_Mode: // source
142 case SkXfermode::kSrcIn_Mode: // source * dest
143 case SkXfermode::kDstIn_Mode: // dest * source
144 case SkXfermode::kDstATop_Mode: // source
149 // Returns true if all pixels painted will be opaque.
150 static inline bool paintIsOpaque(const SkPaint& paint, RegionTracker::DrawType drawType, const SkBitmap* bitmap)
152 if (paint.getAlpha() < 0xFF)
154 bool checkFillOnly = drawType != RegionTracker::FillOrStroke;
155 if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias())
157 SkShader* shader = paint.getShader();
158 if (shader && !shader->isOpaque())
160 if (bitmap && !bitmap->isOpaque())
162 if (paint.getLooper())
164 if (paint.getImageFilter())
166 if (paint.getMaskFilter())
168 SkColorFilter* colorFilter = paint.getColorFilter();
169 if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag))
174 // Returns true if there is a rectangular clip, with the result in |deviceClipRect|.
175 static inline bool getDeviceClipAsRect(const GraphicsContext* context, SkRect& deviceClipRect)
177 // Get the current clip in device coordinate space.
178 if (!context->canvas()->isClipRect()) {
179 deviceClipRect.setEmpty();
183 SkIRect deviceClipIRect;
184 if (context->canvas()->getClipDeviceBounds(&deviceClipIRect))
185 deviceClipRect.set(deviceClipIRect);
187 deviceClipRect.setEmpty();
192 void RegionTracker::pushCanvasLayer(const SkPaint* paint)
194 CanvasLayerState state;
196 state.paint = *paint;
197 m_canvasLayerStack.append(state);
200 void RegionTracker::popCanvasLayer(const GraphicsContext* context)
202 ASSERT(!m_canvasLayerStack.isEmpty());
203 if (m_canvasLayerStack.isEmpty())
206 const CanvasLayerState& canvasLayer = m_canvasLayerStack.last();
207 SkRect layerOpaqueRect = canvasLayer.opaqueRect;
208 SkPaint layerPaint = canvasLayer.paint;
210 // Apply the image mask.
211 if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect))
212 layerOpaqueRect.setEmpty();
214 m_canvasLayerStack.removeLast();
216 applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint);
219 void RegionTracker::setImageMask(const SkRect& imageOpaqueRect)
221 ASSERT(!m_canvasLayerStack.isEmpty());
222 m_canvasLayerStack.last().hasImageMask = true;
223 m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect;
226 void RegionTracker::didDrawRect(const GraphicsContext* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap)
228 // Any stroking may put alpha in pixels even if the filling part does not.
229 if (paint.getStyle() != SkPaint::kFill_Style) {
230 bool fillsBounds = false;
232 if (!paint.canComputeFastBounds()) {
233 didDrawUnbounded(context, paint, FillOrStroke);
236 strokeRect = paint.computeFastBounds(fillRect, &strokeRect);
237 didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, FillOrStroke);
241 bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style;
242 didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly);
245 void RegionTracker::didDrawPath(const GraphicsContext* context, const SkPath& path, const SkPaint& paint)
248 if (path.isRect(&rect)) {
249 didDrawRect(context, rect, paint, 0);
253 bool fillsBounds = false;
255 if (!paint.canComputeFastBounds()) {
256 didDrawUnbounded(context, paint, FillOrStroke);
258 rect = paint.computeFastBounds(path.getBounds(), &rect);
259 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
263 void RegionTracker::didDrawPoints(const GraphicsContext* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
269 rect.fLeft = points[0].fX;
270 rect.fRight = points[0].fX + 1;
271 rect.fTop = points[0].fY;
272 rect.fBottom = points[0].fY + 1;
274 for (int i = 1; i < numPoints; ++i) {
275 rect.fLeft = std::min(rect.fLeft, points[i].fX);
276 rect.fRight = std::max(rect.fRight, points[i].fX + 1);
277 rect.fTop = std::min(rect.fTop, points[i].fY);
278 rect.fBottom = std::max(rect.fBottom, points[i].fY + 1);
281 bool fillsBounds = false;
283 if (!paint.canComputeFastBounds()) {
284 didDrawUnbounded(context, paint, FillOrStroke);
286 rect = paint.computeFastBounds(rect, &rect);
287 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
291 void RegionTracker::didDrawBounded(const GraphicsContext* context, const SkRect& bounds, const SkPaint& paint)
293 bool fillsBounds = false;
295 if (!paint.canComputeFastBounds()) {
296 didDrawUnbounded(context, paint, FillOrStroke);
299 rect = paint.computeFastBounds(bounds, &rect);
300 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
304 void RegionTracker::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType)
306 SkRect targetRect = rect;
308 // Apply the transform to device coordinate space.
309 SkMatrix canvasTransform = context->canvas()->getTotalMatrix();
310 if (!canvasTransform.mapRect(&targetRect))
313 // Apply the current clip.
314 SkRect deviceClipRect;
315 if (!getDeviceClipAsRect(context, deviceClipRect))
317 else if (!targetRect.intersect(deviceClipRect))
320 if (m_trackedRegionType == Overwrite && fillsBounds && xfermodeIsOverwrite(paint)) {
321 markRectAsOpaque(targetRect);
325 bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap);
326 bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque);
328 if (fillsBounds && xfersOpaque) {
329 markRectAsOpaque(targetRect);
330 } else if (m_trackedRegionType == Opaque && !xfermodePreservesOpaque(paint, drawsOpaque)) {
331 markRectAsNonOpaque(targetRect);
335 void RegionTracker::didDrawUnbounded(const GraphicsContext* context, const SkPaint& paint, DrawType drawType)
337 bool drawsOpaque = paintIsOpaque(paint, drawType, 0);
338 bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
343 SkRect deviceClipRect;
344 getDeviceClipAsRect(context, deviceClipRect);
345 markRectAsNonOpaque(deviceClipRect);
348 void RegionTracker::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
350 SkRect deviceClipRect;
351 bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);
353 if (deviceClipIsARect && deviceClipRect.isEmpty())
356 SkRect sourceOpaqueRect = layerOpaqueRect;
357 // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible.
358 SkRect destinationOpaqueRect = currentTrackingOpaqueRect();
360 bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
361 if (!outsideSourceOpaqueRectPreservesOpaque) {
362 if (!deviceClipIsARect) {
363 markAllAsNonOpaque();
366 markRectAsNonOpaque(deviceClipRect);
369 if (!deviceClipIsARect)
371 if (!sourceOpaqueRect.intersect(deviceClipRect))
374 bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
375 bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
376 bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);
378 // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise,
379 // if it preserves opaque then keep the intersection of the two.
380 if (sourceOpaqueRectXfersOpaque)
381 markRectAsOpaque(sourceOpaqueRect);
382 else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect))
383 markRectAsOpaque(sourceOpaqueRect);
386 void RegionTracker::markRectAsOpaque(const SkRect& rect)
388 // We want to keep track of an opaque region but bound its complexity at a constant size.
389 // We keep track of the largest rectangle seen by area. If we can add the new rect to this
390 // rectangle then we do that, as that is the cheapest way to increase the area returned
391 // without increasing the complexity.
393 SkRect& opaqueRect = currentTrackingOpaqueRect();
397 if (opaqueRect.contains(rect))
399 if (rect.contains(opaqueRect)) {
404 if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) {
405 if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft)
406 opaqueRect.fLeft = rect.fLeft;
407 if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight)
408 opaqueRect.fRight = rect.fRight;
409 } else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) {
410 if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop)
411 opaqueRect.fTop = rect.fTop;
412 if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom)
413 opaqueRect.fBottom = rect.fBottom;
416 long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height();
417 long area = (long)rect.width() * (long)rect.height();
418 if (area > opaqueArea)
422 void RegionTracker::markRectAsNonOpaque(const SkRect& rect)
424 // We want to keep as much of the current opaque rectangle as we can, so find the one largest
425 // rectangle inside m_opaqueRect that does not intersect with |rect|.
427 SkRect& opaqueRect = currentTrackingOpaqueRect();
429 if (!SkRect::Intersects(rect, opaqueRect))
431 if (rect.contains(opaqueRect)) {
432 markAllAsNonOpaque();
436 int deltaLeft = rect.fLeft - opaqueRect.fLeft;
437 int deltaRight = opaqueRect.fRight - rect.fRight;
438 int deltaTop = rect.fTop - opaqueRect.fTop;
439 int deltaBottom = opaqueRect.fBottom - rect.fBottom;
441 // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside opaqueRect.
442 // vertical is the larger of the two rectangles above or below |rect| and inside opaqueRect.
443 SkRect horizontal = opaqueRect;
444 if (deltaTop > deltaBottom)
445 horizontal.fBottom = rect.fTop;
447 horizontal.fTop = rect.fBottom;
448 SkRect vertical = opaqueRect;
449 if (deltaLeft > deltaRight)
450 vertical.fRight = rect.fLeft;
452 vertical.fLeft = rect.fRight;
454 if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height())
455 opaqueRect = horizontal;
457 opaqueRect = vertical;
460 void RegionTracker::markAllAsNonOpaque()
462 SkRect& opaqueRect = currentTrackingOpaqueRect();
463 opaqueRect.setEmpty();
466 SkRect& RegionTracker::currentTrackingOpaqueRect()
468 // If we are drawing into a canvas layer, then track the opaque rect in that layer.
469 return m_canvasLayerStack.isEmpty() ? m_opaqueRect : m_canvasLayerStack.last().opaqueRect;