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 "OpaqueRegionSkia.h"
35
#include "PlatformContextSkia.h"
38
#include "SkColorFilter.h"
43
OpaqueRegionSkia::OpaqueRegionSkia()
44
: m_opaqueRect(SkRect::MakeEmpty())
48
OpaqueRegionSkia::~OpaqueRegionSkia()
52
IntRect OpaqueRegionSkia::asRect() const
54
// Returns the largest enclosed rect.
55
int left = SkScalarCeil(m_opaqueRect.fLeft);
56
int top = SkScalarCeil(m_opaqueRect.fTop);
57
int right = SkScalarFloor(m_opaqueRect.fRight);
58
int bottom = SkScalarFloor(m_opaqueRect.fBottom);
59
return IntRect(left, top, right-left, bottom-top);
62
// Returns true if the xfermode will force the dst to be opaque, regardless of the current dst.
63
static inline bool xfermodeIsOpaque(const SkPaint& paint, bool srcIsOpaque)
68
SkXfermode* xfermode = paint.getXfermode();
70
return true; // default to kSrcOver_Mode
71
SkXfermode::Mode mode;
72
if (!xfermode->asMode(&mode))
76
case SkXfermode::kSrc_Mode: // source
77
case SkXfermode::kSrcOver_Mode: // source + dest - source*dest
78
case SkXfermode::kDstOver_Mode: // source + dest - source*dest
79
case SkXfermode::kDstATop_Mode: // source
80
case SkXfermode::kPlus_Mode: // source+dest
81
default: // the rest are all source + dest - source*dest
83
case SkXfermode::kClear_Mode: // 0
84
case SkXfermode::kDst_Mode: // dest
85
case SkXfermode::kSrcIn_Mode: // source * dest
86
case SkXfermode::kDstIn_Mode: // dest * source
87
case SkXfermode::kSrcOut_Mode: // source * (1-dest)
88
case SkXfermode::kDstOut_Mode: // dest * (1-source)
89
case SkXfermode::kSrcATop_Mode: // dest
90
case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest)
95
// Returns true if the xfermode will keep the dst opaque, assuming the dst is already opaque.
96
static inline bool xfermodePreservesOpaque(const SkPaint& paint, bool srcIsOpaque)
98
SkXfermode* xfermode = paint.getXfermode();
100
return true; // default to kSrcOver_Mode
101
SkXfermode::Mode mode;
102
if (!xfermode->asMode(&mode))
106
case SkXfermode::kDst_Mode: // dest
107
case SkXfermode::kSrcOver_Mode: // source + dest - source*dest
108
case SkXfermode::kDstOver_Mode: // source + dest - source*dest
109
case SkXfermode::kSrcATop_Mode: // dest
110
case SkXfermode::kPlus_Mode: // source+dest
111
default: // the rest are all source + dest - source*dest
113
case SkXfermode::kClear_Mode: // 0
114
case SkXfermode::kSrcOut_Mode: // source * (1-dest)
115
case SkXfermode::kDstOut_Mode: // dest * (1-source)
116
case SkXfermode::kXor_Mode: // source + dest - 2*(source*dest)
118
case SkXfermode::kSrc_Mode: // source
119
case SkXfermode::kSrcIn_Mode: // source * dest
120
case SkXfermode::kDstIn_Mode: // dest * source
121
case SkXfermode::kDstATop_Mode: // source
126
// Returns true if all pixels painted will be opaque.
127
static inline bool paintIsOpaque(const SkPaint& paint, OpaqueRegionSkia::DrawType drawType, const SkBitmap* bitmap)
129
if (paint.getAlpha() < 0xFF)
131
bool checkFillOnly = drawType != OpaqueRegionSkia::FillOrStroke;
132
if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias())
134
SkShader* shader = paint.getShader();
135
if (shader && !shader->isOpaque())
137
if (bitmap && !bitmap->isOpaque())
139
if (paint.getLooper())
141
if (paint.getImageFilter())
143
if (paint.getMaskFilter())
145
SkColorFilter* colorFilter = paint.getColorFilter();
146
if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag))
151
// Returns true if there is a rectangular clip, with the result in |deviceClipRect|.
152
static inline bool getDeviceClipAsRect(const PlatformContextSkia* context, SkRect& deviceClipRect)
154
// Get the current clip in device coordinate space.
155
if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType)
158
SkIRect deviceClipIRect;
159
if (context->canvas()->getClipDeviceBounds(&deviceClipIRect))
160
deviceClipRect.set(deviceClipIRect);
162
deviceClipRect.setEmpty();
167
static inline SkRect& currentTrackingOpaqueRect(SkRect& rootOpaqueRect, Vector<OpaqueRegionSkia::CanvasLayerState, 3>& canvasLayerStack)
169
// If we are drawing into a canvas layer, then track the opaque rect in that layer.
170
if (!canvasLayerStack.isEmpty())
171
return canvasLayerStack.last().opaqueRect;
172
return rootOpaqueRect;
175
void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint)
177
CanvasLayerState state;
179
state.paint = *paint;
180
m_canvasLayerStack.append(state);
183
void OpaqueRegionSkia::popCanvasLayer(const PlatformContextSkia* context)
185
ASSERT(!m_canvasLayerStack.isEmpty());
186
if (m_canvasLayerStack.isEmpty())
189
const CanvasLayerState& canvasLayer = m_canvasLayerStack.last();
190
SkRect layerOpaqueRect = canvasLayer.opaqueRect;
191
SkPaint layerPaint = canvasLayer.paint;
193
// Apply the image mask.
194
if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect))
195
layerOpaqueRect.setEmpty();
197
m_canvasLayerStack.removeLast();
199
applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint);
202
void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect)
204
ASSERT(!m_canvasLayerStack.isEmpty());
205
m_canvasLayerStack.last().hasImageMask = true;
206
m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect;
209
void OpaqueRegionSkia::didDrawRect(const PlatformContextSkia* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap)
211
// Any stroking may put alpha in pixels even if the filling part does not.
212
if (paint.getStyle() != SkPaint::kFill_Style) {
213
bool fillsBounds = false;
215
if (!paint.canComputeFastBounds())
216
didDrawUnbounded(context, paint, FillOrStroke);
219
strokeRect = paint.computeFastBounds(fillRect, &strokeRect);
220
didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, FillOrStroke);
224
bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style;
225
didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly);
228
void OpaqueRegionSkia::didDrawPath(const PlatformContextSkia* context, const SkPath& path, const SkPaint& paint)
231
if (path.isRect(&rect)) {
232
didDrawRect(context, rect, paint, 0);
236
bool fillsBounds = false;
238
if (!paint.canComputeFastBounds())
239
didDrawUnbounded(context, paint, FillOrStroke);
241
rect = paint.computeFastBounds(path.getBounds(), &rect);
242
didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
246
void OpaqueRegionSkia::didDrawPoints(const PlatformContextSkia* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
252
rect.fLeft = points[0].fX;
253
rect.fRight = points[0].fX + 1;
254
rect.fTop = points[0].fY;
255
rect.fBottom = points[0].fY + 1;
257
for (int i = 1; i < numPoints; ++i) {
258
rect.fLeft = std::min(rect.fLeft, points[i].fX);
259
rect.fRight = std::max(rect.fRight, points[i].fX + 1);
260
rect.fTop = std::min(rect.fTop, points[i].fY);
261
rect.fBottom = std::max(rect.fBottom, points[i].fY + 1);
264
bool fillsBounds = false;
266
if (!paint.canComputeFastBounds())
267
didDrawUnbounded(context, paint, FillOrStroke);
269
rect = paint.computeFastBounds(rect, &rect);
270
didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
274
void OpaqueRegionSkia::didDrawBounded(const PlatformContextSkia* context, const SkRect& bounds, const SkPaint& paint)
276
bool fillsBounds = false;
278
if (!paint.canComputeFastBounds())
279
didDrawUnbounded(context, paint, FillOrStroke);
282
rect = paint.computeFastBounds(bounds, &rect);
283
didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
287
void OpaqueRegionSkia::didDraw(const PlatformContextSkia* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType)
289
SkRect targetRect = rect;
291
// Apply the transform to device coordinate space.
292
SkMatrix canvasTransform = context->canvas()->getTotalMatrix();
293
if (!canvasTransform.mapRect(&targetRect))
296
// Apply the current clip.
297
SkRect deviceClipRect;
298
if (!getDeviceClipAsRect(context, deviceClipRect))
300
else if (!targetRect.intersect(deviceClipRect))
303
bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap);
304
bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque);
305
bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
307
if (fillsBounds && xfersOpaque)
308
markRectAsOpaque(targetRect);
309
else if (!preservesOpaque)
310
markRectAsNonOpaque(targetRect);
313
void OpaqueRegionSkia::didDrawUnbounded(const PlatformContextSkia* context, const SkPaint& paint, DrawType drawType)
315
bool drawsOpaque = paintIsOpaque(paint, drawType, 0);
316
bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
321
SkRect deviceClipRect;
322
getDeviceClipAsRect(context, deviceClipRect);
323
markRectAsNonOpaque(deviceClipRect);
326
void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const PlatformContextSkia* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
328
SkRect deviceClipRect;
329
bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);
331
if (deviceClipRect.isEmpty())
334
SkRect sourceOpaqueRect = layerOpaqueRect;
335
// Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible.
336
SkRect destinationOpaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
338
bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
339
if (!outsideSourceOpaqueRectPreservesOpaque)
340
markRectAsNonOpaque(deviceClipRect);
342
if (!deviceClipIsARect)
344
if (!sourceOpaqueRect.intersect(deviceClipRect))
347
bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
348
bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
349
bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);
351
// If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise,
352
// if it preserves opaque then keep the intersection of the two.
353
if (sourceOpaqueRectXfersOpaque)
354
markRectAsOpaque(sourceOpaqueRect);
355
else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect))
356
markRectAsOpaque(sourceOpaqueRect);
359
void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect)
361
// We want to keep track of an opaque region but bound its complexity at a constant size.
362
// We keep track of the largest rectangle seen by area. If we can add the new rect to this
363
// rectangle then we do that, as that is the cheapest way to increase the area returned
364
// without increasing the complexity.
366
SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
370
if (opaqueRect.contains(rect))
372
if (rect.contains(opaqueRect)) {
377
if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) {
378
if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft)
379
opaqueRect.fLeft = rect.fLeft;
380
if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight)
381
opaqueRect.fRight = rect.fRight;
382
} else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) {
383
if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop)
384
opaqueRect.fTop = rect.fTop;
385
if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom)
386
opaqueRect.fBottom = rect.fBottom;
389
long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height();
390
long area = (long)rect.width() * (long)rect.height();
391
if (area > opaqueArea)
395
void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect)
397
// We want to keep as much of the current opaque rectangle as we can, so find the one largest
398
// rectangle inside m_opaqueRect that does not intersect with |rect|.
400
SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
402
if (!SkRect::Intersects(rect, opaqueRect))
404
if (rect.contains(opaqueRect)) {
405
markAllAsNonOpaque();
409
int deltaLeft = rect.fLeft - opaqueRect.fLeft;
410
int deltaRight = opaqueRect.fRight - rect.fRight;
411
int deltaTop = rect.fTop - opaqueRect.fTop;
412
int deltaBottom = opaqueRect.fBottom - rect.fBottom;
414
// horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside opaqueRect.
415
// vertical is the larger of the two rectangles above or below |rect| and inside opaqueRect.
416
SkRect horizontal = opaqueRect;
417
if (deltaTop > deltaBottom)
418
horizontal.fBottom = rect.fTop;
420
horizontal.fTop = rect.fBottom;
421
SkRect vertical = opaqueRect;
422
if (deltaLeft > deltaRight)
423
vertical.fRight = rect.fLeft;
425
vertical.fLeft = rect.fRight;
427
if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height())
428
opaqueRect = horizontal;
430
opaqueRect = vertical;
433
void OpaqueRegionSkia::markAllAsNonOpaque()
435
SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
436
opaqueRect.setEmpty();
439
} // namespace WebCore