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