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