#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,
{
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