BitmapTextureGL.cpp [plain text]
#include "config.h"
#include "BitmapTextureGL.h"
#include "Extensions3D.h"
#include "FilterOperations.h"
#include "GraphicsContext.h"
#include "Image.h"
#include "LengthFunctions.h"
#include "NotImplemented.h"
#include "TextureMapperShaderProgram.h"
#include "Timer.h"
#include <wtf/HashMap.h>
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/TemporaryChange.h>
#if USE(CAIRO)
#include "CairoUtilities.h"
#include "RefPtrCairo.h"
#include <cairo.h>
#include <wtf/text/CString.h>
#endif
#if !USE(TEXMAP_OPENGL_ES_2)
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#define GL_UNPACK_SKIP_PIXELS 0x0CF4
#define GL_UNPACK_SKIP_ROWS 0x0CF3
#endif
namespace WebCore {
BitmapTextureGL* toBitmapTextureGL(BitmapTexture* texture)
{
if (!texture || !texture->isBackedByOpenGL())
return 0;
return static_cast<BitmapTextureGL*>(texture);
}
BitmapTextureGL::BitmapTextureGL(PassRefPtr<GraphicsContext3D> context3D)
: m_id(0)
, m_fbo(0)
, m_rbo(0)
, m_depthBufferObject(0)
, m_shouldClear(true)
, m_context3D(context3D)
{
}
bool BitmapTextureGL::canReuseWith(const IntSize& contentsSize, Flags)
{
return contentsSize == m_textureSize;
}
#if OS(DARWIN)
#define DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
#else
#define DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE GraphicsContext3D::UNSIGNED_BYTE
#endif
static void swizzleBGRAToRGBA(uint32_t* data, const IntRect& rect, int stride = 0)
{
stride = stride ? stride : rect.width();
for (int y = rect.y(); y < rect.maxY(); ++y) {
uint32_t* p = data + y * stride;
for (int x = rect.x(); x < rect.maxX(); ++x)
p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
}
}
static bool driverSupportsExternalTextureBGRA(GraphicsContext3D* context)
{
if (context->isGLES2Compliant()) {
static bool supportsExternalTextureBGRA = context->getExtensions()->supports("GL_EXT_texture_format_BGRA8888");
return supportsExternalTextureBGRA;
}
return true;
}
static bool driverSupportsSubImage(GraphicsContext3D* context)
{
if (context->isGLES2Compliant()) {
static bool supportsSubImage = context->getExtensions()->supports("GL_EXT_unpack_subimage");
return supportsSubImage;
}
return true;
}
void BitmapTextureGL::didReset()
{
if (!m_id)
m_id = m_context3D->createTexture();
m_shouldClear = true;
if (m_textureSize == contentSize())
return;
m_textureSize = contentSize();
m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
m_context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
Platform3DObject internalFormat = GraphicsContext3D::RGBA;
Platform3DObject externalFormat = GraphicsContext3D::BGRA;
if (m_context3D->isGLES2Compliant()) {
if (driverSupportsExternalTextureBGRA(m_context3D.get()))
internalFormat = GraphicsContext3D::BGRA;
else
externalFormat = GraphicsContext3D::RGBA;
}
m_context3D->texImage2DDirect(GraphicsContext3D::TEXTURE_2D, 0, internalFormat, m_textureSize.width(), m_textureSize.height(), 0, externalFormat, DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE, 0);
}
void BitmapTextureGL::updateContentsNoSwizzle(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, unsigned bytesPerPixel, Platform3DObject glFormat)
{
m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
if (driverSupportsSubImage(m_context3D.get())) {
m_context3D->pixelStorei(GL_UNPACK_ROW_LENGTH, bytesPerLine / bytesPerPixel);
m_context3D->pixelStorei(GL_UNPACK_SKIP_ROWS, sourceOffset.y());
m_context3D->pixelStorei(GL_UNPACK_SKIP_PIXELS, sourceOffset.x());
}
m_context3D->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(), glFormat, DEFAULT_TEXTURE_PIXEL_TRANSFER_TYPE, srcData);
if (driverSupportsSubImage(m_context3D.get())) {
m_context3D->pixelStorei(GL_UNPACK_ROW_LENGTH, 0);
m_context3D->pixelStorei(GL_UNPACK_SKIP_ROWS, 0);
m_context3D->pixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
}
}
void BitmapTextureGL::updateContents(const void* srcData, const IntRect& targetRect, const IntPoint& sourceOffset, int bytesPerLine, UpdateContentsFlag updateContentsFlag)
{
Platform3DObject glFormat = GraphicsContext3D::RGBA;
m_context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, m_id);
const unsigned bytesPerPixel = 4;
char* data = reinterpret_cast<char*>(const_cast<void*>(srcData));
Vector<char> temporaryData;
IntPoint adjustedSourceOffset = sourceOffset;
bool requireSubImageBuffer = !driverSupportsSubImage(m_context3D.get())
&& !(bytesPerLine == static_cast<int>(targetRect.width() * bytesPerPixel) && adjustedSourceOffset == IntPoint::zero());
if ((!driverSupportsExternalTextureBGRA(m_context3D.get()) && updateContentsFlag == UpdateCannotModifyOriginalImageData) || requireSubImageBuffer) {
temporaryData.resize(targetRect.width() * targetRect.height() * bytesPerPixel);
data = temporaryData.data();
const char* bits = static_cast<const char*>(srcData);
const char* src = bits + sourceOffset.y() * bytesPerLine + sourceOffset.x() * bytesPerPixel;
char* dst = data;
const int targetBytesPerLine = targetRect.width() * bytesPerPixel;
for (int y = 0; y < targetRect.height(); ++y) {
memcpy(dst, src, targetBytesPerLine);
src += bytesPerLine;
dst += targetBytesPerLine;
}
bytesPerLine = targetBytesPerLine;
adjustedSourceOffset = IntPoint(0, 0);
}
if (driverSupportsExternalTextureBGRA(m_context3D.get()))
glFormat = GraphicsContext3D::BGRA;
else
swizzleBGRAToRGBA(reinterpret_cast_ptr<uint32_t*>(data), IntRect(adjustedSourceOffset, targetRect.size()), bytesPerLine / bytesPerPixel);
updateContentsNoSwizzle(data, targetRect, adjustedSourceOffset, bytesPerLine, bytesPerPixel, glFormat);
}
void BitmapTextureGL::updateContents(Image* image, const IntRect& targetRect, const IntPoint& offset, UpdateContentsFlag updateContentsFlag)
{
if (!image)
return;
NativeImagePtr frameImage = image->nativeImageForCurrentFrame();
if (!frameImage)
return;
int bytesPerLine;
const char* imageData;
#if USE(CAIRO)
cairo_surface_t* surface = frameImage.get();
imageData = reinterpret_cast<const char*>(cairo_image_surface_get_data(surface));
bytesPerLine = cairo_image_surface_get_stride(surface);
#endif
updateContents(imageData, targetRect, offset, bytesPerLine, updateContentsFlag);
}
static unsigned getPassesRequiredForFilter(FilterOperation::OperationType type)
{
switch (type) {
case FilterOperation::GRAYSCALE:
case FilterOperation::SEPIA:
case FilterOperation::SATURATE:
case FilterOperation::HUE_ROTATE:
case FilterOperation::INVERT:
case FilterOperation::BRIGHTNESS:
case FilterOperation::CONTRAST:
case FilterOperation::OPACITY:
return 1;
case FilterOperation::BLUR:
case FilterOperation::DROP_SHADOW:
return 2;
default:
return 0;
}
}
PassRefPtr<BitmapTexture> BitmapTextureGL::applyFilters(TextureMapper* textureMapper, const FilterOperations& filters)
{
if (filters.isEmpty())
return this;
TextureMapperGL* texmapGL = static_cast<TextureMapperGL*>(textureMapper);
RefPtr<BitmapTexture> previousSurface = texmapGL->currentSurface();
RefPtr<BitmapTexture> resultSurface = this;
RefPtr<BitmapTexture> intermediateSurface;
RefPtr<BitmapTexture> spareSurface;
m_filterInfo = FilterInfo();
for (size_t i = 0; i < filters.size(); ++i) {
RefPtr<FilterOperation> filter = filters.operations()[i];
ASSERT(filter);
int numPasses = getPassesRequiredForFilter(filter->type());
for (int j = 0; j < numPasses; ++j) {
bool last = (i == filters.size() - 1) && (j == numPasses - 1);
if (!last) {
if (!intermediateSurface)
intermediateSurface = texmapGL->acquireTextureFromPool(contentSize());
texmapGL->bindSurface(intermediateSurface.get());
}
if (last) {
toBitmapTextureGL(resultSurface.get())->m_filterInfo = BitmapTextureGL::FilterInfo(filter, j, spareSurface);
break;
}
texmapGL->drawFiltered(*resultSurface.get(), spareSurface.get(), *filter, j);
if (!j && filter->type() == FilterOperation::DROP_SHADOW) {
spareSurface = resultSurface;
resultSurface = nullptr;
}
std::swap(resultSurface, intermediateSurface);
}
}
texmapGL->bindSurface(previousSurface.get());
return resultSurface;
}
void BitmapTextureGL::initializeStencil()
{
if (m_rbo)
return;
m_rbo = m_context3D->createRenderbuffer();
m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_rbo);
#ifdef TEXMAP_OPENGL_ES_2
m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::STENCIL_INDEX8, m_textureSize.width(), m_textureSize.height());
#else
m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_STENCIL, m_textureSize.width(), m_textureSize.height());
#endif
m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_rbo);
m_context3D->clearStencil(0);
m_context3D->clear(GraphicsContext3D::STENCIL_BUFFER_BIT);
}
void BitmapTextureGL::initializeDepthBuffer()
{
if (m_depthBufferObject)
return;
m_depthBufferObject = m_context3D->createRenderbuffer();
m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBufferObject);
m_context3D->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, m_textureSize.width(), m_textureSize.height());
m_context3D->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
m_context3D->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBufferObject);
}
void BitmapTextureGL::clearIfNeeded()
{
if (!m_shouldClear)
return;
m_clipStack.reset(IntRect(IntPoint::zero(), m_textureSize), TextureMapperGL::ClipStack::DefaultYAxis);
m_clipStack.applyIfNeeded(m_context3D.get());
m_context3D->clearColor(0, 0, 0, 0);
m_context3D->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
m_shouldClear = false;
}
void BitmapTextureGL::createFboIfNeeded()
{
if (m_fbo)
return;
m_fbo = m_context3D->createFramebuffer();
m_context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
m_context3D->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, id(), 0);
m_shouldClear = true;
}
void BitmapTextureGL::bindAsSurface(GraphicsContext3D* context3D)
{
context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, 0);
createFboIfNeeded();
context3D->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo);
context3D->viewport(0, 0, m_textureSize.width(), m_textureSize.height());
clearIfNeeded();
m_clipStack.apply(m_context3D.get());
}
BitmapTextureGL::~BitmapTextureGL()
{
if (m_id)
m_context3D->deleteTexture(m_id);
if (m_fbo)
m_context3D->deleteFramebuffer(m_fbo);
if (m_rbo)
m_context3D->deleteRenderbuffer(m_rbo);
if (m_depthBufferObject)
m_context3D->deleteRenderbuffer(m_depthBufferObject);
}
bool BitmapTextureGL::isValid() const
{
return m_id;
}
IntSize BitmapTextureGL::size() const
{
return m_textureSize;
}
};