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