2
* Copyright (C) 2011 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
27
#include "ImageBufferData.h"
29
#include <CoreGraphics/CoreGraphics.h>
30
#include <wtf/Assertions.h>
33
#include <Accelerate/Accelerate.h>
36
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
37
#include <IOSurface/IOSurface.h>
38
#include <dispatch/dispatch.h>
45
vImagePixelCount scanlineWidth;
46
unsigned char* srcData;
48
unsigned char* destData;
55
ImageBufferData::ImageBufferData(const IntSize&)
57
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
65
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
66
static bool haveVImageRoundingErrorFix() { return true; }
68
// The vImage unpremultiply routine had a rounding bug before 10.6.7 <rdar://problem/8631548>
69
static bool haveVImageRoundingErrorFix()
72
static bool result = (Gestalt(gestaltSystemVersion, &version) == noErr && version > 0x1066);
75
#endif // __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
77
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
78
static void convertScanline(void* data, size_t tileNumber, bool premultiply)
80
ScanlineData* scanlineData = static_cast<ScanlineData*>(data);
83
src.data = scanlineData->srcData + tileNumber * scanlineData->srcRowBytes;
85
src.width = scanlineData->scanlineWidth;
86
src.rowBytes = scanlineData->srcRowBytes;
89
dest.data = scanlineData->destData + tileNumber * scanlineData->destRowBytes;
91
dest.width = scanlineData->scanlineWidth;
92
dest.rowBytes = scanlineData->destRowBytes;
95
if (kvImageNoError != vImagePremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile))
98
if (kvImageNoError != vImageUnpremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile))
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);
107
static void unpremultitplyScanline(void* data, size_t tileNumber)
109
convertScanline(data, tileNumber, false);
112
static void premultitplyScanline(void* data, size_t tileNumber)
114
convertScanline(data, tileNumber, true);
116
#endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
117
#endif // USE(ACCELERATE)
119
PassRefPtr<Uint8ClampedArray> ImageBufferData::getData(const IntRect& rect, const IntSize& size, bool accelerateRendering, bool unmultiplied, float resolutionScale) const
121
Checked<unsigned, RecordOverflow> area = 4;
122
area *= rect.width();
123
area *= rect.height();
124
if (area.hasOverflowed())
127
RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(area.unsafeGet());
128
unsigned char* data = result->data();
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())
137
int originx = rect.x();
139
int destw = rect.width();
145
destw = min<int>(destw, ceilf(size.width() / resolutionScale) - originx);
146
originx *= resolutionScale;
147
if (endx.unsafeGet() > size.width())
149
Checked<int> width = endx - originx;
151
int originy = rect.y();
153
int desth = rect.height();
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;
165
if (width.unsafeGet() <= 0 || height.unsafeGet() <= 0)
166
return result.release();
168
unsigned destBytesPerRow = 4 * rect.width();
169
unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
171
unsigned srcBytesPerRow;
172
unsigned char* srcRows;
174
if (!accelerateRendering) {
175
srcBytesPerRow = 4 * size.width();
176
srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4;
179
if (unmultiplied && haveVImageRoundingErrorFix()) {
181
src.height = height.unsafeGet();
182
src.width = width.unsafeGet();
183
src.rowBytes = srcBytesPerRow;
189
dst.rowBytes = destBytesPerRow;
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.
200
vImageUnpremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
201
return result.release();
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.
211
return result.release();
214
srcBytesPerRow = destBytesPerRow;
219
if ((width * 4).hasOverflowed())
221
for (int y = 0; y < height.unsafeGet(); ++y) {
222
for (int x = 0; x < width.unsafeGet(); x++) {
224
unsigned char alpha = srcRows[basex + 3];
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;
231
reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
233
srcRows += srcBytesPerRow;
234
destRows += destBytesPerRow;
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;
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;
253
src.height = height.unsafeGet();
254
src.width = width.unsafeGet();
255
src.rowBytes = srcBytesPerRow;
261
dest.rowBytes = destBytesPerRow;
262
dest.data = destRows;
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.
273
srcBytesPerRow = destBytesPerRow;
279
ScanlineData scanlineData;
280
scanlineData.scanlineWidth = width.unsafeGet();
281
scanlineData.srcData = srcRows;
282
scanlineData.srcRowBytes = srcBytesPerRow;
283
scanlineData.destData = destRows;
284
scanlineData.destRowBytes = destBytesPerRow;
286
dispatch_apply_f(height.unsafeGet(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, unpremultitplyScanline);
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);
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.
301
srcBytesPerRow = destBytesPerRow;
306
if ((width * 4).hasOverflowed())
310
for (int y = 0; y < height.unsafeGet(); ++y) {
311
for (int x = 0; x < width.unsafeGet(); x++) {
313
unsigned char b = srcRows[basex];
314
unsigned char alpha = srcRows[basex + 3];
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;
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];
327
srcRows += srcBytesPerRow;
328
destRows += destBytesPerRow;
331
for (int y = 0; y < height.unsafeGet(); ++y) {
332
for (int x = 0; x < width.unsafeGet(); x++) {
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];
340
srcRows += srcBytesPerRow;
341
destRows += destBytesPerRow;
344
#endif // USE(ACCELERATE)
345
IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, 0);
347
ASSERT_NOT_REACHED();
348
#endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
351
return result.release();
354
void ImageBufferData::putData(Uint8ClampedArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize& size, bool accelerateRendering, bool unmultiplied, float resolutionScale)
356
ASSERT(sourceRect.width() > 0);
357
ASSERT(sourceRect.height() > 0);
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());
367
Checked<int> endx = (Checked<int>(destPoint.x()) + sourceRect.maxX());
368
endx *= resolutionScale;
369
ASSERT(endx.unsafeGet() <= size.width());
371
Checked<int> width = sourceRect.width();
372
Checked<int> destw = endx - destx;
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());
382
Checked<int> endy = (Checked<int>(destPoint.y()) + sourceRect.maxY());
383
endy *= resolutionScale;
384
ASSERT(endy.unsafeGet() <= size.height());
386
Checked<int> height = sourceRect.height();
387
Checked<int> desth = endy - desty;
389
if (width <= 0 || height <= 0)
392
unsigned srcBytesPerRow = 4 * sourceSize.width();
393
unsigned char* srcRows = source->data() + (originy * srcBytesPerRow + originx * 4).unsafeGet();
394
unsigned destBytesPerRow;
395
unsigned char* destRows;
397
if (!accelerateRendering) {
398
destBytesPerRow = 4 * size.width();
399
destRows = reinterpret_cast<unsigned char*>(m_data) + (desty * destBytesPerRow + destx * 4).unsafeGet();
402
if (haveVImageRoundingErrorFix() && unmultiplied) {
404
src.height = height.unsafeGet();
405
src.width = width.unsafeGet();
406
src.rowBytes = srcBytesPerRow;
410
dst.height = desth.unsafeGet();
411
dst.width = destw.unsafeGet();
412
dst.rowBytes = destBytesPerRow;
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.
423
vImagePremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
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.
437
srcBytesPerRow = destBytesPerRow;
442
for (int y = 0; y < height.unsafeGet(); ++y) {
443
for (int x = 0; x < width.unsafeGet(); x++) {
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;
452
reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
454
destRows += destBytesPerRow;
455
srcRows += srcBytesPerRow;
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();
466
src.height = height.unsafeGet();
467
src.width = width.unsafeGet();
468
src.rowBytes = srcBytesPerRow;
472
dest.height = desth.unsafeGet();
473
dest.width = destw.unsafeGet();
474
dest.rowBytes = destBytesPerRow;
475
dest.data = destRows;
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.
486
srcBytesPerRow = destBytesPerRow;
492
ScanlineData scanlineData;
493
scanlineData.scanlineWidth = width.unsafeGet();
494
scanlineData.srcData = srcRows;
495
scanlineData.srcRowBytes = srcBytesPerRow;
496
scanlineData.destData = destRows;
497
scanlineData.destRowBytes = destBytesPerRow;
499
dispatch_apply_f(height.unsafeGet(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, premultitplyScanline);
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);
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.
514
srcBytesPerRow = destBytesPerRow;
519
for (int y = 0; y < height.unsafeGet(); ++y) {
520
for (int x = 0; x < width.unsafeGet(); x++) {
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;
530
destRows[basex] = srcRows[basex + 2];
531
destRows[basex + 1] = srcRows[basex + 1];
532
destRows[basex + 2] = b;
533
destRows[basex + 3] = alpha;
536
destRows += destBytesPerRow;
537
srcRows += srcBytesPerRow;
539
#endif // USE(ACCELERATE)
541
IOSurfaceUnlock(surface, 0, 0);
543
ASSERT_NOT_REACHED();
544
#endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
548
} // namespace WebCore