1/*
2 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2012 Igalia S.L.
4 Copyright (C) 2012 Adobe Systems Incorporated
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "BitmapTextureGL.h"
24
25#if USE(TEXTURE_MAPPER_GL)
26
27#include "Extensions3D.h"
28#include "FilterOperations.h"
29#include "Image.h"
30#include "LengthFunctions.h"
31#include "NotImplemented.h"
32#include "TextureMapperShaderProgram.h"
33#include "Timer.h"
34#include <wtf/HashMap.h>
35#include <wtf/RefCounted.h>
36#include <wtf/RefPtr.h>
37
38#if USE(CAIRO)
39#include "CairoUtilities.h"
40#include "RefPtrCairo.h"
41#include <cairo.h>
42#include <wtf/text/CString.h>
43#endif
44
45#if OS(DARWIN)
46#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
47#endif
48
49namespace WebCore {
50
51BitmapTextureGL* toBitmapTextureGL(BitmapTexture* texture)
52{
53 if (!texture || !texture->isBackedByOpenGL())
54 return 0;
55
56 return static_cast<BitmapTextureGL*>(texture);
57}
58
59BitmapTextureGL::BitmapTextureGL(const TextureMapperContextAttributes& contextAttributes, const Flags, GLint internalFormat)
60 : m_contextAttributes(contextAttributes)
61{
62 if (internalFormat != GL_DONT_CARE) {
63 m_internalFormat = m_format = internalFormat;
64 return;
65 }
66
67 m_internalFormat = m_format = GL_RGBA;
68}
69
70void BitmapTextureGL::didReset()
71{
72 if (!m_id)
73 glGenTextures(1, &m_id);
74
75 m_shouldClear = true;
76 m_colorConvertFlags = TextureMapperGL::NoFlag;
77 if (m_textureSize == contentSize())
78 return;
79
80 m_textureSize = contentSize();
81 glBindTexture(GL_TEXTURE_2D, m_id);
82 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
83 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
84 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
85 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
86
87 glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_textureSize.width(), m_textureSize.height(), 0, m_format, m_type, 0);
88}
89
90void BitmapTextureGL::updateContents(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine)
91{
92 // We are updating a texture with format RGBA with content from a buffer that has BGRA format. Instead of turning BGRA
93 // into RGBA and then uploading it, we upload it as is. This causes the texture format to be RGBA but the content to be BGRA,
94 // so we mark the texture to convert the colors when painting the texture.
95 m_colorConvertFlags = TextureMapperGL::ShouldConvertTextureBGRAToRGBA;
96
97 glBindTexture(GL_TEXTURE_2D, m_id);
98
99 const unsigned bytesPerPixel = 4;
100 const char* data = static_cast<const char*>(srcData);
101 Vector<char> temporaryData;
102 IntPoint adjustedSourceOffset = sourceOffset;
103
104 // Texture upload requires subimage buffer if driver doesn't support subimage and we don't have full image upload.
105 bool requireSubImageBuffer = !m_contextAttributes.supportsUnpackSubimage
106 && !(bytesPerLine == static_cast<int>(targetRect.width() * bytesPerPixel) && adjustedSourceOffset == IntPoint::zero());
107
108 // prepare temporaryData if necessary
109 if (requireSubImageBuffer) {
110 temporaryData.resize(targetRect.width() * targetRect.height() * bytesPerPixel);
111 char* dst = temporaryData.data();
112 data = dst;
113 const char* bits = static_cast<const char*>(srcData);
114 const char* src = bits + sourceOffset.y() * bytesPerLine + sourceOffset.x() * bytesPerPixel;
115 const int targetBytesPerLine = targetRect.width() * bytesPerPixel;
116 for (int y = 0; y < targetRect.height(); ++y) {
117 memcpy(dst, src, targetBytesPerLine);
118 src += bytesPerLine;
119 dst += targetBytesPerLine;
120 }
121
122 bytesPerLine = targetBytesPerLine;
123 adjustedSourceOffset = IntPoint(0, 0);
124 }
125
126 glBindTexture(GL_TEXTURE_2D, m_id);
127
128 if (m_contextAttributes.supportsUnpackSubimage) {
129 // Use the OpenGL sub-image extension, now that we know it's available.
130 glPixelStorei(GL_UNPACK_ROW_LENGTH, bytesPerLine / bytesPerPixel);
131 glPixelStorei(GL_UNPACK_SKIP_ROWS, adjustedSourceOffset.y());
132 glPixelStorei(GL_UNPACK_SKIP_PIXELS, adjustedSourceOffset.x());
133 }
134
135 glTexSubImage2D(GL_TEXTURE_2D, 0, targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(), m_format, m_type, data);
136
137 if (m_contextAttributes.supportsUnpackSubimage) {
138 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
139 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
140 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
141 }
142}
143
144void BitmapTextureGL::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset)
145{
146 if (!image)
147 return;
148 NativeImagePtr frameImage = image->nativeImageForCurrentFrame();
149 if (!frameImage)
150 return;
151
152 int bytesPerLine;
153 const char* imageData;
154
155#if USE(CAIRO)
156 cairo_surface_t* surface = frameImage.get();
157 imageData = reinterpret_cast<const char*>(cairo_image_surface_get_data(surface));
158 bytesPerLine = cairo_image_surface_get_stride(surface);
159#endif
160
161 updateContents(imageData, targetRect, offset, bytesPerLine);
162}
163
164static unsigned getPassesRequiredForFilter(FilterOperation::OperationType type)
165{
166 switch (type) {
167 case FilterOperation::GRAYSCALE:
168 case FilterOperation::SEPIA:
169 case FilterOperation::SATURATE:
170 case FilterOperation::HUE_ROTATE:
171 case FilterOperation::INVERT:
172 case FilterOperation::BRIGHTNESS:
173 case FilterOperation::CONTRAST:
174 case FilterOperation::OPACITY:
175 return 1;
176 case FilterOperation::BLUR:
177 case FilterOperation::DROP_SHADOW:
178 // We use two-passes (vertical+horizontal) for blur and drop-shadow.
179 return 2;
180 default:
181 return 0;
182 }
183}
184
185RefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper& textureMapper, const FilterOperations& filters)
186{
187 if (filters.isEmpty())
188 return this;
189
190 TextureMapperGL& texmapGL = static_cast<TextureMapperGL&>(textureMapper);
191 RefPtr<BitmapTexture> previousSurface = texmapGL.currentSurface();
192 RefPtr<BitmapTexture> resultSurface = this;
193 RefPtr<BitmapTexture> intermediateSurface;
194 RefPtr<BitmapTexture> spareSurface;
195
196 m_filterInfo = FilterInfo();
197
198 for (size_t i = 0; i < filters.size(); ++i) {
199 RefPtr<FilterOperation> filter = filters.operations()[i];
200 ASSERT(filter);
201
202 int numPasses = getPassesRequiredForFilter(filter->type());
203 for (int j = 0; j < numPasses; ++j) {
204 bool last = (i == filters.size() - 1) && (j == numPasses - 1);
205 if (!last) {
206 if (!intermediateSurface)
207 intermediateSurface = texmapGL.acquireTextureFromPool(contentSize(), BitmapTexture::SupportsAlpha);
208 texmapGL.bindSurface(intermediateSurface.get());
209 }
210
211 if (last) {
212 toBitmapTextureGL(resultSurface.get())->m_filterInfo = BitmapTextureGL::FilterInfo(filter.copyRef(), j, spareSurface.copyRef());
213 break;
214 }
215
216 texmapGL.drawFiltered(*resultSurface.get(), spareSurface.get(), *filter, j);
217 if (!j && filter->type() == FilterOperation::DROP_SHADOW) {
218 spareSurface = resultSurface;
219 resultSurface = nullptr;
220 }
221 std::swap(resultSurface, intermediateSurface);
222 }
223 }
224
225 texmapGL.bindSurface(previousSurface.get());
226 return resultSurface;
227}
228
229void BitmapTextureGL::initializeStencil()
230{
231 if (m_rbo)
232 return;
233
234 glGenRenderbuffers(1, &m_rbo);
235 glBindRenderbuffer(GL_RENDERBUFFER, m_rbo);
236 glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, m_textureSize.width(), m_textureSize.height());
237 glBindRenderbuffer(GL_RENDERBUFFER, 0);
238 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_rbo);
239 glClearStencil(0);
240 glClear(GL_STENCIL_BUFFER_BIT);
241}
242
243void BitmapTextureGL::initializeDepthBuffer()
244{
245 if (m_depthBufferObject)
246 return;
247
248 glGenRenderbuffers(1, &m_depthBufferObject);
249 glBindRenderbuffer(GL_RENDERBUFFER, m_depthBufferObject);
250 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, m_textureSize.width(), m_textureSize.height());
251 glBindRenderbuffer(GL_RENDERBUFFER, 0);
252 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBufferObject);
253}
254
255void BitmapTextureGL::clearIfNeeded()
256{
257 if (!m_shouldClear)
258 return;
259
260 m_clipStack.reset(IntRect(IntPoint::zero(), m_textureSize), ClipStack::YAxisMode::Default);
261 m_clipStack.applyIfNeeded();
262 glClearColor(0, 0, 0, 0);
263 glClear(GL_COLOR_BUFFER_BIT);
264 m_shouldClear = false;
265}
266
267void BitmapTextureGL::createFboIfNeeded()
268{
269 if (m_fbo)
270 return;
271
272 glGenFramebuffers(1, &m_fbo);
273 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
274 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id(), 0);
275 m_shouldClear = true;
276}
277
278void BitmapTextureGL::bindAsSurface()
279{
280 glBindTexture(GL_TEXTURE_2D, 0);
281 createFboIfNeeded();
282 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
283 glViewport(0, 0, m_textureSize.width(), m_textureSize.height());
284 clearIfNeeded();
285 m_clipStack.apply();
286}
287
288BitmapTextureGL::~BitmapTextureGL()
289{
290 if (m_id)
291 glDeleteTextures(1, &m_id);
292
293 if (m_fbo)
294 glDeleteFramebuffers(1, &m_fbo);
295
296 if (m_rbo)
297 glDeleteRenderbuffers(1, &m_rbo);
298
299 if (m_depthBufferObject)
300 glDeleteRenderbuffers(1, &m_depthBufferObject);
301}
302
303bool BitmapTextureGL::isValid() const
304{
305 return m_id;
306}
307
308IntSize BitmapTextureGL::size() const
309{
310 return m_textureSize;
311}
312
313
314void BitmapTextureGL::copyFromExternalTexture(GLuint sourceTextureID)
315{
316 GLint boundTexture = 0;
317 GLint boundFramebuffer = 0;
318 GLint boundActiveTexture = 0;
319
320 glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture);
321 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundFramebuffer);
322 glGetIntegerv(GL_ACTIVE_TEXTURE, &boundActiveTexture);
323
324 glBindTexture(GL_TEXTURE_2D, sourceTextureID);
325
326 GLuint copyFbo = 0;
327 glGenFramebuffers(1, &copyFbo);
328 glBindFramebuffer(GL_FRAMEBUFFER, copyFbo);
329 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTextureID, 0);
330
331 glActiveTexture(GL_TEXTURE0);
332 glBindTexture(GL_TEXTURE_2D, id());
333 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_textureSize.width(), m_textureSize.height());
334
335 glBindTexture(GL_TEXTURE_2D, boundTexture);
336 glBindFramebuffer(GL_FRAMEBUFFER, boundFramebuffer);
337 glBindTexture(GL_TEXTURE_2D, boundTexture);
338 glActiveTexture(boundActiveTexture);
339 glDeleteFramebuffers(1, &copyFbo);
340}
341
342} // namespace WebCore
343
344#endif // USE(TEXTURE_MAPPER_GL)
345