diff options
Diffstat (limited to '')
-rw-r--r-- | xbmc/guilib/DirectXGraphics.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/xbmc/guilib/DirectXGraphics.cpp b/xbmc/guilib/DirectXGraphics.cpp new file mode 100644 index 0000000..5bab895 --- /dev/null +++ b/xbmc/guilib/DirectXGraphics.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DirectXGraphics.h" + +#include "Texture.h" +#include "XBTF.h" + +LPVOID XPhysicalAlloc(SIZE_T s, DWORD ulPhysicalAddress, DWORD ulAlignment, DWORD flProtect) +{ + return malloc(s); +} + +void XPhysicalFree(LPVOID lpAddress) +{ + free(lpAddress); +} + +DWORD GetD3DFormat(XB_D3DFORMAT format) +{ +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \ + ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 )) +#endif + switch (format) + { + case XB_D3DFMT_A8R8G8B8: + case XB_D3DFMT_LIN_A8R8G8B8: + case XB_D3DFMT_P8: + return 21; + case XB_D3DFMT_DXT1: + return MAKEFOURCC('D', 'X', 'T', '1'); + case XB_D3DFMT_DXT2: + return MAKEFOURCC('D', 'X', 'T', '2'); + case XB_D3DFMT_DXT4: + return MAKEFOURCC('D', 'X', 'T', '4'); + default: + return 0; + } +} + +DWORD BytesPerPixelFromFormat(XB_D3DFORMAT format) +{ + switch (format) + { + case XB_D3DFMT_A8R8G8B8: + case XB_D3DFMT_LIN_A8R8G8B8: + case XB_D3DFMT_DXT4: + return 4; + case XB_D3DFMT_P8: + case XB_D3DFMT_DXT1: + case XB_D3DFMT_DXT2: + return 1; + default: + return 0; + } +} + +bool IsPalettedFormat(XB_D3DFORMAT format) +{ + if (format == XB_D3DFMT_P8) + return true; + return false; +} + +void ParseTextureHeader(D3DTexture *tex, XB_D3DFORMAT &fmt, DWORD &width, DWORD &height, DWORD &pitch, DWORD &offset) +{ + fmt = (XB_D3DFORMAT)((tex->Format & 0xff00) >> 8); + offset = tex->Data; + if (tex->Size) + { + width = (tex->Size & 0x00000fff) + 1; + height = ((tex->Size & 0x00fff000) >> 12) + 1; + pitch = (((tex->Size & 0xff000000) >> 24) + 1) << 6; + } + else + { + width = 1 << ((tex->Format & 0x00f00000) >> 20); + height = 1 << ((tex->Format & 0x0f000000) >> 24); + pitch = width * BytesPerPixelFromFormat(fmt); + } +} + +bool IsSwizzledFormat(XB_D3DFORMAT format) +{ + switch (format) + { + case XB_D3DFMT_A8R8G8B8: + case XB_D3DFMT_P8: + return true; + default: + return false; + } +} + +// Unswizzle. +// Format is: + +// 00 01 04 05 +// 02 03 06 07 +// 08 09 12 13 +// 10 11 14 15 ... + +// Currently only works for 32bit and 8bit textures, with power of 2 width and height +void Unswizzle(const void *src, unsigned int depth, unsigned int width, unsigned int height, void *dest) +{ + if (height == 0 || width == 0) + return; + + for (UINT y = 0; y < height; y++) + { + UINT sy = 0; + if (y < width) + { + for (int bit = 0; bit < 16; bit++) + sy |= ((y >> bit) & 1) << (2*bit); + sy <<= 1; // y counts twice + } + else + { + UINT y_mask = y % width; + for (int bit = 0; bit < 16; bit++) + sy |= ((y_mask >> bit) & 1) << (2*bit); + sy <<= 1; // y counts twice + sy += (y / width) * width * width; + } + BYTE *d = (BYTE *)dest + y * width * depth; + for (UINT x = 0; x < width; x++) + { + UINT sx = 0; + if (x < height * 2) + { + for (int bit = 0; bit < 16; bit++) + sx |= ((x >> bit) & 1) << (2*bit); + } + else + { + int x_mask = x % (2*height); + for (int bit = 0; bit < 16; bit++) + sx |= ((x_mask >> bit) & 1) << (2*bit); + sx += (x / (2 * height)) * 2 * height * height; + } + BYTE *s = (BYTE *)src + (sx + sy)*depth; + for (unsigned int i = 0; i < depth; ++i) + *d++ = *s++; + } + } +} + +void DXT1toARGB(const void *src, void *dest, unsigned int destWidth) +{ + const BYTE *b = (const BYTE *)src; + // colour is in R5G6B5 format, convert to R8G8B8 + DWORD colour[4]; + BYTE red[4]; + BYTE green[4]; + BYTE blue[4]; + for (int i = 0; i < 2; i++) + { + red[i] = b[2*i+1] & 0xf8; + green[i] = ((b[2*i+1] & 0x7) << 5) | ((b[2*i] & 0xe0) >> 3); + blue[i] = (b[2*i] & 0x1f) << 3; + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i]; + } + if (colour[0] > colour[1]) + { + red[2] = (2 * red[0] + red[1] + 1) / 3; + green[2] = (2 * green[0] + green[1] + 1) / 3; + blue[2] = (2 * blue[0] + blue[1] + 1) / 3; + red[3] = (red[0] + 2 * red[1] + 1) / 3; + green[3] = (green[0] + 2 * green[1] + 1) / 3; + blue[3] = (blue[0] + 2 * blue[1] + 1) / 3; + for (int i = 0; i < 4; i++) + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i] | 0xFF000000; + } + else + { + red[2] = (red[0] + red[1]) / 2; + green[2] = (green[0] + green[1]) / 2; + blue[2] = (blue[0] + blue[1]) / 2; + for (int i = 0; i < 3; i++) + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i] | 0xFF000000; + colour[3] = 0; // transparent + } + // ok, now grab the bits + for (int y = 0; y < 4; y++) + { + DWORD *d = (DWORD *)dest + destWidth * y; + *d++ = colour[(b[4 + y] & 0x03)]; + *d++ = colour[(b[4 + y] & 0x0c) >> 2]; + *d++ = colour[(b[4 + y] & 0x30) >> 4]; + *d++ = colour[(b[4 + y] & 0xc0) >> 6]; + } +} + +void DXT4toARGB(const void *src, void *dest, unsigned int destWidth) +{ + const BYTE *b = (const BYTE *)src; + BYTE alpha[8]; + alpha[0] = b[0]; + alpha[1] = b[1]; + if (alpha[0] > alpha[1]) + { + alpha[2] = (6 * alpha[0] + 1 * alpha[1]+ 3) / 7; + alpha[3] = (5 * alpha[0] + 2 * alpha[1] + 3) / 7; // bit code 011 + alpha[4] = (4 * alpha[0] + 3 * alpha[1] + 3) / 7; // bit code 100 + alpha[5] = (3 * alpha[0] + 4 * alpha[1] + 3) / 7; // bit code 101 + alpha[6] = (2 * alpha[0] + 5 * alpha[1] + 3) / 7; // bit code 110 + alpha[7] = (1 * alpha[0] + 6 * alpha[1] + 3) / 7; // bit code 111 + } + else + { + alpha[2] = (4 * alpha[0] + 1 * alpha[1] + 2) / 5; // Bit code 010 + alpha[3] = (3 * alpha[0] + 2 * alpha[1] + 2) / 5; // Bit code 011 + alpha[4] = (2 * alpha[0] + 3 * alpha[1] + 2) / 5; // Bit code 100 + alpha[5] = (1 * alpha[0] + 4 * alpha[1] + 2) / 5; // Bit code 101 + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } + // ok, now grab the bits + BYTE a[4][4]; + a[0][0] = alpha[(b[2] & 0xe0) >> 5]; + a[0][1] = alpha[(b[2] & 0x1c) >> 2]; + a[0][2] = alpha[((b[2] & 0x03) << 1) | ((b[3] & 0x80) >> 7)]; + a[0][3] = alpha[(b[3] & 0x70) >> 4]; + a[1][0] = alpha[(b[3] & 0x0e) >> 1]; + a[1][1] = alpha[((b[3] & 0x01) << 2) | ((b[4] & 0xc0) >> 6)]; + a[1][2] = alpha[(b[4] & 0x38) >> 3]; + a[1][3] = alpha[(b[4] & 0x07)]; + a[2][0] = alpha[(b[5] & 0xe0) >> 5]; + a[2][1] = alpha[(b[5] & 0x1c) >> 2]; + a[2][2] = alpha[((b[5] & 0x03) << 1) | ((b[6] & 0x80) >> 7)]; + a[2][3] = alpha[(b[6] & 0x70) >> 4]; + a[3][0] = alpha[(b[6] & 0x0e) >> 1]; + a[3][1] = alpha[((b[6] & 0x01) << 2) | ((b[7] & 0xc0) >> 6)]; + a[3][2] = alpha[(b[7] & 0x38) >> 3]; + a[3][3] = alpha[(b[7] & 0x07)]; + + b = (BYTE *)src + 8; + // colour is in R5G6B5 format, convert to R8G8B8 + DWORD colour[4]; + BYTE red[4]; + BYTE green[4]; + BYTE blue[4]; + for (int i = 0; i < 2; i++) + { + red[i] = b[2*i+1] & 0xf8; + green[i] = ((b[2*i+1] & 0x7) << 5) | ((b[2*i] & 0xe0) >> 3); + blue[i] = (b[2*i] & 0x1f) << 3; + } + red[2] = (2 * red[0] + red[1] + 1) / 3; + green[2] = (2 * green[0] + green[1] + 1) / 3; + blue[2] = (2 * blue[0] + blue[1] + 1) / 3; + red[3] = (red[0] + 2 * red[1] + 1) / 3; + green[3] = (green[0] + 2 * green[1] + 1) / 3; + blue[3] = (blue[0] + 2 * blue[1] + 1) / 3; + for (int i = 0; i < 4; i++) + colour[i] = (red[i] << 16) | (green[i] << 8) | blue[i]; + // and assign them to our texture + for (int y = 0; y < 4; y++) + { + DWORD *d = (DWORD *)dest + destWidth * y; + *d++ = colour[(b[4 + y] & 0x03)] | (a[y][0] << 24); + *d++ = colour[(b[4 + y] & 0x0e) >> 2] | (a[y][1] << 24); + *d++ = colour[(b[4 + y] & 0x30) >> 4] | (a[y][2] << 24); + *d++ = colour[(b[4 + y] & 0xe0) >> 6] | (a[y][3] << 24); + } + +} + +void ConvertDXT1(const void *src, unsigned int width, unsigned int height, void *dest) +{ + for (unsigned int y = 0; y < height; y += 4) + { + for (unsigned int x = 0; x < width; x += 4) + { + const BYTE *s = (const BYTE *)src + y * width / 2 + x * 2; + DWORD *d = (DWORD *)dest + y * width + x; + DXT1toARGB(s, d, width); + } + } +} + +void ConvertDXT4(const void *src, unsigned int width, unsigned int height, void *dest) +{ + // [4 4 4 4][4 4 4 4] + // + // + // + for (unsigned int y = 0; y < height; y += 4) + { + for (unsigned int x = 0; x < width; x += 4) + { + const BYTE *s = (const BYTE *)src + y * width + x * 4; + DWORD *d = (DWORD *)dest + y * width + x; + DXT4toARGB(s, d, width); + } + } +} + +void GetTextureFromData(D3DTexture* pTex, void* texData, std::unique_ptr<CTexture>* ppTexture) +{ + XB_D3DFORMAT fmt; + DWORD width, height, pitch, offset; + ParseTextureHeader(pTex, fmt, width, height, pitch, offset); + + *ppTexture = CTexture::CreateTexture(width, height, XB_FMT_A8R8G8B8); + + if (*ppTexture) + { + BYTE *texDataStart = (BYTE *)texData; + COLOR *color = (COLOR *)texData; + texDataStart += offset; +/* DXMERGE - We should really support DXT1,DXT2 and DXT4 in both renderers + Perhaps we should extend CTexture::Update() to support a bunch of different texture types + Rather than assuming linear 32bits + We could just override, as at least then all the loading code from various texture formats + will be in one place + + BYTE *dstPixels = (BYTE *)lr.pBits; + DWORD destPitch = lr.Pitch; + if (fmt == XB_D3DFMT_DXT1) // Not sure if these are 100% correct, but they seem to work :P + { + pitch /= 2; + destPitch /= 4; + } + else if (fmt == XB_D3DFMT_DXT2) + { + destPitch /= 4; + } + else if (fmt == XB_D3DFMT_DXT4) + { + pitch /= 4; + destPitch /= 4; + } +*/ + if (fmt == XB_D3DFMT_DXT1) + { + pitch = width * 4; + BYTE *decoded = new BYTE[pitch * height]; + ConvertDXT1(texDataStart, width, height, decoded); + texDataStart = decoded; + } + else if (fmt == XB_D3DFMT_DXT2 || fmt == XB_D3DFMT_DXT4) + { + pitch = width * 4; + BYTE *decoded = new BYTE[pitch * height]; + ConvertDXT4(texDataStart, width, height, decoded); + texDataStart = decoded; + } + if (IsSwizzledFormat(fmt)) + { // first we unswizzle + BYTE *unswizzled = new BYTE[pitch * height]; + Unswizzle(texDataStart, BytesPerPixelFromFormat(fmt), width, height, unswizzled); + texDataStart = unswizzled; + } + + if (IsPalettedFormat(fmt)) + (*ppTexture)->LoadPaletted(width, height, pitch, XB_FMT_A8R8G8B8, texDataStart, color); + else + (*ppTexture)->LoadFromMemory(width, height, pitch, XB_FMT_A8R8G8B8, true, texDataStart); + + if (IsSwizzledFormat(fmt) || fmt == XB_D3DFMT_DXT1 || fmt == XB_D3DFMT_DXT2 || fmt == XB_D3DFMT_DXT4) + { + delete[] texDataStart; + } + } +} |