~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/platform/graphics/skia/OpaqueRegionSkia.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "OpaqueRegionSkia.h"
 
34
 
 
35
#include "PlatformContextSkia.h"
 
36
 
 
37
#include "SkCanvas.h"
 
38
#include "SkColorFilter.h"
 
39
#include "SkShader.h"
 
40
 
 
41
namespace WebCore {
 
42
 
 
43
OpaqueRegionSkia::OpaqueRegionSkia()
 
44
    : m_opaqueRect(SkRect::MakeEmpty())
 
45
{
 
46
}
 
47
 
 
48
OpaqueRegionSkia::~OpaqueRegionSkia()
 
49
{
 
50
}
 
51
 
 
52
IntRect OpaqueRegionSkia::asRect() const
 
53
{
 
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);
 
60
}
 
61
 
 
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)
 
64
{
 
65
    if (!srcIsOpaque)
 
66
        return false;
 
67
 
 
68
    SkXfermode* xfermode = paint.getXfermode();
 
69
    if (!xfermode)
 
70
        return true; // default to kSrcOver_Mode
 
71
    SkXfermode::Mode mode;
 
72
    if (!xfermode->asMode(&mode))
 
73
        return false;
 
74
 
 
75
    switch (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
 
82
        return true;
 
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)
 
91
        return false;
 
92
    }
 
93
}
 
94
 
 
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)
 
97
{
 
98
    SkXfermode* xfermode = paint.getXfermode();
 
99
    if (!xfermode)
 
100
        return true; // default to kSrcOver_Mode
 
101
    SkXfermode::Mode mode;
 
102
    if (!xfermode->asMode(&mode))
 
103
        return false;
 
104
 
 
105
    switch (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
 
112
        return true;
 
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)
 
117
        return false;
 
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
 
122
        return srcIsOpaque;
 
123
    }
 
124
}
 
125
 
 
126
// Returns true if all pixels painted will be opaque.
 
127
static inline bool paintIsOpaque(const SkPaint& paint, OpaqueRegionSkia::DrawType drawType, const SkBitmap* bitmap)
 
128
{
 
129
    if (paint.getAlpha() < 0xFF)
 
130
        return false;
 
131
    bool checkFillOnly = drawType != OpaqueRegionSkia::FillOrStroke;
 
132
    if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias())
 
133
        return false;
 
134
    SkShader* shader = paint.getShader();
 
135
    if (shader && !shader->isOpaque())
 
136
        return false;
 
137
    if (bitmap && !bitmap->isOpaque())
 
138
        return false;
 
139
    if (paint.getLooper())
 
140
        return false;
 
141
    if (paint.getImageFilter())
 
142
        return false;
 
143
    if (paint.getMaskFilter())
 
144
        return false;
 
145
    SkColorFilter* colorFilter = paint.getColorFilter();
 
146
    if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag))
 
147
        return false;
 
148
    return true;
 
149
}
 
150
 
 
151
// Returns true if there is a rectangular clip, with the result in |deviceClipRect|.
 
152
static inline bool getDeviceClipAsRect(const PlatformContextSkia* context, SkRect& deviceClipRect)
 
153
{
 
154
    // Get the current clip in device coordinate space.
 
155
    if (context->canvas()->getClipType() != SkCanvas::kRect_ClipType)
 
156
        return false;
 
157
 
 
158
    SkIRect deviceClipIRect;
 
159
    if (context->canvas()->getClipDeviceBounds(&deviceClipIRect))
 
160
        deviceClipRect.set(deviceClipIRect);
 
161
    else
 
162
        deviceClipRect.setEmpty();
 
163
 
 
164
    return true;
 
165
}
 
166
 
 
167
static inline SkRect& currentTrackingOpaqueRect(SkRect& rootOpaqueRect, Vector<OpaqueRegionSkia::CanvasLayerState, 3>& canvasLayerStack)
 
168
{
 
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;
 
173
}
 
174
 
 
175
void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint)
 
176
{
 
177
    CanvasLayerState state;
 
178
    if (paint)
 
179
        state.paint = *paint;
 
180
    m_canvasLayerStack.append(state);
 
181
}
 
182
 
 
183
void OpaqueRegionSkia::popCanvasLayer(const PlatformContextSkia* context)
 
184
{
 
185
    ASSERT(!m_canvasLayerStack.isEmpty());
 
186
    if (m_canvasLayerStack.isEmpty())
 
187
        return;
 
188
 
 
189
    const CanvasLayerState& canvasLayer = m_canvasLayerStack.last();
 
190
    SkRect layerOpaqueRect = canvasLayer.opaqueRect;
 
191
    SkPaint layerPaint = canvasLayer.paint;
 
192
 
 
193
    // Apply the image mask.
 
194
    if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect))
 
195
        layerOpaqueRect.setEmpty();
 
196
 
 
197
    m_canvasLayerStack.removeLast();
 
198
 
 
199
    applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint);
 
200
}
 
201
 
 
202
void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect)
 
203
{
 
204
    ASSERT(!m_canvasLayerStack.isEmpty());
 
205
    m_canvasLayerStack.last().hasImageMask = true;
 
206
    m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect;
 
207
}
 
208
 
 
209
void OpaqueRegionSkia::didDrawRect(const PlatformContextSkia* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap)
 
210
{
 
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;
 
214
 
 
215
        if (!paint.canComputeFastBounds())
 
216
            didDrawUnbounded(context, paint, FillOrStroke);
 
217
        else {
 
218
            SkRect strokeRect;
 
219
            strokeRect = paint.computeFastBounds(fillRect, &strokeRect);
 
220
            didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, FillOrStroke);
 
221
        }
 
222
    }
 
223
 
 
224
    bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style;
 
225
    didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly);
 
226
}
 
227
 
 
228
void OpaqueRegionSkia::didDrawPath(const PlatformContextSkia* context, const SkPath& path, const SkPaint& paint)
 
229
{
 
230
    SkRect rect;
 
231
    if (path.isRect(&rect)) {
 
232
        didDrawRect(context, rect, paint, 0);
 
233
        return;
 
234
    }
 
235
 
 
236
    bool fillsBounds = false;
 
237
 
 
238
    if (!paint.canComputeFastBounds())
 
239
        didDrawUnbounded(context, paint, FillOrStroke);
 
240
    else {
 
241
        rect = paint.computeFastBounds(path.getBounds(), &rect);
 
242
        didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
 
243
    }
 
244
}
 
245
 
 
246
void OpaqueRegionSkia::didDrawPoints(const PlatformContextSkia* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
 
247
{
 
248
    if (!numPoints)
 
249
        return;
 
250
 
 
251
    SkRect rect;
 
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;
 
256
 
 
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);
 
262
    }
 
263
 
 
264
    bool fillsBounds = false;
 
265
 
 
266
    if (!paint.canComputeFastBounds())
 
267
        didDrawUnbounded(context, paint, FillOrStroke);
 
268
    else {
 
269
        rect = paint.computeFastBounds(rect, &rect);
 
270
        didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
 
271
    }
 
272
}
 
273
 
 
274
void OpaqueRegionSkia::didDrawBounded(const PlatformContextSkia* context, const SkRect& bounds, const SkPaint& paint)
 
275
{
 
276
    bool fillsBounds = false;
 
277
 
 
278
    if (!paint.canComputeFastBounds())
 
279
        didDrawUnbounded(context, paint, FillOrStroke);
 
280
    else {
 
281
        SkRect rect;
 
282
        rect = paint.computeFastBounds(bounds, &rect);
 
283
        didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
 
284
    }
 
285
}
 
286
 
 
287
void OpaqueRegionSkia::didDraw(const PlatformContextSkia* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType)
 
288
{
 
289
    SkRect targetRect = rect;
 
290
 
 
291
    // Apply the transform to device coordinate space.
 
292
    SkMatrix canvasTransform = context->canvas()->getTotalMatrix();
 
293
    if (!canvasTransform.mapRect(&targetRect))
 
294
        fillsBounds = false;
 
295
 
 
296
    // Apply the current clip.
 
297
    SkRect deviceClipRect;
 
298
    if (!getDeviceClipAsRect(context, deviceClipRect))
 
299
        fillsBounds = false;
 
300
    else if (!targetRect.intersect(deviceClipRect))
 
301
        return;
 
302
 
 
303
    bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap);
 
304
    bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque);
 
305
    bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
 
306
 
 
307
    if (fillsBounds && xfersOpaque)
 
308
        markRectAsOpaque(targetRect);
 
309
    else if (!preservesOpaque)
 
310
        markRectAsNonOpaque(targetRect);
 
311
}
 
312
 
 
313
void OpaqueRegionSkia::didDrawUnbounded(const PlatformContextSkia* context, const SkPaint& paint, DrawType drawType)
 
314
{
 
315
    bool drawsOpaque = paintIsOpaque(paint, drawType, 0);
 
316
    bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
 
317
 
 
318
    if (preservesOpaque)
 
319
        return;
 
320
 
 
321
    SkRect deviceClipRect;
 
322
    getDeviceClipAsRect(context, deviceClipRect);
 
323
    markRectAsNonOpaque(deviceClipRect);
 
324
}
 
325
 
 
326
void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const PlatformContextSkia* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
 
327
{
 
328
    SkRect deviceClipRect;
 
329
    bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);
 
330
 
 
331
    if (deviceClipRect.isEmpty())
 
332
        return;
 
333
 
 
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);
 
337
 
 
338
    bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
 
339
    if (!outsideSourceOpaqueRectPreservesOpaque)
 
340
        markRectAsNonOpaque(deviceClipRect);
 
341
 
 
342
    if (!deviceClipIsARect)
 
343
        return;
 
344
    if (!sourceOpaqueRect.intersect(deviceClipRect))
 
345
        return;
 
346
 
 
347
    bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
 
348
    bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
 
349
    bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);
 
350
 
 
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);
 
357
}
 
358
 
 
359
void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect)
 
360
{
 
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.
 
365
 
 
366
    SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
 
367
 
 
368
    if (rect.isEmpty())
 
369
        return;
 
370
    if (opaqueRect.contains(rect))
 
371
        return;
 
372
    if (rect.contains(opaqueRect)) {
 
373
        opaqueRect = rect;
 
374
        return;
 
375
    }
 
376
 
 
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;
 
387
    }
 
388
 
 
389
    long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height();
 
390
    long area = (long)rect.width() * (long)rect.height();
 
391
    if (area > opaqueArea)
 
392
        opaqueRect = rect;
 
393
}
 
394
 
 
395
void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect)
 
396
{
 
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|.
 
399
 
 
400
    SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
 
401
 
 
402
    if (!SkRect::Intersects(rect, opaqueRect))
 
403
        return;
 
404
    if (rect.contains(opaqueRect)) {
 
405
        markAllAsNonOpaque();
 
406
        return;
 
407
    }
 
408
 
 
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;
 
413
 
 
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;
 
419
    else
 
420
        horizontal.fTop = rect.fBottom;
 
421
    SkRect vertical = opaqueRect;
 
422
    if (deltaLeft > deltaRight)
 
423
        vertical.fRight = rect.fLeft;
 
424
    else
 
425
        vertical.fLeft = rect.fRight;
 
426
 
 
427
    if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height())
 
428
        opaqueRect = horizontal;
 
429
    else
 
430
        opaqueRect = vertical;
 
431
}
 
432
 
 
433
void OpaqueRegionSkia::markAllAsNonOpaque()
 
434
{
 
435
    SkRect& opaqueRect = currentTrackingOpaqueRect(m_opaqueRect, m_canvasLayerStack);
 
436
    opaqueRect.setEmpty();
 
437
}
 
438
 
 
439
} // namespace WebCore