537cdb6e8298abe64ac4202b9941bf3671883e98
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageBufferDataCG.cpp
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 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 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 "GraphicsContext.h"
30 #include "IntRect.h"
31 #include <CoreGraphics/CoreGraphics.h>
32 #include <wtf/Assertions.h>
33
34 #if USE(ACCELERATE)
35 #include <Accelerate/Accelerate.h>
36 #endif
37
38 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
39 #include "IOSurface.h"
40 #include "IOSurfaceSPI.h"
41 #include <dispatch/dispatch.h>
42 #endif
43
44 #if USE(ACCELERATE)
45 struct ScanlineData {
46     vImagePixelCount scanlineWidth;
47     unsigned char* srcData;
48     size_t srcRowBytes;
49     unsigned char* destData;
50     size_t destRowBytes;
51 };
52 #endif
53
54 // CA uses ARGB32 for textures and ARGB32 -> ARGB32 resampling is optimized.
55 #define USE_ARGB32 PLATFORM(IOS)
56
57 namespace WebCore {
58
59 ImageBufferData::ImageBufferData()
60     : m_data(nullptr)
61 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
62     , m_surface(nullptr)
63 #endif
64 {
65 }
66
67 #if USE(ACCELERATE)
68
69 #if USE_ARGB32 || USE(IOSURFACE_CANVAS_BACKING_STORE)
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 are BGRA, ImageData expects RGBA.
95     const uint8_t map[4] = { 2, 1, 0, 3 };
96     vImagePermuteChannels_ARGB8888(&dest, &dest, map, kvImageDoNotTile);
97 }
98
99 static void unpremultitplyScanline(void* data, size_t tileNumber)
100 {
101     convertScanline(data, tileNumber, false);
102 }
103
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
111 PassRefPtr<Uint8ClampedArray> ImageBufferData::getData(const IntRect& rect, const IntSize& size, bool accelerateRendering, bool unmultiplied, float resolutionScale) const
112 {
113     Checked<unsigned, RecordOverflow> area = 4;
114     area *= rect.width();
115     area *= rect.height();
116     if (area.hasOverflowed())
117         return 0;
118
119     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(area.unsafeGet());
120     unsigned char* data = result->data();
121     
122     Checked<int> endx = rect.maxX();
123     endx *= ceilf(resolutionScale);
124     Checked<int> endy = rect.maxY();
125     endy *= resolutionScale;
126     if (rect.x() < 0 || rect.y() < 0 || endx.unsafeGet() > size.width() || endy.unsafeGet() > size.height())
127         result->zeroFill();
128     
129     int originx = rect.x();
130     int destx = 0;
131     Checked<int> destw = rect.width();
132     if (originx < 0) {
133         destw += originx;
134         destx = -originx;
135         originx = 0;
136     }
137     destw = std::min<int>(destw.unsafeGet(), ceilf(size.width() / resolutionScale) - originx);
138     originx *= resolutionScale;
139     if (endx.unsafeGet() > size.width())
140         endx = size.width();
141     Checked<int> width = endx - originx;
142     
143     int originy = rect.y();
144     int desty = 0;
145     Checked<int> desth = rect.height();
146     if (originy < 0) {
147         desth += originy;
148         desty = -originy;
149         originy = 0;
150     }
151     desth = std::min<int>(desth.unsafeGet(), ceilf(size.height() / resolutionScale) - originy);
152     originy *= resolutionScale;
153     if (endy.unsafeGet() > size.height())
154         endy = size.height();
155     Checked<int> height = endy - originy;
156     
157     if (width.unsafeGet() <= 0 || height.unsafeGet() <= 0)
158         return result.release();
159     
160     unsigned destBytesPerRow = 4 * rect.width();
161     unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
162     
163     unsigned srcBytesPerRow;
164     unsigned char* srcRows;
165     
166     if (!accelerateRendering) {
167         srcBytesPerRow = m_bytesPerRow.unsafeGet();
168         srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4;
169         
170 #if USE(ACCELERATE)
171         if (unmultiplied) {
172 #if USE_ARGB32
173             ScanlineData scanlineData;
174             scanlineData.scanlineWidth = destw.unsafeGet();
175             scanlineData.srcData = srcRows;
176             scanlineData.srcRowBytes = srcBytesPerRow;
177             scanlineData.destData = destRows;
178             scanlineData.destRowBytes = destBytesPerRow;
179
180             dispatch_apply_f(desth.unsafeGet(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, unpremultitplyScanline);
181 #else
182             vImage_Buffer src;
183             src.height = height.unsafeGet();
184             src.width = width.unsafeGet();
185             src.rowBytes = srcBytesPerRow;
186             src.data = srcRows;
187             
188             vImage_Buffer dst;
189             dst.height = desth.unsafeGet();
190             dst.width = destw.unsafeGet();
191             dst.rowBytes = destBytesPerRow;
192             dst.data = destRows;
193
194             if (resolutionScale != 1) {
195                 vImage_AffineTransform scaleTransform = { 1 / resolutionScale, 0, 0, 1 / resolutionScale, 0, 0 }; // FIXME: Add subpixel translation.
196                 Pixel_8888 backgroundColor;
197                 vImageAffineWarp_ARGB8888(&src, &dst, 0, &scaleTransform, backgroundColor, kvImageEdgeExtend);
198                 // The unpremultiplying will be done in-place.
199                 src = dst;
200             }
201
202             vImageUnpremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
203 #endif
204             return result.release();
205         }
206 #endif
207         if (resolutionScale != 1) {
208             RetainPtr<CGContextRef> sourceContext = adoptCF(CGBitmapContextCreate(srcRows, width.unsafeGet(), height.unsafeGet(), 8, srcBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
209             RetainPtr<CGImageRef> sourceImage = adoptCF(CGBitmapContextCreateImage(sourceContext.get()));
210             RetainPtr<CGContextRef> destinationContext = adoptCF(CGBitmapContextCreate(destRows, destw.unsafeGet(), desth.unsafeGet(), 8, destBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
211             CGContextSetBlendMode(destinationContext.get(), kCGBlendModeCopy);
212             CGContextDrawImage(destinationContext.get(), CGRectMake(0, 0, width.unsafeGet() / resolutionScale, height.unsafeGet() / resolutionScale), sourceImage.get()); // FIXME: Add subpixel translation.
213             if (!unmultiplied)
214                 return result.release();
215
216             srcRows = destRows;
217             srcBytesPerRow = destBytesPerRow;
218             width = destw;
219             height = desth;
220         }
221         if (unmultiplied) {
222             if ((width * 4).hasOverflowed())
223                 CRASH();
224             for (int y = 0; y < height.unsafeGet(); ++y) {
225                 for (int x = 0; x < width.unsafeGet(); x++) {
226                     int basex = x * 4;
227                     unsigned char alpha = srcRows[basex + 3];
228 #if USE_ARGB32
229                     // Byte order is different as we use image buffers of ARGB32
230                     if (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 #else
242                     if (alpha) {
243                         destRows[basex] = (srcRows[basex] * 255) / alpha;
244                         destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
245                         destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha;
246                         destRows[basex + 3] = alpha;
247                     } else
248                         reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
249 #endif
250                 }
251                 srcRows += srcBytesPerRow;
252                 destRows += destBytesPerRow;
253             }
254         } else {
255             for (int y = 0; y < height.unsafeGet(); ++y) {
256 #if USE_ARGB32
257                 for (int x = 0; x < width.unsafeGet(); x++) {
258                     int basex = x * 4;
259                     destRows[basex] = srcRows[basex + 2];
260                     destRows[basex + 1] = srcRows[basex + 1];
261                     destRows[basex + 2] = srcRows[basex];
262                     destRows[basex + 3] = srcRows[basex + 3];
263                 }
264 #else
265                 for (int x = 0; x < (width * 4).unsafeGet(); x += 4)
266                     reinterpret_cast<uint32_t*>(destRows + x)[0] = reinterpret_cast<uint32_t*>(srcRows + x)[0];
267 #endif
268                 srcRows += srcBytesPerRow;
269                 destRows += destBytesPerRow;
270             }
271         }
272     } else {
273 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
274         // FIXME: WebCore::IOSurface should have a locking RAII object and base-address getter.
275         IOSurfaceRef surface = m_surface->surface();
276         IOSurfaceLock(surface, kIOSurfaceLockReadOnly, 0);
277         srcBytesPerRow = IOSurfaceGetBytesPerRow(surface);
278         srcRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + originy * srcBytesPerRow + originx * 4;
279
280 #if USE(ACCELERATE)
281         vImage_Buffer src;
282         src.height = height.unsafeGet();
283         src.width = width.unsafeGet();
284         src.rowBytes = srcBytesPerRow;
285         src.data = srcRows;
286
287         vImage_Buffer dest;
288         dest.height = desth.unsafeGet();
289         dest.width = destw.unsafeGet();
290         dest.rowBytes = destBytesPerRow;
291         dest.data = destRows;
292
293         if (resolutionScale != 1) {
294             vImage_AffineTransform scaleTransform = { 1 / resolutionScale, 0, 0, 1 / resolutionScale, 0, 0 }; // FIXME: Add subpixel translation.
295             Pixel_8888 backgroundColor;
296             vImageAffineWarp_ARGB8888(&src, &dest, 0, &scaleTransform, backgroundColor, kvImageEdgeExtend);
297             // The unpremultiplying and channel-swapping will be done in-place.
298             if (unmultiplied) {
299                 srcRows = destRows;
300                 width = destw;
301                 height = desth;
302                 srcBytesPerRow = destBytesPerRow;
303             } else
304                 src = dest;
305         }
306
307         if (unmultiplied) {
308             ScanlineData scanlineData;
309             scanlineData.scanlineWidth = destw.unsafeGet();
310             scanlineData.srcData = srcRows;
311             scanlineData.srcRowBytes = srcBytesPerRow;
312             scanlineData.destData = destRows;
313             scanlineData.destRowBytes = destBytesPerRow;
314
315             dispatch_apply_f(desth.unsafeGet(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, unpremultitplyScanline);
316         } else {
317             // Swap pixel channels from BGRA to RGBA.
318             const uint8_t map[4] = { 2, 1, 0, 3 };
319             vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
320         }
321 #else
322         if (resolutionScale != 1) {
323             RetainPtr<CGContextRef> sourceContext = adoptCF(CGBitmapContextCreate(srcRows, width.unsafeGet(), height.unsafeGet(), 8, srcBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
324             RetainPtr<CGImageRef> sourceImage = adoptCF(CGBitmapContextCreateImage(sourceContext.get()));
325             RetainPtr<CGContextRef> destinationContext = adoptCF(CGBitmapContextCreate(destRows, destw.unsafeGet(), desth.unsafeGet(), 8, destBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
326             CGContextSetBlendMode(destinationContext.get(), kCGBlendModeCopy);
327             CGContextDrawImage(destinationContext.get(), CGRectMake(0, 0, width.unsafeGet() / resolutionScale, height.unsafeGet() / resolutionScale), sourceImage.get()); // FIXME: Add subpixel translation.
328
329             srcRows = destRows;
330             srcBytesPerRow = destBytesPerRow;
331             width = destw;
332             height = desth;
333         }
334         
335         if ((width * 4).hasOverflowed())
336             CRASH();
337
338         if (unmultiplied) {
339             for (int y = 0; y < height.unsafeGet(); ++y) {
340                 for (int x = 0; x < width.unsafeGet(); x++) {
341                     int basex = x * 4;
342                     unsigned char b = srcRows[basex];
343                     unsigned char alpha = srcRows[basex + 3];
344                     if (alpha) {
345                         destRows[basex] = (srcRows[basex + 2] * 255) / alpha;
346                         destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
347                         destRows[basex + 2] = (b * 255) / alpha;
348                         destRows[basex + 3] = alpha;
349                     } else {
350                         destRows[basex] = srcRows[basex + 2];
351                         destRows[basex + 1] = srcRows[basex + 1];
352                         destRows[basex + 2] = b;
353                         destRows[basex + 3] = srcRows[basex + 3];
354                     }
355                 }
356                 srcRows += srcBytesPerRow;
357                 destRows += destBytesPerRow;
358             }
359         } else {
360             for (int y = 0; y < height.unsafeGet(); ++y) {
361                 for (int x = 0; x < width.unsafeGet(); x++) {
362                     int basex = x * 4;
363                     unsigned char b = srcRows[basex];
364                     destRows[basex] = srcRows[basex + 2];
365                     destRows[basex + 1] = srcRows[basex + 1];
366                     destRows[basex + 2] = b;
367                     destRows[basex + 3] = srcRows[basex + 3];
368                 }
369                 srcRows += srcBytesPerRow;
370                 destRows += destBytesPerRow;
371             }
372         }
373 #endif // USE(ACCELERATE)
374         IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, 0);
375 #else
376         ASSERT_NOT_REACHED();
377 #endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
378     }
379     
380     return result.release();
381 }
382
383 void ImageBufferData::putData(Uint8ClampedArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize& size, bool accelerateRendering, bool unmultiplied, float resolutionScale)
384 {
385 #if ASSERT_DISABLED
386     UNUSED_PARAM(size);
387 #endif
388
389     ASSERT(sourceRect.width() > 0);
390     ASSERT(sourceRect.height() > 0);
391     
392     Checked<int> originx = sourceRect.x();
393     Checked<int> destx = (Checked<int>(destPoint.x()) + sourceRect.x());
394     destx *= resolutionScale;
395     ASSERT(destx.unsafeGet() >= 0);
396     ASSERT(destx.unsafeGet() < size.width());
397     ASSERT(originx.unsafeGet() >= 0);
398     ASSERT(originx.unsafeGet() <= sourceRect.maxX());
399     
400     Checked<int> endx = (Checked<int>(destPoint.x()) + sourceRect.maxX());
401     endx *= resolutionScale;
402     ASSERT(endx.unsafeGet() <= size.width());
403     
404     Checked<int> width = sourceRect.width();
405     Checked<int> destw = endx - destx;
406
407     Checked<int> originy = sourceRect.y();
408     Checked<int> desty = (Checked<int>(destPoint.y()) + sourceRect.y());
409     desty *= resolutionScale;
410     ASSERT(desty.unsafeGet() >= 0);
411     ASSERT(desty.unsafeGet() < size.height());
412     ASSERT(originy.unsafeGet() >= 0);
413     ASSERT(originy.unsafeGet() <= sourceRect.maxY());
414     
415     Checked<int> endy = (Checked<int>(destPoint.y()) + sourceRect.maxY());
416     endy *= resolutionScale;
417     ASSERT(endy.unsafeGet() <= size.height());
418
419     Checked<int> height = sourceRect.height();
420     Checked<int> desth = endy - desty;
421     
422     if (width <= 0 || height <= 0)
423         return;
424     
425     unsigned srcBytesPerRow = 4 * sourceSize.width();
426     unsigned char* srcRows = source->data() + (originy * srcBytesPerRow + originx * 4).unsafeGet();
427     unsigned destBytesPerRow;
428     unsigned char* destRows;
429     
430     if (!accelerateRendering) {
431         destBytesPerRow = m_bytesPerRow.unsafeGet();
432         destRows = reinterpret_cast<unsigned char*>(m_data) + (desty * destBytesPerRow + destx * 4).unsafeGet();
433         
434 #if  USE(ACCELERATE)
435         if (unmultiplied) {
436 #if USE_ARGB32
437             // FIXME: Are scanlineData.scanlineWidth and the number of iterations specified to dispatch_apply_f() correct?
438             ScanlineData scanlineData;
439             scanlineData.scanlineWidth = width.unsafeGet();
440             scanlineData.srcData = srcRows;
441             scanlineData.srcRowBytes = srcBytesPerRow;
442             scanlineData.destData = destRows;
443             scanlineData.destRowBytes = destBytesPerRow;
444
445             dispatch_apply_f(height.unsafeGet(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, premultitplyScanline);
446 #else
447             vImage_Buffer src;
448             src.height = height.unsafeGet();
449             src.width = width.unsafeGet();
450             src.rowBytes = srcBytesPerRow;
451             src.data = srcRows;
452             
453             vImage_Buffer dst;
454             dst.height = desth.unsafeGet();
455             dst.width = destw.unsafeGet();
456             dst.rowBytes = destBytesPerRow;
457             dst.data = destRows;
458
459             if (resolutionScale != 1) {
460                 vImage_AffineTransform scaleTransform = { resolutionScale, 0, 0, resolutionScale, 0, 0 }; // FIXME: Add subpixel translation.
461                 Pixel_8888 backgroundColor;
462                 vImageAffineWarp_ARGB8888(&src, &dst, 0, &scaleTransform, backgroundColor, kvImageEdgeExtend);
463                 // The premultiplying will be done in-place.
464                 src = dst;
465             }
466
467             vImagePremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
468 #endif
469             return;
470         }
471 #endif
472         if (resolutionScale != 1) {
473             RetainPtr<CGContextRef> sourceContext = adoptCF(CGBitmapContextCreate(srcRows, width.unsafeGet(), height.unsafeGet(), 8, srcBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
474             RetainPtr<CGImageRef> sourceImage = adoptCF(CGBitmapContextCreateImage(sourceContext.get()));
475             RetainPtr<CGContextRef> destinationContext = adoptCF(CGBitmapContextCreate(destRows, destw.unsafeGet(), desth.unsafeGet(), 8, destBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
476             CGContextSetBlendMode(destinationContext.get(), kCGBlendModeCopy);
477             CGContextDrawImage(destinationContext.get(), CGRectMake(0, 0, width.unsafeGet() / resolutionScale, height.unsafeGet() / resolutionScale), sourceImage.get()); // FIXME: Add subpixel translation.
478             if (!unmultiplied)
479                 return;
480
481             srcRows = destRows;
482             srcBytesPerRow = destBytesPerRow;
483             width = destw;
484             height = desth;
485         }
486
487         for (int y = 0; y < height.unsafeGet(); ++y) {
488             for (int x = 0; x < width.unsafeGet(); x++) {
489                 int basex = x * 4;
490                 unsigned char alpha = srcRows[basex + 3];
491 #if USE_ARGB32
492                 // Byte order is different as we use image buffers of ARGB32
493                 if (unmultiplied && alpha != 255) {
494                     destRows[basex] = (srcRows[basex + 2] * alpha + 254) / 255;
495                     destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
496                     destRows[basex + 2] = (srcRows[basex + 0] * alpha + 254) / 255;
497                     destRows[basex + 3] = alpha;
498                 } else {
499                     destRows[basex] = srcRows[basex + 2];
500                     destRows[basex + 1] = srcRows[basex + 1];
501                     destRows[basex + 2] = srcRows[basex];
502                     destRows[basex + 3] = alpha;
503                 }
504 #else
505                 if (unmultiplied && alpha != 255) {
506                     destRows[basex] = (srcRows[basex] * alpha + 254) / 255;
507                     destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
508                     destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255;
509                     destRows[basex + 3] = alpha;
510                 } else
511                     reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
512 #endif
513             }
514             destRows += destBytesPerRow;
515             srcRows += srcBytesPerRow;
516         }
517     } else {
518 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
519         IOSurfaceRef surface = m_surface->surface();
520         IOSurfaceLock(surface, 0, 0);
521         destBytesPerRow = IOSurfaceGetBytesPerRow(surface);
522         destRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + (desty * destBytesPerRow + destx * 4).unsafeGet();
523
524 #if USE(ACCELERATE)
525         vImage_Buffer src;
526         src.height = height.unsafeGet();
527         src.width = width.unsafeGet();
528         src.rowBytes = srcBytesPerRow;
529         src.data = srcRows;
530
531         vImage_Buffer dest;
532         dest.height = desth.unsafeGet();
533         dest.width = destw.unsafeGet();
534         dest.rowBytes = destBytesPerRow;
535         dest.data = destRows;
536
537         if (resolutionScale != 1) {
538             vImage_AffineTransform scaleTransform = { resolutionScale, 0, 0, resolutionScale, 0, 0 }; // FIXME: Add subpixel translation.
539             Pixel_8888 backgroundColor;
540             vImageAffineWarp_ARGB8888(&src, &dest, 0, &scaleTransform, backgroundColor, kvImageEdgeExtend);
541             // The unpremultiplying and channel-swapping will be done in-place.
542             if (unmultiplied) {
543                 srcRows = destRows;
544                 width = destw;
545                 height = desth;
546                 srcBytesPerRow = destBytesPerRow;
547             } else
548                 src = dest;
549         }
550
551         if (unmultiplied) {
552             ScanlineData scanlineData;
553             scanlineData.scanlineWidth = width.unsafeGet();
554             scanlineData.srcData = srcRows;
555             scanlineData.srcRowBytes = srcBytesPerRow;
556             scanlineData.destData = destRows;
557             scanlineData.destRowBytes = destBytesPerRow;
558
559             dispatch_apply_f(height.unsafeGet(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, premultitplyScanline);
560         } else {
561             // Swap pixel channels from RGBA to BGRA.
562             const uint8_t map[4] = { 2, 1, 0, 3 };
563             vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
564         }
565 #else
566         if (resolutionScale != 1) {
567             RetainPtr<CGContextRef> sourceContext = adoptCF(CGBitmapContextCreate(srcRows, width.unsafeGet(), height.unsafeGet(), 8, srcBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
568             RetainPtr<CGImageRef> sourceImage = adoptCF(CGBitmapContextCreateImage(sourceContext.get()));
569             RetainPtr<CGContextRef> destinationContext = adoptCF(CGBitmapContextCreate(destRows, destw.unsafeGet(), desth.unsafeGet(), 8, destBytesPerRow, m_colorSpace, kCGImageAlphaPremultipliedLast));
570             CGContextSetBlendMode(destinationContext.get(), kCGBlendModeCopy);
571             CGContextDrawImage(destinationContext.get(), CGRectMake(0, 0, width.unsafeGet() / resolutionScale, height.unsafeGet() / resolutionScale), sourceImage.get()); // FIXME: Add subpixel translation.
572
573             srcRows = destRows;
574             srcBytesPerRow = destBytesPerRow;
575             width = destw;
576             height = desth;
577         }
578
579         for (int y = 0; y < height.unsafeGet(); ++y) {
580             for (int x = 0; x < width.unsafeGet(); x++) {
581                 int basex = x * 4;
582                 unsigned char b = srcRows[basex];
583                 unsigned char alpha = srcRows[basex + 3];
584                 if (unmultiplied && alpha != 255) {
585                     destRows[basex] = (srcRows[basex + 2] * alpha + 254) / 255;
586                     destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
587                     destRows[basex + 2] = (b * alpha + 254) / 255;
588                     destRows[basex + 3] = alpha;
589                 } else {
590                     destRows[basex] = srcRows[basex + 2];
591                     destRows[basex + 1] = srcRows[basex + 1];
592                     destRows[basex + 2] = b;
593                     destRows[basex + 3] = alpha;
594                 }
595             }
596             destRows += destBytesPerRow;
597             srcRows += srcBytesPerRow;
598         }
599 #endif // USE(ACCELERATE)
600
601         IOSurfaceUnlock(surface, 0, 0);
602 #else
603         ASSERT_NOT_REACHED();
604 #endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
605     }
606 }
607
608 } // namespace WebCore