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