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