GraphicsContext3DCG.cpp [plain text]
#include "config.h"
#if ENABLE(WEBGL)
#include "GraphicsContext3D.h"
#include "BitmapImage.h"
#include "GraphicsContextCG.h"
#include "Image.h"
#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
};
static GraphicsContext3D::SourceDataFormat getSourceDataFormat(unsigned int componentsPerPixel, AlphaFormat alphaFormat, bool is16BitFormat, bool bigEndian)
{
const static SourceDataFormatBase formatTableBase[4][AlphaFormatNumFormats] = { { SourceFormatBaseR, SourceFormatBaseA, SourceFormatBaseA }, { SourceFormatBaseNumFormats, SourceFormatBaseAR, SourceFormatBaseRA }, { SourceFormatBaseRGB, SourceFormatBaseNumFormats, SourceFormatBaseNumFormats }, { SourceFormatBaseNumFormats, SourceFormatBaseARGB, SourceFormatBaseRGBA } };
const static GraphicsContext3D::SourceDataFormat formatTable[SourceFormatBaseNumFormats][4] = { { 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,
GC3Denum format,
GC3Denum type,
bool premultiplyAlpha,
bool ignoreGammaAndColorProfile,
Vector<uint8_t>& outputVector)
{
if (!image)
return false;
CGImageRef cgImage;
RetainPtr<CGImageRef> decodedImage;
bool hasAlpha = image->isBitmapImage() ? static_cast<BitmapImage*>(image)->frameHasAlphaAtIndex(0) : true;
if ((ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && image->data()) {
ImageSource decoder(ImageSource::AlphaNotPremultiplied,
ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
decoder.setData(image->data(), true);
if (!decoder.frameCount())
return false;
decodedImage.adoptCF(decoder.createFrameAtIndex(0));
cgImage = decodedImage.get();
} else
cgImage = image->nativeImageForCurrentFrame();
if (!cgImage)
return false;
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
if (!width || !height)
return false;
CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
CGColorSpaceModel model = CGColorSpaceGetModel(colorSpace);
if (model == kCGColorSpaceModelIndexed) {
RetainPtr<CGContextRef> bitmapContext;
bitmapContext.adoptCF(CGBitmapContextCreate(0, width, height, 8, width * 4,
deviceRGBColorSpaceRef(),
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
if (!bitmapContext)
return false;
CGContextSetBlendMode(bitmapContext.get(), kCGBlendModeCopy);
CGContextSetInterpolationQuality(bitmapContext.get(), kCGInterpolationNone);
CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, width, height), cgImage);
decodedImage.adoptCF(CGBitmapContextCreateImage(bitmapContext.get()));
cgImage = decodedImage.get();
}
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;
if (bitsPerComponent == 16) {
switch (bitInfo & kCGBitmapByteOrderMask) {
case kCGBitmapByteOrder16Big:
bigEndianSource = true;
break;
case kCGBitmapByteOrder16Little:
bigEndianSource = false;
break;
case kCGBitmapByteOrderDefault:
bigEndianSource = true;
break;
default:
return false;
}
} else {
switch (bitInfo & kCGBitmapByteOrderMask) {
case kCGBitmapByteOrder32Big:
bigEndianSource = true;
break;
case kCGBitmapByteOrder32Little:
bigEndianSource = false;
break;
case kCGBitmapByteOrderDefault:
bigEndianSource = true;
break;
default:
return false;
}
}
AlphaOp neededAlphaOp = AlphaDoNothing;
AlphaFormat alphaFormat = AlphaFormatNone;
switch (CGImageGetAlphaInfo(cgImage)) {
case kCGImageAlphaPremultipliedFirst:
if (!premultiplyAlpha)
neededAlphaOp = AlphaDoUnmultiply;
alphaFormat = AlphaFormatFirst;
break;
case kCGImageAlphaFirst:
if (premultiplyAlpha)
neededAlphaOp = AlphaDoPremultiply;
alphaFormat = AlphaFormatFirst;
break;
case kCGImageAlphaNoneSkipFirst:
alphaFormat = AlphaFormatFirst;
break;
case kCGImageAlphaPremultipliedLast:
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;
}
SourceDataFormat srcDataFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
if (srcDataFormat == SourceFormatNumFormats)
return false;
RetainPtr<CFDataRef> pixelData;
pixelData.adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(cgImage)));
if (!pixelData)
return false;
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<CGImageRef> cgImage(AdoptCF, CGImageCreate(imageWidth, imageHeight, 8, 32, rowBytes, deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
dataProvider.get(), 0, false, kCGRenderingIntentDefault));
CGRect rect = CGRectMake(0, 0, canvasWidth, canvasHeight);
CGContextSaveGState(context);
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
CGContextDrawImage(context, rect, cgImage.get());
CGContextRestoreGState(context);
}
}
#endif // ENABLE(WEBGL)