Home | History | Annotate | Download | only in skia
      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 
     42 OpaqueRegionSkia::OpaqueRegionSkia()
     43     : m_opaqueRect(SkRect::MakeEmpty())
     44 {
     45 }
     46 
     47 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.
     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.
     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.
    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|.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    430 void OpaqueRegionSkia::markAllAsNonOpaque()
    431 {
    432     SkRect& opaqueRect = currentTrackingOpaqueRect();
    433     opaqueRect.setEmpty();
    434 }
    435 
    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