| /* |
| * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. |
| * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved. |
| * Copyright (C) 2012 Company 100, Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * 2. Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
| * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
| * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
| * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "core/platform/graphics/filters/custom/CustomFilterRenderer.h" |
| |
| #include "core/platform/graphics/GraphicsContext3D.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterArrayParameter.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterCompiledProgram.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterConstants.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterMesh.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterNumberParameter.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterParameter.h" |
| #include "core/platform/graphics/filters/custom/CustomFilterTransformParameter.h" |
| #include "platform/transforms/TransformationMatrix.h" |
| |
| namespace WebCore { |
| |
| static void orthogonalProjectionMatrix(TransformationMatrix& matrix, float left, float right, float bottom, float top) |
| { |
| ASSERT(matrix.isIdentity()); |
| |
| float deltaX = right - left; |
| float deltaY = top - bottom; |
| if (!deltaX || !deltaY) |
| return; |
| matrix.setM11(2.0f / deltaX); |
| matrix.setM41(-(right + left) / deltaX); |
| matrix.setM22(2.0f / deltaY); |
| matrix.setM42(-(top + bottom) / deltaY); |
| |
| // Use big enough near/far values, so that simple rotations of rather large objects will not |
| // get clipped. 10000 should cover most of the screen resolutions. |
| const float farValue = 10000; |
| const float nearValue = -10000; |
| matrix.setM33(-2.0f / (farValue - nearValue)); |
| matrix.setM43(- (farValue + nearValue) / (farValue - nearValue)); |
| matrix.setM44(1.0f); |
| } |
| |
| PassRefPtr<CustomFilterRenderer> CustomFilterRenderer::create(PassRefPtr<GraphicsContext3D> context, CustomFilterProgramType programType, const CustomFilterParameterList& parameters, |
| unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType) |
| { |
| return adoptRef(new CustomFilterRenderer(context, programType, parameters, meshRows, meshColumns, meshType)); |
| } |
| |
| CustomFilterRenderer::CustomFilterRenderer(PassRefPtr<GraphicsContext3D> context, CustomFilterProgramType programType, const CustomFilterParameterList& parameters, |
| unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType) |
| : m_context(context) |
| , m_programType(programType) |
| , m_parameters(parameters) |
| , m_meshRows(meshRows) |
| , m_meshColumns(meshColumns) |
| , m_meshType(meshType) |
| { |
| } |
| |
| CustomFilterRenderer::~CustomFilterRenderer() |
| { |
| } |
| |
| bool CustomFilterRenderer::premultipliedAlpha() const |
| { |
| return m_programType == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE; |
| } |
| |
| bool CustomFilterRenderer::programNeedsInputTexture() const |
| { |
| ASSERT(m_compiledProgram.get()); |
| return m_compiledProgram->samplerLocation() != -1; |
| } |
| |
| void CustomFilterRenderer::draw(Platform3DObject inputTexture, const IntSize& size) |
| { |
| // 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. |
| // https://bugs.webkit.org/show_bug.cgi?id=100107 |
| m_contextSize = size; |
| |
| bindProgramAndBuffers(inputTexture); |
| m_context->drawElements(GraphicsContext3D::TRIANGLES, m_mesh->indicesCount(), GraphicsContext3D::UNSIGNED_SHORT, 0); |
| unbindVertexAttributes(); |
| } |
| |
| void CustomFilterRenderer::setCompiledProgram(PassRefPtr<CustomFilterCompiledProgram> compiledProgram) |
| { |
| m_compiledProgram = compiledProgram; |
| } |
| |
| bool CustomFilterRenderer::prepareForDrawing() |
| { |
| m_context->makeContextCurrent(); |
| if (!m_compiledProgram || !m_compiledProgram->isInitialized()) |
| return false; |
| initializeMeshIfNeeded(); |
| return true; |
| } |
| |
| void CustomFilterRenderer::initializeMeshIfNeeded() |
| { |
| if (m_mesh.get()) |
| return; |
| |
| // FIXME: Sharing the mesh would just save the time needed to upload it to the GPU, so I assume we could |
| // benchmark that for performance. |
| // https://bugs.webkit.org/show_bug.cgi?id=88429 |
| m_mesh = CustomFilterMesh::create(m_context.get(), m_meshColumns, m_meshRows, FloatRect(0, 0, 1, 1), m_meshType); |
| } |
| |
| void CustomFilterRenderer::bindVertexAttribute(int attributeLocation, unsigned size, unsigned offset) |
| { |
| if (attributeLocation != -1) { |
| m_context->vertexAttribPointer(attributeLocation, size, GraphicsContext3D::FLOAT, false, m_mesh->bytesPerVertex(), offset); |
| m_context->enableVertexAttribArray(attributeLocation); |
| } |
| } |
| |
| void CustomFilterRenderer::unbindVertexAttribute(int attributeLocation) |
| { |
| if (attributeLocation != -1) |
| m_context->disableVertexAttribArray(attributeLocation); |
| } |
| |
| void CustomFilterRenderer::bindProgramArrayParameters(int uniformLocation, CustomFilterArrayParameter* arrayParameter) |
| { |
| unsigned parameterSize = arrayParameter->size(); |
| Vector<GC3Dfloat> floatVector; |
| |
| for (unsigned i = 0; i < parameterSize; ++i) |
| floatVector.append(arrayParameter->valueAt(i)); |
| |
| m_context->uniform1fv(uniformLocation, parameterSize, floatVector.data()); |
| } |
| |
| void CustomFilterRenderer::bindProgramNumberParameters(int uniformLocation, CustomFilterNumberParameter* numberParameter) |
| { |
| switch (numberParameter->size()) { |
| case 1: |
| m_context->uniform1f(uniformLocation, numberParameter->valueAt(0)); |
| break; |
| case 2: |
| m_context->uniform2f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1)); |
| break; |
| case 3: |
| m_context->uniform3f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2)); |
| break; |
| case 4: |
| m_context->uniform4f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2), numberParameter->valueAt(3)); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| } |
| |
| void CustomFilterRenderer::bindProgramTransformParameter(int uniformLocation, CustomFilterTransformParameter* transformParameter) |
| { |
| TransformationMatrix matrix; |
| if (m_contextSize.width() && m_contextSize.height()) { |
| // 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 |
| // 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 |
| // supposed to multiply to the left of the coordinates of the vertices. |
| // 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 |
| // in order to rotate around the middle of mesh. |
| matrix.scale3d(1.0 / m_contextSize.width(), 1.0 / m_contextSize.height(), 1); |
| transformParameter->applyTransform(matrix, m_contextSize); |
| matrix.scale3d(m_contextSize.width(), m_contextSize.height(), 1); |
| } |
| float glMatrix[16]; |
| matrix.toColumnMajorFloatArray(glMatrix); |
| m_context->uniformMatrix4fv(uniformLocation, 1, false, &glMatrix[0]); |
| } |
| |
| void CustomFilterRenderer::bindProgramParameters() |
| { |
| // FIXME: Find a way to reset uniforms that are not specified in CSS. This is needed to avoid using values |
| // set by other previous rendered filters. |
| // https://bugs.webkit.org/show_bug.cgi?id=76440 |
| |
| size_t parametersSize = m_parameters.size(); |
| for (size_t i = 0; i < parametersSize; ++i) { |
| CustomFilterParameter* parameter = m_parameters.at(i).get(); |
| int uniformLocation = m_compiledProgram->uniformLocationByName(parameter->name()); |
| if (uniformLocation == -1) |
| continue; |
| switch (parameter->parameterType()) { |
| case CustomFilterParameter::ARRAY: |
| bindProgramArrayParameters(uniformLocation, static_cast<CustomFilterArrayParameter*>(parameter)); |
| break; |
| case CustomFilterParameter::NUMBER: |
| bindProgramNumberParameters(uniformLocation, static_cast<CustomFilterNumberParameter*>(parameter)); |
| break; |
| case CustomFilterParameter::TRANSFORM: |
| bindProgramTransformParameter(uniformLocation, static_cast<CustomFilterTransformParameter*>(parameter)); |
| break; |
| } |
| } |
| } |
| |
| void CustomFilterRenderer::bindProgramAndBuffers(Platform3DObject inputTexture) |
| { |
| ASSERT(m_compiledProgram->isInitialized()); |
| |
| m_context->useProgram(m_compiledProgram->program()); |
| |
| if (programNeedsInputTexture()) { |
| // We should be binding the DOM element texture sampler only if the author is using the CSS mix function. |
| ASSERT(m_programType == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE); |
| ASSERT(m_compiledProgram->samplerLocation() != -1); |
| |
| m_context->activeTexture(GraphicsContext3D::TEXTURE0); |
| m_context->uniform1i(m_compiledProgram->samplerLocation(), 0); |
| m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, inputTexture); |
| m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR); |
| m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR); |
| m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE); |
| m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE); |
| } |
| |
| if (m_compiledProgram->projectionMatrixLocation() != -1) { |
| TransformationMatrix projectionMatrix; |
| orthogonalProjectionMatrix(projectionMatrix, -0.5, 0.5, -0.5, 0.5); |
| float glProjectionMatrix[16]; |
| projectionMatrix.toColumnMajorFloatArray(glProjectionMatrix); |
| m_context->uniformMatrix4fv(m_compiledProgram->projectionMatrixLocation(), 1, false, &glProjectionMatrix[0]); |
| } |
| |
| ASSERT(m_meshColumns); |
| ASSERT(m_meshRows); |
| |
| if (m_compiledProgram->meshSizeLocation() != -1) |
| m_context->uniform2f(m_compiledProgram->meshSizeLocation(), m_meshColumns, m_meshRows); |
| |
| if (m_compiledProgram->tileSizeLocation() != -1) |
| m_context->uniform2f(m_compiledProgram->tileSizeLocation(), 1.0 / m_meshColumns, 1.0 / m_meshRows); |
| |
| if (m_compiledProgram->meshBoxLocation() != -1) { |
| // FIXME: This will change when filter margins will be implemented, |
| // see https://bugs.webkit.org/show_bug.cgi?id=71400 |
| m_context->uniform4f(m_compiledProgram->meshBoxLocation(), -0.5, -0.5, 1.0, 1.0); |
| } |
| |
| if (m_compiledProgram->samplerSizeLocation() != -1) |
| m_context->uniform2f(m_compiledProgram->samplerSizeLocation(), m_contextSize.width(), m_contextSize.height()); |
| |
| m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_mesh->verticesBufferObject()); |
| m_context->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, m_mesh->elementsBufferObject()); |
| |
| bindVertexAttribute(m_compiledProgram->positionAttribLocation(), PositionAttribSize, PositionAttribOffset); |
| bindVertexAttribute(m_compiledProgram->texAttribLocation(), TexAttribSize, TexAttribOffset); |
| bindVertexAttribute(m_compiledProgram->meshAttribLocation(), MeshAttribSize, MeshAttribOffset); |
| if (m_meshType == MeshTypeDetached) |
| bindVertexAttribute(m_compiledProgram->triangleAttribLocation(), TriangleAttribSize, TriangleAttribOffset); |
| |
| bindProgramParameters(); |
| } |
| |
| void CustomFilterRenderer::unbindVertexAttributes() |
| { |
| unbindVertexAttribute(m_compiledProgram->positionAttribLocation()); |
| unbindVertexAttribute(m_compiledProgram->texAttribLocation()); |
| unbindVertexAttribute(m_compiledProgram->meshAttribLocation()); |
| if (m_meshType == MeshTypeDetached) |
| unbindVertexAttribute(m_compiledProgram->triangleAttribLocation()); |
| } |
| |
| } // namespace WebCore |