2011-01-24 Chris Marrin <cmarrin@apple.com>
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / GraphicsContext3DCG.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28
29 #if ENABLE(WEBGL)
30
31 #include "GraphicsContext3D.h"
32 #include "GraphicsContextCG.h"
33
34 #include "Image.h"
35
36 #include <CoreGraphics/CGBitmapContext.h>
37 #include <CoreGraphics/CGContext.h>
38 #include <CoreGraphics/CGDataProvider.h>
39 #include <CoreGraphics/CGImage.h>
40
41 #include <wtf/RetainPtr.h>
42
43 namespace WebCore {
44
45 enum SourceDataFormatBase {
46     SourceFormatBaseR = 0,
47     SourceFormatBaseA,
48     SourceFormatBaseRA,
49     SourceFormatBaseAR,
50     SourceFormatBaseRGB,
51     SourceFormatBaseRGBA,
52     SourceFormatBaseARGB,
53     SourceFormatBaseNumFormats
54 };
55
56 enum AlphaFormat {
57     AlphaFormatNone = 0,
58     AlphaFormatFirst,
59     AlphaFormatLast,
60     AlphaFormatNumFormats
61 };
62
63 // This returns SourceFormatNumFormats if the combination of input parameters is unsupported.
64 static GraphicsContext3D::SourceDataFormat getSourceDataFormat(unsigned int componentsPerPixel, AlphaFormat alphaFormat, bool is16BitFormat, bool bigEndian)
65 {
66     const static SourceDataFormatBase formatTableBase[4][AlphaFormatNumFormats] = { // componentsPerPixel x AlphaFormat
67         // AlphaFormatNone            AlphaFormatFirst            AlphaFormatLast
68         { SourceFormatBaseR,          SourceFormatBaseA,          SourceFormatBaseA          }, // 1 componentsPerPixel
69         { SourceFormatBaseNumFormats, SourceFormatBaseAR,         SourceFormatBaseRA         }, // 2 componentsPerPixel
70         { SourceFormatBaseRGB,        SourceFormatBaseNumFormats, SourceFormatBaseNumFormats }, // 3 componentsPerPixel
71         { SourceFormatBaseNumFormats, SourceFormatBaseARGB,       SourceFormatBaseRGBA        } // 4 componentsPerPixel
72     };
73     const static GraphicsContext3D::SourceDataFormat formatTable[SourceFormatBaseNumFormats][4] = { // SourceDataFormatBase x bitsPerComponent x endian
74         // 8bits, little endian                 8bits, big endian                     16bits, little endian                        16bits, big endian
75         { GraphicsContext3D::SourceFormatR8,    GraphicsContext3D::SourceFormatR8,    GraphicsContext3D::SourceFormatR16Little,    GraphicsContext3D::SourceFormatR16Big },
76         { GraphicsContext3D::SourceFormatA8,    GraphicsContext3D::SourceFormatA8,    GraphicsContext3D::SourceFormatA16Little,    GraphicsContext3D::SourceFormatA16Big },
77         { GraphicsContext3D::SourceFormatAR8,   GraphicsContext3D::SourceFormatRA8,   GraphicsContext3D::SourceFormatRA16Little,   GraphicsContext3D::SourceFormatRA16Big },
78         { GraphicsContext3D::SourceFormatRA8,   GraphicsContext3D::SourceFormatAR8,   GraphicsContext3D::SourceFormatAR16Little,   GraphicsContext3D::SourceFormatAR16Big },
79         { GraphicsContext3D::SourceFormatBGR8,  GraphicsContext3D::SourceFormatRGB8,  GraphicsContext3D::SourceFormatRGB16Little,  GraphicsContext3D::SourceFormatRGB16Big },
80         { GraphicsContext3D::SourceFormatABGR8, GraphicsContext3D::SourceFormatRGBA8, GraphicsContext3D::SourceFormatRGBA16Little, GraphicsContext3D::SourceFormatRGBA16Big },
81         { GraphicsContext3D::SourceFormatBGRA8, GraphicsContext3D::SourceFormatARGB8, GraphicsContext3D::SourceFormatARGB16Little, GraphicsContext3D::SourceFormatARGB16Big }
82     };
83
84     ASSERT(componentsPerPixel <= 4 && componentsPerPixel > 0);
85     SourceDataFormatBase formatBase = formatTableBase[componentsPerPixel - 1][alphaFormat];
86     if (formatBase == SourceFormatBaseNumFormats)
87         return GraphicsContext3D::SourceFormatNumFormats;
88     return formatTable[formatBase][(is16BitFormat ? 2 : 0) + (bigEndian ? 1 : 0)];
89 }
90
91 bool GraphicsContext3D::getImageData(Image* image,
92                                      GC3Denum format,
93                                      GC3Denum type,
94                                      bool premultiplyAlpha,
95                                      bool ignoreGammaAndColorProfile,
96                                      Vector<uint8_t>& outputVector)
97 {
98     if (!image)
99         return false;
100     CGImageRef cgImage;
101     RetainPtr<CGImageRef> decodedImage;
102     if (image->data()) {
103         ImageSource decoder(ImageSource::AlphaNotPremultiplied,
104                             ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
105         decoder.setData(image->data(), true);
106         if (!decoder.frameCount())
107             return false;
108         decodedImage = decoder.createFrameAtIndex(0);
109         cgImage = decodedImage.get();
110     } else
111         cgImage = image->nativeImageForCurrentFrame();
112     if (!cgImage)
113         return false;
114
115     size_t width = CGImageGetWidth(cgImage);
116     size_t height = CGImageGetHeight(cgImage);
117     if (!width || !height)
118         return false;
119     size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
120     size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
121     if (bitsPerComponent != 8 && bitsPerComponent != 16)
122         return false;
123     if (bitsPerPixel % bitsPerComponent)
124         return false;
125     size_t componentsPerPixel = bitsPerPixel / bitsPerComponent;
126
127     CGBitmapInfo bitInfo = CGImageGetBitmapInfo(cgImage);
128     bool bigEndianSource = false;
129     // These could technically be combined into one large switch
130     // statement, but we prefer not to so that we fail fast if we
131     // encounter an unexpected image configuration.
132     if (bitsPerComponent == 16) {
133         switch (bitInfo & kCGBitmapByteOrderMask) {
134         case kCGBitmapByteOrder16Big:
135             bigEndianSource = true;
136             break;
137         case kCGBitmapByteOrder16Little:
138             bigEndianSource = false;
139             break;
140         case kCGBitmapByteOrderDefault:
141             // This is a bug in earlier version of cg where the default endian
142             // is little whereas the decoded 16-bit png image data is actually
143             // Big. Later version (10.6.4) no longer returns ByteOrderDefault.
144             bigEndianSource = true;
145             break;
146         default:
147             return false;
148         }
149     } else {
150         switch (bitInfo & kCGBitmapByteOrderMask) {
151         case kCGBitmapByteOrder32Big:
152             bigEndianSource = true;
153             break;
154         case kCGBitmapByteOrder32Little:
155             bigEndianSource = false;
156             break;
157         case kCGBitmapByteOrderDefault:
158             // It appears that the default byte order is actually big
159             // endian even on little endian architectures.
160             bigEndianSource = true;
161             break;
162         default:
163             return false;
164         }
165     }
166
167     AlphaOp neededAlphaOp = AlphaDoNothing;
168     AlphaFormat alphaFormat = AlphaFormatNone;
169     switch (CGImageGetAlphaInfo(cgImage)) {
170     case kCGImageAlphaPremultipliedFirst:
171         // This path is only accessible for MacOS earlier than 10.6.4.
172         // This is a special case for texImage2D with HTMLCanvasElement input,
173         // in which case image->data() should be null.
174         ASSERT(!image->data());
175         if (!premultiplyAlpha)
176             neededAlphaOp = AlphaDoUnmultiply;
177         alphaFormat = AlphaFormatFirst;
178         break;
179     case kCGImageAlphaFirst:
180         // This path is only accessible for MacOS earlier than 10.6.4.
181         if (premultiplyAlpha)
182             neededAlphaOp = AlphaDoPremultiply;
183         alphaFormat = AlphaFormatFirst;
184         break;
185     case kCGImageAlphaNoneSkipFirst:
186         // This path is only accessible for MacOS earlier than 10.6.4.
187         alphaFormat = AlphaFormatFirst;
188         break;
189     case kCGImageAlphaPremultipliedLast:
190         // This is a special case for texImage2D with HTMLCanvasElement input,
191         // in which case image->data() should be null.
192         ASSERT(!image->data());
193         if (!premultiplyAlpha)
194             neededAlphaOp = AlphaDoUnmultiply;
195         alphaFormat = AlphaFormatLast;
196         break;
197     case kCGImageAlphaLast:
198         if (premultiplyAlpha)
199             neededAlphaOp = AlphaDoPremultiply;
200         alphaFormat = AlphaFormatLast;
201         break;
202     case kCGImageAlphaNoneSkipLast:
203         alphaFormat = AlphaFormatLast;
204         break;
205     case kCGImageAlphaNone:
206         alphaFormat = AlphaFormatNone;
207         break;
208     default:
209         return false;
210     }
211     SourceDataFormat srcDataFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
212     if (srcDataFormat == SourceFormatNumFormats)
213         return false;
214
215     RetainPtr<CFDataRef> pixelData;
216     pixelData.adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(cgImage)));
217     if (!pixelData)
218         return false;
219     const UInt8* rgba = CFDataGetBytePtr(pixelData.get());
220     outputVector.resize(width * height * 4);
221     unsigned int srcUnpackAlignment = 0;
222     size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
223     unsigned int padding = bytesPerRow - bitsPerPixel / 8 * width;
224     if (padding) {
225         srcUnpackAlignment = padding + 1;
226         while (bytesPerRow % srcUnpackAlignment)
227             ++srcUnpackAlignment;
228     }
229     bool rt = packPixels(rgba, srcDataFormat, width, height, srcUnpackAlignment,
230                          format, type, neededAlphaOp, outputVector.data());
231     return rt;
232 }
233
234 void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, CGContextRef context)
235 {
236     if (!imagePixels || imageWidth <= 0 || imageHeight <= 0 || canvasWidth <= 0 || canvasHeight <= 0 || !context)
237         return;
238     int rowBytes = imageWidth * 4;
239     RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithData(0, imagePixels, rowBytes * imageHeight, 0));
240     RetainPtr<CGImageRef> cgImage(AdoptCF, CGImageCreate(imageWidth, imageHeight, 8, 32, rowBytes, deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
241         dataProvider.get(), 0, false, kCGRenderingIntentDefault));
242     // CSS styling may cause the canvas's content to be resized on
243     // the page. Go back to the Canvas to figure out the correct
244     // width and height to draw.
245     CGRect rect = CGRectMake(0, 0, canvasWidth, canvasHeight);
246     // We want to completely overwrite the previous frame's
247     // rendering results.
248     CGContextSaveGState(context);
249     CGContextSetBlendMode(context, kCGBlendModeCopy);
250     CGContextSetInterpolationQuality(context, kCGInterpolationNone);
251     CGContextDrawImage(context, rect, cgImage.get());
252     CGContextRestoreGState(context);
253 }
254
255 } // namespace WebCore
256
257 #endif // ENABLE(WEBGL)