1/*
2 * Copyright (C) 2016 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#pragma once
27
28#include "Color.h"
29#include "IntRect.h"
30#include "IntSize.h"
31#include "NativeImage.h"
32#include "SharedBuffer.h"
33
34namespace WebCore {
35
36#if USE(CAIRO)
37// Due to the pixman 16.16 floating point representation, cairo is not able to handle
38// images whose size is bigger than 32768.
39static const int cairoMaxImageSize = 32768;
40#endif
41
42class ImageBackingStore {
43 WTF_MAKE_FAST_ALLOCATED;
44public:
45 static std::unique_ptr<ImageBackingStore> create(const IntSize& size, bool premultiplyAlpha = true)
46 {
47 return std::unique_ptr<ImageBackingStore>(new ImageBackingStore(size, premultiplyAlpha));
48 }
49
50 static std::unique_ptr<ImageBackingStore> create(const ImageBackingStore& other)
51 {
52 return std::unique_ptr<ImageBackingStore>(new ImageBackingStore(other));
53 }
54
55 NativeImagePtr image() const;
56
57 bool setSize(const IntSize& size)
58 {
59 if (size.isEmpty())
60 return false;
61
62 Vector<char> buffer;
63 size_t bufferSize = size.area().unsafeGet() * sizeof(RGBA32);
64
65 if (!buffer.tryReserveCapacity(bufferSize))
66 return false;
67
68 buffer.grow(bufferSize);
69 m_pixels = SharedBuffer::create(WTFMove(buffer));
70 m_pixelsPtr = reinterpret_cast<RGBA32*>(const_cast<char*>(m_pixels->data()));
71 m_size = size;
72 m_frameRect = IntRect(IntPoint(), m_size);
73 clear();
74 return true;
75 }
76
77 void setFrameRect(const IntRect& frameRect)
78 {
79 ASSERT(!m_size.isEmpty());
80 ASSERT(inBounds(frameRect));
81 m_frameRect = frameRect;
82 }
83
84 const IntSize& size() const { return m_size; }
85 const IntRect& frameRect() const { return m_frameRect; }
86
87 void clear()
88 {
89 memset(m_pixelsPtr, 0, (m_size.area() * sizeof(RGBA32)).unsafeGet());
90 }
91
92 void clearRect(const IntRect& rect)
93 {
94 if (rect.isEmpty() || !inBounds(rect))
95 return;
96
97 size_t rowBytes = rect.width() * sizeof(RGBA32);
98 RGBA32* start = pixelAt(rect.x(), rect.y());
99 for (int i = 0; i < rect.height(); ++i) {
100 memset(start, 0, rowBytes);
101 start += m_size.width();
102 }
103 }
104
105 void fillRect(const IntRect &rect, unsigned r, unsigned g, unsigned b, unsigned a)
106 {
107 if (rect.isEmpty() || !inBounds(rect))
108 return;
109
110 RGBA32* start = pixelAt(rect.x(), rect.y());
111 RGBA32 pixelValue = this->pixelValue(r, g, b, a);
112 for (int i = 0; i < rect.height(); ++i) {
113 for (int j = 0; j < rect.width(); ++j)
114 start[j] = pixelValue;
115 start += m_size.width();
116 }
117 }
118
119 void repeatFirstRow(const IntRect& rect)
120 {
121 if (rect.isEmpty() || !inBounds(rect))
122 return;
123
124 size_t rowBytes = rect.width() * sizeof(RGBA32);
125 RGBA32* src = pixelAt(rect.x(), rect.y());
126 RGBA32* dest = src + m_size.width();
127 for (int i = 1; i < rect.height(); ++i) {
128 memcpy(dest, src, rowBytes);
129 dest += m_size.width();
130 }
131 }
132
133 RGBA32* pixelAt(int x, int y) const
134 {
135 ASSERT(inBounds(IntPoint(x, y)));
136 return m_pixelsPtr + y * m_size.width() + x;
137 }
138
139 void setPixel(RGBA32* dest, unsigned r, unsigned g, unsigned b, unsigned a)
140 {
141 ASSERT(dest);
142 *dest = pixelValue(r, g, b, a);
143 }
144
145 void setPixel(int x, int y, unsigned r, unsigned g, unsigned b, unsigned a)
146 {
147 setPixel(pixelAt(x, y), r, g, b, a);
148 }
149
150 void blendPixel(RGBA32* dest, unsigned r, unsigned g, unsigned b, unsigned a)
151 {
152 if (!a)
153 return;
154
155 if (a >= 255 || !alphaChannel(*dest)) {
156 setPixel(dest, r, g, b, a);
157 return;
158 }
159
160 if (!m_premultiplyAlpha)
161 *dest = makePremultipliedRGBA(redChannel(*dest), greenChannel(*dest), blueChannel(*dest), alphaChannel(*dest), false);
162
163 unsigned d = 255 - a;
164
165 r = fastDivideBy255(r * a + redChannel(*dest) * d);
166 g = fastDivideBy255(g * a + greenChannel(*dest) * d);
167 b = fastDivideBy255(b * a + blueChannel(*dest) * d);
168 a += fastDivideBy255(d * alphaChannel(*dest));
169
170 if (m_premultiplyAlpha)
171 *dest = makeRGBA(r, g, b, a);
172 else
173 *dest = makeUnPremultipliedRGBA(r, g, b, a);
174 }
175
176 static bool isOverSize(const IntSize& size)
177 {
178#if USE(CAIRO)
179 // FIXME: this is a workaround to avoid the cairo image size limit, but we should implement support for
180 // bigger images. See https://bugs.webkit.org/show_bug.cgi?id=177227.
181 //
182 // If the image is bigger than the cairo limit it can't be displayed, so we don't even try to decode it.
183 if (size.width() > cairoMaxImageSize || size.height() > cairoMaxImageSize)
184 return true;
185#endif
186 static unsigned long long MaxPixels = ((1 << 29) - 1);
187 unsigned long long pixels = static_cast<unsigned long long>(size.width()) * static_cast<unsigned long long>(size.height());
188 return pixels > MaxPixels;
189 }
190
191private:
192 ImageBackingStore(const IntSize& size, bool premultiplyAlpha = true)
193 : m_premultiplyAlpha(premultiplyAlpha)
194 {
195 ASSERT(!size.isEmpty() && !isOverSize(size));
196 setSize(size);
197 }
198
199 ImageBackingStore(const ImageBackingStore& other)
200 : m_size(other.m_size)
201 , m_premultiplyAlpha(other.m_premultiplyAlpha)
202 {
203 ASSERT(!m_size.isEmpty() && !isOverSize(m_size));
204 m_pixels = SharedBuffer::create(other.m_pixels->data(), other.m_pixels->size());
205 m_pixelsPtr = reinterpret_cast<RGBA32*>(const_cast<char*>(m_pixels->data()));
206 }
207
208 bool inBounds(const IntPoint& point) const
209 {
210 return IntRect(IntPoint(), m_size).contains(point);
211 }
212
213 bool inBounds(const IntRect& rect) const
214 {
215 return IntRect(IntPoint(), m_size).contains(rect);
216 }
217
218 RGBA32 pixelValue(unsigned r, unsigned g, unsigned b, unsigned a) const
219 {
220 if (m_premultiplyAlpha && !a)
221 return 0;
222
223 if (m_premultiplyAlpha && a < 255)
224 return makePremultipliedRGBA(r, g, b, a, false);
225
226 return makeRGBA(r, g, b, a);
227 }
228
229 RefPtr<SharedBuffer> m_pixels;
230 RGBA32* m_pixelsPtr { nullptr };
231 IntSize m_size;
232 IntRect m_frameRect; // This will always just be the entire buffer except for GIF and PNG frames
233 bool m_premultiplyAlpha { true };
234};
235
236}
237