1/*
2 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
3 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
4 * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "ImageBuffer.h"
30
31#include "ColorUtilities.h"
32#include "GraphicsContext.h"
33#include "IntRect.h"
34#include <wtf/MathExtras.h>
35
36namespace WebCore {
37
38static const float MaxClampedLength = 4096;
39static const float MaxClampedArea = MaxClampedLength * MaxClampedLength;
40
41std::unique_ptr<ImageBuffer> ImageBuffer::create(const FloatSize& size, RenderingMode renderingMode, float resolutionScale, ColorSpace colorSpace, const HostWindow* hostWindow)
42{
43 bool success = false;
44 std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, hostWindow, success));
45 if (!success)
46 return nullptr;
47 return buffer;
48}
49
50#if USE(DIRECT2D)
51std::unique_ptr<ImageBuffer> ImageBuffer::create(const FloatSize& size, RenderingMode renderingMode, const GraphicsContext* targetContext, float resolutionScale, ColorSpace colorSpace, const HostWindow* hostWindow)
52{
53 bool success = false;
54 std::unique_ptr<ImageBuffer> buffer(new ImageBuffer(size, resolutionScale, colorSpace, renderingMode, hostWindow, targetContext, success));
55 if (!success)
56 return nullptr;
57 return buffer;
58}
59#endif
60
61bool ImageBuffer::sizeNeedsClamping(const FloatSize& size)
62{
63 if (size.isEmpty())
64 return false;
65
66 return floorf(size.height()) * floorf(size.width()) > MaxClampedArea;
67}
68
69bool ImageBuffer::sizeNeedsClamping(const FloatSize& size, FloatSize& scale)
70{
71 FloatSize scaledSize(size);
72 scaledSize.scale(scale.width(), scale.height());
73
74 if (!sizeNeedsClamping(scaledSize))
75 return false;
76
77 // The area of scaled size is bigger than the upper limit, adjust the scale to fit.
78 scale.scale(sqrtf(MaxClampedArea / (scaledSize.width() * scaledSize.height())));
79 ASSERT(!sizeNeedsClamping(size, scale));
80 return true;
81}
82
83FloatSize ImageBuffer::clampedSize(const FloatSize& size)
84{
85 return size.shrunkTo(FloatSize(MaxClampedLength, MaxClampedLength));
86}
87
88FloatSize ImageBuffer::clampedSize(const FloatSize& size, FloatSize& scale)
89{
90 if (size.isEmpty())
91 return size;
92
93 FloatSize clampedSize = ImageBuffer::clampedSize(size);
94 scale = clampedSize / size;
95 ASSERT(!sizeNeedsClamping(clampedSize));
96 ASSERT(!sizeNeedsClamping(size, scale));
97 return clampedSize;
98}
99
100FloatRect ImageBuffer::clampedRect(const FloatRect& rect)
101{
102 return FloatRect(rect.location(), clampedSize(rect.size()));
103}
104
105#if !USE(CG) && !USE(CAIRO)
106Vector<uint8_t> ImageBuffer::toBGRAData() const
107{
108 // FIXME: Implement this for other backends.
109 return { };
110}
111#endif
112
113#if !(USE(CG) || USE(DIRECT2D))
114
115FloatSize ImageBuffer::sizeForDestinationSize(FloatSize size) const
116{
117 return size;
118}
119
120void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
121{
122 if (srcColorSpace == dstColorSpace)
123 return;
124
125 // only sRGB <-> linearRGB are supported at the moment
126 if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceSRGB)
127 || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceSRGB))
128 return;
129
130 if (dstColorSpace == ColorSpaceLinearRGB) {
131 static const std::array<uint8_t, 256> linearRgbLUT = [] {
132 std::array<uint8_t, 256> array;
133 for (unsigned i = 0; i < 256; i++) {
134 float color = i / 255.0f;
135 color = sRGBToLinearColorComponent(color);
136 array[i] = static_cast<uint8_t>(round(color * 255));
137 }
138 return array;
139 }();
140 platformTransformColorSpace(linearRgbLUT);
141 } else if (dstColorSpace == ColorSpaceSRGB) {
142 static const std::array<uint8_t, 256> deviceRgbLUT= [] {
143 std::array<uint8_t, 256> array;
144 for (unsigned i = 0; i < 256; i++) {
145 float color = i / 255.0f;
146 color = linearToSRGBColorComponent(color);
147 array[i] = static_cast<uint8_t>(round(color * 255));
148 }
149 return array;
150 }();
151 platformTransformColorSpace(deviceRgbLUT);
152 }
153}
154
155#endif // USE(CG)
156
157inline void ImageBuffer::genericConvertToLuminanceMask()
158{
159 IntRect luminanceRect(IntPoint(), internalSize());
160 auto srcPixelArray = getUnmultipliedImageData(luminanceRect);
161 if (!srcPixelArray)
162 return;
163
164 unsigned pixelArrayLength = srcPixelArray->length();
165 for (unsigned pixelOffset = 0; pixelOffset < pixelArrayLength; pixelOffset += 4) {
166 uint8_t a = srcPixelArray->item(pixelOffset + 3);
167 if (!a)
168 continue;
169 uint8_t r = srcPixelArray->item(pixelOffset);
170 uint8_t g = srcPixelArray->item(pixelOffset + 1);
171 uint8_t b = srcPixelArray->item(pixelOffset + 2);
172
173 double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0);
174 srcPixelArray->set(pixelOffset + 3, luma);
175 }
176 putByteArray(*srcPixelArray, AlphaPremultiplication::Unpremultiplied, luminanceRect.size(), luminanceRect, IntPoint());
177}
178
179void ImageBuffer::convertToLuminanceMask()
180{
181 // Add platform specific functions with platformConvertToLuminanceMask here later.
182 genericConvertToLuminanceMask();
183}
184
185#if !USE(CAIRO)
186PlatformLayer* ImageBuffer::platformLayer() const
187{
188 return 0;
189}
190
191bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D&, GC3Denum, Platform3DObject, GC3Denum, bool, bool)
192{
193 return false;
194}
195#endif
196
197std::unique_ptr<ImageBuffer> ImageBuffer::copyRectToBuffer(const FloatRect& rect, ColorSpace colorSpace, const GraphicsContext& context)
198{
199 if (rect.isEmpty())
200 return nullptr;
201
202 IntSize scaledSize = ImageBuffer::compatibleBufferSize(rect.size(), context);
203
204 auto buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, colorSpace, context);
205 if (!buffer)
206 return nullptr;
207
208 buffer->context().drawImageBuffer(*this, -rect.location());
209 return buffer;
210}
211
212std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, ColorSpace colorSpace, const GraphicsContext& context)
213{
214 if (size.isEmpty())
215 return nullptr;
216
217 IntSize scaledSize = ImageBuffer::compatibleBufferSize(size, context);
218
219 auto buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, colorSpace, context);
220 if (!buffer)
221 return nullptr;
222
223 // Set up a corresponding scale factor on the graphics context.
224 buffer->context().scale(scaledSize / size);
225 return buffer;
226}
227
228std::unique_ptr<ImageBuffer> ImageBuffer::createCompatibleBuffer(const FloatSize& size, float resolutionScale, ColorSpace colorSpace, const GraphicsContext& context)
229{
230#if USE(DIRECT2D)
231 return create(size, context.renderingMode(), &context, resolutionScale, colorSpace);
232#else
233 return create(size, context.renderingMode(), resolutionScale, colorSpace);
234#endif
235}
236
237IntSize ImageBuffer::compatibleBufferSize(const FloatSize& size, const GraphicsContext& context)
238{
239 // Enlarge the buffer size if the context's transform is scaling it so we need a higher
240 // resolution than one pixel per unit.
241 return expandedIntSize(size * context.scaleFactor());
242}
243
244bool ImageBuffer::isCompatibleWithContext(const GraphicsContext& context) const
245{
246 return areEssentiallyEqual(context.scaleFactor(), this->context().scaleFactor());
247}
248
249#if !USE(IOSURFACE_CANVAS_BACKING_STORE)
250size_t ImageBuffer::memoryCost() const
251{
252 // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how.
253 // It's safe to access internalSize() because it doesn't do any pointer chasing.
254 return 4 * internalSize().width() * internalSize().height();
255}
256
257size_t ImageBuffer::externalMemoryCost() const
258{
259 return 0;
260}
261#endif
262
263}
264