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