[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / platform / graphics / win / ImageBufferDirect2D.cpp
1 /*
2  * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ImageBuffer.h"
28
29 #if USE(DIRECT2D)
30
31 #include "BitmapImage.h"
32 #include "COMPtr.h"
33 #include "Direct2DUtilities.h"
34 #include "GraphicsContext.h"
35 #include "ImageData.h"
36 #include "ImageDecoderDirect2D.h"
37 #include "IntRect.h"
38 #include "MIMETypeRegistry.h"
39 #include "NotImplemented.h"
40 #include "PlatformContextDirect2D.h"
41 #include <d2d1.h>
42 #include <math.h>
43 #include <wincodec.h>
44 #include <wtf/Assertions.h>
45 #include <wtf/CheckedArithmetic.h>
46 #include <wtf/MainThread.h>
47 #include <wtf/RetainPtr.h>
48 #include <wtf/text/Base64.h>
49 #include <wtf/text/WTFString.h>
50
51
52 namespace WebCore {
53
54 static FloatSize scaleSizeToUserSpace(const FloatSize& logicalSize, const IntSize& backingStoreSize, const IntSize& internalSize)
55 {
56     float xMagnification = static_cast<float>(backingStoreSize.width()) / internalSize.width();
57     float yMagnification = static_cast<float>(backingStoreSize.height()) / internalSize.height();
58     return FloatSize(logicalSize.width() * xMagnification, logicalSize.height() * yMagnification);
59 }
60
61 std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, const GraphicsContext& context)
62 {
63     if (size.isEmpty())
64         return nullptr;
65
66     RenderingMode renderingMode = context.renderingMode();
67     IntSize scaledSize = ImageBuffer::compatibleBufferSize(size, context);
68     bool success = false;
69     std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(scaledSize, 1, ColorSpaceSRGB, renderingMode, nullptr, &context, success));
70
71     if (!success)
72         return nullptr;
73
74     // Set up a corresponding scale factor on the graphics context.
75     buffer->context().scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height()));
76     return buffer;
77 }
78
79 ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace /*colorSpace*/, RenderingMode renderingMode, const HostWindow*, const GraphicsContext*, bool& success)
80     : m_logicalSize(size)
81     , m_resolutionScale(resolutionScale)
82 {
83     success = false; // Make early return mean failure.
84     float scaledWidth = std::ceil(resolutionScale * size.width());
85     float scaledHeight = std::ceil(resolutionScale * size.height());
86
87     // FIXME: Should we automatically use a lower resolution?
88     if (!FloatSize(scaledWidth, scaledHeight).isExpressibleAsIntSize())
89         return;
90
91     m_size = IntSize(scaledWidth, scaledHeight);
92     m_data.backingStoreSize = m_size;
93
94     bool accelerateRendering = renderingMode == Accelerated;
95     if (m_size.width() <= 0 || m_size.height() <= 0)
96         return;
97
98     // Prevent integer overflows
99     m_data.bytesPerRow = 4 * Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.width());
100     Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.height()) * m_data.bytesPerRow;
101     if (numBytes.hasOverflowed())
102         return;
103
104     m_data.data = Vector<char>(numBytes.unsafeGet(), 0);
105
106     m_data.bitmapSource = Direct2D::createDirect2DImageSurfaceWithData(m_data.data.data(), m_size, m_data.bytesPerRow.unsafeGet());
107     if (!m_data.bitmapSource)
108         return;
109
110     COMPtr<ID2D1RenderTarget> bitmapContext = Direct2D::createRenderTargetFromWICBitmap(m_data.bitmapSource.get());
111     if (!bitmapContext)
112         return;
113
114     // Note: This places the bitmapcontext into a locked state because of the BeginDraw call in the constructor.
115     m_data.platformContext = makeUnique<PlatformContextDirect2D>(bitmapContext.get());
116     m_data.context = makeUnique<GraphicsContext>(m_data.platformContext.get(), GraphicsContext::BitmapRenderingContextType::GPUMemory);
117
118     success = true;
119 }
120
121 ImageBuffer::ImageBuffer(const FloatSize& size, float resolutionScale, ColorSpace imageColorSpace, RenderingMode renderingMode, const HostWindow*, bool& success)
122     : ImageBuffer(size, resolutionScale, imageColorSpace, renderingMode, nullptr, nullptr, success)
123 {
124 }
125
126 ImageBuffer::~ImageBuffer() = default;
127
128 FloatSize ImageBuffer::sizeForDestinationSize(FloatSize destinationSize) const
129 {
130     return scaleSizeToUserSpace(destinationSize, m_data.backingStoreSize, internalSize());
131 }
132
133 GraphicsContext& ImageBuffer::context() const
134 {
135     return *m_data.context;
136 }
137
138 void ImageBuffer::flushContext() const
139 {
140     context().flush();
141 }
142
143 static COMPtr<IWICBitmap> createCroppedImageIfNecessary(IWICBitmap* image, const IntSize& bounds)
144 {
145     FloatSize imageSize = image ? nativeImageSize(image) : FloatSize();
146
147     if (image && (static_cast<size_t>(imageSize.width()) != static_cast<size_t>(bounds.width()) || static_cast<size_t>(imageSize.height()) != static_cast<size_t>(bounds.height()))) {
148         D2D_POINT_2U origin = { };
149         WICRect croppedDimensions = { 0, 0, bounds.width(), bounds.height() };
150
151         COMPtr<IWICBitmapClipper> bitmapClipper;
152         HRESULT hr = ImageDecoderDirect2D::systemImagingFactory()->CreateBitmapClipper(&bitmapClipper);
153         if (SUCCEEDED(hr)) {
154             hr = bitmapClipper->Initialize(image, &croppedDimensions);
155             if (SUCCEEDED(hr)) {
156                 COMPtr<IWICBitmap> croppedBitmap;
157                 hr = ImageDecoderDirect2D::systemImagingFactory()->CreateBitmapFromSource(image, WICBitmapNoCache, &croppedBitmap);
158                 if (SUCCEEDED(hr))
159                     return croppedBitmap;
160             }
161         }
162     }
163
164     return image;
165 }
166
167 static RefPtr<Image> createBitmapImageAfterScalingIfNeeded(COMPtr<IWICBitmap>&& image, IntSize internalSize, IntSize logicalSize, IntSize backingStoreSize, float resolutionScale, PreserveResolution preserveResolution)
168 {
169     if (resolutionScale == 1 || preserveResolution == PreserveResolution::Yes)
170         image = createCroppedImageIfNecessary(image.get(), internalSize);
171     else {
172         // FIXME: Need to implement scaled version
173         notImplemented();
174     }
175
176     if (!image)
177         return nullptr;
178
179     return BitmapImage::create(WTFMove(image));
180 }
181
182 RefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, PreserveResolution preserveResolution) const
183 {
184     COMPtr<IWICBitmap> image;
185     if (m_resolutionScale == 1 || preserveResolution == PreserveResolution::Yes)
186         image = copyNativeImage(copyBehavior);
187     else
188         image = copyNativeImage(DontCopyBackingStore);
189
190     return createBitmapImageAfterScalingIfNeeded(WTFMove(image), internalSize(), logicalSize(), m_data.backingStoreSize, m_resolutionScale, preserveResolution);
191 }
192
193 RefPtr<Image> ImageBuffer::sinkIntoImage(std::unique_ptr<ImageBuffer> imageBuffer, PreserveResolution preserveResolution)
194 {
195     IntSize internalSize = imageBuffer->internalSize();
196     IntSize logicalSize = imageBuffer->logicalSize();
197     IntSize backingStoreSize = imageBuffer->m_data.backingStoreSize;
198     float resolutionScale = imageBuffer->m_resolutionScale;
199
200     return createBitmapImageAfterScalingIfNeeded(sinkIntoNativeImage(WTFMove(imageBuffer)), internalSize, logicalSize, backingStoreSize, resolutionScale, preserveResolution);
201 }
202
203 BackingStoreCopy ImageBuffer::fastCopyImageMode()
204 {
205     return DontCopyBackingStore;
206 }
207
208 COMPtr<IWICBitmap> ImageBuffer::sinkIntoNativeImage(std::unique_ptr<ImageBuffer> imageBuffer)
209 {
210     // FIXME: See if we can reuse the on-hardware image.
211     return imageBuffer->copyNativeImage(DontCopyBackingStore);
212 }
213
214 COMPtr<IWICBitmap> ImageBuffer::copyNativeImage(BackingStoreCopy copyBehavior) const
215 {
216     // FIXME: m_data.data is nullptr even when asking to copy backing store leading to test failures.
217     if (copyBehavior == CopyBackingStore && m_data.data.isEmpty())
218         copyBehavior = DontCopyBackingStore;
219
220     Checked<size_t, RecordOverflow> numBytes = Checked<unsigned, RecordOverflow>(m_data.backingStoreSize.height()) * m_data.bytesPerRow;
221     if (numBytes.hasOverflowed())
222         return nullptr;
223
224     HRESULT hr = S_OK;
225     COMPtr<IWICBitmap> image;
226     if (!context().isAcceleratedContext()) {
227         switch (copyBehavior) {
228         case DontCopyBackingStore:
229             hr = ImageDecoderDirect2D::systemImagingFactory()->CreateBitmapFromSource(m_data.bitmapSource.get(), WICBitmapNoCache, &image);
230             break;
231         case CopyBackingStore:
232             hr = ImageDecoderDirect2D::systemImagingFactory()->CreateBitmapFromSource(m_data.bitmapSource.get(), WICBitmapCacheOnDemand, &image);
233             break;
234         default:
235             ASSERT_NOT_REACHED();
236             break;
237         }
238     }
239 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
240     else
241         image = m_data.surface->createImage();
242 #endif
243
244     return image;
245 }
246
247 void ImageBuffer::drawConsuming(std::unique_ptr<ImageBuffer> imageBuffer, GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
248 {
249     imageBuffer->draw(destContext, destRect, srcRect, op, blendMode);
250 }
251
252 void ImageBuffer::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode)
253 {
254     FloatRect adjustedSrcRect = srcRect;
255     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
256
257     FloatSize currentImageSize = nativeImageSize(m_data.bitmapSource);
258
259     // You can't convert a IWICBitmap to a ID2D1Bitmap with an active GraphicsContext attached to it.
260     m_data.context->endDraw();
261
262     destContext.drawNativeImage(m_data.bitmapSource, currentImageSize, destRect, adjustedSrcRect, op, blendMode);
263
264     m_data.context->beginDraw();
265
266     destContext.flush();
267 }
268
269 void ImageBuffer::drawPattern(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
270 {
271     FloatRect adjustedSrcRect = srcRect;
272     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
273
274     if (!context().isAcceleratedContext()) {
275         if (&destContext == &context() || destContext.isAcceleratedContext()) {
276             if (RefPtr<Image> copy = copyImage(CopyBackingStore)) // Drawing into our own buffer, need to deep copy.
277                 copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
278         } else {
279             if (RefPtr<Image> imageForRendering = copyImage(DontCopyBackingStore))
280                 imageForRendering->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
281         }
282     } else {
283         if (RefPtr<Image> copy = copyImage(CopyBackingStore))
284             copy->drawPattern(destContext, destRect, adjustedSrcRect, patternTransform, phase, spacing, op, blendMode);
285     }
286 }
287
288 RefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
289 {
290     if (context().isAcceleratedContext())
291         flushContext();
292
293     IntRect srcRect = rect;
294     if (coordinateSystem == LogicalCoordinateSystem)
295         srcRect.scale(m_resolutionScale);
296
297     if (pixelArrayDimensions)
298         *pixelArrayDimensions = srcRect.size();
299
300     return m_data.getData(AlphaPremultiplication::Unpremultiplied, srcRect, internalSize(), context().isAcceleratedContext(), 1);
301 }
302
303 RefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect, IntSize* pixelArrayDimensions, CoordinateSystem coordinateSystem) const
304 {
305     if (context().isAcceleratedContext())
306         flushContext();
307
308     IntRect srcRect = rect;
309     if (coordinateSystem == LogicalCoordinateSystem)
310         srcRect.scale(m_resolutionScale);
311
312     if (pixelArrayDimensions)
313         *pixelArrayDimensions = srcRect.size();
314
315     return m_data.getData(AlphaPremultiplication::Premultiplied, srcRect, internalSize(), context().isAcceleratedContext(), 1);
316 }
317
318 void ImageBuffer::putByteArray(const Uint8ClampedArray& source, AlphaPremultiplication bufferFormat, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, CoordinateSystem coordinateSystem)
319 {
320     if (context().isAcceleratedContext())
321         flushContext();
322
323     IntRect scaledSourceRect = sourceRect;
324     IntSize scaledSourceSize = sourceSize;
325     if (coordinateSystem == LogicalCoordinateSystem) {
326         scaledSourceRect.scale(m_resolutionScale);
327         scaledSourceSize.scale(m_resolutionScale);
328     }
329
330     m_data.putData(source, bufferFormat, scaledSourceSize, scaledSourceRect, destPoint, internalSize(), context().isAcceleratedContext(), 1);
331 }
332
333 String ImageBuffer::toDataURL(const String&, Optional<double>, PreserveResolution) const
334 {
335     notImplemented();
336     return "data:,"_s;
337 }
338
339 Vector<uint8_t> ImageBuffer::toData(const String& mimeType, Optional<double> quality) const
340 {
341     notImplemented();
342     return { };
343 }
344
345 String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
346 {
347     notImplemented();
348     return "data:,"_s;
349 }
350
351 void ImageBuffer::transformColorSpace(ColorSpace, ColorSpace)
352 {
353     notImplemented();
354 }
355
356 } // namespace WebCore
357
358 #endif