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
52namespace WebCore {
53
54class TextureMapperGLData {
55 WTF_MAKE_FAST_ALLOCATED;
56public:
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
78private:
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
119TextureMapperGLData::TextureMapperGLData(void* platformContext)
120 : m_sharedGLData(SharedGLData::currentSharedGLData(platformContext))
121{
122}
123
124TextureMapperGLData::~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
135void 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
150GLuint 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
163GLuint 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
173Ref<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
180TextureMapperGL::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
193ClipStack& TextureMapperGL::clipStack()
194{
195 return data().currentSurface ? toBitmapTextureGL(data().currentSurface.get())->clipStack() : m_clipStack;
196}
197
198void 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
222void 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
248void 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.
265void 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
318static 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.
349static const unsigned GaussianKernelHalfWidth = 11;
350static const float GaussianKernelStep = 0.2;
351
352static inline float gauss(float x)
353{
354 return exp(-(x * x) / 2.);
355}
356
357static 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
381static 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
440static 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
452void 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
466void 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
507void 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
528void 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
534void 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
564void 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
574void 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
604void 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
648void 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
659static 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
670TextureMapperGL::~TextureMapperGL()
671{
672 delete m_data;
673}
674
675void 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
685void 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
697BitmapTexture* TextureMapperGL::currentSurface()
698{
699 return data().currentSurface.get();
700}
701
702bool 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
721void 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
775void TextureMapperGL::endClip()
776{
777 clipStack().pop();
778 clipStack().applyIfNeeded();
779}
780
781IntRect TextureMapperGL::clipBounds()
782{
783 return clipStack().current().scissorBox;
784}
785
786Ref<BitmapTexture> TextureMapperGL::createTexture(GLint internalFormat)
787{
788 return BitmapTextureGL::create(m_contextAttributes, internalFormat);
789}
790
791std::unique_ptr<TextureMapper> TextureMapper::platformCreateAccelerated()
792{
793 return std::make_unique<TextureMapperGL>();
794}
795
796};
797
798#endif // USE(TEXTURE_MAPPER_GL)
799