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