1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Copyright (C) 2010 Mozilla Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29
30#if USE(3D_GRAPHICS)
31
32#include "GraphicsContext3D.h"
33
34#include "Extensions3D.h"
35#include "FormatConverter.h"
36#include "Image.h"
37#include "ImageData.h"
38#include "ImageObserver.h"
39
40namespace WebCore {
41
42namespace {
43
44GraphicsContext3D::DataFormat getDataFormat(GC3Denum destinationFormat, GC3Denum destinationType)
45{
46    GraphicsContext3D::DataFormat dstFormat = GraphicsContext3D::DataFormatRGBA8;
47    switch (destinationType) {
48    case GraphicsContext3D::UNSIGNED_BYTE:
49        switch (destinationFormat) {
50        case GraphicsContext3D::RGB:
51            dstFormat = GraphicsContext3D::DataFormatRGB8;
52            break;
53        case GraphicsContext3D::RGBA:
54            dstFormat = GraphicsContext3D::DataFormatRGBA8;
55            break;
56        case GraphicsContext3D::ALPHA:
57            dstFormat = GraphicsContext3D::DataFormatA8;
58            break;
59        case GraphicsContext3D::LUMINANCE:
60            dstFormat = GraphicsContext3D::DataFormatR8;
61            break;
62        case GraphicsContext3D::LUMINANCE_ALPHA:
63            dstFormat = GraphicsContext3D::DataFormatRA8;
64            break;
65        default:
66            ASSERT_NOT_REACHED();
67        }
68        break;
69    case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4:
70        dstFormat = GraphicsContext3D::DataFormatRGBA4444;
71        break;
72    case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1:
73        dstFormat = GraphicsContext3D::DataFormatRGBA5551;
74        break;
75    case GraphicsContext3D::UNSIGNED_SHORT_5_6_5:
76        dstFormat = GraphicsContext3D::DataFormatRGB565;
77        break;
78    case GraphicsContext3D::HALF_FLOAT_OES: // OES_texture_half_float
79        switch (destinationFormat) {
80        case GraphicsContext3D::RGB:
81            dstFormat = GraphicsContext3D::DataFormatRGB16F;
82            break;
83        case GraphicsContext3D::RGBA:
84            dstFormat = GraphicsContext3D::DataFormatRGBA16F;
85            break;
86        case GraphicsContext3D::ALPHA:
87            dstFormat = GraphicsContext3D::DataFormatA16F;
88            break;
89        case GraphicsContext3D::LUMINANCE:
90            dstFormat = GraphicsContext3D::DataFormatR16F;
91            break;
92        case GraphicsContext3D::LUMINANCE_ALPHA:
93            dstFormat = GraphicsContext3D::DataFormatRA16F;
94            break;
95        default:
96            ASSERT_NOT_REACHED();
97        }
98        break;
99    case GraphicsContext3D::FLOAT: // OES_texture_float
100        switch (destinationFormat) {
101        case GraphicsContext3D::RGB:
102            dstFormat = GraphicsContext3D::DataFormatRGB32F;
103            break;
104        case GraphicsContext3D::RGBA:
105            dstFormat = GraphicsContext3D::DataFormatRGBA32F;
106            break;
107        case GraphicsContext3D::ALPHA:
108            dstFormat = GraphicsContext3D::DataFormatA32F;
109            break;
110        case GraphicsContext3D::LUMINANCE:
111            dstFormat = GraphicsContext3D::DataFormatR32F;
112            break;
113        case GraphicsContext3D::LUMINANCE_ALPHA:
114            dstFormat = GraphicsContext3D::DataFormatRA32F;
115            break;
116        default:
117            ASSERT_NOT_REACHED();
118        }
119        break;
120    default:
121        ASSERT_NOT_REACHED();
122    }
123    return dstFormat;
124}
125
126} // anonymous namespace
127
128bool GraphicsContext3D::texImage2DResourceSafe(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, GC3Dint unpackAlignment)
129{
130    ASSERT(unpackAlignment == 1 || unpackAlignment == 2 || unpackAlignment == 4 || unpackAlignment == 8);
131    std::unique_ptr<unsigned char[]> zero;
132    if (!isResourceSafe() && width > 0 && height > 0) {
133        unsigned int size;
134        GC3Denum error = computeImageSizeInBytes(format, type, width, height, unpackAlignment, &size, 0);
135        if (error != GraphicsContext3D::NO_ERROR) {
136            synthesizeGLError(error);
137            return false;
138        }
139        zero = std::make_unique<unsigned char[]>(size);
140        if (!zero) {
141            synthesizeGLError(GraphicsContext3D::INVALID_VALUE);
142            return false;
143        }
144        memset(zero.get(), 0, size);
145    }
146    return texImage2D(target, level, internalformat, width, height, border, format, type, zero.get());
147}
148
149bool GraphicsContext3D::computeFormatAndTypeParameters(GC3Denum format, GC3Denum type, unsigned int* componentsPerPixel, unsigned int* bytesPerComponent)
150{
151    switch (format) {
152    case GraphicsContext3D::ALPHA:
153    case GraphicsContext3D::LUMINANCE:
154    case GraphicsContext3D::DEPTH_COMPONENT:
155    case GraphicsContext3D::DEPTH_STENCIL:
156        *componentsPerPixel = 1;
157        break;
158    case GraphicsContext3D::LUMINANCE_ALPHA:
159        *componentsPerPixel = 2;
160        break;
161    case GraphicsContext3D::RGB:
162        *componentsPerPixel = 3;
163        break;
164    case GraphicsContext3D::RGBA:
165    case Extensions3D::BGRA_EXT: // GL_EXT_texture_format_BGRA8888
166        *componentsPerPixel = 4;
167        break;
168    default:
169        return false;
170    }
171    switch (type) {
172    case GraphicsContext3D::UNSIGNED_BYTE:
173        *bytesPerComponent = sizeof(GC3Dubyte);
174        break;
175    case GraphicsContext3D::UNSIGNED_SHORT:
176        *bytesPerComponent = sizeof(GC3Dushort);
177        break;
178    case GraphicsContext3D::UNSIGNED_SHORT_5_6_5:
179    case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4:
180    case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1:
181        *componentsPerPixel = 1;
182        *bytesPerComponent = sizeof(GC3Dushort);
183        break;
184    case GraphicsContext3D::UNSIGNED_INT_24_8:
185    case GraphicsContext3D::UNSIGNED_INT:
186        *bytesPerComponent = sizeof(GC3Duint);
187        break;
188    case GraphicsContext3D::FLOAT: // OES_texture_float
189        *bytesPerComponent = sizeof(GC3Dfloat);
190        break;
191    case GraphicsContext3D::HALF_FLOAT_OES: // OES_texture_half_float
192        *bytesPerComponent = sizeof(GC3Dhalffloat);
193        break;
194    default:
195        return false;
196    }
197    return true;
198}
199
200GC3Denum GraphicsContext3D::computeImageSizeInBytes(GC3Denum format, GC3Denum type, GC3Dsizei width, GC3Dsizei height, GC3Dint alignment, unsigned int* imageSizeInBytes, unsigned int* paddingInBytes)
201{
202    ASSERT(imageSizeInBytes);
203    ASSERT(alignment == 1 || alignment == 2 || alignment == 4 || alignment == 8);
204    if (width < 0 || height < 0)
205        return GraphicsContext3D::INVALID_VALUE;
206    unsigned int bytesPerComponent, componentsPerPixel;
207    if (!computeFormatAndTypeParameters(format, type, &bytesPerComponent, &componentsPerPixel))
208        return GraphicsContext3D::INVALID_ENUM;
209    if (!width || !height) {
210        *imageSizeInBytes = 0;
211        if (paddingInBytes)
212            *paddingInBytes = 0;
213        return GraphicsContext3D::NO_ERROR;
214    }
215    Checked<uint32_t, RecordOverflow> checkedValue = bytesPerComponent * componentsPerPixel;
216    checkedValue *=  width;
217    if (checkedValue.hasOverflowed())
218        return GraphicsContext3D::INVALID_VALUE;
219    unsigned int validRowSize = checkedValue.unsafeGet();
220    unsigned int padding = 0;
221    unsigned int residual = validRowSize % alignment;
222    if (residual) {
223        padding = alignment - residual;
224        checkedValue += padding;
225    }
226    // Last row needs no padding.
227    checkedValue *= (height - 1);
228    checkedValue += validRowSize;
229    if (checkedValue.hasOverflowed())
230        return GraphicsContext3D::INVALID_VALUE;
231    *imageSizeInBytes = checkedValue.unsafeGet();
232    if (paddingInBytes)
233        *paddingInBytes = padding;
234    return GraphicsContext3D::NO_ERROR;
235}
236
237GraphicsContext3D::ImageExtractor::ImageExtractor(Image* image, ImageHtmlDomSource imageHtmlDomSource, bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
238{
239    m_image = image;
240    m_imageHtmlDomSource = imageHtmlDomSource;
241    m_extractSucceeded = extractImage(premultiplyAlpha, ignoreGammaAndColorProfile);
242}
243
244bool GraphicsContext3D::packImageData( Image* image, const void* pixels, GC3Denum format, GC3Denum type, bool flipY, AlphaOp alphaOp, DataFormat sourceFormat, unsigned width, unsigned height, unsigned sourceUnpackAlignment, Vector<uint8_t>& data)
245{
246    if (!pixels)
247        return false;
248
249    unsigned packedSize;
250    // Output data is tightly packed (alignment == 1).
251    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
252        return false;
253    data.resize(packedSize);
254
255    if (!packPixels(reinterpret_cast<const uint8_t*>(pixels), sourceFormat, width, height, sourceUnpackAlignment, format, type, alphaOp, data.data(), flipY))
256        return false;
257    if (ImageObserver* observer = image->imageObserver())
258        observer->didDraw(image);
259    return true;
260}
261
262bool GraphicsContext3D::extractImageData(ImageData* imageData, GC3Denum format, GC3Denum type, bool flipY, bool premultiplyAlpha, Vector<uint8_t>& data)
263{
264    if (!imageData)
265        return false;
266    int width = imageData->width();
267    int height = imageData->height();
268
269    unsigned int packedSize;
270    // Output data is tightly packed (alignment == 1).
271    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
272        return false;
273    data.resize(packedSize);
274
275    if (!packPixels(imageData->data()->data(), DataFormatRGBA8, width, height, 0, format, type, premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing, data.data(), flipY))
276        return false;
277
278    return true;
279}
280
281bool GraphicsContext3D::extractTextureData(unsigned int width, unsigned int height, GC3Denum format, GC3Denum type, unsigned int unpackAlignment, bool flipY, bool premultiplyAlpha, const void* pixels, Vector<uint8_t>& data)
282{
283    // Assumes format, type, etc. have already been validated.
284    DataFormat sourceDataFormat = getDataFormat(format, type);
285
286    // Resize the output buffer.
287    unsigned int componentsPerPixel, bytesPerComponent;
288    if (!computeFormatAndTypeParameters(format, type, &componentsPerPixel, &bytesPerComponent))
289        return false;
290    unsigned int bytesPerPixel = componentsPerPixel * bytesPerComponent;
291    data.resize(width * height * bytesPerPixel);
292
293    if (!packPixels(static_cast<const uint8_t*>(pixels), sourceDataFormat, width, height, unpackAlignment, format, type, (premultiplyAlpha ? AlphaDoPremultiply : AlphaDoNothing), data.data(), flipY))
294        return false;
295
296    return true;
297}
298
299ALWAYS_INLINE unsigned TexelBytesForFormat(GraphicsContext3D::DataFormat format)
300{
301    switch (format) {
302    case GraphicsContext3D::DataFormatR8:
303    case GraphicsContext3D::DataFormatA8:
304        return 1;
305    case GraphicsContext3D::DataFormatRA8:
306    case GraphicsContext3D::DataFormatAR8:
307    case GraphicsContext3D::DataFormatRGBA5551:
308    case GraphicsContext3D::DataFormatRGBA4444:
309    case GraphicsContext3D::DataFormatRGB565:
310    case GraphicsContext3D::DataFormatA16F:
311    case GraphicsContext3D::DataFormatR16F:
312        return 2;
313    case GraphicsContext3D::DataFormatRGB8:
314    case GraphicsContext3D::DataFormatBGR8:
315        return 3;
316    case GraphicsContext3D::DataFormatRGBA8:
317    case GraphicsContext3D::DataFormatARGB8:
318    case GraphicsContext3D::DataFormatABGR8:
319    case GraphicsContext3D::DataFormatBGRA8:
320    case GraphicsContext3D::DataFormatR32F:
321    case GraphicsContext3D::DataFormatA32F:
322    case GraphicsContext3D::DataFormatRA16F:
323        return 4;
324    case GraphicsContext3D::DataFormatRGB16F:
325        return 6;
326    case GraphicsContext3D::DataFormatRA32F:
327    case GraphicsContext3D::DataFormatRGBA16F:
328        return 8;
329    case GraphicsContext3D::DataFormatRGB32F:
330        return 12;
331    case GraphicsContext3D::DataFormatRGBA32F:
332        return 16;
333    default:
334        return 0;
335    }
336}
337
338bool GraphicsContext3D::packPixels(const uint8_t* sourceData, DataFormat sourceDataFormat, unsigned width, unsigned height, unsigned sourceUnpackAlignment, unsigned destinationFormat, unsigned destinationType, AlphaOp alphaOp, void* destinationData, bool flipY)
339{
340    int validSrc = width * TexelBytesForFormat(sourceDataFormat);
341    int remainder = sourceUnpackAlignment ? (validSrc % sourceUnpackAlignment) : 0;
342    int srcStride = remainder ? (validSrc + sourceUnpackAlignment - remainder) : validSrc;
343
344    DataFormat dstDataFormat = getDataFormat(destinationFormat, destinationType);
345    int dstStride = width * TexelBytesForFormat(dstDataFormat);
346    if (flipY) {
347        destinationData = static_cast<uint8_t*>(destinationData) + dstStride*(height - 1);
348        dstStride = -dstStride;
349    }
350    if (!hasAlpha(sourceDataFormat) || !hasColor(sourceDataFormat) || !hasColor(dstDataFormat))
351        alphaOp = AlphaDoNothing;
352
353    if (sourceDataFormat == dstDataFormat && alphaOp == AlphaDoNothing) {
354        const uint8_t* ptr = sourceData;
355        const uint8_t* ptrEnd = sourceData + srcStride * height;
356        unsigned rowSize = (dstStride > 0) ? dstStride: -dstStride;
357        uint8_t* dst = static_cast<uint8_t*>(destinationData);
358        while (ptr < ptrEnd) {
359            memcpy(dst, ptr, rowSize);
360            ptr += srcStride;
361            dst += dstStride;
362        }
363        return true;
364    }
365
366    FormatConverter converter(width, height, sourceData, destinationData, srcStride, dstStride);
367    converter.convert(sourceDataFormat, dstDataFormat, alphaOp);
368    if (!converter.success())
369        return false;
370    return true;
371}
372
373unsigned GraphicsContext3D::getClearBitsByAttachmentType(GC3Denum attachment)
374{
375    switch (attachment) {
376    case GraphicsContext3D::COLOR_ATTACHMENT0:
377    case Extensions3D::COLOR_ATTACHMENT1_EXT:
378    case Extensions3D::COLOR_ATTACHMENT2_EXT:
379    case Extensions3D::COLOR_ATTACHMENT3_EXT:
380    case Extensions3D::COLOR_ATTACHMENT4_EXT:
381    case Extensions3D::COLOR_ATTACHMENT5_EXT:
382    case Extensions3D::COLOR_ATTACHMENT6_EXT:
383    case Extensions3D::COLOR_ATTACHMENT7_EXT:
384    case Extensions3D::COLOR_ATTACHMENT8_EXT:
385    case Extensions3D::COLOR_ATTACHMENT9_EXT:
386    case Extensions3D::COLOR_ATTACHMENT10_EXT:
387    case Extensions3D::COLOR_ATTACHMENT11_EXT:
388    case Extensions3D::COLOR_ATTACHMENT12_EXT:
389    case Extensions3D::COLOR_ATTACHMENT13_EXT:
390    case Extensions3D::COLOR_ATTACHMENT14_EXT:
391    case Extensions3D::COLOR_ATTACHMENT15_EXT:
392        return GraphicsContext3D::COLOR_BUFFER_BIT;
393    case GraphicsContext3D::DEPTH_ATTACHMENT:
394        return GraphicsContext3D::DEPTH_BUFFER_BIT;
395    case GraphicsContext3D::STENCIL_ATTACHMENT:
396        return GraphicsContext3D::STENCIL_BUFFER_BIT;
397    case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT:
398        return GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT;
399    default:
400        return 0;
401    }
402}
403
404unsigned GraphicsContext3D::getClearBitsByFormat(GC3Denum format)
405{
406    switch (format) {
407    case GraphicsContext3D::ALPHA:
408    case GraphicsContext3D::LUMINANCE:
409    case GraphicsContext3D::LUMINANCE_ALPHA:
410    case GraphicsContext3D::RGB:
411    case GraphicsContext3D::RGB565:
412    case GraphicsContext3D::RGBA:
413    case GraphicsContext3D::RGBA4:
414    case GraphicsContext3D::RGB5_A1:
415        return GraphicsContext3D::COLOR_BUFFER_BIT;
416    case GraphicsContext3D::DEPTH_COMPONENT16:
417    case GraphicsContext3D::DEPTH_COMPONENT:
418        return GraphicsContext3D::DEPTH_BUFFER_BIT;
419    case GraphicsContext3D::STENCIL_INDEX8:
420        return GraphicsContext3D::STENCIL_BUFFER_BIT;
421    case GraphicsContext3D::DEPTH_STENCIL:
422        return GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT;
423    default:
424        return 0;
425    }
426}
427
428unsigned GraphicsContext3D::getChannelBitsByFormat(GC3Denum format)
429{
430    switch (format) {
431    case GraphicsContext3D::ALPHA:
432        return ChannelAlpha;
433    case GraphicsContext3D::LUMINANCE:
434        return ChannelRGB;
435    case GraphicsContext3D::LUMINANCE_ALPHA:
436        return ChannelRGBA;
437    case GraphicsContext3D::RGB:
438    case GraphicsContext3D::RGB565:
439        return ChannelRGB;
440    case GraphicsContext3D::RGBA:
441    case GraphicsContext3D::RGBA4:
442    case GraphicsContext3D::RGB5_A1:
443        return ChannelRGBA;
444    case GraphicsContext3D::DEPTH_COMPONENT16:
445    case GraphicsContext3D::DEPTH_COMPONENT:
446        return ChannelDepth;
447    case GraphicsContext3D::STENCIL_INDEX8:
448        return ChannelStencil;
449    case GraphicsContext3D::DEPTH_STENCIL:
450        return ChannelDepth | ChannelStencil;
451    default:
452        return 0;
453    }
454}
455
456} // namespace WebCore
457
458#endif // USE(3D_GRAPHICS)
459