Home | History | Annotate | Download | only in chromium
      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 
     58 namespace WebCore {
     59 
     60 // Number of line segments used to approximate bezier curves.
     61 const int pathTesselation = 30;
     62 typedef void (GLAPIENTRY *TESSCB)();
     63 typedef WTF::Vector<float> FloatVector;
     64 typedef WTF::Vector<FloatPoint> FloatPointVector;
     65 
     66 struct 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 
     76 struct 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 
    133 static inline FloatPoint operator*(const FloatPoint& f, float scale)
    134 {
    135     return FloatPoint(f.x() * scale, f.y() * scale);
    136 }
    137 
    138 static inline FloatPoint operator*(float scale, const FloatPoint& f)
    139 {
    140     return FloatPoint(f.x() * scale, f.y() * scale);
    141 }
    142 
    143 static inline FloatSize operator*(const FloatSize& f, float scale)
    144 {
    145     return FloatSize(f.width() * scale, f.height() * scale);
    146 }
    147 
    148 static inline FloatSize operator*(float scale, const FloatSize& f)
    149 {
    150     return FloatSize(f.width() * scale, f.height() * scale);
    151 }
    152 
    153 class 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 
    174 class 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 
    197 GLES2Canvas::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 
    212 GLES2Canvas::~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 
    220 void GLES2Canvas::bindFramebuffer()
    221 {
    222     m_drawingBuffer->bind();
    223 }
    224 
    225 void 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 
    238 void GLES2Canvas::applyState()
    239 {
    240     bindFramebuffer();
    241     m_context->applyCompositeOperator(m_state->m_compositeOp);
    242     applyClipping(m_state->clippingEnabled());
    243 }
    244 
    245 void 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 
    258 void 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 
    270 void 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 
    282 void GLES2Canvas::fillRect(const FloatRect& rect)
    283 {
    284     fillRect(rect, m_state->applyAlpha(m_state->m_fillColor), ColorSpaceDeviceRGB);
    285 }
    286 
    287 void 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 
    299 void GLES2Canvas::setFillColor(const Color& color, ColorSpace colorSpace)
    300 {
    301     m_state->m_fillColor = color;
    302 }
    303 
    304 void GLES2Canvas::setAlpha(float alpha)
    305 {
    306     m_state->m_alpha = alpha;
    307 }
    308 
    309 void GLES2Canvas::setShadowColor(const Color& color, ColorSpace)
    310 {
    311     m_state->m_shadowColor = color;
    312 }
    313 
    314 void GLES2Canvas::setShadowOffset(const FloatSize& offset)
    315 {
    316     m_state->m_shadowOffset = offset;
    317 }
    318 
    319 void GLES2Canvas::setShadowBlur(float shadowBlur)
    320 {
    321     m_state->m_shadowBlur = shadowBlur;
    322 }
    323 
    324 void GLES2Canvas::setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms)
    325 {
    326     m_state->m_shadowsIgnoreTransforms = shadowsIgnoreTransforms;
    327 }
    328 
    329 void GLES2Canvas::translate(float x, float y)
    330 {
    331     m_state->m_ctm.translate(x, y);
    332 }
    333 
    334 void GLES2Canvas::rotate(float angleInRadians)
    335 {
    336     m_state->m_ctm.rotate(angleInRadians * (180.0f / M_PI));
    337 }
    338 
    339 void GLES2Canvas::scale(const FloatSize& size)
    340 {
    341     m_state->m_ctm.scale(size.width(), size.height());
    342 }
    343 
    344 void GLES2Canvas::concatCTM(const AffineTransform& affine)
    345 {
    346     m_state->m_ctm *= affine;
    347 }
    348 
    349 void GLES2Canvas::setCTM(const AffineTransform& affine)
    350 {
    351     m_state->m_ctm = affine;
    352 }
    353 
    354 void 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 
    366 void GLES2Canvas::clipOut(const Path& path)
    367 {
    368     ASSERT(!"clipOut is unsupported in GLES2Canvas.\n");
    369 }
    370 
    371 void GLES2Canvas::save()
    372 {
    373     m_stateStack.append(State(m_stateStack.last()));
    374     m_state = &m_stateStack.last();
    375 }
    376 
    377 void 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 
    395 void 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 
    408 void 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 
    414 void 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 
    430 void 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 
    448 void 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 
    471 static float gauss(float x, float sigma)
    472 {
    473     return exp(- (x * x) / (2.0f * sigma * sigma));
    474 }
    475 
    476 static 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 
    490 void 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 
    508 void 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 
    538 void GLES2Canvas::setCompositeOperation(CompositeOperator op)
    539 {
    540     m_state->m_compositeOp = op;
    541 }
    542 
    543 Texture* GLES2Canvas::createTexture(NativeImagePtr ptr, Texture::Format format, int width, int height)
    544 {
    545     return m_context->createTexture(ptr, format, width, height);
    546 }
    547 
    548 Texture* 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.
    556 static 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 
    564 static 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 
    573 struct 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 
    583 static void beginData(GLenum type, void* data)
    584 {
    585     ASSERT(type == GL_TRIANGLES);
    586 }
    587 
    588 static void edgeFlagData(GLboolean flag, void* data)
    589 {
    590 }
    591 
    592 static void vertexData(void* vertexData, void* data)
    593 {
    594     static_cast<PolygonData*>(data)->m_indices->append(reinterpret_cast<long>(vertexData));
    595 }
    596 
    597 static void endData(void* data)
    598 {
    599 }
    600 
    601 static 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 
    610 typedef void (*TESSCB)();
    611 
    612 void 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 
    709 void 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 
    743 FloatRect 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 
    751 void 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 
    759 void 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 
    785 void 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 
    870 void 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 
    885 void 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 
    900 void 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