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 "GraphicsContextGPU.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 {
PathAndTransformWebCore::PathAndTransform67     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 GraphicsContextGPU::State {
StateWebCore::GraphicsContextGPU::State77     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     }
StateWebCore::GraphicsContextGPU::State88     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.
applyAlphaWebCore::GraphicsContextGPU::State115     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     }
shadowActiveWebCore::GraphicsContextGPU::State126     bool shadowActive() const
127     {
128         return m_shadowColor.alpha() > 0 && (m_shadowBlur || m_shadowOffset.width() || m_shadowOffset.height());
129     }
clippingEnabledWebCore::GraphicsContextGPU::State130     bool clippingEnabled() { return m_numClippingPaths > 0; }
131 };
132 
operator *(const FloatPoint & f,float scale)133 static inline FloatPoint operator*(const FloatPoint& f, float scale)
134 {
135     return FloatPoint(f.x() * scale, f.y() * scale);
136 }
137 
operator *(float scale,const FloatPoint & f)138 static inline FloatPoint operator*(float scale, const FloatPoint& f)
139 {
140     return FloatPoint(f.x() * scale, f.y() * scale);
141 }
142 
operator *(const FloatSize & f,float scale)143 static inline FloatSize operator*(const FloatSize& f, float scale)
144 {
145     return FloatSize(f.width() * scale, f.height() * scale);
146 }
147 
operator *(float scale,const FloatSize & f)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:
Quadratic(FloatPoint a,FloatPoint b,FloatPoint c)155     Quadratic(FloatPoint a, FloatPoint b, FloatPoint c) :
156         m_a(a), m_b(b), m_c(c)
157     {
158     }
fromBezier(FloatPoint p0,FloatPoint p1,FloatPoint p2)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     }
evaluate(float t)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:
Cubic(FloatPoint a,FloatPoint b,FloatPoint c,FloatPoint d)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     }
fromBezier(FloatPoint p0,FloatPoint p1,FloatPoint p2,FloatPoint p3)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     }
evaluate(float t)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 
GraphicsContextGPU(SharedGraphicsContext3D * context,DrawingBuffer * drawingBuffer,const IntSize & size)197 GraphicsContextGPU::GraphicsContextGPU(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 
~GraphicsContextGPU()212 GraphicsContextGPU::~GraphicsContextGPU()
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 
bindFramebuffer()220 void GraphicsContextGPU::bindFramebuffer()
221 {
222     m_drawingBuffer->bind();
223 }
224 
clearRect(const FloatRect & rect)225 void GraphicsContextGPU::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 
applyState()238 void GraphicsContextGPU::applyState()
239 {
240     bindFramebuffer();
241     m_context->applyCompositeOperator(m_state->m_compositeOp);
242     applyClipping(m_state->clippingEnabled());
243 }
244 
scissorClear(float x,float y,float width,float height)245 void GraphicsContextGPU::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 
fillPath(const Path & path)258 void GraphicsContextGPU::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 
fillRect(const FloatRect & rect,const Color & color,ColorSpace colorSpace)270 void GraphicsContextGPU::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 
fillRect(const FloatRect & rect)282 void GraphicsContextGPU::fillRect(const FloatRect& rect)
283 {
284     fillRect(rect, m_state->applyAlpha(m_state->m_fillColor), ColorSpaceDeviceRGB);
285 }
286 
fillRectInternal(const FloatRect & rect,const Color & color)287 void GraphicsContextGPU::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 
setFillColor(const Color & color,ColorSpace colorSpace)299 void GraphicsContextGPU::setFillColor(const Color& color, ColorSpace colorSpace)
300 {
301     m_state->m_fillColor = color;
302 }
303 
setAlpha(float alpha)304 void GraphicsContextGPU::setAlpha(float alpha)
305 {
306     m_state->m_alpha = alpha;
307 }
308 
setShadowColor(const Color & color,ColorSpace)309 void GraphicsContextGPU::setShadowColor(const Color& color, ColorSpace)
310 {
311     m_state->m_shadowColor = color;
312 }
313 
setShadowOffset(const FloatSize & offset)314 void GraphicsContextGPU::setShadowOffset(const FloatSize& offset)
315 {
316     m_state->m_shadowOffset = offset;
317 }
318 
setShadowBlur(float shadowBlur)319 void GraphicsContextGPU::setShadowBlur(float shadowBlur)
320 {
321     m_state->m_shadowBlur = shadowBlur;
322 }
323 
setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms)324 void GraphicsContextGPU::setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms)
325 {
326     m_state->m_shadowsIgnoreTransforms = shadowsIgnoreTransforms;
327 }
328 
translate(float x,float y)329 void GraphicsContextGPU::translate(float x, float y)
330 {
331     m_state->m_ctm.translate(x, y);
332 }
333 
rotate(float angleInRadians)334 void GraphicsContextGPU::rotate(float angleInRadians)
335 {
336     m_state->m_ctm.rotate(angleInRadians * (180.0f / M_PI));
337 }
338 
scale(const FloatSize & size)339 void GraphicsContextGPU::scale(const FloatSize& size)
340 {
341     m_state->m_ctm.scale(size.width(), size.height());
342 }
343 
concatCTM(const AffineTransform & affine)344 void GraphicsContextGPU::concatCTM(const AffineTransform& affine)
345 {
346     m_state->m_ctm *= affine;
347 }
348 
setCTM(const AffineTransform & affine)349 void GraphicsContextGPU::setCTM(const AffineTransform& affine)
350 {
351     m_state->m_ctm = affine;
352 }
353 
clipPath(const Path & path)354 void GraphicsContextGPU::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 
clipOut(const Path & path)366 void GraphicsContextGPU::clipOut(const Path& path)
367 {
368     ASSERT(!"clipOut is unsupported in GraphicsContextGPU.\n");
369 }
370 
save()371 void GraphicsContextGPU::save()
372 {
373     m_stateStack.append(State(m_stateStack.last()));
374     m_state = &m_stateStack.last();
375 }
376 
restore()377 void GraphicsContextGPU::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 
drawTexturedRect(unsigned texture,const IntSize & textureSize,const FloatRect & srcRect,const FloatRect & dstRect,ColorSpace colorSpace,CompositeOperator compositeOp)395 void GraphicsContextGPU::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 
drawTexturedRect(Texture * texture,const FloatRect & srcRect,const FloatRect & dstRect,ColorSpace colorSpace,CompositeOperator compositeOp)408 void GraphicsContextGPU::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 
drawTexturedRect(Texture * texture,const FloatRect & srcRect,const FloatRect & dstRect,const AffineTransform & transform,float alpha,ColorSpace colorSpace,CompositeOperator compositeOp,bool clip)414 void GraphicsContextGPU::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 
drawTexturedRectTile(Texture * texture,int tile,const FloatRect & srcRect,const FloatRect & dstRect,const AffineTransform & transform,float alpha)430 void GraphicsContextGPU::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 
convolveRect(unsigned texture,const IntSize & textureSize,const FloatRect & srcRect,const FloatRect & dstRect,float imageIncrement[2],const float * kernel,int kernelWidth)448 void GraphicsContextGPU::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 
gauss(float x,float sigma)471 static float gauss(float x, float sigma)
472 {
473     return exp(- (x * x) / (2.0f * sigma * sigma));
474 }
475 
buildKernel(float sigma,float * kernel,int kernelWidth)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 
drawTexturedQuad(const IntSize & textureSize,const FloatRect & srcRect,const FloatRect & dstRect,const AffineTransform & transform,float alpha)490 void GraphicsContextGPU::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 
drawTexturedQuadMitchell(const IntSize & textureSize,const FloatRect & srcRect,const FloatRect & dstRect,const AffineTransform & transform,float alpha)508 void GraphicsContextGPU::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 
setCompositeOperation(CompositeOperator op)538 void GraphicsContextGPU::setCompositeOperation(CompositeOperator op)
539 {
540     m_state->m_compositeOp = op;
541 }
542 
createTexture(NativeImagePtr ptr,Texture::Format format,int width,int height)543 Texture* GraphicsContextGPU::createTexture(NativeImagePtr ptr, Texture::Format format, int width, int height)
544 {
545     return m_context->createTexture(ptr, format, width, height);
546 }
547 
getTexture(NativeImagePtr ptr)548 Texture* GraphicsContextGPU::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.
interpolateQuadratic(FloatPointVector * vertices,const FloatPoint & p0,const FloatPoint & p1,const FloatPoint & p2)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 
interpolateCubic(FloatPointVector * vertices,const FloatPoint & p0,const FloatPoint & p1,const FloatPoint & p2,const FloatPoint & p3)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 {
PolygonDataWebCore::PolygonData574     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 
beginData(GLenum type,void * data)583 static void beginData(GLenum type, void* data)
584 {
585     ASSERT(type == GL_TRIANGLES);
586 }
587 
edgeFlagData(GLboolean flag,void * data)588 static void edgeFlagData(GLboolean flag, void* data)
589 {
590 }
591 
vertexData(void * vertexData,void * data)592 static void vertexData(void* vertexData, void* data)
593 {
594     static_cast<PolygonData*>(data)->m_indices->append(reinterpret_cast<long>(vertexData));
595 }
596 
endData(void * data)597 static void endData(void* data)
598 {
599 }
600 
combineData(GLdouble coords[3],void * vertexData[4],GLfloat weight[4],void ** outData,void * data)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 
tesselateAndFillPath(const Path & path,const Color & color)612 void GraphicsContextGPU::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 
fillPathInternal(const Path & path,const Color & color)709 void GraphicsContextGPU::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 
flipRect(const FloatRect & rect)743 FloatRect GraphicsContextGPU::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 
clearBorders(const FloatRect & rect,int width)751 void GraphicsContextGPU::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 
beginShadowDraw()759 void GraphicsContextGPU::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 
endShadowDraw(const FloatRect & boundingBox)785 void GraphicsContextGPU::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 
beginStencilDraw(unsigned op)870 void GraphicsContextGPU::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 
applyClipping(bool enable)885 void GraphicsContextGPU::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 
checkGLError(const char * header)900 void GraphicsContextGPU::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