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