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 "SkiaUtils.h" 34 35#include "ImageBuffer.h" 36#include "SharedBuffer.h" 37#include "SkCanvas.h" 38#include "SkColorPriv.h" 39#include "SkMatrix.h" 40#include "SkRegion.h" 41#include "SkUnPreMultiply.h" 42 43#if PLATFORM(ANDROID) 44#include "GraphicsContext.h" 45#include "PlatformGraphicsContextSkia.h" 46#endif 47 48namespace WebCore { 49 50#if PLATFORM(ANDROID) 51static const struct CompositOpToSkiaMode { 52 uint8_t mCompositOp; 53 uint8_t mMode; 54} gMapCompositOpsToSkiaModes[] = { 55 { CompositeClear, SkXfermode::kClear_Mode }, 56 { CompositeCopy, SkXfermode::kSrc_Mode }, 57 { CompositeSourceOver, SkXfermode::kSrcOver_Mode }, 58 { CompositeSourceIn, SkXfermode::kSrcIn_Mode }, 59 { CompositeSourceOut, SkXfermode::kSrcOut_Mode }, 60 { CompositeSourceAtop, SkXfermode::kSrcATop_Mode }, 61 { CompositeDestinationOver, SkXfermode::kDstOver_Mode }, 62 { CompositeDestinationIn, SkXfermode::kDstIn_Mode }, 63 { CompositeDestinationOut, SkXfermode::kDstOut_Mode }, 64 { CompositeDestinationAtop, SkXfermode::kDstATop_Mode }, 65 { CompositeXOR, SkXfermode::kXor_Mode }, 66 // need more details on the composite modes to be sure these are right 67 { CompositePlusDarker, SkXfermode::kDarken_Mode }, 68 { CompositeHighlight, SkXfermode::kSrcOver_Mode }, // TODO 69 { CompositePlusLighter, SkXfermode::kPlus_Mode } 70}; 71 72SkXfermode::Mode WebCoreCompositeToSkiaCOmposite(CompositeOperator op) 73{ 74 const CompositOpToSkiaMode* table = gMapCompositOpsToSkiaModes; 75 76 for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToSkiaModes); i++) { 77 if (table[i].mCompositOp == op) 78 return (SkXfermode::Mode)table[i].mMode; 79 } 80 81 SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositeOperator %d\n", op)); 82 return SkXfermode::kSrcOver_Mode; // fall-back 83} 84 85#endif 86 87static const struct CompositOpToXfermodeMode { 88 uint8_t mCompositOp; 89 uint8_t m_xfermodeMode; 90} gMapCompositOpsToXfermodeModes[] = { 91 { CompositeClear, SkXfermode::kClear_Mode }, 92 { CompositeCopy, SkXfermode::kSrc_Mode }, 93 { CompositeSourceOver, SkXfermode::kSrcOver_Mode }, 94 { CompositeSourceIn, SkXfermode::kSrcIn_Mode }, 95 { CompositeSourceOut, SkXfermode::kSrcOut_Mode }, 96 { CompositeSourceAtop, SkXfermode::kSrcATop_Mode }, 97 { CompositeDestinationOver, SkXfermode::kDstOver_Mode }, 98 { CompositeDestinationIn, SkXfermode::kDstIn_Mode }, 99 { CompositeDestinationOut, SkXfermode::kDstOut_Mode }, 100 { CompositeDestinationAtop, SkXfermode::kDstATop_Mode }, 101 { CompositeXOR, SkXfermode::kXor_Mode }, 102 { CompositePlusDarker, SkXfermode::kDarken_Mode }, 103 { CompositeHighlight, SkXfermode::kSrcOver_Mode }, // TODO 104 { CompositePlusLighter, SkXfermode::kPlus_Mode } 105}; 106 107SkXfermode::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op) 108{ 109 const CompositOpToXfermodeMode* table = gMapCompositOpsToXfermodeModes; 110 111 for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes); i++) { 112 if (table[i].mCompositOp == op) 113 return (SkXfermode::Mode)table[i].m_xfermodeMode; 114 } 115 116 SkDEBUGF(("GraphicsContext::setPlatformCompositeOperation unknown CompositeOperator %d\n", op)); 117 return SkXfermode::kSrcOver_Mode; // fall-back 118} 119 120#if PLATFORM(ANDROID) 121Color SkPMColorToWebCoreColor(SkPMColor pm) 122{ 123 SkColor c = SkUnPreMultiply::PMColorToColor(pm); 124 // need the cast to find the right constructor 125 return WebCore::Color((int)SkColorGetR(c), (int)SkColorGetG(c), 126 (int)SkColorGetB(c), (int)SkColorGetA(c)); 127} 128#else 129static U8CPU InvScaleByte(U8CPU component, uint32_t scale) 130{ 131 SkASSERT(component == (uint8_t)component); 132 return (component * scale + 0x8000) >> 16; 133} 134 135SkColor SkPMColorToColor(SkPMColor pm) 136{ 137 if (!pm) 138 return 0; 139 unsigned a = SkGetPackedA32(pm); 140 if (!a) { 141 // A zero alpha value when there are non-zero R, G, or B channels is an 142 // invalid premultiplied color (since all channels should have been 143 // multiplied by 0 if a=0). 144 SkASSERT(false); 145 // In production, return 0 to protect against division by zero. 146 return 0; 147 } 148 149 uint32_t scale = (255 << 16) / a; 150 151 return SkColorSetARGB(a, 152 InvScaleByte(SkGetPackedR32(pm), scale), 153 InvScaleByte(SkGetPackedG32(pm), scale), 154 InvScaleByte(SkGetPackedB32(pm), scale)); 155} 156 157Color SkPMColorToWebCoreColor(SkPMColor pm) 158{ 159 return SkPMColorToColor(pm); 160} 161#endif 162 163void IntersectRectAndRegion(const SkRegion& region, const SkRect& srcRect, SkRect* destRect) { 164 // The cliperator requires an int rect, so we round out. 165 SkIRect srcRectRounded; 166 srcRect.roundOut(&srcRectRounded); 167 168 // The Cliperator will iterate over a bunch of rects where our transformed 169 // rect and the clipping region (which may be non-square) overlap. 170 SkRegion::Cliperator cliperator(region, srcRectRounded); 171 if (cliperator.done()) { 172 destRect->setEmpty(); 173 return; 174 } 175 176 // Get the union of all visible rects in the clip that overlap our bitmap. 177 SkIRect currentVisibleRect = cliperator.rect(); 178 cliperator.next(); 179 while (!cliperator.done()) { 180 currentVisibleRect.join(cliperator.rect()); 181 cliperator.next(); 182 } 183 184 destRect->set(currentVisibleRect); 185} 186 187void ClipRectToCanvas(const SkCanvas& canvas, const SkRect& srcRect, SkRect* destRect) { 188 // Translate into the canvas' coordinate space. This is where the clipping 189 // region applies. 190 SkRect transformedSrc; 191 canvas.getTotalMatrix().mapRect(&transformedSrc, srcRect); 192 193 // Do the intersection. 194 SkRect transformedDest; 195 IntersectRectAndRegion(canvas.getTotalClip(), transformedSrc, &transformedDest); 196 197 // Now transform it back into world space. 198 SkMatrix inverseTransform; 199 canvas.getTotalMatrix().invert(&inverseTransform); 200 inverseTransform.mapRect(destRect, transformedDest); 201} 202 203bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) 204{ 205 SkRegion rgn; 206 SkRegion clip; 207 208 SkPath::FillType originalFillType = originalPath->getFillType(); 209 210 const SkPath* path = originalPath; 211 SkPath scaledPath; 212 int scale = 1; 213 214 SkRect bounds = originalPath->getBounds(); 215 216 // We can immediately return false if the point is outside the bounding 217 // rect. We don't use bounds.contains() here, since it would exclude 218 // points on the right and bottom edges of the bounding rect, and we want 219 // to include them. 220 SkScalar fX = SkFloatToScalar(point.x()); 221 SkScalar fY = SkFloatToScalar(point.y()); 222 if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop || fY > bounds.fBottom) 223 return false; 224 225 originalPath->setFillType(ft); 226 227 // Skia has trouble with coordinates close to the max signed 16-bit values 228 // If we have those, we need to scale. 229 // 230 // TODO: remove this code once Skia is patched to work properly with large 231 // values 232 const SkScalar kMaxCoordinate = SkIntToScalar(1<<15); 233 SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); 234 235 if (biggestCoord > kMaxCoordinate) { 236 scale = SkScalarCeil(SkScalarDiv(biggestCoord, kMaxCoordinate)); 237 238 SkMatrix m; 239 m.setScale(SkScalarInvert(SkIntToScalar(scale)), SkScalarInvert(SkIntToScalar(scale))); 240 originalPath->transform(m, &scaledPath); 241 path = &scaledPath; 242 } 243 244 int x = static_cast<int>(floorf(point.x() / scale)); 245 int y = static_cast<int>(floorf(point.y() / scale)); 246 clip.setRect(x - 1, y - 1, x + 1, y + 1); 247 248 bool contains = rgn.setPath(*path, clip); 249 250 originalPath->setFillType(originalFillType); 251 return contains; 252} 253 254#if PLATFORM(ANDROID) 255GraphicsContext* scratchContext() 256{ 257 static GraphicsContext* scratch = 0; 258 if (!scratch) { 259 SkBitmap bm; 260 bm.setConfig(SkBitmap::kNo_Config, 1, 1); 261 SkCanvas* canvas = new SkCanvas(bm); 262 PlatformGraphicsContextSkia* pgc = new PlatformGraphicsContextSkia(canvas); 263 scratch = new GraphicsContext(pgc); 264 } 265 return scratch; 266} 267#else 268GraphicsContext* scratchContext() 269{ 270 static ImageBuffer* scratch = ImageBuffer::create(IntSize(1, 1)).leakPtr(); 271 // We don't bother checking for failure creating the ImageBuffer, since our 272 // ImageBuffer initializer won't fail. 273 return scratch->context(); 274} 275#endif 276 277} // namespace WebCore 278