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 "core/platform/graphics/skia/OpaqueRegionSkia.h"
     34 
     35 #include "core/platform/graphics/GraphicsContext.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 GraphicsContext* 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 void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint)
    168 {
    169     CanvasLayerState state;
    170     if (paint)
    171         state.paint = *paint;
    172     m_canvasLayerStack.append(state);
    173 }
    174 
    175 void OpaqueRegionSkia::popCanvasLayer(const GraphicsContext* context)
    176 {
    177     ASSERT(!m_canvasLayerStack.isEmpty());
    178     if (m_canvasLayerStack.isEmpty())
    179         return;
    180 
    181     const CanvasLayerState& canvasLayer = m_canvasLayerStack.last();
    182     SkRect layerOpaqueRect = canvasLayer.opaqueRect;
    183     SkPaint layerPaint = canvasLayer.paint;
    184 
    185     // Apply the image mask.
    186     if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect))
    187         layerOpaqueRect.setEmpty();
    188 
    189     m_canvasLayerStack.removeLast();
    190 
    191     applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint);
    192 }
    193 
    194 void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect)
    195 {
    196     ASSERT(!m_canvasLayerStack.isEmpty());
    197     m_canvasLayerStack.last().hasImageMask = true;
    198     m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect;
    199 }
    200 
    201 void OpaqueRegionSkia::didDrawRect(const GraphicsContext* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap)
    202 {
    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     SkRect rect;
    223     if (path.isRect(&rect)) {
    224         didDrawRect(context, rect, paint, 0);
    225         return;
    226     }
    227 
    228     bool fillsBounds = false;
    229 
    230     if (!paint.canComputeFastBounds())
    231         didDrawUnbounded(context, paint, FillOrStroke);
    232     else {
    233         rect = paint.computeFastBounds(path.getBounds(), &rect);
    234         didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
    235     }
    236 }
    237 
    238 void OpaqueRegionSkia::didDrawPoints(const GraphicsContext* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
    239 {
    240     if (!numPoints)
    241         return;
    242 
    243     SkRect rect;
    244     rect.fLeft = points[0].fX;
    245     rect.fRight = points[0].fX + 1;
    246     rect.fTop = points[0].fY;
    247     rect.fBottom = points[0].fY + 1;
    248 
    249     for (int i = 1; i < numPoints; ++i) {
    250         rect.fLeft = std::min(rect.fLeft, points[i].fX);
    251         rect.fRight = std::max(rect.fRight, points[i].fX + 1);
    252         rect.fTop = std::min(rect.fTop, points[i].fY);
    253         rect.fBottom = std::max(rect.fBottom, points[i].fY + 1);
    254     }
    255 
    256     bool fillsBounds = false;
    257 
    258     if (!paint.canComputeFastBounds())
    259         didDrawUnbounded(context, paint, FillOrStroke);
    260     else {
    261         rect = paint.computeFastBounds(rect, &rect);
    262         didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
    263     }
    264 }
    265 
    266 void OpaqueRegionSkia::didDrawBounded(const GraphicsContext* context, const SkRect& bounds, const SkPaint& paint)
    267 {
    268     bool fillsBounds = false;
    269 
    270     if (!paint.canComputeFastBounds())
    271         didDrawUnbounded(context, paint, FillOrStroke);
    272     else {
    273         SkRect rect;
    274         rect = paint.computeFastBounds(bounds, &rect);
    275         didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
    276     }
    277 }
    278 
    279 void OpaqueRegionSkia::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType)
    280 {
    281     SkRect targetRect = rect;
    282 
    283     // Apply the transform to device coordinate space.
    284     SkMatrix canvasTransform = context->canvas()->getTotalMatrix();
    285     if (!canvasTransform.mapRect(&targetRect))
    286         fillsBounds = false;
    287 
    288     // Apply the current clip.
    289     SkRect deviceClipRect;
    290     if (!getDeviceClipAsRect(context, deviceClipRect))
    291         fillsBounds = false;
    292     else if (!targetRect.intersect(deviceClipRect))
    293         return;
    294 
    295     bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap);
    296     bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque);
    297     bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
    298 
    299     if (fillsBounds && xfersOpaque)
    300         markRectAsOpaque(targetRect);
    301     else if (!preservesOpaque)
    302         markRectAsNonOpaque(targetRect);
    303 }
    304 
    305 void OpaqueRegionSkia::didDrawUnbounded(const GraphicsContext* context, const SkPaint& paint, DrawType drawType)
    306 {
    307     bool drawsOpaque = paintIsOpaque(paint, drawType, 0);
    308     bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
    309 
    310     if (preservesOpaque)
    311         return;
    312 
    313     SkRect deviceClipRect;
    314     getDeviceClipAsRect(context, deviceClipRect);
    315     markRectAsNonOpaque(deviceClipRect);
    316 }
    317 
    318 void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
    319 {
    320     SkRect deviceClipRect;
    321     bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);
    322 
    323     if (deviceClipRect.isEmpty())
    324         return;
    325 
    326     SkRect sourceOpaqueRect = layerOpaqueRect;
    327     // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible.
    328     SkRect destinationOpaqueRect = currentTrackingOpaqueRect();
    329 
    330     bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
    331     if (!outsideSourceOpaqueRectPreservesOpaque)
    332         markRectAsNonOpaque(deviceClipRect);
    333 
    334     if (!deviceClipIsARect)
    335         return;
    336     if (!sourceOpaqueRect.intersect(deviceClipRect))
    337         return;
    338 
    339     bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
    340     bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
    341     bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);
    342 
    343     // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise,
    344     // if it preserves opaque then keep the intersection of the two.
    345     if (sourceOpaqueRectXfersOpaque)
    346         markRectAsOpaque(sourceOpaqueRect);
    347     else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect))
    348         markRectAsOpaque(sourceOpaqueRect);
    349 }
    350 
    351 void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect)
    352 {
    353     // We want to keep track of an opaque region but bound its complexity at a constant size.
    354     // We keep track of the largest rectangle seen by area. If we can add the new rect to this
    355     // rectangle then we do that, as that is the cheapest way to increase the area returned
    356     // without increasing the complexity.
    357 
    358     SkRect& opaqueRect = currentTrackingOpaqueRect();
    359 
    360     if (rect.isEmpty())
    361         return;
    362     if (opaqueRect.contains(rect))
    363         return;
    364     if (rect.contains(opaqueRect)) {
    365         opaqueRect = rect;
    366         return;
    367     }
    368 
    369     if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) {
    370         if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft)
    371             opaqueRect.fLeft = rect.fLeft;
    372         if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight)
    373             opaqueRect.fRight = rect.fRight;
    374     } else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) {
    375         if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop)
    376             opaqueRect.fTop = rect.fTop;
    377         if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom)
    378             opaqueRect.fBottom = rect.fBottom;
    379     }
    380 
    381     long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height();
    382     long area = (long)rect.width() * (long)rect.height();
    383     if (area > opaqueArea)
    384         opaqueRect = rect;
    385 }
    386 
    387 void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect)
    388 {
    389     // We want to keep as much of the current opaque rectangle as we can, so find the one largest
    390     // rectangle inside m_opaqueRect that does not intersect with |rect|.
    391 
    392     SkRect& opaqueRect = currentTrackingOpaqueRect();
    393 
    394     if (!SkRect::Intersects(rect, opaqueRect))
    395         return;
    396     if (rect.contains(opaqueRect)) {
    397         markAllAsNonOpaque();
    398         return;
    399     }
    400 
    401     int deltaLeft = rect.fLeft - opaqueRect.fLeft;
    402     int deltaRight = opaqueRect.fRight - rect.fRight;
    403     int deltaTop = rect.fTop - opaqueRect.fTop;
    404     int deltaBottom = opaqueRect.fBottom - rect.fBottom;
    405 
    406     // horizontal is the larger of the two rectangles to the left or to the right of |rect| and inside opaqueRect.
    407     // vertical is the larger of the two rectangles above or below |rect| and inside opaqueRect.
    408     SkRect horizontal = opaqueRect;
    409     if (deltaTop > deltaBottom)
    410         horizontal.fBottom = rect.fTop;
    411     else
    412         horizontal.fTop = rect.fBottom;
    413     SkRect vertical = opaqueRect;
    414     if (deltaLeft > deltaRight)
    415         vertical.fRight = rect.fLeft;
    416     else
    417         vertical.fLeft = rect.fRight;
    418 
    419     if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height())
    420         opaqueRect = horizontal;
    421     else
    422         opaqueRect = vertical;
    423 }
    424 
    425 void OpaqueRegionSkia::markAllAsNonOpaque()
    426 {
    427     SkRect& opaqueRect = currentTrackingOpaqueRect();
    428     opaqueRect.setEmpty();
    429 }
    430 
    431 SkRect& OpaqueRegionSkia::currentTrackingOpaqueRect()
    432 {
    433     // If we are drawing into a canvas layer, then track the opaque rect in that layer.
    434     return m_canvasLayerStack.isEmpty() ? m_opaqueRect : m_canvasLayerStack.last().opaqueRect;
    435 }
    436 
    437 } // namespace WebCore
    438