Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / GraphicsContext3DCG.cpp
index 187d296..f5a3684 100644 (file)
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
 #include "config.h"
 
-#if ENABLE(WEBGL)
+#if ENABLE(GRAPHICS_CONTEXT_3D)
 
 #include "GraphicsContext3D.h"
-#include "GraphicsContextCG.h"
 
+#include "BitmapImage.h"
+#include "GraphicsContextCG.h"
 #include "Image.h"
 
+#if HAVE(ARM_NEON_INTRINSICS)
+#include "GraphicsContext3DNEON.h"
+#endif
+
 #include <CoreGraphics/CGBitmapContext.h>
 #include <CoreGraphics/CGContext.h>
 #include <CoreGraphics/CGDataProvider.h>
 #include <CoreGraphics/CGImage.h>
 
 #include <wtf/RetainPtr.h>
+#include <wtf/StdLibExtras.h>
 
 namespace WebCore {
 
@@ -61,7 +67,7 @@ enum AlphaFormat {
 };
 
 // This returns SourceFormatNumFormats if the combination of input parameters is unsupported.
-static GraphicsContext3D::SourceDataFormat getSourceDataFormat(unsigned int componentsPerPixel, AlphaFormat alphaFormat, bool is16BitFormat, bool bigEndian)
+static GraphicsContext3D::DataFormat getSourceDataFormat(unsigned componentsPerPixel, AlphaFormat alphaFormat, bool is16BitFormat, bool bigEndian)
 {
     const static SourceDataFormatBase formatTableBase[4][AlphaFormatNumFormats] = { // componentsPerPixel x AlphaFormat
         // AlphaFormatNone            AlphaFormatFirst            AlphaFormatLast
@@ -70,58 +76,277 @@ static GraphicsContext3D::SourceDataFormat getSourceDataFormat(unsigned int comp
         { SourceFormatBaseRGB,        SourceFormatBaseNumFormats, SourceFormatBaseNumFormats }, // 3 componentsPerPixel
         { SourceFormatBaseNumFormats, SourceFormatBaseARGB,       SourceFormatBaseRGBA        } // 4 componentsPerPixel
     };
-    const static GraphicsContext3D::SourceDataFormat formatTable[SourceFormatBaseNumFormats][4] = { // SourceDataFormatBase x bitsPerComponent x endian
+    const static GraphicsContext3D::DataFormat 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 }
+        { GraphicsContext3D::DataFormatR8,    GraphicsContext3D::DataFormatR8,    GraphicsContext3D::DataFormatR16Little,    GraphicsContext3D::DataFormatR16Big },
+        { GraphicsContext3D::DataFormatA8,    GraphicsContext3D::DataFormatA8,    GraphicsContext3D::DataFormatA16Little,    GraphicsContext3D::DataFormatA16Big },
+        { GraphicsContext3D::DataFormatAR8,   GraphicsContext3D::DataFormatRA8,   GraphicsContext3D::DataFormatRA16Little,   GraphicsContext3D::DataFormatRA16Big },
+        { GraphicsContext3D::DataFormatRA8,   GraphicsContext3D::DataFormatAR8,   GraphicsContext3D::DataFormatAR16Little,   GraphicsContext3D::DataFormatAR16Big },
+        { GraphicsContext3D::DataFormatBGR8,  GraphicsContext3D::DataFormatRGB8,  GraphicsContext3D::DataFormatRGB16Little,  GraphicsContext3D::DataFormatRGB16Big },
+        { GraphicsContext3D::DataFormatABGR8, GraphicsContext3D::DataFormatRGBA8, GraphicsContext3D::DataFormatRGBA16Little, GraphicsContext3D::DataFormatRGBA16Big },
+        { GraphicsContext3D::DataFormatBGRA8, GraphicsContext3D::DataFormatARGB8, GraphicsContext3D::DataFormatARGB16Little, GraphicsContext3D::DataFormatARGB16Big }
     };
 
     ASSERT(componentsPerPixel <= 4 && componentsPerPixel > 0);
     SourceDataFormatBase formatBase = formatTableBase[componentsPerPixel - 1][alphaFormat];
     if (formatBase == SourceFormatBaseNumFormats)
-        return GraphicsContext3D::SourceFormatNumFormats;
+        return GraphicsContext3D::DataFormatNumFormats;
     return formatTable[formatBase][(is16BitFormat ? 2 : 0) + (bigEndian ? 1 : 0)];
 }
 
-bool GraphicsContext3D::getImageData(Image* image,
-                                     GC3Denum format,
-                                     GC3Denum type,
-                                     bool premultiplyAlpha,
-                                     bool ignoreGammaAndColorProfile,
-                                     Vector<uint8_t>& outputVector)
+namespace {
+uint8_t convertColor16LittleTo8(uint16_t value)
+{
+    return value >> 8;
+}
+
+uint8_t convertColor16BigTo8(uint16_t value)
+{
+    return static_cast<uint8_t>(value & 0x00FF);
+}
+
+template<int format, typename SourceType, typename DstType>
+ALWAYS_INLINE void convert16BitFormatToRGBA8(const SourceType*, DstType*, unsigned)
+{
+    ASSERT_NOT_REACHED();
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGBA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+#if HAVE(ARM_NEON_INTRINSICS)
+    SIMD::unpackOneRowOfRGBA16LittleToRGBA8(source, destination, pixelsPerRow);
+#endif
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16LittleTo8(source[0]);
+        destination[1] = convertColor16LittleTo8(source[1]);
+        destination[2] = convertColor16LittleTo8(source[2]);
+        destination[3] = convertColor16LittleTo8(source[3]);
+        source += 4;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGBA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16BigTo8(source[0]);
+        destination[1] = convertColor16BigTo8(source[1]);
+        destination[2] = convertColor16BigTo8(source[2]);
+        destination[3] = convertColor16BigTo8(source[3]);
+        source += 4;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGB16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+#if HAVE(ARM_NEON_INTRINSICS)
+    SIMD::unpackOneRowOfRGB16LittleToRGBA8(source, destination, pixelsPerRow);
+#endif
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16LittleTo8(source[0]);
+        destination[1] = convertColor16LittleTo8(source[1]);
+        destination[2] = convertColor16LittleTo8(source[2]);
+        destination[3] = 0xFF;
+        source += 3;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGB16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16BigTo8(source[0]);
+        destination[1] = convertColor16BigTo8(source[1]);
+        destination[2] = convertColor16BigTo8(source[2]);
+        destination[3] = 0xFF;
+        source += 3;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatARGB16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+#if HAVE(ARM_NEON_INTRINSICS)
+    SIMD::unpackOneRowOfARGB16LittleToRGBA8(source, destination, pixelsPerRow);
+#endif
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16LittleTo8(source[1]);
+        destination[1] = convertColor16LittleTo8(source[2]);
+        destination[2] = convertColor16LittleTo8(source[3]);
+        destination[3] = convertColor16LittleTo8(source[0]);
+        source += 4;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatARGB16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16BigTo8(source[1]);
+        destination[1] = convertColor16BigTo8(source[2]);
+        destination[2] = convertColor16BigTo8(source[3]);
+        destination[3] = convertColor16BigTo8(source[0]);
+        source += 4;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatR16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16LittleTo8(source[0]);
+        destination[1] = convertColor16LittleTo8(source[0]);
+        destination[2] = convertColor16LittleTo8(source[0]);
+        destination[3] = 0xFF;
+        source += 1;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatR16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16BigTo8(source[0]);
+        destination[1] = convertColor16BigTo8(source[0]);
+        destination[2] = convertColor16BigTo8(source[0]);
+        destination[3] = 0xFF;
+        source += 1;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16LittleTo8(source[0]);
+        destination[1] = convertColor16LittleTo8(source[0]);
+        destination[2] = convertColor16LittleTo8(source[0]);
+        destination[3] = convertColor16LittleTo8(source[1]);
+        source += 2;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
 {
-    if (!image)
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16BigTo8(source[0]);
+        destination[1] = convertColor16BigTo8(source[0]);
+        destination[2] = convertColor16BigTo8(source[0]);
+        destination[3] = convertColor16BigTo8(source[1]);
+        source += 2;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatAR16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16LittleTo8(source[1]);
+        destination[1] = convertColor16LittleTo8(source[1]);
+        destination[2] = convertColor16LittleTo8(source[1]);
+        destination[3] = convertColor16LittleTo8(source[0]);
+        source += 2;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatAR16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = convertColor16BigTo8(source[1]);
+        destination[1] = convertColor16BigTo8(source[1]);
+        destination[2] = convertColor16BigTo8(source[1]);
+        destination[3] = convertColor16BigTo8(source[0]);
+        source += 2;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = 0x0;
+        destination[1] = 0x0;
+        destination[2] = 0x0;
+        destination[3] = convertColor16LittleTo8(source[0]);
+        source += 1;
+        destination += 4;
+    }
+}
+
+template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+    for (unsigned i = 0; i < pixelsPerRow; ++i) {
+        destination[0] = 0x0;
+        destination[1] = 0x0;
+        destination[2] = 0x0;
+        destination[3] = convertColor16BigTo8(source[0]);
+        source += 1;
+        destination += 4;
+    }
+}
+
+void convert16BitFormatToRGBA8(GraphicsContext3D::DataFormat srcFormat, const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
+{
+#define CONVERT16BITFORMATTORGBA8(SrcFormat) \
+    case SrcFormat: \
+        return convert16BitFormatToRGBA8<SrcFormat>(source, destination, pixelsPerRow);
+
+    switch (srcFormat) {
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatR16Little)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatR16Big)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatA16Little)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatA16Big)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRA16Little)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRA16Big)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatAR16Little)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatAR16Big)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGB16Little)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGB16Big)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGBA16Little)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGBA16Big)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatARGB16Little)
+        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatARGB16Big)
+    default:
+        ASSERT_NOT_REACHED();
+    }
+#undef CONVERT16BITFORMATTORGBA8
+}
+
+}
+
+GraphicsContext3D::ImageExtractor::~ImageExtractor() = default;
+
+bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
+{
+    if (!m_image)
         return false;
-    CGImageRef cgImage;
-    RetainPtr<CGImageRef> decodedImage;
-    if (image->data()) {
-        ImageSource decoder(ImageSource::AlphaNotPremultiplied,
-                            ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
-        decoder.setData(image->data(), true);
+    bool hasAlpha = !m_image->currentFrameKnownToBeOpaque();
+    if ((ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && m_image->data()) {
+        ImageSource decoder(nullptr, AlphaOption::NotPremultiplied, ignoreGammaAndColorProfile ? GammaAndColorProfileOption::Ignored : GammaAndColorProfileOption::Applied);
+        decoder.setData(m_image->data(), true);
         if (!decoder.frameCount())
             return false;
-        decodedImage.adoptCF(decoder.createFrameAtIndex(0));
-        cgImage = decodedImage.get();
+
+        m_decodedImage = decoder.createFrameImageAtIndex(0);
+        m_cgImage = m_decodedImage;
     } else
-        cgImage = image->nativeImageForCurrentFrame();
-    if (!cgImage)
+        m_cgImage = m_image->nativeImageForCurrentFrame();
+    if (!m_cgImage)
         return false;
 
-    size_t width = CGImageGetWidth(cgImage);
-    size_t height = CGImageGetHeight(cgImage);
-    if (!width || !height)
+    m_imageWidth = CGImageGetWidth(m_cgImage.get());
+    m_imageHeight = CGImageGetHeight(m_cgImage.get());
+    if (!m_imageWidth || !m_imageHeight)
         return false;
 
     // See whether the image is using an indexed color space, and if
     // so, re-render it into an RGB color space. The image re-packing
     // code requires color data, not color table indices, for the
     // image data.
-    CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
+    CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_cgImage.get());
     CGColorSpaceModel model = CGColorSpaceGetModel(colorSpace);
     if (model == kCGColorSpaceModelIndexed) {
         RetainPtr<CGContextRef> bitmapContext;
@@ -129,30 +354,29 @@ bool GraphicsContext3D::getImageData(Image* image,
         // the color table, which would allow us to avoid premultiplying the
         // alpha channel. Creation of a bitmap context with an alpha channel
         // doesn't seem to work unless it's premultiplied.
-        bitmapContext.adoptCF(CGBitmapContextCreate(0, width, height, 8, width * 4,
-                                                    deviceRGBColorSpaceRef(),
-                                                    kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
+        bitmapContext = adoptCF(CGBitmapContextCreate(0, m_imageWidth, m_imageHeight, 8, m_imageWidth * 4,
+            sRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
         if (!bitmapContext)
             return false;
 
         CGContextSetBlendMode(bitmapContext.get(), kCGBlendModeCopy);
         CGContextSetInterpolationQuality(bitmapContext.get(), kCGInterpolationNone);
-        CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, width, height), cgImage);
+        CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, m_imageWidth, m_imageHeight), m_cgImage.get());
 
         // Now discard the original CG image and replace it with a copy from the bitmap context.
-        decodedImage.adoptCF(CGBitmapContextCreateImage(bitmapContext.get()));
-        cgImage = decodedImage.get();
+        m_decodedImage = adoptCF(CGBitmapContextCreateImage(bitmapContext.get()));
+        m_cgImage = m_decodedImage.get();
     }
 
-    size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
-    size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
+    size_t bitsPerComponent = CGImageGetBitsPerComponent(m_cgImage.get());
+    size_t bitsPerPixel = CGImageGetBitsPerPixel(m_cgImage.get());
     if (bitsPerComponent != 8 && bitsPerComponent != 16)
         return false;
     if (bitsPerPixel % bitsPerComponent)
         return false;
     size_t componentsPerPixel = bitsPerPixel / bitsPerComponent;
 
-    CGBitmapInfo bitInfo = CGImageGetBitmapInfo(cgImage);
+    CGBitmapInfo bitInfo = CGImageGetBitmapInfo(m_cgImage.get());
     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
@@ -192,23 +416,18 @@ bool GraphicsContext3D::getImageData(Image* image,
         }
     }
 
-    AlphaOp neededAlphaOp = AlphaDoNothing;
+    m_alphaOp = AlphaDoNothing;
     AlphaFormat alphaFormat = AlphaFormatNone;
-    switch (CGImageGetAlphaInfo(cgImage)) {
+    switch (CGImageGetAlphaInfo(m_cgImage.get())) {
     case kCGImageAlphaPremultipliedFirst:
-        // This is a special case for texImage2D with HTMLCanvasElement input,
-        // in which case image->data() should be null, or indexed color models,
-        // where we need premultiplied alpha to create the bitmap context
-        // successfully.
-        ASSERT(!image->data() || model == kCGColorSpaceModelIndexed);
         if (!premultiplyAlpha)
-            neededAlphaOp = AlphaDoUnmultiply;
+            m_alphaOp = AlphaDoUnmultiply;
         alphaFormat = AlphaFormatFirst;
         break;
     case kCGImageAlphaFirst:
         // This path is only accessible for MacOS earlier than 10.6.4.
         if (premultiplyAlpha)
-            neededAlphaOp = AlphaDoPremultiply;
+            m_alphaOp = AlphaDoPremultiply;
         alphaFormat = AlphaFormatFirst;
         break;
     case kCGImageAlphaNoneSkipFirst:
@@ -216,16 +435,13 @@ bool GraphicsContext3D::getImageData(Image* image,
         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;
+            m_alphaOp = AlphaDoUnmultiply;
         alphaFormat = AlphaFormatLast;
         break;
     case kCGImageAlphaLast:
         if (premultiplyAlpha)
-            neededAlphaOp = AlphaDoPremultiply;
+            m_alphaOp = AlphaDoPremultiply;
         alphaFormat = AlphaFormatLast;
         break;
     case kCGImageAlphaNoneSkipLast:
@@ -237,50 +453,89 @@ bool GraphicsContext3D::getImageData(Image* image,
     default:
         return false;
     }
-    SourceDataFormat srcDataFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
-    if (srcDataFormat == SourceFormatNumFormats)
+
+    m_imageSourceFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
+    if (m_imageSourceFormat == DataFormatNumFormats)
         return false;
 
-    RetainPtr<CFDataRef> pixelData;
-    pixelData.adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(cgImage)));
-    if (!pixelData)
+    m_pixelData = adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(m_cgImage.get())));
+    if (!m_pixelData)
         return false;
-    const UInt8* rgba = CFDataGetBytePtr(pixelData.get());
-    outputVector.resize(width * height * 4);
+
+    m_imagePixelData = reinterpret_cast<const void*>(CFDataGetBytePtr(m_pixelData.get()));
+
     unsigned int srcUnpackAlignment = 0;
-    size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
-    unsigned int padding = bytesPerRow - bitsPerPixel / 8 * width;
+    size_t bytesPerRow = CGImageGetBytesPerRow(m_cgImage.get());
+    unsigned padding = bytesPerRow - bitsPerPixel / 8 * m_imageWidth;
     if (padding) {
         srcUnpackAlignment = padding + 1;
         while (bytesPerRow % srcUnpackAlignment)
             ++srcUnpackAlignment;
     }
-    bool rt = packPixels(rgba, srcDataFormat, width, height, srcUnpackAlignment,
-                         format, type, neededAlphaOp, outputVector.data());
-    return rt;
+
+    m_imageSourceUnpackAlignment = srcUnpackAlignment;
+    // Using a bitmap context created according to destination format and drawing the CGImage to the bitmap context can also do the format conversion,
+    // but it would premultiply the alpha channel as a side effect.
+    // Prefer to mannually Convert 16bit per-component formats to RGBA8 formats instead.
+    if (bitsPerComponent == 16) {
+        m_formalizedRGBA8Data = std::make_unique<uint8_t[]>(m_imageWidth * m_imageHeight * 4);
+        const uint16_t* source = reinterpret_cast<const uint16_t*>(m_imagePixelData);
+        uint8_t* destination = m_formalizedRGBA8Data.get();
+        const ptrdiff_t srcStrideInElements = bytesPerRow / sizeof(uint16_t);
+        const ptrdiff_t dstStrideInElements = 4 * m_imageWidth;
+        for (unsigned i =0; i < m_imageHeight; i++) {
+            convert16BitFormatToRGBA8(m_imageSourceFormat, source, destination, m_imageWidth);
+            source += srcStrideInElements;
+            destination += dstStrideInElements;
+        }
+        m_imagePixelData = reinterpret_cast<const void*>(m_formalizedRGBA8Data.get());
+        m_imageSourceFormat = DataFormatRGBA8;
+        m_imageSourceUnpackAlignment = 1;
+    }
+    return true;
+}
+
+static void releaseImageData(void*, const void* data, size_t)
+{
+    fastFree(const_cast<void*>(data));
 }
 
-void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, CGContextRef context)
+void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, GraphicsContext& context)
 {
-    if (!imagePixels || imageWidth <= 0 || imageHeight <= 0 || canvasWidth <= 0 || canvasHeight <= 0 || !context)
+    if (!imagePixels || imageWidth <= 0 || imageHeight <= 0 || canvasWidth <= 0 || canvasHeight <= 0)
         return;
     int rowBytes = imageWidth * 4;
-    RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithData(0, imagePixels, rowBytes * imageHeight, 0));
-    RetainPtr<CGImageRef> cgImage(AdoptCF, CGImageCreate(imageWidth, imageHeight, 8, 32, rowBytes, deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+    RetainPtr<CGDataProviderRef> dataProvider;
+
+    if (context.isAcceleratedContext()) {
+        unsigned char* copiedPixels;
+
+        if (!tryFastCalloc(imageHeight, rowBytes).getValue(copiedPixels))
+            return;
+
+        memcpy(copiedPixels, imagePixels, rowBytes * imageHeight);
+        dataProvider = adoptCF(CGDataProviderCreateWithData(0, copiedPixels, rowBytes * imageHeight, releaseImageData));
+    } else
+        dataProvider = adoptCF(CGDataProviderCreateWithData(0, imagePixels, rowBytes * imageHeight, 0));
+
+    RetainPtr<CGImageRef> cgImage = adoptCF(CGImageCreate(imageWidth, imageHeight, 8, 32, rowBytes, sRGBColorSpaceRef(), 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);
+    FloatRect canvasRect(0, 0, canvasWidth, canvasHeight);
+    FloatSize imageSize(imageWidth, imageHeight);
     // 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);
+
+    GraphicsContextStateSaver stateSaver(context);
+    context.scale(FloatSize(1, -1));
+    context.translate(0, -imageHeight);
+    context.setImageInterpolationQuality(InterpolationNone);
+    context.drawNativeImage(cgImage, imageSize, canvasRect, FloatRect(FloatPoint(), imageSize), CompositeCopy);
 }
 
 } // namespace WebCore
 
-#endif // ENABLE(WEBGL)
+#endif // ENABLE(GRAPHICS_CONTEXT_3D)