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