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