2004-07-08 16:40:24 +02:00
|
|
|
// 2d texture format codecs
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
2004-07-08 16:40:24 +02:00
|
|
|
// Copyright (c) 2004 Jan Wassenberg
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// General Public License for more details.
|
|
|
|
//
|
|
|
|
// Contact info:
|
|
|
|
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
|
|
|
// http://www.stud.uni-karlsruhe.de/~urkt/
|
|
|
|
|
2004-05-08 03:11:51 +02:00
|
|
|
#include "precompiled.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-06-04 14:41:53 +02:00
|
|
|
#include <math.h>
|
|
|
|
#include <stdlib.h>
|
2005-06-28 06:06:25 +02:00
|
|
|
|
2004-07-10 23:25:35 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
2005-01-27 16:40:23 +01:00
|
|
|
#include "lib.h"
|
2005-08-12 19:06:53 +02:00
|
|
|
#include "../res.h"
|
2005-01-27 16:40:23 +01:00
|
|
|
#include "tex.h"
|
2005-09-02 04:56:54 +02:00
|
|
|
#include "tex_codec.h"
|
2004-07-08 16:40:24 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
#define ERR_TOO_SHORT -4
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// rationale for default: see tex_set_global_orientation
|
|
|
|
static int global_orientation = TEX_TOP_DOWN;
|
2005-01-09 15:23:59 +01:00
|
|
|
|
|
|
|
|
2005-09-02 20:38:25 +02:00
|
|
|
|
|
|
|
// check if the given texture format is acceptable: 8bpp grey,
|
|
|
|
// 24bpp color or 32bpp color+alpha (BGR / upside down are permitted).
|
|
|
|
// basically, this is the "plain" format understood by all codecs and
|
|
|
|
// tex_codec_plain_transform.
|
|
|
|
// return 0 if ok or a negative error code.
|
|
|
|
static int validate_format(uint bpp, uint flags)
|
|
|
|
{
|
|
|
|
const bool alpha = (flags & TEX_ALPHA ) != 0;
|
|
|
|
const bool grey = (flags & TEX_GREY ) != 0;
|
|
|
|
const bool dxt = (flags & TEX_DXT ) != 0;
|
|
|
|
const bool mipmaps = (flags & TEX_MIPMAPS) != 0;
|
|
|
|
|
|
|
|
if(dxt || mipmaps)
|
|
|
|
return ERR_TEX_FMT_INVALID;
|
|
|
|
|
|
|
|
// grey must be 8bpp without alpha, or it's invalid.
|
|
|
|
if(grey)
|
|
|
|
{
|
|
|
|
if(bpp == 8 && !alpha)
|
|
|
|
return 0;
|
|
|
|
return ERR_TEX_FMT_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(bpp == 24 && !alpha)
|
|
|
|
return 0;
|
|
|
|
if(bpp == 32 && alpha)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return ERR_TEX_FMT_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// handles BGR and row flipping in "plain" format (see below).
|
2005-01-07 14:48:49 +01:00
|
|
|
//
|
2005-09-02 04:56:54 +02:00
|
|
|
// called by codecs after they get their format-specific transforms out of
|
|
|
|
// the way. note that this approach requires several passes over the image,
|
|
|
|
// but is much easier to maintain than providing all<->all conversion paths.
|
2005-01-09 15:23:59 +01:00
|
|
|
//
|
|
|
|
// somewhat optimized (loops are hoisted, cache associativity accounted for)
|
2005-09-07 00:44:48 +02:00
|
|
|
static int plain_transform(Tex* t, uint transforms)
|
2005-09-02 04:56:54 +02:00
|
|
|
{
|
|
|
|
// extract texture info
|
|
|
|
const uint w = t->w, h = t->h, bpp = t->bpp, flags = t->flags;
|
|
|
|
u8* const img = tex_get_data(t);
|
|
|
|
|
|
|
|
// sanity checks (not errors, we just can't handle these cases)
|
|
|
|
// .. unknown transform
|
2005-09-07 00:44:48 +02:00
|
|
|
if(transforms & ~(TEX_BGR|TEX_ORIENTATION))
|
2005-09-02 04:56:54 +02:00
|
|
|
return TEX_CODEC_CANNOT_HANDLE;
|
|
|
|
// .. img is not in "plain" format
|
2005-09-02 20:38:25 +02:00
|
|
|
if(validate_format(bpp, flags) != 0)
|
2005-09-02 04:56:54 +02:00
|
|
|
return TEX_CODEC_CANNOT_HANDLE;
|
|
|
|
// .. nothing to do
|
2005-09-07 00:44:48 +02:00
|
|
|
if(!transforms)
|
2005-01-09 15:23:59 +01:00
|
|
|
return 0;
|
2004-08-08 20:07:46 +02:00
|
|
|
|
2005-01-09 15:23:59 +01:00
|
|
|
// setup row source/destination pointers (simplifies outer loop)
|
|
|
|
u8* dst = img;
|
|
|
|
const u8* src = img;
|
2005-09-02 04:56:54 +02:00
|
|
|
const size_t pitch = w * bpp/8;
|
2005-01-09 15:23:59 +01:00
|
|
|
ssize_t row_ofs = (ssize_t)pitch;
|
2005-09-02 04:56:54 +02:00
|
|
|
// avoid y*pitch multiply in row loop; instead, add row_ofs.
|
2005-01-09 15:23:59 +01:00
|
|
|
void* clone_img = 0;
|
|
|
|
// flipping rows (0,1,2 -> 2,1,0)
|
2005-09-07 00:44:48 +02:00
|
|
|
if(transforms & TEX_ORIENTATION)
|
2004-08-08 20:07:46 +02:00
|
|
|
{
|
2005-01-09 15:23:59 +01:00
|
|
|
// 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;
|
2005-01-23 19:23:29 +01:00
|
|
|
clone_img = mem_alloc(size, 32*KiB);
|
2005-01-09 15:23:59 +01:00
|
|
|
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;
|
2004-08-08 20:07:46 +02:00
|
|
|
}
|
2005-01-09 15:23:59 +01:00
|
|
|
|
|
|
|
// no BGR convert necessary
|
2005-09-07 00:44:48 +02:00
|
|
|
if(!(transforms & TEX_BGR))
|
2005-09-02 04:56:54 +02:00
|
|
|
{
|
2005-01-09 15:23:59 +01:00
|
|
|
for(uint y = 0; y < h; y++)
|
|
|
|
{
|
|
|
|
memcpy(dst, src, pitch);
|
|
|
|
dst += pitch;
|
|
|
|
src += row_ofs;
|
|
|
|
}
|
2005-09-02 04:56:54 +02:00
|
|
|
}
|
2005-01-09 15:23:59 +01:00
|
|
|
// RGB <-> BGR
|
2005-09-02 04:56:54 +02:00
|
|
|
else if(bpp == 24)
|
|
|
|
{
|
2005-01-09 15:23:59 +01:00
|
|
|
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
|
|
|
|
}
|
2005-09-02 04:56:54 +02:00
|
|
|
}
|
2005-01-09 15:23:59 +01:00
|
|
|
// RGBA <-> BGRA
|
2005-09-02 04:56:54 +02:00
|
|
|
else if(bpp == 32)
|
|
|
|
{
|
2005-01-09 15:23:59 +01:00
|
|
|
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
|
|
|
|
}
|
2005-09-02 04:56:54 +02:00
|
|
|
}
|
2005-01-09 15:23:59 +01:00
|
|
|
|
|
|
|
if(clone_img)
|
|
|
|
mem_free(clone_img);
|
|
|
|
|
|
|
|
return 0;
|
2005-01-07 03:00:14 +01:00
|
|
|
}
|
2004-08-08 20:07:46 +02:00
|
|
|
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// support routines for codecs
|
|
|
|
//-----------------------------------------------------------------------------
|
2004-08-08 20:07:46 +02:00
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// should be a tight bound because we iterate this many times (for convenience)
|
|
|
|
static const uint MAX_CODECS = 8;
|
|
|
|
static const TexCodecVTbl* codecs[MAX_CODECS];
|
2004-08-08 20:07:46 +02:00
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// add this vtbl to the codec list. called at NLSO init time by the
|
|
|
|
// TEX_CODEC_REGISTER in each codec file. note that call order and therefore
|
|
|
|
// order in the list is undefined, but since each codec only steps up if it
|
|
|
|
// can handle the given format, this is not a problem.
|
|
|
|
int tex_codec_register(const TexCodecVTbl* c)
|
|
|
|
{
|
2005-09-08 03:47:45 +02:00
|
|
|
debug_assert(c != 0 && "tex_codec_register(0) - why?");
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
for(int i = 0; i < MAX_CODECS; i++)
|
2005-01-07 03:00:14 +01:00
|
|
|
{
|
2005-09-02 04:56:54 +02:00
|
|
|
// slot available
|
|
|
|
if(codecs[i] == 0)
|
2005-01-07 03:00:14 +01:00
|
|
|
{
|
2005-09-02 04:56:54 +02:00
|
|
|
codecs[i] = c;
|
|
|
|
return 0; // success
|
2005-01-07 03:00:14 +01:00
|
|
|
}
|
2004-08-08 20:07:46 +02:00
|
|
|
}
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// didn't find a free slot.
|
|
|
|
debug_warn("tex_codec_register: increase MAX_CODECS");
|
|
|
|
return 0; // failure, but caller ignores return value
|
2005-01-07 03:00:14 +01:00
|
|
|
}
|
2004-08-08 20:07:46 +02:00
|
|
|
|
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
// find codec that recognizes the desired output file extension
|
|
|
|
int tex_codec_for_filename(const char* fn, const TexCodecVTbl** c)
|
|
|
|
{
|
|
|
|
const char* ext = strrchr(fn, '.');
|
|
|
|
if(!ext)
|
|
|
|
return ERR_UNKNOWN_FORMAT;
|
|
|
|
ext++; // skip '.'
|
|
|
|
|
|
|
|
for(uint i = 0; i < MAX_CODECS; i++)
|
|
|
|
{
|
|
|
|
*c = codecs[i];
|
|
|
|
// skip if 0 (e.g. if MAX_CODECS != num codecs)
|
|
|
|
if(!*c)
|
|
|
|
continue;
|
|
|
|
// we found it
|
|
|
|
if((*c)->is_ext(ext))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ERR_UNKNOWN_FORMAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// find codec that recognizes the header's magic field
|
|
|
|
int tex_codec_for_header(const u8* file, size_t file_size, const TexCodecVTbl** c)
|
|
|
|
{
|
|
|
|
// we guarantee at least 4 bytes for is_hdr to look at
|
|
|
|
if(file_size < 4)
|
|
|
|
return ERR_TOO_SHORT;
|
|
|
|
for(uint i = 0; i < MAX_CODECS; i++)
|
|
|
|
{
|
|
|
|
*c = codecs[i];
|
|
|
|
// skip if 0 (e.g. if MAX_CODECS != num codecs)
|
|
|
|
if(!*c)
|
|
|
|
continue;
|
|
|
|
// we found it
|
|
|
|
if((*c)->is_hdr(file))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ERR_UNKNOWN_FORMAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// allocate an array of row pointers that point into the given texture data.
|
|
|
|
// <file_orientation> indicates whether the file format is top-down or
|
|
|
|
// bottom-up; the row array is inverted if necessary to match global
|
|
|
|
// orienatation. (this is more efficient than "transforming" later)
|
2005-09-02 20:38:25 +02:00
|
|
|
//
|
2005-09-02 04:56:54 +02:00
|
|
|
// used by PNG and JPG codecs; caller must free() rows when done.
|
2005-09-02 20:38:25 +02:00
|
|
|
//
|
|
|
|
// note: we don't allocate the data param ourselves because this function is
|
|
|
|
// needed for encoding, too (where data is already present).
|
2005-09-02 04:56:54 +02:00
|
|
|
int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
|
2005-09-07 00:44:48 +02:00
|
|
|
uint src_flags, uint dst_orientation, RowArray& rows)
|
2004-12-01 22:34:49 +01:00
|
|
|
{
|
2005-09-07 00:44:48 +02:00
|
|
|
// convenience for caller: allow passing all flags.
|
|
|
|
const uint src_orientation = src_flags & TEX_ORIENTATION;
|
|
|
|
if(dst_orientation == 0)
|
|
|
|
dst_orientation = global_orientation;
|
2005-09-02 04:56:54 +02:00
|
|
|
|
|
|
|
rows = (RowArray)malloc(h * sizeof(RowPtr));
|
|
|
|
if(!rows)
|
2004-12-01 22:34:49 +01:00
|
|
|
return ERR_NO_MEM;
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// determine start position and direction
|
2005-09-07 00:44:48 +02:00
|
|
|
const bool flip = (src_orientation ^ dst_orientation) != 0;
|
|
|
|
RowPtr pos = flip? data+pitch*(h-1) : data;
|
|
|
|
const ssize_t add = flip? -(ssize_t)pitch : (ssize_t)pitch;
|
|
|
|
const RowPtr end = flip? data-pitch : data+pitch*h;
|
2005-09-02 04:56:54 +02:00
|
|
|
|
|
|
|
for(size_t i = 0; i < h; i++)
|
|
|
|
{
|
|
|
|
rows[i] = pos;
|
|
|
|
pos += add;
|
|
|
|
}
|
2004-12-01 22:34:49 +01:00
|
|
|
|
2005-09-07 00:44:48 +02:00
|
|
|
debug_assert(pos == end);
|
2004-12-01 22:34:49 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-07 00:44:48 +02:00
|
|
|
int tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da)
|
|
|
|
{
|
|
|
|
RETURN_ERR(tex_transform(t, transforms));
|
|
|
|
|
|
|
|
void* img_data = tex_get_data(t); const size_t img_size = tex_img_size(t);
|
|
|
|
RETURN_ERR(da_append(da, hdr, hdr_size));
|
|
|
|
RETURN_ERR(da_append(da, img_data, img_size));
|
|
|
|
return 0;
|
2005-09-02 04:56:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// API
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// switch between top-down and bottom-up orientation.
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
2005-09-02 04:56:54 +02:00
|
|
|
// 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.
|
|
|
|
// notes:
|
|
|
|
// - there's no way to tell which orientation a DDS file has;
|
|
|
|
// 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.
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
2005-09-02 04:56:54 +02:00
|
|
|
// the app can change orientation, e.g. to speed up loading
|
|
|
|
// "upside-down" formats, or to match OpenGL's bottom-up convention.
|
|
|
|
void tex_set_global_orientation(int o)
|
2004-07-08 16:40:24 +02:00
|
|
|
{
|
2005-09-02 04:56:54 +02:00
|
|
|
debug_assert(o == TEX_TOP_DOWN || o == TEX_BOTTOM_UP);
|
|
|
|
global_orientation = o;
|
2004-07-08 16:40:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
u8* tex_get_data(const Tex* t)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-09-10 16:28:55 +02:00
|
|
|
u8* p = (u8*)mem_get_ptr(t->hm);
|
|
|
|
if(!p)
|
|
|
|
return 0;
|
|
|
|
return p + t->ofs;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t tex_img_size(const Tex* t)
|
|
|
|
{
|
|
|
|
return t->w * t->h * t->bpp/8;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-08 03:47:45 +02:00
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
size_t tex_hdr_size(const char* fn)
|
|
|
|
{
|
|
|
|
const TexCodecVTbl* c;
|
|
|
|
CHECK_ERR(tex_codec_for_filename(fn, &c));
|
|
|
|
return c->hdr_size(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tex_load_mem(Handle hm, Tex* t)
|
|
|
|
{
|
2005-09-08 03:47:45 +02:00
|
|
|
u8* file; size_t file_size;
|
|
|
|
CHECK_ERR(mem_get(hm, &file, &file_size));
|
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
const TexCodecVTbl* c;
|
|
|
|
CHECK_ERR(tex_codec_for_header(file, file_size, &c));
|
2004-06-26 00:18:03 +02:00
|
|
|
|
2005-09-08 03:47:45 +02:00
|
|
|
// make sure enough of the file has been read
|
|
|
|
const size_t min_hdr_size = c->hdr_size(0);
|
|
|
|
if(file_size < min_hdr_size)
|
|
|
|
return ERR_TOO_SHORT;
|
|
|
|
const size_t hdr_size = c->hdr_size(file);
|
|
|
|
if(file_size < hdr_size)
|
|
|
|
return ERR_TOO_SHORT;
|
|
|
|
|
|
|
|
|
|
|
|
DynArray da;
|
|
|
|
CHECK_ERR(da_wrap_fixed(&da, file, file_size));
|
2005-09-02 04:56:54 +02:00
|
|
|
t->hm = hm;
|
2005-09-08 03:47:45 +02:00
|
|
|
t->ofs = hdr_size;
|
2004-06-26 00:18:03 +02:00
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
int err = c->decode(&da, t);
|
2005-09-08 03:47:45 +02:00
|
|
|
if(err < 0)
|
|
|
|
{
|
2005-09-10 16:28:55 +02:00
|
|
|
debug_printf("tex_load_mem (%s): %d", c->name, err);
|
2005-09-08 03:47:45 +02:00
|
|
|
debug_warn("tex_load_mem failed");
|
|
|
|
return err;
|
|
|
|
}
|
2004-06-26 00:18:03 +02:00
|
|
|
|
2005-09-08 03:47:45 +02:00
|
|
|
// sanity checks
|
|
|
|
if(!t->w || !t->h || t->bpp > 32)
|
|
|
|
return ERR_TEX_FMT_INVALID;
|
|
|
|
// TODO: need to compare against the new t->hm (file may be compressed, cannot use file_size)
|
|
|
|
//if(mem_size < t->ofs + tex_img_size(t))
|
|
|
|
// return ERR_TOO_SHORT;
|
|
|
|
|
|
|
|
// flip image to global orientation
|
|
|
|
uint orientation = t->flags & TEX_ORIENTATION;
|
|
|
|
// .. but only if it knows which way around it is (DDS doesn't)
|
|
|
|
if(orientation)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-09-08 03:47:45 +02:00
|
|
|
uint transforms = orientation ^ global_orientation;
|
|
|
|
WARN_ERR(plain_transform(t, transforms));
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2005-09-08 03:47:45 +02:00
|
|
|
return 0;
|
2004-07-08 16:40:24 +02:00
|
|
|
}
|
|
|
|
|
2004-08-08 20:07:46 +02:00
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
int tex_load(const char* fn, Tex* t)
|
2004-08-08 20:07:46 +02:00
|
|
|
{
|
2005-09-02 04:56:54 +02:00
|
|
|
// load file
|
|
|
|
void* p; size_t size; // unused
|
|
|
|
Handle hm = vfs_load(fn, p, size);
|
|
|
|
RETURN_ERR(hm); // (need handle below; can't test return value directly)
|
2005-09-08 03:47:45 +02:00
|
|
|
int ret = tex_load_mem(hm, t);
|
2005-09-02 04:56:54 +02:00
|
|
|
// do not free hm! it either still holds the image data (i.e. texture
|
|
|
|
// wasn't compressed) or was replaced by a new buffer for the image data.
|
|
|
|
if(ret < 0)
|
|
|
|
memset(t, 0, sizeof(Tex));
|
|
|
|
return ret;
|
2004-08-08 20:07:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
int tex_free(Tex* t)
|
2004-08-08 20:07:46 +02:00
|
|
|
{
|
2005-09-02 04:56:54 +02:00
|
|
|
mem_free_h(t->hm);
|
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-07 00:44:48 +02:00
|
|
|
int tex_transform(Tex* t, uint transforms)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-09-02 04:56:54 +02:00
|
|
|
// find codec that understands the data, and transform
|
|
|
|
for(int i = 0; i < MAX_CODECS; i++)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-09-07 00:44:48 +02:00
|
|
|
// MAX_CODECS isn't a tight bound and we have hit a 0 entry
|
|
|
|
if(!codecs[i])
|
|
|
|
continue;
|
|
|
|
int err = codecs[i]->transform(t, transforms);
|
2005-09-02 04:56:54 +02:00
|
|
|
if(err == TEX_CODEC_CANNOT_HANDLE)
|
|
|
|
continue;
|
|
|
|
if(err == 0)
|
|
|
|
return 0;
|
|
|
|
debug_printf("tex_transform (%s): failed, error %d", codecs[i]->name, err);
|
|
|
|
CHECK_ERR(err);
|
2004-06-26 00:18:03 +02:00
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
// last chance
|
2005-09-07 00:44:48 +02:00
|
|
|
return plain_transform(t, transforms);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2004-07-08 16:40:24 +02:00
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
int tex_write(const char* fn, uint w, uint h, uint bpp, uint flags, void* in_img)
|
2004-07-08 16:40:24 +02:00
|
|
|
{
|
2005-09-02 20:38:25 +02:00
|
|
|
if(validate_format(bpp, flags) != 0)
|
|
|
|
return ERR_TEX_FMT_INVALID;
|
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
const size_t in_img_size = w * h * bpp / 8;
|
2005-01-09 15:23:59 +01:00
|
|
|
|
2005-09-02 04:56:54 +02:00
|
|
|
Handle hm = mem_assign(in_img, in_img_size, 0, 0, 0, 0, 0);
|
|
|
|
Tex t =
|
2004-12-01 22:34:49 +01:00
|
|
|
{
|
2005-09-02 04:56:54 +02:00
|
|
|
hm,
|
|
|
|
0, // image data offset
|
|
|
|
w, h, bpp, flags
|
2004-12-01 22:34:49 +01:00
|
|
|
};
|
2005-09-02 20:38:25 +02:00
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
|
|
|
|
// we could be clever here and avoid the extra alloc if our current
|
|
|
|
// memory block ensued from the same kind of texture file. this is
|
|
|
|
// most likely the case if in_img == <hm's user pointer> + c->hdr_size(0).
|
|
|
|
// advantage is avoiding
|
2005-09-07 00:44:48 +02:00
|
|
|
DynArray da;
|
|
|
|
const size_t max_out_size = (w*h*4 + 256*KiB)*2;
|
|
|
|
CHECK_ERR(da_alloc(&da, max_out_size));
|
|
|
|
|
2005-09-10 16:28:55 +02:00
|
|
|
const TexCodecVTbl* c;
|
|
|
|
CHECK_ERR(tex_codec_for_filename(fn, &c));
|
|
|
|
|
|
|
|
// encode
|
|
|
|
int err = c->encode(&t, &da);
|
|
|
|
if(err < 0)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-09-10 16:28:55 +02:00
|
|
|
debug_printf("tex_write (%s): %d", c->name, err);
|
|
|
|
debug_warn("tex_writefailed");
|
|
|
|
goto fail;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
2005-09-10 16:28:55 +02:00
|
|
|
const size_t rounded_size = round_up(da.cur_size, FILE_BLOCK_SIZE);
|
2005-09-07 00:44:48 +02:00
|
|
|
CHECK_ERR(da_set_size(&da, rounded_size));
|
|
|
|
WARN_ERR(vfs_store(fn, da.base, da.pos));
|
|
|
|
err = 0;
|
2005-09-10 16:28:55 +02:00
|
|
|
|
|
|
|
fail:
|
2005-09-07 00:44:48 +02:00
|
|
|
WARN_ERR(da_free(&da));
|
|
|
|
return err;
|
2004-06-26 00:18:03 +02:00
|
|
|
}
|