Home | History | Annotate | Download | only in skia
      1 /*
      2  * Copyright (c) 2006,2007,2008, 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/SkiaUtils.h"
     34 
     35 #include "SkColorPriv.h"
     36 #include "SkRegion.h"
     37 #include "platform/graphics/GraphicsContext.h"
     38 #include "platform/graphics/ImageBuffer.h"
     39 
     40 namespace blink {
     41 
     42 static const struct CompositOpToXfermodeMode {
     43     CompositeOperator mCompositOp;
     44     SkXfermode::Mode m_xfermodeMode;
     45 } gMapCompositOpsToXfermodeModes[] = {
     46     { CompositeClear,           SkXfermode::kClear_Mode },
     47     { CompositeCopy,            SkXfermode::kSrc_Mode },
     48     { CompositeSourceOver,      SkXfermode::kSrcOver_Mode },
     49     { CompositeSourceIn,        SkXfermode::kSrcIn_Mode },
     50     { CompositeSourceOut,       SkXfermode::kSrcOut_Mode },
     51     { CompositeSourceAtop,      SkXfermode::kSrcATop_Mode },
     52     { CompositeDestinationOver, SkXfermode::kDstOver_Mode },
     53     { CompositeDestinationIn,   SkXfermode::kDstIn_Mode },
     54     { CompositeDestinationOut,  SkXfermode::kDstOut_Mode },
     55     { CompositeDestinationAtop, SkXfermode::kDstATop_Mode },
     56     { CompositeXOR,             SkXfermode::kXor_Mode },
     57     { CompositePlusDarker,      SkXfermode::kDarken_Mode },
     58     { CompositePlusLighter,     SkXfermode::kPlus_Mode }
     59 };
     60 
     61 // keep this array in sync with WebBlendMode enum in public/platform/WebBlendMode.h
     62 static const SkXfermode::Mode gMapBlendOpsToXfermodeModes[] = {
     63     SkXfermode::kClear_Mode, // WebBlendModeNormal
     64     SkXfermode::kMultiply_Mode, // WebBlendModeMultiply
     65     SkXfermode::kScreen_Mode, // WebBlendModeScreen
     66     SkXfermode::kOverlay_Mode, // WebBlendModeOverlay
     67     SkXfermode::kDarken_Mode, // WebBlendModeDarken
     68     SkXfermode::kLighten_Mode, // WebBlendModeLighten
     69     SkXfermode::kColorDodge_Mode, // WebBlendModeColorDodge
     70     SkXfermode::kColorBurn_Mode, // WebBlendModeColorBurn
     71     SkXfermode::kHardLight_Mode, // WebBlendModeHardLight
     72     SkXfermode::kSoftLight_Mode, // WebBlendModeSoftLight
     73     SkXfermode::kDifference_Mode, // WebBlendModeDifference
     74     SkXfermode::kExclusion_Mode, // WebBlendModeExclusion
     75     SkXfermode::kHue_Mode, // WebBlendModeHue
     76     SkXfermode::kSaturation_Mode, // WebBlendModeSaturation
     77     SkXfermode::kColor_Mode, // WebBlendModeColor
     78     SkXfermode::kLuminosity_Mode // WebBlendModeLuminosity
     79 };
     80 
     81 SkXfermode::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op, WebBlendMode blendMode)
     82 {
     83     if (blendMode != WebBlendModeNormal) {
     84         if (static_cast<uint8_t>(blendMode) >= SK_ARRAY_COUNT(gMapBlendOpsToXfermodeModes)) {
     85             SkDEBUGF(("GraphicsContext::setPlatformCompositeOperation unknown WebBlendMode %d\n", blendMode));
     86             return SkXfermode::kSrcOver_Mode;
     87         }
     88         return gMapBlendOpsToXfermodeModes[static_cast<uint8_t>(blendMode)];
     89     }
     90 
     91     const CompositOpToXfermodeMode* table = gMapCompositOpsToXfermodeModes;
     92     if (static_cast<uint8_t>(op) >= SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes)) {
     93         SkDEBUGF(("GraphicsContext::setPlatformCompositeOperation unknown CompositeOperator %d\n", op));
     94         return SkXfermode::kSrcOver_Mode;
     95     }
     96     SkASSERT(table[static_cast<uint8_t>(op)].mCompositOp == op);
     97     return table[static_cast<uint8_t>(op)].m_xfermodeMode;
     98 }
     99 
    100 static U8CPU InvScaleByte(U8CPU component, uint32_t scale)
    101 {
    102     SkASSERT(component == (uint8_t)component);
    103     return (component * scale + 0x8000) >> 16;
    104 }
    105 
    106 SkColor SkPMColorToColor(SkPMColor pm)
    107 {
    108     if (!pm)
    109         return 0;
    110     unsigned a = SkGetPackedA32(pm);
    111     if (!a) {
    112         // A zero alpha value when there are non-zero R, G, or B channels is an
    113         // invalid premultiplied color (since all channels should have been
    114         // multiplied by 0 if a=0).
    115         SkASSERT(false);
    116         // In production, return 0 to protect against division by zero.
    117         return 0;
    118     }
    119 
    120     uint32_t scale = (255 << 16) / a;
    121 
    122     return SkColorSetARGB(a,
    123                           InvScaleByte(SkGetPackedR32(pm), scale),
    124                           InvScaleByte(SkGetPackedG32(pm), scale),
    125                           InvScaleByte(SkGetPackedB32(pm), scale));
    126 }
    127 
    128 bool SkPathContainsPoint(const SkPath& originalPath, const FloatPoint& point, SkPath::FillType ft)
    129 {
    130     SkRect bounds = originalPath.getBounds();
    131 
    132     // We can immediately return false if the point is outside the bounding
    133     // rect.  We don't use bounds.contains() here, since it would exclude
    134     // points on the right and bottom edges of the bounding rect, and we want
    135     // to include them.
    136     SkScalar fX = SkFloatToScalar(point.x());
    137     SkScalar fY = SkFloatToScalar(point.y());
    138     if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom)
    139         return false;
    140 
    141     // Scale the path to a large size before hit testing for two reasons:
    142     // 1) Skia has trouble with coordinates close to the max signed 16-bit values, so we scale larger paths down.
    143     //    TODO: when Skia is patched to work properly with large values, this will not be necessary.
    144     // 2) Skia does not support analytic hit testing, so we scale paths up to do raster hit testing with subpixel accuracy.
    145     SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop);
    146     if (SkScalarNearlyZero(biggestCoord))
    147         return false;
    148     biggestCoord = std::max(std::max(biggestCoord, fX + 1), fY + 1);
    149 
    150     const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15);
    151     SkScalar scale = SkScalarDiv(kMaxCoordinate, biggestCoord);
    152 
    153     SkRegion rgn;
    154     SkRegion clip;
    155     SkMatrix m;
    156     SkPath scaledPath(originalPath);
    157 
    158     scaledPath.setFillType(ft);
    159     m.setScale(scale, scale);
    160     scaledPath.transform(m, 0);
    161 
    162     int x = static_cast<int>(floorf(0.5f + point.x() * scale));
    163     int y = static_cast<int>(floorf(0.5f + point.y() * scale));
    164     clip.setRect(x - 1, y - 1, x + 1, y + 1);
    165 
    166     return rgn.setPath(scaledPath, clip);
    167 }
    168 
    169 SkMatrix affineTransformToSkMatrix(const AffineTransform& source)
    170 {
    171     SkMatrix result;
    172 
    173     result.setScaleX(WebCoreDoubleToSkScalar(source.a()));
    174     result.setSkewX(WebCoreDoubleToSkScalar(source.c()));
    175     result.setTranslateX(WebCoreDoubleToSkScalar(source.e()));
    176 
    177     result.setScaleY(WebCoreDoubleToSkScalar(source.d()));
    178     result.setSkewY(WebCoreDoubleToSkScalar(source.b()));
    179     result.setTranslateY(WebCoreDoubleToSkScalar(source.f()));
    180 
    181     // FIXME: Set perspective properly.
    182     result.setPerspX(0);
    183     result.setPerspY(0);
    184     result.set(SkMatrix::kMPersp2, SK_Scalar1);
    185 
    186     return result;
    187 }
    188 
    189 bool nearlyIntegral(float value)
    190 {
    191     return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon();
    192 }
    193 
    194 InterpolationQuality limitInterpolationQuality(const GraphicsContext* context, InterpolationQuality resampling)
    195 {
    196     return std::min(resampling, context->imageInterpolationQuality());
    197 }
    198 
    199 InterpolationQuality computeInterpolationQuality(
    200     const SkMatrix& matrix,
    201     float srcWidth,
    202     float srcHeight,
    203     float destWidth,
    204     float destHeight,
    205     bool isDataComplete)
    206 {
    207     // The percent change below which we will not resample. This usually means
    208     // an off-by-one error on the web page, and just doing nearest neighbor
    209     // sampling is usually good enough.
    210     const float kFractionalChangeThreshold = 0.025f;
    211 
    212     // Images smaller than this in either direction are considered "small" and
    213     // are not resampled ever (see below).
    214     const int kSmallImageSizeThreshold = 8;
    215 
    216     // The amount an image can be stretched in a single direction before we
    217     // say that it is being stretched so much that it must be a line or
    218     // background that doesn't need resampling.
    219     const float kLargeStretch = 3.0f;
    220 
    221     // Figure out if we should resample this image. We try to prune out some
    222     // common cases where resampling won't give us anything, since it is much
    223     // slower than drawing stretched.
    224     float diffWidth = fabs(destWidth - srcWidth);
    225     float diffHeight = fabs(destHeight - srcHeight);
    226     bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon();
    227     bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon();
    228     // We don't need to resample if the source and destination are the same.
    229     if (widthNearlyEqual && heightNearlyEqual)
    230         return InterpolationNone;
    231 
    232     if (srcWidth <= kSmallImageSizeThreshold
    233         || srcHeight <= kSmallImageSizeThreshold
    234         || destWidth <= kSmallImageSizeThreshold
    235         || destHeight <= kSmallImageSizeThreshold) {
    236         // Small image detected.
    237 
    238         // Resample in the case where the new size would be non-integral.
    239         // This can cause noticeable breaks in repeating patterns, except
    240         // when the source image is only one pixel wide in that dimension.
    241         if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits<float>::epsilon())
    242             || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limits<float>::epsilon()))
    243             return InterpolationLow;
    244 
    245         // Otherwise, don't resample small images. These are often used for
    246         // borders and rules (think 1x1 images used to make lines).
    247         return InterpolationNone;
    248     }
    249 
    250     if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
    251         // Large image detected.
    252 
    253         // Don't resample if it is being stretched a lot in only one direction.
    254         // This is trying to catch cases where somebody has created a border
    255         // (which might be large) and then is stretching it to fill some part
    256         // of the page.
    257         if (widthNearlyEqual || heightNearlyEqual)
    258             return InterpolationNone;
    259 
    260         // The image is growing a lot and in more than one direction. Resampling
    261         // is slow and doesn't give us very much when growing a lot.
    262         return InterpolationLow;
    263     }
    264 
    265     if ((diffWidth / srcWidth < kFractionalChangeThreshold)
    266         && (diffHeight / srcHeight < kFractionalChangeThreshold)) {
    267         // It is disappointingly common on the web for image sizes to be off by
    268         // one or two pixels. We don't bother resampling if the size difference
    269         // is a small fraction of the original size.
    270         return InterpolationNone;
    271     }
    272 
    273     // When the image is not yet done loading, use linear. We don't cache the
    274     // partially resampled images, and as they come in incrementally, it causes
    275     // us to have to resample the whole thing every time.
    276     if (!isDataComplete)
    277         return InterpolationLow;
    278 
    279     // Everything else gets resampled.
    280     // High quality interpolation only enabled for scaling and translation.
    281     if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
    282         return InterpolationHigh;
    283 
    284     return InterpolationLow;
    285 }
    286 
    287 
    288 bool shouldDrawAntiAliased(const GraphicsContext* context, const SkRect& destRect)
    289 {
    290     if (!context->shouldAntialias())
    291         return false;
    292     const SkMatrix totalMatrix = context->getTotalMatrix();
    293     // Don't disable anti-aliasing if we're rotated or skewed.
    294     if (!totalMatrix.rectStaysRect())
    295         return true;
    296     // Disable anti-aliasing for scales or n*90 degree rotations.
    297     // Allow to opt out of the optimization though for "hairline" geometry
    298     // images - using the shouldAntialiasHairlineImages() GraphicsContext flag.
    299     if (!context->shouldAntialiasHairlineImages())
    300         return false;
    301     // Check if the dimensions of the destination are "small" (less than one
    302     // device pixel). To prevent sudden drop-outs. Since we know that
    303     // kRectStaysRect_Mask is set, the matrix either has scale and no skew or
    304     // vice versa. We can query the kAffine_Mask flag to determine which case
    305     // it is.
    306     // FIXME: This queries the CTM while drawing, which is generally
    307     // discouraged. Always drawing with AA can negatively impact performance
    308     // though - that's why it's not always on.
    309     SkScalar widthExpansion, heightExpansion;
    310     if (totalMatrix.getType() & SkMatrix::kAffine_Mask)
    311         widthExpansion = totalMatrix[SkMatrix::kMSkewY], heightExpansion = totalMatrix[SkMatrix::kMSkewX];
    312     else
    313         widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = totalMatrix[SkMatrix::kMScaleY];
    314     return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1;
    315 }
    316 
    317 }  // namespace blink
    318