• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
OpaqueRegionSkia()42 OpaqueRegionSkia::OpaqueRegionSkia()
43     : m_opaqueRect(SkRect::MakeEmpty())
44 {
45 }
46 
asRect() const47 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.
xfermodeIsOpaque(const SkPaint & paint,bool srcIsOpaque)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.
xfermodePreservesOpaque(const SkPaint & paint,bool srcIsOpaque)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.
paintIsOpaque(const SkPaint & paint,OpaqueRegionSkia::DrawType drawType,const SkBitmap * bitmap)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|.
getDeviceClipAsRect(const GraphicsContext * context,SkRect & 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 
pushCanvasLayer(const SkPaint * paint)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 
popCanvasLayer(const GraphicsContext * context)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 
setImageMask(const SkRect & imageOpaqueRect)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 
didDrawRect(const GraphicsContext * context,const SkRect & fillRect,const SkPaint & paint,const SkBitmap * sourceBitmap)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 
didDrawPath(const GraphicsContext * context,const SkPath & path,const SkPaint & paint)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 
didDrawPoints(const GraphicsContext * context,SkCanvas::PointMode mode,int numPoints,const SkPoint points[],const SkPaint & paint)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 
didDrawBounded(const GraphicsContext * context,const SkRect & bounds,const SkPaint & paint)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 
didDraw(const GraphicsContext * context,const SkRect & rect,const SkPaint & paint,const SkBitmap * sourceBitmap,bool fillsBounds,DrawType drawType)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 
didDrawUnbounded(const GraphicsContext * context,const SkPaint & paint,DrawType drawType)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 
applyOpaqueRegionFromLayer(const GraphicsContext * context,const SkRect & layerOpaqueRect,const SkPaint & paint)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 
markRectAsOpaque(const SkRect & rect)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 
markRectAsNonOpaque(const SkRect & rect)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 
markAllAsNonOpaque()430 void OpaqueRegionSkia::markAllAsNonOpaque()
431 {
432     SkRect& opaqueRect = currentTrackingOpaqueRect();
433     opaqueRect.setEmpty();
434 }
435 
currentTrackingOpaqueRect()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