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
48 namespace WebCore {
49
50 #if PLATFORM(ANDROID)
51 static 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
WebCoreCompositeToSkiaCOmposite(CompositeOperator op)72 SkXfermode::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
87 static 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
WebCoreCompositeToSkiaComposite(CompositeOperator op)107 SkXfermode::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)
SkPMColorToWebCoreColor(SkPMColor pm)121 Color 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
InvScaleByte(U8CPU component,uint32_t scale)129 static U8CPU InvScaleByte(U8CPU component, uint32_t scale)
130 {
131 SkASSERT(component == (uint8_t)component);
132 return (component * scale + 0x8000) >> 16;
133 }
134
SkPMColorToColor(SkPMColor pm)135 SkColor 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
SkPMColorToWebCoreColor(SkPMColor pm)157 Color SkPMColorToWebCoreColor(SkPMColor pm)
158 {
159 return SkPMColorToColor(pm);
160 }
161 #endif
162
IntersectRectAndRegion(const SkRegion & region,const SkRect & srcRect,SkRect * destRect)163 void 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
ClipRectToCanvas(const SkCanvas & canvas,const SkRect & srcRect,SkRect * destRect)187 void 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
SkPathContainsPoint(SkPath * originalPath,const FloatPoint & point,SkPath::FillType ft)203 bool 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)
scratchContext()255 GraphicsContext* 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
scratchContext()268 GraphicsContext* 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