GLES2Canvas.cpp revision 2bde8e466a4451c7319e3a072d118917957d6554
1/*
2 * Copyright (c) 2010, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#include "GLES2Canvas.h"
34
35#include "DrawingBuffer.h"
36#include "FloatRect.h"
37#include "FloatSize.h"
38#include "GraphicsContext3D.h"
39#include "internal_glu.h"
40#include "IntRect.h"
41#include "LoopBlinnPathProcessor.h"
42#include "LoopBlinnSolidFillShader.h"
43#include "Path.h"
44#include "PlatformString.h"
45#include "SharedGraphicsContext3D.h"
46#if USE(SKIA)
47#include "SkPath.h"
48#endif
49#include "Texture.h"
50
51#define _USE_MATH_DEFINES
52#include <math.h>
53
54#include <wtf/OwnArrayPtr.h>
55#include <wtf/text/CString.h>
56
57namespace WebCore {
58
59// Number of line segments used to approximate bezier curves.
60const int pathTesselation = 30;
61typedef void (GLAPIENTRY *TESSCB)();
62typedef WTF::Vector<float> FloatVector;
63typedef WTF::Vector<double> DoubleVector;
64
65struct PathAndTransform {
66    PathAndTransform(const Path& p, const AffineTransform& t)
67        : path(p)
68        , transform(t)
69    {
70    }
71    Path path;
72    AffineTransform transform;
73};
74
75struct GLES2Canvas::State {
76    State()
77        : m_fillColor(0, 0, 0, 255)
78        , m_shadowColor(0, 0, 0, 0)
79        , m_alpha(1.0f)
80        , m_compositeOp(CompositeSourceOver)
81        , m_numClippingPaths(0)
82        , m_shadowOffset(0, 0)
83        , m_shadowBlur(0)
84        , m_shadowsIgnoreTransforms(false)
85    {
86    }
87    State(const State& other)
88        : m_fillColor(other.m_fillColor)
89        , m_shadowColor(other.m_shadowColor)
90        , m_alpha(other.m_alpha)
91        , m_compositeOp(other.m_compositeOp)
92        , m_ctm(other.m_ctm)
93        , m_clippingPaths() // Don't copy; clipping paths are tracked per-state.
94        , m_numClippingPaths(other.m_numClippingPaths)
95        , m_shadowOffset(other.m_shadowOffset)
96        , m_shadowBlur(other.m_shadowBlur)
97        , m_shadowsIgnoreTransforms(other.m_shadowsIgnoreTransforms)
98    {
99    }
100    Color m_fillColor;
101    Color m_shadowColor;
102    float m_alpha;
103    CompositeOperator m_compositeOp;
104    AffineTransform m_ctm;
105    WTF::Vector<PathAndTransform> m_clippingPaths;
106    int m_numClippingPaths;
107    FloatSize m_shadowOffset;
108    float m_shadowBlur;
109    bool m_shadowsIgnoreTransforms;
110
111    // Helper function for applying the state's alpha value to the given input
112    // color to produce a new output color. The logic is the same as
113    // PlatformContextSkia::State::applyAlpha(), but the type is different.
114    Color applyAlpha(const Color& c)
115    {
116        int s = roundf(m_alpha * 256);
117        if (s >= 256)
118            return c;
119        if (s < 0)
120            return Color();
121
122        int a = (c.alpha() * s) >> 8;
123        return Color(c.red(), c.green(), c.blue(), a);
124    }
125    bool shadowActive() const
126    {
127        return m_shadowColor.alpha() > 0 && (m_shadowBlur || m_shadowOffset.width() || m_shadowOffset.height());
128    }
129    bool clippingEnabled() { return m_numClippingPaths > 0; }
130};
131
132static inline FloatPoint operator*(const FloatPoint& f, float scale)
133{
134    return FloatPoint(f.x() * scale, f.y() * scale);
135}
136
137static inline FloatPoint operator*(float scale, const FloatPoint& f)
138{
139    return FloatPoint(f.x() * scale, f.y() * scale);
140}
141
142static inline FloatSize operator*(const FloatSize& f, float scale)
143{
144    return FloatSize(f.width() * scale, f.height() * scale);
145}
146
147static inline FloatSize operator*(float scale, const FloatSize& f)
148{
149    return FloatSize(f.width() * scale, f.height() * scale);
150}
151
152class Quadratic {
153  public:
154    Quadratic(FloatPoint a, FloatPoint b, FloatPoint c) :
155        m_a(a), m_b(b), m_c(c)
156    {
157    }
158    static Quadratic fromBezier(FloatPoint p0, FloatPoint p1, FloatPoint p2)
159    {
160        FloatSize p1s(p1.x(), p1.y());
161        FloatSize p2s(p2.x(), p2.y());
162        FloatPoint b = -2.0f * p0 + 2.0f * p1s;
163        FloatPoint c =         p0 - 2.0f * p1s + p2s;
164        return Quadratic(p0, b, c);
165    }
166    inline FloatPoint evaluate(float t)
167    {
168        return m_a + t * (m_b + t * m_c);
169    }
170    FloatPoint m_a, m_b, m_c, m_d;
171};
172
173class Cubic {
174  public:
175    Cubic(FloatPoint a, FloatPoint b, FloatPoint c, FloatPoint d) :
176        m_a(a), m_b(b), m_c(c), m_d(d)
177    {
178    }
179    static Cubic fromBezier(FloatPoint p0, FloatPoint p1, FloatPoint p2, FloatPoint p3)
180    {
181        FloatSize p1s(p1.x(), p1.y());
182        FloatSize p2s(p2.x(), p2.y());
183        FloatSize p3s(p3.x(), p3.y());
184        FloatPoint b = -3.0f * p0 + 3.0f * p1s;
185        FloatPoint c =  3.0f * p0 - 6.0f * p1s + 3.0f * p2s;
186        FloatPoint d = -1.0f * p0 + 3.0f * p1s - 3.0f * p2s + p3s;
187        return Cubic(p0, b, c, d);
188    }
189    FloatPoint evaluate(float t)
190    {
191        return m_a + t * (m_b + t * (m_c + t * m_d));
192    }
193    FloatPoint m_a, m_b, m_c, m_d;
194};
195
196GLES2Canvas::GLES2Canvas(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const IntSize& size)
197    : m_size(size)
198    , m_context(context)
199    , m_drawingBuffer(drawingBuffer)
200    , m_state(0)
201    , m_pathVertexBuffer(0)
202{
203    m_flipMatrix.translate(-1.0f, 1.0f);
204    m_flipMatrix.scale(2.0f / size.width(), -2.0f / size.height());
205
206    m_stateStack.append(State());
207    m_state = &m_stateStack.last();
208}
209
210GLES2Canvas::~GLES2Canvas()
211{
212}
213
214void GLES2Canvas::bindFramebuffer()
215{
216    m_drawingBuffer->bind();
217}
218
219void GLES2Canvas::clearRect(const FloatRect& rect)
220{
221    bindFramebuffer();
222    if (m_state->m_ctm.isIdentity() && !m_state->clippingEnabled()) {
223        scissorClear(rect.x(), rect.y(), rect.width(), rect.height());
224    } else {
225        save();
226        setCompositeOperation(CompositeClear);
227        fillRect(rect, Color(RGBA32(0)), ColorSpaceDeviceRGB);
228        restore();
229    }
230}
231
232void GLES2Canvas::scissorClear(float x, float y, float width, float height)
233{
234    int intX = static_cast<int>(x + 0.5f);
235    int intY = static_cast<int>(y + 0.5f);
236    int intWidth = static_cast<int>(x + width + 0.5f) - intX;
237    int intHeight = static_cast<int>(y + height + 0.5f) - intY;
238    m_context->scissor(intX, m_size.height() - intHeight - intY, intWidth, intHeight);
239    m_context->enable(GraphicsContext3D::SCISSOR_TEST);
240    m_context->clearColor(Color(RGBA32(0)));
241    m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
242    m_context->disable(GraphicsContext3D::SCISSOR_TEST);
243}
244
245void GLES2Canvas::fillPath(const Path& path)
246{
247    if (m_state->shadowActive()) {
248        beginShadowDraw();
249        fillPathInternal(path, m_state->m_shadowColor);
250        endShadowDraw(path.boundingRect());
251    }
252
253    bindFramebuffer();
254    m_context->applyCompositeOperator(m_state->m_compositeOp);
255    applyClipping(m_state->clippingEnabled());
256
257    fillPathInternal(path, m_state->applyAlpha(m_state->m_fillColor));
258}
259
260void GLES2Canvas::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
261{
262    if (m_state->shadowActive()) {
263        beginShadowDraw();
264        fillRectInternal(rect, m_state->m_shadowColor);
265        endShadowDraw(rect);
266    }
267
268    bindFramebuffer();
269    m_context->applyCompositeOperator(m_state->m_compositeOp);
270    applyClipping(m_state->clippingEnabled());
271
272    fillRectInternal(rect, color);
273}
274
275void GLES2Canvas::fillRect(const FloatRect& rect)
276{
277    fillRect(rect, m_state->applyAlpha(m_state->m_fillColor), ColorSpaceDeviceRGB);
278}
279
280void GLES2Canvas::fillRectInternal(const FloatRect& rect, const Color& color)
281{
282    AffineTransform matrix(m_flipMatrix);
283    matrix *= m_state->m_ctm;
284    matrix.translate(rect.x(), rect.y());
285    matrix.scale(rect.width(), rect.height());
286
287    m_context->useQuadVertices();
288    m_context->useFillSolidProgram(matrix, color);
289    m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4);
290}
291
292void GLES2Canvas::setFillColor(const Color& color, ColorSpace colorSpace)
293{
294    m_state->m_fillColor = color;
295}
296
297void GLES2Canvas::setAlpha(float alpha)
298{
299    m_state->m_alpha = alpha;
300}
301
302void GLES2Canvas::setShadowColor(const Color& color, ColorSpace)
303{
304    m_state->m_shadowColor = color;
305}
306
307void GLES2Canvas::setShadowOffset(const FloatSize& offset)
308{
309    m_state->m_shadowOffset = offset;
310}
311
312void GLES2Canvas::setShadowBlur(float shadowBlur)
313{
314    m_state->m_shadowBlur = shadowBlur;
315}
316
317void GLES2Canvas::setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms)
318{
319    m_state->m_shadowsIgnoreTransforms = shadowsIgnoreTransforms;
320}
321
322void GLES2Canvas::translate(float x, float y)
323{
324    m_state->m_ctm.translate(x, y);
325}
326
327void GLES2Canvas::rotate(float angleInRadians)
328{
329    m_state->m_ctm.rotate(angleInRadians * (180.0f / M_PI));
330}
331
332void GLES2Canvas::scale(const FloatSize& size)
333{
334    m_state->m_ctm.scale(size.width(), size.height());
335}
336
337void GLES2Canvas::concatCTM(const AffineTransform& affine)
338{
339    m_state->m_ctm *= affine;
340}
341
342void GLES2Canvas::setCTM(const AffineTransform& affine)
343{
344    m_state->m_ctm = affine;
345}
346
347void GLES2Canvas::clipPath(const Path& path)
348{
349    bindFramebuffer();
350    checkGLError("bindFramebuffer");
351    beginStencilDraw(GraphicsContext3D::INCR);
352    // Red is used so we can see it if it ends up in the color buffer.
353    Color red(255, 0, 0, 255);
354    fillPathInternal(path, red);
355    m_state->m_clippingPaths.append(PathAndTransform(path, m_state->m_ctm));
356    m_state->m_numClippingPaths++;
357}
358
359void GLES2Canvas::clipOut(const Path& path)
360{
361    ASSERT(!"clipOut is unsupported in GLES2Canvas.\n");
362}
363
364void GLES2Canvas::save()
365{
366    m_stateStack.append(State(m_stateStack.last()));
367    m_state = &m_stateStack.last();
368}
369
370void GLES2Canvas::restore()
371{
372    ASSERT(!m_stateStack.isEmpty());
373    const Vector<PathAndTransform>& clippingPaths = m_state->m_clippingPaths;
374    if (!clippingPaths.isEmpty()) {
375        beginStencilDraw(GraphicsContext3D::DECR);
376        WTF::Vector<PathAndTransform>::const_iterator pathIter;
377        for (pathIter = clippingPaths.begin(); pathIter < clippingPaths.end(); ++pathIter) {
378            m_state->m_ctm = pathIter->transform;
379            // Red is used so we can see it if it ends up in the color buffer.
380            Color red(255, 0, 0, 255);
381            fillPathInternal(pathIter->path, red);
382        }
383    }
384    m_stateStack.removeLast();
385    m_state = &m_stateStack.last();
386}
387
388void GLES2Canvas::drawTexturedRect(unsigned texture, const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace colorSpace, CompositeOperator compositeOp)
389{
390    bindFramebuffer();
391    m_context->applyCompositeOperator(compositeOp);
392    applyClipping(false);
393
394    m_context->setActiveTexture(GraphicsContext3D::TEXTURE0);
395
396    m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, texture);
397
398    drawTexturedQuad(textureSize, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha);
399}
400
401void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace colorSpace, CompositeOperator compositeOp)
402{
403    drawTexturedRect(texture, srcRect, dstRect, m_state->m_ctm, m_state->m_alpha, colorSpace, compositeOp, m_state->clippingEnabled());
404}
405
406
407void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha, ColorSpace colorSpace, CompositeOperator compositeOp, bool clip)
408{
409    bindFramebuffer();
410    m_context->applyCompositeOperator(compositeOp);
411    applyClipping(clip);
412    const TilingData& tiles = texture->tiles();
413    IntRect tileIdxRect = tiles.overlappedTileIndices(srcRect);
414
415    m_context->setActiveTexture(GraphicsContext3D::TEXTURE0);
416
417    for (int y = tileIdxRect.y(); y <= tileIdxRect.maxY(); y++) {
418        for (int x = tileIdxRect.x(); x <= tileIdxRect.maxX(); x++)
419            drawTexturedRectTile(texture, tiles.tileIndex(x, y), srcRect, dstRect, transform, alpha);
420    }
421}
422
423void GLES2Canvas::drawTexturedRectTile(Texture* texture, int tile, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha)
424{
425    if (dstRect.isEmpty())
426        return;
427
428    const TilingData& tiles = texture->tiles();
429
430    texture->bindTile(tile);
431
432    FloatRect srcRectClippedInTileSpace;
433    FloatRect dstRectIntersected;
434    tiles.intersectDrawQuad(srcRect, dstRect, tile, &srcRectClippedInTileSpace, &dstRectIntersected);
435
436    IntRect tileBoundsWithBorder = tiles.tileBoundsWithBorder(tile);
437
438    drawTexturedQuad(tileBoundsWithBorder.size(), srcRectClippedInTileSpace, dstRectIntersected, transform, alpha);
439}
440
441void GLES2Canvas::convolveRect(unsigned texture, const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, float imageIncrement[2], const float* kernel, int kernelWidth)
442{
443    m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, texture);
444    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
445    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
446    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST);
447    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::NEAREST);
448
449    AffineTransform matrix(m_flipMatrix);
450    matrix.translate(dstRect.x(), dstRect.y());
451    matrix.scale(dstRect.width(), dstRect.height());
452
453    AffineTransform texMatrix;
454    texMatrix.scale(1.0f / textureSize.width(), 1.0f / textureSize.height());
455    texMatrix.translate(srcRect.x(), srcRect.y());
456    texMatrix.scale(srcRect.width(), srcRect.height());
457
458    m_context->useQuadVertices();
459    m_context->useConvolutionProgram(matrix, texMatrix, kernel, kernelWidth, imageIncrement);
460    m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4);
461    checkGLError("glDrawArrays");
462}
463
464static float gauss(float x, float sigma)
465{
466    return exp(- (x * x) / (2.0f * sigma * sigma));
467}
468
469static void buildKernel(float sigma, float* kernel, int kernelWidth)
470{
471    float halfWidth = (kernelWidth - 1.0f) / 2.0f;
472    float sum = 0.0f;
473    for (int i = 0; i < kernelWidth; ++i) {
474        kernel[i] = gauss(i - halfWidth, sigma);
475        sum += kernel[i];
476    }
477    // Normalize the kernel
478    float scale = 1.0f / sum;
479    for (int i = 0; i < kernelWidth; ++i)
480        kernel[i] *= scale;
481}
482
483void GLES2Canvas::drawTexturedQuad(const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha)
484{
485    AffineTransform matrix(m_flipMatrix);
486    matrix *= transform;
487    matrix.translate(dstRect.x(), dstRect.y());
488    matrix.scale(dstRect.width(), dstRect.height());
489
490    AffineTransform texMatrix;
491    texMatrix.scale(1.0f / textureSize.width(), 1.0f / textureSize.height());
492    texMatrix.translate(srcRect.x(), srcRect.y());
493    texMatrix.scale(srcRect.width(), srcRect.height());
494
495    m_context->useQuadVertices();
496    m_context->useTextureProgram(matrix, texMatrix, alpha);
497    m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4);
498    checkGLError("glDrawArrays");
499}
500
501void GLES2Canvas::drawTexturedQuadMitchell(const IntSize& textureSize, const FloatRect& srcRect, const FloatRect& dstRect, const AffineTransform& transform, float alpha)
502{
503    static const float mitchellCoefficients[16] = {
504         0.0f / 18.0f,   1.0f / 18.0f,  16.0f / 18.0f,   1.0f / 18.0f,
505         0.0f / 18.0f,   9.0f / 18.0f,   0.0f / 18.0f,  -9.0f / 18.0f,
506        -6.0f / 18.0f,  27.0f / 18.0f, -36.0f / 18.0f,  15.0f / 18.0f,
507         7.0f / 18.0f, -21.0f / 18.0f,  21.0f / 18.0f,  -7.0f / 18.0f,
508    };
509
510    AffineTransform matrix(m_flipMatrix);
511    matrix *= transform;
512    matrix.translate(dstRect.x(), dstRect.y());
513    matrix.scale(dstRect.width(), dstRect.height());
514
515    float imageIncrement[2] = { 1.0f / textureSize.width(), 1.0f / textureSize.height() };
516
517    AffineTransform texMatrix;
518    texMatrix.scale(imageIncrement[0], imageIncrement[1]);
519    texMatrix.translate(srcRect.x(), srcRect.y());
520    texMatrix.scale(srcRect.width(), srcRect.height());
521
522    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST);
523    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::NEAREST);
524
525    m_context->useQuadVertices();
526    m_context->useBicubicProgram(matrix, texMatrix, mitchellCoefficients, imageIncrement, alpha);
527    m_context->drawArrays(GraphicsContext3D::TRIANGLE_STRIP, 0, 4);
528    checkGLError("glDrawArrays");
529}
530
531void GLES2Canvas::setCompositeOperation(CompositeOperator op)
532{
533    m_state->m_compositeOp = op;
534}
535
536Texture* GLES2Canvas::createTexture(NativeImagePtr ptr, Texture::Format format, int width, int height)
537{
538    return m_context->createTexture(ptr, format, width, height);
539}
540
541Texture* GLES2Canvas::getTexture(NativeImagePtr ptr)
542{
543    return m_context->getTexture(ptr);
544}
545
546#if USE(SKIA)
547// This is actually cross-platform code, but since its only caller is inside a
548// USE(SKIA), it will cause a warning-as-error on Chrome/Mac.
549static void interpolateQuadratic(DoubleVector* vertices, const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2)
550{
551    float tIncrement = 1.0f / pathTesselation, t = tIncrement;
552    Quadratic c = Quadratic::fromBezier(p0, p1, p2);
553    for (int i = 0; i < pathTesselation; ++i, t += tIncrement) {
554        FloatPoint p = c.evaluate(t);
555        vertices->append(p.x());
556        vertices->append(p.y());
557        vertices->append(1.0);
558    }
559}
560
561static void interpolateCubic(DoubleVector* vertices, const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3)
562{
563    float tIncrement = 1.0f / pathTesselation, t = tIncrement;
564    Cubic c = Cubic::fromBezier(p0, p1, p2, p3);
565    for (int i = 0; i < pathTesselation; ++i, t += tIncrement) {
566        FloatPoint p = c.evaluate(t);
567        vertices->append(p.x());
568        vertices->append(p.y());
569        vertices->append(1.0);
570    }
571}
572#endif
573
574struct PolygonData {
575    PolygonData(FloatVector* vertices, WTF::Vector<short>* indices)
576      : m_vertices(vertices)
577      , m_indices(indices)
578    {
579    }
580    FloatVector* m_vertices;
581    WTF::Vector<short>* m_indices;
582};
583
584static void beginData(GLenum type, void* data)
585{
586    ASSERT(type == GL_TRIANGLES);
587}
588
589static void edgeFlagData(GLboolean flag, void* data)
590{
591}
592
593static void vertexData(void* vertexData, void* data)
594{
595    static_cast<PolygonData*>(data)->m_indices->append(reinterpret_cast<long>(vertexData));
596}
597
598static void endData(void* data)
599{
600}
601
602static void combineData(GLdouble coords[3], void* vertexData[4],
603                                 GLfloat weight[4], void **outData, void* data)
604{
605    PolygonData* polygonData = static_cast<PolygonData*>(data);
606    int index = polygonData->m_vertices->size() / 3;
607    polygonData->m_vertices->append(static_cast<float>(coords[0]));
608    polygonData->m_vertices->append(static_cast<float>(coords[1]));
609    polygonData->m_vertices->append(1.0f);
610    *outData = reinterpret_cast<void*>(index);
611}
612
613typedef void (*TESSCB)();
614
615void GLES2Canvas::createVertexBufferFromPath(const Path& path, int* count, unsigned* vertexBuffer, unsigned* indexBuffer)
616{
617    *vertexBuffer = m_context->graphicsContext3D()->createBuffer();
618    checkGLError("createVertexBufferFromPath, createBuffer");
619    *indexBuffer = m_context->graphicsContext3D()->createBuffer();
620    checkGLError("createVertexBufferFromPath, createBuffer");
621    DoubleVector inVertices;
622    WTF::Vector<size_t> contours;
623#if USE(SKIA)
624    const SkPath* skPath = path.platformPath();
625    SkPoint pts[4];
626    SkPath::Iter iter(*skPath, true);
627    SkPath::Verb verb;
628    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
629        switch (verb) {
630        case SkPath::kMove_Verb:
631            inVertices.append(pts[0].fX);
632            inVertices.append(pts[0].fY);
633            inVertices.append(1.0);
634            break;
635        case SkPath::kLine_Verb:
636            inVertices.append(pts[1].fX);
637            inVertices.append(pts[1].fY);
638            inVertices.append(1.0);
639            break;
640        case SkPath::kQuad_Verb:
641            interpolateQuadratic(&inVertices, pts[0], pts[1], pts[2]);
642            break;
643        case SkPath::kCubic_Verb:
644            interpolateCubic(&inVertices, pts[0], pts[1], pts[2], pts[3]);
645            break;
646        case SkPath::kClose_Verb:
647            contours.append(inVertices.size() / 3);
648            break;
649        case SkPath::kDone_Verb:
650            break;
651        }
652    }
653#else
654    ASSERT(!"Path extraction not implemented on this platform.");
655#endif
656
657    GLUtesselator* tess = internal_gluNewTess();
658    internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
659    internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
660    internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
661    internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
662    internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
663    internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
664    WTF::Vector<short> indices;
665    FloatVector vertices;
666    vertices.reserveInitialCapacity(inVertices.size());
667    PolygonData data(&vertices, &indices);
668    internal_gluTessBeginPolygon(tess, &data);
669    WTF::Vector<size_t>::const_iterator contour;
670    size_t i = 0;
671    for (contour = contours.begin(); contour != contours.end(); ++contour) {
672        internal_gluTessBeginContour(tess);
673        for (; i < *contour; ++i) {
674            vertices.append(inVertices[i * 3]);
675            vertices.append(inVertices[i * 3 + 1]);
676            vertices.append(1.0f);
677            internal_gluTessVertex(tess, &inVertices[i * 3], reinterpret_cast<void*>(i));
678        }
679        internal_gluTessEndContour(tess);
680    }
681    internal_gluTessEndPolygon(tess);
682    internal_gluDeleteTess(tess);
683
684    m_context->graphicsContext3D()->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, *vertexBuffer);
685    checkGLError("createVertexBufferFromPath, bindBuffer ARRAY_BUFFER");
686    m_context->graphicsContext3D()->bufferData(GraphicsContext3D::ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GraphicsContext3D::STREAM_DRAW);
687    checkGLError("createVertexBufferFromPath, bufferData ARRAY_BUFFER");
688
689    m_context->graphicsContext3D()->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, *indexBuffer);
690    checkGLError("createVertexBufferFromPath, bindBuffer ELEMENT_ARRAY_BUFFER");
691    m_context->graphicsContext3D()->bufferData(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(short), indices.data(), GraphicsContext3D::STREAM_DRAW);
692    checkGLError("createVertexBufferFromPath, bufferData ELEMENT_ARRAY_BUFFER");
693    *count = indices.size();
694}
695
696void GLES2Canvas::fillPathInternal(const Path& path, const Color& color)
697{
698    if (SharedGraphicsContext3D::useLoopBlinnForPathRendering()) {
699        m_pathCache.clear();
700        LoopBlinnPathProcessor processor;
701        processor.process(path, m_pathCache);
702        if (!m_pathVertexBuffer)
703            m_pathVertexBuffer = m_context->createBuffer();
704        m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_pathVertexBuffer);
705        int byteSizeOfVertices = 2 * m_pathCache.numberOfVertices() * sizeof(float);
706        int byteSizeOfTexCoords = 3 * m_pathCache.numberOfVertices() * sizeof(float);
707        int byteSizeOfInteriorVertices = 2 * m_pathCache.numberOfInteriorVertices() * sizeof(float);
708        m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER,
709                              byteSizeOfVertices + byteSizeOfTexCoords + byteSizeOfInteriorVertices,
710                              GraphicsContext3D::STATIC_DRAW);
711        m_context->bufferSubData(GraphicsContext3D::ARRAY_BUFFER, 0, byteSizeOfVertices, m_pathCache.vertices());
712        m_context->bufferSubData(GraphicsContext3D::ARRAY_BUFFER, byteSizeOfVertices, byteSizeOfTexCoords, m_pathCache.texcoords());
713        m_context->bufferSubData(GraphicsContext3D::ARRAY_BUFFER, byteSizeOfVertices + byteSizeOfTexCoords, byteSizeOfInteriorVertices, m_pathCache.interiorVertices());
714
715        AffineTransform matrix(m_flipMatrix);
716        matrix *= m_state->m_ctm;
717
718        // Draw the exterior
719        m_context->useLoopBlinnExteriorProgram(0, byteSizeOfVertices, matrix, color);
720        m_context->drawArrays(GraphicsContext3D::TRIANGLES, 0, m_pathCache.numberOfVertices());
721
722        // Draw the interior
723        m_context->useLoopBlinnInteriorProgram(byteSizeOfVertices + byteSizeOfTexCoords, matrix, color);
724        m_context->drawArrays(GraphicsContext3D::TRIANGLES, 0, m_pathCache.numberOfInteriorVertices());
725    } else {
726        int count;
727        unsigned vertexBuffer, indexBuffer;
728        createVertexBufferFromPath(path, &count, &vertexBuffer, &indexBuffer);
729
730        AffineTransform matrix(m_flipMatrix);
731        matrix *= m_state->m_ctm;
732
733        m_context->graphicsContext3D()->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, vertexBuffer);
734        checkGLError("bindBuffer");
735        m_context->graphicsContext3D()->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, indexBuffer);
736        checkGLError("bindBuffer");
737
738        m_context->useFillSolidProgram(matrix, color);
739        checkGLError("useFillSolidProgram");
740
741        m_context->graphicsContext3D()->drawElements(GraphicsContext3D::TRIANGLES, count, GraphicsContext3D::UNSIGNED_SHORT, 0);
742        checkGLError("drawArrays");
743
744        m_context->graphicsContext3D()->deleteBuffer(vertexBuffer);
745        checkGLError("deleteBuffer");
746
747        m_context->graphicsContext3D()->deleteBuffer(indexBuffer);
748        checkGLError("deleteBuffer");
749    }
750}
751
752FloatRect GLES2Canvas::flipRect(const FloatRect& rect)
753{
754    FloatRect flippedRect(rect);
755    flippedRect.setY(m_size.height() - rect.y());
756    flippedRect.setHeight(-rect.height());
757    return flippedRect;
758}
759
760void GLES2Canvas::clearBorders(const FloatRect& rect, int width)
761{
762    scissorClear(rect.x(), rect.y() - width, rect.width() + width, width);
763    scissorClear(rect.maxX(), rect.y(), width, rect.height() + width);
764    scissorClear(rect.x() - width, rect.maxY(), rect.width() + width, width);
765    scissorClear(rect.x() - width, rect.y() - width, width, rect.height() + width);
766}
767
768void GLES2Canvas::beginShadowDraw()
769{
770    float offsetX = m_state->m_shadowOffset.width();
771    float offsetY = m_state->m_shadowOffset.height();
772    save();
773    if (m_state->m_shadowsIgnoreTransforms) {
774        AffineTransform newCTM;
775        newCTM.translate(offsetX, -offsetY);
776        newCTM *= m_state->m_ctm;
777        m_state->m_ctm = newCTM;
778    } else
779        m_state->m_ctm.translate(offsetX, offsetY);
780
781    if (m_state->m_shadowBlur > 0) {
782        // Draw hard shadow to offscreen buffer 0.
783        DrawingBuffer* dstBuffer = m_context->getOffscreenBuffer(0, m_size);
784        dstBuffer->bind();
785        m_context->applyCompositeOperator(CompositeCopy);
786        applyClipping(false);
787        m_context->clearColor(Color(RGBA32(0)));
788        m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
789    } else {
790        bindFramebuffer();
791        m_context->applyCompositeOperator(m_state->m_compositeOp);
792        applyClipping(m_state->clippingEnabled());
793    }
794}
795
796void GLES2Canvas::endShadowDraw(const FloatRect& boundingBox)
797{
798    if (m_state->m_shadowBlur > 0) {
799        // Buffer 0 contains the primitive drawn with a hard shadow.
800        DrawingBuffer* srcBuffer = m_context->getOffscreenBuffer(0, m_size);
801        DrawingBuffer* dstBuffer = m_context->getOffscreenBuffer(1, m_size);
802
803        float sigma = m_state->m_shadowBlur * 0.333333f;
804        FloatRect shadowBoundingBox(m_state->m_ctm.mapRect(boundingBox));
805        FloatRect rect(FloatPoint(0, 0), m_size);
806        FloatRect srcRect(shadowBoundingBox);
807
808        int scaleFactor = 1;
809        while (sigma > cMaxSigma) {
810            srcRect.scale(0.5f);
811            scaleFactor *= 2;
812            sigma *= 0.5f;
813        }
814        srcRect = enclosingIntRect(srcRect);
815        srcRect.scale(scaleFactor);
816        for (int i = 1; i < scaleFactor; i *= 2) {
817            dstBuffer->bind();
818            m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, srcBuffer->colorBuffer());
819            m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
820            m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
821            m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
822            m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
823            FloatRect dstRect(srcRect);
824            dstRect.scale(0.5f);
825            // Clear out 1 pixel border for linear filtering.
826            clearBorders(dstRect, 1);
827            drawTexturedQuad(srcBuffer->size(), flipRect(srcRect), dstRect, AffineTransform(), 1.0);
828            srcRect = dstRect;
829            std::swap(srcBuffer, dstBuffer);
830        }
831
832        int halfWidth = static_cast<int>(sigma * 3.0f);
833        int kernelWidth = halfWidth * 2 + 1;
834        OwnArrayPtr<float> kernel = adoptArrayPtr(new float[kernelWidth]);
835        buildKernel(sigma, kernel.get(), kernelWidth);
836
837        if (scaleFactor > 1) {
838            scissorClear(srcRect.maxX(), srcRect.y(), kernelWidth, srcRect.height());
839            scissorClear(srcRect.x() - kernelWidth, srcRect.y(), kernelWidth, srcRect.height());
840        }
841
842        // Blur in X offscreen.
843        dstBuffer->bind();
844        srcRect.inflateX(halfWidth);
845        srcRect.intersect(rect);
846        float imageIncrementX[2] = {1.0f / srcBuffer->size().width(), 0.0f};
847        convolveRect(srcBuffer->colorBuffer(), srcBuffer->size(), flipRect(srcRect), srcRect, imageIncrementX, kernel.get(), kernelWidth);
848
849        if (scaleFactor > 1) {
850            scissorClear(srcRect.x(), srcRect.maxY(), srcRect.width(), kernelWidth);
851            scissorClear(srcRect.x(), srcRect.y() - kernelWidth, srcRect.width(), kernelWidth);
852        }
853        srcRect.inflateY(halfWidth);
854        srcRect.intersect(rect);
855        std::swap(srcBuffer, dstBuffer);
856
857        float imageIncrementY[2] = {0.0f, 1.0f / srcBuffer->size().height()};
858        if (scaleFactor > 1) {
859            // Blur in Y offscreen.
860            dstBuffer->bind();
861            convolveRect(srcBuffer->colorBuffer(), srcBuffer->size(), flipRect(srcRect), srcRect, imageIncrementY, kernel.get(), kernelWidth);
862            // Clear out 2 pixel border for bicubic filtering.
863            clearBorders(srcRect, 2);
864            std::swap(srcBuffer, dstBuffer);
865
866            // Upsample srcBuffer -> main framebuffer using bicubic filtering.
867            bindFramebuffer();
868            m_context->applyCompositeOperator(m_state->m_compositeOp);
869            applyClipping(m_state->clippingEnabled());
870            m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, srcBuffer->colorBuffer());
871            FloatRect dstRect = srcRect;
872            dstRect.scale(scaleFactor);
873            drawTexturedQuadMitchell(srcBuffer->size(), flipRect(srcRect), dstRect, AffineTransform(), 1.0);
874        } else {
875            // Blur in Y directly to framebuffer.
876            bindFramebuffer();
877            m_context->applyCompositeOperator(m_state->m_compositeOp);
878            applyClipping(m_state->clippingEnabled());
879
880            convolveRect(srcBuffer->colorBuffer(), srcBuffer->size(), flipRect(srcRect), srcRect, imageIncrementY, kernel.get(), kernelWidth);
881        }
882    }
883    restore();
884}
885
886void GLES2Canvas::beginStencilDraw(unsigned op)
887{
888    // Turn on stencil test.
889    m_context->enableStencil(true);
890    checkGLError("enable STENCIL_TEST");
891
892    // Stencil test never passes, so colorbuffer is not drawn.
893    m_context->graphicsContext3D()->stencilFunc(GraphicsContext3D::NEVER, 1, 1);
894    checkGLError("stencilFunc");
895
896    // All writes incremement the stencil buffer.
897    m_context->graphicsContext3D()->stencilOp(op, op, op);
898    checkGLError("stencilOp");
899}
900
901void GLES2Canvas::applyClipping(bool enable)
902{
903    m_context->enableStencil(enable);
904    if (enable) {
905        // Enable drawing only where stencil is non-zero.
906        m_context->graphicsContext3D()->stencilFunc(GraphicsContext3D::EQUAL, m_state->m_numClippingPaths, -1);
907        checkGLError("stencilFunc");
908        // Keep all stencil values the same.
909        m_context->graphicsContext3D()->stencilOp(GraphicsContext3D::KEEP,
910                                                  GraphicsContext3D::KEEP,
911                                                  GraphicsContext3D::KEEP);
912        checkGLError("stencilOp");
913    }
914}
915
916void GLES2Canvas::checkGLError(const char* header)
917{
918#ifndef NDEBUG
919    unsigned err;
920    while ((err = m_context->getError()) != GraphicsContext3D::NO_ERROR) {
921        const char* errorStr = "*** UNKNOWN ERROR ***";
922        switch (err) {
923        case GraphicsContext3D::INVALID_ENUM:
924            errorStr = "GraphicsContext3D::INVALID_ENUM";
925            break;
926        case GraphicsContext3D::INVALID_VALUE:
927            errorStr = "GraphicsContext3D::INVALID_VALUE";
928            break;
929        case GraphicsContext3D::INVALID_OPERATION:
930            errorStr = "GraphicsContext3D::INVALID_OPERATION";
931            break;
932        case GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION:
933            errorStr = "GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION";
934            break;
935        case GraphicsContext3D::OUT_OF_MEMORY:
936            errorStr = "GraphicsContext3D::OUT_OF_MEMORY";
937            break;
938        }
939        if (header)
940            LOG_ERROR("%s:  %s", header, errorStr);
941        else
942            LOG_ERROR("%s", errorStr);
943    }
944#endif
945}
946
947}
948