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