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