[CSS Shaders] Make CSS Shaders compile on Chromium
[WebKit-https.git] / Source / WebCore / platform / graphics / filters / FECustomFilter.cpp
1 /*
2  * Copyright (C) 2011 Adobe Systems Incorporated. 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
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include "config.h"
31
32 #if ENABLE(CSS_SHADERS) && ENABLE(WEBGL)
33 #include "FECustomFilter.h"
34
35 #include "CustomFilterMesh.h"
36 #include "CustomFilterNumberParameter.h"
37 #include "CustomFilterParameter.h"
38 #include "CustomFilterProgram.h"
39 #include "CustomFilterShader.h"
40 #include "DrawingBuffer.h"
41 #include "GraphicsContext3D.h"
42 #include "ImageData.h"
43 #include "RenderTreeAsText.h"
44 #include "TextStream.h"
45 #include "Texture.h"
46 #include "TilingData.h"
47 #include "TransformationMatrix.h"
48
49 #include <wtf/ByteArray.h>
50
51 namespace WebCore {
52
53 static void orthogonalProjectionMatrix(TransformationMatrix& matrix, float left, float right, float bottom, float top)
54 {
55     ASSERT(matrix.isIdentity());
56     
57     float deltaX = right - left;
58     float deltaY = top - bottom;
59     if (!deltaX || !deltaY)
60         return;
61     matrix.setM11(2.0f / deltaX);
62     matrix.setM41(-(right + left) / deltaX);
63     matrix.setM22(2.0f / deltaY);
64     matrix.setM42(-(top + bottom) / deltaY);
65
66     // Use big enough near/far values, so that simple rotations of rather large objects will not
67     // get clipped. 10000 should cover most of the screen resolutions.
68     const float farValue = 10000;
69     const float nearValue = -10000;
70     matrix.setM33(-2.0f / (farValue - nearValue));
71     matrix.setM43(- (farValue + nearValue) / (farValue - nearValue));
72     matrix.setM44(1.0f);
73 }
74
75 FECustomFilter::FECustomFilter(Filter* filter, HostWindow* hostWindow, PassRefPtr<CustomFilterProgram> program, const CustomFilterParameterList& parameters,
76                                unsigned meshRows, unsigned meshColumns, CustomFilterOperation::MeshBoxType meshBoxType,
77                                CustomFilterOperation::MeshType meshType)
78     : FilterEffect(filter)
79     , m_hostWindow(hostWindow)
80     , m_program(program)
81     , m_parameters(parameters)
82     , m_meshRows(meshRows)
83     , m_meshColumns(meshColumns)
84     , m_meshBoxType(meshBoxType)
85     , m_meshType(meshType)
86 {
87 }
88
89 PassRefPtr<FECustomFilter> FECustomFilter::create(Filter* filter, HostWindow* hostWindow, PassRefPtr<CustomFilterProgram> program, const CustomFilterParameterList& parameters,
90                                            unsigned meshRows, unsigned meshColumns, CustomFilterOperation::MeshBoxType meshBoxType,
91                                            CustomFilterOperation::MeshType meshType)
92 {
93     return adoptRef(new FECustomFilter(filter, hostWindow, program, parameters, meshRows, meshColumns, meshBoxType, meshType));
94 }
95
96 void FECustomFilter::platformApplySoftware()
97 {
98     ByteArray* dstPixelArray = createPremultipliedImageResult();
99     if (!dstPixelArray)
100         return;
101
102     FilterEffect* in = inputEffect(0);
103     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
104     RefPtr<ByteArray> srcPixelArray = in->asPremultipliedImage(effectDrawingRect);
105     
106     IntSize newContextSize(effectDrawingRect.size());
107     bool hadContext = m_context;
108     if (!m_context)
109         initializeContext(newContextSize);
110     
111     if (!hadContext || m_contextSize != newContextSize)
112         resizeContext(newContextSize);
113     
114     // Do not draw the filter if the input image cannot fit inside a single GPU texture.
115     if (m_inputTexture->tiles().numTiles() != 1)
116         return;
117     
118     // The shader had compiler errors. We cannot draw anything.
119     if (!m_shader->isInitialized())
120         return;
121     
122     m_context->clearColor(0, 0, 0, 0);
123     m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT | GraphicsContext3D::DEPTH_BUFFER_BIT);
124     
125     bindProgramAndBuffers(srcPixelArray.get());
126     
127     m_context->drawElements(GraphicsContext3D::TRIANGLES, m_mesh->indicesCount(), GraphicsContext3D::UNSIGNED_SHORT, 0);
128     
129     m_drawingBuffer->commit();
130
131     RefPtr<ImageData> imageData = m_context->paintRenderingResultsToImageData(m_drawingBuffer.get());
132     ByteArray* gpuResult = imageData->data()->data();
133     ASSERT(gpuResult->length() == dstPixelArray->length());
134     memcpy(dstPixelArray->data(), gpuResult->data(), gpuResult->length());
135 }
136
137 void FECustomFilter::initializeContext(const IntSize& contextSize)
138 {
139     GraphicsContext3D::Attributes attributes;
140     attributes.preserveDrawingBuffer = true;
141     attributes.premultipliedAlpha = false;
142     
143     ASSERT(!m_context.get());
144     m_context = GraphicsContext3D::create(attributes, m_hostWindow, GraphicsContext3D::RenderOffscreen);
145     m_drawingBuffer = DrawingBuffer::create(m_context.get(), contextSize, !attributes.preserveDrawingBuffer);
146     
147     m_shader = m_program->createShaderWithContext(m_context.get());
148     m_mesh = CustomFilterMesh::create(m_context.get(), m_meshColumns, m_meshRows, 
149                                       FloatRect(0, 0, 1, 1),
150                                       m_meshType);
151 }
152
153 void FECustomFilter::resizeContext(const IntSize& newContextSize)
154 {
155     m_inputTexture = 0;
156     m_drawingBuffer->reset(newContextSize);
157     m_context->reshape(newContextSize.width(), newContextSize.height());
158     m_context->viewport(0, 0, newContextSize.width(), newContextSize.height());
159     m_inputTexture = Texture::create(m_context.get(), Texture::RGBA8, newContextSize.width(), newContextSize.height());
160     m_contextSize = newContextSize;
161 }
162
163 void FECustomFilter::bindVertexAttribute(int attributeLocation, unsigned size, unsigned& offset)
164 {
165     if (attributeLocation != -1) {
166         m_context->vertexAttribPointer(attributeLocation, size, GraphicsContext3D::FLOAT, false, m_mesh->bytesPerVertex(), offset);
167         m_context->enableVertexAttribArray(attributeLocation);
168     }
169     offset += size * sizeof(float);
170 }
171
172 void FECustomFilter::bindProgramNumberParameters(int uniformLocation, CustomFilterNumberParameter* numberParameter)
173 {
174     switch (numberParameter->size()) {
175     case 1:
176         m_context->uniform1f(uniformLocation, numberParameter->valueAt(0));
177         break;
178     case 2:
179         m_context->uniform2f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1));
180         break;
181     case 3:
182         m_context->uniform3f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2));
183         break;
184     case 4:
185         m_context->uniform4f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2), numberParameter->valueAt(3));
186         break;
187     default:
188         ASSERT_NOT_REACHED();
189     }
190 }
191
192 void FECustomFilter::bindProgramParameters()
193 {
194     // FIXME: Find a way to reset uniforms that are not specified in CSS. This is needed to avoid using values
195     // set by other previous rendered filters.
196     // https://bugs.webkit.org/show_bug.cgi?id=76440
197     
198     size_t parametersSize = m_parameters.size();
199     for (size_t i = 0; i < parametersSize; ++i) {
200         CustomFilterParameter* parameter = m_parameters.at(i).get();
201         int uniformLocation = m_shader->uniformLocationByName(parameter->name());
202         if (uniformLocation == -1)
203             continue;
204         switch (parameter->parameterType()) {
205         case CustomFilterParameter::NUMBER:
206             bindProgramNumberParameters(uniformLocation, static_cast<CustomFilterNumberParameter*>(parameter));
207             break;
208         }
209     }
210 }
211
212 void FECustomFilter::bindProgramAndBuffers(ByteArray* srcPixelArray)
213 {
214     m_context->useProgram(m_shader->program());
215     
216     if (m_shader->samplerLocation() != -1) {
217         m_context->activeTexture(GraphicsContext3D::TEXTURE0);
218         m_context->uniform1i(m_shader->samplerLocation(), 0);
219         m_inputTexture->load(srcPixelArray->data());
220         m_inputTexture->bindTile(0);
221     }
222     
223     if (m_shader->projectionMatrixLocation() != -1) {
224         TransformationMatrix projectionMatrix; 
225 #if PLATFORM(CHROMIUM)
226         // We flip-y the projection matrix here because Chromium will flip-y the resulting image for us.
227         orthogonalProjectionMatrix(projectionMatrix, -0.5, 0.5, 0.5, -0.5);
228 #else
229         orthogonalProjectionMatrix(projectionMatrix, -0.5, 0.5, -0.5, 0.5);
230 #endif
231         float glProjectionMatrix[16];
232         projectionMatrix.toColumnMajorFloatArray(glProjectionMatrix);
233         m_context->uniformMatrix4fv(m_shader->projectionMatrixLocation(), 1, false, &glProjectionMatrix[0]);
234     }
235     
236     m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_mesh->verticesBufferObject());
237     m_context->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, m_mesh->elementsBufferObject());
238
239     unsigned offset = 0;
240     bindVertexAttribute(m_shader->positionAttribLocation(), 4, offset);
241     bindVertexAttribute(m_shader->texAttribLocation(), 2, offset);
242     bindVertexAttribute(m_shader->meshAttribLocation(), 2, offset);
243     if (m_meshType == CustomFilterOperation::DETACHED)
244         bindVertexAttribute(m_shader->triangleAttribLocation(), 3, offset);
245     
246     bindProgramParameters();
247 }
248
249 void FECustomFilter::dump()
250 {
251 }
252
253 TextStream& FECustomFilter::externalRepresentation(TextStream& ts, int indent) const
254 {
255     writeIndent(ts, indent);
256     ts << "[feCustomFilter";
257     FilterEffect::externalRepresentation(ts);
258     ts << "]\n";
259     inputEffect(0)->externalRepresentation(ts, indent + 1);
260     return ts;
261 }
262
263 } // namespace WebCore
264
265 #endif // ENABLE(CSS_SHADERS) && ENABLE(WEBGL)