307 lines
9.2 KiB
C++
307 lines
9.2 KiB
C++
// dds.cpp
|
|
//
|
|
// Copyright (C) 2001, Chris Laurel
|
|
//
|
|
// 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 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <celutil/debug.h>
|
|
#include <celutil/bytes.h>
|
|
#include <celengine/image.h>
|
|
#include "glsupport.h"
|
|
#include "dds_decompress.h"
|
|
|
|
using namespace celestia;
|
|
using namespace std;
|
|
|
|
|
|
struct DDPixelFormat
|
|
{
|
|
uint32_t size;
|
|
uint32_t flags;
|
|
uint32_t fourCC;
|
|
uint32_t bpp;
|
|
uint32_t redMask;
|
|
uint32_t greenMask;
|
|
uint32_t blueMask;
|
|
uint32_t alphaMask;
|
|
};
|
|
|
|
struct DDSCaps
|
|
{
|
|
uint32_t caps;
|
|
uint32_t caps2;
|
|
uint32_t caps3;
|
|
uint32_t caps4;
|
|
};
|
|
|
|
struct DDColorKey
|
|
{
|
|
uint32_t lowVal;
|
|
uint32_t highVal;
|
|
};
|
|
|
|
struct DDSurfaceDesc
|
|
{
|
|
uint32_t size;
|
|
uint32_t flags;
|
|
uint32_t height;
|
|
uint32_t width;
|
|
uint32_t pitch;
|
|
uint32_t depth;
|
|
uint32_t mipMapLevels;
|
|
uint32_t alphaBitDepth;
|
|
uint32_t reserved;
|
|
uint32_t surface;
|
|
|
|
DDColorKey ckDestOverlay;
|
|
DDColorKey ckDestBlt;
|
|
DDColorKey ckSrcOverlay;
|
|
DDColorKey ckSrcBlt;
|
|
|
|
DDPixelFormat format;
|
|
DDSCaps caps;
|
|
|
|
uint32_t textureStage;
|
|
};
|
|
|
|
|
|
static uint32_t FourCC(const char* s)
|
|
{
|
|
return (((uint32_t) s[3] << 24) |
|
|
((uint32_t) s[2] << 16) |
|
|
((uint32_t) s[1] << 8) |
|
|
(uint32_t) s[0]);
|
|
}
|
|
|
|
|
|
#define DDPF_RGB 0x40
|
|
#define DDPF_FOURCC 0x04
|
|
|
|
|
|
// decompress a DXTc texture, taken from https://github.com/ptitSeb/gl4es
|
|
GLvoid *decompressDXTc(GLsizei width, GLsizei height, GLenum format, int transparent0, int* simpleAlpha, int* complexAlpha, ifstream &in) {
|
|
// decompress a DXTc image
|
|
// get pixel size of decompressed image => fixed RGBA
|
|
int pixelsize = 4;
|
|
// TODO: check with the size of the input data stream if the stream is in fact decompressed
|
|
// alloc memory
|
|
GLvoid *pixels = malloc(((width + 3) & ~3) * ((height + 3) & ~3) * pixelsize);
|
|
// decompress loop
|
|
int blocksize = 0;
|
|
#define DDS_MAX_BLOCK_SIZE 16
|
|
switch (format)
|
|
{
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
blocksize = 8;
|
|
break;
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
blocksize = 16;
|
|
break;
|
|
}
|
|
char block[DDS_MAX_BLOCK_SIZE]; // enough to hold DXT1/3/5 blocks
|
|
for (int y = 0; y < height; y += 4)
|
|
{
|
|
for (int x = 0; x < width; x += 4)
|
|
{
|
|
if (!in.good())
|
|
{
|
|
free(pixels);
|
|
return nullptr;
|
|
}
|
|
in.read(block, blocksize);
|
|
switch(format)
|
|
{
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
DecompressBlockDXT1(x, y, width, (uint8_t*)block, transparent0, simpleAlpha, complexAlpha, (uint32_t *)pixels);
|
|
break;
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
DecompressBlockDXT3(x, y, width, (uint8_t*)block, transparent0, simpleAlpha, complexAlpha, (uint32_t *)pixels);
|
|
break;
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
DecompressBlockDXT5(x, y, width, (uint8_t*)block, transparent0, simpleAlpha, complexAlpha, (uint32_t *)pixels);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return pixels;
|
|
}
|
|
|
|
Image* LoadDDSImage(const fs::path& filename)
|
|
{
|
|
ifstream in(filename.string(), ios::in | ios::binary);
|
|
if (!in.good())
|
|
{
|
|
DPRINTF(LOG_LEVEL_ERROR, "Error opening DDS texture file %s.\n", filename);
|
|
return nullptr;
|
|
}
|
|
|
|
char header[4];
|
|
in.read(header, sizeof header);
|
|
if (header[0] != 'D' || header[1] != 'D' ||
|
|
header[2] != 'S' || header[3] != ' ')
|
|
{
|
|
DPRINTF(LOG_LEVEL_ERROR, "DDS texture file %s has bad header.\n", filename);
|
|
return nullptr;
|
|
}
|
|
|
|
DDSurfaceDesc ddsd;
|
|
in.read(reinterpret_cast<char*>(&ddsd), sizeof ddsd);
|
|
LE_TO_CPU_INT32(ddsd.size, ddsd.size);
|
|
LE_TO_CPU_INT32(ddsd.pitch, ddsd.pitch);
|
|
LE_TO_CPU_INT32(ddsd.width, ddsd.width);
|
|
LE_TO_CPU_INT32(ddsd.height, ddsd.height);
|
|
LE_TO_CPU_INT32(ddsd.mipMapLevels, ddsd.mipMapLevels);
|
|
LE_TO_CPU_INT32(ddsd.format.flags, ddsd.format.flags);
|
|
LE_TO_CPU_INT32(ddsd.format.redMask, ddsd.format.redMask);
|
|
LE_TO_CPU_INT32(ddsd.format.greenMask, ddsd.format.greenMask);
|
|
LE_TO_CPU_INT32(ddsd.format.blueMask, ddsd.format.blueMask);
|
|
LE_TO_CPU_INT32(ddsd.format.alphaMask, ddsd.format.alphaMask);
|
|
LE_TO_CPU_INT32(ddsd.format.bpp, ddsd.format.bpp);
|
|
LE_TO_CPU_INT32(ddsd.format.fourCC, ddsd.format.fourCC);
|
|
|
|
int format = -1;
|
|
|
|
if (ddsd.format.fourCC != 0)
|
|
{
|
|
if (ddsd.format.fourCC == FourCC("DXT1"))
|
|
{
|
|
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
|
}
|
|
else if (ddsd.format.fourCC == FourCC("DXT3"))
|
|
{
|
|
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
|
}
|
|
else if (ddsd.format.fourCC == FourCC("DXT5"))
|
|
{
|
|
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
|
}
|
|
else
|
|
{
|
|
cerr << "Unknown FourCC in DDS file: " << ddsd.format.fourCC;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
clog << "DDS Format: " << ddsd.format.fourCC << '\n';
|
|
if (ddsd.format.bpp == 32)
|
|
{
|
|
if (ddsd.format.redMask == 0x00ff0000 &&
|
|
ddsd.format.greenMask == 0x0000ff00 &&
|
|
ddsd.format.blueMask == 0x000000ff &&
|
|
ddsd.format.alphaMask == 0xff000000)
|
|
{
|
|
format = GL_BGRA_EXT;
|
|
}
|
|
else if (ddsd.format.redMask == 0x000000ff &&
|
|
ddsd.format.greenMask == 0x0000ff00 &&
|
|
ddsd.format.blueMask == 0x00ff0000 &&
|
|
ddsd.format.alphaMask == 0xff000000)
|
|
{
|
|
format = GL_RGBA;
|
|
}
|
|
}
|
|
else if (ddsd.format.bpp == 24)
|
|
{
|
|
if (ddsd.format.redMask == 0x000000ff &&
|
|
ddsd.format.greenMask == 0x0000ff00 &&
|
|
ddsd.format.blueMask == 0x00ff0000)
|
|
{
|
|
format = GL_RGB;
|
|
}
|
|
#ifndef GL_ES
|
|
else if (ddsd.format.redMask == 0x00ff0000 &&
|
|
ddsd.format.greenMask == 0x0000ff00 &&
|
|
ddsd.format.blueMask == 0x000000ff)
|
|
{
|
|
format = GL_BGR;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (format == -1)
|
|
{
|
|
DPRINTF(LOG_LEVEL_ERROR, "Unsupported format for DDS texture file %s.\n", filename);
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the platform supports compressed DTXc textures
|
|
if (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
|
|
format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
|
|
format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
|
|
{
|
|
if (!gl::EXT_texture_compression_s3tc)
|
|
{
|
|
// DXTc texture not supported, decompress DXTc to RGBA
|
|
GLvoid *pixels = NULL;
|
|
int simpleAlpha = 0;
|
|
int complexAlpha = 0;
|
|
int transparent0 = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 1 : 0;
|
|
if ((ddsd.width & 3) || (ddsd.height & 3))
|
|
{
|
|
GLvoid *tmp;
|
|
GLsizei nw = ddsd.width;
|
|
GLsizei nh = ddsd.height;
|
|
if (nw < 4) nw = 4;
|
|
if (nh < 4) nh = 4;
|
|
tmp = decompressDXTc(nw, nh, format, transparent0, &simpleAlpha, &complexAlpha, in);
|
|
if (tmp != nullptr)
|
|
{
|
|
pixels = malloc(4 * ddsd.width * ddsd.height);
|
|
// crop
|
|
for (int y=0; y<ddsd.height; y++)
|
|
memcpy((char *)pixels + y * ddsd.width * 4, (char *)tmp + y * nw * 4, ddsd.width * 4);
|
|
free(tmp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pixels = decompressDXTc(ddsd.width, ddsd.height, format, transparent0, &simpleAlpha, &complexAlpha, in);
|
|
}
|
|
|
|
if (pixels == nullptr)
|
|
{
|
|
DPRINTF(LOG_LEVEL_ERROR, "Failed to decompress DDS texture file %s.\n", filename);
|
|
return nullptr;
|
|
}
|
|
|
|
Image *img = new Image(GL_RGBA, ddsd.width, ddsd.height);
|
|
memcpy(img->getPixels(), pixels, 4 * ddsd.width * ddsd.height);
|
|
free(pixels);
|
|
return img;
|
|
}
|
|
}
|
|
|
|
// TODO: Verify that the reported texture size matches the amount of
|
|
// data expected.
|
|
|
|
Image* img = new Image(format,
|
|
(int) ddsd.width,
|
|
(int) ddsd.height,
|
|
max(ddsd.mipMapLevels, 1u));
|
|
in.read(reinterpret_cast<char*>(img->getPixels()), img->getSize());
|
|
if (!in.eof() && !in.good())
|
|
{
|
|
DPRINTF(LOG_LEVEL_ERROR, "Failed reading data from DDS texture file %s.\n", filename);
|
|
delete img;
|
|
return nullptr;
|
|
}
|
|
|
|
#if 0
|
|
cout << "sizeof(ddsd) = " << sizeof(ddsd) << '\n';
|
|
cout << "dimensions: " << ddsd.width << 'x' << ddsd.height << '\n';
|
|
cout << "mipmap levels: " << ddsd.mipMapLevels << '\n';
|
|
cout << "fourCC: " << ddsd.format.fourCC << '\n';
|
|
cout << "bpp: " << ddsd.format.bpp << '\n';
|
|
#endif
|
|
|
|
return img;
|
|
}
|