1/*
2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
3 * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
4 * Copyright (C) 2013 Google 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/FECustomFilter.h"
34
35#include "platform/graphics/Extensions3D.h"
36#include "platform/graphics/GraphicsContext3D.h"
37#include "platform/graphics/filters/custom/CustomFilterRenderer.h"
38#include "platform/graphics/filters/custom/CustomFilterValidatedProgram.h"
39#include "platform/text/TextStream.h"
40#include "wtf/Uint8ClampedArray.h"
41
42namespace WebCore {
43
44FECustomFilter::FECustomFilter(Filter* filter, PassRefPtr<GraphicsContext3D> context, PassRefPtr<CustomFilterValidatedProgram> validatedProgram, const CustomFilterParameterList& parameters,
45    unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
46    : FilterEffect(filter)
47    , m_context(context)
48    , m_validatedProgram(validatedProgram)
49    , m_inputTexture(0)
50    , m_frameBuffer(0)
51    , m_depthBuffer(0)
52    , m_destTexture(0)
53    , m_triedMultisampleBuffer(false)
54    , m_multisampleFrameBuffer(0)
55    , m_multisampleRenderBuffer(0)
56    , m_multisampleDepthBuffer(0)
57{
58    // We will not pass a CustomFilterCompiledProgram here, as we only want to compile it when we actually need it in the first paint.
59    m_customFilterRenderer = CustomFilterRenderer::create(m_context, m_validatedProgram->programInfo().programType(), parameters, meshRows, meshColumns, meshType);
60}
61
62PassRefPtr<FECustomFilter> FECustomFilter::create(Filter* filter, PassRefPtr<GraphicsContext3D> context, PassRefPtr<CustomFilterValidatedProgram> validatedProgram, const CustomFilterParameterList& parameters,
63    unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
64{
65    return adoptRef(new FECustomFilter(filter, context, validatedProgram, parameters, meshRows, meshColumns, meshType));
66}
67
68FECustomFilter::~FECustomFilter()
69{
70    deleteRenderBuffers();
71}
72
73void FECustomFilter::deleteRenderBuffers()
74{
75    ASSERT(m_context);
76    m_context->makeContextCurrent();
77    if (m_inputTexture) {
78        m_context->deleteTexture(m_inputTexture);
79        m_inputTexture = 0;
80    }
81    if (m_frameBuffer) {
82        // Make sure to unbind any framebuffer from the context first, otherwise
83        // some platforms might refuse to bind the same buffer id again.
84        m_context->bindFramebuffer(GL_FRAMEBUFFER, 0);
85        m_context->deleteFramebuffer(m_frameBuffer);
86        m_frameBuffer = 0;
87    }
88    if (m_depthBuffer) {
89        m_context->deleteRenderbuffer(m_depthBuffer);
90        m_depthBuffer = 0;
91    }
92    if (m_destTexture) {
93        m_context->deleteTexture(m_destTexture);
94        m_destTexture = 0;
95    }
96    deleteMultisampleRenderBuffers();
97}
98
99void FECustomFilter::deleteMultisampleRenderBuffers()
100{
101    if (m_multisampleFrameBuffer) {
102        // Make sure to unbind any framebuffer from the context first, otherwise
103        // some platforms might refuse to bind the same buffer id again.
104        m_context->bindFramebuffer(GL_FRAMEBUFFER, 0);
105        m_context->deleteFramebuffer(m_multisampleFrameBuffer);
106        m_multisampleFrameBuffer = 0;
107    }
108    if (m_multisampleRenderBuffer) {
109        m_context->deleteRenderbuffer(m_multisampleRenderBuffer);
110        m_multisampleRenderBuffer = 0;
111    }
112    if (m_multisampleDepthBuffer) {
113        m_context->deleteRenderbuffer(m_multisampleDepthBuffer);
114        m_multisampleDepthBuffer = 0;
115    }
116}
117
118void FECustomFilter::applySoftware()
119{
120    if (!applyShader())
121        clearShaderResult();
122}
123
124void FECustomFilter::clearShaderResult()
125{
126    clearResult();
127    Uint8ClampedArray* dstPixelArray = createUnmultipliedImageResult();
128    if (!dstPixelArray)
129        return;
130
131    FilterEffect* in = inputEffect(0);
132    setIsAlphaImage(in->isAlphaImage());
133    IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
134    in->copyUnmultipliedImage(dstPixelArray, effectDrawingRect);
135}
136
137void FECustomFilter::drawFilterMesh(Platform3DObject inputTexture)
138{
139    bool multisample = canUseMultisampleBuffers();
140    m_context->bindFramebuffer(GL_FRAMEBUFFER, multisample ? m_multisampleFrameBuffer : m_frameBuffer);
141    m_context->viewport(0, 0, m_contextSize.width(), m_contextSize.height());
142
143    m_context->clearColor(0, 0, 0, 0);
144    m_context->clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
145
146    m_customFilterRenderer->draw(inputTexture, m_contextSize);
147
148    if (multisample)
149        resolveMultisampleBuffer();
150}
151
152bool FECustomFilter::prepareForDrawing()
153{
154    m_context->makeContextCurrent();
155
156    // Lazily inject the compiled program into the CustomFilterRenderer.
157    if (!m_customFilterRenderer->compiledProgram())
158        m_customFilterRenderer->setCompiledProgram(m_validatedProgram->compiledProgram());
159
160    if (!m_customFilterRenderer->prepareForDrawing())
161        return false;
162
163    // Only allocate a texture if the program needs one and the caller doesn't allocate one by itself.
164    if ((m_customFilterRenderer->programNeedsInputTexture() && !ensureInputTexture()) || !ensureFrameBuffer())
165        return false;
166
167    return true;
168}
169
170bool FECustomFilter::applyShader()
171{
172    Uint8ClampedArray* dstPixelArray = m_customFilterRenderer->premultipliedAlpha() ? createPremultipliedImageResult() : createUnmultipliedImageResult();
173    if (!dstPixelArray)
174        return false;
175
176    if (!prepareForDrawing())
177        return false;
178
179    FilterEffect* in = inputEffect(0);
180    IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
181    IntSize newContextSize(effectDrawingRect.size());
182    if (!resizeContextIfNeeded(newContextSize))
183        return false;
184
185    bool needsInputTexture = m_customFilterRenderer->programNeedsInputTexture();
186    if (needsInputTexture) {
187        RefPtr<Uint8ClampedArray> srcPixelArray = in->asUnmultipliedImage(effectDrawingRect);
188        uploadInputTexture(srcPixelArray.get());
189    }
190    drawFilterMesh(needsInputTexture ? m_inputTexture : 0);
191
192    ASSERT(static_cast<size_t>(newContextSize.width() * newContextSize.height() * 4) == dstPixelArray->length());
193    m_context->readPixels(0, 0, newContextSize.width(), newContextSize.height(), GL_RGBA, GL_UNSIGNED_BYTE, dstPixelArray->data());
194
195    return true;
196}
197
198bool FECustomFilter::ensureInputTexture()
199{
200    if (!m_inputTexture)
201        m_inputTexture = m_context->createTexture();
202    return m_inputTexture;
203}
204
205void FECustomFilter::uploadInputTexture(Uint8ClampedArray* srcPixelArray)
206{
207    m_context->bindTexture(GL_TEXTURE_2D, m_inputTexture);
208    m_context->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_contextSize.width(), m_contextSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, srcPixelArray->data());
209}
210
211bool FECustomFilter::ensureFrameBuffer()
212{
213    if (!m_frameBuffer)
214        m_frameBuffer = m_context->createFramebuffer();
215    if (!m_depthBuffer)
216        m_depthBuffer = m_context->createRenderbuffer();
217    if (!m_destTexture)
218        m_destTexture = m_context->createTexture();
219    return m_frameBuffer && m_depthBuffer && m_destTexture;
220}
221
222bool FECustomFilter::createMultisampleBuffer()
223{
224    ASSERT(!m_triedMultisampleBuffer);
225    m_triedMultisampleBuffer = true;
226
227    Extensions3D* extensions = m_context->extensions();
228    if (!extensions
229        || !extensions->supports("GL_ANGLE_framebuffer_multisample")
230        || !extensions->supports("GL_ANGLE_framebuffer_blit")
231        || !extensions->supports("GL_OES_rgb8_rgba8"))
232        return false;
233
234    extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
235    extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
236    extensions->ensureEnabled("GL_OES_rgb8_rgba8");
237
238    if (!m_multisampleFrameBuffer)
239        m_multisampleFrameBuffer = m_context->createFramebuffer();
240    if (!m_multisampleRenderBuffer)
241        m_multisampleRenderBuffer = m_context->createRenderbuffer();
242    if (!m_multisampleDepthBuffer)
243        m_multisampleDepthBuffer = m_context->createRenderbuffer();
244
245    return true;
246}
247
248void FECustomFilter::resolveMultisampleBuffer()
249{
250    ASSERT(m_triedMultisampleBuffer && m_multisampleFrameBuffer && m_multisampleRenderBuffer && m_multisampleDepthBuffer);
251    m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFrameBuffer);
252    m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_frameBuffer);
253
254    ASSERT(m_context->extensions());
255    m_context->extensions()->blitFramebuffer(0, 0, m_contextSize.width(), m_contextSize.height(), 0, 0, m_contextSize.width(), m_contextSize.height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
256
257    m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, 0);
258    m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, 0);
259
260    m_context->bindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);
261}
262
263bool FECustomFilter::canUseMultisampleBuffers() const
264{
265    return m_triedMultisampleBuffer && m_multisampleFrameBuffer && m_multisampleRenderBuffer && m_multisampleDepthBuffer;
266}
267
268bool FECustomFilter::resizeMultisampleBuffers(const IntSize& newContextSize)
269{
270    if (!m_triedMultisampleBuffer && !createMultisampleBuffer())
271        return false;
272
273    if (!canUseMultisampleBuffers())
274        return false;
275
276    static const int kMaxSampleCount = 4;
277    int maxSupportedSampleCount = 0;
278    m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSupportedSampleCount);
279    int sampleCount = std::min(kMaxSampleCount, maxSupportedSampleCount);
280    if (!sampleCount) {
281        deleteMultisampleRenderBuffers();
282        return false;
283    }
284
285    Extensions3D* extensions = m_context->extensions();
286    ASSERT(extensions);
287
288    m_context->bindFramebuffer(GL_FRAMEBUFFER, m_multisampleFrameBuffer);
289
290    m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleRenderBuffer);
291    extensions->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, Extensions3D::RGBA8_OES, newContextSize.width(), newContextSize.height());
292    m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_multisampleRenderBuffer);
293
294    m_context->bindRenderbuffer(GL_RENDERBUFFER, m_multisampleDepthBuffer);
295    extensions->renderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, GL_DEPTH_COMPONENT16, newContextSize.width(), newContextSize.height());
296    m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_multisampleDepthBuffer);
297
298    m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
299
300    if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
301        deleteMultisampleRenderBuffers();
302        return false;
303    }
304
305    return true;
306}
307
308bool FECustomFilter::resizeContextIfNeeded(const IntSize& newContextSize)
309{
310    if (newContextSize.isEmpty())
311        return false;
312    if (m_contextSize == newContextSize)
313        return true;
314
315    int maxTextureSize = 0;
316    m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
317    if (newContextSize.height() > maxTextureSize || newContextSize.width() > maxTextureSize)
318        return false;
319
320    return resizeContext(newContextSize);
321}
322
323bool FECustomFilter::resizeContext(const IntSize& newContextSize)
324{
325    bool multisample = resizeMultisampleBuffers(newContextSize);
326
327    m_context->bindFramebuffer(GL_FRAMEBUFFER, m_frameBuffer);
328    m_context->bindTexture(GL_TEXTURE_2D, m_destTexture);
329    // We are going to clear the output buffer anyway, so we can safely initialize the destination texture with garbage data.
330    // FIXME: GraphicsContext3D::texImage2DDirect is not implemented on Chromium.
331    m_context->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newContextSize.width(), newContextSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
332    m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_destTexture, 0);
333
334    // We don't need the depth buffer for the texture framebuffer, if we already
335    // have a multisample buffer.
336    if (!multisample) {
337        m_context->bindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
338        m_context->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, newContextSize.width(), newContextSize.height());
339        m_context->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
340    }
341
342    if (m_context->checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
343        return false;
344
345    if (multisample) {
346        // Clear the framebuffer first, otherwise the first blit will fail.
347        m_context->clearColor(0, 0, 0, 0);
348        m_context->clear(GL_COLOR_BUFFER_BIT);
349    }
350
351    m_context->bindRenderbuffer(GL_RENDERBUFFER, 0);
352
353    m_contextSize = newContextSize;
354    return true;
355}
356
357TextStream& FECustomFilter::externalRepresentation(TextStream& ts, int indent) const
358{
359    writeIndent(ts, indent);
360    ts << "[feCustomFilter";
361    FilterEffect::externalRepresentation(ts);
362    ts << "]\n";
363    inputEffect(0)->externalRepresentation(ts, indent + 1);
364    return ts;
365}
366
367} // namespace WebCore
368