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     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.
     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.
     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.
    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|.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    420 void OpaqueRegionSkia::markAllAsNonOpaque()
    421 {
    422     SkRect& opaqueRect = currentTrackingOpaqueRect();
    423     opaqueRect.setEmpty();
    424 }
    425 
    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