• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
41 struct ScanlineData {
42     vImagePixelCount scanlineWidth;
43     unsigned char* srcData;
44     size_t srcRowBytes;
45     unsigned char* destData;
46     size_t destRowBytes;
47 };
48 #endif
49 
50 namespace WebCore {
51 
ImageBufferData(const IntSize &)52 ImageBufferData::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>
haveVImageRoundingErrorFix()62 static 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)
convertScanline(void * data,size_t tileNumber,bool premultiply)70 static 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 
unpremultitplyScanline(void * data,size_t tileNumber)99 static void unpremultitplyScanline(void* data, size_t tileNumber)
100 {
101     convertScanline(data, tileNumber, false);
102 }
103 
premultitplyScanline(void * data,size_t tileNumber)104 static 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 
getData(const IntRect & rect,const IntSize & size,bool accelerateRendering,bool unmultiplied) const111 PassRefPtr<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 
putData(ByteArray * & source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint,const IntSize & size,bool accelerateRendering,bool unmultiplied)255 void 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