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