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