blob: 8ce207a73143b62884658f13f9c97928f87cde45 [file] [log] [blame]
// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Surface.hpp"
#include "Color.hpp"
#include "Context.hpp"
#include "ETC_Decoder.hpp"
#include "Renderer.hpp"
#include "Common/Half.hpp"
#include "Common/Memory.hpp"
#include "Common/CPUID.hpp"
#include "Common/Resource.hpp"
#include "Common/Debug.hpp"
#include "Reactor/Reactor.hpp"
#if defined(__i386__) || defined(__x86_64__)
#include <xmmintrin.h>
#include <emmintrin.h>
#endif
#undef min
#undef max
namespace sw
{
extern bool quadLayoutEnabled;
extern bool complementaryDepthBuffer;
extern TranscendentalPrecision logPrecision;
unsigned int *Surface::palette = 0;
unsigned int Surface::paletteID = 0;
void Surface::Buffer::write(int x, int y, int z, const Color<float> &color)
{
ASSERT((x >= -border) && (x < (width + border)));
ASSERT((y >= -border) && (y < (height + border)));
ASSERT((z >= 0) && (z < depth));
byte *element = (byte*)buffer + (x + border) * bytes + (y + border) * pitchB + z * samples * sliceB;
for(int i = 0; i < samples; i++)
{
write(element, color);
element += sliceB;
}
}
void Surface::Buffer::write(int x, int y, const Color<float> &color)
{
ASSERT((x >= -border) && (x < (width + border)));
ASSERT((y >= -border) && (y < (height + border)));
byte *element = (byte*)buffer + (x + border) * bytes + (y + border) * pitchB;
for(int i = 0; i < samples; i++)
{
write(element, color);
element += sliceB;
}
}
inline void Surface::Buffer::write(void *element, const Color<float> &color)
{
float r = color.r;
float g = color.g;
float b = color.b;
float a = color.a;
if(isSRGBformat(format))
{
r = linearToSRGB(r);
g = linearToSRGB(g);
b = linearToSRGB(b);
}
switch(format)
{
case FORMAT_A8:
*(unsigned char*)element = unorm<8>(a);
break;
case FORMAT_R8_SNORM:
*(char*)element = snorm<8>(r);
break;
case FORMAT_R8:
*(unsigned char*)element = unorm<8>(r);
break;
case FORMAT_R8I:
*(char*)element = scast<8>(r);
break;
case FORMAT_R8UI:
*(unsigned char*)element = ucast<8>(r);
break;
case FORMAT_R16I:
*(short*)element = scast<16>(r);
break;
case FORMAT_R16UI:
*(unsigned short*)element = ucast<16>(r);
break;
case FORMAT_R32I:
*(int*)element = static_cast<int>(r);
break;
case FORMAT_R32UI:
*(unsigned int*)element = static_cast<unsigned int>(r);
break;
case FORMAT_R3G3B2:
*(unsigned char*)element = (unorm<3>(r) << 5) | (unorm<3>(g) << 2) | (unorm<2>(b) << 0);
break;
case FORMAT_A8R3G3B2:
*(unsigned short*)element = (unorm<8>(a) << 8) | (unorm<3>(r) << 5) | (unorm<3>(g) << 2) | (unorm<2>(b) << 0);
break;
case FORMAT_X4R4G4B4:
*(unsigned short*)element = 0xF000 | (unorm<4>(r) << 8) | (unorm<4>(g) << 4) | (unorm<4>(b) << 0);
break;
case FORMAT_A4R4G4B4:
*(unsigned short*)element = (unorm<4>(a) << 12) | (unorm<4>(r) << 8) | (unorm<4>(g) << 4) | (unorm<4>(b) << 0);
break;
case FORMAT_R4G4B4A4:
*(unsigned short*)element = (unorm<4>(r) << 12) | (unorm<4>(g) << 8) | (unorm<4>(b) << 4) | (unorm<4>(a) << 0);
break;
case FORMAT_R5G6B5:
*(unsigned short*)element = (unorm<5>(r) << 11) | (unorm<6>(g) << 5) | (unorm<5>(b) << 0);
break;
case FORMAT_A1R5G5B5:
*(unsigned short*)element = (unorm<1>(a) << 15) | (unorm<5>(r) << 10) | (unorm<5>(g) << 5) | (unorm<5>(b) << 0);
break;
case FORMAT_R5G5B5A1:
*(unsigned short*)element = (unorm<5>(r) << 11) | (unorm<5>(g) << 6) | (unorm<5>(b) << 1) | (unorm<5>(a) << 0);
break;
case FORMAT_X1R5G5B5:
*(unsigned short*)element = 0x8000 | (unorm<5>(r) << 10) | (unorm<5>(g) << 5) | (unorm<5>(b) << 0);
break;
case FORMAT_A8R8G8B8:
*(unsigned int*)element = (unorm<8>(a) << 24) | (unorm<8>(r) << 16) | (unorm<8>(g) << 8) | (unorm<8>(b) << 0);
break;
case FORMAT_X8R8G8B8:
*(unsigned int*)element = 0xFF000000 | (unorm<8>(r) << 16) | (unorm<8>(g) << 8) | (unorm<8>(b) << 0);
break;
case FORMAT_A8B8G8R8_SNORM:
*(unsigned int*)element = (static_cast<unsigned int>(snorm<8>(a)) << 24) |
(static_cast<unsigned int>(snorm<8>(b)) << 16) |
(static_cast<unsigned int>(snorm<8>(g)) << 8) |
(static_cast<unsigned int>(snorm<8>(r)) << 0);
break;
case FORMAT_A8B8G8R8:
case FORMAT_SRGB8_A8:
*(unsigned int*)element = (unorm<8>(a) << 24) | (unorm<8>(b) << 16) | (unorm<8>(g) << 8) | (unorm<8>(r) << 0);
break;
case FORMAT_A8B8G8R8I:
*(unsigned int*)element = (static_cast<unsigned int>(scast<8>(a)) << 24) |
(static_cast<unsigned int>(scast<8>(b)) << 16) |
(static_cast<unsigned int>(scast<8>(g)) << 8) |
(static_cast<unsigned int>(scast<8>(r)) << 0);
break;
case FORMAT_A8B8G8R8UI:
*(unsigned int*)element = (ucast<8>(a) << 24) | (ucast<8>(b) << 16) | (ucast<8>(g) << 8) | (ucast<8>(r) << 0);
break;
case FORMAT_X8B8G8R8_SNORM:
*(unsigned int*)element = 0x7F000000 |
(static_cast<unsigned int>(snorm<8>(b)) << 16) |
(static_cast<unsigned int>(snorm<8>(g)) << 8) |
(static_cast<unsigned int>(snorm<8>(r)) << 0);
break;
case FORMAT_X8B8G8R8:
case FORMAT_SRGB8_X8:
*(unsigned int*)element = 0xFF000000 | (unorm<8>(b) << 16) | (unorm<8>(g) << 8) | (unorm<8>(r) << 0);
break;
case FORMAT_X8B8G8R8I:
*(unsigned int*)element = 0x7F000000 |
(static_cast<unsigned int>(scast<8>(b)) << 16) |
(static_cast<unsigned int>(scast<8>(g)) << 8) |
(static_cast<unsigned int>(scast<8>(r)) << 0);
case FORMAT_X8B8G8R8UI:
*(unsigned int*)element = 0xFF000000 | (ucast<8>(b) << 16) | (ucast<8>(g) << 8) | (ucast<8>(r) << 0);
break;
case FORMAT_A2R10G10B10:
*(unsigned int*)element = (unorm<2>(a) << 30) | (unorm<10>(r) << 20) | (unorm<10>(g) << 10) | (unorm<10>(b) << 0);
break;
case FORMAT_A2B10G10R10:
case FORMAT_A2B10G10R10UI:
*(unsigned int*)element = (unorm<2>(a) << 30) | (unorm<10>(b) << 20) | (unorm<10>(g) << 10) | (unorm<10>(r) << 0);
break;
case FORMAT_G8R8_SNORM:
*(unsigned short*)element = (static_cast<unsigned short>(snorm<8>(g)) << 8) |
(static_cast<unsigned short>(snorm<8>(r)) << 0);
break;
case FORMAT_G8R8:
*(unsigned short*)element = (unorm<8>(g) << 8) | (unorm<8>(r) << 0);
break;
case FORMAT_G8R8I:
*(unsigned short*)element = (static_cast<unsigned short>(scast<8>(g)) << 8) |
(static_cast<unsigned short>(scast<8>(r)) << 0);
break;
case FORMAT_G8R8UI:
*(unsigned short*)element = (ucast<8>(g) << 8) | (ucast<8>(r) << 0);
break;
case FORMAT_G16R16:
*(unsigned int*)element = (unorm<16>(g) << 16) | (unorm<16>(r) << 0);
break;
case FORMAT_G16R16I:
*(unsigned int*)element = (static_cast<unsigned int>(scast<16>(g)) << 16) |
(static_cast<unsigned int>(scast<16>(r)) << 0);
break;
case FORMAT_G16R16UI:
*(unsigned int*)element = (ucast<16>(g) << 16) | (ucast<16>(r) << 0);
break;
case FORMAT_G32R32I:
case FORMAT_G32R32UI:
((unsigned int*)element)[0] = static_cast<unsigned int>(r);
((unsigned int*)element)[1] = static_cast<unsigned int>(g);
break;
case FORMAT_A16B16G16R16:
((unsigned short*)element)[0] = unorm<16>(r);
((unsigned short*)element)[1] = unorm<16>(g);
((unsigned short*)element)[2] = unorm<16>(b);
((unsigned short*)element)[3] = unorm<16>(a);
break;
case FORMAT_A16B16G16R16I:
((unsigned short*)element)[0] = static_cast<unsigned short>(scast<16>(r));
((unsigned short*)element)[1] = static_cast<unsigned short>(scast<16>(g));
((unsigned short*)element)[2] = static_cast<unsigned short>(scast<16>(b));
((unsigned short*)element)[3] = static_cast<unsigned short>(scast<16>(a));
break;
case FORMAT_A16B16G16R16UI:
((unsigned short*)element)[0] = static_cast<unsigned short>(ucast<16>(r));
((unsigned short*)element)[1] = static_cast<unsigned short>(ucast<16>(g));
((unsigned short*)element)[2] = static_cast<unsigned short>(ucast<16>(b));
((unsigned short*)element)[3] = static_cast<unsigned short>(ucast<16>(a));
break;
case FORMAT_X16B16G16R16I:
((unsigned short*)element)[0] = static_cast<unsigned short>(scast<16>(r));
((unsigned short*)element)[1] = static_cast<unsigned short>(scast<16>(g));
((unsigned short*)element)[2] = static_cast<unsigned short>(scast<16>(b));
break;
case FORMAT_X16B16G16R16UI:
((unsigned short*)element)[0] = static_cast<unsigned short>(ucast<16>(r));
((unsigned short*)element)[1] = static_cast<unsigned short>(ucast<16>(g));
((unsigned short*)element)[2] = static_cast<unsigned short>(ucast<16>(b));
break;
case FORMAT_A32B32G32R32I:
case FORMAT_A32B32G32R32UI:
((unsigned int*)element)[0] = static_cast<unsigned int>(r);
((unsigned int*)element)[1] = static_cast<unsigned int>(g);
((unsigned int*)element)[2] = static_cast<unsigned int>(b);
((unsigned int*)element)[3] = static_cast<unsigned int>(a);
break;
case FORMAT_X32B32G32R32I:
case FORMAT_X32B32G32R32UI:
((unsigned int*)element)[0] = static_cast<unsigned int>(r);
((unsigned int*)element)[1] = static_cast<unsigned int>(g);
((unsigned int*)element)[2] = static_cast<unsigned int>(b);
break;
case FORMAT_V8U8:
*(unsigned short*)element = (snorm<8>(g) << 8) | (snorm<8>(r) << 0);
break;
case FORMAT_L6V5U5:
*(unsigned short*)element = (unorm<6>(b) << 10) | (snorm<5>(g) << 5) | (snorm<5>(r) << 0);
break;
case FORMAT_Q8W8V8U8:
*(unsigned int*)element = (snorm<8>(a) << 24) | (snorm<8>(b) << 16) | (snorm<8>(g) << 8) | (snorm<8>(r) << 0);
break;
case FORMAT_X8L8V8U8:
*(unsigned int*)element = 0xFF000000 | (unorm<8>(b) << 16) | (snorm<8>(g) << 8) | (snorm<8>(r) << 0);
break;
case FORMAT_V16U16:
*(unsigned int*)element = (snorm<16>(g) << 16) | (snorm<16>(r) << 0);
break;
case FORMAT_A2W10V10U10:
*(unsigned int*)element = (unorm<2>(a) << 30) | (snorm<10>(b) << 20) | (snorm<10>(g) << 10) | (snorm<10>(r) << 0);
break;
case FORMAT_A16W16V16U16:
((unsigned short*)element)[0] = snorm<16>(r);
((unsigned short*)element)[1] = snorm<16>(g);
((unsigned short*)element)[2] = snorm<16>(b);
((unsigned short*)element)[3] = unorm<16>(a);
break;
case FORMAT_Q16W16V16U16:
((unsigned short*)element)[0] = snorm<16>(r);
((unsigned short*)element)[1] = snorm<16>(g);
((unsigned short*)element)[2] = snorm<16>(b);
((unsigned short*)element)[3] = snorm<16>(a);
break;
case FORMAT_R8G8B8:
((unsigned char*)element)[0] = unorm<8>(b);
((unsigned char*)element)[1] = unorm<8>(g);
((unsigned char*)element)[2] = unorm<8>(r);
break;
case FORMAT_B8G8R8:
((unsigned char*)element)[0] = unorm<8>(r);
((unsigned char*)element)[1] = unorm<8>(g);
((unsigned char*)element)[2] = unorm<8>(b);
break;
case FORMAT_R16F:
*(half*)element = (half)r;
break;
case FORMAT_A16F:
*(half*)element = (half)a;
break;
case FORMAT_G16R16F:
((half*)element)[0] = (half)r;
((half*)element)[1] = (half)g;
break;
case FORMAT_X16B16G16R16F_UNSIGNED:
r = max(r, 0.0f); g = max(g, 0.0f); b = max(b, 0.0f);
// Fall through to FORMAT_X16B16G16R16F.
case FORMAT_X16B16G16R16F:
((half*)element)[3] = 1.0f;
// Fall through to FORMAT_B16G16R16F.
case FORMAT_B16G16R16F:
((half*)element)[0] = (half)r;
((half*)element)[1] = (half)g;
((half*)element)[2] = (half)b;
break;
case FORMAT_A16B16G16R16F:
((half*)element)[0] = (half)r;
((half*)element)[1] = (half)g;
((half*)element)[2] = (half)b;
((half*)element)[3] = (half)a;
break;
case FORMAT_A32F:
*(float*)element = a;
break;
case FORMAT_R32F:
*(float*)element = r;
break;
case FORMAT_G32R32F:
((float*)element)[0] = r;
((float*)element)[1] = g;
break;
case FORMAT_X32B32G32R32F_UNSIGNED:
r = max(r, 0.0f); g = max(g, 0.0f); b = max(b, 0.0f);
// Fall through to FORMAT_X32B32G32R32F.
case FORMAT_X32B32G32R32F:
((float*)element)[3] = 1.0f;
// Fall through to FORMAT_B32G32R32F.
case FORMAT_B32G32R32F:
((float*)element)[0] = r;
((float*)element)[1] = g;
((float*)element)[2] = b;
break;
case FORMAT_A32B32G32R32F:
((float*)element)[0] = r;
((float*)element)[1] = g;
((float*)element)[2] = b;
((float*)element)[3] = a;
break;
case FORMAT_D32F:
case FORMAT_D32FS8:
case FORMAT_D32F_LOCKABLE:
case FORMAT_D32FS8_TEXTURE:
case FORMAT_D32F_SHADOW:
case FORMAT_D32FS8_SHADOW:
*((float*)element) = r;
break;
case FORMAT_D32F_COMPLEMENTARY:
case FORMAT_D32FS8_COMPLEMENTARY:
*((float*)element) = 1 - r;
break;
case FORMAT_S8:
*((unsigned char*)element) = unorm<8>(r);
break;
case FORMAT_L8:
*(unsigned char*)element = unorm<8>(r);
break;
case FORMAT_A4L4:
*(unsigned char*)element = (unorm<4>(a) << 4) | (unorm<4>(r) << 0);
break;
case FORMAT_L16:
*(unsigned short*)element = unorm<16>(r);
break;
case FORMAT_A8L8:
*(unsigned short*)element = (unorm<8>(a) << 8) | (unorm<8>(r) << 0);
break;
case FORMAT_L16F:
*(half*)element = (half)r;
break;
case FORMAT_A16L16F:
((half*)element)[0] = (half)r;
((half*)element)[1] = (half)a;
break;
case FORMAT_L32F:
*(float*)element = r;
break;
case FORMAT_A32L32F:
((float*)element)[0] = r;
((float*)element)[1] = a;
break;
default:
ASSERT(false);
}
}
Color<float> Surface::Buffer::read(int x, int y, int z) const
{
ASSERT((x >= -border) && (x < (width + border)));
ASSERT((y >= -border) && (y < (height + border)));
ASSERT((z >= 0) && (z < depth));
void *element = (unsigned char*)buffer + (x + border) * bytes + (y + border) * pitchB + z * samples * sliceB;
return read(element);
}
Color<float> Surface::Buffer::read(int x, int y) const
{
ASSERT((x >= -border) && (x < (width + border)));
ASSERT((y >= -border) && (y < (height + border)));
void *element = (unsigned char*)buffer + (x + border) * bytes + (y + border) * pitchB;
return read(element);
}
inline Color<float> Surface::Buffer::read(void *element) const
{
float r = 0.0f;
float g = 0.0f;
float b = 0.0f;
float a = 1.0f;
switch(format)
{
case FORMAT_P8:
{
ASSERT(palette);
unsigned int abgr = palette[*(unsigned char*)element];
r = (abgr & 0x000000FF) * (1.0f / 0x000000FF);
g = (abgr & 0x0000FF00) * (1.0f / 0x0000FF00);
b = (abgr & 0x00FF0000) * (1.0f / 0x00FF0000);
a = (abgr & 0xFF000000) * (1.0f / 0xFF000000);
}
break;
case FORMAT_A8P8:
{
ASSERT(palette);
unsigned int bgr = palette[((unsigned char*)element)[0]];
r = (bgr & 0x000000FF) * (1.0f / 0x000000FF);
g = (bgr & 0x0000FF00) * (1.0f / 0x0000FF00);
b = (bgr & 0x00FF0000) * (1.0f / 0x00FF0000);
a = ((unsigned char*)element)[1] * (1.0f / 0xFF);
}
break;
case FORMAT_A8:
r = 0;
g = 0;
b = 0;
a = *(unsigned char*)element * (1.0f / 0xFF);
break;
case FORMAT_R8_SNORM:
r = max((*(signed char*)element) * (1.0f / 0x7F), -1.0f);
break;
case FORMAT_R8:
r = *(unsigned char*)element * (1.0f / 0xFF);
break;
case FORMAT_R8I:
r = *(signed char*)element;
break;
case FORMAT_R8UI:
r = *(unsigned char*)element;
break;
case FORMAT_R3G3B2:
{
unsigned char rgb = *(unsigned char*)element;
r = (rgb & 0xE0) * (1.0f / 0xE0);
g = (rgb & 0x1C) * (1.0f / 0x1C);
b = (rgb & 0x03) * (1.0f / 0x03);
}
break;
case FORMAT_A8R3G3B2:
{
unsigned short argb = *(unsigned short*)element;
a = (argb & 0xFF00) * (1.0f / 0xFF00);
r = (argb & 0x00E0) * (1.0f / 0x00E0);
g = (argb & 0x001C) * (1.0f / 0x001C);
b = (argb & 0x0003) * (1.0f / 0x0003);
}
break;
case FORMAT_X4R4G4B4:
{
unsigned short rgb = *(unsigned short*)element;
r = (rgb & 0x0F00) * (1.0f / 0x0F00);
g = (rgb & 0x00F0) * (1.0f / 0x00F0);
b = (rgb & 0x000F) * (1.0f / 0x000F);
}
break;
case FORMAT_A4R4G4B4:
{
unsigned short argb = *(unsigned short*)element;
a = (argb & 0xF000) * (1.0f / 0xF000);
r = (argb & 0x0F00) * (1.0f / 0x0F00);
g = (argb & 0x00F0) * (1.0f / 0x00F0);
b = (argb & 0x000F) * (1.0f / 0x000F);
}
break;
case FORMAT_R4G4B4A4:
{
unsigned short rgba = *(unsigned short*)element;
r = (rgba & 0xF000) * (1.0f / 0xF000);
g = (rgba & 0x0F00) * (1.0f / 0x0F00);
b = (rgba & 0x00F0) * (1.0f / 0x00F0);
a = (rgba & 0x000F) * (1.0f / 0x000F);
}
break;
case FORMAT_R5G6B5:
{
unsigned short rgb = *(unsigned short*)element;
r = (rgb & 0xF800) * (1.0f / 0xF800);
g = (rgb & 0x07E0) * (1.0f / 0x07E0);
b = (rgb & 0x001F) * (1.0f / 0x001F);
}
break;
case FORMAT_A1R5G5B5:
{
unsigned short argb = *(unsigned short*)element;
a = (argb & 0x8000) * (1.0f / 0x8000);
r = (argb & 0x7C00) * (1.0f / 0x7C00);
g = (argb & 0x03E0) * (1.0f / 0x03E0);
b = (argb & 0x001F) * (1.0f / 0x001F);
}
break;
case FORMAT_R5G5B5A1:
{
unsigned short rgba = *(unsigned short*)element;
r = (rgba & 0xF800) * (1.0f / 0xF800);
g = (rgba & 0x07C0) * (1.0f / 0x07C0);
b = (rgba & 0x003E) * (1.0f / 0x003E);
a = (rgba & 0x0001) * (1.0f / 0x0001);
}
break;
case FORMAT_X1R5G5B5:
{
unsigned short xrgb = *(unsigned short*)element;
r = (xrgb & 0x7C00) * (1.0f / 0x7C00);
g = (xrgb & 0x03E0) * (1.0f / 0x03E0);
b = (xrgb & 0x001F) * (1.0f / 0x001F);
}
break;
case FORMAT_A8R8G8B8:
{
unsigned int argb = *(unsigned int*)element;
a = (argb & 0xFF000000) * (1.0f / 0xFF000000);
r = (argb & 0x00FF0000) * (1.0f / 0x00FF0000);
g = (argb & 0x0000FF00) * (1.0f / 0x0000FF00);
b = (argb & 0x000000FF) * (1.0f / 0x000000FF);
}
break;
case FORMAT_X8R8G8B8:
{
unsigned int xrgb = *(unsigned int*)element;
r = (xrgb & 0x00FF0000) * (1.0f / 0x00FF0000);
g = (xrgb & 0x0000FF00) * (1.0f / 0x0000FF00);
b = (xrgb & 0x000000FF) * (1.0f / 0x000000FF);
}
break;
case FORMAT_A8B8G8R8_SNORM:
{
signed char* abgr = (signed char*)element;
r = max(abgr[0] * (1.0f / 0x7F), -1.0f);
g = max(abgr[1] * (1.0f / 0x7F), -1.0f);
b = max(abgr[2] * (1.0f / 0x7F), -1.0f);
a = max(abgr[3] * (1.0f / 0x7F), -1.0f);
}
break;
case FORMAT_A8B8G8R8:
case FORMAT_SRGB8_A8:
{
unsigned int abgr = *(unsigned int*)element;
a = (abgr & 0xFF000000) * (1.0f / 0xFF000000);
b = (abgr & 0x00FF0000) * (1.0f / 0x00FF0000);
g = (abgr & 0x0000FF00) * (1.0f / 0x0000FF00);
r = (abgr & 0x000000FF) * (1.0f / 0x000000FF);
}
break;
case FORMAT_A8B8G8R8I:
{
signed char* abgr = (signed char*)element;
r = abgr[0];
g = abgr[1];
b = abgr[2];
a = abgr[3];
}
break;
case FORMAT_A8B8G8R8UI:
{
unsigned char* abgr = (unsigned char*)element;
r = abgr[0];
g = abgr[1];
b = abgr[2];
a = abgr[3];
}
break;
case FORMAT_X8B8G8R8_SNORM:
{
signed char* bgr = (signed char*)element;
r = max(bgr[0] * (1.0f / 0x7F), -1.0f);
g = max(bgr[1] * (1.0f / 0x7F), -1.0f);
b = max(bgr[2] * (1.0f / 0x7F), -1.0f);
}
break;
case FORMAT_X8B8G8R8:
case FORMAT_SRGB8_X8:
{
unsigned int xbgr = *(unsigned int*)element;
b = (xbgr & 0x00FF0000) * (1.0f / 0x00FF0000);
g = (xbgr & 0x0000FF00) * (1.0f / 0x0000FF00);
r = (xbgr & 0x000000FF) * (1.0f / 0x000000FF);
}
break;
case FORMAT_X8B8G8R8I:
{
signed char* bgr = (signed char*)element;
r = bgr[0];
g = bgr[1];
b = bgr[2];
}
break;
case FORMAT_X8B8G8R8UI:
{
unsigned char* bgr = (unsigned char*)element;
r = bgr[0];
g = bgr[1];
b = bgr[2];
}
break;
case FORMAT_G8R8_SNORM:
{
signed char* gr = (signed char*)element;
r = (gr[0] & 0xFF00) * (1.0f / 0xFF00);
g = (gr[1] & 0x00FF) * (1.0f / 0x00FF);
}
break;
case FORMAT_G8R8:
{
unsigned short gr = *(unsigned short*)element;
g = (gr & 0xFF00) * (1.0f / 0xFF00);
r = (gr & 0x00FF) * (1.0f / 0x00FF);
}
break;
case FORMAT_G8R8I:
{
signed char* gr = (signed char*)element;
r = gr[0];
g = gr[1];
}
break;
case FORMAT_G8R8UI:
{
unsigned char* gr = (unsigned char*)element;
r = gr[0];
g = gr[1];
}
break;
case FORMAT_R16I:
r = *((short*)element);
break;
case FORMAT_R16UI:
r = *((unsigned short*)element);
break;
case FORMAT_G16R16I:
{
short* gr = (short*)element;
r = gr[0];
g = gr[1];
}
break;
case FORMAT_G16R16:
{
unsigned int gr = *(unsigned int*)element;
g = (gr & 0xFFFF0000) * (1.0f / 0xFFFF0000);
r = (gr & 0x0000FFFF) * (1.0f / 0x0000FFFF);
}
break;
case FORMAT_G16R16UI:
{
unsigned short* gr = (unsigned short*)element;
r = gr[0];
g = gr[1];
}
break;
case FORMAT_A2R10G10B10:
{
unsigned int argb = *(unsigned int*)element;
a = (argb & 0xC0000000) * (1.0f / 0xC0000000);
r = (argb & 0x3FF00000) * (1.0f / 0x3FF00000);
g = (argb & 0x000FFC00) * (1.0f / 0x000FFC00);
b = (argb & 0x000003FF) * (1.0f / 0x000003FF);
}
break;
case FORMAT_A2B10G10R10:
{
unsigned int abgr = *(unsigned int*)element;
a = (abgr & 0xC0000000) * (1.0f / 0xC0000000);
b = (abgr & 0x3FF00000) * (1.0f / 0x3FF00000);
g = (abgr & 0x000FFC00) * (1.0f / 0x000FFC00);
r = (abgr & 0x000003FF) * (1.0f / 0x000003FF);
}
break;
case FORMAT_A2B10G10R10UI:
{
unsigned int abgr = *(unsigned int*)element;
a = static_cast<float>((abgr & 0xC0000000) >> 30);
b = static_cast<float>((abgr & 0x3FF00000) >> 20);
g = static_cast<float>((abgr & 0x000FFC00) >> 10);
r = static_cast<float>(abgr & 0x000003FF);
}
break;
case FORMAT_A16B16G16R16I:
{
short* abgr = (short*)element;
r = abgr[0];
g = abgr[1];
b = abgr[2];
a = abgr[3];
}
break;
case FORMAT_A16B16G16R16:
r = ((unsigned short*)element)[0] * (1.0f / 0xFFFF);
g = ((unsigned short*)element)[1] * (1.0f / 0xFFFF);
b = ((unsigned short*)element)[2] * (1.0f / 0xFFFF);
a = ((unsigned short*)element)[3] * (1.0f / 0xFFFF);
break;
case FORMAT_A16B16G16R16UI:
{
unsigned short* abgr = (unsigned short*)element;
r = abgr[0];
g = abgr[1];
b = abgr[2];
a = abgr[3];
}
break;
case FORMAT_X16B16G16R16I:
{
short* bgr = (short*)element;
r = bgr[0];
g = bgr[1];
b = bgr[2];
}
break;
case FORMAT_X16B16G16R16UI:
{
unsigned short* bgr = (unsigned short*)element;
r = bgr[0];
g = bgr[1];
b = bgr[2];
}
break;
case FORMAT_A32B32G32R32I:
{
int* abgr = (int*)element;
r = static_cast<float>(abgr[0]);
g = static_cast<float>(abgr[1]);
b = static_cast<float>(abgr[2]);
a = static_cast<float>(abgr[3]);
}
break;
case FORMAT_A32B32G32R32UI:
{
unsigned int* abgr = (unsigned int*)element;
r = static_cast<float>(abgr[0]);
g = static_cast<float>(abgr[1]);
b = static_cast<float>(abgr[2]);
a = static_cast<float>(abgr[3]);
}
break;
case FORMAT_X32B32G32R32I:
{
int* bgr = (int*)element;
r = static_cast<float>(bgr[0]);
g = static_cast<float>(bgr[1]);
b = static_cast<float>(bgr[2]);
}
break;
case FORMAT_X32B32G32R32UI:
{
unsigned int* bgr = (unsigned int*)element;
r = static_cast<float>(bgr[0]);
g = static_cast<float>(bgr[1]);
b = static_cast<float>(bgr[2]);
}
break;
case FORMAT_G32R32I:
{
int* gr = (int*)element;
r = static_cast<float>(gr[0]);
g = static_cast<float>(gr[1]);
}
break;
case FORMAT_G32R32UI:
{
unsigned int* gr = (unsigned int*)element;
r = static_cast<float>(gr[0]);
g = static_cast<float>(gr[1]);
}
break;
case FORMAT_R32I:
r = static_cast<float>(*((int*)element));
break;
case FORMAT_R32UI:
r = static_cast<float>(*((unsigned int*)element));
break;
case FORMAT_V8U8:
{
unsigned short vu = *(unsigned short*)element;
r = ((int)(vu & 0x00FF) << 24) * (1.0f / 0x7F000000);
g = ((int)(vu & 0xFF00) << 16) * (1.0f / 0x7F000000);
}
break;
case FORMAT_L6V5U5:
{
unsigned short lvu = *(unsigned short*)element;
r = ((int)(lvu & 0x001F) << 27) * (1.0f / 0x78000000);
g = ((int)(lvu & 0x03E0) << 22) * (1.0f / 0x78000000);
b = (lvu & 0xFC00) * (1.0f / 0xFC00);
}
break;
case FORMAT_Q8W8V8U8:
{
unsigned int qwvu = *(unsigned int*)element;
r = ((int)(qwvu & 0x000000FF) << 24) * (1.0f / 0x7F000000);
g = ((int)(qwvu & 0x0000FF00) << 16) * (1.0f / 0x7F000000);
b = ((int)(qwvu & 0x00FF0000) << 8) * (1.0f / 0x7F000000);
a = ((int)(qwvu & 0xFF000000) << 0) * (1.0f / 0x7F000000);
}
break;
case FORMAT_X8L8V8U8:
{
unsigned int xlvu = *(unsigned int*)element;
r = ((int)(xlvu & 0x000000FF) << 24) * (1.0f / 0x7F000000);
g = ((int)(xlvu & 0x0000FF00) << 16) * (1.0f / 0x7F000000);
b = (xlvu & 0x00FF0000) * (1.0f / 0x00FF0000);
}
break;
case FORMAT_R8G8B8:
r = ((unsigned char*)element)[2] * (1.0f / 0xFF);
g = ((unsigned char*)element)[1] * (1.0f / 0xFF);
b = ((unsigned char*)element)[0] * (1.0f / 0xFF);
break;
case FORMAT_B8G8R8:
r = ((unsigned char*)element)[0] * (1.0f / 0xFF);
g = ((unsigned char*)element)[1] * (1.0f / 0xFF);
b = ((unsigned char*)element)[2] * (1.0f / 0xFF);
break;
case FORMAT_V16U16:
{
unsigned int vu = *(unsigned int*)element;
r = ((int)(vu & 0x0000FFFF) << 16) * (1.0f / 0x7FFF0000);
g = ((int)(vu & 0xFFFF0000) << 0) * (1.0f / 0x7FFF0000);
}
break;
case FORMAT_A2W10V10U10:
{
unsigned int awvu = *(unsigned int*)element;
r = ((int)(awvu & 0x000003FF) << 22) * (1.0f / 0x7FC00000);
g = ((int)(awvu & 0x000FFC00) << 12) * (1.0f / 0x7FC00000);
b = ((int)(awvu & 0x3FF00000) << 2) * (1.0f / 0x7FC00000);
a = (awvu & 0xC0000000) * (1.0f / 0xC0000000);
}
break;
case FORMAT_A16W16V16U16:
r = ((signed short*)element)[0] * (1.0f / 0x7FFF);
g = ((signed short*)element)[1] * (1.0f / 0x7FFF);
b = ((signed short*)element)[2] * (1.0f / 0x7FFF);
a = ((unsigned short*)element)[3] * (1.0f / 0xFFFF);
break;
case FORMAT_Q16W16V16U16:
r = ((signed short*)element)[0] * (1.0f / 0x7FFF);
g = ((signed short*)element)[1] * (1.0f / 0x7FFF);
b = ((signed short*)element)[2] * (1.0f / 0x7FFF);
a = ((signed short*)element)[3] * (1.0f / 0x7FFF);
break;
case FORMAT_L8:
r =
g =
b = *(unsigned char*)element * (1.0f / 0xFF);
break;
case FORMAT_A4L4:
{
unsigned char al = *(unsigned char*)element;
r =
g =
b = (al & 0x0F) * (1.0f / 0x0F);
a = (al & 0xF0) * (1.0f / 0xF0);
}
break;
case FORMAT_L16:
r =
g =
b = *(unsigned short*)element * (1.0f / 0xFFFF);
break;
case FORMAT_A8L8:
r =
g =
b = ((unsigned char*)element)[0] * (1.0f / 0xFF);
a = ((unsigned char*)element)[1] * (1.0f / 0xFF);
break;
case FORMAT_L16F:
r =
g =
b = *(half*)element;
break;
case FORMAT_A16L16F:
r =
g =
b = ((half*)element)[0];
a = ((half*)element)[1];
break;
case FORMAT_L32F:
r =
g =
b = *(float*)element;
break;
case FORMAT_A32L32F:
r =
g =
b = ((float*)element)[0];
a = ((float*)element)[1];
break;
case FORMAT_A16F:
a = *(half*)element;
break;
case FORMAT_R16F:
r = *(half*)element;
break;
case FORMAT_G16R16F:
r = ((half*)element)[0];
g = ((half*)element)[1];
break;
case FORMAT_X16B16G16R16F:
case FORMAT_X16B16G16R16F_UNSIGNED:
case FORMAT_B16G16R16F:
r = ((half*)element)[0];
g = ((half*)element)[1];
b = ((half*)element)[2];
break;
case FORMAT_A16B16G16R16F:
r = ((half*)element)[0];
g = ((half*)element)[1];
b = ((half*)element)[2];
a = ((half*)element)[3];
break;
case FORMAT_A32F:
a = *(float*)element;
break;
case FORMAT_R32F:
r = *(float*)element;
break;
case FORMAT_G32R32F:
r = ((float*)element)[0];
g = ((float*)element)[1];
break;
case FORMAT_X32B32G32R32F:
case FORMAT_X32B32G32R32F_UNSIGNED:
case FORMAT_B32G32R32F:
r = ((float*)element)[0];
g = ((float*)element)[1];
b = ((float*)element)[2];
break;
case FORMAT_A32B32G32R32F:
r = ((float*)element)[0];
g = ((float*)element)[1];
b = ((float*)element)[2];
a = ((float*)element)[3];
break;
case FORMAT_D32F:
case FORMAT_D32FS8:
case FORMAT_D32F_LOCKABLE:
case FORMAT_D32FS8_TEXTURE:
case FORMAT_D32F_SHADOW:
case FORMAT_D32FS8_SHADOW:
r = *(float*)element;
g = r;
b = r;
a = r;
break;
case FORMAT_D32F_COMPLEMENTARY:
case FORMAT_D32FS8_COMPLEMENTARY:
r = 1.0f - *(float*)element;
g = r;
b = r;
a = r;
break;
case FORMAT_S8:
r = *(unsigned char*)element * (1.0f / 0xFF);
break;
default:
ASSERT(false);
}
if(isSRGBformat(format))
{
r = sRGBtoLinear(r);
g = sRGBtoLinear(g);
b = sRGBtoLinear(b);
}
return Color<float>(r, g, b, a);
}
Color<float> Surface::Buffer::sample(float x, float y, float z) const
{
x -= 0.5f;
y -= 0.5f;
z -= 0.5f;
int x0 = clamp((int)x, 0, width - 1);
int x1 = (x0 + 1 >= width) ? x0 : x0 + 1;
int y0 = clamp((int)y, 0, height - 1);
int y1 = (y0 + 1 >= height) ? y0 : y0 + 1;
int z0 = clamp((int)z, 0, depth - 1);
int z1 = (z0 + 1 >= depth) ? z0 : z0 + 1;
Color<float> c000 = read(x0, y0, z0);
Color<float> c100 = read(x1, y0, z0);
Color<float> c010 = read(x0, y1, z0);
Color<float> c110 = read(x1, y1, z0);
Color<float> c001 = read(x0, y0, z1);
Color<float> c101 = read(x1, y0, z1);
Color<float> c011 = read(x0, y1, z1);
Color<float> c111 = read(x1, y1, z1);
float fx = x - x0;
float fy = y - y0;
float fz = z - z0;
c000 *= (1 - fx) * (1 - fy) * (1 - fz);
c100 *= fx * (1 - fy) * (1 - fz);
c010 *= (1 - fx) * fy * (1 - fz);
c110 *= fx * fy * (1 - fz);
c001 *= (1 - fx) * (1 - fy) * fz;
c101 *= fx * (1 - fy) * fz;
c011 *= (1 - fx) * fy * fz;
c111 *= fx * fy * fz;
return c000 + c100 + c010 + c110 + c001 + c101 + c011 + c111;
}
Color<float> Surface::Buffer::sample(float x, float y, int layer) const
{
x -= 0.5f;
y -= 0.5f;
int x0 = clamp((int)x, 0, width - 1);
int x1 = (x0 + 1 >= width) ? x0 : x0 + 1;
int y0 = clamp((int)y, 0, height - 1);
int y1 = (y0 + 1 >= height) ? y0 : y0 + 1;
Color<float> c00 = read(x0, y0, layer);
Color<float> c10 = read(x1, y0, layer);
Color<float> c01 = read(x0, y1, layer);
Color<float> c11 = read(x1, y1, layer);
float fx = x - x0;
float fy = y - y0;
c00 *= (1 - fx) * (1 - fy);
c10 *= fx * (1 - fy);
c01 *= (1 - fx) * fy;
c11 *= fx * fy;
return c00 + c10 + c01 + c11;
}
void *Surface::Buffer::lockRect(int x, int y, int z, Lock lock)
{
this->lock = lock;
switch(lock)
{
case LOCK_UNLOCKED:
case LOCK_READONLY:
case LOCK_UPDATE:
break;
case LOCK_WRITEONLY:
case LOCK_READWRITE:
case LOCK_DISCARD:
dirty = true;
break;
default:
ASSERT(false);
}
if(buffer)
{
x += border;
y += border;
switch(format)
{
case FORMAT_DXT1:
case FORMAT_ATI1:
case FORMAT_ETC1:
case FORMAT_R11_EAC:
case FORMAT_SIGNED_R11_EAC:
case FORMAT_RGB8_ETC2:
case FORMAT_SRGB8_ETC2:
case FORMAT_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case FORMAT_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
return (unsigned char*)buffer + 8 * (x / 4) + (y / 4) * pitchB + z * sliceB;
case FORMAT_RG11_EAC:
case FORMAT_SIGNED_RG11_EAC:
case FORMAT_RGBA8_ETC2_EAC:
case FORMAT_SRGB8_ALPHA8_ETC2_EAC:
return (unsigned char*)buffer + 16 * (x / 4) + (y / 4) * pitchB + z * sliceB;
case FORMAT_DXT3:
case FORMAT_DXT5:
case FORMAT_ATI2:
return (unsigned char*)buffer + 16 * (x / 4) + (y / 4) * pitchB + z * sliceB;
default:
return (unsigned char*)buffer + x * bytes + y * pitchB + z * samples * sliceB;
}
}
return nullptr;
}
void Surface::Buffer::unlockRect()
{
lock = LOCK_UNLOCKED;
}
class SurfaceImplementation : public Surface
{
public:
SurfaceImplementation(int width, int height, int depth, Format format, void *pixels, int pitch, int slice)
: Surface(width, height, depth, format, pixels, pitch, slice) {}
SurfaceImplementation(Resource *texture, int width, int height, int depth, int border, int samples, Format format, bool lockable, bool renderTarget, int pitchP = 0)
: Surface(texture, width, height, depth, border, samples, format, lockable, renderTarget, pitchP) {}
~SurfaceImplementation() override {}
void *lockInternal(int x, int y, int z, Lock lock, Accessor client) override
{
return Surface::lockInternal(x, y, z, lock, client);
}
void unlockInternal() override
{
Surface::unlockInternal();
}
};
Surface *Surface::create(int width, int height, int depth, Format format, void *pixels, int pitch, int slice)
{
return new SurfaceImplementation(width, height, depth, format, pixels, pitch, slice);
}
Surface *Surface::create(Resource *texture, int width, int height, int depth, int border, int samples, Format format, bool lockable, bool renderTarget, int pitchPprovided)
{
return new SurfaceImplementation(texture, width, height, depth, border, samples, format, lockable, renderTarget, pitchPprovided);
}
Surface::Surface(int width, int height, int depth, Format format, void *pixels, int pitch, int slice) : lockable(true), renderTarget(false)
{
resource = new Resource(0);
hasParent = false;
ownExternal = false;
depth = max(1, depth);
external.buffer = pixels;
external.width = width;
external.height = height;
external.depth = depth;
external.samples = 1;
external.format = format;
external.bytes = bytes(external.format);
external.pitchB = pitch;
external.pitchP = external.bytes ? pitch / external.bytes : 0;
external.sliceB = slice;
external.sliceP = external.bytes ? slice / external.bytes : 0;
external.border = 0;
external.lock = LOCK_UNLOCKED;
external.dirty = true;
internal.buffer = nullptr;
internal.width = width;
internal.height = height;
internal.depth = depth;
internal.samples = 1;
internal.format = selectInternalFormat(format);
internal.bytes = bytes(internal.format);
internal.pitchB = pitchB(internal.width, 0, internal.format, false);
internal.pitchP = pitchP(internal.width, 0, internal.format, false);
internal.sliceB = sliceB(internal.width, internal.height, 0, internal.format, false);
internal.sliceP = sliceP(internal.width, internal.height, 0, internal.format, false);
internal.border = 0;
internal.lock = LOCK_UNLOCKED;
internal.dirty = false;
stencil.buffer = nullptr;
stencil.width = width;
stencil.height = height;
stencil.depth = depth;
stencil.samples = 1;
stencil.format = isStencil(format) ? FORMAT_S8 : FORMAT_NULL;
stencil.bytes = bytes(stencil.format);
stencil.pitchB = pitchB(stencil.width, 0, stencil.format, false);
stencil.pitchP = pitchP(stencil.width, 0, stencil.format, false);
stencil.sliceB = sliceB(stencil.width, stencil.height, 0, stencil.format, false);
stencil.sliceP = sliceP(stencil.width, stencil.height, 0, stencil.format, false);
stencil.border = 0;
stencil.lock = LOCK_UNLOCKED;
stencil.dirty = false;
dirtyContents = true;
paletteUsed = 0;
}
Surface::Surface(Resource *texture, int width, int height, int depth, int border, int samples, Format format, bool lockable, bool renderTarget, int pitchPprovided) : lockable(lockable), renderTarget(renderTarget)
{
resource = texture ? texture : new Resource(0);
hasParent = texture != nullptr;
ownExternal = true;
depth = max(1, depth);
samples = max(1, samples);
external.buffer = nullptr;
external.width = width;
external.height = height;
external.depth = depth;
external.samples = (short)samples;
external.format = format;
external.bytes = bytes(external.format);
external.pitchB = !pitchPprovided ? pitchB(external.width, 0, external.format, renderTarget && !texture) : pitchPprovided * external.bytes;
external.pitchP = !pitchPprovided ? pitchP(external.width, 0, external.format, renderTarget && !texture) : pitchPprovided;
external.sliceB = sliceB(external.width, external.height, 0, external.format, renderTarget && !texture);
external.sliceP = sliceP(external.width, external.height, 0, external.format, renderTarget && !texture);
external.border = 0;
external.lock = LOCK_UNLOCKED;
external.dirty = false;
internal.buffer = nullptr;
internal.width = width;
internal.height = height;
internal.depth = depth;
internal.samples = (short)samples;
internal.format = selectInternalFormat(format);
internal.bytes = bytes(internal.format);
internal.pitchB = !pitchPprovided ? pitchB(internal.width, border, internal.format, renderTarget) : pitchPprovided * internal.bytes;
internal.pitchP = !pitchPprovided ? pitchP(internal.width, border, internal.format, renderTarget) : pitchPprovided;
internal.sliceB = sliceB(internal.width, internal.height, border, internal.format, renderTarget);
internal.sliceP = sliceP(internal.width, internal.height, border, internal.format, renderTarget);
internal.border = (short)border;
internal.lock = LOCK_UNLOCKED;
internal.dirty = false;
stencil.buffer = nullptr;
stencil.width = width;
stencil.height = height;
stencil.depth = depth;
stencil.samples = (short)samples;
stencil.format = isStencil(format) ? FORMAT_S8 : FORMAT_NULL;
stencil.bytes = bytes(stencil.format);
stencil.pitchB = pitchB(stencil.width, 0, stencil.format, renderTarget);
stencil.pitchP = pitchP(stencil.width, 0, stencil.format, renderTarget);
stencil.sliceB = sliceB(stencil.width, stencil.height, 0, stencil.format, renderTarget);
stencil.sliceP = sliceP(stencil.width, stencil.height, 0, stencil.format, renderTarget);
stencil.border = 0;
stencil.lock = LOCK_UNLOCKED;
stencil.dirty = false;
dirtyContents = true;
paletteUsed = 0;
}
Surface::~Surface()
{
// sync() must be called before this destructor to ensure all locks have been released.
// We can't call it here because the parent resource may already have been destroyed.
ASSERT(isUnlocked());
if(!hasParent)
{
resource->destruct();
}
if(ownExternal)
{
deallocate(external.buffer);
}
if(internal.buffer != external.buffer)
{
deallocate(internal.buffer);
}
deallocate(stencil.buffer);
external.buffer = nullptr;
internal.buffer = nullptr;
stencil.buffer = nullptr;
}
void *Surface::lockExternal(int x, int y, int z, Lock lock, Accessor client)
{
resource->lock(client);
if(!external.buffer)
{
if(internal.buffer && identicalBuffers())
{
external.buffer = internal.buffer;
}
else
{
external.buffer = allocateBuffer(external.width, external.height, external.depth, external.border, external.samples, external.format);
}
}
if(internal.dirty)
{
if(lock != LOCK_DISCARD)
{
update(external, internal);
}
internal.dirty = false;
}
switch(lock)
{
case LOCK_READONLY:
break;
case LOCK_WRITEONLY:
case LOCK_READWRITE:
case LOCK_DISCARD:
dirtyContents = true;
break;
default:
ASSERT(false);
}
return external.lockRect(x, y, z, lock);
}
void Surface::unlockExternal()
{
external.unlockRect();
resource->unlock();
}
void *Surface::lockInternal(int x, int y, int z, Lock lock, Accessor client)
{
if(lock != LOCK_UNLOCKED)
{
resource->lock(client);
}
if(!internal.buffer)
{
if(external.buffer && identicalBuffers())
{
internal.buffer = external.buffer;
}
else
{
internal.buffer = allocateBuffer(internal.width, internal.height, internal.depth, internal.border, internal.samples, internal.format);
}
}
// FIXME: WHQL requires conversion to lower external precision and back
if(logPrecision >= WHQL)
{
if(internal.dirty && renderTarget && internal.format != external.format)
{
if(lock != LOCK_DISCARD)
{
switch(external.format)
{
case FORMAT_R3G3B2:
case FORMAT_A8R3G3B2:
case FORMAT_A1R5G5B5:
case FORMAT_A2R10G10B10:
case FORMAT_A2B10G10R10:
lockExternal(0, 0, 0, LOCK_READWRITE, client);
unlockExternal();
break;
default:
// Difference passes WHQL
break;
}
}
}
}
if(external.dirty || (isPalette(external.format) && paletteUsed != Surface::paletteID))
{
if(lock != LOCK_DISCARD)
{
update(internal, external);
}
external.dirty = false;
paletteUsed = Surface::paletteID;
}
switch(lock)
{
case LOCK_UNLOCKED:
case LOCK_READONLY:
break;
case LOCK_WRITEONLY:
case LOCK_READWRITE:
case LOCK_DISCARD:
dirtyContents = true;
break;
default:
ASSERT(false);
}
if(lock == LOCK_READONLY && client == PUBLIC)
{
resolve();
}
return internal.lockRect(x, y, z, lock);
}
void Surface::unlockInternal()
{
internal.unlockRect();
resource->unlock();
}
void *Surface::lockStencil(int x, int y, int front, Accessor client)
{
resource->lock(client);
if(stencil.format == FORMAT_NULL)
{
return nullptr;
}
if(!stencil.buffer)
{
stencil.buffer = allocateBuffer(stencil.width, stencil.height, stencil.depth, stencil.border, stencil.samples, stencil.format);
}
return stencil.lockRect(x, y, front, LOCK_READWRITE); // FIXME
}
void Surface::unlockStencil()
{
stencil.unlockRect();
resource->unlock();
}
int Surface::bytes(Format format)
{
switch(format)
{
case FORMAT_NULL: return 0;
case FORMAT_P8: return 1;
case FORMAT_A8P8: return 2;
case FORMAT_A8: return 1;
case FORMAT_R8I: return 1;
case FORMAT_R8: return 1;
case FORMAT_R3G3B2: return 1;
case FORMAT_R16I: return 2;
case FORMAT_R16UI: return 2;
case FORMAT_A8R3G3B2: return 2;
case FORMAT_R5G6B5: return 2;
case FORMAT_A1R5G5B5: return 2;
case FORMAT_X1R5G5B5: return 2;
case FORMAT_R5G5B5A1: return 2;
case FORMAT_X4R4G4B4: return 2;
case FORMAT_A4R4G4B4: return 2;
case FORMAT_R4G4B4A4: return 2;
case FORMAT_R8G8B8: return 3;
case FORMAT_B8G8R8: return 3;
case FORMAT_R32I: return 4;
case FORMAT_R32UI: return 4;
case FORMAT_X8R8G8B8: return 4;
// case FORMAT_X8G8R8B8Q: return 4;
case FORMAT_A8R8G8B8: return 4;
// case FORMAT_A8G8R8B8Q: return 4;
case FORMAT_X8B8G8R8I: return 4;
case FORMAT_X8B8G8R8: return 4;
case FORMAT_SRGB8_X8: return 4;
case FORMAT_SRGB8_A8: return 4;
case FORMAT_A8B8G8R8I: return 4;
case FORMAT_R8UI: return 1;
case FORMAT_G8R8UI: return 2;
case FORMAT_X8B8G8R8UI: return 4;
case FORMAT_A8B8G8R8UI: return 4;
case FORMAT_A8B8G8R8: return 4;
case FORMAT_R8_SNORM: return 1;
case FORMAT_G8R8_SNORM: return 2;
case FORMAT_X8B8G8R8_SNORM: return 4;
case FORMAT_A8B8G8R8_SNORM: return 4;
case FORMAT_A2R10G10B10: return 4;
case FORMAT_A2B10G10R10: return 4;
case FORMAT_A2B10G10R10UI: return 4;
case FORMAT_G8R8I: return 2;
case FORMAT_G8R8: return 2;
case FORMAT_G16R16I: return 4;
case FORMAT_G16R16UI: return 4;
case FORMAT_G16R16: return 4;
case FORMAT_G32R32I: return 8;
case FORMAT_G32R32UI: return 8;
case FORMAT_X16B16G16R16I: return 8;
case FORMAT_X16B16G16R16UI: return 8;
case FORMAT_A16B16G16R16I: return 8;
case FORMAT_A16B16G16R16UI: return 8;
case FORMAT_A16B16G16R16: return 8;
case FORMAT_X32B32G32R32I: return 16;
case FORMAT_X32B32G32R32UI: return 16;
case FORMAT_A32B32G32R32I: return 16;
case FORMAT_A32B32G32R32UI: return 16;
// Compressed formats
case FORMAT_DXT1: return 2; // Column of four pixels
case FORMAT_DXT3: return 4; // Column of four pixels
case FORMAT_DXT5: return 4; // Column of four pixels
case FORMAT_ATI1: return 2; // Column of four pixels
case FORMAT_ATI2: return 4; // Column of four pixels
case FORMAT_ETC1: return 2; // Column of four pixels
case FORMAT_R11_EAC: return 2;
case FORMAT_SIGNED_R11_EAC: return 2;
case FORMAT_RG11_EAC: return 4;
case FORMAT_SIGNED_RG11_EAC: return 4;
case FORMAT_RGB8_ETC2: return 2;
case FORMAT_SRGB8_ETC2: return 2;
case FORMAT_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: return 2;
case FORMAT_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: return 2;
case FORMAT_RGBA8_ETC2_EAC: return 4;
case FORMAT_SRGB8_ALPHA8_ETC2_EAC: return 4;
// Bumpmap formats
case FORMAT_V8U8: return 2;
case FORMAT_L6V5U5: return 2;
case FORMAT_Q8W8V8U8: return 4;
case FORMAT_X8L8V8U8: return 4;
case FORMAT_A2W10V10U10: return 4;
case FORMAT_V16U16: return 4;
case FORMAT_A16W16V16U16: return 8;
case FORMAT_Q16W16V16U16: return 8;
// Luminance formats
case FORMAT_L8: return 1;
case FORMAT_A4L4: return 1;
case FORMAT_L16: return 2;
case FORMAT_A8L8: return 2;
case FORMAT_L16F: return 2;
case FORMAT_A16L16F: return 4;
case FORMAT_L32F: return 4;
case FORMAT_A32L32F: return 8;
// Floating-point formats
case FORMAT_A16F: return 2;
case FORMAT_R16F: return 2;
case FORMAT_G16R16F: return 4;
case FORMAT_B16G16R16F: return 6;
case FORMAT_X16B16G16R16F: return 8;
case FORMAT_A16B16G16R16F: return 8;
case FORMAT_X16B16G16R16F_UNSIGNED: return 8;
case FORMAT_A32F: return 4;
case FORMAT_R32F: return 4;
case FORMAT_G32R32F: return 8;
case FORMAT_B32G32R32F: return 12;
case FORMAT_X32B32G32R32F: return 16;
case FORMAT_A32B32G32R32F: return 16;
case FORMAT_X32B32G32R32F_UNSIGNED: return 16;
// Depth/stencil formats
case FORMAT_D16: return 2;
case FORMAT_D32: return 4;
case FORMAT_D24X8: return 4;
case FORMAT_D24S8: return 4;
case FORMAT_D24FS8: return 4;
case FORMAT_D32F: return 4;
case FORMAT_D32FS8: return 4;
case FORMAT_D32F_COMPLEMENTARY: return 4;
case FORMAT_D32FS8_COMPLEMENTARY: return 4;
case FORMAT_D32F_LOCKABLE: return 4;
case FORMAT_D32FS8_TEXTURE: return 4;
case FORMAT_D32F_SHADOW: return 4;
case FORMAT_D32FS8_SHADOW: return 4;
case FORMAT_DF24S8: return 4;
case FORMAT_DF16S8: return 2;
case FORMAT_INTZ: return 4;
case FORMAT_S8: return 1;
case FORMAT_YV12_BT601: return 1; // Y plane only
case FORMAT_YV12_BT709: return 1; // Y plane only
case FORMAT_YV12_JFIF: return 1; // Y plane only
default:
ASSERT(false);
}
return 0;
}
int Surface::pitchB(int width, int border, Format format, bool target)
{
width += 2 * border;
// Render targets require 2x2 quads
if(target || isDepth(format) || isStencil(format))
{
width = align<2>(width);
}
switch(format)
{
case FORMAT_DXT1:
case FORMAT_ETC1:
case FORMAT_R11_EAC:
case FORMAT_SIGNED_R11_EAC:
case FORMAT_RGB8_ETC2:
case FORMAT_SRGB8_ETC2:
case FORMAT_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case FORMAT_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
return 8 * ((width + 3) / 4); // 64 bit per 4x4 block, computed per 4 rows
case FORMAT_RG11_EAC:
case FORMAT_SIGNED_RG11_EAC:
case FORMAT_RGBA8_ETC2_EAC:
case FORMAT_SRGB8_ALPHA8_ETC2_EAC:
return 16 * ((width + 3) / 4); // 128 bit per 4x4 block, computed per 4 rows
case FORMAT_DXT3:
case FORMAT_DXT5:
return 16 * ((width + 3) / 4); // 128 bit per 4x4 block, computed per 4 rows
case FORMAT_ATI1:
return 2 * ((width + 3) / 4); // 64 bit per 4x4 block, computed per row
case FORMAT_ATI2:
return 4 * ((width + 3) / 4); // 128 bit per 4x4 block, computed per row
case FORMAT_YV12_BT601:
case FORMAT_YV12_BT709:
case FORMAT_YV12_JFIF:
return align<16>(width);
default:
return bytes(format) * width;
}
}
int Surface::pitchP(int width, int border, Format format, bool target)
{
int B = bytes(format);
return B > 0 ? pitchB(width, border, format, target) / B : 0;
}
int Surface::sliceB(int width, int height, int border, Format format, bool target)
{
height += 2 * border;
// Render targets require 2x2 quads
if(target || isDepth(format) || isStencil(format))
{
height = align<2>(height);
}
switch(format)
{
case FORMAT_DXT1:
case FORMAT_DXT3:
case FORMAT_DXT5:
case FORMAT_ETC1:
case FORMAT_R11_EAC:
case FORMAT_SIGNED_R11_EAC:
case FORMAT_RG11_EAC:
case FORMAT_SIGNED_RG11_EAC:
case FORMAT_RGB8_ETC2:
case FORMAT_SRGB8_ETC2:
case FORMAT_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case FORMAT_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case FORMAT_RGBA8_ETC2_EAC:
case FORMAT_SRGB8_ALPHA8_ETC2_EAC:
return pitchB(width, border, format, target) * ((height + 3) / 4); // Pitch computed per 4 rows
case FORMAT_ATI1:
case FORMAT_ATI2:
return pitchB(width, border, format, target) * align<4>(height); // Pitch computed per row
default:
return pitchB(width, border, format, target) * height; // Pitch computed per row
}
}
int Surface::sliceP(int width, int height, int border, Format format, bool target)
{
int B = bytes(format);
return B > 0 ? sliceB(width, height, border, format, target) / B : 0;
}
void Surface::update(Buffer &destination, Buffer &source)
{
// ASSERT(source.lock != LOCK_UNLOCKED);
// ASSERT(destination.lock != LOCK_UNLOCKED);
if(destination.buffer != source.buffer)
{
ASSERT(source.dirty && !destination.dirty);
switch(source.format)
{
case FORMAT_R8G8B8: decodeR8G8B8(destination, source); break; // FIXME: Check destination format
case FORMAT_X1R5G5B5: decodeX1R5G5B5(destination, source); break; // FIXME: Check destination format
case FORMAT_A1R5G5B5: decodeA1R5G5B5(destination, source); break; // FIXME: Check destination format
case FORMAT_X4R4G4B4: decodeX4R4G4B4(destination, source); break; // FIXME: Check destination format
case FORMAT_A4R4G4B4: decodeA4R4G4B4(destination, source); break; // FIXME: Check destination format
case FORMAT_P8: decodeP8(destination, source); break; // FIXME: Check destination format
case FORMAT_DXT1: decodeDXT1(destination, source); break; // FIXME: Check destination format
case FORMAT_DXT3: decodeDXT3(destination, source); break; // FIXME: Check destination format
case FORMAT_DXT5: decodeDXT5(destination, source); break; // FIXME: Check destination format
case FORMAT_ATI1: decodeATI1(destination, source); break; // FIXME: Check destination format
case FORMAT_ATI2: decodeATI2(destination, source); break; // FIXME: Check destination format
case FORMAT_R11_EAC: decodeEAC(destination, source, 1, false); break; // FIXME: Check destination format
case FORMAT_SIGNED_R11_EAC: decodeEAC(destination, source, 1, true); break; // FIXME: Check destination format
case FORMAT_RG11_EAC: decodeEAC(destination, source, 2, false); break; // FIXME: Check destination format
case FORMAT_SIGNED_RG11_EAC: decodeEAC(destination, source, 2, true); break; // FIXME: Check destination format
case FORMAT_ETC1:
case FORMAT_RGB8_ETC2: decodeETC2(destination, source, 0, false); break; // FIXME: Check destination format
case FORMAT_SRGB8_ETC2: decodeETC2(destination, source, 0, true); break; // FIXME: Check destination format
case FORMAT_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: decodeETC2(destination, source, 1, false); break; // FIXME: Check destination format
case FORMAT_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: decodeETC2(destination, source, 1, true); break; // FIXME: Check destination format
case FORMAT_RGBA8_ETC2_EAC: decodeETC2(destination, source, 8, false); break; // FIXME: Check destination format
case FORMAT_SRGB8_ALPHA8_ETC2_EAC: decodeETC2(destination, source, 8, true); break; // FIXME: Check destination format
default: genericUpdate(destination, source); break;
}
}
}
void Surface::genericUpdate(Buffer &destination, Buffer &source)
{
unsigned char *sourceSlice = (unsigned char*)source.lockRect(0, 0, 0, sw::LOCK_READONLY);
unsigned char *destinationSlice = (unsigned char*)destination.lockRect(0, 0, 0, sw::LOCK_UPDATE);
int depth = min(destination.depth, source.depth);
int height = min(destination.height, source.height);
int width = min(destination.width, source.width);
int rowBytes = width * source.bytes;
for(int z = 0; z < depth; z++)
{
unsigned char *sourceRow = sourceSlice;
unsigned char *destinationRow = destinationSlice;
for(int y = 0; y < height; y++)
{
if(source.format == destination.format)
{
memcpy(destinationRow, sourceRow, rowBytes);
}
else
{
unsigned char *sourceElement = sourceRow;
unsigned char *destinationElement = destinationRow;
for(int x = 0; x < width; x++)
{
Color<float> color = source.read(sourceElement);
destination.write(destinationElement, color);
sourceElement += source.bytes;
destinationElement += destination.bytes;
}
}
sourceRow += source.pitchB;
destinationRow += destination.pitchB;
}
sourceSlice += source.sliceB;
destinationSlice += destination.sliceB;
}
source.unlockRect();
destination.unlockRect();
}
void Surface::decodeR8G8B8(Buffer &destination, Buffer &source)
{
unsigned char *sourceSlice = (unsigned char*)source.lockRect(0, 0, 0, sw::LOCK_READONLY);
unsigned char *destinationSlice = (unsigned char*)destination.lockRect(0, 0, 0, sw::LOCK_UPDATE);
int depth = min(destination.depth, source.depth);
int height = min(destination.height, source.height);
int width = min(destination.width, source.width);
for(int z = 0; z < depth; z++)
{
unsigned char *sourceRow = sourceSlice;
unsigned char *destinationRow = destinationSlice;
for(int y = 0; y < height; y++)
{
unsigned char *sourceElement = sourceRow;
unsigned char *destinationElement = destinationRow;
for(int x = 0; x < width; x++)
{
unsigned int b = sourceElement[0];
unsigned int g = sourceElement[1];
unsigned int r = sourceElement[2];
*(unsigned int*)destinationElement = 0xFF000000 | (r << 16) | (g << 8) | (b << 0);
sourceElement += source.bytes;
destinationElement += destination.bytes;
}
sourceRow += source.pitchB;
destinationRow += destination.pitchB;
}
sourceSlice += source.sliceB;
destinationSlice += destination.sliceB;
}
source.unlockRect();
destination.unlockRect();
}
void Surface::decodeX1R5G5B5(Buffer &destination, Buffer &source)
{
unsigned char *sourceSlice = (unsigned char*)source.lockRect(0, 0, 0, sw::LOCK_READONLY);
unsigned char *destinationSlice = (unsigned char*)destination.lockRect(0, 0, 0, sw::LOCK_UPDATE);
int depth = min(destination.depth, source.depth);
int height = min(destination.height, source.height);
int width = min(destination.width, source.width);
for(int z = 0; z < depth; z++)
{
unsigned char *sourceRow = sourceSlice;
unsigned char *destinationRow = destinationSlice;
for(int y = 0; y < height; y++)
{
unsigned char *sourceElement = sourceRow;
unsigned char *destinationElement = destinationRow;
for(int x = 0; x < width; x++)
{
unsigned int xrgb = *(unsigned short*)sourceElement;
unsigned int r = (((xrgb & 0x7C00) * 134771 + 0x800000) >> 8) & 0x00FF0000;
unsigned int g = (((xrgb & 0x03E0) * 16846 + 0x8000) >> 8) & 0x0000FF00;
unsigned int b = (((xrgb & 0x001F) * 2106 + 0x80) >> 8);
*(unsigned int*)destinationElement = 0xFF000000 | r | g | b;
sourceElement += source.bytes;
destinationElement += destination.bytes;
}
sourceRow += source.pitchB;
destinationRow += destination.pitchB;
}
sourceSlice += source.sliceB;
destinationSlice += destination.sliceB;
}
source.unlockRect();
destination.unlockRect();
}
void Surface::decodeA1R5G5B5(Buffer &destination, Buffer &source)
{
unsigned char *sourceSlice = (unsigned char*)source.lockRect(0, 0, 0, sw::LOCK_READONLY);
unsigned char *destinationSlice = (unsigned char*)destination.lockRect(0, 0, 0, sw::LOCK_UPDATE);
int depth = min(destination.depth, source.depth);
int height = min(destination.height, source.height);
int width = min(destination.width, source.width);
for(int z = 0; z < depth; z++)
{
unsigned char *sourceRow = sourceSlice;
unsigned char *destinationRow = destinationSlice;
for(int y = 0; y < height; y++)
{
unsigned char *sourceElement = sourceRow;
unsigned char *destinationElement = destinationRow;
for(int x = 0; x < width; x++)
{
unsigned int argb = *(unsigned short*)sourceElement;
unsigned int a = (argb & 0x8000) * 130560;
unsigned int r = (((argb & 0x7C00) * 134771 + 0x800000) >> 8) & 0x00FF0000;
unsigned int g = (((argb & 0x03E0) * 16846 + 0x8000) >> 8) & 0x0000FF00;
unsigned int b = (((argb & 0x001F) * 2106 + 0x80) >> 8);
*(unsigned int*)destinationElement = a | r | g | b;
sourceElement += source.bytes;
destinationElement += destination.bytes;
}
sourceRow += source.pitchB;
destinationRow += destination.pitchB;
}
sourceSlice += source.sliceB;
destinationSlice += destination.sliceB;
}
source.unlockRect();
destination.unlockRect();
}
void Surface::decodeX4R4G4B4(Buffer &destination, Buffer &source)
{
unsigned char *sourceSlice = (unsigned char*)source.lockRect(0, 0, 0, sw::LOCK_READONLY);
unsigned char *destinationSlice = (unsigned char*)destination.lockRect(0, 0, 0, sw::LOCK_UPDATE);
int depth = min(destination.depth, source.depth);
int height = min(destination.height, source.height);
int width = min(destination.width, source.width);
for(int z = 0; z < depth; z++)
{
unsigned char *sourceRow = sourceSlice;
unsigned char *destinationRow = destinationSlice;
for(int y = 0; y < height; y++)
{
unsigned char *sourceElement = sourceRow;
unsigned char *destinationElement = destinationRow;
for(int x = 0; x < width; x++)
{
unsigned int xrgb = *(unsigned short*)sourceElement;
unsigned int r = ((xrgb & 0x0F00) * 0x00001100) & 0x00FF0000;
unsigned int g = ((xrgb & 0x00F0) * 0x00000110) & 0x0000FF00;
unsigned int b = (xrgb & 0x000F) * 0x00000011;
*(unsigned int*)destinationElement = 0xFF000000 | r | g | b;
sourceElement += source.bytes;
destinationElement += destination.bytes;
}
sourceRow += source.pitchB;
destinationRow += destination.pitchB;
}
sourceSlice += source.sliceB;
destinationSlice += destination.sliceB;
}
source.unlockRect();
destination.unlockRect();
}
void Surface::decodeA4R4G4B4(Buffer &destination, Buffer &source)
{
unsigned char *sourceSlice = (unsigned char*)source.lockRect(0, 0, 0, sw::LOCK_READONLY);
unsigned char *destinationSlice = (unsigned char*)destination.lockRect(0, 0, 0, sw::LOCK_UPDATE);
int depth = min(destination.depth, source.depth);
int height = min(destination.height, source.height);
int width = min(destination.width, source.width);
for(int z = 0; z < depth; z++)
{
unsigned char *sourceRow = sourceSlice;
unsigned char *destinationRow = destinationSlice;
for(int y = 0; y < height; y++)
{
unsigned char *sourceElement = sourceRow;
unsigned char *destinationElement = destinationRow;
for(int x = 0; x < width; x++)
{
unsigned int argb = *(unsigned short*)sourceElement;
unsigned int a = ((argb & 0xF000) * 0x00011000) & 0xFF000000;
unsigned int r = ((argb & 0x0F00) * 0x00001100) & 0x00FF0000;
unsigned int g = ((argb & 0x00F0) * 0x00000110) & 0x0000FF00;
unsigned int b = (argb & 0x000F) * 0x00000011;
*(unsigned int*)destinationElement = a | r | g | b;
sourceElement += source.bytes;
destinationElement += destination.bytes;
}
sourceRow += source.pitchB;
destinationRow += destination.pitchB;
}
sourceSlice += source.sliceB;
destinationSlice += destination.sliceB;
}
source.unlockRect();
destination.unlockRect();
}
void Surface::decodeP8(Buffer &destination, Buffer &source)
{
unsigned char *sourceSlice = (unsigned char*)source.lockRect(0, 0, 0, sw::LOCK_READONLY);
unsigned char *destinationSlice = (unsigned char*)destination.lockRect(0, 0, 0, sw::LOCK_UPDATE);
int depth = min(destination.depth, source.depth);
int height = min(destination.height, source.height);
int width = min(destination.width, source.width);
for(int z = 0; z < depth; z++)
{
unsigned char *sourceRow = sourceSlice;
unsigned char *destinationRow = destinationSlice;
for(int y = 0; y < height; y++)
{
unsigned char *sourceElement = sourceRow;
unsigned char *destinationElement = destinationRow;
for(int x = 0; x < width; x++)
{
unsigned int abgr = palette[*(unsigned char*)sourceElement];
unsigned int r = (abgr & 0x000000FF) << 16;
unsigned int g = (abgr & 0x0000FF00) << 0;
unsigned int b = (abgr & 0x00FF0000) >> 16;
unsigned int a = (abgr & 0xFF000000) >> 0;
*(unsigned int*)destinationElement = a | r | g | b;
sourceElement += source.bytes;
destinationElement += destination.bytes;
}
sourceRow += source.pitchB;
destinationRow += destination.pitchB;
}
sourceSlice += source.sliceB;
destinationSlice += destination.sliceB;
}
source.unlockRect();
destination.unlockRect();
}
void Surface::decodeDXT1(Buffer &internal, Buffer &external)
{
unsigned int *destSlice = (unsigned int*)internal.lockRect(0, 0, 0, LOCK_UPDATE);
const DXT1 *source = (const DXT1*)external.lockRect(0, 0, 0, LOCK_READONLY);
for(int z = 0; z < external.depth; z++)
{
unsigned int *dest = destSlice;
for(int y = 0; y < external.height; y += 4)
{
for(int x = 0; x < external.width; x += 4)
{
Color<byte> c[4];
c[0] = source->c0;
c[1] = source->c1;
if(source->c0 > source->c1) // No transparency
{
// c2 = 2 / 3 * c0 + 1 / 3 * c1
c[2].r = (byte)((2 * (word)c[0].r + (word)c[1].r + 1) / 3);
c[2].g = (byte)((2 * (word)c[0].g + (word)c[1].g + 1) / 3);
c[2].b = (byte)((2 * (word)c[0].b + (word)c[1].b + 1) / 3);
c[2].a = 0xFF;
// c3 = 1 / 3 * c0 + 2 / 3 * c1
c[3].r = (byte)(((word)c[0].r + 2 * (word)c[1].r + 1) / 3);
c[3].g = (byte)(((word)c[0].g + 2 * (word)c[1].g + 1) / 3);
c[3].b = (byte)(((word)c[0].b + 2 * (word)c[1].b + 1) / 3);
c[3].a = 0xFF;
}
else // c3 transparent
{
// c2 = 1 / 2 * c0 + 1 / 2 * c1
c[2].r = (byte)(((word)c[0].r + (word)c[1].r) / 2);
c[2].g = (byte)(((word)c[0].g + (word)c[1].g) / 2);
c[2].b = (byte)(((word)c[0].b + (word)c[1].b) / 2);
c[2].a = 0xFF;
c[3].r = 0;
c[3].g = 0;
c[3].b = 0;
c[3].a = 0;
}
for(int j = 0; j < 4 && (y + j) < internal.height; j++)
{
for(int i = 0; i < 4 && (x + i) < internal.width; i++)
{
dest[(x + i) + (y + j) * internal.pitchP] = c[(unsigned int)(source->lut >> 2 * (i + j * 4)) % 4];
}
}
source++;
}
}
(byte*&)destSlice += internal.sliceB;
}
external.unlockRect();
internal.unlockRect();
}
void Surface::decodeDXT3(Buffer &internal, Buffer &external)
{
unsigned int *destSlice = (unsigned int*)internal.lockRect(0, 0, 0, LOCK_UPDATE);
const DXT3 *source = (const DXT3*)external.lockRect(0, 0, 0, LOCK_READONLY);
for(int z = 0; z < external.depth; z++)
{
unsigned int *dest = destSlice;
for(int y = 0; y < external.height; y += 4)
{
for(int x = 0; x < external.width; x += 4)
{
Color<byte> c[4];
c[0] = source->c0;
c[1] = source->c1;
// c2 = 2 / 3 * c0 + 1 / 3 * c1
c[2].r = (byte)((2 * (word)c[0].r + (word)c[1].r + 1) / 3);
c[2].g = (byte)((2 * (word)c[0].g + (word)c[1].g + 1) / 3);
c[2].b = (byte)((2 * (word)c[0].b + (word)c[1].b + 1) / 3);
// c3 = 1 / 3 * c0 + 2 / 3 * c1
c[3].r = (byte)(((word)c[0].r + 2 * (word)c[1].r + 1) / 3);
c[3].g = (byte)(((word)c[0].g + 2 * (word)c[1].g + 1) / 3);
c[3].b = (byte)(((word)c[0].b + 2 * (word)c[1].b + 1) / 3);
for(int j = 0; j < 4 && (y + j) < internal.height; j++)
{
for(int i = 0; i < 4 && (x + i) < internal.width; i++)
{
unsigned int a = (unsigned int)(source->a >> 4 * (i + j * 4)) & 0x0F;
unsigned int color = (c[(unsigned int)(source->lut >> 2 * (i + j *