#include "config.h"
#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(CSS_SHADERS)
#include "Texture.h"
#include "Extensions3D.h"
#include "FloatRect.h"
#include "GraphicsContext3D.h"
#include "IntRect.h"
#include <algorithm>
#include <wtf/OwnArrayPtr.h>
using namespace std;
namespace WebCore {
Texture::Texture(GraphicsContext3D* context, PassOwnPtr<Vector<unsigned int> > tileTextureIds, Format format, int width, int height, int maxTextureSize)
: m_context(context)
, m_format(format)
, m_tiles(maxTextureSize, width, height, true)
, m_tileTextureIds(tileTextureIds)
{
}
Texture::~Texture()
{
for (unsigned int i = 0; i < m_tileTextureIds->size(); i++)
m_context->deleteTexture(m_tileTextureIds->at(i));
}
static void convertFormat(GraphicsContext3D* context, Texture::Format format, unsigned int* glFormat, unsigned int* glType, bool* swizzle)
{
*swizzle = false;
switch (format) {
case Texture::RGBA8:
*glFormat = GraphicsContext3D::RGBA;
*glType = GraphicsContext3D::UNSIGNED_BYTE;
break;
case Texture::BGRA8:
if (context->getExtensions()->supports("GL_EXT_texture_format_BGRA8888")) {
*glFormat = Extensions3D::BGRA_EXT;
*glType = GraphicsContext3D::UNSIGNED_BYTE;
} else {
*glFormat = GraphicsContext3D::RGBA;
*glType = GraphicsContext3D::UNSIGNED_BYTE;
*swizzle = true;
}
break;
default:
ASSERT_NOT_REACHED();
break;
}
}
PassRefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, int width, int height)
{
int maxTextureSize = 0;
context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
TilingData tiling(maxTextureSize, width, height, true);
int numTiles = tiling.numTilesX() * tiling.numTilesY();
if (numTiles / tiling.numTilesX() != tiling.numTilesY()) {
tiling.setTotalSize(0, 0);
numTiles = 0;
}
OwnPtr<Vector<unsigned int> > textureIds = adoptPtr(new Vector<unsigned int>(numTiles));
textureIds->fill(0, numTiles);
for (int i = 0; i < numTiles; i++) {
int textureId = context->createTexture();
if (!textureId) {
for (int i = 0; i < numTiles; i++)
context->deleteTexture(textureIds->at(i));
return 0;
}
textureIds->at(i) = textureId;
int xIndex = i % tiling.numTilesX();
int yIndex = i / tiling.numTilesX();
IntRect tileBoundsWithBorder = tiling.tileBoundsWithBorder(xIndex, yIndex);
unsigned int glFormat = 0;
unsigned int glType = 0;
bool swizzle;
convertFormat(context, format, &glFormat, &glType, &swizzle);
context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId);
context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, glFormat,
tileBoundsWithBorder.width(),
tileBoundsWithBorder.height(),
0, glFormat, glType);
}
return adoptRef(new Texture(context, textureIds.release(), format, width, height, maxTextureSize));
}
template <bool swizzle>
static uint32_t* copySubRect(uint32_t* src, int srcX, int srcY, uint32_t* dst, int width, int height, int srcStride)
{
uint32_t* srcOffset = src + srcX + srcY * srcStride;
if (!swizzle && width == srcStride)
return srcOffset;
if (swizzle) {
uint32_t* dstPixel = dst;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width ; ++x) {
uint32_t pixel = srcOffset[x + y * srcStride];
*dstPixel = (pixel & 0xFF00FF00) | ((pixel & 0x00FF0000) >> 16) | ((pixel & 0x000000FF) << 16);
dstPixel++;
}
}
} else {
for (int y = 0; y < height; ++y) {
memcpy(dst + y * width, srcOffset + y * srcStride, 4 * width);
}
}
return dst;
}
void Texture::load(void* pixels)
{
updateSubRect(pixels, IntRect(0, 0, m_tiles.totalSizeX(), m_tiles.totalSizeY()));
}
void Texture::updateSubRect(void* pixels, const IntRect& updateRect)
{
IntRect updateRectSanitized(updateRect);
updateRectSanitized.intersect(IntRect(0, 0, m_tiles.totalSizeX(), m_tiles.totalSizeY()));
uint32_t* pixels32 = static_cast<uint32_t*>(pixels);
unsigned int glFormat = 0;
unsigned int glType = 0;
bool swizzle;
convertFormat(m_context, m_format, &glFormat, &glType, &swizzle);
if (swizzle) {
ASSERT(glFormat == GraphicsContext3D::RGBA && glType == GraphicsContext3D::UNSIGNED_BYTE);
}
int tempBuffSize = min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRectSanitized.width()) *
min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRectSanitized.height());
OwnArrayPtr<uint32_t> tempBuff = adoptArrayPtr(new uint32_t[tempBuffSize]);
for (int tile = 0; tile < m_tiles.numTilesX() * m_tiles.numTilesY(); tile++) {
int xIndex = tile % m_tiles.numTilesX();
int yIndex = tile / m_tiles.numTilesX();
IntRect tileBoundsWithBorder = m_tiles.tileBoundsWithBorder(xIndex, yIndex);
IntRect updateRectIntersected = updateRectSanitized;
updateRectIntersected.intersect(tileBoundsWithBorder);
IntRect dstRect = updateRectIntersected;
dstRect.moveBy(-tileBoundsWithBorder.location());
if (updateRectIntersected.isEmpty())
continue;
uint32_t* uploadBuff = 0;
if (swizzle) {
uploadBuff = copySubRect<true>(
pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSizeX());
} else {
uploadBuff = copySubRect<false>(
pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSizeX());
}
m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_tileTextureIds->at(tile));
m_context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0 ,
dstRect.x(),
dstRect.y(),
updateRectIntersected.width(),
updateRectIntersected.height(), glFormat, glType, uploadBuff);
}
}
void Texture::bindTile(int tile)
{
m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_tileTextureIds->at(tile));
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);
}
}
#endif