forked from 0ad/0ad
lib: add big endian readers; add VC intrinsic bswap routines
tex: improve flip / bgr convert (now generalized "transforms"); add support for transforming image before encoding wsdl: add SDL_ShowCursor, minimize when tasking out main: WriteScreenshot improved, fixes flipped screenshots This was SVN commit r1689.
This commit is contained in:
parent
33051d0421
commit
56949e6fc4
@ -343,6 +343,28 @@ u32 read_le32(const void* p)
|
||||
}
|
||||
|
||||
|
||||
u16 read_be16(const void* p)
|
||||
{
|
||||
#if SDL_BYTE_ORDER == SDL_BIG_ENDIAN
|
||||
return *(u16*)p;
|
||||
#else
|
||||
const u8* _p = (const u8*)p;
|
||||
return (u16)_p[0] | (u16)_p[1] << 8;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
u32 read_be32(const void* p)
|
||||
{
|
||||
#if SDL_BYTE_ORDER == SDL_BIG_ENDIAN
|
||||
return *(u32*)p;
|
||||
#else
|
||||
return SDL_Swap32(*(u32*)p);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
// big endian!
|
||||
void base32(const int len, const u8* in, u8* out)
|
||||
{
|
||||
|
@ -336,6 +336,8 @@ extern u16 subusw(u16 x, u16 y);
|
||||
|
||||
extern u16 read_le16(const void* p);
|
||||
extern u32 read_le32(const void* p);
|
||||
extern u16 read_be16(const void* p);
|
||||
extern u32 read_be32(const void* p);
|
||||
|
||||
|
||||
extern bool is_pow2(long n);
|
||||
|
@ -108,21 +108,26 @@ struct Codec
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// texture orientation
|
||||
// image transformations
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TGA and BMP are normally bottom-up, while PNG and JPG are always top-down.
|
||||
// we don't want to dump the burden of flipping them to a common orientation
|
||||
// on the application - that would affect all drawing code.
|
||||
// instead, we flip the texture data to <global_orientation> when loading.
|
||||
// TexInfo.flags indicate deviations from the standard image format
|
||||
// (left-to-right, RGBA layout, up/down=global_orientation).
|
||||
//
|
||||
// codecs using libraries with a row-array mechanism (jpg, png) can
|
||||
// just invert the row array pointers; otherwise (e.g. tga, bmp),
|
||||
// we flip the texel rows in-place.
|
||||
// we don't want to dump the burden of dealing with that on
|
||||
// the app - that would affect all drawing code.
|
||||
// instead, we convert as much as is convenient at load-time.
|
||||
//
|
||||
// this is slow; in release builds, we should be using formats optimized
|
||||
// for their intended use that don't require preprocessing.
|
||||
// supported transforms: BGR<->RGB, row flip.
|
||||
// we don't bother providing a true S3TC aka DXTC codec -
|
||||
// they are always passed unmodified to OpenGL, and decoding is complicated.
|
||||
//
|
||||
// converting is slow; in release builds, we should be using formats
|
||||
// optimized for their intended use that don't require preprocessing.
|
||||
|
||||
|
||||
// switch between top-down and bottom-up orientation.
|
||||
//
|
||||
// the default top-down is to match the Photoshop DDS plugin's output.
|
||||
// DDS is the optimized format, so we don't want to have to flip that.
|
||||
@ -131,46 +136,97 @@ struct Codec
|
||||
// we have to go with what the DDS encoder uses.
|
||||
// - flipping DDS is possible without re-encoding; we'd have to shuffle
|
||||
// around the pixel values inside the 4x4 blocks.
|
||||
//
|
||||
// the app can change orientation, e.g. to speed up loading
|
||||
// "upside-down" formats, or to match OpenGL's bottom-up convention.
|
||||
|
||||
static TexOrientation global_orientation = TEX_TOP_DOWN;
|
||||
static int global_orientation = TEX_TOP_DOWN;
|
||||
|
||||
void tex_set_global_orientation(TexOrientation o)
|
||||
void tex_set_global_orientation(int o)
|
||||
{
|
||||
if(o == TEX_TOP_DOWN || o == TEX_BOTTOM_UP)
|
||||
global_orientation = o;
|
||||
else
|
||||
debug_warn("tex_set_global_orientation: invalid param");
|
||||
}
|
||||
|
||||
|
||||
// if fmt_orientation doesn't match the desired global orientation,
|
||||
// tex will be flipped in-place. pitch is the total row width [bytes].
|
||||
void flip_tex_rows(void* tex, size_t pitch, size_t h, TexOrientation fmt_orientation)
|
||||
// somewhat optimized (loops are hoisted, cache associativity accounted for)
|
||||
int transform(TexInfo* t, u8* img, int transforms)
|
||||
{
|
||||
// already correct, we're done
|
||||
if(fmt_orientation == global_orientation)
|
||||
return;
|
||||
// nothing to do - bail.
|
||||
if(!transforms)
|
||||
return 0;
|
||||
|
||||
size_t line = 0; // counter
|
||||
const u8* src;
|
||||
u8* dst;
|
||||
const uint w = t->w, h = t->h, bpp_8 = t->bpp / 8;
|
||||
const size_t pitch = w * bpp_8;
|
||||
|
||||
// setup row source/destination pointers (simplifies outer loop)
|
||||
u8* dst = img;
|
||||
const u8* src = img;
|
||||
ssize_t row_ofs = (ssize_t)pitch;
|
||||
// avoid y*pitch multiply in row loop; instead, add row_ofs.
|
||||
void* clone_img = 0;
|
||||
// flipping rows (0,1,2 -> 2,1,0)
|
||||
if(transforms & TEX_ORIENTATION)
|
||||
{
|
||||
// L1 cache is typically A2 => swapping in-place with a line buffer
|
||||
// leads to thrashing. we'll assume the whole texture*2 fits in cache,
|
||||
// allocate a copy, and transfer directly from there.
|
||||
//
|
||||
// note: we don't want to return a new buffer: the user assumes
|
||||
// buffer address will remain unchanged.
|
||||
const size_t size = h*pitch;
|
||||
clone_img = mem_alloc(size, 32*KB);
|
||||
if(!clone_img)
|
||||
return ERR_NO_MEM;
|
||||
memcpy(clone_img, img, size);
|
||||
src = (const u8*)clone_img+size-pitch; // last row
|
||||
row_ofs = -(ssize_t)pitch;
|
||||
}
|
||||
|
||||
const size_t size = pitch * h;
|
||||
void* clone_buf = mem_alloc(size, 32*KB);
|
||||
memcpy(clone_buf, tex, size);
|
||||
src = (const u8*)clone_buf + size-pitch;
|
||||
dst = (u8*)tex;
|
||||
for(; line < h; line++)
|
||||
|
||||
// no BGR convert necessary
|
||||
if(!(transforms & TEX_BGR))
|
||||
for(uint y = 0; y < h; y++)
|
||||
{
|
||||
memcpy(dst, src, pitch);
|
||||
src -= pitch;
|
||||
dst += pitch;
|
||||
src += row_ofs;
|
||||
}
|
||||
mem_free(clone_buf);
|
||||
// RGB <-> BGR
|
||||
else if(bpp_8 == 3)
|
||||
for(uint y = 0; y < h; y++)
|
||||
{
|
||||
for(uint x = 0; x < w; x++)
|
||||
{
|
||||
// need temporaries in case src == dst (i.e. not flipping)
|
||||
const u8 b = src[0], g = src[1], r = src[2];
|
||||
dst[0] = r; dst[1] = g; dst[2] = b;
|
||||
dst += 3;
|
||||
src += 3;
|
||||
}
|
||||
src += row_ofs - pitch; // flip? previous row : stay
|
||||
}
|
||||
// RGBA <-> BGRA
|
||||
else
|
||||
for(uint y = 0; y < h; y++)
|
||||
{
|
||||
for(uint x = 0; x < w; x++)
|
||||
{
|
||||
// need temporaries in case src == dst (i.e. not flipping)
|
||||
const u8 b = src[0], g = src[1], r = src[2], a = src[3];
|
||||
dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = a;
|
||||
dst += 4;
|
||||
src += 4;
|
||||
}
|
||||
src += row_ofs - pitch; // flip? previous row : stay
|
||||
}
|
||||
|
||||
if(clone_img)
|
||||
mem_free(clone_img);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -178,16 +234,19 @@ typedef const u8* RowPtr;
|
||||
typedef RowPtr* RowArray;
|
||||
|
||||
// allocate and set up rows to point into the given texture data.
|
||||
// invert if the format's orientation doesn't match the current global setting.
|
||||
// invert if transforms & TEX_ORIENTATION; caller is responsible for comparing
|
||||
// a file format's orientation with the global setting (when decoding) or
|
||||
// with the image format (when encoding).
|
||||
// caller must free() rows when done.
|
||||
static int alloc_rows(const u8* tex, size_t h, size_t pitch,
|
||||
TexOrientation fmt_orientation, RowArray& rows)
|
||||
int transforms, RowArray& rows)
|
||||
{
|
||||
rows = (RowArray)malloc(h * sizeof(RowPtr));
|
||||
if(!rows)
|
||||
return ERR_NO_MEM;
|
||||
|
||||
// rows are inverted; current position pointer counts backwards.
|
||||
if(fmt_orientation != global_orientation)
|
||||
if(transforms & TEX_ORIENTATION)
|
||||
{
|
||||
RowPtr pos = tex + pitch*h;
|
||||
for(size_t i = 0; i < h; i++)
|
||||
@ -215,10 +274,10 @@ static int alloc_rows(const u8* tex, size_t h, size_t pitch,
|
||||
|
||||
|
||||
// write header and image out to file <fn>.
|
||||
// pixels are BGR-flipped if desired. bpp can be 8, 24, or 32.
|
||||
// pixels are BGR-flipped if flags & TEX_BGR. bpp can be 8, 24, or 32.
|
||||
// note: if bpp=32, alpha is assumed to be the last component.
|
||||
static int write_img(const char* fn, const void* hdr, size_t hdr_size, const void* img, size_t img_size,
|
||||
bool flip_bgr, int bpp)
|
||||
static int write_img(const char* fn, const void* hdr, size_t hdr_size,
|
||||
int bpp, const void* img, size_t img_size)
|
||||
{
|
||||
const size_t file_size = hdr_size + img_size;
|
||||
u8* file = (u8*)mem_alloc(file_size);
|
||||
@ -226,37 +285,7 @@ static int write_img(const char* fn, const void* hdr, size_t hdr_size, const voi
|
||||
return ERR_NO_MEM;
|
||||
|
||||
memcpy(file, hdr, hdr_size);
|
||||
|
||||
// fast: no conversion necessary
|
||||
if(!flip_bgr || bpp == 8)
|
||||
memcpy((char*)file+hdr_size, img, img_size);
|
||||
// slow: convert each pixel
|
||||
else
|
||||
{
|
||||
size_t pixels_left = img_size / (bpp / 8);
|
||||
u8* dst = (u8*)file+hdr_size;
|
||||
const u8* src = (const u8*)img;
|
||||
|
||||
if(bpp == 24)
|
||||
while(pixels_left--)
|
||||
{
|
||||
dst[0] = src[2];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[0];
|
||||
dst += 3;
|
||||
src += 3;
|
||||
}
|
||||
else
|
||||
while(pixels_left--)
|
||||
{
|
||||
dst[0] = src[2];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[0];
|
||||
dst[3] = src[3];
|
||||
dst += 4;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_ERR(vfs_store(fn, file, file_size, FILE_NO_AIO));
|
||||
mem_free(file);
|
||||
@ -486,7 +515,8 @@ enum TgaImgType
|
||||
enum TgaImgDesc
|
||||
{
|
||||
TGA_RIGHT_TO_LEFT = BIT(4),
|
||||
TGA_TOP_DOWN = BIT(5)
|
||||
TGA_TOP_DOWN = BIT(5),
|
||||
TGA_BOTTOM_UP = 0 // opposite of TGA_TOP_DOWN
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@ -557,19 +587,16 @@ fail:
|
||||
}
|
||||
|
||||
const u8 type = hdr->img_type;
|
||||
const u16 w = read_le16(&hdr->w);
|
||||
const u16 h = read_le16(&hdr->h);
|
||||
const u8 bpp = hdr->bpp;
|
||||
const uint w = read_le16(&hdr->w);
|
||||
const uint h = read_le16(&hdr->h);
|
||||
const uint bpp = hdr->bpp;
|
||||
const u8 desc = hdr->img_desc;
|
||||
|
||||
const u8 alpha_bits = desc & 0x0f;
|
||||
const int orientation = (desc & TGA_TOP_DOWN)? TEX_TOP_DOWN : TEX_BOTTOM_UP;
|
||||
u8* const img = file + hdr_size;
|
||||
const size_t img_size = w * h * bpp/8;
|
||||
|
||||
const size_t pitch = w * bpp/8;
|
||||
const size_t img_size = h * pitch;
|
||||
void* img = file + hdr_size;
|
||||
|
||||
TexOrientation fo = (desc & TGA_TOP_DOWN)? TEX_TOP_DOWN : TEX_BOTTOM_UP;
|
||||
flip_tex_rows(img, pitch, h, fo);
|
||||
|
||||
int flags = 0;
|
||||
if(alpha_bits != 0)
|
||||
@ -595,6 +622,9 @@ fail:
|
||||
t->bpp = bpp;
|
||||
t->flags = flags;
|
||||
|
||||
const int transforms = orientation ^ global_orientation;
|
||||
transform(t, img, transforms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -603,20 +633,28 @@ static int tga_encode(TexInfo* t, const char* fn, u8* img, size_t img_size)
|
||||
{
|
||||
CHECK_ERR(fmt_8_or_24_or_32(t->bpp, t->flags));
|
||||
|
||||
TgaImgDesc img_desc = (t->flags & TEX_TOP_DOWN)? TGA_TOP_DOWN : TGA_BOTTOM_UP;
|
||||
TgaImgType img_type = (t->flags & TEX_GRAY)? TGA_GRAY : TGA_TRUE_COLOR;
|
||||
|
||||
// transform
|
||||
int transforms = t->flags;
|
||||
transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit.
|
||||
transforms ^= TEX_BGR; // TGA is native BGR.
|
||||
transform(t, img, transforms);
|
||||
|
||||
TgaHeader hdr =
|
||||
{
|
||||
0, // no image identifier present
|
||||
0, // no color map present
|
||||
(t->flags & TEX_GRAY)? TGA_GRAY : TGA_TRUE_COLOR,
|
||||
img_type,
|
||||
{0,0,0,0,0}, // unused (color map)
|
||||
0, 0, // unused (origin)
|
||||
t->w,
|
||||
t->h,
|
||||
t->bpp,
|
||||
0 // image descriptor (TgaImgDesc)
|
||||
img_desc
|
||||
};
|
||||
const bool flip_bgr = !(t->flags & TEX_BGR); // TGA is native BGR
|
||||
return write_img(fn, &hdr, sizeof(hdr), img, img_size, flip_bgr, t->bpp);
|
||||
return write_img(fn, &hdr, sizeof(hdr), t->bpp, img, img_size);
|
||||
}
|
||||
|
||||
#endif // #ifndef NO_TGA
|
||||
@ -686,7 +724,7 @@ static inline bool bmp_ext(const char* ext)
|
||||
|
||||
|
||||
// requirements: uncompressed, direct color, bottom up
|
||||
static int bmp_decode(TexInfo* t, const char* fn, u8* in, size_t file_size)
|
||||
static int bmp_decode(TexInfo* t, const char* fn, u8* file, size_t file_size)
|
||||
{
|
||||
const char* err = 0;
|
||||
|
||||
@ -701,21 +739,19 @@ fail:
|
||||
return ERR_CORRUPTED;
|
||||
}
|
||||
|
||||
const BmpHeader* hdr = (const BmpHeader*)in;
|
||||
const BmpHeader* hdr = (const BmpHeader*)file;
|
||||
const long w = (long)read_le32(&hdr->biWidth);
|
||||
const long h_ = (long)read_le32(&hdr->biHeight);
|
||||
const u16 bpp = read_le16(&hdr->biBitCount);
|
||||
const u32 compress = read_le32(&hdr->biCompression);
|
||||
const u32 ofs = read_le32(&hdr->bfOffBits);
|
||||
|
||||
const TexOrientation fo = (h_ < 0)? TEX_TOP_DOWN : TEX_BOTTOM_UP;
|
||||
const int orientation = (h_ < 0)? TEX_TOP_DOWN : TEX_BOTTOM_UP;
|
||||
const long h = abs(h_);
|
||||
|
||||
const size_t pitch = w * bpp/8;
|
||||
const size_t img_size = h * pitch;
|
||||
void* img = in + ofs;
|
||||
|
||||
flip_tex_rows(img, pitch, h, fo);
|
||||
u8* const img = file + ofs;
|
||||
|
||||
int flags = TEX_BGR;
|
||||
if(bpp == 32)
|
||||
@ -736,6 +772,9 @@ fail:
|
||||
t->bpp = bpp;
|
||||
t->flags = flags;
|
||||
|
||||
const int transforms = orientation ^ global_orientation;
|
||||
transform(t, img, transforms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -744,8 +783,14 @@ static int bmp_encode(TexInfo* t, const char* fn, u8* img, size_t img_size)
|
||||
{
|
||||
CHECK_ERR(fmt_8_or_24_or_32(t->bpp, t->flags));
|
||||
|
||||
const size_t hdr_size = sizeof(BmpHeader);
|
||||
const size_t hdr_size = sizeof(BmpHeader); // needed for BITMAPFILEHEADER
|
||||
const size_t file_size = hdr_size + img_size;
|
||||
const long h = (t->flags & TEX_TOP_DOWN)? -(int)t->h : t->h;
|
||||
|
||||
int transforms = t->flags;
|
||||
transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit.
|
||||
transforms ^= TEX_BGR; // BMP is native BGR.
|
||||
transform(t, img, transforms);
|
||||
|
||||
const BmpHeader hdr =
|
||||
{
|
||||
@ -758,7 +803,7 @@ static int bmp_encode(TexInfo* t, const char* fn, u8* img, size_t img_size)
|
||||
// BITMAPINFOHEADER
|
||||
40, // biSize = sizeof(BITMAPINFOHEADER)
|
||||
t->w,
|
||||
t->h,
|
||||
h,
|
||||
1, // biPlanes
|
||||
t->bpp,
|
||||
BI_RGB, // biCompression
|
||||
@ -766,8 +811,7 @@ static int bmp_encode(TexInfo* t, const char* fn, u8* img, size_t img_size)
|
||||
0, 0, 0, 0 // unused (bi?PelsPerMeter, biClr*)
|
||||
};
|
||||
|
||||
const bool flip_bgr = !(t->flags & TEX_BGR); // BMP is native BGR
|
||||
return write_img(fn, &hdr, sizeof(hdr), img, img_size, flip_bgr, t->bpp);
|
||||
return write_img(fn, &hdr, sizeof(hdr), t->bpp, img, img_size);
|
||||
}
|
||||
|
||||
#endif // #ifndef NO_BMP
|
||||
@ -798,10 +842,8 @@ static inline bool raw_ext(const char* ext)
|
||||
}
|
||||
|
||||
|
||||
static int raw_decode(TexInfo* t, const char* fn, u8* in, size_t file_size)
|
||||
static int raw_decode(TexInfo* t, const char* fn, u8* file, size_t file_size)
|
||||
{
|
||||
UNUSED(in);
|
||||
|
||||
// TODO: allow 8 bit format. problem: how to differentiate from 32? filename?
|
||||
|
||||
for(uint i = 2; i <= 4; i++)
|
||||
@ -810,6 +852,9 @@ static int raw_decode(TexInfo* t, const char* fn, u8* in, size_t file_size)
|
||||
if(dim*dim*i != file_size)
|
||||
continue;
|
||||
|
||||
const int orientation = TEX_BOTTOM_UP;
|
||||
u8* const img = file;
|
||||
|
||||
// formats are: GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA
|
||||
int flags = (i == 3)? 0 : TEX_ALPHA;
|
||||
|
||||
@ -819,6 +864,9 @@ static int raw_decode(TexInfo* t, const char* fn, u8* in, size_t file_size)
|
||||
t->bpp = i*8;
|
||||
t->flags = flags;
|
||||
|
||||
const int transforms = orientation ^ global_orientation;
|
||||
transform(t, img, transforms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -831,8 +879,13 @@ static int raw_encode(TexInfo* t, const char* fn, u8* img, size_t img_size)
|
||||
{
|
||||
CHECK_ERR(fmt_8_or_24_or_32(t->bpp, t->flags));
|
||||
|
||||
const bool flip_bgr = !(t->flags & TEX_BGR); // RAW is native BGR
|
||||
return write_img(fn, 0, 0, img, img_size, flip_bgr, t->bpp);
|
||||
// transforms
|
||||
const int transforms = t->flags;
|
||||
transforms ^= TEX_BOTTOM_UP; // RAW is native bottom-up.
|
||||
transforms ^= TEX_BGR; // RAW is native BGR.
|
||||
transform(t, img, transforms);
|
||||
|
||||
return write_img(fn, 0, 0, t->bpp, img, img_size);
|
||||
}
|
||||
|
||||
#endif // #ifndef NO_RAW
|
||||
@ -959,7 +1012,8 @@ fail:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int ret = alloc_rows(img, h, pitch, TEX_TOP_DOWN, rows);
|
||||
const int transforms = TEX_TOP_DOWN ^ global_orientation;
|
||||
int ret = alloc_rows(img, h, pitch, transforms, rows);
|
||||
if(ret < 0)
|
||||
{
|
||||
err = ret;
|
||||
@ -1037,6 +1091,9 @@ fail:
|
||||
}
|
||||
|
||||
{
|
||||
const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
|
||||
// PNG is native RGB.
|
||||
|
||||
const png_uint_32 w = t->w, h = t->h;
|
||||
const size_t pitch = w * t->bpp / 8;
|
||||
|
||||
@ -1068,7 +1125,8 @@ fail:
|
||||
png_set_IHDR(png_ptr, info_ptr, w, h, 8, color_type,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
int ret = alloc_rows(img, h, pitch, TEX_TOP_DOWN, rows);
|
||||
const int transforms = TEX_TOP_DOWN ^ t->flags;
|
||||
int ret = alloc_rows(img, h, pitch, transforms, rows);
|
||||
if(ret < 0)
|
||||
{
|
||||
err = ret;
|
||||
@ -1076,7 +1134,7 @@ fail:
|
||||
}
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);
|
||||
png_write_png(png_ptr, info_ptr, png_transforms, 0);
|
||||
|
||||
err = 0;
|
||||
|
||||
@ -1433,83 +1491,110 @@ ret:
|
||||
// limitation: palette images aren't supported
|
||||
static int jpg_encode(TexInfo* t, const char* fn, u8* img, size_t img_size)
|
||||
{
|
||||
return -1;//VFS dest not implemented yet
|
||||
|
||||
const char* msg = 0;
|
||||
int err = -1;
|
||||
|
||||
// freed when ret is reached.
|
||||
png_structp png_ptr = 0;
|
||||
png_infop info_ptr = 0;
|
||||
// freed when ret is reached:
|
||||
struct jpeg_compress_struct cinfo;
|
||||
// contains the JPEG compression parameters and pointers to
|
||||
// working space (allocated as needed by the JPEG library).
|
||||
RowArray rows = 0;
|
||||
Handle hf = 0;
|
||||
// array of pointers to scanlines in img, set by alloc_rows.
|
||||
// jpeg won't output more than a few scanlines at a time,
|
||||
// so we need an output loop anyway, but passing at least 2..4
|
||||
// rows is more efficient in low-quality modes (due to less copying).
|
||||
|
||||
// allocate PNG structures; use default stderr and longjmp error handlers
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
||||
if(!png_ptr)
|
||||
goto fail;
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if(!info_ptr)
|
||||
goto fail;
|
||||
// freed when fail is reached:
|
||||
|
||||
// setup error handling
|
||||
if(setjmp(png_jmpbuf(png_ptr)))
|
||||
// set up our error handler, which overrides jpeg's default
|
||||
// write-to-stderr-and-exit behavior.
|
||||
// notes:
|
||||
// - must be done before jpeg_create_compress, in case that fails
|
||||
// (unlikely, but possible if out of memory).
|
||||
// - valid over cinfo lifetime (avoids dangling pointer in cinfo)
|
||||
JpgErrMgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = jpg_error_exit;
|
||||
jerr.pub.output_message = jpg_output_message;
|
||||
jerr.msg[0] = '\0';
|
||||
// required for "already have message" check in output_message
|
||||
if(setjmp(jerr.call_site))
|
||||
{
|
||||
fail:
|
||||
debug_out("png_encode: %s: %s\n", fn, msg? msg : "unknown");
|
||||
// either JPEG has raised an error, or code below failed.
|
||||
// warn user, and skip to cleanup code.
|
||||
debug_out("jpg_encode: %s: %s\n", fn, msg? msg : "unknown");
|
||||
goto ret;
|
||||
}
|
||||
|
||||
{
|
||||
const png_uint_32 w = t->w, h = t->h;
|
||||
const size_t pitch = w * t->bpp / 8;
|
||||
|
||||
int color_type;
|
||||
switch(t->flags & (TEX_GRAY|TEX_ALPHA))
|
||||
{
|
||||
case TEX_GRAY|TEX_ALPHA:
|
||||
color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
|
||||
break;
|
||||
case TEX_GRAY:
|
||||
color_type = PNG_COLOR_TYPE_GRAY;
|
||||
break;
|
||||
case TEX_ALPHA:
|
||||
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
||||
break;
|
||||
default:
|
||||
color_type = PNG_COLOR_TYPE_RGB;
|
||||
break;
|
||||
}
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
hf = vfs_open(fn, FILE_WRITE|FILE_NO_AIO);
|
||||
if(hf < 0)
|
||||
{
|
||||
err = (int)hf;
|
||||
goto fail;
|
||||
}
|
||||
png_set_write_fn(png_ptr, &hf, png_write, png_flush);
|
||||
/// jpeg_vfs_dest(&cinfo, file, file_size);
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, w, h, 8, color_type,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
//
|
||||
// describe input image
|
||||
//
|
||||
|
||||
int ret = alloc_rows(img, h, pitch, TEX_TOP_DOWN, rows);
|
||||
// required:
|
||||
cinfo.image_width = t->w;
|
||||
cinfo.image_height = t->h;
|
||||
cinfo.input_components = t->bpp / 8;
|
||||
cinfo.in_color_space = (t->bpp == 8)? JCS_GRAYSCALE : JCS_RGB;
|
||||
|
||||
// defaults depend on cinfo.in_color_space already having been set!
|
||||
jpeg_set_defaults(&cinfo);
|
||||
|
||||
// more settings (e.g. quality)
|
||||
|
||||
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
// TRUE ensures that we will write a complete interchange-JPEG file.
|
||||
// don't change unless you are very sure of what you're doing.
|
||||
|
||||
|
||||
// make sure we have RGB
|
||||
const int bgr_transform = t->flags & TEX_BGR; // JPG is native RGB.
|
||||
transform(t, img, bgr_transform);
|
||||
|
||||
const size_t pitch = t->w * t->bpp / 8;
|
||||
const int transform = t->flags ^ TEX_TOP_DOWN;
|
||||
int ret = alloc_rows(img, t->h, pitch, transform, rows);
|
||||
if(ret < 0)
|
||||
{
|
||||
err = ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
|
||||
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, 0);
|
||||
|
||||
// could use cinfo.output_scanline to keep track of progress,
|
||||
// but we need to count lines_left anyway (paranoia).
|
||||
JSAMPARRAY row = (JSAMPARRAY)rows;
|
||||
JDIMENSION lines_left = t->h;
|
||||
while(lines_left != 0)
|
||||
{
|
||||
JDIMENSION lines_read = jpeg_write_scanlines(&cinfo, row, lines_left);
|
||||
row += lines_read;
|
||||
lines_left -= lines_read;
|
||||
|
||||
// we've decoded in-place; no need to further process
|
||||
}
|
||||
|
||||
(void)jpeg_finish_compress(&cinfo);
|
||||
|
||||
if(jerr.pub.num_warnings != 0)
|
||||
debug_out("jpg_encode: corrupt-data warning(s) occurred\n");
|
||||
|
||||
err = 0;
|
||||
|
||||
}
|
||||
|
||||
// shared cleanup
|
||||
ret:
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
free(rows);
|
||||
vfs_close(hf);
|
||||
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
// releases a "good deal" of memory
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -26,9 +26,16 @@
|
||||
enum TexInfoFlags
|
||||
{
|
||||
TEX_DXT = 0x07, // mask; value = {1,3,5}
|
||||
TEX_BGR = 8,
|
||||
TEX_ALPHA = 16,
|
||||
TEX_GRAY = 32,
|
||||
TEX_BGR = 0x08,
|
||||
TEX_ALPHA = 0x10,
|
||||
TEX_GRAY = 0x20,
|
||||
|
||||
// orientation - never returned by tex_load, since it automatically
|
||||
// flips to match global orientation. these are passed to tex_write
|
||||
// to indicate the image orientation, or to tex_set_global_orientation.
|
||||
TEX_BOTTOM_UP = 0x40,
|
||||
TEX_TOP_DOWN = 0x80,
|
||||
TEX_ORIENTATION = TEX_BOTTOM_UP|TEX_TOP_DOWN // mask
|
||||
};
|
||||
|
||||
// minimize size - stored in ogl tex resource control block
|
||||
@ -49,13 +56,7 @@ extern int tex_free(TexInfo* ti);
|
||||
extern int tex_write(const char* fn, int w, int h, int bpp, int flags, void* img);
|
||||
|
||||
|
||||
enum TexOrientation
|
||||
{
|
||||
// value doesn't matter; we XOR global and format's settings
|
||||
TEX_BOTTOM_UP = 0,
|
||||
TEX_TOP_DOWN = 1
|
||||
};
|
||||
|
||||
extern void tex_set_global_orientation(TexOrientation);
|
||||
// param: either TEX_BOTTOM_UP or TEX_TOP_DOWN
|
||||
extern void tex_set_global_orientation(int orientation);
|
||||
|
||||
#endif // __TEX_H__
|
||||
|
@ -114,13 +114,19 @@ static LRESULT CALLBACK wndproc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA
|
||||
app_active = (wParam & 0xffff) != 0;
|
||||
|
||||
gamma_swap(!app_active);
|
||||
debug_out("active=%d\n", app_active);
|
||||
|
||||
if(fullscreen)
|
||||
{
|
||||
// (re)activating
|
||||
if(app_active)
|
||||
{
|
||||
ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
|
||||
ShowWindow(hWnd, SW_RESTORE);
|
||||
}
|
||||
// deactivating
|
||||
else
|
||||
{
|
||||
ChangeDisplaySettings(0, 0);
|
||||
ShowWindow(hWnd, SW_MINIMIZE);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -970,7 +976,21 @@ inline int SDL_WarpMouse(int x, int y)
|
||||
}
|
||||
|
||||
|
||||
|
||||
int SDL_ShowCursor(int toggle)
|
||||
{
|
||||
static int cursor_visible = SDL_ENABLE;
|
||||
if(toggle != SDL_QUERY)
|
||||
{
|
||||
// only call Windows ShowCursor if changing the visibility -
|
||||
// it maintains a counter.
|
||||
if(cursor_visible != toggle)
|
||||
{
|
||||
ShowCursor(toggle);
|
||||
cursor_visible = toggle;
|
||||
}
|
||||
}
|
||||
return cursor_visible;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -86,6 +86,14 @@ extern int SDL_KillThread(SDL_Thread*);
|
||||
|
||||
extern int SDL_WarpMouse(int, int);
|
||||
|
||||
enum ShowCursorToggle
|
||||
{
|
||||
SDL_DISABLE = 0,
|
||||
SDL_ENABLE = 1,
|
||||
SDL_QUERY = 2
|
||||
};
|
||||
extern int SDL_ShowCursor(int toggle);
|
||||
|
||||
|
||||
extern int SDL_SetGamma(float r, float g, float b);
|
||||
|
||||
@ -110,6 +118,9 @@ extern int SDL_SetGamma(float r, float g, float b);
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma intrinsic(_byteswap_ushort)
|
||||
#pragma intrinsic(_byteswap_ulong)
|
||||
#pragma intrinsic(_byteswap_uint64)
|
||||
# define SDL_Swap16 _byteswap_ushort
|
||||
# define SDL_Swap32 _byteswap_ulong
|
||||
# define SDL_Swap64 _byteswap_uint64
|
||||
|
@ -304,23 +304,27 @@ static void WriteScreenshot(bool use_bmp = false)
|
||||
// could fix via enumerating all files, but it's not worth it ATM.
|
||||
char fn[VFS_MAX_PATH];
|
||||
|
||||
const char* file_format =
|
||||
use_bmp ? "screenshots/screenshot%04d.bmp"
|
||||
: "screenshots/screenshot%04d.png";
|
||||
const char* file_format_string = "screenshots/screenshot%04d.%s";
|
||||
// %04d -> always 4 digits, so sorting by filename works correctly.
|
||||
const char* extension = use_bmp? "bmp" : "png";
|
||||
|
||||
static int next_num = 1;
|
||||
do
|
||||
sprintf(fn, file_format, next_num++);
|
||||
sprintf(fn, file_format_string, next_num++, extension);
|
||||
while(vfs_exists(fn));
|
||||
|
||||
const int w = g_xres, h = g_yres;
|
||||
const int bpp = 24;
|
||||
const GLenum fmt = use_bmp? GL_BGR : GL_RGB;
|
||||
const int flags = use_bmp? TEX_BGR|TEX_BOTTOM_UP : TEX_BOTTOM_UP;
|
||||
// we want writing BMP to be as fast as possible,
|
||||
// so read data from OpenGL in BMP format to obviate conversion.
|
||||
const size_t size = w * h * bpp;
|
||||
void* img = mem_alloc(size);
|
||||
|
||||
glReadPixels(0, 0, w, h, use_bmp?GL_BGR:GL_RGB, GL_UNSIGNED_BYTE, img);
|
||||
glReadPixels(0, 0, w, h, fmt, GL_UNSIGNED_BYTE, img);
|
||||
|
||||
if(tex_write(fn, w, h, bpp, use_bmp?TEX_BGR:0, img) < 0)
|
||||
if(tex_write(fn, w, h, bpp, flags, img) < 0)
|
||||
debug_warn("WriteScreenshot: tex_write failed");
|
||||
|
||||
mem_free(img);
|
||||
|
Loading…
Reference in New Issue
Block a user