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 "TextureMapperGL.h" |
24 | |
25 | #if USE(TEXTURE_MAPPER_GL) |
26 | |
27 | #include "BitmapTextureGL.h" |
28 | #include "BitmapTexturePool.h" |
29 | #include "Extensions3D.h" |
30 | #include "FilterOperations.h" |
31 | #include "FloatQuad.h" |
32 | #include "GLContext.h" |
33 | #include "GraphicsContext.h" |
34 | #include "Image.h" |
35 | #include "LengthFunctions.h" |
36 | #include "NotImplemented.h" |
37 | #include "TextureMapperShaderProgram.h" |
38 | #include "Timer.h" |
39 | #include <wtf/HashMap.h> |
40 | #include <wtf/NeverDestroyed.h> |
41 | #include <wtf/Ref.h> |
42 | #include <wtf/RefCounted.h> |
43 | #include <wtf/SetForScope.h> |
44 | |
45 | #if USE(CAIRO) |
46 | #include "CairoUtilities.h" |
47 | #include "RefPtrCairo.h" |
48 | #include <cairo.h> |
49 | #include <wtf/text/CString.h> |
50 | #endif |
51 | |
52 | namespace WebCore { |
53 | |
54 | class TextureMapperGLData { |
55 | WTF_MAKE_FAST_ALLOCATED; |
56 | public: |
57 | explicit TextureMapperGLData(void*); |
58 | ~TextureMapperGLData(); |
59 | |
60 | void initializeStencil(); |
61 | GLuint getStaticVBO(GLenum target, GLsizeiptr, const void* data); |
62 | GLuint getVAO(); |
63 | Ref<TextureMapperShaderProgram> getShaderProgram(TextureMapperShaderProgram::Options); |
64 | |
65 | TransformationMatrix projectionMatrix; |
66 | TextureMapper::PaintFlags PaintFlags { 0 }; |
67 | GLint previousProgram { 0 }; |
68 | GLint previousVAO { 0 }; |
69 | GLint targetFrameBuffer { 0 }; |
70 | bool didModifyStencil { false }; |
71 | GLint previousScissorState { 0 }; |
72 | GLint previousDepthState { 0 }; |
73 | GLint viewport[4] { 0, }; |
74 | GLint previousScissor[4] { 0, }; |
75 | RefPtr<BitmapTexture> currentSurface; |
76 | const BitmapTextureGL::FilterInfo* filterInfo { nullptr }; |
77 | |
78 | private: |
79 | class SharedGLData : public RefCounted<SharedGLData> { |
80 | public: |
81 | static Ref<SharedGLData> currentSharedGLData(void* platformContext) |
82 | { |
83 | auto it = contextDataMap().find(platformContext); |
84 | if (it != contextDataMap().end()) |
85 | return *it->value; |
86 | |
87 | Ref<SharedGLData> data = adoptRef(*new SharedGLData); |
88 | contextDataMap().add(platformContext, data.ptr()); |
89 | return data; |
90 | } |
91 | |
92 | ~SharedGLData() |
93 | { |
94 | ASSERT(std::any_of(contextDataMap().begin(), contextDataMap().end(), |
95 | [this](auto& entry) { return entry.value == this; })); |
96 | contextDataMap().removeIf([this](auto& entry) { return entry.value == this; }); |
97 | } |
98 | |
99 | private: |
100 | friend class TextureMapperGLData; |
101 | |
102 | using GLContextDataMap = HashMap<void*, SharedGLData*>; |
103 | static GLContextDataMap& contextDataMap() |
104 | { |
105 | static NeverDestroyed<GLContextDataMap> map; |
106 | return map; |
107 | } |
108 | |
109 | SharedGLData() = default; |
110 | |
111 | HashMap<TextureMapperShaderProgram::Options, RefPtr<TextureMapperShaderProgram>> m_programs; |
112 | }; |
113 | |
114 | Ref<SharedGLData> m_sharedGLData; |
115 | HashMap<const void*, GLuint> m_vbos; |
116 | GLuint m_vao { 0 }; |
117 | }; |
118 | |
119 | TextureMapperGLData::TextureMapperGLData(void* platformContext) |
120 | : m_sharedGLData(SharedGLData::currentSharedGLData(platformContext)) |
121 | { |
122 | } |
123 | |
124 | TextureMapperGLData::~TextureMapperGLData() |
125 | { |
126 | for (auto& entry : m_vbos) |
127 | glDeleteBuffers(1, &entry.value); |
128 | |
129 | #if !USE(OPENGL_ES) |
130 | if (GLContext::current()->version() >= 320 && m_vao) |
131 | glDeleteVertexArrays(1, &m_vao); |
132 | #endif |
133 | } |
134 | |
135 | void TextureMapperGLData::initializeStencil() |
136 | { |
137 | if (currentSurface) { |
138 | static_cast<BitmapTextureGL*>(currentSurface.get())->initializeStencil(); |
139 | return; |
140 | } |
141 | |
142 | if (didModifyStencil) |
143 | return; |
144 | |
145 | glClearStencil(0); |
146 | glClear(GL_STENCIL_BUFFER_BIT); |
147 | didModifyStencil = true; |
148 | } |
149 | |
150 | GLuint TextureMapperGLData::getStaticVBO(GLenum target, GLsizeiptr size, const void* data) |
151 | { |
152 | auto addResult = m_vbos.ensure(data, |
153 | [this, target, size, data] { |
154 | GLuint vbo = 0; |
155 | glGenBuffers(1, &vbo); |
156 | glBindBuffer(target, vbo); |
157 | glBufferData(target, size, data, GL_STATIC_DRAW); |
158 | return vbo; |
159 | }); |
160 | return addResult.iterator->value; |
161 | } |
162 | |
163 | GLuint TextureMapperGLData::getVAO() |
164 | { |
165 | #if !USE(OPENGL_ES) |
166 | if (GLContext::current()->version() >= 320 && !m_vao) |
167 | glGenVertexArrays(1, &m_vao); |
168 | #endif |
169 | |
170 | return m_vao; |
171 | } |
172 | |
173 | Ref<TextureMapperShaderProgram> TextureMapperGLData::getShaderProgram(TextureMapperShaderProgram::Options options) |
174 | { |
175 | auto addResult = m_sharedGLData->m_programs.ensure(options, |
176 | [options] { return TextureMapperShaderProgram::create(options); }); |
177 | return *addResult.iterator->value; |
178 | } |
179 | |
180 | TextureMapperGL::TextureMapperGL() |
181 | : m_contextAttributes(TextureMapperContextAttributes::get()) |
182 | , m_enableEdgeDistanceAntialiasing(false) |
183 | { |
184 | void* platformContext = GLContext::current()->platformContext(); |
185 | ASSERT(platformContext); |
186 | |
187 | m_data = new TextureMapperGLData(platformContext); |
188 | #if USE(TEXTURE_MAPPER_GL) |
189 | m_texturePool = std::make_unique<BitmapTexturePool>(m_contextAttributes); |
190 | #endif |
191 | } |
192 | |
193 | ClipStack& TextureMapperGL::clipStack() |
194 | { |
195 | return data().currentSurface ? toBitmapTextureGL(data().currentSurface.get())->clipStack() : m_clipStack; |
196 | } |
197 | |
198 | void TextureMapperGL::beginPainting(PaintFlags flags) |
199 | { |
200 | glGetIntegerv(GL_CURRENT_PROGRAM, &data().previousProgram); |
201 | data().previousScissorState = glIsEnabled(GL_SCISSOR_TEST); |
202 | data().previousDepthState = glIsEnabled(GL_DEPTH_TEST); |
203 | glDisable(GL_DEPTH_TEST); |
204 | glEnable(GL_SCISSOR_TEST); |
205 | data().didModifyStencil = false; |
206 | glDepthMask(0); |
207 | glGetIntegerv(GL_VIEWPORT, data().viewport); |
208 | glGetIntegerv(GL_SCISSOR_BOX, data().previousScissor); |
209 | m_clipStack.reset(IntRect(0, 0, data().viewport[2], data().viewport[3]), flags & PaintingMirrored ? ClipStack::YAxisMode::Default : ClipStack::YAxisMode::Inverted); |
210 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, &data().targetFrameBuffer); |
211 | data().PaintFlags = flags; |
212 | bindSurface(0); |
213 | |
214 | #if !USE(OPENGL_ES) |
215 | if (GLContext::current()->version() >= 320) { |
216 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &data().previousVAO); |
217 | glBindVertexArray(data().getVAO()); |
218 | } |
219 | #endif |
220 | } |
221 | |
222 | void TextureMapperGL::endPainting() |
223 | { |
224 | if (data().didModifyStencil) { |
225 | glClearStencil(1); |
226 | glClear(GL_STENCIL_BUFFER_BIT); |
227 | } |
228 | |
229 | glUseProgram(data().previousProgram); |
230 | |
231 | glScissor(data().previousScissor[0], data().previousScissor[1], data().previousScissor[2], data().previousScissor[3]); |
232 | if (data().previousScissorState) |
233 | glEnable(GL_SCISSOR_TEST); |
234 | else |
235 | glDisable(GL_SCISSOR_TEST); |
236 | |
237 | if (data().previousDepthState) |
238 | glEnable(GL_DEPTH_TEST); |
239 | else |
240 | glDisable(GL_DEPTH_TEST); |
241 | |
242 | #if !USE(OPENGL_ES) |
243 | if (GLContext::current()->version() >= 320) |
244 | glBindVertexArray(data().previousVAO); |
245 | #endif |
246 | } |
247 | |
248 | void TextureMapperGL::drawBorder(const Color& color, float width, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix) |
249 | { |
250 | if (clipStack().isCurrentScissorBoxEmpty()) |
251 | return; |
252 | |
253 | Ref<TextureMapperShaderProgram> program = data().getShaderProgram(TextureMapperShaderProgram::SolidColor); |
254 | glUseProgram(program->programID()); |
255 | |
256 | float r, g, b, a; |
257 | Color(premultipliedARGBFromColor(color)).getRGBA(r, g, b, a); |
258 | glUniform4f(program->colorLocation(), r, g, b, a); |
259 | glLineWidth(width); |
260 | |
261 | draw(targetRect, modelViewMatrix, program.get(), GL_LINE_LOOP, !color.isOpaque() ? ShouldBlend : 0); |
262 | } |
263 | |
264 | // FIXME: drawNumber() should save a number texture-atlas and re-use whenever possible. |
265 | void TextureMapperGL::drawNumber(int number, const Color& color, const FloatPoint& targetPoint, const TransformationMatrix& modelViewMatrix) |
266 | { |
267 | int pointSize = 8; |
268 | |
269 | #if USE(CAIRO) |
270 | CString counterString = String::number(number).ascii(); |
271 | // cairo_text_extents() requires a cairo_t, so dimensions need to be guesstimated. |
272 | int width = counterString.length() * pointSize * 1.2; |
273 | int height = pointSize * 1.5; |
274 | |
275 | cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); |
276 | cairo_t* cr = cairo_create(surface); |
277 | |
278 | // Since we won't swap R+B when uploading a texture, paint with the swapped R+B color. |
279 | if (color.isExtended()) |
280 | cairo_set_source_rgba(cr, color.asExtended().blue(), color.asExtended().green(), color.asExtended().red(), color.asExtended().alpha()); |
281 | else { |
282 | float r, g, b, a; |
283 | color.getRGBA(r, g, b, a); |
284 | cairo_set_source_rgba(cr, b, g, r, a); |
285 | } |
286 | |
287 | cairo_rectangle(cr, 0, 0, width, height); |
288 | cairo_fill(cr); |
289 | |
290 | cairo_select_font_face(cr, "Monospace" , CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); |
291 | cairo_set_font_size(cr, pointSize); |
292 | cairo_set_source_rgb(cr, 1, 1, 1); |
293 | cairo_move_to(cr, 2, pointSize); |
294 | cairo_show_text(cr, counterString.data()); |
295 | |
296 | IntSize size(width, height); |
297 | IntRect sourceRect(IntPoint::zero(), size); |
298 | IntRect targetRect(roundedIntPoint(targetPoint), size); |
299 | |
300 | RefPtr<BitmapTexture> texture = acquireTextureFromPool(size); |
301 | const unsigned char* bits = cairo_image_surface_get_data(surface); |
302 | int stride = cairo_image_surface_get_stride(surface); |
303 | static_cast<BitmapTextureGL*>(texture.get())->updateContents(bits, sourceRect, IntPoint::zero(), stride); |
304 | drawTexture(*texture, targetRect, modelViewMatrix, 1.0f, AllEdges); |
305 | |
306 | cairo_surface_destroy(surface); |
307 | cairo_destroy(cr); |
308 | |
309 | #else |
310 | UNUSED_PARAM(number); |
311 | UNUSED_PARAM(pointSize); |
312 | UNUSED_PARAM(targetPoint); |
313 | UNUSED_PARAM(modelViewMatrix); |
314 | notImplemented(); |
315 | #endif |
316 | } |
317 | |
318 | static TextureMapperShaderProgram::Options optionsForFilterType(FilterOperation::OperationType type, unsigned pass) |
319 | { |
320 | switch (type) { |
321 | case FilterOperation::GRAYSCALE: |
322 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::GrayscaleFilter; |
323 | case FilterOperation::SEPIA: |
324 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::SepiaFilter; |
325 | case FilterOperation::SATURATE: |
326 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::SaturateFilter; |
327 | case FilterOperation::HUE_ROTATE: |
328 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::HueRotateFilter; |
329 | case FilterOperation::INVERT: |
330 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::InvertFilter; |
331 | case FilterOperation::BRIGHTNESS: |
332 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::BrightnessFilter; |
333 | case FilterOperation::CONTRAST: |
334 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::ContrastFilter; |
335 | case FilterOperation::OPACITY: |
336 | return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::OpacityFilter; |
337 | case FilterOperation::BLUR: |
338 | return TextureMapperShaderProgram::BlurFilter; |
339 | case FilterOperation::DROP_SHADOW: |
340 | return TextureMapperShaderProgram::AlphaBlur |
341 | | (pass ? TextureMapperShaderProgram::ContentTexture | TextureMapperShaderProgram::SolidColor: 0); |
342 | default: |
343 | ASSERT_NOT_REACHED(); |
344 | return 0; |
345 | } |
346 | } |
347 | |
348 | // Create a normal distribution of 21 values between -2 and 2. |
349 | static const unsigned GaussianKernelHalfWidth = 11; |
350 | static const float GaussianKernelStep = 0.2; |
351 | |
352 | static inline float gauss(float x) |
353 | { |
354 | return exp(-(x * x) / 2.); |
355 | } |
356 | |
357 | static float* gaussianKernel() |
358 | { |
359 | static bool prepared = false; |
360 | static float kernel[GaussianKernelHalfWidth] = {0, }; |
361 | |
362 | if (prepared) |
363 | return kernel; |
364 | |
365 | kernel[0] = gauss(0); |
366 | float sum = kernel[0]; |
367 | for (unsigned i = 1; i < GaussianKernelHalfWidth; ++i) { |
368 | kernel[i] = gauss(i * GaussianKernelStep); |
369 | sum += 2 * kernel[i]; |
370 | } |
371 | |
372 | // Normalize the kernel. |
373 | float scale = 1 / sum; |
374 | for (unsigned i = 0; i < GaussianKernelHalfWidth; ++i) |
375 | kernel[i] *= scale; |
376 | |
377 | prepared = true; |
378 | return kernel; |
379 | } |
380 | |
381 | static void prepareFilterProgram(TextureMapperShaderProgram& program, const FilterOperation& operation, unsigned pass, const IntSize& size, GLuint contentTexture) |
382 | { |
383 | glUseProgram(program.programID()); |
384 | |
385 | switch (operation.type()) { |
386 | case FilterOperation::GRAYSCALE: |
387 | case FilterOperation::SEPIA: |
388 | case FilterOperation::SATURATE: |
389 | case FilterOperation::HUE_ROTATE: |
390 | glUniform1f(program.filterAmountLocation(), static_cast<const BasicColorMatrixFilterOperation&>(operation).amount()); |
391 | break; |
392 | case FilterOperation::INVERT: |
393 | case FilterOperation::BRIGHTNESS: |
394 | case FilterOperation::CONTRAST: |
395 | case FilterOperation::OPACITY: |
396 | glUniform1f(program.filterAmountLocation(), static_cast<const BasicComponentTransferFilterOperation&>(operation).amount()); |
397 | break; |
398 | case FilterOperation::BLUR: { |
399 | const BlurFilterOperation& blur = static_cast<const BlurFilterOperation&>(operation); |
400 | FloatSize radius; |
401 | |
402 | // Blur is done in two passes, first horizontally and then vertically. The same shader is used for both. |
403 | if (pass) |
404 | radius.setHeight(floatValueForLength(blur.stdDeviation(), size.height()) / size.height()); |
405 | else |
406 | radius.setWidth(floatValueForLength(blur.stdDeviation(), size.width()) / size.width()); |
407 | |
408 | glUniform2f(program.blurRadiusLocation(), radius.width(), radius.height()); |
409 | glUniform1fv(program.gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel()); |
410 | break; |
411 | } |
412 | case FilterOperation::DROP_SHADOW: { |
413 | const DropShadowFilterOperation& shadow = static_cast<const DropShadowFilterOperation&>(operation); |
414 | glUniform1fv(program.gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel()); |
415 | switch (pass) { |
416 | case 0: |
417 | // First pass: horizontal alpha blur. |
418 | glUniform2f(program.blurRadiusLocation(), shadow.stdDeviation() / float(size.width()), 0); |
419 | glUniform2f(program.shadowOffsetLocation(), float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height())); |
420 | break; |
421 | case 1: |
422 | // Second pass: we need the shadow color and the content texture for compositing. |
423 | float r, g, b, a; |
424 | Color(premultipliedARGBFromColor(shadow.color())).getRGBA(r, g, b, a); |
425 | glUniform4f(program.colorLocation(), r, g, b, a); |
426 | glUniform2f(program.blurRadiusLocation(), 0, shadow.stdDeviation() / float(size.height())); |
427 | glUniform2f(program.shadowOffsetLocation(), 0, 0); |
428 | glActiveTexture(GL_TEXTURE1); |
429 | glBindTexture(GL_TEXTURE_2D, contentTexture); |
430 | glUniform1i(program.contentTextureLocation(), 1); |
431 | break; |
432 | } |
433 | break; |
434 | } |
435 | default: |
436 | break; |
437 | } |
438 | } |
439 | |
440 | static TransformationMatrix colorSpaceMatrixForFlags(TextureMapperGL::Flags flags) |
441 | { |
442 | // The matrix is initially the identity one, which means no color conversion. |
443 | TransformationMatrix matrix; |
444 | if (flags & TextureMapperGL::ShouldConvertTextureBGRAToRGBA) |
445 | matrix.setMatrix(0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); |
446 | else if (flags & TextureMapperGL::ShouldConvertTextureARGBToRGBA) |
447 | matrix.setMatrix(0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0); |
448 | |
449 | return matrix; |
450 | } |
451 | |
452 | void TextureMapperGL::drawTexture(const BitmapTexture& texture, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity, unsigned exposedEdges) |
453 | { |
454 | if (!texture.isValid()) |
455 | return; |
456 | |
457 | if (clipStack().isCurrentScissorBoxEmpty()) |
458 | return; |
459 | |
460 | const BitmapTextureGL& textureGL = static_cast<const BitmapTextureGL&>(texture); |
461 | SetForScope<const BitmapTextureGL::FilterInfo*> filterInfo(data().filterInfo, textureGL.filterInfo()); |
462 | |
463 | drawTexture(textureGL.id(), textureGL.colorConvertFlags() | (textureGL.isOpaque() ? 0 : ShouldBlend), textureGL.size(), targetRect, matrix, opacity, exposedEdges); |
464 | } |
465 | |
466 | void TextureMapperGL::drawTexture(GLuint texture, Flags flags, const IntSize& textureSize, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity, unsigned exposedEdges) |
467 | { |
468 | bool useRect = flags & ShouldUseARBTextureRect; |
469 | bool useAntialiasing = m_enableEdgeDistanceAntialiasing |
470 | && exposedEdges == AllEdges |
471 | && !modelViewMatrix.mapQuad(targetRect).isRectilinear(); |
472 | |
473 | TextureMapperShaderProgram::Options options = TextureMapperShaderProgram::Texture; |
474 | if (useRect) |
475 | options |= TextureMapperShaderProgram::Rect; |
476 | if (opacity < 1) |
477 | options |= TextureMapperShaderProgram::Opacity; |
478 | if (useAntialiasing) { |
479 | options |= TextureMapperShaderProgram::Antialiasing; |
480 | flags |= ShouldAntialias; |
481 | } |
482 | if (wrapMode() == RepeatWrap && !m_contextAttributes.supportsNPOTTextures) |
483 | options |= TextureMapperShaderProgram::ManualRepeat; |
484 | |
485 | RefPtr<FilterOperation> filter = data().filterInfo ? data().filterInfo->filter: 0; |
486 | GLuint filterContentTextureID = 0; |
487 | |
488 | if (filter) { |
489 | if (data().filterInfo->contentTexture) |
490 | filterContentTextureID = toBitmapTextureGL(data().filterInfo->contentTexture.get())->id(); |
491 | options |= optionsForFilterType(filter->type(), data().filterInfo->pass); |
492 | if (filter->affectsOpacity()) |
493 | flags |= ShouldBlend; |
494 | } |
495 | |
496 | if (useAntialiasing || opacity < 1) |
497 | flags |= ShouldBlend; |
498 | |
499 | Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options); |
500 | |
501 | if (filter) |
502 | prepareFilterProgram(program.get(), *filter.get(), data().filterInfo->pass, textureSize, filterContentTextureID); |
503 | |
504 | drawTexturedQuadWithProgram(program.get(), texture, flags, textureSize, targetRect, modelViewMatrix, opacity); |
505 | } |
506 | |
507 | void TextureMapperGL::drawSolidColor(const FloatRect& rect, const TransformationMatrix& matrix, const Color& color, bool isBlendingAllowed) |
508 | { |
509 | Flags flags = 0; |
510 | TextureMapperShaderProgram::Options options = TextureMapperShaderProgram::SolidColor; |
511 | if (!matrix.mapQuad(rect).isRectilinear()) { |
512 | options |= TextureMapperShaderProgram::Antialiasing; |
513 | flags |= ShouldAntialias | (isBlendingAllowed ? ShouldBlend : 0); |
514 | } |
515 | |
516 | Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options); |
517 | glUseProgram(program->programID()); |
518 | |
519 | float r, g, b, a; |
520 | Color(premultipliedARGBFromColor(color)).getRGBA(r, g, b, a); |
521 | glUniform4f(program->colorLocation(), r, g, b, a); |
522 | if (a < 1 && isBlendingAllowed) |
523 | flags |= ShouldBlend; |
524 | |
525 | draw(rect, matrix, program.get(), GL_TRIANGLE_FAN, flags); |
526 | } |
527 | |
528 | void TextureMapperGL::clearColor(const Color& color) |
529 | { |
530 | glClearColor(color.red() / 255.0f, color.green() / 255.0f, color.blue() / 255.0f, color.alpha() / 255.0f); |
531 | glClear(GL_COLOR_BUFFER_BIT); |
532 | } |
533 | |
534 | void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram& program) |
535 | { |
536 | const GLfloat left = 0; |
537 | const GLfloat top = 0; |
538 | const GLfloat right = 1; |
539 | const GLfloat bottom = 1; |
540 | const GLfloat center = 0.5; |
541 | |
542 | // Each 4d triangle consists of a center point and two edge points, where the zw coordinates |
543 | // of each vertex equals the nearest point to the vertex on the edge. |
544 | #define SIDE_TRIANGLE_DATA(x1, y1, x2, y2) \ |
545 | x1, y1, x1, y1, \ |
546 | x2, y2, x2, y2, \ |
547 | center, center, (x1 + x2) / 2, (y1 + y2) / 2 |
548 | |
549 | static const GLfloat unitRectSideTriangles[] = { |
550 | SIDE_TRIANGLE_DATA(left, top, right, top), |
551 | SIDE_TRIANGLE_DATA(left, top, left, bottom), |
552 | SIDE_TRIANGLE_DATA(right, top, right, bottom), |
553 | SIDE_TRIANGLE_DATA(left, bottom, right, bottom) |
554 | }; |
555 | #undef SIDE_TRIANGLE_DATA |
556 | |
557 | GLuint vbo = data().getStaticVBO(GL_ARRAY_BUFFER, sizeof(GC3Dfloat) * 48, unitRectSideTriangles); |
558 | glBindBuffer(GL_ARRAY_BUFFER, vbo); |
559 | glVertexAttribPointer(program.vertexLocation(), 4, GL_FLOAT, false, 0, 0); |
560 | glDrawArrays(GL_TRIANGLES, 0, 12); |
561 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
562 | } |
563 | |
564 | void TextureMapperGL::drawUnitRect(TextureMapperShaderProgram& program, GLenum drawingMode) |
565 | { |
566 | static const GLfloat unitRect[] = { 0, 0, 1, 0, 1, 1, 0, 1 }; |
567 | GLuint vbo = data().getStaticVBO(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, unitRect); |
568 | glBindBuffer(GL_ARRAY_BUFFER, vbo); |
569 | glVertexAttribPointer(program.vertexLocation(), 2, GL_FLOAT, false, 0, 0); |
570 | glDrawArrays(drawingMode, 0, 4); |
571 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
572 | } |
573 | |
574 | void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram& program, GLenum drawingMode, Flags flags) |
575 | { |
576 | TransformationMatrix matrix(modelViewMatrix); |
577 | matrix.multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), rect)); |
578 | |
579 | glEnableVertexAttribArray(program.vertexLocation()); |
580 | program.setMatrix(program.modelViewMatrixLocation(), matrix); |
581 | program.setMatrix(program.projectionMatrixLocation(), data().projectionMatrix); |
582 | |
583 | if (isInMaskMode()) { |
584 | glBlendFunc(GL_ZERO, GL_SRC_ALPHA); |
585 | glEnable(GL_BLEND); |
586 | } else { |
587 | if (flags & ShouldBlend) { |
588 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
589 | glEnable(GL_BLEND); |
590 | } else |
591 | glDisable(GL_BLEND); |
592 | } |
593 | |
594 | if (flags & ShouldAntialias) |
595 | drawEdgeTriangles(program); |
596 | else |
597 | drawUnitRect(program, drawingMode); |
598 | |
599 | glDisableVertexAttribArray(program.vertexLocation()); |
600 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
601 | glEnable(GL_BLEND); |
602 | } |
603 | |
604 | void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram& program, uint32_t texture, Flags flags, const IntSize& size, const FloatRect& rect, const TransformationMatrix& modelViewMatrix, float opacity) |
605 | { |
606 | glUseProgram(program.programID()); |
607 | glActiveTexture(GL_TEXTURE0); |
608 | GLenum target = flags & ShouldUseARBTextureRect ? GLenum(GL_TEXTURE_RECTANGLE_ARB) : GLenum(GL_TEXTURE_2D); |
609 | glBindTexture(target, texture); |
610 | glUniform1i(program.samplerLocation(), 0); |
611 | if (wrapMode() == RepeatWrap && m_contextAttributes.supportsNPOTTextures) { |
612 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
613 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
614 | } |
615 | |
616 | TransformationMatrix patternTransform = this->patternTransform(); |
617 | if (flags & ShouldRotateTexture90) { |
618 | patternTransform.rotate(-90); |
619 | patternTransform.translate(-1, 0); |
620 | } |
621 | if (flags & ShouldRotateTexture180) { |
622 | patternTransform.rotate(180); |
623 | patternTransform.translate(-1, -1); |
624 | } |
625 | if (flags & ShouldRotateTexture270) { |
626 | patternTransform.rotate(-270); |
627 | patternTransform.translate(0, -1); |
628 | } |
629 | if (flags & ShouldFlipTexture) |
630 | patternTransform.flipY(); |
631 | if (flags & ShouldUseARBTextureRect) |
632 | patternTransform.scaleNonUniform(size.width(), size.height()); |
633 | if (flags & ShouldFlipTexture) |
634 | patternTransform.translate(0, -1); |
635 | |
636 | program.setMatrix(program.textureSpaceMatrixLocation(), patternTransform); |
637 | program.setMatrix(program.textureColorSpaceMatrixLocation(), colorSpaceMatrixForFlags(flags)); |
638 | glUniform1f(program.opacityLocation(), opacity); |
639 | |
640 | if (opacity < 1) |
641 | flags |= ShouldBlend; |
642 | |
643 | draw(rect, modelViewMatrix, program, GL_TRIANGLE_FAN, flags); |
644 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
645 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
646 | } |
647 | |
648 | void TextureMapperGL::drawFiltered(const BitmapTexture& sampler, const BitmapTexture* contentTexture, const FilterOperation& filter, int pass) |
649 | { |
650 | // For standard filters, we always draw the whole texture without transformations. |
651 | TextureMapperShaderProgram::Options options = optionsForFilterType(filter.type(), pass); |
652 | Ref<TextureMapperShaderProgram> program = data().getShaderProgram(options); |
653 | |
654 | prepareFilterProgram(program.get(), filter, pass, sampler.contentSize(), contentTexture ? static_cast<const BitmapTextureGL*>(contentTexture)->id() : 0); |
655 | FloatRect targetRect(IntPoint::zero(), sampler.contentSize()); |
656 | drawTexturedQuadWithProgram(program.get(), static_cast<const BitmapTextureGL&>(sampler).id(), 0, IntSize(1, 1), targetRect, TransformationMatrix(), 1); |
657 | } |
658 | |
659 | static inline TransformationMatrix createProjectionMatrix(const IntSize& size, bool mirrored) |
660 | { |
661 | const float nearValue = 9999999; |
662 | const float farValue = -99999; |
663 | |
664 | return TransformationMatrix(2.0 / float(size.width()), 0, 0, 0, |
665 | 0, (mirrored ? 2.0 : -2.0) / float(size.height()), 0, 0, |
666 | 0, 0, -2.f / (farValue - nearValue), 0, |
667 | -1, mirrored ? -1 : 1, -(farValue + nearValue) / (farValue - nearValue), 1); |
668 | } |
669 | |
670 | TextureMapperGL::~TextureMapperGL() |
671 | { |
672 | delete m_data; |
673 | } |
674 | |
675 | void TextureMapperGL::bindDefaultSurface() |
676 | { |
677 | glBindFramebuffer(GL_FRAMEBUFFER, data().targetFrameBuffer); |
678 | auto& viewport = data().viewport; |
679 | data().projectionMatrix = createProjectionMatrix(IntSize(viewport[2], viewport[3]), data().PaintFlags & PaintingMirrored); |
680 | glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); |
681 | m_clipStack.apply(); |
682 | data().currentSurface = nullptr; |
683 | } |
684 | |
685 | void TextureMapperGL::bindSurface(BitmapTexture *surface) |
686 | { |
687 | if (!surface) { |
688 | bindDefaultSurface(); |
689 | return; |
690 | } |
691 | |
692 | static_cast<BitmapTextureGL*>(surface)->bindAsSurface(); |
693 | data().projectionMatrix = createProjectionMatrix(surface->size(), true /* mirrored */); |
694 | data().currentSurface = surface; |
695 | } |
696 | |
697 | BitmapTexture* TextureMapperGL::currentSurface() |
698 | { |
699 | return data().currentSurface.get(); |
700 | } |
701 | |
702 | bool TextureMapperGL::beginScissorClip(const TransformationMatrix& modelViewMatrix, const FloatRect& targetRect) |
703 | { |
704 | // 3D transforms are currently not supported in scissor clipping |
705 | // resulting in cropped surfaces when z>0. |
706 | if (!modelViewMatrix.isAffine()) |
707 | return false; |
708 | |
709 | FloatQuad quad = modelViewMatrix.projectQuad(targetRect); |
710 | IntRect rect = quad.enclosingBoundingBox(); |
711 | |
712 | // Only use scissors on rectilinear clips. |
713 | if (!quad.isRectilinear() || rect.isEmpty()) |
714 | return false; |
715 | |
716 | clipStack().intersect(rect); |
717 | clipStack().applyIfNeeded(); |
718 | return true; |
719 | } |
720 | |
721 | void TextureMapperGL::beginClip(const TransformationMatrix& modelViewMatrix, const FloatRect& targetRect) |
722 | { |
723 | clipStack().push(); |
724 | if (beginScissorClip(modelViewMatrix, targetRect)) |
725 | return; |
726 | |
727 | data().initializeStencil(); |
728 | |
729 | Ref<TextureMapperShaderProgram> program = data().getShaderProgram(TextureMapperShaderProgram::SolidColor); |
730 | |
731 | glUseProgram(program->programID()); |
732 | glEnableVertexAttribArray(program->vertexLocation()); |
733 | const GLfloat unitRect[] = {0, 0, 1, 0, 1, 1, 0, 1}; |
734 | GLuint vbo = data().getStaticVBO(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, unitRect); |
735 | glBindBuffer(GL_ARRAY_BUFFER, vbo); |
736 | glVertexAttribPointer(program->vertexLocation(), 2, GL_FLOAT, false, 0, 0); |
737 | |
738 | TransformationMatrix matrix(modelViewMatrix); |
739 | matrix.multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect)); |
740 | |
741 | static const TransformationMatrix fullProjectionMatrix = TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), FloatRect(-1, -1, 2, 2)); |
742 | |
743 | int stencilIndex = clipStack().getStencilIndex(); |
744 | |
745 | glEnable(GL_STENCIL_TEST); |
746 | |
747 | // Make sure we don't do any actual drawing. |
748 | glStencilFunc(GL_NEVER, stencilIndex, stencilIndex); |
749 | |
750 | // Operate only on the stencilIndex and above. |
751 | glStencilMask(0xff & ~(stencilIndex - 1)); |
752 | |
753 | // First clear the entire buffer at the current index. |
754 | program->setMatrix(program->projectionMatrixLocation(), fullProjectionMatrix); |
755 | program->setMatrix(program->modelViewMatrixLocation(), TransformationMatrix()); |
756 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); |
757 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
758 | |
759 | // Now apply the current index to the new quad. |
760 | glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
761 | program->setMatrix(program->projectionMatrixLocation(), data().projectionMatrix); |
762 | program->setMatrix(program->modelViewMatrixLocation(), matrix); |
763 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
764 | |
765 | // Clear the state. |
766 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
767 | glDisableVertexAttribArray(program->vertexLocation()); |
768 | glStencilMask(0); |
769 | |
770 | // Increase stencilIndex and apply stencil testing. |
771 | clipStack().setStencilIndex(stencilIndex * 2); |
772 | clipStack().applyIfNeeded(); |
773 | } |
774 | |
775 | void TextureMapperGL::endClip() |
776 | { |
777 | clipStack().pop(); |
778 | clipStack().applyIfNeeded(); |
779 | } |
780 | |
781 | IntRect TextureMapperGL::clipBounds() |
782 | { |
783 | return clipStack().current().scissorBox; |
784 | } |
785 | |
786 | Ref<BitmapTexture> TextureMapperGL::createTexture(GLint internalFormat) |
787 | { |
788 | return BitmapTextureGL::create(m_contextAttributes, internalFormat); |
789 | } |
790 | |
791 | std::unique_ptr<TextureMapper> TextureMapper::platformCreateAccelerated() |
792 | { |
793 | return std::make_unique<TextureMapperGL>(); |
794 | } |
795 | |
796 | }; |
797 | |
798 | #endif // USE(TEXTURE_MAPPER_GL) |
799 | |