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