blob: 86981364513b8e6f0d7b2dda5bb8d1f872e7f576 [file] [log] [blame]
/*
* Copyright (c) 2010, Google 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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
* OWNER OR CONTRIBUTORS 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"
#if ENABLE(ACCELERATED_2D_CANVAS)
#include "Texture.h"
#include "Extensions3D.h"
#include "FloatRect.h"
#include "GraphicsContext3D.h"
#include "IntRect.h"
#include <algorithm>
#include <wtf/StdLibExtras.h>
#include <wtf/UniqueArray.h>
namespace WebCore {
Texture::Texture(GraphicsContext3D* context, std::unique_ptr<Vector<unsigned>> tileTextureIds, Format format, int width, int height, int maxTextureSize)
: m_context(context)
, m_format(format)
, m_tiles(IntSize(maxTextureSize, maxTextureSize), IntSize(width, height), true)
, m_tileTextureIds(WTFMove(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;
}
}
RefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, int width, int height)
{
int maxTextureSize = 0;
context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
TilingData tiling(IntSize(maxTextureSize, maxTextureSize), IntSize(width, height), true);
// Check for overflow.
int numTiles = tiling.numTilesX() * tiling.numTilesY();
if (numTiles / tiling.numTilesX() != tiling.numTilesY()) {
tiling.setTotalSize(IntSize());
numTiles = 0;
}
auto textureIds = std::make_unique<Vector<unsigned>>(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 nullptr;
}
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, WTFMove(textureIds), 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.totalSize().width(), m_tiles.totalSize().height()));
}
void Texture::updateSubRect(void* pixels, const IntRect& updateRect)
{
IntRect updateRectSanitized(updateRect);
updateRectSanitized.intersect(IntRect(0, 0, m_tiles.totalSize().width(), m_tiles.totalSize().height()));
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);
// FIXME: This could use PBO's to save doing an extra copy here.
}
int tempBuffSize = // Temporary buffer size is the smaller of the max texture size or the updateRectSanitized
std::min(m_tiles.maxTextureSize().width(), m_tiles.borderTexels() + updateRectSanitized.width()) *
std::min(m_tiles.maxTextureSize().height(), m_tiles.borderTexels() + updateRectSanitized.height());
auto tempBuff = makeUniqueArray<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();
// Intersect with tile
IntRect tileBoundsWithBorder = m_tiles.tileBoundsWithBorder(xIndex, yIndex);
IntRect updateRectIntersected = updateRectSanitized;
updateRectIntersected.intersect(tileBoundsWithBorder);
IntRect dstRect = updateRectIntersected;
dstRect.moveBy(-tileBoundsWithBorder.location());
if (updateRectIntersected.isEmpty())
continue;
// Copy sub rectangle out of larger pixel data
uint32_t* uploadBuff = 0;
if (swizzle) {
uploadBuff = copySubRect<true>(
pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSize().width());
} else {
uploadBuff = copySubRect<false>(
pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSize().width());
}
m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_tileTextureIds->at(tile));
m_context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0 /* level */,
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