6aee23fdb90d2bf9210f581d808fd36805be224a
[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     : TextureMapper(OpenGLMode)
245     , m_enableEdgeDistanceAntialiasing(false)
246 {
247     m_context3D = GraphicsContext3D::createForCurrentGLContext();
248     m_data = new TextureMapperGLData(m_context3D.get());
249     m_texturePool = std::make_unique<BitmapTexturePool>(m_context3D);
250 }
251
252 TextureMapperGL::ClipStack& TextureMapperGL::clipStack()
253 {
254     return data().currentSurface ? toBitmapTextureGL(data().currentSurface.get())->clipStack() : m_clipStack;
255 }
256
257 void TextureMapperGL::beginPainting(PaintFlags flags)
258 {
259     m_context3D->getIntegerv(GraphicsContext3D::CURRENT_PROGRAM, &data().previousProgram);
260     data().previousScissorState = m_context3D->isEnabled(GraphicsContext3D::SCISSOR_TEST);
261     data().previousDepthState = m_context3D->isEnabled(GraphicsContext3D::DEPTH_TEST);
262     m_context3D->disable(GraphicsContext3D::DEPTH_TEST);
263     m_context3D->enable(GraphicsContext3D::SCISSOR_TEST);
264     data().didModifyStencil = false;
265     m_context3D->depthMask(0);
266     m_context3D->getIntegerv(GraphicsContext3D::VIEWPORT, data().viewport);
267     m_context3D->getIntegerv(GraphicsContext3D::SCISSOR_BOX, data().previousScissor);
268     m_clipStack.reset(IntRect(0, 0, data().viewport[2], data().viewport[3]), ClipStack::InvertedYAxis);
269     m_context3D->getIntegerv(GraphicsContext3D::FRAMEBUFFER_BINDING, &data().targetFrameBuffer);
270     data().PaintFlags = flags;
271     bindSurface(0);
272 }
273
274 void TextureMapperGL::endPainting()
275 {
276     if (data().didModifyStencil) {
277         m_context3D->clearStencil(1);
278         m_context3D->clear(GraphicsContext3D::STENCIL_BUFFER_BIT);
279     }
280
281     m_context3D->useProgram(data().previousProgram);
282
283     m_context3D->scissor(data().previousScissor[0], data().previousScissor[1], data().previousScissor[2], data().previousScissor[3]);
284     if (data().previousScissorState)
285         m_context3D->enable(GraphicsContext3D::SCISSOR_TEST);
286     else
287         m_context3D->disable(GraphicsContext3D::SCISSOR_TEST);
288
289     if (data().previousDepthState)
290         m_context3D->enable(GraphicsContext3D::DEPTH_TEST);
291     else
292         m_context3D->disable(GraphicsContext3D::DEPTH_TEST);
293 }
294
295 void TextureMapperGL::drawBorder(const Color& color, float width, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix)
296 {
297     if (clipStack().isCurrentScissorBoxEmpty())
298         return;
299
300     RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(TextureMapperShaderProgram::SolidColor);
301     m_context3D->useProgram(program->programID());
302
303     float r, g, b, a;
304     Color(premultipliedARGBFromColor(color)).getRGBA(r, g, b, a);
305     m_context3D->uniform4f(program->colorLocation(), r, g, b, a);
306     m_context3D->lineWidth(width);
307
308     draw(targetRect, modelViewMatrix, program.get(), GraphicsContext3D::LINE_LOOP, color.hasAlpha() ? ShouldBlend : 0);
309 }
310
311 // FIXME: drawNumber() should save a number texture-atlas and re-use whenever possible.
312 void TextureMapperGL::drawNumber(int number, const Color& color, const FloatPoint& targetPoint, const TransformationMatrix& modelViewMatrix)
313 {
314     int pointSize = 8;
315
316 #if USE(CAIRO)
317     CString counterString = String::number(number).ascii();
318     // cairo_text_extents() requires a cairo_t, so dimensions need to be guesstimated.
319     int width = counterString.length() * pointSize * 1.2;
320     int height = pointSize * 1.5;
321
322     cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
323     cairo_t* cr = cairo_create(surface);
324
325     float r, g, b, a;
326     color.getRGBA(r, g, b, a);
327     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.
328     cairo_rectangle(cr, 0, 0, width, height);
329     cairo_fill(cr);
330
331     cairo_select_font_face(cr, "Monospace", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
332     cairo_set_font_size(cr, pointSize);
333     cairo_set_source_rgb(cr, 1, 1, 1);
334     cairo_move_to(cr, 2, pointSize);
335     cairo_show_text(cr, counterString.data());
336
337     IntSize size(width, height);
338     IntRect sourceRect(IntPoint::zero(), size);
339     IntRect targetRect(roundedIntPoint(targetPoint), size);
340
341     RefPtr<BitmapTexture> texture = acquireTextureFromPool(size);
342     const unsigned char* bits = cairo_image_surface_get_data(surface);
343     int stride = cairo_image_surface_get_stride(surface);
344     static_cast<BitmapTextureGL*>(texture.get())->updateContentsNoSwizzle(bits, sourceRect, IntPoint::zero(), stride);
345     drawTexture(*texture, targetRect, modelViewMatrix, 1.0f, AllEdges);
346
347     cairo_surface_destroy(surface);
348     cairo_destroy(cr);
349
350 #else
351     UNUSED_PARAM(number);
352     UNUSED_PARAM(pointSize);
353     UNUSED_PARAM(targetPoint);
354     UNUSED_PARAM(modelViewMatrix);
355     notImplemented();
356 #endif
357 }
358
359 static TextureMapperShaderProgram::Options optionsForFilterType(FilterOperation::OperationType type, unsigned pass)
360 {
361     switch (type) {
362     case FilterOperation::GRAYSCALE:
363         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::GrayscaleFilter;
364     case FilterOperation::SEPIA:
365         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::SepiaFilter;
366     case FilterOperation::SATURATE:
367         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::SaturateFilter;
368     case FilterOperation::HUE_ROTATE:
369         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::HueRotateFilter;
370     case FilterOperation::INVERT:
371         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::InvertFilter;
372     case FilterOperation::BRIGHTNESS:
373         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::BrightnessFilter;
374     case FilterOperation::CONTRAST:
375         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::ContrastFilter;
376     case FilterOperation::OPACITY:
377         return TextureMapperShaderProgram::Texture | TextureMapperShaderProgram::OpacityFilter;
378     case FilterOperation::BLUR:
379         return TextureMapperShaderProgram::BlurFilter;
380     case FilterOperation::DROP_SHADOW:
381         return TextureMapperShaderProgram::AlphaBlur
382             | (pass ? TextureMapperShaderProgram::ContentTexture | TextureMapperShaderProgram::SolidColor: 0);
383     default:
384         ASSERT_NOT_REACHED();
385         return 0;
386     }
387 }
388
389 // Create a normal distribution of 21 values between -2 and 2.
390 static const unsigned GaussianKernelHalfWidth = 11;
391 static const float GaussianKernelStep = 0.2;
392
393 static inline float gauss(float x)
394 {
395     return exp(-(x * x) / 2.);
396 }
397
398 static float* gaussianKernel()
399 {
400     static bool prepared = false;
401     static float kernel[GaussianKernelHalfWidth] = {0, };
402
403     if (prepared)
404         return kernel;
405
406     kernel[0] = gauss(0);
407     float sum = kernel[0];
408     for (unsigned i = 1; i < GaussianKernelHalfWidth; ++i) {
409         kernel[i] = gauss(i * GaussianKernelStep);
410         sum += 2 * kernel[i];
411     }
412
413     // Normalize the kernel.
414     float scale = 1 / sum;
415     for (unsigned i = 0; i < GaussianKernelHalfWidth; ++i)
416         kernel[i] *= scale;
417
418     prepared = true;
419     return kernel;
420 }
421
422 static void prepareFilterProgram(TextureMapperShaderProgram* program, const FilterOperation& operation, unsigned pass, const IntSize& size, GC3Duint contentTexture)
423 {
424     RefPtr<GraphicsContext3D> context = program->context();
425     context->useProgram(program->programID());
426
427     switch (operation.type()) {
428     case FilterOperation::GRAYSCALE:
429     case FilterOperation::SEPIA:
430     case FilterOperation::SATURATE:
431     case FilterOperation::HUE_ROTATE:
432         context->uniform1f(program->filterAmountLocation(), static_cast<const BasicColorMatrixFilterOperation&>(operation).amount());
433         break;
434     case FilterOperation::INVERT:
435     case FilterOperation::BRIGHTNESS:
436     case FilterOperation::CONTRAST:
437     case FilterOperation::OPACITY:
438         context->uniform1f(program->filterAmountLocation(), static_cast<const BasicComponentTransferFilterOperation&>(operation).amount());
439         break;
440     case FilterOperation::BLUR: {
441         const BlurFilterOperation& blur = static_cast<const BlurFilterOperation&>(operation);
442         FloatSize radius;
443
444         // Blur is done in two passes, first horizontally and then vertically. The same shader is used for both.
445         if (pass)
446             radius.setHeight(floatValueForLength(blur.stdDeviation(), size.height()) / size.height());
447         else
448             radius.setWidth(floatValueForLength(blur.stdDeviation(), size.width()) / size.width());
449
450         context->uniform2f(program->blurRadiusLocation(), radius.width(), radius.height());
451         context->uniform1fv(program->gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel());
452         break;
453     }
454     case FilterOperation::DROP_SHADOW: {
455         const DropShadowFilterOperation& shadow = static_cast<const DropShadowFilterOperation&>(operation);
456         context->uniform1fv(program->gaussianKernelLocation(), GaussianKernelHalfWidth, gaussianKernel());
457         switch (pass) {
458         case 0:
459             // First pass: horizontal alpha blur.
460             context->uniform2f(program->blurRadiusLocation(), shadow.stdDeviation() / float(size.width()), 0);
461             context->uniform2f(program->shadowOffsetLocation(), float(shadow.location().x()) / float(size.width()), float(shadow.location().y()) / float(size.height()));
462             break;
463         case 1:
464             // Second pass: we need the shadow color and the content texture for compositing.
465             float r, g, b, a;
466             Color(premultipliedARGBFromColor(shadow.color())).getRGBA(r, g, b, a);
467             context->uniform4f(program->colorLocation(), r, g, b, a);
468             context->uniform2f(program->blurRadiusLocation(), 0, shadow.stdDeviation() / float(size.height()));
469             context->uniform2f(program->shadowOffsetLocation(), 0, 0);
470             context->activeTexture(GraphicsContext3D::TEXTURE1);
471             context->bindTexture(GraphicsContext3D::TEXTURE_2D, contentTexture);
472             context->uniform1i(program->contentTextureLocation(), 1);
473             break;
474         }
475         break;
476     }
477     default:
478         break;
479     }
480 }
481
482 void TextureMapperGL::drawTexture(const BitmapTexture& texture, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity, unsigned exposedEdges)
483 {
484     if (!texture.isValid())
485         return;
486
487     if (clipStack().isCurrentScissorBoxEmpty())
488         return;
489
490     const BitmapTextureGL& textureGL = static_cast<const BitmapTextureGL&>(texture);
491     TemporaryChange<const BitmapTextureGL::FilterInfo*> filterInfo(data().filterInfo, textureGL.filterInfo());
492
493     drawTexture(textureGL.id(), textureGL.isOpaque() ? 0 : ShouldBlend, textureGL.size(), targetRect, matrix, opacity, exposedEdges);
494 }
495
496 void TextureMapperGL::drawTexture(Platform3DObject texture, Flags flags, const IntSize& textureSize, const FloatRect& targetRect, const TransformationMatrix& modelViewMatrix, float opacity, unsigned exposedEdges)
497 {
498     bool useRect = flags & ShouldUseARBTextureRect;
499     bool useAntialiasing = m_enableEdgeDistanceAntialiasing
500         && exposedEdges == AllEdges
501         && !modelViewMatrix.mapQuad(targetRect).isRectilinear();
502
503     TextureMapperShaderProgram::Options options = TextureMapperShaderProgram::Texture;
504     if (useRect)
505         options |= TextureMapperShaderProgram::Rect;
506     if (opacity < 1)
507         options |= TextureMapperShaderProgram::Opacity;
508     if (useAntialiasing) {
509         options |= TextureMapperShaderProgram::Antialiasing;
510         flags |= ShouldAntialias;
511     }
512
513     RefPtr<FilterOperation> filter = data().filterInfo ? data().filterInfo->filter: 0;
514     GC3Duint filterContentTextureID = 0;
515
516     if (filter) {
517         if (data().filterInfo->contentTexture)
518             filterContentTextureID = toBitmapTextureGL(data().filterInfo->contentTexture.get())->id();
519         options |= optionsForFilterType(filter->type(), data().filterInfo->pass);
520         if (filter->affectsOpacity())
521             flags |= ShouldBlend;
522     }
523
524     if (useAntialiasing || opacity < 1)
525         flags |= ShouldBlend;
526
527     RefPtr<TextureMapperShaderProgram> program;
528     program = data().sharedGLData().getShaderProgram(options);
529
530     if (filter)
531         prepareFilterProgram(program.get(), *filter.get(), data().filterInfo->pass, textureSize, filterContentTextureID);
532
533     drawTexturedQuadWithProgram(program.get(), texture, flags, textureSize, targetRect, modelViewMatrix, opacity);
534 }
535
536 void TextureMapperGL::drawSolidColor(const FloatRect& rect, const TransformationMatrix& matrix, const Color& color)
537 {
538     Flags flags = 0;
539     TextureMapperShaderProgram::Options options = TextureMapperShaderProgram::SolidColor;
540     if (!matrix.mapQuad(rect).isRectilinear()) {
541         options |= TextureMapperShaderProgram::Antialiasing;
542         flags |= ShouldBlend | ShouldAntialias;
543     }
544
545     RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(options);
546     m_context3D->useProgram(program->programID());
547
548     float r, g, b, a;
549     Color(premultipliedARGBFromColor(color)).getRGBA(r, g, b, a);
550     m_context3D->uniform4f(program->colorLocation(), r, g, b, a);
551     if (a < 1)
552         flags |= ShouldBlend;
553
554     draw(rect, matrix, program.get(), GraphicsContext3D::TRIANGLE_FAN, flags);
555 }
556
557 void TextureMapperGL::drawEdgeTriangles(TextureMapperShaderProgram* program)
558 {
559     const GC3Dfloat left = 0;
560     const GC3Dfloat top = 0;
561     const GC3Dfloat right = 1;
562     const GC3Dfloat bottom = 1;
563     const GC3Dfloat center = 0.5;
564
565 // Each 4d triangle consists of a center point and two edge points, where the zw coordinates
566 // of each vertex equals the nearest point to the vertex on the edge.
567 #define SIDE_TRIANGLE_DATA(x1, y1, x2, y2) \
568     x1, y1, x1, y1, \
569     x2, y2, x2, y2, \
570     center, center, (x1 + x2) / 2, (y1 + y2) / 2
571
572     static const GC3Dfloat unitRectSideTriangles[] = {
573         SIDE_TRIANGLE_DATA(left, top, right, top),
574         SIDE_TRIANGLE_DATA(left, top, left, bottom),
575         SIDE_TRIANGLE_DATA(right, top, right, bottom),
576         SIDE_TRIANGLE_DATA(left, bottom, right, bottom)
577     };
578 #undef SIDE_TRIANGLE_DATA
579
580     Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 48, unitRectSideTriangles);
581     m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo);
582     m_context3D->vertexAttribPointer(program->vertexLocation(), 4, GraphicsContext3D::FLOAT, false, 0, 0);
583     m_context3D->drawArrays(GraphicsContext3D::TRIANGLES, 0, 12);
584     m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0);
585 }
586
587 void TextureMapperGL::drawUnitRect(TextureMapperShaderProgram* program, GC3Denum drawingMode)
588 {
589     static const GC3Dfloat unitRect[] = { 0, 0, 1, 0, 1, 1, 0, 1 };
590     Platform3DObject vbo = data().getStaticVBO(GraphicsContext3D::ARRAY_BUFFER, sizeof(GC3Dfloat) * 8, unitRect);
591     m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vbo);
592     m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, 0);
593     m_context3D->drawArrays(drawingMode, 0, 4);
594     m_context3D->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0);
595 }
596
597 void TextureMapperGL::draw(const FloatRect& rect, const TransformationMatrix& modelViewMatrix, TextureMapperShaderProgram* shaderProgram, GC3Denum drawingMode, Flags flags)
598 {
599     TransformationMatrix matrix =
600         TransformationMatrix(modelViewMatrix).multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), rect));
601
602     m_context3D->enableVertexAttribArray(shaderProgram->vertexLocation());
603     shaderProgram->setMatrix(shaderProgram->modelViewMatrixLocation(), matrix);
604     shaderProgram->setMatrix(shaderProgram->projectionMatrixLocation(), data().projectionMatrix);
605
606     if (isInMaskMode()) {
607         m_context3D->blendFunc(GraphicsContext3D::ZERO, GraphicsContext3D::SRC_ALPHA);
608         m_context3D->enable(GraphicsContext3D::BLEND);
609     } else {
610         if (flags & ShouldBlend) {
611             m_context3D->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA);
612             m_context3D->enable(GraphicsContext3D::BLEND);
613         } else
614             m_context3D->disable(GraphicsContext3D::BLEND);
615     }
616
617     if (flags & ShouldAntialias)
618         drawEdgeTriangles(shaderProgram);
619     else
620         drawUnitRect(shaderProgram, drawingMode);
621
622     m_context3D->disableVertexAttribArray(shaderProgram->vertexLocation());
623     m_context3D->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA);
624     m_context3D->enable(GraphicsContext3D::BLEND);
625 }
626
627 void TextureMapperGL::drawTexturedQuadWithProgram(TextureMapperShaderProgram* program, uint32_t texture, Flags flags, const IntSize& size, const FloatRect& rect, const TransformationMatrix& modelViewMatrix, float opacity)
628 {
629     m_context3D->useProgram(program->programID());
630     m_context3D->activeTexture(GraphicsContext3D::TEXTURE0);
631     GC3Denum target = flags & ShouldUseARBTextureRect ? GC3Denum(Extensions3D::TEXTURE_RECTANGLE_ARB) : GC3Denum(GraphicsContext3D::TEXTURE_2D);
632     m_context3D->bindTexture(target, texture);
633     m_context3D->uniform1i(program->samplerLocation(), 0);
634     if (wrapMode() == RepeatWrap) {
635         m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::REPEAT);
636         m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::REPEAT);
637     }
638
639     TransformationMatrix patternTransform = this->patternTransform();
640     if (flags & ShouldFlipTexture)
641         patternTransform.flipY();
642     if (flags & ShouldUseARBTextureRect)
643         patternTransform.scaleNonUniform(size.width(), size.height());
644     if (flags & ShouldFlipTexture)
645         patternTransform.translate(0, -1);
646
647     program->setMatrix(program->textureSpaceMatrixLocation(), patternTransform);
648     m_context3D->uniform1f(program->opacityLocation(), opacity);
649
650     if (opacity < 1)
651         flags |= ShouldBlend;
652
653     draw(rect, modelViewMatrix, program, GraphicsContext3D::TRIANGLE_FAN, flags);
654     m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
655     m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
656 }
657
658 void TextureMapperGL::drawFiltered(const BitmapTexture& sampler, const BitmapTexture* contentTexture, const FilterOperation& filter, int pass)
659 {
660     // For standard filters, we always draw the whole texture without transformations.
661     TextureMapperShaderProgram::Options options = optionsForFilterType(filter.type(), pass);
662     RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(options);
663     ASSERT(program);
664
665     prepareFilterProgram(program.get(), filter, pass, sampler.contentSize(), contentTexture ? static_cast<const BitmapTextureGL*>(contentTexture)->id() : 0);
666     FloatRect targetRect(IntPoint::zero(), sampler.contentSize());
667     drawTexturedQuadWithProgram(program.get(), static_cast<const BitmapTextureGL&>(sampler).id(), 0, IntSize(1, 1), targetRect, TransformationMatrix(), 1);
668 }
669
670 static inline TransformationMatrix createProjectionMatrix(const IntSize& size, bool mirrored)
671 {
672     const float nearValue = 9999999;
673     const float farValue = -99999;
674
675     return TransformationMatrix(2.0 / float(size.width()), 0, 0, 0,
676                                 0, (mirrored ? 2.0 : -2.0) / float(size.height()), 0, 0,
677                                 0, 0, -2.f / (farValue - nearValue), 0,
678                                 -1, mirrored ? -1 : 1, -(farValue + nearValue) / (farValue - nearValue), 1);
679 }
680
681 TextureMapperGL::~TextureMapperGL()
682 {
683     delete m_data;
684 }
685
686 void TextureMapperGL::bindDefaultSurface()
687 {
688     m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, data().targetFrameBuffer);
689     IntSize viewportSize(data().viewport[2], data().viewport[3]);
690     data().projectionMatrix = createProjectionMatrix(viewportSize, data().PaintFlags & PaintingMirrored);
691     m_context3D->viewport(data().viewport[0], data().viewport[1], viewportSize.width(), viewportSize.height());
692     m_clipStack.apply(m_context3D.get());
693     data().currentSurface.clear();
694 }
695
696 void TextureMapperGL::bindSurface(BitmapTexture *surface)
697 {
698     if (!surface) {
699         bindDefaultSurface();
700         return;
701     }
702
703     static_cast<BitmapTextureGL*>(surface)->bindAsSurface(m_context3D.get());
704     data().projectionMatrix = createProjectionMatrix(surface->size(), true /* mirrored */);
705     data().currentSurface = surface;
706 }
707
708 BitmapTexture* TextureMapperGL::currentSurface()
709 {
710     return data().currentSurface.get();
711 }
712
713 bool TextureMapperGL::beginScissorClip(const TransformationMatrix& modelViewMatrix, const FloatRect& targetRect)
714 {
715     // 3D transforms are currently not supported in scissor clipping
716     // resulting in cropped surfaces when z>0.
717     if (!modelViewMatrix.isAffine())
718         return false;
719
720     FloatQuad quad = modelViewMatrix.projectQuad(targetRect);
721     IntRect rect = quad.enclosingBoundingBox();
722
723     // Only use scissors on rectilinear clips.
724     if (!quad.isRectilinear() || rect.isEmpty())
725         return false;
726
727     clipStack().intersect(rect);
728     clipStack().applyIfNeeded(m_context3D.get());
729     return true;
730 }
731
732 void TextureMapperGL::beginClip(const TransformationMatrix& modelViewMatrix, const FloatRect& targetRect)
733 {
734     clipStack().push();
735     if (beginScissorClip(modelViewMatrix, targetRect))
736         return;
737
738     data().initializeStencil();
739
740     RefPtr<TextureMapperShaderProgram> program = data().sharedGLData().getShaderProgram(TextureMapperShaderProgram::SolidColor);
741
742     m_context3D->useProgram(program->programID());
743     m_context3D->enableVertexAttribArray(program->vertexLocation());
744     const GC3Dfloat unitRect[] = {0, 0, 1, 0, 1, 1, 0, 1};
745     m_context3D->vertexAttribPointer(program->vertexLocation(), 2, GraphicsContext3D::FLOAT, false, 0, GC3Dintptr(unitRect));
746
747     TransformationMatrix matrix = TransformationMatrix(modelViewMatrix)
748         .multiply(TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), targetRect));
749
750     static const TransformationMatrix fullProjectionMatrix = TransformationMatrix::rectToRect(FloatRect(0, 0, 1, 1), FloatRect(-1, -1, 2, 2));
751
752     int stencilIndex = clipStack().getStencilIndex();
753
754     m_context3D->enable(GraphicsContext3D::STENCIL_TEST);
755
756     // Make sure we don't do any actual drawing.
757     m_context3D->stencilFunc(GraphicsContext3D::NEVER, stencilIndex, stencilIndex);
758
759     // Operate only on the stencilIndex and above.
760     m_context3D->stencilMask(0xff & ~(stencilIndex - 1));
761
762     // First clear the entire buffer at the current index.
763     program->setMatrix(program->projectionMatrixLocation(), fullProjectionMatrix);
764     program->setMatrix(program->modelViewMatrixLocation(), TransformationMatrix());
765     m_context3D->stencilOp(GraphicsContext3D::ZERO, GraphicsContext3D::ZERO, GraphicsContext3D::ZERO);
766     m_context3D->drawArrays(GraphicsContext3D::TRIANGLE_FAN, 0, 4);
767
768     // Now apply the current index to the new quad.
769     m_context3D->stencilOp(GraphicsContext3D::REPLACE, GraphicsContext3D::REPLACE, GraphicsContext3D::REPLACE);
770     program->setMatrix(program->projectionMatrixLocation(), data().projectionMatrix);
771     program->setMatrix(program->modelViewMatrixLocation(), matrix);
772     m_context3D->drawArrays(GraphicsContext3D::TRIANGLE_FAN, 0, 4);
773
774     // Clear the state.
775     m_context3D->disableVertexAttribArray(program->vertexLocation());
776     m_context3D->stencilMask(0);
777
778     // Increase stencilIndex and apply stencil testing.
779     clipStack().setStencilIndex(stencilIndex * 2);
780     clipStack().applyIfNeeded(m_context3D.get());
781 }
782
783 void TextureMapperGL::endClip()
784 {
785     clipStack().pop();
786     clipStack().applyIfNeeded(m_context3D.get());
787 }
788
789 IntRect TextureMapperGL::clipBounds()
790 {
791     return clipStack().current().scissorBox;
792 }
793
794 PassRefPtr<BitmapTexture> TextureMapperGL::createTexture()
795 {
796     BitmapTextureGL* texture = new BitmapTextureGL(m_context3D);
797     return adoptRef(texture);
798 }
799
800 #if USE(TEXTURE_MAPPER_GL)
801 std::unique_ptr<TextureMapper> TextureMapper::platformCreateAccelerated()
802 {
803     return std::make_unique<TextureMapperGL>();
804 }
805 #endif
806
807 };
808 #endif