[WTF] Import std::optional reference implementation as WTF::Optional
[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_compositorTexture(0)
78 #endif
79     , m_texture(0)
80 #endif
81 {
82 #if ENABLE(ACCELERATED_2D_CANVAS) && USE(COORDINATED_GRAPHICS_THREADED)
83     if (m_renderingMode == RenderingMode::Accelerated)
84         m_platformLayerProxy = adoptRef(new TextureMapperPlatformLayerProxy);
85 #endif
86 }
87
88 ImageBufferData::~ImageBufferData()
89 {
90     if (m_renderingMode != Accelerated)
91         return;
92
93 #if ENABLE(ACCELERATED_2D_CANVAS)
94     GLContext* previousActiveContext = GLContext::current();
95     PlatformDisplay::sharedDisplayForCompositing().sharingGLContext()->makeContextCurrent();
96
97     if (m_texture)
98         glDeleteTextures(1, &m_texture);
99
100 #if USE(COORDINATED_GRAPHICS_THREADED)
101     if (m_compositorTexture)
102         glDeleteTextures(1, &m_compositorTexture);
103 #endif
104
105     if (previousActiveContext)
106         previousActiveContext->makeContextCurrent();
107 #endif
108 }
109
110 #if ENABLE(ACCELERATED_2D_CANVAS)
111 #if USE(COORDINATED_GRAPHICS_THREADED)
112 void ImageBufferData::createCompositorBuffer()
113 {
114     auto* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext();
115     context->makeContextCurrent();
116
117     glGenTextures(1, &m_compositorTexture);
118     glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
119     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
120     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
121     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
122     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
123     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
124     glTexImage2D(GL_TEXTURE_2D, 0 , GL_RGBA, m_size.width(), m_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
125
126     m_compositorSurface = adoptRef(cairo_gl_surface_create_for_texture(context->cairoDevice(), CAIRO_CONTENT_COLOR_ALPHA, m_compositorTexture, m_size.width(), m_size.height()));
127     m_compositorCr = adoptRef(cairo_create(m_compositorSurface.get()));
128     cairo_set_antialias(m_compositorCr.get(), CAIRO_ANTIALIAS_NONE);
129 }
130
131 void ImageBufferData::swapBuffersIfNeeded()
132 {
133     GLContext* previousActiveContext = GLContext::current();
134
135     if (!m_compositorTexture) {
136         createCompositorBuffer();
137         LockHolder holder(m_platformLayerProxy->lock());
138         m_platformLayerProxy->pushNextBuffer(std::make_unique<TextureMapperPlatformLayerBuffer>(m_compositorTexture, m_size, TextureMapperGL::ShouldBlend));
139     }
140
141     // It would be great if we could just swap the buffers here as we do with webgl, but that breaks the cases
142     // where one frame uses the content already rendered in the previous frame. So we just copy the content
143     // into the compositor buffer.
144     cairo_set_source_surface(m_compositorCr.get(), m_surface.get(), 0, 0);
145     cairo_set_operator(m_compositorCr.get(), CAIRO_OPERATOR_SOURCE);
146     cairo_paint(m_compositorCr.get());
147
148     if (previousActiveContext)
149         previousActiveContext->makeContextCurrent();
150 }
151 #endif
152
153 void clearSurface(cairo_surface_t* surface)
154 {
155     if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
156         return;
157
158     RefPtr<cairo_t> cr = adoptRef(cairo_create(surface));
159     cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR);
160     cairo_paint(cr.get());
161 }
162
163 void ImageBufferData::createCairoGLSurface()
164 {
165     auto* context = PlatformDisplay::sharedDisplayForCompositing().sharingGLContext();
166     context->makeContextCurrent();
167
168     // We must generate the texture ourselves, because there is no Cairo API for extracting it
169     // from a pre-existing surface.
170     glGenTextures(1, &m_texture);
171     glBindTexture(GL_TEXTURE_2D, m_texture);
172     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
173     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
174     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
175     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
176
177     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
178
179     glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, m_size.width(), m_size.height(), 0 /* border */, GL_RGBA, GL_UNSIGNED_BYTE, 0);
180
181     cairo_device_t* device = context->cairoDevice();
182
183     // Thread-awareness is a huge performance hit on non-Intel drivers.
184     cairo_gl_device_set_thread_aware(device, FALSE);
185
186     m_surface = adoptRef(cairo_gl_surface_create_for_texture(device, CAIRO_CONTENT_COLOR_ALPHA, m_texture, m_size.width(), m_size.height()));
187     clearSurface(m_surface.get());
188 }
189 #endif
190
191 ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace, RenderingMode renderingMode, bool& success)
192     : m_data(IntSize(size), renderingMode)
193     , m_logicalSize(size)
194     , m_resolutionScale(resolutionScale)
195 {
196     success = false;  // Make early return mean error.
197
198     float scaledWidth = ceilf(m_resolutionScale * size.width());
199     float scaledHeight = ceilf(m_resolutionScale * size.height());
200
201     // FIXME: Should we automatically use a lower resolution?
202     if (!FloatSize(scaledWidth, scaledHeight).isExpressibleAsIntSize())
203         return;
204
205     m_size = IntSize(scaledWidth, scaledHeight);
206     m_data.m_size = m_size;
207
208     if (m_size.isEmpty())
209         return;
210
211 #if ENABLE(ACCELERATED_2D_CANVAS)
212     if (m_data.m_renderingMode == Accelerated) {
213         m_data.createCairoGLSurface();
214         if (!m_data.m_surface || cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS)
215             m_data.m_renderingMode = Unaccelerated; // If allocation fails, fall back to non-accelerated path.
216     }
217     if (m_data.m_renderingMode == Unaccelerated)
218 #else
219     ASSERT(m_data.m_renderingMode != Accelerated);
220 #endif
221         m_data.m_surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, m_size.width(), m_size.height()));
222
223     if (cairo_surface_status(m_data.m_surface.get()) != CAIRO_STATUS_SUCCESS)
224         return;  // create will notice we didn't set m_initialized and fail.
225
226     cairoSurfaceSetDeviceScale(m_data.m_surface.get(), m_resolutionScale, m_resolutionScale);
227
228     RefPtr<cairo_t> cr = adoptRef(cairo_create(m_data.m_surface.get()));
229     m_data.m_platformContext.setCr(cr.get());
230     m_data.m_context = std::make_unique<GraphicsContext>(&m_data.m_platformContext);
231     success = true;
232 }
233
234 ImageBuffer::~ImageBuffer()
235 {
236 }
237
238 std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, const GraphicsContext& context)
239 {
240     return createCompatibleBuffer(size, ColorSpaceSRGB, context);
241 }
242
243 GraphicsContext& ImageBuffer::context() const
244 {
245     return *m_data.m_context;
246 }
247
248 RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, ScaleBehavior scaleBehavior)
249 {
250     return imageBuffer->copyImage(DontCopyBackingStore, scaleBehavior);
251 }
252
253 RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
254 {
255     // copyCairoImageSurface inherits surface's device scale factor.
256     if (copyBehavior == CopyBackingStore)
257         return BitmapImage::create(copyCairoImageSurface(m_data.m_surface.get()));
258
259     // BitmapImage will release the passed in surface on destruction
260     return BitmapImage::create(RefPtr<cairo_surface_t>(m_data.m_surface));
261 }
262
263 BackingStoreCopy ImageBuffer::fastCopyImageMode()
264 {
265     return DontCopyBackingStore;
266 }
267
268 void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
269 {
270     imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
271 }
272
273 void ImageBuffer::draw(GraphicsContext& destinationContext, const FloatRect& destRect, const FloatRect& srcRect,
274     CompositeOperator op, BlendMode blendMode)
275 {
276     BackingStoreCopy copyMode = &destinationContext == &context() ? CopyBackingStore : DontCopyBackingStore;
277     RefPtr<Image> image = copyImage(copyMode);
278     destinationContext.drawImage(*image, destRect, srcRect, ImagePaintingOptions(op, blendMode, ImageOrientationDescription()));
279 }
280
281 void ImageBuffer::drawPattern(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform,
282     const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode)
283 {
284     if (RefPtr<Image> image = copyImage(DontCopyBackingStore))
285         image->drawPattern(context, destRect, srcRect, patternTransform, phase, spacing, op);
286 }
287
288 void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable)
289 {
290     // FIXME: Enable color space conversions on accelerated canvases.
291     if (cairo_surface_get_type(m_data.m_surface.get()) != CAIRO_SURFACE_TYPE_IMAGE)
292         return;
293
294     unsigned char* dataSrc = cairo_image_surface_get_data(m_data.m_surface.get());
295     int stride = cairo_image_surface_get_stride(m_data.m_surface.get());
296     for (int y = 0; y < m_size.height(); ++y) {
297         unsigned* row = reinterpret_cast_ptr<unsigned*>(dataSrc + stride * y);
298         for (int x = 0; x < m_size.width(); x++) {
299             unsigned* pixel = row + x;
300             Color pixelColor = colorFromPremultipliedARGB(*pixel);
301             pixelColor = Color(lookUpTable[pixelColor.red()],
302                                lookUpTable[pixelColor.green()],
303                                lookUpTable[pixelColor.blue()],
304                                pixelColor.alpha());
305             *pixel = premultipliedARGBFromColor(pixelColor);
306         }
307     }
308     cairo_surface_mark_dirty_rectangle(m_data.m_surface.get(), 0, 0, m_logicalSize.width(), m_logicalSize.height());
309 }
310
311 RefPtr<cairo_surface_t> copySurfaceToImageAndAdjustRect(cairo_surface_t* surface, IntRect& rect)
312 {
313     cairo_surface_type_t surfaceType = cairo_surface_get_type(surface);
314
315     // If we already have an image, we write directly to the underlying data;
316     // otherwise we create a temporary surface image
317     if (surfaceType == CAIRO_SURFACE_TYPE_IMAGE)
318         return surface;
319     
320     rect.setX(0);
321     rect.setY(0);
322     return adoptRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect.width(), rect.height()));
323 }
324
325 template <Multiply multiplied>
326 RefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, const IntRect& logicalRect, const ImageBufferData& data, const IntSize& size, const IntSize& logicalSize, float resolutionScale)
327 {
328     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
329     if (!result)
330         return nullptr;
331
332     if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > size.width() || (rect.y() + rect.height()) > size.height())
333         result->zeroFill();
334
335     int originx = rect.x();
336     int destx = 0;
337     if (originx < 0) {
338         destx = -originx;
339         originx = 0;
340     }
341     int endx = rect.maxX();
342     if (endx > size.width())
343         endx = size.width();
344     int numColumns = endx - originx;
345
346     int originy = rect.y();
347     int desty = 0;
348     if (originy < 0) {
349         desty = -originy;
350         originy = 0;
351     }
352     int endy = rect.maxY();
353     if (endy > size.height())
354         endy = size.height();
355     int numRows = endy - originy;
356
357     // The size of the derived surface is in BackingStoreCoordinateSystem.
358     // We need to set the device scale for the derived surface from this ImageBuffer.
359     IntRect imageRect(originx, originy, numColumns, numRows);
360     RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(data.m_surface.get(), imageRect);
361     cairoSurfaceSetDeviceScale(imageSurface.get(), resolutionScale, resolutionScale);
362     originx = imageRect.x();
363     originy = imageRect.y();
364     if (imageSurface != data.m_surface.get()) {
365         // This cairo surface operation is done in LogicalCoordinateSystem.
366         IntRect logicalArea = intersection(logicalRect, IntRect(0, 0, logicalSize.width(), logicalSize.height()));
367         copyRectFromOneSurfaceToAnother(data.m_surface.get(), imageSurface.get(), IntSize(-logicalArea.x(), -logicalArea.y()), IntRect(IntPoint(), logicalArea.size()), IntSize(), CAIRO_OPERATOR_SOURCE);
368     }
369
370     unsigned char* dataSrc = cairo_image_surface_get_data(imageSurface.get());
371     unsigned char* dataDst = result->data();
372     int stride = cairo_image_surface_get_stride(imageSurface.get());
373     unsigned destBytesPerRow = 4 * rect.width();
374
375     unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4;
376     for (int y = 0; y < numRows; ++y) {
377         unsigned* row = reinterpret_cast_ptr<unsigned*>(dataSrc + stride * (y + originy));
378         for (int x = 0; x < numColumns; x++) {
379             int basex = x * 4;
380             unsigned* pixel = row + x + originx;
381
382             // Avoid calling Color::colorFromPremultipliedARGB() because one
383             // function call per pixel is too expensive.
384             unsigned alpha = (*pixel & 0xFF000000) >> 24;
385             unsigned red = (*pixel & 0x00FF0000) >> 16;
386             unsigned green = (*pixel & 0x0000FF00) >> 8;
387             unsigned blue = (*pixel & 0x000000FF);
388
389             if (multiplied == Unmultiplied) {
390                 if (alpha && alpha != 255) {
391                     red = red * 255 / alpha;
392                     green = green * 255 / alpha;
393                     blue = blue * 255 / alpha;
394                 }
395             }
396
397             destRows[basex]     = red;
398             destRows[basex + 1] = green;
399             destRows[basex + 2] = blue;
400             destRows[basex + 3] = alpha;
401         }
402         destRows += destBytesPerRow;
403     }
404
405     return result.release();
406 }
407
408 template<typename Unit>
409 inline Unit logicalUnit(const Unit& value, ImageBuffer::CoordinateSystem coordinateSystemOfValue, float resolutionScale)
410 {
411     if (coordinateSystemOfValue == ImageBuffer::LogicalCoordinateSystem || resolutionScale == 1.0)
412         return value;
413     Unit result(value);
414     result.scale(1.0 / resolutionScale);
415     return result;
416 }
417
418 template<typename Unit>
419 inline Unit backingStoreUnit(const Unit& value, ImageBuffer::CoordinateSystem coordinateSystemOfValue, float resolutionScale)
420 {
421     if (coordinateSystemOfValue == ImageBuffer::BackingStoreCoordinateSystem || resolutionScale == 1.0)
422         return value;
423     Unit result(value);
424     result.scale(resolutionScale);
425     return result;
426 }
427
428 RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
429 {
430     IntRect logicalRect = logicalUnit(rect, coordinateSystem, m_resolutionScale);
431     IntRect backingStoreRect = backingStoreUnit(rect, coordinateSystem, m_resolutionScale);
432     return getImageData<Unmultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale);
433 }
434
435 RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, CoordinateSystem coordinateSystem) const
436 {
437     IntRect logicalRect = logicalUnit(rect, coordinateSystem, m_resolutionScale);
438     IntRect backingStoreRect = backingStoreUnit(rect, coordinateSystem, m_resolutionScale);
439     return getImageData<Premultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale);
440 }
441
442 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem)
443 {
444     IntRect scaledSourceRect = backingStoreUnit(sourceRect, coordinateSystem, m_resolutionScale);
445     IntSize scaledSourceSize = backingStoreUnit(sourceSize, coordinateSystem, m_resolutionScale);
446     IntPoint scaledDestPoint = backingStoreUnit(destPoint, coordinateSystem, m_resolutionScale);
447     IntRect logicalSourceRect = logicalUnit(sourceRect, coordinateSystem, m_resolutionScale);
448     IntPoint logicalDestPoint = logicalUnit(destPoint, coordinateSystem, m_resolutionScale);
449
450     ASSERT(scaledSourceRect.width() > 0);
451     ASSERT(scaledSourceRect.height() > 0);
452
453     int originx = scaledSourceRect.x();
454     int destx = scaledDestPoint.x() + scaledSourceRect.x();
455     int logicalDestx = logicalDestPoint.x() + logicalSourceRect.x();
456     ASSERT(destx >= 0);
457     ASSERT(destx < m_size.width());
458     ASSERT(originx >= 0);
459     ASSERT(originx <= scaledSourceRect.maxX());
460
461     int endx = scaledDestPoint.x() + scaledSourceRect.maxX();
462     int logicalEndx = logicalDestPoint.x() + logicalSourceRect.maxX();
463     ASSERT(endx <= m_size.width());
464
465     int numColumns = endx - destx;
466     int logicalNumColumns = logicalEndx - logicalDestx;
467
468     int originy = scaledSourceRect.y();
469     int desty = scaledDestPoint.y() + scaledSourceRect.y();
470     int logicalDesty = logicalDestPoint.y() + logicalSourceRect.y();
471     ASSERT(desty >= 0);
472     ASSERT(desty < m_size.height());
473     ASSERT(originy >= 0);
474     ASSERT(originy <= scaledSourceRect.maxY());
475
476     int endy = scaledDestPoint.y() + scaledSourceRect.maxY();
477     int logicalEndy = logicalDestPoint.y() + logicalSourceRect.maxY();
478     ASSERT(endy <= m_size.height());
479     int numRows = endy - desty;
480     int logicalNumRows = logicalEndy - logicalDesty;
481
482     // The size of the derived surface is in BackingStoreCoordinateSystem.
483     // We need to set the device scale for the derived surface from this ImageBuffer.
484     IntRect imageRect(destx, desty, numColumns, numRows);
485     RefPtr<cairo_surface_t> imageSurface = copySurfaceToImageAndAdjustRect(m_data.m_surface.get(), imageRect);
486     cairoSurfaceSetDeviceScale(imageSurface.get(), m_resolutionScale, m_resolutionScale);
487     destx = imageRect.x();
488     desty = imageRect.y();
489
490     unsigned char* pixelData = cairo_image_surface_get_data(imageSurface.get());
491
492     unsigned srcBytesPerRow = 4 * scaledSourceSize.width();
493     int stride = cairo_image_surface_get_stride(imageSurface.get());
494
495     unsigned char* srcRows = source->data() + originy * srcBytesPerRow + originx * 4;
496     for (int y = 0; y < numRows; ++y) {
497         unsigned* row = reinterpret_cast_ptr<unsigned*>(pixelData + stride * (y + desty));
498         for (int x = 0; x < numColumns; x++) {
499             int basex = x * 4;
500             unsigned* pixel = row + x + destx;
501
502             // Avoid calling Color::premultipliedARGBFromColor() because one
503             // function call per pixel is too expensive.
504             unsigned red = srcRows[basex];
505             unsigned green = srcRows[basex + 1];
506             unsigned blue = srcRows[basex + 2];
507             unsigned alpha = srcRows[basex + 3];
508
509             if (multiplied == Unmultiplied) {
510                 if (alpha != 255) {
511                     red = (red * alpha + 254) / 255;
512                     green = (green * alpha + 254) / 255;
513                     blue = (blue * alpha + 254) / 255;
514                 }
515             }
516
517             *pixel = (alpha << 24) | red  << 16 | green  << 8 | blue;
518         }
519         srcRows += srcBytesPerRow;
520     }
521
522     // This cairo surface operation is done in LogicalCoordinateSystem.
523     cairo_surface_mark_dirty_rectangle(imageSurface.get(), logicalDestx, logicalDesty, logicalNumColumns, logicalNumRows);
524
525     if (imageSurface != m_data.m_surface.get()) {
526         // This cairo surface operation is done in LogicalCoordinateSystem.
527         copyRectFromOneSurfaceToAnother(imageSurface.get(), m_data.m_surface.get(), IntSize(), IntRect(0, 0, logicalNumColumns, logicalNumRows), IntSize(logicalDestPoint.x() + logicalSourceRect.x(), logicalDestPoint.y() + logicalSourceRect.y()), CAIRO_OPERATOR_SOURCE);
528     }
529 }
530
531 #if !PLATFORM(GTK) && !PLATFORM(EFL)
532 static cairo_status_t writeFunction(void* output, const unsigned char* data, unsigned int length)
533 {
534     if (!reinterpret_cast<Vector<unsigned char>*>(output)->tryAppend(data, length))
535         return CAIRO_STATUS_WRITE_ERROR;
536     return CAIRO_STATUS_SUCCESS;
537 }
538
539 static bool encodeImage(cairo_surface_t* image, const String& mimeType, Vector<char>* output)
540 {
541     ASSERT_UNUSED(mimeType, mimeType == "image/png"); // Only PNG output is supported for now.
542
543     return cairo_surface_write_to_png_stream(image, writeFunction, output) == CAIRO_STATUS_SUCCESS;
544 }
545
546 String ImageBuffer::toDataURL(const String& mimeType, std::optional<double>, CoordinateSystem) const
547 {
548     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
549
550     cairo_surface_t* image = cairo_get_target(context().platformContext()->cr());
551
552     Vector<char> encodedImage;
553     if (!image || !encodeImage(image, mimeType, &encodedImage))
554         return "data:,";
555
556     Vector<char> base64Data;
557     base64Encode(encodedImage, base64Data);
558
559     return "data:" + mimeType + ";base64," + base64Data;
560 }
561 #endif
562
563 #if ENABLE(ACCELERATED_2D_CANVAS) && !USE(COORDINATED_GRAPHICS_THREADED)
564 void ImageBufferData::paintToTextureMapper(TextureMapper& textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity)
565 {
566     ASSERT(m_texture);
567
568     // Cairo may change the active context, so we make sure to change it back after flushing.
569     GLContext* previousActiveContext = GLContext::current();
570     cairo_surface_flush(m_surface.get());
571     previousActiveContext->makeContextCurrent();
572
573     static_cast<TextureMapperGL&>(textureMapper).drawTexture(m_texture, TextureMapperGL::ShouldBlend, m_size, targetRect, matrix, opacity);
574 }
575 #endif
576
577 PlatformLayer* ImageBuffer::platformLayer() const
578 {
579 #if ENABLE(ACCELERATED_2D_CANVAS)
580     if (m_data.m_texture)
581         return const_cast<ImageBufferData*>(&m_data);
582 #endif
583     return 0;
584 }
585
586 bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, GC3Denum target, Platform3DObject destinationTexture, GC3Denum internalformat, bool premultiplyAlpha, bool flipY)
587 {
588 #if ENABLE(ACCELERATED_2D_CANVAS)
589     ASSERT_WITH_MESSAGE(m_resolutionScale == 1.0, "Since the HiDPI Canvas feature is removed, the resolution factor here is always 1.");
590     if (premultiplyAlpha || flipY)
591         return false;
592
593     if (!m_data.m_texture)
594         return false;
595
596     GC3Denum bindTextureTarget;
597     switch (target) {
598     case GL_TEXTURE_2D:
599         bindTextureTarget = GL_TEXTURE_2D;
600         break;
601     case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
602     case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
603     case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
604     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
605     case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
606     case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
607         bindTextureTarget = GL_TEXTURE_CUBE_MAP;
608         break;
609     default:
610         return false;
611     }
612
613     cairo_surface_flush(m_data.m_surface.get());
614
615     std::unique_ptr<GLContext> context = GLContext::createOffscreenContext(&PlatformDisplay::sharedDisplayForCompositing());
616     context->makeContextCurrent();
617     uint32_t fbo;
618     glGenFramebuffers(1, &fbo);
619     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
620     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_data.m_texture, 0);
621     glBindTexture(bindTextureTarget, destinationTexture);
622     glCopyTexImage2D(target, 0, internalformat, 0, 0, m_size.width(), m_size.height(), 0);
623     glBindTexture(bindTextureTarget, 0);
624     glBindFramebuffer(GL_FRAMEBUFFER, 0);
625     glFlush();
626     glDeleteFramebuffers(1, &fbo);
627     return true;
628 #else
629     UNUSED_PARAM(target);
630     UNUSED_PARAM(destinationTexture);
631     UNUSED_PARAM(internalformat);
632     UNUSED_PARAM(premultiplyAlpha);
633     UNUSED_PARAM(flipY);
634     return false;
635 #endif
636 }
637
638 } // namespace WebCore
639
640 #endif // USE(CAIRO)