/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "graphics/tinygl/gl.h" #include "graphics/tinygl/zgl.h" #include "graphics/tinygl/zbuffer.h" #include "graphics/tinygl/pixelbuffer.h" #include "graphics/tinygl/texelbuffer.h" namespace TinyGL { #define ZB_POINT_ST_UNIT (1 << ZB_POINT_ST_FRAC_BITS) #define ZB_POINT_ST_FRAC_MASK (ZB_POINT_ST_UNIT - 1) TexelBuffer::TexelBuffer(uint width, uint height, uint textureSize) { assert(width); assert(height); assert(textureSize); _width = width; _height = height; _fracTextureUnit = textureSize << ZB_POINT_ST_FRAC_BITS; _fracTextureMask = _fracTextureUnit - 1; _widthRatio = (float) width / textureSize; _heightRatio = (float) height / textureSize; } static inline uint wrap(uint wrap_mode, int coord, uint _fracTextureUnit, uint _fracTextureMask) { switch (wrap_mode) { case TGL_MIRRORED_REPEAT: if (coord & _fracTextureUnit) return _fracTextureMask - (coord & _fracTextureMask); return coord & _fracTextureMask; case TGL_CLAMP_TO_EDGE: if (coord < 0) return 0; if ((uint) coord > _fracTextureMask) return _fracTextureMask; return coord; default: // Fall through case TGL_REPEAT: return coord & _fracTextureMask; } } void TexelBuffer::getARGBAt( uint wrap_s, uint wrap_t, int s, int t, uint8 &a, uint8 &r, uint8 &g, uint8 &b ) const { uint x, y; x = wrap(wrap_s, s, _fracTextureUnit, _fracTextureMask) * _widthRatio; y = wrap(wrap_t, t, _fracTextureUnit, _fracTextureMask) * _heightRatio; getARGBAt( (x >> ZB_POINT_ST_FRAC_BITS) + (y >> ZB_POINT_ST_FRAC_BITS) * _width, x & ZB_POINT_ST_FRAC_MASK, y & ZB_POINT_ST_FRAC_MASK, a, r, g, b ); } // Nearest: store texture in original size. BaseNearestTexelBuffer::BaseNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize) : TexelBuffer(width, height, textureSize), _format(format) { uint count = _width * _height * _format.bytesPerPixel; _buf = new byte[count]; memcpy(_buf, buf, count); } BaseNearestTexelBuffer::~BaseNearestTexelBuffer() { delete[] _buf; } // Bilinear: each texture coordinates corresponds to the 4 original image // pixels linear interpolation has to work on, so that they are near each // other in CPU data cache, and a single actual memory fetch happens. This // allows applying linear filtering at render time at a very low performance // cost. As we expect to work on small-ish textures (512*512 ?) the 4x memory // usage increase should be negligible. #define A_OFFSET (0 * 4) #define R_OFFSET (1 * 4) #define G_OFFSET (2 * 4) #define B_OFFSET (3 * 4) #define P00_OFFSET 0 #define P01_OFFSET 1 #define P10_OFFSET 2 #define P11_OFFSET 3 #define PIXEL_PER_TEXEL_SHIFT 2 BilinearTexelBuffer::BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize) : TexelBuffer(width, height, textureSize) { const Graphics::PixelBuffer src(format, buf); uint pixel00_offset = 0, pixel11_offset, pixel01_offset, pixel10_offset; uint8 *texel8; uint32 *texel32; texel32 = _texels = new uint32[_width * _height << PIXEL_PER_TEXEL_SHIFT]; for (uint y = 0; y < _height; y++) { for (uint x = 0; x < _width; x++) { texel8 = (uint8 *)texel32; pixel11_offset = pixel00_offset + _width + 1; src.getARGBAt( pixel00_offset, *(texel8 + P00_OFFSET + A_OFFSET), *(texel8 + P00_OFFSET + R_OFFSET), *(texel8 + P00_OFFSET + G_OFFSET), *(texel8 + P00_OFFSET + B_OFFSET) ); if ((x + 1) == _width) { pixel11_offset -= 1; pixel01_offset = pixel00_offset; } else pixel01_offset = pixel00_offset + 1; src.getARGBAt( pixel01_offset, *(texel8 + P01_OFFSET + A_OFFSET), *(texel8 + P01_OFFSET + R_OFFSET), *(texel8 + P01_OFFSET + G_OFFSET), *(texel8 + P01_OFFSET + B_OFFSET) ); if ((y + 1) == _height) { pixel11_offset -= _width; pixel10_offset = pixel00_offset; } else pixel10_offset = pixel00_offset + _width; src.getARGBAt( pixel10_offset, *(texel8 + P10_OFFSET + A_OFFSET), *(texel8 + P10_OFFSET + R_OFFSET), *(texel8 + P10_OFFSET + G_OFFSET), *(texel8 + P10_OFFSET + B_OFFSET) ); src.getARGBAt( pixel11_offset, *(texel8 + P11_OFFSET + A_OFFSET), *(texel8 + P11_OFFSET + R_OFFSET), *(texel8 + P11_OFFSET + G_OFFSET), *(texel8 + P11_OFFSET + B_OFFSET) ); texel32 += 1 << PIXEL_PER_TEXEL_SHIFT; pixel00_offset++; } } } BilinearTexelBuffer::~BilinearTexelBuffer() { delete[] _texels; } static inline int interpolate(int v00, int v01, int v10, int xf, int yf) { return v00 + (((v01 - v00) * xf + (v10 - v00) * yf) >> ZB_POINT_ST_FRAC_BITS); } void BilinearTexelBuffer::getARGBAt( uint pixel, uint ds, uint dt, uint8 &a, uint8 &r, uint8 &g, uint8 &b ) const { uint p00_offset, p01_offset, p10_offset; uint8 *texel = (uint8 *)(_texels + (pixel << PIXEL_PER_TEXEL_SHIFT)); if ((ds + dt) > ZB_POINT_ST_UNIT) { p00_offset = P11_OFFSET; p10_offset = P01_OFFSET; p01_offset = P10_OFFSET; ds = ZB_POINT_ST_UNIT - ds; dt = ZB_POINT_ST_UNIT - dt; } else { p00_offset = P00_OFFSET; p10_offset = P10_OFFSET; p01_offset = P01_OFFSET; } a = interpolate( *(texel + p00_offset + A_OFFSET), *(texel + p01_offset + A_OFFSET), *(texel + p10_offset + A_OFFSET), ds, dt ); r = interpolate( *(texel + p00_offset + R_OFFSET), *(texel + p01_offset + R_OFFSET), *(texel + p10_offset + R_OFFSET), ds, dt ); g = interpolate( *(texel + p00_offset + G_OFFSET), *(texel + p01_offset + G_OFFSET), *(texel + p10_offset + G_OFFSET), ds, dt ); b = interpolate( *(texel + p00_offset + B_OFFSET), *(texel + p01_offset + B_OFFSET), *(texel + p10_offset + B_OFFSET), ds, dt ); } } // end of namespace TinyGL