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/skia/OpaqueRegionSkia.h"
35 #include "platform/graphics/GraphicsContext.h"
37 #include "SkColorFilter.h"
42 OpaqueRegionSkia::OpaqueRegionSkia()
43 : m_opaqueRect(SkRect::MakeEmpty())
47 IntRect OpaqueRegionSkia::asRect() const
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);
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)
66 SkXfermode* xfermode = paint.getXfermode();
68 return true; // default to kSrcOver_Mode
69 SkXfermode::Mode mode;
70 if (!xfermode->asMode(&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
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)
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)
96 SkXfermode* xfermode = paint.getXfermode();
98 return true; // default to kSrcOver_Mode
99 SkXfermode::Mode mode;
100 if (!xfermode->asMode(&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
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)
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
124 // Returns true if all pixels painted will be opaque.
125 static inline bool paintIsOpaque(const SkPaint& paint, OpaqueRegionSkia::DrawType drawType, const SkBitmap* bitmap)
127 if (paint.getAlpha() < 0xFF)
129 bool checkFillOnly = drawType != OpaqueRegionSkia::FillOrStroke;
130 if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias())
132 SkShader* shader = paint.getShader();
133 if (shader && !shader->isOpaque())
135 if (bitmap && !bitmap->isOpaque())
137 if (paint.getLooper())
139 if (paint.getImageFilter())
141 if (paint.getMaskFilter())
143 SkColorFilter* colorFilter = paint.getColorFilter();
144 if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag))
149 // Returns true if there is a rectangular clip, with the result in |deviceClipRect|.
150 static inline bool getDeviceClipAsRect(const GraphicsContext* context, SkRect& deviceClipRect)
152 // Get the current clip in device coordinate space.
153 if (!context->canvas()->isClipRect())
156 SkIRect deviceClipIRect;
157 if (context->canvas()->getClipDeviceBounds(&deviceClipIRect))
158 deviceClipRect.set(deviceClipIRect);
160 deviceClipRect.setEmpty();
165 void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint)
167 CanvasLayerState state;
169 state.paint = *paint;
170 m_canvasLayerStack.append(state);
173 void OpaqueRegionSkia::popCanvasLayer(const GraphicsContext* context)
175 ASSERT(!context->paintingDisabled());
176 ASSERT(!m_canvasLayerStack.isEmpty());
177 if (m_canvasLayerStack.isEmpty())
180 const CanvasLayerState& canvasLayer = m_canvasLayerStack.last();
181 SkRect layerOpaqueRect = canvasLayer.opaqueRect;
182 SkPaint layerPaint = canvasLayer.paint;
184 // Apply the image mask.
185 if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect))
186 layerOpaqueRect.setEmpty();
188 m_canvasLayerStack.removeLast();
190 applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint);
193 void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect)
195 ASSERT(!m_canvasLayerStack.isEmpty());
196 m_canvasLayerStack.last().hasImageMask = true;
197 m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect;
200 void OpaqueRegionSkia::didDrawRect(const GraphicsContext* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap)
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;
207 if (!paint.canComputeFastBounds())
208 didDrawUnbounded(context, paint, FillOrStroke);
211 strokeRect = paint.computeFastBounds(fillRect, &strokeRect);
212 didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, FillOrStroke);
216 bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style;
217 didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly);
220 void OpaqueRegionSkia::didDrawPath(const GraphicsContext* context, const SkPath& path, const SkPaint& paint)
222 ASSERT(!context->paintingDisabled());
224 if (path.isRect(&rect)) {
225 didDrawRect(context, rect, paint, 0);
229 bool fillsBounds = false;
231 if (!paint.canComputeFastBounds())
232 didDrawUnbounded(context, paint, FillOrStroke);
234 rect = paint.computeFastBounds(path.getBounds(), &rect);
235 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
239 void OpaqueRegionSkia::didDrawPoints(const GraphicsContext* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
241 ASSERT(!context->paintingDisabled());
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;
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);
258 bool fillsBounds = false;
260 if (!paint.canComputeFastBounds())
261 didDrawUnbounded(context, paint, FillOrStroke);
263 rect = paint.computeFastBounds(rect, &rect);
264 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
268 void OpaqueRegionSkia::didDrawBounded(const GraphicsContext* context, const SkRect& bounds, const SkPaint& paint)
270 ASSERT(!context->paintingDisabled());
271 bool fillsBounds = false;
273 if (!paint.canComputeFastBounds())
274 didDrawUnbounded(context, paint, FillOrStroke);
277 rect = paint.computeFastBounds(bounds, &rect);
278 didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
282 void OpaqueRegionSkia::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType)
284 ASSERT(!context->paintingDisabled());
285 SkRect targetRect = rect;
287 // Apply the transform to device coordinate space.
288 SkMatrix canvasTransform = context->canvas()->getTotalMatrix();
289 if (!canvasTransform.mapRect(&targetRect))
292 // Apply the current clip.
293 SkRect deviceClipRect;
294 if (!getDeviceClipAsRect(context, deviceClipRect))
296 else if (!targetRect.intersect(deviceClipRect))
299 bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap);
300 bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque);
301 bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
303 if (fillsBounds && xfersOpaque)
304 markRectAsOpaque(targetRect);
305 else if (!preservesOpaque)
306 markRectAsNonOpaque(targetRect);
309 void OpaqueRegionSkia::didDrawUnbounded(const GraphicsContext* context, const SkPaint& paint, DrawType drawType)
311 ASSERT(!context->paintingDisabled());
312 bool drawsOpaque = paintIsOpaque(paint, drawType, 0);
313 bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
318 SkRect deviceClipRect;
319 getDeviceClipAsRect(context, deviceClipRect);
320 markRectAsNonOpaque(deviceClipRect);
323 void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
325 SkRect deviceClipRect;
326 bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);
328 if (deviceClipRect.isEmpty())
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();
335 bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
336 if (!outsideSourceOpaqueRectPreservesOpaque)
337 markRectAsNonOpaque(deviceClipRect);
339 if (!deviceClipIsARect)
341 if (!sourceOpaqueRect.intersect(deviceClipRect))
344 bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
345 bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
346 bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);
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);
356 void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect)
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.
363 SkRect& opaqueRect = currentTrackingOpaqueRect();
367 if (opaqueRect.contains(rect))
369 if (rect.contains(opaqueRect)) {
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;
386 long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height();
387 long area = (long)rect.width() * (long)rect.height();
388 if (area > opaqueArea)
392 void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect)
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|.
397 SkRect& opaqueRect = currentTrackingOpaqueRect();
399 if (!SkRect::Intersects(rect, opaqueRect))
401 if (rect.contains(opaqueRect)) {
402 markAllAsNonOpaque();
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;
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;
417 horizontal.fTop = rect.fBottom;
418 SkRect vertical = opaqueRect;
419 if (deltaLeft > deltaRight)
420 vertical.fRight = rect.fLeft;
422 vertical.fLeft = rect.fRight;
424 if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height())
425 opaqueRect = horizontal;
427 opaqueRect = vertical;
430 void OpaqueRegionSkia::markAllAsNonOpaque()
432 SkRect& opaqueRect = currentTrackingOpaqueRect();
433 opaqueRect.setEmpty();
436 SkRect& OpaqueRegionSkia::currentTrackingOpaqueRect()
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;
442 } // namespace WebCore