X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=WebCore%2Fplatform%2Fgraphics%2Fcg%2FGraphicsContext3DCG.cpp;h=0c6acf9bb2c5157d182cd38f4ea7fa980d3e1c68;hb=28040489d744e0c5d475a88663056c9040ed5320;hp=9f0f3537511ea16f727dc80bcf71f41258fe91e3;hpb=ca9cb53ed1119a3fd98fafa0972ffeb56dee1c24;p=android-x86%2Fexternal-webkit.git diff --git a/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp index 9f0f35375..0c6acf9bb 100644 --- a/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp @@ -34,10 +34,59 @@ #include #include +#include #include +#include + 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 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 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(width), static_cast(height)), - cgImage); - CGContextRelease(tempContext); - // Pack the pixel data into the output vector. - unsigned long componentsPerPixel, bytesPerComponent; - if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel, &bytesPerComponent)) + + RetainPtr 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 dataProvider(AdoptCF, CGDataProviderCreateWithData(0, imagePixels, rowBytes * imageHeight, 0)); + RetainPtr colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr 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