0453a6257f0dec794e378fea561c2b716e30a2c6
[WebKit-https.git] / Source / WebCore / platform / graphics / cairo / ImageBufferCairo.cpp
1 /*
2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org>
4  * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
5  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "ImageBuffer.h"
31
32 #if USE(CAIRO)
33
34 #include "BitmapImage.h"
35 #include "CairoUtilities.h"
36 #include "Color.h"
37 #include "GraphicsContext.h"
38 #include "MIMETypeRegistry.h"
39 #include "NotImplemented.h"
40 #include "Pattern.h"
41 #include "PlatformContextCairo.h"
42 #include "RefPtrCairo.h"
43 #include <cairo.h>
44 #include <runtime/JSCInlines.h>
45 #include <runtime/TypedArrayInlines.h>
46 #include <wtf/Vector.h>
47 #include <wtf/text/Base64.h>
48 #include <wtf/text/WTFString.h>
49
50 #if ENABLE(ACCELERATED_2D_CANVAS)
51 #include "GLContext.h"
52 #include "TextureMapperGL.h"
53 #include <cairo-gl.h>
54
55 #if USE(OPENGL_ES_2)
56 #include <GLES2/gl2.h>
57 #else
58 #include "OpenGLShims.h"
59 #endif
60
61 #if USE(COORDINATED_GRAPHICS_THREADED)
62 #include "TextureMapperPlatformLayerBuffer.h"
63 #include "TextureMapperPlatformLayerProxy.h"
64 #endif
65 #endif
66
67 using namespace std;
68
69 namespace WebCore {
70
71 ImageBufferData::ImageBufferData(const IntSize& size, RenderingMode renderingMode)
72     : m_platformContext(0)
73     , m_size(size)
74     , m_renderingMode(renderingMode)
75 #if ENABLE(ACCELERATED_2D_CANVAS)
76 #if USE(COORDINATED_GRAPHICS_THREADED)
77     , m_platformLayerProxy(adoptRef(new TextureMapperPlatformLayerProxy))
78     , m_compositorTexture(0)
79 #endif
80     , m_texture(0)
81 #endif
82 {
83 }
84
85 ImageBufferData::~ImageBufferData()
86 {
87     if (m_renderingMode != Accelerated)
88         return;
89
90 #if ENABLE(ACCELERATED_2D_CANVAS)
91     GLContext* previousActiveContext = GLContext::getCurrent();
92     GLContext::sharingContext()->makeContextCurrent();
93
94     if (m_texture)
95         glDeleteTextures(1, &m_texture);
96
97 #if USE(COORDINATED_GRAPHICS_THREADED)
98     if (m_compositorTexture)
99         glDeleteTextures(1, &m_compositorTexture);
100 #endif
101
102     if (previousActiveContext)
103         previousActiveContext->makeContextCurrent();
104 #endif
105 }
106
107 #if ENABLE(ACCELERATED_2D_CANVAS)
108 #if USE(COORDINATED_GRAPHICS_THREADED)
109 void ImageBufferData::createCompositorBuffer()
110 {
111     GLContext::sharingContext()->makeContextCurrent();
112
113     glGenTextures(1, &m_compositorTexture);
114     glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
115     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
116     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
117     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
118     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
119     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
120     glTexImage2D(GL_TEXTURE_2D, 0 , GL_RGBA, m_size.width(), m_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
121
122     cairo_device_t* device = GLContext::sharingContext()->cairoDevice();
123     m_compositorSurface = adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, m_compositorTexture, m_size.width(), m_size.height()));
124     m_compositorCr = adoptRef(cairo_create(m_compositorSurface.get()));
125     cairo_set_antialias(m_compositorCr.get(), CAIRO_ANTIALIAS_NONE);
126 }
127
128 void ImageBufferData::swapBuffersIfNeeded()
129 {
130     GLContext* previousActiveContext = GLContext::getCurrent();
131
132     if (!m_compositorTexture) {
133         createCompositorBuffer();
134         LockHolder holder(m_platformLayerProxy->lock());
135         m_platformLayerProxy->pushNextBuffer(std::make_unique<TextureMapperPlatformLayerBuffer>(m_compositorTexture, m_size, TextureMapperGL::ShouldBlend));
136     }
137
138     // It would be great if we could just swap the buffers here as we do with webgl, but that breaks the cases
139     // where one frame uses the content already rendered in the previous frame. So we just copy the content
140     // into the compositor buffer.
141     cairo_set_source_surface(m_compositorCr.get(), m_surface.get(), 0, 0);
142     cairo_set_operator(m_compositorCr.get(), CAIRO_OPERATOR_SOURCE);
143     cairo_paint(m_compositorCr.get());
144
145     if (previousActiveContext)
146         previousActiveContext->makeContextCurrent();
147 }
148 #endif
149
150 void clearSurface(cairo_surface_t* surface)
151 {
152     if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
153         return;
154
155     RefPtr<cairo_t> cr = adoptRef(cairo_create(surface));
156     cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR);
157     cairo_paint(cr.get());
158 }
159
160 void ImageBufferData::createCairoGLSurface()
161 {
162     GLContext::sharingContext()->makeContextCurrent();
163
164     // We must generate the texture ourselves, because there is no Cairo API for extracting it
165     // from a pre-existing surface.
166     glGenTextures(1, &m_texture);
167     glBindTexture(GL_TEXTURE_2D, m_texture);
168     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
169     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
170     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
171     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
172
173     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
174
175     glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, m_size.width(), m_size.height(), 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, 0);
176
177     GLContext* context = GLContext::sharingContext();
178     cairo_device_t* device = context->cairoDevice();
179
180     // Thread-awareness is a huge performance hit on non-Intel drivers.
181     cairo_gl_device_set_thread_aware(device, FALSE);
182
183     m_surface = adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, m_texture, m_size.width(), m_size.height()));
184     clearSurface(m_surface.get());
185 }
186 #endif
187
188 ImageBuffer::ImageBuffer(const FloatSize& size, float /* resolutionScale */, ColorSpace, RenderingMode renderingMode, bool& success)
189     : m_data(IntSize(size), renderingMode)
190     , m_size(size)
191     , m_logicalSize(size)
192 {
193     success = false;  // Make early return mean error.
194     if (m_size.isEmpty())
195         return;
196
197 #if ENABLE(ACCELERATED_2D_CANVAS)
198     if (m_data.m_renderingMode == Accelerated) {
199         m_data.createCairoGLSurface();
200         if (!m_data.m_surface || cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS)
201             m_data.m_renderingMode = Unaccelerated; // If allocation fails, fall back to non-accelerated path.
202     }
203     if (m_data.m_renderingMode == Unaccelerated)
204 #else
205     ASSERT(m_data.m_renderingMode != Accelerated);
206 #endif
207         m_data.m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width(), size.height()));
208
209     if (cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS)
210         return;  // create will notice we didn't set m_initialized and fail.
211
212     RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface.get()));
213     m_data.m_platformContext.setCr(cr.get());
214     m_data.m_context = std::make_unique<GraphicsContext>(&m_data.m_platformContext);
215     success = true;
216 }
217
218 ImageBuffer::~ImageBuffer()
219 {
220 }
221
222 GraphicsContext& ImageBuffer::context() const
223 {
224     return *m_data.m_context;
225 }
226
227 RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, ScaleBehavior scaleBehavior)
228 {
229     return imageBuffer->copyImage(DontCopyBackingStore, scaleBehavior);
230 }
231
232 RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
233 {
234     if (copyBehavior == CopyBackingStore)
235         return BitmapImage::create(copyCairoImageSurface(m_data.m_surface.get()));
236
237     // BitmapImage will release the passed in surface on destruction
238     return BitmapImage::create(RefPtr<cairo_surface_t>(m_data.m_surface));
239 }
240
241 BackingStoreCopy ImageBuffer::fastCopyImageMode()
242 {
243     return DontCopyBackingStore;
244 }
245
246 void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
247 {
248     imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
249 }
250
251 void ImageBuffer::draw(GraphicsContext& destinationContext, const FloatRect& destRect, const FloatRect& srcRect,
252     CompositeOperator op, BlendMode blendMode)
253 {
254     BackingStoreCopy copyMode = &destinationContext == &context() ? CopyBackingStore : DontCopyBackingStore;
255     RefPtr<Image> image = copyImage(copyMode);
256     destinationContext.drawImage(*image, destRect, srcRect, ImagePaintingOptions(op, blendMode, ImageOrientationDescription()));
257 }
258
259 void ImageBuffer::drawPattern(GraphicsContext& context, const FloatRect& srcRect, const AffineTransform& patternTransform,
260     const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, const FloatRect& destRect, BlendMode)
261 {
262     if (RefPtr<Image> image = copyImage(DontCopyBackingStore))
263         image->drawPattern(context, srcRect, patternTransform, phase, spacing, op, destRect);
264 }
265
266 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
267 {
268     // FIXME: Enable color space conversions on accelerated canvases.
269     if (cairo_surface_get_type(m_data.m_surface.get()) != CAIRO_SURFACE_TYPE_IMAGE)
270         return;
271
272     unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface.get());
273     int stride = cairo_image_surface_get_stride(m_data.m_surface.get());
274     for (int y = 0; y < m_size.height(); ++y) {
275         unsigned* row = reinterpret_cast_ptr<unsigned*>(dataSrc + stride * y);
276         for (int x = 0; x < m_size.width(); x++) {
277             unsigned* pixel = row + x;
278             Color pixelColor = colorFromPremultipliedARGB(*pixel);
279             pixelColor = Color(lookUpTable[pixelColor.red()],
280                                lookUpTable[pixelColor.green()],
281                                lookUpTable[pixelColor.blue()],
282                                pixelColor.alpha());
283             *pixel = premultipliedARGBFromColor(pixelColor);
284         }
285     }
286     cairo_surface_mark_dirty_rectangle(m_data.m_surface.get(), 0, 0, m_size.width(), m_size.height());
287 }
288
289 RefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* surface, IntRect& rect)
290 {
291     cairo_surface_type_t surfaceType = cairo_surface_get_type(surface);
292
293     // If we already have an image, we write directly to the underlying data;
294     // otherwise we create a temporary surface image
295     if (surfaceType == CAIRO_SURFACE_TYPE_IMAGE)
296         return surface;
297     
298     rect.setX(0);
299     rect.setY(0);
300     return adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect.width(), rect.height()));
301 }
302
303 template <Multiply multiplied>
304 RefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const ImageBufferData& data, const IntSize& size)
305 {
306     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
307
308     if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
309         result->zeroFill();
310
311     int originx = rect.x();
312     int destx = 0;
313     if (originx < 0) {
314         destx = -originx;
315         originx = 0;
316     }
317     int endx = rect.maxX();
318     if (endx > size.width())
319         endx = size.width();
320     int numColumns = endx - originx;
321
322     int originy = rect.y();
323     int desty = 0;
324     if (originy < 0) {
325         desty = -originy;
326         originy = 0;
327     }
328     int endy = rect.maxY();
329     if (endy > size.height())
330         endy = size.height();
331     int numRows = endy - originy;
332
333     IntRect imageRect(originx, originy, numColumns, numRows);
334     RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(data.m_surface.get(), imageRect);
335     originx = imageRect.x();
336     originy = imageRect.y();
337     if (imageSurface != data.m_surface.get()) {
338         IntRect area = intersection(rect, IntRect(0, 0, size.width(), size.height()));
339         copyRectFromOneSurfaceToAnother(data.m_surface.get(), imageSurface.get(), IntSize(-area.x(), -area.y()), IntRect(IntPoint(), area.size()), IntSize(), CAIRO_OPERATOR_SOURCE);
340     }
341
342     unsigned char* dataSrc = cairo_image_surface_get_data(imageSurface.get());
343     unsigned char* dataDst = result->data();
344     int stride = cairo_image_surface_get_stride(imageSurface.get());
345     unsigned destBytesPerRow = 4 * rect.width();
346
347     unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4;
348     for (int y = 0; y < numRows; ++y) {
349         unsigned* row = reinterpret_cast_ptr<unsigned*>(dataSrc + stride * (y + originy));
350         for (int x = 0; x < numColumns; x++) {
351             int basex = x * 4;
352             unsigned* pixel = row + x + originx;
353
354             // Avoid calling Color::colorFromPremultipliedARGB() because one
355             // function call per pixel is too expensive.
356             unsigned alpha = (*pixel & 0xFF000000) >> 24;
357             unsigned red = (*pixel & 0x00FF0000) >> 16;
358             unsigned green = (*pixel & 0x0000FF00) >> 8;
359             unsigned blue = (*pixel & 0x000000FF);
360
361             if (multiplied == Unmultiplied) {
362                 if (alpha && alpha != 255) {
363                     red = red * 255 / alpha;
364                     green = green * 255 / alpha;
365                     blue = blue * 255 / alpha;
366                 }
367             }
368
369             destRows[basex]     = red;
370             destRows[basex + 1] = green;
371             destRows[basex + 2] = blue;
372             destRows[basex + 3] = alpha;
373         }
374         destRows += destBytesPerRow;
375     }
376
377     return result.release();
378 }
379
380 RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem) const
381 {
382     return getImageData<Unmultiplied>(rect, m_data, m_size);
383 }
384
385 RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem) const
386 {
387     return getImageData<Premultiplied>(rect, m_data, m_size);
388 }
389
390 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem)
391 {
392
393     ASSERT(sourceRect.width() > 0);
394     ASSERT(sourceRect.height() > 0);
395
396     int originx = sourceRect.x();
397     int destx = destPoint.x() + sourceRect.x();
398     ASSERT(destx >= 0);
399     ASSERT(destx < m_size.width());
400     ASSERT(originx >= 0);
401     ASSERT(originx <= sourceRect.maxX());
402
403     int endx = destPoint.x() + sourceRect.maxX();
404     ASSERT(endx <= m_size.width());
405
406     int numColumns = endx - destx;
407
408     int originy = sourceRect.y();
409     int desty = destPoint.y() + sourceRect.y();
410     ASSERT(desty >= 0);
411     ASSERT(desty < m_size.height());
412     ASSERT(originy >= 0);
413     ASSERT(originy <= sourceRect.maxY());
414
415     int endy = destPoint.y() + sourceRect.maxY();
416     ASSERT(endy <= m_size.height());
417     int numRows = endy - desty;
418
419     IntRect imageRect(destx, desty, numColumns, numRows);
420     RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(m_data.m_surface.get(), imageRect);
421     destx = imageRect.x();
422     desty = imageRect.y();
423
424     unsigned char* pixelData = cairo_image_surface_get_data(imageSurface.get());
425
426     unsigned srcBytesPerRow = 4 * sourceSize.width();
427     int stride = cairo_image_surface_get_stride(imageSurface.get());
428
429     unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4;
430     for (int y = 0; y < numRows; ++y) {
431         unsigned* row = reinterpret_cast_ptr<unsigned*>(pixelData + stride * (y + desty));
432         for (int x = 0; x < numColumns; x++) {
433             int basex = x * 4;
434             unsigned* pixel = row + x + destx;
435
436             // Avoid calling Color::premultipliedARGBFromColor() because one
437             // function call per pixel is too expensive.
438             unsigned red = srcRows[basex];
439             unsigned green = srcRows[basex + 1];
440             unsigned blue = srcRows[basex + 2];
441             unsigned alpha = srcRows[basex + 3];
442
443             if (multiplied == Unmultiplied) {
444                 if (alpha != 255) {
445                     red = (red * alpha + 254) / 255;
446                     green = (green * alpha + 254) / 255;
447                     blue = (blue * alpha + 254) / 255;
448                 }
449             }
450
451             *pixel = (alpha << 24) | red  << 16 | green  << 8 | blue;
452         }
453         srcRows += srcBytesPerRow;
454     }
455
456     cairo_surface_mark_dirty_rectangle(imageSurface.get(), destx, desty, numColumns, numRows);
457
458     if (imageSurface != m_data.m_surface.get())
459         copyRectFromOneSurfaceToAnother(imageSurface.get(), m_data.m_surface.get(), IntSize(), IntRect(0, 0, numColumns, numRows), IntSize(destPoint.x() + sourceRect.x(), destPoint.y() + sourceRect.y()), CAIRO_OPERATOR_SOURCE);
460 }
461
462 #if !PLATFORM(GTK) && !PLATFORM(EFL)
463 static cairo_status_t writeFunction(void* output, const unsigned char* data, unsigned int length)
464 {
465     if (!reinterpret_cast<Vector<unsigned char>*>(output)->tryAppend(data, length))
466         return CAIRO_STATUS_WRITE_ERROR;
467     return CAIRO_STATUS_SUCCESS;
468 }
469
470 static bool encodeImage(cairo_surface_t* image, const String& mimeType, Vector<char>* output)
471 {
472     ASSERT_UNUSED(mimeType, mimeType == "image/png"); // Only PNG output is supported for now.
473
474     return cairo_surface_write_to_png_stream(image, writeFunction, output) == CAIRO_STATUS_SUCCESS;
475 }
476
477 String ImageBuffer::toDataURL(const String& mimeType, const double*, CoordinateSystem) const
478 {
479     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
480
481     cairo_surface_t* image = cairo_get_target(context().platformContext()->cr());
482
483     Vector<char> encodedImage;
484     if (!image || !encodeImage(image, mimeType, &encodedImage))
485         return "data:,";
486
487     Vector<char> base64Data;
488     base64Encode(encodedImage, base64Data);
489
490     return "data:" + mimeType + ";base64," + base64Data;
491 }
492 #endif
493
494 #if ENABLE(ACCELERATED_2D_CANVAS) && !USE(COORDINATED_GRAPHICS_THREADED)
495 void ImageBufferData::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
496 {
497     ASSERT(m_texture);
498
499     // Cairo may change the active context, so we make sure to change it back after flushing.
500     GLContext* previousActiveContext = GLContext::getCurrent();
501     cairo_surface_flush(m_surface.get());
502     previousActiveContext->makeContextCurrent();
503
504     static_cast<TextureMapperGL&>(textureMapper).drawTexture(m_texture, TextureMapperGL::ShouldBlend, m_size, targetRect, matrix, opacity);
505 }
506 #endif
507
508 PlatformLayer* ImageBuffer::platformLayer() const
509 {
510 #if ENABLE(ACCELERATED_2D_CANVAS)
511     if (m_data.m_texture)
512         return const_cast<ImageBufferData*>(&m_data);
513 #endif
514     return 0;
515 }
516
517 bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, GC3Denum target, Platform3DObject destinationTexture, GC3Denum internalformat, bool premultiplyAlpha, bool flipY)
518 {
519 #if ENABLE(ACCELERATED_2D_CANVAS)
520     if (premultiplyAlpha || flipY)
521         return false;
522
523     if (!m_data.m_texture)
524         return false;
525
526     GC3Denum bindTextureTarget;
527     switch (target) {
528     case GL_TEXTURE_2D:
529         bindTextureTarget = GL_TEXTURE_2D;
530         break;
531     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
532     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
533     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
534     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
535     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
536     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
537         bindTextureTarget = GL_TEXTURE_CUBE_MAP;
538         break;
539     default:
540         return false;
541     }
542
543     cairo_surface_flush(m_data.m_surface.get());
544
545     std::unique_ptr<GLContext> context = GLContext::createContextForWindow(0, GLContext::sharingContext());
546     context->makeContextCurrent();
547     uint32_t fbo;
548     glGenFramebuffers(1, &fbo);
549     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
550     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_data.m_texture, 0);
551     glBindTexture(bindTextureTarget, destinationTexture);
552     glCopyTexImage2D(target, 0, internalformat, 0, 0, m_size.width(), m_size.height(), 0);
553     glBindTexture(bindTextureTarget, 0);
554     glBindFramebuffer(GL_FRAMEBUFFER, 0);
555     glFlush();
556     glDeleteFramebuffers(1, &fbo);
557     return true;
558 #else
559     UNUSED_PARAM(target);
560     UNUSED_PARAM(destinationTexture);
561     UNUSED_PARAM(internalformat);
562     UNUSED_PARAM(premultiplyAlpha);
563     UNUSED_PARAM(flipY);
564     return false;
565 #endif
566 }
567
568 } // namespace WebCore
569
570 #endif // USE(CAIRO)