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