Home | History | Annotate | Download | only in cg
      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 <wtf/Assertions.h>
     30 
     31 #if USE(ACCELERATE)
     32 #include <Accelerate/Accelerate.h>
     33 #endif
     34 
     35 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
     36 #include <IOSurface/IOSurface.h>
     37 #include <dispatch/dispatch.h>
     38 #endif
     39 
     40 #if USE(ACCELERATE)
     41 struct ScanlineData {
     42     vImagePixelCount scanlineWidth;
     43     unsigned char* srcData;
     44     size_t srcRowBytes;
     45     unsigned char* destData;
     46     size_t destRowBytes;
     47 };
     48 #endif
     49 
     50 namespace WebCore {
     51 
     52 ImageBufferData::ImageBufferData(const IntSize&)
     53 : m_data(0)
     54 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
     55 , m_surface(0)
     56 #endif
     57 {
     58 }
     59 
     60 #if USE(ACCELERATE)
     61 // The vImage unpremultiply routine had a rounding bug before 10.6.7 <rdar://problem/8631548>
     62 static bool haveVImageRoundingErrorFix()
     63 {
     64     SInt32 version;
     65     static bool result = (Gestalt(gestaltSystemVersion, &version) == noErr && version > 0x1066);
     66     return result;
     67 }
     68 
     69 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
     70 static void convertScanline(void* data, size_t tileNumber, bool premultiply)
     71 {
     72     ScanlineData* scanlineData = static_cast<ScanlineData*>(data);
     73 
     74     vImage_Buffer src;
     75     src.data = scanlineData->srcData + tileNumber * scanlineData->srcRowBytes;
     76     src.height = 1;
     77     src.width = scanlineData->scanlineWidth;
     78     src.rowBytes = scanlineData->srcRowBytes;
     79 
     80     vImage_Buffer dest;
     81     dest.data = scanlineData->destData + tileNumber * scanlineData->destRowBytes;
     82     dest.height = 1;
     83     dest.width = scanlineData->scanlineWidth;
     84     dest.rowBytes = scanlineData->destRowBytes;
     85 
     86     if (premultiply) {
     87         if (kvImageNoError != vImagePremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile))
     88             return;
     89     } else {
     90         if (kvImageNoError != vImageUnpremultiplyData_RGBA8888(&src, &dest, kvImageDoNotTile))
     91             return;
     92     }
     93 
     94     // Swap channels 1 and 3, to convert BGRA<->RGBA. IOSurfaces is BGRA, ImageData expects RGBA.
     95     const uint8_t map[4] = { 2, 1, 0, 3 };
     96     vImagePermuteChannels_ARGB8888(&dest, &dest, map, kvImageDoNotTile);
     97 }
     98 
     99 static void unpremultitplyScanline(void* data, size_t tileNumber)
    100 {
    101     convertScanline(data, tileNumber, false);
    102 }
    103 
    104 static void premultitplyScanline(void* data, size_t tileNumber)
    105 {
    106     convertScanline(data, tileNumber, true);
    107 }
    108 #endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
    109 #endif // USE(ACCELERATE)
    110 
    111 PassRefPtr<ByteArray> ImageBufferData::getData(const IntRect& rect, const IntSize& size, bool accelerateRendering, bool unmultiplied) const
    112 {
    113     float area = 4.0f * rect.width() * rect.height();
    114     if (area > static_cast<float>(std::numeric_limits<int>::max()))
    115         return 0;
    116 
    117     RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4);
    118     unsigned char* data = result->data();
    119 
    120     if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height())
    121         memset(data, 0, result->length());
    122 
    123     int originx = rect.x();
    124     int destx = 0;
    125     if (originx < 0) {
    126         destx = -originx;
    127         originx = 0;
    128     }
    129     int endx = rect.maxX();
    130     if (endx > size.width())
    131         endx = size.width();
    132     int width = endx - originx;
    133 
    134     int originy = rect.y();
    135     int desty = 0;
    136     if (originy < 0) {
    137         desty = -originy;
    138         originy = 0;
    139     }
    140     int endy = rect.maxY();
    141     if (endy > size.height())
    142         endy = size.height();
    143     int height = endy - originy;
    144 
    145     if (width <= 0 || height <= 0)
    146         return result.release();
    147 
    148     unsigned destBytesPerRow = 4 * rect.width();
    149     unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
    150 
    151     unsigned srcBytesPerRow;
    152     unsigned char* srcRows;
    153 
    154     if (!accelerateRendering) {
    155         srcBytesPerRow = 4 * size.width();
    156         srcRows = reinterpret_cast<unsigned char*>(m_data) + originy * srcBytesPerRow + originx * 4;
    157 
    158 #if USE(ACCELERATE)
    159         if (unmultiplied && haveVImageRoundingErrorFix()) {
    160             vImage_Buffer src;
    161             src.height = height;
    162             src.width = width;
    163             src.rowBytes = srcBytesPerRow;
    164             src.data = srcRows;
    165 
    166             vImage_Buffer dst;
    167             dst.height = height;
    168             dst.width = width;
    169             dst.rowBytes = destBytesPerRow;
    170             dst.data = destRows;
    171 
    172             vImageUnpremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
    173             return result.release();
    174         }
    175 #endif
    176         for (int y = 0; y < height; ++y) {
    177             for (int x = 0; x < width; x++) {
    178                 int basex = x * 4;
    179                 unsigned char alpha = srcRows[basex + 3];
    180                 if (unmultiplied && alpha) {
    181                     destRows[basex] = (srcRows[basex] * 255) / alpha;
    182                     destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
    183                     destRows[basex + 2] = (srcRows[basex + 2] * 255) / alpha;
    184                     destRows[basex + 3] = alpha;
    185                 } else
    186                     reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
    187             }
    188             srcRows += srcBytesPerRow;
    189             destRows += destBytesPerRow;
    190         }
    191     } else {
    192 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
    193         IOSurfaceRef surface = m_surface.get();
    194         IOSurfaceLock(surface, kIOSurfaceLockReadOnly, 0);
    195         srcBytesPerRow = IOSurfaceGetBytesPerRow(surface);
    196         srcRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + originy * srcBytesPerRow + originx * 4;
    197 
    198 #if USE(ACCELERATE)
    199         if (unmultiplied) {
    200             ScanlineData scanlineData;
    201             scanlineData.scanlineWidth = width;
    202             scanlineData.srcData = srcRows;
    203             scanlineData.srcRowBytes = srcBytesPerRow;
    204             scanlineData.destData = destRows;
    205             scanlineData.destRowBytes = destBytesPerRow;
    206 
    207             dispatch_apply_f(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, unpremultitplyScanline);
    208         } else {
    209             vImage_Buffer src;
    210             src.height = height;
    211             src.width = width;
    212             src.rowBytes = srcBytesPerRow;
    213             src.data = srcRows;
    214 
    215             vImage_Buffer dest;
    216             dest.height = height;
    217             dest.width = width;
    218             dest.rowBytes = destBytesPerRow;
    219             dest.data = destRows;
    220 
    221             // Swap pixel channels from BGRA to RGBA.
    222             const uint8_t map[4] = { 2, 1, 0, 3 };
    223             vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
    224         }
    225 #else
    226         for (int y = 0; y < height; ++y) {
    227             for (int x = 0; x < width; x++) {
    228                 int basex = x * 4;
    229                 unsigned char alpha = srcRows[basex + 3];
    230                 if (unmultiplied && alpha) {
    231                     destRows[basex] = (srcRows[basex + 2] * 255) / alpha;
    232                     destRows[basex + 1] = (srcRows[basex + 1] * 255) / alpha;
    233                     destRows[basex + 2] = (srcRows[basex] * 255) / alpha;
    234                     destRows[basex + 3] = alpha;
    235                 } else {
    236                     destRows[basex] = srcRows[basex + 2];
    237                     destRows[basex + 1] = srcRows[basex + 1];
    238                     destRows[basex + 2] = srcRows[basex];
    239                     destRows[basex + 3] = alpha;
    240                 }
    241             }
    242             srcRows += srcBytesPerRow;
    243             destRows += destBytesPerRow;
    244         }
    245 #endif // USE(ACCELERATE)
    246         IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, 0);
    247 #else
    248         ASSERT_NOT_REACHED();
    249 #endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
    250     }
    251 
    252     return result.release();
    253 }
    254 
    255 void ImageBufferData::putData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, const IntSize& size, bool accelerateRendering, bool unmultiplied)
    256 {
    257     ASSERT(sourceRect.width() > 0);
    258     ASSERT(sourceRect.height() > 0);
    259 
    260     int originx = sourceRect.x();
    261     int destx = destPoint.x() + sourceRect.x();
    262     ASSERT(destx >= 0);
    263     ASSERT(destx < size.width());
    264     ASSERT(originx >= 0);
    265     ASSERT(originx <= sourceRect.maxX());
    266 
    267     int endx = destPoint.x() + sourceRect.maxX();
    268     ASSERT(endx <= size.width());
    269 
    270     int width = endx - destx;
    271 
    272     int originy = sourceRect.y();
    273     int desty = destPoint.y() + sourceRect.y();
    274     ASSERT(desty >= 0);
    275     ASSERT(desty < size.height());
    276     ASSERT(originy >= 0);
    277     ASSERT(originy <= sourceRect.maxY());
    278 
    279     int endy = destPoint.y() + sourceRect.maxY();
    280     ASSERT(endy <= size.height());
    281     int height = endy - desty;
    282 
    283     if (width <= 0 || height <= 0)
    284         return;
    285 
    286     unsigned srcBytesPerRow = 4 * sourceSize.width();
    287     unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4;
    288     unsigned destBytesPerRow;
    289     unsigned char* destRows;
    290 
    291     if (!accelerateRendering) {
    292         destBytesPerRow = 4 * size.width();
    293         destRows = reinterpret_cast<unsigned char*>(m_data) + desty * destBytesPerRow + destx * 4;
    294 
    295 #if USE(ACCELERATE)
    296         if (haveVImageRoundingErrorFix() && unmultiplied) {
    297             vImage_Buffer src;
    298             src.height = height;
    299             src.width = width;
    300             src.rowBytes = srcBytesPerRow;
    301             src.data = srcRows;
    302 
    303             vImage_Buffer dst;
    304             dst.height = height;
    305             dst.width = width;
    306             dst.rowBytes = destBytesPerRow;
    307             dst.data = destRows;
    308 
    309             vImagePremultiplyData_RGBA8888(&src, &dst, kvImageNoFlags);
    310             return;
    311         }
    312 #endif
    313         for (int y = 0; y < height; ++y) {
    314             for (int x = 0; x < width; x++) {
    315                 int basex = x * 4;
    316                 unsigned char alpha = srcRows[basex + 3];
    317                 if (unmultiplied && alpha != 255) {
    318                     destRows[basex] = (srcRows[basex] * alpha + 254) / 255;
    319                     destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
    320                     destRows[basex + 2] = (srcRows[basex + 2] * alpha + 254) / 255;
    321                     destRows[basex + 3] = alpha;
    322                 } else
    323                     reinterpret_cast<uint32_t*>(destRows + basex)[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0];
    324             }
    325             destRows += destBytesPerRow;
    326             srcRows += srcBytesPerRow;
    327         }
    328     } else {
    329 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
    330         IOSurfaceRef surface = m_surface.get();
    331         IOSurfaceLock(surface, 0, 0);
    332         destBytesPerRow = IOSurfaceGetBytesPerRow(surface);
    333         destRows = (unsigned char*)(IOSurfaceGetBaseAddress(surface)) + desty * destBytesPerRow + destx * 4;
    334 
    335 #if USE(ACCELERATE)
    336         if (unmultiplied) {
    337             ScanlineData scanlineData;
    338             scanlineData.scanlineWidth = width;
    339             scanlineData.srcData = srcRows;
    340             scanlineData.srcRowBytes = srcBytesPerRow;
    341             scanlineData.destData = destRows;
    342             scanlineData.destRowBytes = destBytesPerRow;
    343 
    344             dispatch_apply_f(height, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), &scanlineData, premultitplyScanline);
    345         } else {
    346             vImage_Buffer src;
    347             src.height = height;
    348             src.width = width;
    349             src.rowBytes = srcBytesPerRow;
    350             src.data = srcRows;
    351 
    352             vImage_Buffer dest;
    353             dest.height = height;
    354             dest.width = width;
    355             dest.rowBytes = destBytesPerRow;
    356             dest.data = destRows;
    357 
    358             // Swap pixel channels from RGBA to BGRA.
    359             const uint8_t map[4] = { 2, 1, 0, 3 };
    360             vImagePermuteChannels_ARGB8888(&src, &dest, map, kvImageNoFlags);
    361         }
    362 #else
    363         for (int y = 0; y < height; ++y) {
    364             for (int x = 0; x < width; x++) {
    365                 int basex = x * 4;
    366                 unsigned char alpha = srcRows[basex + 3];
    367                 if (unmultiplied && alpha != 255) {
    368                     destRows[basex] = (srcRows[basex + 2] * alpha + 254) / 255;
    369                     destRows[basex + 1] = (srcRows[basex + 1] * alpha + 254) / 255;
    370                     destRows[basex + 2] = (srcRows[basex] * alpha + 254) / 255;
    371                     destRows[basex + 3] = alpha;
    372                 } else {
    373                     destRows[basex] = srcRows[basex + 2];
    374                     destRows[basex + 1] = srcRows[basex + 1];
    375                     destRows[basex + 2] = srcRows[basex];
    376                     destRows[basex + 3] = alpha;
    377                 }
    378             }
    379             destRows += destBytesPerRow;
    380             srcRows += srcBytesPerRow;
    381         }
    382 #endif // USE(ACCELERATE)
    383 
    384         IOSurfaceUnlock(surface, 0, 0);
    385 #else
    386         ASSERT_NOT_REACHED();
    387 #endif // USE(IOSURFACE_CANVAS_BACKING_STORE)
    388     }
    389 }
    390 
    391 } // namespace WebCore
    392