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 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 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 USE(3D_GRAPHICS)
30
31#include "GraphicsContext3D.h"
32
33#include "BitmapImage.h"
34#include "GraphicsContextCG.h"
35#include "Image.h"
36
37#if HAVE(ARM_NEON_INTRINSICS)
38#include "GraphicsContext3DNEON.h"
39#endif
40
41#include <CoreGraphics/CGBitmapContext.h>
42#include <CoreGraphics/CGContext.h>
43#include <CoreGraphics/CGDataProvider.h>
44#include <CoreGraphics/CGImage.h>
45
46#include <wtf/RetainPtr.h>
47#include <wtf/StdLibExtras.h>
48
49namespace WebCore {
50
51enum SourceDataFormatBase {
52    SourceFormatBaseR = 0,
53    SourceFormatBaseA,
54    SourceFormatBaseRA,
55    SourceFormatBaseAR,
56    SourceFormatBaseRGB,
57    SourceFormatBaseRGBA,
58    SourceFormatBaseARGB,
59    SourceFormatBaseNumFormats
60};
61
62enum AlphaFormat {
63    AlphaFormatNone = 0,
64    AlphaFormatFirst,
65    AlphaFormatLast,
66    AlphaFormatNumFormats
67};
68
69// This returns SourceFormatNumFormats if the combination of input parameters is unsupported.
70static GraphicsContext3D::DataFormat getSourceDataFormat(unsigned componentsPerPixel, AlphaFormat alphaFormat, bool is16BitFormat, bool bigEndian)
71{
72    const static SourceDataFormatBase formatTableBase[4][AlphaFormatNumFormats] = { // componentsPerPixel x AlphaFormat
73        // AlphaFormatNone            AlphaFormatFirst            AlphaFormatLast
74        { SourceFormatBaseR,          SourceFormatBaseA,          SourceFormatBaseA          }, // 1 componentsPerPixel
75        { SourceFormatBaseNumFormats, SourceFormatBaseAR,         SourceFormatBaseRA         }, // 2 componentsPerPixel
76        { SourceFormatBaseRGB,        SourceFormatBaseNumFormats, SourceFormatBaseNumFormats }, // 3 componentsPerPixel
77        { SourceFormatBaseNumFormats, SourceFormatBaseARGB,       SourceFormatBaseRGBA        } // 4 componentsPerPixel
78    };
79    const static GraphicsContext3D::DataFormat formatTable[SourceFormatBaseNumFormats][4] = { // SourceDataFormatBase x bitsPerComponent x endian
80        // 8bits, little endian                 8bits, big endian                     16bits, little endian                        16bits, big endian
81        { GraphicsContext3D::DataFormatR8,    GraphicsContext3D::DataFormatR8,    GraphicsContext3D::DataFormatR16Little,    GraphicsContext3D::DataFormatR16Big },
82        { GraphicsContext3D::DataFormatA8,    GraphicsContext3D::DataFormatA8,    GraphicsContext3D::DataFormatA16Little,    GraphicsContext3D::DataFormatA16Big },
83        { GraphicsContext3D::DataFormatAR8,   GraphicsContext3D::DataFormatRA8,   GraphicsContext3D::DataFormatRA16Little,   GraphicsContext3D::DataFormatRA16Big },
84        { GraphicsContext3D::DataFormatRA8,   GraphicsContext3D::DataFormatAR8,   GraphicsContext3D::DataFormatAR16Little,   GraphicsContext3D::DataFormatAR16Big },
85        { GraphicsContext3D::DataFormatBGR8,  GraphicsContext3D::DataFormatRGB8,  GraphicsContext3D::DataFormatRGB16Little,  GraphicsContext3D::DataFormatRGB16Big },
86        { GraphicsContext3D::DataFormatABGR8, GraphicsContext3D::DataFormatRGBA8, GraphicsContext3D::DataFormatRGBA16Little, GraphicsContext3D::DataFormatRGBA16Big },
87        { GraphicsContext3D::DataFormatBGRA8, GraphicsContext3D::DataFormatARGB8, GraphicsContext3D::DataFormatARGB16Little, GraphicsContext3D::DataFormatARGB16Big }
88    };
89
90    ASSERT(componentsPerPixel <= 4 && componentsPerPixel > 0);
91    SourceDataFormatBase formatBase = formatTableBase[componentsPerPixel - 1][alphaFormat];
92    if (formatBase == SourceFormatBaseNumFormats)
93        return GraphicsContext3D::DataFormatNumFormats;
94    return formatTable[formatBase][(is16BitFormat ? 2 : 0) + (bigEndian ? 1 : 0)];
95}
96
97namespace {
98uint8_t convertColor16LittleTo8(uint16_t value)
99{
100    return value >> 8;
101}
102
103uint8_t convertColor16BigTo8(uint16_t value)
104{
105    return static_cast<uint8_t>(value & 0x00FF);
106}
107
108template<int format, typename SourceType, typename DstType>
109ALWAYS_INLINE void convert16BitFormatToRGBA8(const SourceType*, DstType*, unsigned)
110{
111    ASSERT_NOT_REACHED();
112}
113
114template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGBA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
115{
116#if HAVE(ARM_NEON_INTRINSICS)
117    SIMD::unpackOneRowOfRGBA16LittleToRGBA8(source, destination, pixelsPerRow);
118#endif
119    for (unsigned i = 0; i < pixelsPerRow; ++i) {
120        destination[0] = convertColor16LittleTo8(source[0]);
121        destination[1] = convertColor16LittleTo8(source[1]);
122        destination[2] = convertColor16LittleTo8(source[2]);
123        destination[3] = convertColor16LittleTo8(source[3]);
124        source += 4;
125        destination += 4;
126    }
127}
128
129template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGBA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
130{
131    for (unsigned i = 0; i < pixelsPerRow; ++i) {
132        destination[0] = convertColor16BigTo8(source[0]);
133        destination[1] = convertColor16BigTo8(source[1]);
134        destination[2] = convertColor16BigTo8(source[2]);
135        destination[3] = convertColor16BigTo8(source[3]);
136        source += 4;
137        destination += 4;
138    }
139}
140
141template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGB16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
142{
143#if HAVE(ARM_NEON_INTRINSICS)
144    SIMD::unpackOneRowOfRGB16LittleToRGBA8(source, destination, pixelsPerRow);
145#endif
146    for (unsigned i = 0; i < pixelsPerRow; ++i) {
147        destination[0] = convertColor16LittleTo8(source[0]);
148        destination[1] = convertColor16LittleTo8(source[1]);
149        destination[2] = convertColor16LittleTo8(source[2]);
150        destination[3] = 0xFF;
151        source += 3;
152        destination += 4;
153    }
154}
155
156template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRGB16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
157{
158    for (unsigned i = 0; i < pixelsPerRow; ++i) {
159        destination[0] = convertColor16BigTo8(source[0]);
160        destination[1] = convertColor16BigTo8(source[1]);
161        destination[2] = convertColor16BigTo8(source[2]);
162        destination[3] = 0xFF;
163        source += 3;
164        destination += 4;
165    }
166}
167
168template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatARGB16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
169{
170#if HAVE(ARM_NEON_INTRINSICS)
171    SIMD::unpackOneRowOfARGB16LittleToRGBA8(source, destination, pixelsPerRow);
172#endif
173    for (unsigned i = 0; i < pixelsPerRow; ++i) {
174        destination[0] = convertColor16LittleTo8(source[1]);
175        destination[1] = convertColor16LittleTo8(source[2]);
176        destination[2] = convertColor16LittleTo8(source[3]);
177        destination[3] = convertColor16LittleTo8(source[0]);
178        source += 4;
179        destination += 4;
180    }
181}
182
183template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatARGB16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
184{
185    for (unsigned i = 0; i < pixelsPerRow; ++i) {
186        destination[0] = convertColor16BigTo8(source[1]);
187        destination[1] = convertColor16BigTo8(source[2]);
188        destination[2] = convertColor16BigTo8(source[3]);
189        destination[3] = convertColor16BigTo8(source[0]);
190        source += 4;
191        destination += 4;
192    }
193}
194
195template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatR16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
196{
197    for (unsigned i = 0; i < pixelsPerRow; ++i) {
198        destination[0] = convertColor16LittleTo8(source[0]);
199        destination[1] = convertColor16LittleTo8(source[0]);
200        destination[2] = convertColor16LittleTo8(source[0]);
201        destination[3] = 0xFF;
202        source += 1;
203        destination += 4;
204    }
205}
206
207template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatR16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
208{
209    for (unsigned i = 0; i < pixelsPerRow; ++i) {
210        destination[0] = convertColor16BigTo8(source[0]);
211        destination[1] = convertColor16BigTo8(source[0]);
212        destination[2] = convertColor16BigTo8(source[0]);
213        destination[3] = 0xFF;
214        source += 1;
215        destination += 4;
216    }
217}
218
219template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
220{
221    for (unsigned i = 0; i < pixelsPerRow; ++i) {
222        destination[0] = convertColor16LittleTo8(source[0]);
223        destination[1] = convertColor16LittleTo8(source[0]);
224        destination[2] = convertColor16LittleTo8(source[0]);
225        destination[3] = convertColor16LittleTo8(source[1]);
226        source += 2;
227        destination += 4;
228    }
229}
230
231template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatRA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
232{
233    for (unsigned i = 0; i < pixelsPerRow; ++i) {
234        destination[0] = convertColor16BigTo8(source[0]);
235        destination[1] = convertColor16BigTo8(source[0]);
236        destination[2] = convertColor16BigTo8(source[0]);
237        destination[3] = convertColor16BigTo8(source[1]);
238        source += 2;
239        destination += 4;
240    }
241}
242
243template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatAR16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
244{
245    for (unsigned i = 0; i < pixelsPerRow; ++i) {
246        destination[0] = convertColor16LittleTo8(source[1]);
247        destination[1] = convertColor16LittleTo8(source[1]);
248        destination[2] = convertColor16LittleTo8(source[1]);
249        destination[3] = convertColor16LittleTo8(source[0]);
250        source += 2;
251        destination += 4;
252    }
253}
254
255template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatAR16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
256{
257    for (unsigned i = 0; i < pixelsPerRow; ++i) {
258        destination[0] = convertColor16BigTo8(source[1]);
259        destination[1] = convertColor16BigTo8(source[1]);
260        destination[2] = convertColor16BigTo8(source[1]);
261        destination[3] = convertColor16BigTo8(source[0]);
262        source += 2;
263        destination += 4;
264    }
265}
266
267template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatA16Little, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
268{
269    for (unsigned i = 0; i < pixelsPerRow; ++i) {
270        destination[0] = 0x0;
271        destination[1] = 0x0;
272        destination[2] = 0x0;
273        destination[3] = convertColor16LittleTo8(source[0]);
274        source += 1;
275        destination += 4;
276    }
277}
278
279template<> ALWAYS_INLINE void convert16BitFormatToRGBA8<GraphicsContext3D::DataFormatA16Big, uint16_t, uint8_t>(const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
280{
281    for (unsigned i = 0; i < pixelsPerRow; ++i) {
282        destination[0] = 0x0;
283        destination[1] = 0x0;
284        destination[2] = 0x0;
285        destination[3] = convertColor16BigTo8(source[0]);
286        source += 1;
287        destination += 4;
288    }
289}
290
291void convert16BitFormatToRGBA8(GraphicsContext3D::DataFormat srcFormat, const uint16_t* source, uint8_t* destination, unsigned pixelsPerRow)
292{
293#define CONVERT16BITFORMATTORGBA8(SrcFormat) \
294    case SrcFormat: \
295        return convert16BitFormatToRGBA8<SrcFormat>(source, destination, pixelsPerRow);
296
297    switch (srcFormat) {
298        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatR16Little)
299        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatR16Big)
300        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatA16Little)
301        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatA16Big)
302        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRA16Little)
303        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRA16Big)
304        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatAR16Little)
305        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatAR16Big)
306        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGB16Little)
307        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGB16Big)
308        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGBA16Little)
309        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatRGBA16Big)
310        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatARGB16Little)
311        CONVERT16BITFORMATTORGBA8(GraphicsContext3D::DataFormatARGB16Big)
312    default:
313        ASSERT_NOT_REACHED();
314    }
315#undef CONVERT16BITFORMATTORGBA8
316}
317
318}
319
320GraphicsContext3D::ImageExtractor::~ImageExtractor()
321{
322}
323
324bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
325{
326    if (!m_image)
327        return false;
328    bool hasAlpha = !m_image->currentFrameKnownToBeOpaque();
329    if ((ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && m_image->data()) {
330        ImageSource decoder(ImageSource::AlphaNotPremultiplied,
331                            ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
332        decoder.setData(m_image->data(), true);
333        if (!decoder.frameCount())
334            return false;
335#if PLATFORM(IOS)
336        float scaleHint = 1;
337        if (m_image->isBitmapImage()) {
338            FloatSize originalSize = toBitmapImage(m_image)->originalSize();
339            if (!originalSize.isEmpty())
340                scaleHint = std::min<float>(1, std::max(m_image->size().width() / originalSize.width(), m_image->size().width() / originalSize.height()));
341        }
342        m_decodedImage = adoptCF(decoder.createFrameAtIndex(0, &scaleHint));
343#else
344        m_decodedImage = adoptCF(decoder.createFrameAtIndex(0));
345#endif
346        m_cgImage = m_decodedImage.get();
347    } else
348        m_cgImage = m_image->nativeImageForCurrentFrame();
349    if (!m_cgImage)
350        return false;
351
352    m_imageWidth = CGImageGetWidth(m_cgImage);
353    m_imageHeight = CGImageGetHeight(m_cgImage);
354    if (!m_imageWidth || !m_imageHeight)
355        return false;
356
357    // See whether the image is using an indexed color space, and if
358    // so, re-render it into an RGB color space. The image re-packing
359    // code requires color data, not color table indices, for the
360    // image data.
361    CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_cgImage);
362    CGColorSpaceModel model = CGColorSpaceGetModel(colorSpace);
363    if (model == kCGColorSpaceModelIndexed) {
364        RetainPtr<CGContextRef> bitmapContext;
365        // FIXME: we should probably manually convert the image by indexing into
366        // the color table, which would allow us to avoid premultiplying the
367        // alpha channel. Creation of a bitmap context with an alpha channel
368        // doesn't seem to work unless it's premultiplied.
369        bitmapContext = adoptCF(CGBitmapContextCreate(0, m_imageWidth, m_imageHeight, 8, m_imageWidth * 4,
370            deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
371        if (!bitmapContext)
372            return false;
373
374        CGContextSetBlendMode(bitmapContext.get(), kCGBlendModeCopy);
375        CGContextSetInterpolationQuality(bitmapContext.get(), kCGInterpolationNone);
376        CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, m_imageWidth, m_imageHeight), m_cgImage);
377
378        // Now discard the original CG image and replace it with a copy from the bitmap context.
379        m_decodedImage = adoptCF(CGBitmapContextCreateImage(bitmapContext.get()));
380        m_cgImage = m_decodedImage.get();
381    }
382
383    size_t bitsPerComponent = CGImageGetBitsPerComponent(m_cgImage);
384    size_t bitsPerPixel = CGImageGetBitsPerPixel(m_cgImage);
385    if (bitsPerComponent != 8 && bitsPerComponent != 16)
386        return false;
387    if (bitsPerPixel % bitsPerComponent)
388        return false;
389    size_t componentsPerPixel = bitsPerPixel / bitsPerComponent;
390
391    CGBitmapInfo bitInfo = CGImageGetBitmapInfo(m_cgImage);
392    bool bigEndianSource = false;
393    // These could technically be combined into one large switch
394    // statement, but we prefer not to so that we fail fast if we
395    // encounter an unexpected image configuration.
396    if (bitsPerComponent == 16) {
397        switch (bitInfo & kCGBitmapByteOrderMask) {
398        case kCGBitmapByteOrder16Big:
399            bigEndianSource = true;
400            break;
401        case kCGBitmapByteOrder16Little:
402            bigEndianSource = false;
403            break;
404        case kCGBitmapByteOrderDefault:
405            // This is a bug in earlier version of cg where the default endian
406            // is little whereas the decoded 16-bit png image data is actually
407            // Big. Later version (10.6.4) no longer returns ByteOrderDefault.
408            bigEndianSource = true;
409            break;
410        default:
411            return false;
412        }
413    } else {
414        switch (bitInfo & kCGBitmapByteOrderMask) {
415        case kCGBitmapByteOrder32Big:
416            bigEndianSource = true;
417            break;
418        case kCGBitmapByteOrder32Little:
419            bigEndianSource = false;
420            break;
421        case kCGBitmapByteOrderDefault:
422            // It appears that the default byte order is actually big
423            // endian even on little endian architectures.
424            bigEndianSource = true;
425            break;
426        default:
427            return false;
428        }
429    }
430
431    m_alphaOp = AlphaDoNothing;
432    AlphaFormat alphaFormat = AlphaFormatNone;
433    switch (CGImageGetAlphaInfo(m_cgImage)) {
434    case kCGImageAlphaPremultipliedFirst:
435        if (!premultiplyAlpha)
436            m_alphaOp = AlphaDoUnmultiply;
437        alphaFormat = AlphaFormatFirst;
438        break;
439    case kCGImageAlphaFirst:
440        // This path is only accessible for MacOS earlier than 10.6.4.
441        if (premultiplyAlpha)
442            m_alphaOp = AlphaDoPremultiply;
443        alphaFormat = AlphaFormatFirst;
444        break;
445    case kCGImageAlphaNoneSkipFirst:
446        // This path is only accessible for MacOS earlier than 10.6.4.
447        alphaFormat = AlphaFormatFirst;
448        break;
449    case kCGImageAlphaPremultipliedLast:
450        if (!premultiplyAlpha)
451            m_alphaOp = AlphaDoUnmultiply;
452        alphaFormat = AlphaFormatLast;
453        break;
454    case kCGImageAlphaLast:
455        if (premultiplyAlpha)
456            m_alphaOp = AlphaDoPremultiply;
457        alphaFormat = AlphaFormatLast;
458        break;
459    case kCGImageAlphaNoneSkipLast:
460        alphaFormat = AlphaFormatLast;
461        break;
462    case kCGImageAlphaNone:
463        alphaFormat = AlphaFormatNone;
464        break;
465    default:
466        return false;
467    }
468
469    m_imageSourceFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
470    if (m_imageSourceFormat == DataFormatNumFormats)
471        return false;
472
473    m_pixelData = adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(m_cgImage)));
474    if (!m_pixelData)
475        return false;
476
477    m_imagePixelData = reinterpret_cast<const void*>(CFDataGetBytePtr(m_pixelData.get()));
478
479    unsigned int srcUnpackAlignment = 0;
480    size_t bytesPerRow = CGImageGetBytesPerRow(m_cgImage);
481    unsigned padding = bytesPerRow - bitsPerPixel / 8 * m_imageWidth;
482    if (padding) {
483        srcUnpackAlignment = padding + 1;
484        while (bytesPerRow % srcUnpackAlignment)
485            ++srcUnpackAlignment;
486    }
487
488    m_imageSourceUnpackAlignment = srcUnpackAlignment;
489    // Using a bitmap context created according to destination format and drawing the CGImage to the bitmap context can also do the format conversion,
490    // but it would premultiply the alpha channel as a side effect.
491    // Prefer to mannually Convert 16bit per-component formats to RGBA8 formats instead.
492    if (bitsPerComponent == 16) {
493        m_formalizedRGBA8Data = std::make_unique<uint8_t[]>(m_imageWidth * m_imageHeight * 4);
494        const uint16_t* source = reinterpret_cast<const uint16_t*>(m_imagePixelData);
495        uint8_t* destination = m_formalizedRGBA8Data.get();
496        const ptrdiff_t srcStrideInElements = bytesPerRow / sizeof(uint16_t);
497        const ptrdiff_t dstStrideInElements = 4 * m_imageWidth;
498        for (unsigned i =0; i < m_imageHeight; i++) {
499            convert16BitFormatToRGBA8(m_imageSourceFormat, source, destination, m_imageWidth);
500            source += srcStrideInElements;
501            destination += dstStrideInElements;
502        }
503        m_imagePixelData = reinterpret_cast<const void*>(m_formalizedRGBA8Data.get());
504        m_imageSourceFormat = DataFormatRGBA8;
505        m_imageSourceUnpackAlignment = 1;
506    }
507    return true;
508}
509
510static void releaseImageData(void*, const void* data, size_t)
511{
512    fastFree(const_cast<void*>(data));
513}
514
515void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, GraphicsContext* context)
516{
517    if (!imagePixels || imageWidth <= 0 || imageHeight <= 0 || canvasWidth <= 0 || canvasHeight <= 0 || !context)
518        return;
519    int rowBytes = imageWidth * 4;
520    RetainPtr<CGDataProviderRef> dataProvider;
521
522    if (context->isAcceleratedContext()) {
523        unsigned char* copiedPixels;
524
525        if (!tryFastCalloc(imageHeight, rowBytes).getValue(copiedPixels))
526            return;
527
528        memcpy(copiedPixels, imagePixels, rowBytes * imageHeight);
529        dataProvider = adoptCF(CGDataProviderCreateWithData(0, copiedPixels, rowBytes * imageHeight, releaseImageData));
530    } else
531        dataProvider = adoptCF(CGDataProviderCreateWithData(0, imagePixels, rowBytes * imageHeight, 0));
532
533    RetainPtr<CGImageRef> cgImage = adoptCF(CGImageCreate(imageWidth, imageHeight, 8, 32, rowBytes, deviceRGBColorSpaceRef(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
534        dataProvider.get(), 0, false, kCGRenderingIntentDefault));
535
536    // CSS styling may cause the canvas's content to be resized on
537    // the page. Go back to the Canvas to figure out the correct
538    // width and height to draw.
539    FloatRect canvasRect(0, 0, canvasWidth, canvasHeight);
540    FloatSize imageSize(imageWidth, imageHeight);
541    // We want to completely overwrite the previous frame's
542    // rendering results.
543
544    GraphicsContextStateSaver stateSaver(*context);
545    context->scale(FloatSize(1, -1));
546    context->translate(0, -imageHeight);
547    context->setImageInterpolationQuality(InterpolationNone);
548    context->drawNativeImage(cgImage.get(), imageSize, ColorSpaceDeviceRGB, canvasRect, FloatRect(FloatPoint(), imageSize), 1, CompositeCopy);
549}
550
551} // namespace WebCore
552
553#endif // USE(3D_GRAPHICS)
554