1/*
2 * Copyright (C) 2011 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ImageBufferData.h"
28
29#include <wtf/Assertions.h>
30
31#if USE(ACCELERATE)
32#include <Accelerate/Accelerate.h>
33#endif
34
35#if USE(IOSURFACE_CANVAS_BACKING_STORE)
36#include <IOSurface/IOSurface.h>
37#include <dispatch/dispatch.h>
38#endif
39
40#if USE(ACCELERATE)
41struct ScanlineData {
42    vImagePixelCount scanlineWidth;
43    unsigned char* srcData;
44    size_t srcRowBytes;
45    unsigned char* destData;
46    size_t destRowBytes;
47};
48#endif
49
50namespace WebCore {
51
52ImageBufferData::ImageBufferData(const IntSize&)
53: m_data(0)
54#if USE(IOSURFACE_CANVAS_BACKING_STORE)
55, m_surface(0)
56#endif
57{
58}
59
60#if USE(ACCELERATE)
61// The vImage unpremultiply routine had a rounding bug before 10.6.7 <rdar://problem/8631548>
62static bool haveVImageRoundingErrorFix()
63{
64    SInt32 version;
65    static bool result = (Gestalt(gestaltSystemVersion, &version) == noErr && version > 0x1066);
66    return result;
67}
68
69#if USE(IOSURFACE_CANVAS_BACKING_STORE)
70static void convertScanline(void* data, size_t tileNumber, bool premultiply)
71{
72    ScanlineData* scanlineData = static_cast<ScanlineData*>(data);
73
74    vImage_Buffer src;
75    src.data = scanlineData->srcData + tileNumber * scanlineData->srcRowBytes;
76    src.height = 1;
77    src.width = scanlineData->scanlineWidth;
78    src.rowBytes = scanlineData->srcRowBytes;
79
80    vImage_Buffer dest;
81    dest.data = scanlineData->destData + tileNumber * scanlineData->destRowBytes;
82    dest.height = 1;
83    dest.width = scanlineData->scanlineWidth;
84    dest.rowBytes = scanlineData->destRowBytes;
85
86    if (premultiply) {
87        if (kvImageNoError != vImagePremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile))
88            return;
89    } else {
90        if (kvImageNoError != vImageUnpremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile))
91            return;
92    }
93
94    // Swap channels 1 and 3, to convert BGRA<->RGBA. IOSurfaces is BGRA, ImageData expects RGBA.
95    const uint8_t map[4] = { 2, 1, 0, 3 };
96    vImagePermuteChannels_ARGB8888(&dest, &dest, map, kvImageDoNotTile);
97}
98
99static void unpremultitplyScanline(void* data, size_t tileNumber)
100{
101    convertScanline(data, tileNumber, false);
102}
103
104static void premultitplyScanline(void* data, size_t tileNumber)
105{
106    convertScanline(data, tileNumber, true);
107}
108#endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
109#endif // USE(ACCELERATE)
110
111PassRefPtr<ByteArray> ImageBufferData::getData(const IntRect& rect, const IntSize& size, bool accelerateRendering, bool unmultiplied) const
112{
113    float area = 4.0f * rect.width() * rect.height();
114    if (area > static_cast<float>(std::numeric_limits<int>::max()))
115        return 0;
116
117    RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
118    unsigned char* data = result->data();
119
120    if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height())
121        memset(data, 0, result->length());
122
123    int originx = rect.x();
124    int destx = 0;
125    if (originx < 0) {
126        destx = -originx;
127        originx = 0;
128    }
129    int endx = rect.maxX();
130    if (endx > size.width())
131        endx = size.width();
132    int width = endx - originx;
133
134    int originy = rect.y();
135    int desty = 0;
136    if (originy < 0) {
137        desty = -originy;
138        originy = 0;
139    }
140    int endy = rect.maxY();
141    if (endy > size.height())
142        endy = size.height();
143    int height = endy - originy;
144
145    if (width <= 0 || height <= 0)
146        return result.release();
147
148    unsigned destBytesPerRow = 4 * rect.width();
149    unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
150
151    unsigned srcBytesPerRow;
152    unsigned char* srcRows;
153
154    if (!accelerateRendering) {
155        srcBytesPerRow = 4 * size.width();
156        srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4;
157
158#if USE(ACCELERATE)
159        if (unmultiplied && haveVImageRoundingErrorFix()) {
160            vImage_Buffer src;
161            src.height = height;
162            src.width = width;
163            src.rowBytes = srcBytesPerRow;
164            src.data = srcRows;
165
166            vImage_Buffer dst;
167            dst.height = height;
168            dst.width = width;
169            dst.rowBytes = destBytesPerRow;
170            dst.data = destRows;
171
172            vImageUnpremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
173            return result.release();
174        }
175#endif
176        for (int y = 0; y < height; ++y) {
177            for (int x = 0; x < width; x++) {
178                int basex = x * 4;
179                unsigned char alpha = srcRows[basex + 3];
180                if (unmultiplied && alpha) {
181                    destRows[basex] = (srcRows[basex] * 255) / alpha;
182                    destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
183                    destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha;
184                    destRows[basex + 3] = alpha;
185                } else
186                    reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
187            }
188            srcRows += srcBytesPerRow;
189            destRows += destBytesPerRow;
190        }
191    } else {
192#if USE(IOSURFACE_CANVAS_BACKING_STORE)
193        IOSurfaceRef surface = m_surface.get();
194        IOSurfaceLock(surface, kIOSurfaceLockReadOnly, 0);
195        srcBytesPerRow = IOSurfaceGetBytesPerRow(surface);
196        srcRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + originy * srcBytesPerRow + originx * 4;
197
198#if USE(ACCELERATE)
199        if (unmultiplied) {
200            ScanlineData scanlineData;
201            scanlineData.scanlineWidth = width;
202            scanlineData.srcData = srcRows;
203            scanlineData.srcRowBytes = srcBytesPerRow;
204            scanlineData.destData = destRows;
205            scanlineData.destRowBytes = destBytesPerRow;
206
207            dispatch_apply_f(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, unpremultitplyScanline);
208        } else {
209            vImage_Buffer src;
210            src.height = height;
211            src.width = width;
212            src.rowBytes = srcBytesPerRow;
213            src.data = srcRows;
214
215            vImage_Buffer dest;
216            dest.height = height;
217            dest.width = width;
218            dest.rowBytes = destBytesPerRow;
219            dest.data = destRows;
220
221            // Swap pixel channels from BGRA to RGBA.
222            const uint8_t map[4] = { 2, 1, 0, 3 };
223            vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
224        }
225#else
226        for (int y = 0; y < height; ++y) {
227            for (int x = 0; x < width; x++) {
228                int basex = x * 4;
229                unsigned char alpha = srcRows[basex + 3];
230                if (unmultiplied && alpha) {
231                    destRows[basex] = (srcRows[basex + 2] * 255) / alpha;
232                    destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
233                    destRows[basex + 2] = (srcRows[basex] * 255) / alpha;
234                    destRows[basex + 3] = alpha;
235                } else {
236                    destRows[basex] = srcRows[basex + 2];
237                    destRows[basex + 1] = srcRows[basex + 1];
238                    destRows[basex + 2] = srcRows[basex];
239                    destRows[basex + 3] = alpha;
240                }
241            }
242            srcRows += srcBytesPerRow;
243            destRows += destBytesPerRow;
244        }
245#endif // USE(ACCELERATE)
246        IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, 0);
247#else
248        ASSERT_NOT_REACHED();
249#endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
250    }
251
252    return result.release();
253}
254
255void ImageBufferData::putData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize& size, bool accelerateRendering, bool unmultiplied)
256{
257    ASSERT(sourceRect.width() > 0);
258    ASSERT(sourceRect.height() > 0);
259
260    int originx = sourceRect.x();
261    int destx = destPoint.x() + sourceRect.x();
262    ASSERT(destx >= 0);
263    ASSERT(destx < size.width());
264    ASSERT(originx >= 0);
265    ASSERT(originx <= sourceRect.maxX());
266
267    int endx = destPoint.x() + sourceRect.maxX();
268    ASSERT(endx <= size.width());
269
270    int width = endx - destx;
271
272    int originy = sourceRect.y();
273    int desty = destPoint.y() + sourceRect.y();
274    ASSERT(desty >= 0);
275    ASSERT(desty < size.height());
276    ASSERT(originy >= 0);
277    ASSERT(originy <= sourceRect.maxY());
278
279    int endy = destPoint.y() + sourceRect.maxY();
280    ASSERT(endy <= size.height());
281    int height = endy - desty;
282
283    if (width <= 0 || height <= 0)
284        return;
285
286    unsigned srcBytesPerRow = 4 * sourceSize.width();
287    unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4;
288    unsigned destBytesPerRow;
289    unsigned char* destRows;
290
291    if (!accelerateRendering) {
292        destBytesPerRow = 4 * size.width();
293        destRows = reinterpret_cast<unsigned char*>(m_data) + desty * destBytesPerRow + destx * 4;
294
295#if USE(ACCELERATE)
296        if (haveVImageRoundingErrorFix() && unmultiplied) {
297            vImage_Buffer src;
298            src.height = height;
299            src.width = width;
300            src.rowBytes = srcBytesPerRow;
301            src.data = srcRows;
302
303            vImage_Buffer dst;
304            dst.height = height;
305            dst.width = width;
306            dst.rowBytes = destBytesPerRow;
307            dst.data = destRows;
308
309            vImagePremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
310            return;
311        }
312#endif
313        for (int y = 0; y < height; ++y) {
314            for (int x = 0; x < width; x++) {
315                int basex = x * 4;
316                unsigned char alpha = srcRows[basex + 3];
317                if (unmultiplied && alpha != 255) {
318                    destRows[basex] = (srcRows[basex] * alpha + 254) / 255;
319                    destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
320                    destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255;
321                    destRows[basex + 3] = alpha;
322                } else
323                    reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
324            }
325            destRows += destBytesPerRow;
326            srcRows += srcBytesPerRow;
327        }
328    } else {
329#if USE(IOSURFACE_CANVAS_BACKING_STORE)
330        IOSurfaceRef surface = m_surface.get();
331        IOSurfaceLock(surface, 0, 0);
332        destBytesPerRow = IOSurfaceGetBytesPerRow(surface);
333        destRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + desty * destBytesPerRow + destx * 4;
334
335#if USE(ACCELERATE)
336        if (unmultiplied) {
337            ScanlineData scanlineData;
338            scanlineData.scanlineWidth = width;
339            scanlineData.srcData = srcRows;
340            scanlineData.srcRowBytes = srcBytesPerRow;
341            scanlineData.destData = destRows;
342            scanlineData.destRowBytes = destBytesPerRow;
343
344            dispatch_apply_f(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, premultitplyScanline);
345        } else {
346            vImage_Buffer src;
347            src.height = height;
348            src.width = width;
349            src.rowBytes = srcBytesPerRow;
350            src.data = srcRows;
351
352            vImage_Buffer dest;
353            dest.height = height;
354            dest.width = width;
355            dest.rowBytes = destBytesPerRow;
356            dest.data = destRows;
357
358            // Swap pixel channels from RGBA to BGRA.
359            const uint8_t map[4] = { 2, 1, 0, 3 };
360            vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
361        }
362#else
363        for (int y = 0; y < height; ++y) {
364            for (int x = 0; x < width; x++) {
365                int basex = x * 4;
366                unsigned char alpha = srcRows[basex + 3];
367                if (unmultiplied && alpha != 255) {
368                    destRows[basex] = (srcRows[basex + 2] * alpha + 254) / 255;
369                    destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
370                    destRows[basex + 2] = (srcRows[basex] * alpha + 254) / 255;
371                    destRows[basex + 3] = alpha;
372                } else {
373                    destRows[basex] = srcRows[basex + 2];
374                    destRows[basex + 1] = srcRows[basex + 1];
375                    destRows[basex + 2] = srcRows[basex];
376                    destRows[basex + 3] = alpha;
377                }
378            }
379            destRows += destBytesPerRow;
380            srcRows += srcBytesPerRow;
381        }
382#endif // USE(ACCELERATE)
383
384        IOSurfaceUnlock(surface, 0, 0);
385#else
386        ASSERT_NOT_REACHED();
387#endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
388    }
389}
390
391} // namespace WebCore
392