1/*
2 * Copyright (c) 2010, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#if ENABLE(ACCELERATED_2D_CANVAS)
34
35#include "Texture.h"
36
37#include "Extensions3D.h"
38#include "FloatRect.h"
39#include "GraphicsContext3D.h"
40#include "IntRect.h"
41#include <algorithm>
42#include <wtf/StdLibExtras.h>
43
44namespace WebCore {
45
46Texture::Texture(GraphicsContext3D* context, PassOwnPtr<Vector<unsigned int>> tileTextureIds, Format format, int width, int height, int maxTextureSize)
47    : m_context(context)
48    , m_format(format)
49    , m_tiles(IntSize(maxTextureSize, maxTextureSize), IntSize(width, height), true)
50    , m_tileTextureIds(tileTextureIds)
51{
52}
53
54Texture::~Texture()
55{
56    for (unsigned int i = 0; i < m_tileTextureIds->size(); i++)
57        m_context->deleteTexture(m_tileTextureIds->at(i));
58}
59
60static void convertFormat(GraphicsContext3D* context, Texture::Format format, unsigned int* glFormat, unsigned int* glType, bool* swizzle)
61{
62    *swizzle = false;
63    switch (format) {
64    case Texture::RGBA8:
65        *glFormat = GraphicsContext3D::RGBA;
66        *glType = GraphicsContext3D::UNSIGNED_BYTE;
67        break;
68    case Texture::BGRA8:
69        if (context->getExtensions()->supports("GL_EXT_texture_format_BGRA8888")) {
70            *glFormat = Extensions3D::BGRA_EXT;
71            *glType = GraphicsContext3D::UNSIGNED_BYTE;
72        } else {
73            *glFormat = GraphicsContext3D::RGBA;
74            *glType = GraphicsContext3D::UNSIGNED_BYTE;
75            *swizzle = true;
76        }
77        break;
78    default:
79        ASSERT_NOT_REACHED();
80        break;
81    }
82}
83
84PassRefPtr<Texture> Texture::create(GraphicsContext3D* context, Format format, int width, int height)
85{
86    int maxTextureSize = 0;
87    context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
88    TilingData tiling(IntSize(maxTextureSize, maxTextureSize), IntSize(width, height), true);
89
90    // Check for overflow.
91    int numTiles = tiling.numTilesX() * tiling.numTilesY();
92    if (numTiles / tiling.numTilesX() != tiling.numTilesY()) {
93        tiling.setTotalSize(IntSize());
94        numTiles = 0;
95    }
96
97    OwnPtr<Vector<unsigned int>> textureIds = adoptPtr(new Vector<unsigned int>(numTiles));
98    textureIds->fill(0, numTiles);
99
100    for (int i = 0; i < numTiles; i++) {
101        int textureId = context->createTexture();
102        if (!textureId) {
103            for (int i = 0; i < numTiles; i++)
104                context->deleteTexture(textureIds->at(i));
105            return 0;
106        }
107        textureIds->at(i) = textureId;
108
109        int xIndex = i % tiling.numTilesX();
110        int yIndex = i / tiling.numTilesX();
111        IntRect tileBoundsWithBorder = tiling.tileBoundsWithBorder(xIndex, yIndex);
112
113        unsigned int glFormat = 0;
114        unsigned int glType = 0;
115        bool swizzle;
116        convertFormat(context, format, &glFormat, &glType, &swizzle);
117        context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId);
118        context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, glFormat,
119                                        tileBoundsWithBorder.width(),
120                                        tileBoundsWithBorder.height(),
121                                        0, glFormat, glType);
122    }
123    return adoptRef(new Texture(context, textureIds.release(), format, width, height, maxTextureSize));
124}
125
126template <bool swizzle>
127static uint32_t* copySubRect(uint32_t* src, int srcX, int srcY, uint32_t* dst, int width, int height, int srcStride)
128{
129    uint32_t* srcOffset = src + srcX + srcY * srcStride;
130
131    if (!swizzle && width == srcStride)
132        return srcOffset;
133
134    if (swizzle) {
135        uint32_t* dstPixel = dst;
136        for (int y = 0; y < height; ++y) {
137            for (int x = 0; x < width ; ++x) {
138                uint32_t pixel = srcOffset[x + y * srcStride];
139                *dstPixel = (pixel & 0xFF00FF00) | ((pixel & 0x00FF0000) >> 16) | ((pixel & 0x000000FF) << 16);
140                dstPixel++;
141            }
142        }
143    } else {
144        for (int y = 0; y < height; ++y) {
145            memcpy(dst + y * width, srcOffset + y * srcStride, 4 * width);
146        }
147    }
148    return dst;
149}
150
151void Texture::load(void* pixels)
152{
153    updateSubRect(pixels, IntRect(0, 0, m_tiles.totalSize().width(), m_tiles.totalSize().height()));
154}
155
156void Texture::updateSubRect(void* pixels, const IntRect& updateRect)
157{
158    IntRect updateRectSanitized(updateRect);
159    updateRectSanitized.intersect(IntRect(0, 0, m_tiles.totalSize().width(), m_tiles.totalSize().height()));
160
161    uint32_t* pixels32 = static_cast<uint32_t*>(pixels);
162    unsigned int glFormat = 0;
163    unsigned int glType = 0;
164    bool swizzle;
165    convertFormat(m_context, m_format, &glFormat, &glType, &swizzle);
166    if (swizzle) {
167        ASSERT(glFormat == GraphicsContext3D::RGBA && glType == GraphicsContext3D::UNSIGNED_BYTE);
168        // FIXME:  This could use PBO's to save doing an extra copy here.
169    }
170    int tempBuffSize = // Temporary buffer size is the smaller of the max texture size or the updateRectSanitized
171        std::min(m_tiles.maxTextureSize().width(), m_tiles.borderTexels() + updateRectSanitized.width()) *
172        std::min(m_tiles.maxTextureSize().height(), m_tiles.borderTexels() + updateRectSanitized.height());
173    auto tempBuff = std::make_unique<uint32_t[]>(tempBuffSize);
174
175    for (int tile = 0; tile < m_tiles.numTilesX() * m_tiles.numTilesY(); tile++) {
176        int xIndex = tile % m_tiles.numTilesX();
177        int yIndex = tile / m_tiles.numTilesX();
178
179        // Intersect with tile
180        IntRect tileBoundsWithBorder = m_tiles.tileBoundsWithBorder(xIndex, yIndex);
181
182        IntRect updateRectIntersected = updateRectSanitized;
183        updateRectIntersected.intersect(tileBoundsWithBorder);
184
185        IntRect dstRect = updateRectIntersected;
186        dstRect.moveBy(-tileBoundsWithBorder.location());
187
188        if (updateRectIntersected.isEmpty())
189            continue;
190
191        // Copy sub rectangle out of larger pixel data
192        uint32_t* uploadBuff = 0;
193        if (swizzle) {
194            uploadBuff = copySubRect<true>(
195            pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
196            tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSize().width());
197        } else {
198            uploadBuff = copySubRect<false>(
199            pixels32, updateRectIntersected.x(), updateRectIntersected.y(),
200            tempBuff.get(), updateRectIntersected.width(), updateRectIntersected.height(), m_tiles.totalSize().width());
201        }
202
203        m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_tileTextureIds->at(tile));
204        m_context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0 /* level */,
205            dstRect.x(),
206            dstRect.y(),
207            updateRectIntersected.width(),
208            updateRectIntersected.height(), glFormat, glType, uploadBuff);
209    }
210}
211
212void Texture::bindTile(int tile)
213{
214    m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_tileTextureIds->at(tile));
215    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
216    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
217    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
218    m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
219}
220
221}
222
223#endif
224