OSDN Git Service

Merge WebKit at r71558: Initial merge by git.
[android-x86/external-webkit.git] / WebCore / platform / graphics / cg / GraphicsContext3DCG.cpp
index 9f0f353..0c6acf9 100644 (file)
 
 #include <CoreGraphics/CGBitmapContext.h>
 #include <CoreGraphics/CGContext.h>
+#include <CoreGraphics/CGDataProvider.h>
 #include <CoreGraphics/CGImage.h>
 
+#include <wtf/RetainPtr.h>
+
 namespace WebCore {
 
+enum SourceDataFormatBase {
+    SourceFormatBaseR = 0,
+    SourceFormatBaseA,
+    SourceFormatBaseRA,
+    SourceFormatBaseAR,
+    SourceFormatBaseRGB,
+    SourceFormatBaseRGBA,
+    SourceFormatBaseARGB,
+    SourceFormatBaseNumFormats
+};
+
+enum AlphaFormat {
+    AlphaFormatNone = 0,
+    AlphaFormatFirst,
+    AlphaFormatLast,
+    AlphaFormatNumFormats
+};
+
+// This returns SourceFormatNumFormats if the combination of input parameters is unsupported.
+static GraphicsContext3D::SourceDataFormat getSourceDataFormat(unsigned int componentsPerPixel, AlphaFormat alphaFormat, bool is16BitFormat, bool bigEndian)
+{
+    const static SourceDataFormatBase formatTableBase[4][AlphaFormatNumFormats] = { // componentsPerPixel x AlphaFormat
+        // AlphaFormatNone            AlphaFormatFirst            AlphaFormatLast
+        { SourceFormatBaseR,          SourceFormatBaseA,          SourceFormatBaseA          }, // 1 componentsPerPixel
+        { SourceFormatBaseNumFormats, SourceFormatBaseAR,         SourceFormatBaseRA         }, // 2 componentsPerPixel
+        { SourceFormatBaseRGB,        SourceFormatBaseNumFormats, SourceFormatBaseNumFormats }, // 3 componentsPerPixel
+        { SourceFormatBaseNumFormats, SourceFormatBaseARGB,       SourceFormatBaseRGBA        } // 4 componentsPerPixel
+    };
+    const static GraphicsContext3D::SourceDataFormat formatTable[SourceFormatBaseNumFormats][4] = { // SourceDataFormatBase x bitsPerComponent x endian
+        // 8bits, little endian                 8bits, big endian                     16bits, little endian                        16bits, big endian
+        { GraphicsContext3D::SourceFormatR8,    GraphicsContext3D::SourceFormatR8,    GraphicsContext3D::SourceFormatR16Little,    GraphicsContext3D::SourceFormatR16Big },
+        { GraphicsContext3D::SourceFormatA8,    GraphicsContext3D::SourceFormatA8,    GraphicsContext3D::SourceFormatA16Little,    GraphicsContext3D::SourceFormatA16Big },
+        { GraphicsContext3D::SourceFormatAR8,   GraphicsContext3D::SourceFormatRA8,   GraphicsContext3D::SourceFormatRA16Little,   GraphicsContext3D::SourceFormatRA16Big },
+        { GraphicsContext3D::SourceFormatRA8,   GraphicsContext3D::SourceFormatAR8,   GraphicsContext3D::SourceFormatAR16Little,   GraphicsContext3D::SourceFormatAR16Big },
+        { GraphicsContext3D::SourceFormatBGR8,  GraphicsContext3D::SourceFormatRGB8,  GraphicsContext3D::SourceFormatRGB16Little,  GraphicsContext3D::SourceFormatRGB16Big },
+        { GraphicsContext3D::SourceFormatABGR8, GraphicsContext3D::SourceFormatRGBA8, GraphicsContext3D::SourceFormatRGBA16Little, GraphicsContext3D::SourceFormatRGBA16Big },
+        { GraphicsContext3D::SourceFormatBGRA8, GraphicsContext3D::SourceFormatARGB8, GraphicsContext3D::SourceFormatARGB16Little, GraphicsContext3D::SourceFormatARGB16Big }
+    };
+
+    ASSERT(componentsPerPixel <= 4 && componentsPerPixel > 0);
+    SourceDataFormatBase formatBase = formatTableBase[componentsPerPixel - 1][alphaFormat];
+    if (formatBase == SourceFormatBaseNumFormats)
+        return GraphicsContext3D::SourceFormatNumFormats;
+    return formatTable[formatBase][(is16BitFormat ? 2 : 0) + (bigEndian ? 1 : 0)];
+}
+
 bool GraphicsContext3D::getImageData(Image* image,
                                      unsigned int format,
                                      unsigned int type,
@@ -46,64 +95,160 @@ bool GraphicsContext3D::getImageData(Image* image,
 {
     if (!image)
         return false;
-    CGImageRef cgImage = image->nativeImageForCurrentFrame();
+    CGImageRef cgImage;
+    RetainPtr<CGImageRef> decodedImage;
+    if (image->data()) {
+        ImageSource decoder(false);
+        decoder.setData(image->data(), true);
+        if (!decoder.frameCount())
+            return false;
+        decodedImage = decoder.createFrameAtIndex(0);
+        cgImage = decodedImage.get();
+    } else
+        cgImage = image->nativeImageForCurrentFrame();
     if (!cgImage)
         return false;
-    int width = CGImageGetWidth(cgImage);
-    int height = CGImageGetHeight(cgImage);
-    // FIXME: we should get rid of this temporary copy where possible.
-    int tempRowBytes = width * 4;
-    Vector<uint8_t> tempVector;
-    tempVector.resize(height * tempRowBytes);
-    // Try to reuse the color space from the image to preserve its colors.
-    // Some images use a color space (such as indexed) unsupported by the bitmap context.
-    CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
-    bool releaseColorSpace = false;
-    CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
-    switch (colorSpaceModel) {
-    case kCGColorSpaceModelMonochrome:
-    case kCGColorSpaceModelRGB:
-    case kCGColorSpaceModelCMYK:
-    case kCGColorSpaceModelLab:
-    case kCGColorSpaceModelDeviceN:
+
+    size_t width = CGImageGetWidth(cgImage);
+    size_t height = CGImageGetHeight(cgImage);
+    if (!width || !height)
+        return false;
+    size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
+    size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
+    if (bitsPerComponent != 8 && bitsPerComponent != 16)
+        return false;
+    if (bitsPerPixel % bitsPerComponent)
+        return false;
+    size_t componentsPerPixel = bitsPerPixel / bitsPerComponent;
+
+    CGBitmapInfo bitInfo = CGImageGetBitmapInfo(cgImage);
+    bool bigEndianSource = false;
+    // These could technically be combined into one large switch
+    // statement, but we prefer not to so that we fail fast if we
+    // encounter an unexpected image configuration.
+    if (bitsPerComponent == 16) {
+        switch (bitInfo & kCGBitmapByteOrderMask) {
+        case kCGBitmapByteOrder16Big:
+            bigEndianSource = true;
+            break;
+        case kCGBitmapByteOrder16Little:
+            bigEndianSource = false;
+            break;
+        case kCGBitmapByteOrderDefault:
+            // This is a bug in earlier version of cg where the default endian
+            // is little whereas the decoded 16-bit png image data is actually
+            // Big. Later version (10.6.4) no longer returns ByteOrderDefault.
+            bigEndianSource = true;
+            break;
+        default:
+            return false;
+        }
+    } else {
+        switch (bitInfo & kCGBitmapByteOrderMask) {
+        case kCGBitmapByteOrder32Big:
+            bigEndianSource = true;
+            break;
+        case kCGBitmapByteOrder32Little:
+            bigEndianSource = false;
+            break;
+        case kCGBitmapByteOrderDefault:
+            // It appears that the default byte order is actually big
+            // endian even on little endian architectures.
+            bigEndianSource = true;
+            break;
+        default:
+            return false;
+        }
+    }
+
+    AlphaOp neededAlphaOp = AlphaDoNothing;
+    AlphaFormat alphaFormat = AlphaFormatNone;
+    switch (CGImageGetAlphaInfo(cgImage)) {
+    case kCGImageAlphaPremultipliedFirst:
+        // This path is only accessible for MacOS earlier than 10.6.4.
+        // This is a special case for texImage2D with HTMLCanvasElement input,
+        // in which case image->data() should be null.
+        ASSERT(!image->data());
+        if (!premultiplyAlpha)
+            neededAlphaOp = AlphaDoUnmultiply;
+        alphaFormat = AlphaFormatFirst;
         break;
-    default:
-        colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
-        releaseColorSpace = true;
+    case kCGImageAlphaFirst:
+        // This path is only accessible for MacOS earlier than 10.6.4.
+        if (premultiplyAlpha)
+            neededAlphaOp = AlphaDoPremultiply;
+        alphaFormat = AlphaFormatFirst;
+        break;
+    case kCGImageAlphaNoneSkipFirst:
+        // This path is only accessible for MacOS earlier than 10.6.4.
+        alphaFormat = AlphaFormatFirst;
+        break;
+    case kCGImageAlphaPremultipliedLast:
+        // This is a special case for texImage2D with HTMLCanvasElement input,
+        // in which case image->data() should be null.
+        ASSERT(!image->data());
+        if (!premultiplyAlpha)
+            neededAlphaOp = AlphaDoUnmultiply;
+        alphaFormat = AlphaFormatLast;
+        break;
+    case kCGImageAlphaLast:
+        if (premultiplyAlpha)
+            neededAlphaOp = AlphaDoPremultiply;
+        alphaFormat = AlphaFormatLast;
+        break;
+    case kCGImageAlphaNoneSkipLast:
+        alphaFormat = AlphaFormatLast;
+        break;
+    case kCGImageAlphaNone:
+        alphaFormat = AlphaFormatNone;
         break;
+    default:
+        return false;
     }
-    CGContextRef tempContext = CGBitmapContextCreate(tempVector.data(),
-                                                     width, height, 8, tempRowBytes,
-                                                     colorSpace,
-                                                     // FIXME: change this!
-                                                     kCGImageAlphaPremultipliedLast);
-    if (releaseColorSpace)
-        CGColorSpaceRelease(colorSpace);
-    if (!tempContext)
+    SourceDataFormat srcDataFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
+    if (srcDataFormat == SourceFormatNumFormats)
         return false;
-    CGContextSetBlendMode(tempContext, kCGBlendModeCopy);
-    CGContextDrawImage(tempContext,
-                       CGRectMake(0, 0, static_cast<CGFloat>(width), static_cast<CGFloat>(height)),
-                       cgImage);
-    CGContextRelease(tempContext);
-    // Pack the pixel data into the output vector.
-    unsigned long componentsPerPixel, bytesPerComponent;
-    if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel, &bytesPerComponent))
+
+    RetainPtr<CFDataRef> pixelData;
+    pixelData.adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(cgImage)));
+    if (!pixelData)
         return false;
-    int rowBytes = width * componentsPerPixel * bytesPerComponent;
-    outputVector.resize(height * rowBytes);
-    CGImageAlphaInfo info = CGImageGetAlphaInfo(cgImage);
-    bool hasAlphaChannel = (info != kCGImageAlphaNone
-                            && info != kCGImageAlphaNoneSkipLast
-                            && info != kCGImageAlphaNoneSkipFirst);
-    AlphaOp neededAlphaOp = kAlphaDoNothing;
-    if (!premultiplyAlpha && hasAlphaChannel)
-        // FIXME: must fetch the image data before the premultiplication step.
-        neededAlphaOp = kAlphaDoUnmultiply;
-    return packPixels(tempVector.data(), kSourceFormatRGBA8, width, height, 0,
-                      format, type, neededAlphaOp, outputVector.data());
+    const UInt8* rgba = CFDataGetBytePtr(pixelData.get());
+    outputVector.resize(width * height * 4);
+    unsigned int srcUnpackAlignment = 0;
+    size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
+    unsigned int padding = bytesPerRow - bitsPerPixel / 8 * width;
+    if (padding) {
+        srcUnpackAlignment = padding + 1;
+        while (bytesPerRow % srcUnpackAlignment)
+            ++srcUnpackAlignment;
+    }
+    bool rt = packPixels(rgba, srcDataFormat, width, height, srcUnpackAlignment,
+                         format, type, neededAlphaOp, outputVector.data());
+    return rt;
 }
 
+void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, CGContextRef context)
+{
+    if (!imagePixels || imageWidth <= 0 || imageHeight <= 0 || canvasWidth <= 0 || canvasHeight <= 0 || !context)
+        return;
+    int rowBytes = imageWidth * 4;
+    RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithData(0, imagePixels, rowBytes * imageHeight, 0));
+    RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
+    RetainPtr<CGImageRef> cgImage(AdoptCF, CGImageCreate(imageWidth, imageHeight, 8, 32, rowBytes, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+        dataProvider.get(), 0, false, kCGRenderingIntentDefault));
+    // CSS styling may cause the canvas's content to be resized on
+    // the page. Go back to the Canvas to figure out the correct
+    // width and height to draw.
+    CGRect rect = CGRectMake(0, 0, canvasWidth, canvasHeight);
+    // We want to completely overwrite the previous frame's
+    // rendering results.
+    CGContextSaveGState(context);
+    CGContextSetBlendMode(context, kCGBlendModeCopy);
+    CGContextSetInterpolationQuality(context, kCGInterpolationNone);
+    CGContextDrawImage(context, rect, cgImage.get());
+    CGContextRestoreGState(context);
+}
 
 } // namespace WebCore