1 /*
2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
3 * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
4 * Copyright (C) 2012 Company 100, Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials
16 * provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include "config.h"
33 #include "platform/graphics/filters/custom/CustomFilterRenderer.h"
34
35 #include "platform/graphics/GraphicsContext3D.h"
36 #include "platform/graphics/filters/custom/CustomFilterArrayParameter.h"
37 #include "platform/graphics/filters/custom/CustomFilterCompiledProgram.h"
38 #include "platform/graphics/filters/custom/CustomFilterMesh.h"
39 #include "platform/graphics/filters/custom/CustomFilterNumberParameter.h"
40 #include "platform/graphics/filters/custom/CustomFilterParameter.h"
41 #include "platform/graphics/filters/custom/CustomFilterTransformParameter.h"
42 #include "platform/transforms/TransformationMatrix.h"
43
44 namespace WebCore {
45
orthogonalProjectionMatrix(TransformationMatrix & matrix,float left,float right,float bottom,float top)46 static void orthogonalProjectionMatrix(TransformationMatrix& matrix, float left, float right, float bottom, float top)
47 {
48 ASSERT(matrix.isIdentity());
49
50 float deltaX = right - left;
51 float deltaY = top - bottom;
52 if (!deltaX || !deltaY)
53 return;
54 matrix.setM11(2.0f / deltaX);
55 matrix.setM41(-(right + left) / deltaX);
56 matrix.setM22(2.0f / deltaY);
57 matrix.setM42(-(top + bottom) / deltaY);
58
59 // Use big enough near/far values, so that simple rotations of rather large objects will not
60 // get clipped. 10000 should cover most of the screen resolutions.
61 const float farValue = 10000;
62 const float nearValue = -10000;
63 matrix.setM33(-2.0f / (farValue - nearValue));
64 matrix.setM43(- (farValue + nearValue) / (farValue - nearValue));
65 matrix.setM44(1.0f);
66 }
67
create(PassRefPtr<GraphicsContext3D> context,CustomFilterProgramType programType,const CustomFilterParameterList & parameters,unsigned meshRows,unsigned meshColumns,CustomFilterMeshType meshType)68 PassRefPtr<CustomFilterRenderer> CustomFilterRenderer::create(PassRefPtr<GraphicsContext3D> context, CustomFilterProgramType programType, const CustomFilterParameterList& parameters,
69 unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
70 {
71 return adoptRef(new CustomFilterRenderer(context, programType, parameters, meshRows, meshColumns, meshType));
72 }
73
CustomFilterRenderer(PassRefPtr<GraphicsContext3D> context,CustomFilterProgramType programType,const CustomFilterParameterList & parameters,unsigned meshRows,unsigned meshColumns,CustomFilterMeshType meshType)74 CustomFilterRenderer::CustomFilterRenderer(PassRefPtr<GraphicsContext3D> context, CustomFilterProgramType programType, const CustomFilterParameterList& parameters,
75 unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
76 : m_context(context)
77 , m_programType(programType)
78 , m_parameters(parameters)
79 , m_meshRows(meshRows)
80 , m_meshColumns(meshColumns)
81 , m_meshType(meshType)
82 {
83 }
84
~CustomFilterRenderer()85 CustomFilterRenderer::~CustomFilterRenderer()
86 {
87 }
88
premultipliedAlpha() const89 bool CustomFilterRenderer::premultipliedAlpha() const
90 {
91 return m_programType == ProgramTypeBlendsElementTexture;
92 }
93
programNeedsInputTexture() const94 bool CustomFilterRenderer::programNeedsInputTexture() const
95 {
96 ASSERT(m_compiledProgram.get());
97 return m_compiledProgram->samplerLocation() != -1;
98 }
99
draw(Platform3DObject inputTexture,const IntSize & size)100 void CustomFilterRenderer::draw(Platform3DObject inputTexture, const IntSize& size)
101 {
102 // FIXME: We would need something like CustomFilterRendererState that will contain the size and other parameters in the future. We should pass that to bindProgramBuffers instead of storing it.
103 // https://bugs.webkit.org/show_bug.cgi?id=100107
104 m_contextSize = size;
105
106 bindProgramAndBuffers(inputTexture);
107 m_context->drawElements(GL_TRIANGLES, m_mesh->indicesCount(), GL_UNSIGNED_SHORT, 0);
108 unbindVertexAttributes();
109 }
110
setCompiledProgram(PassRefPtr<CustomFilterCompiledProgram> compiledProgram)111 void CustomFilterRenderer::setCompiledProgram(PassRefPtr<CustomFilterCompiledProgram> compiledProgram)
112 {
113 m_compiledProgram = compiledProgram;
114 }
115
prepareForDrawing()116 bool CustomFilterRenderer::prepareForDrawing()
117 {
118 m_context->makeContextCurrent();
119 if (!m_compiledProgram || !m_compiledProgram->isInitialized())
120 return false;
121 initializeMeshIfNeeded();
122 return true;
123 }
124
initializeMeshIfNeeded()125 void CustomFilterRenderer::initializeMeshIfNeeded()
126 {
127 if (m_mesh.get())
128 return;
129
130 // FIXME: Sharing the mesh would just save the time needed to upload it to the GPU, so I assume we could
131 // benchmark that for performance.
132 // https://bugs.webkit.org/show_bug.cgi?id=88429
133 m_mesh = CustomFilterMesh::create(m_context.get(), m_meshColumns, m_meshRows, FloatRect(0, 0, 1, 1), m_meshType);
134 }
135
bindVertexAttribute(int attributeLocation,unsigned size,unsigned offset)136 void CustomFilterRenderer::bindVertexAttribute(int attributeLocation, unsigned size, unsigned offset)
137 {
138 if (attributeLocation != -1) {
139 m_context->vertexAttribPointer(attributeLocation, size, GL_FLOAT, false, m_mesh->bytesPerVertex(), offset);
140 m_context->enableVertexAttribArray(attributeLocation);
141 }
142 }
143
unbindVertexAttribute(int attributeLocation)144 void CustomFilterRenderer::unbindVertexAttribute(int attributeLocation)
145 {
146 if (attributeLocation != -1)
147 m_context->disableVertexAttribArray(attributeLocation);
148 }
149
bindProgramArrayParameters(int uniformLocation,CustomFilterArrayParameter * arrayParameter)150 void CustomFilterRenderer::bindProgramArrayParameters(int uniformLocation, CustomFilterArrayParameter* arrayParameter)
151 {
152 unsigned parameterSize = arrayParameter->size();
153 Vector<GC3Dfloat> floatVector;
154
155 for (unsigned i = 0; i < parameterSize; ++i)
156 floatVector.append(arrayParameter->valueAt(i));
157
158 m_context->uniform1fv(uniformLocation, parameterSize, floatVector.data());
159 }
160
bindProgramNumberParameters(int uniformLocation,CustomFilterNumberParameter * numberParameter)161 void CustomFilterRenderer::bindProgramNumberParameters(int uniformLocation, CustomFilterNumberParameter* numberParameter)
162 {
163 switch (numberParameter->size()) {
164 case 1:
165 m_context->uniform1f(uniformLocation, numberParameter->valueAt(0));
166 break;
167 case 2:
168 m_context->uniform2f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1));
169 break;
170 case 3:
171 m_context->uniform3f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2));
172 break;
173 case 4:
174 m_context->uniform4f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2), numberParameter->valueAt(3));
175 break;
176 default:
177 ASSERT_NOT_REACHED();
178 }
179 }
180
bindProgramTransformParameter(int uniformLocation,CustomFilterTransformParameter * transformParameter)181 void CustomFilterRenderer::bindProgramTransformParameter(int uniformLocation, CustomFilterTransformParameter* transformParameter)
182 {
183 TransformationMatrix matrix;
184 if (m_contextSize.width() && m_contextSize.height()) {
185 // The viewport is a box with the size of 1 unit, so we are scaling up here to make sure that translations happen using real pixel
186 // units. At the end we scale back down in order to map it back to the original box. Note that transforms come in reverse order, because it is
187 // supposed to multiply to the left of the coordinates of the vertices.
188 // Note that the origin (0, 0) of the viewport is in the middle of the context, so there's no need to change the origin of the transform
189 // in order to rotate around the middle of mesh.
190 matrix.scale3d(1.0 / m_contextSize.width(), 1.0 / m_contextSize.height(), 1);
191 transformParameter->applyTransform(matrix, m_contextSize);
192 matrix.scale3d(m_contextSize.width(), m_contextSize.height(), 1);
193 }
194 float glMatrix[16];
195 matrix.toColumnMajorFloatArray(glMatrix);
196 m_context->uniformMatrix4fv(uniformLocation, 1, false, &glMatrix[0]);
197 }
198
bindProgramParameters()199 void CustomFilterRenderer::bindProgramParameters()
200 {
201 // FIXME: Find a way to reset uniforms that are not specified in CSS. This is needed to avoid using values
202 // set by other previous rendered filters.
203 // https://bugs.webkit.org/show_bug.cgi?id=76440
204
205 size_t parametersSize = m_parameters.size();
206 for (size_t i = 0; i < parametersSize; ++i) {
207 CustomFilterParameter* parameter = m_parameters.at(i).get();
208 int uniformLocation = m_compiledProgram->uniformLocationByName(parameter->name());
209 if (uniformLocation == -1)
210 continue;
211 switch (parameter->parameterType()) {
212 case CustomFilterParameter::Array:
213 bindProgramArrayParameters(uniformLocation, static_cast<CustomFilterArrayParameter*>(parameter));
214 break;
215 case CustomFilterParameter::Number:
216 bindProgramNumberParameters(uniformLocation, static_cast<CustomFilterNumberParameter*>(parameter));
217 break;
218 case CustomFilterParameter::Transform:
219 bindProgramTransformParameter(uniformLocation, static_cast<CustomFilterTransformParameter*>(parameter));
220 break;
221 }
222 }
223 }
224
bindProgramAndBuffers(Platform3DObject inputTexture)225 void CustomFilterRenderer::bindProgramAndBuffers(Platform3DObject inputTexture)
226 {
227 ASSERT(m_compiledProgram->isInitialized());
228
229 m_context->useProgram(m_compiledProgram->program());
230
231 if (programNeedsInputTexture()) {
232 // We should be binding the DOM element texture sampler only if the author is using the CSS mix function.
233 ASSERT(m_programType == ProgramTypeBlendsElementTexture);
234 ASSERT(m_compiledProgram->samplerLocation() != -1);
235
236 m_context->activeTexture(GL_TEXTURE0);
237 m_context->uniform1i(m_compiledProgram->samplerLocation(), 0);
238 m_context->bindTexture(GL_TEXTURE_2D, inputTexture);
239 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
240 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
241 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
242 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
243 }
244
245 if (m_compiledProgram->projectionMatrixLocation() != -1) {
246 TransformationMatrix projectionMatrix;
247 orthogonalProjectionMatrix(projectionMatrix, -0.5, 0.5, -0.5, 0.5);
248 float glProjectionMatrix[16];
249 projectionMatrix.toColumnMajorFloatArray(glProjectionMatrix);
250 m_context->uniformMatrix4fv(m_compiledProgram->projectionMatrixLocation(), 1, false, &glProjectionMatrix[0]);
251 }
252
253 ASSERT(m_meshColumns);
254 ASSERT(m_meshRows);
255
256 if (m_compiledProgram->meshSizeLocation() != -1)
257 m_context->uniform2f(m_compiledProgram->meshSizeLocation(), m_meshColumns, m_meshRows);
258
259 if (m_compiledProgram->tileSizeLocation() != -1)
260 m_context->uniform2f(m_compiledProgram->tileSizeLocation(), 1.0 / m_meshColumns, 1.0 / m_meshRows);
261
262 if (m_compiledProgram->meshBoxLocation() != -1) {
263 // FIXME: This will change when filter margins will be implemented,
264 // see https://bugs.webkit.org/show_bug.cgi?id=71400
265 m_context->uniform4f(m_compiledProgram->meshBoxLocation(), -0.5, -0.5, 1.0, 1.0);
266 }
267
268 if (m_compiledProgram->samplerSizeLocation() != -1)
269 m_context->uniform2f(m_compiledProgram->samplerSizeLocation(), m_contextSize.width(), m_contextSize.height());
270
271 m_context->bindBuffer(GL_ARRAY_BUFFER, m_mesh->verticesBufferObject());
272 m_context->bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_mesh->elementsBufferObject());
273
274 bindVertexAttribute(m_compiledProgram->positionAttribLocation(), PositionAttribSize, PositionAttribOffset);
275 bindVertexAttribute(m_compiledProgram->texAttribLocation(), TexAttribSize, TexAttribOffset);
276 bindVertexAttribute(m_compiledProgram->meshAttribLocation(), MeshAttribSize, MeshAttribOffset);
277 if (m_meshType == MeshTypeDetached)
278 bindVertexAttribute(m_compiledProgram->triangleAttribLocation(), TriangleAttribSize, TriangleAttribOffset);
279
280 bindProgramParameters();
281 }
282
unbindVertexAttributes()283 void CustomFilterRenderer::unbindVertexAttributes()
284 {
285 unbindVertexAttribute(m_compiledProgram->positionAttribLocation());
286 unbindVertexAttribute(m_compiledProgram->texAttribLocation());
287 unbindVertexAttribute(m_compiledProgram->meshAttribLocation());
288 if (m_meshType == MeshTypeDetached)
289 unbindVertexAttribute(m_compiledProgram->triangleAttribLocation());
290 }
291
292 } // namespace WebCore
293