2026-04-10 14:03:00 -07:00
|
|
|
/* Copyright (C) 2026 Wildfire Games.
|
2009-04-18 10:00:33 -07:00
|
|
|
*
|
2010-02-08 08:23:39 -08:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
|
* the following conditions:
|
2016-11-23 05:02:58 -08:00
|
|
|
*
|
2010-02-08 08:23:39 -08:00
|
|
|
* The above copyright notice and this permission notice shall be included
|
|
|
|
|
* in all copies or substantial portions of the Software.
|
2016-11-23 05:02:58 -08:00
|
|
|
*
|
2010-02-08 08:23:39 -08:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
2009-04-18 10:00:33 -07:00
|
|
|
*/
|
|
|
|
|
|
2004-05-07 18:11:51 -07:00
|
|
|
#include "precompiled.h"
|
2004-03-02 15:56:51 -08:00
|
|
|
|
2022-01-23 23:00:55 -08:00
|
|
|
#include "tex.h"
|
2004-07-10 14:25:35 -07:00
|
|
|
|
2025-07-26 00:43:13 -07:00
|
|
|
#include "lib/alignment.h"
|
|
|
|
|
#include "lib/allocators/dynarray.h"
|
2011-04-29 12:10:34 -07:00
|
|
|
#include "lib/allocators/shared_ptr.h"
|
2022-01-23 23:00:55 -08:00
|
|
|
#include "lib/bits.h"
|
2025-07-26 00:43:13 -07:00
|
|
|
#include "lib/debug.h"
|
|
|
|
|
#include "lib/posix/posix_types.h"
|
2022-01-23 23:00:55 -08:00
|
|
|
#include "lib/tex/tex_codec.h"
|
|
|
|
|
#include "lib/timer.h"
|
2007-01-01 13:25:47 -08:00
|
|
|
|
2022-01-23 23:00:55 -08:00
|
|
|
#include <algorithm>
|
2025-07-26 00:43:13 -07:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <cstring>
|
2004-03-02 15:56:51 -08:00
|
|
|
|
2022-04-12 10:39:05 -07:00
|
|
|
static const StatusDefinition texStatusDefinitions[] =
|
|
|
|
|
{
|
|
|
|
|
{ ERR::TEX_UNKNOWN_FORMAT, L"Unknown texture format" },
|
2011-05-05 06:03:34 -07:00
|
|
|
{ ERR::TEX_FMT_INVALID, L"Invalid/unsupported texture format" },
|
|
|
|
|
{ ERR::TEX_INVALID_COLOR_TYPE, L"Invalid color type" },
|
|
|
|
|
{ ERR::TEX_NOT_8BIT_PRECISION, L"Not 8-bit channel precision" },
|
|
|
|
|
{ ERR::TEX_INVALID_LAYOUT, L"Unsupported texel layout, e.g. right-to-left" },
|
|
|
|
|
{ ERR::TEX_COMPRESSED, L"Unsupported texture compression" },
|
|
|
|
|
{ WARN::TEX_INVALID_DATA, L"Warning: invalid texel data encountered" },
|
|
|
|
|
{ ERR::TEX_INVALID_SIZE, L"Texture size is incorrect" },
|
|
|
|
|
{ INFO::TEX_CODEC_CANNOT_HANDLE, L"Texture codec cannot handle the given format" }
|
|
|
|
|
};
|
|
|
|
|
STATUS_ADD_DEFINITIONS(texStatusDefinitions);
|
2006-09-22 06:19:40 -07:00
|
|
|
|
|
|
|
|
|
2006-05-30 21:01:59 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// validation
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2005-09-19 16:40:33 -07:00
|
|
|
// be careful not to use other tex_* APIs here because they call us.
|
2014-03-12 19:37:05 -07:00
|
|
|
Status Tex::validate() const
|
2005-09-19 16:40:33 -07:00
|
|
|
{
|
2014-03-12 19:37:05 -07:00
|
|
|
if(m_Flags & TEX_UNDEFINED_FLAGS)
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::_1;
|
2007-12-20 12:14:21 -08:00
|
|
|
|
2022-02-13 14:07:56 -08:00
|
|
|
// pixel data (only check validity if the image is still in memory).
|
2014-03-12 19:37:05 -07:00
|
|
|
if(m_Data)
|
2005-09-19 16:40:33 -07:00
|
|
|
{
|
2005-10-11 21:41:28 -07:00
|
|
|
// file size smaller than header+pixels.
|
2005-09-19 16:40:33 -07:00
|
|
|
// possible causes: texture file header is invalid,
|
|
|
|
|
// or file wasn't loaded completely.
|
2014-03-12 19:37:05 -07:00
|
|
|
if(m_DataSize < m_Ofs + m_Width*m_Height*m_Bpp/8)
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::_2;
|
2005-09-19 16:40:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bits per pixel
|
|
|
|
|
// (we don't bother checking all values; a sanity check is enough)
|
2014-03-12 19:37:05 -07:00
|
|
|
if(m_Bpp % 4 || m_Bpp > 32)
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::_3;
|
2005-09-19 16:40:33 -07:00
|
|
|
|
|
|
|
|
// flags
|
2005-10-11 21:41:28 -07:00
|
|
|
// .. DXT value
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t dxt = m_Flags & TEX_DXT;
|
2005-09-19 16:40:33 -07:00
|
|
|
if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5)
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::_4;
|
2005-09-19 16:40:33 -07:00
|
|
|
// .. orientation
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t orientation = m_Flags & TEX_ORIENTATION;
|
2005-09-19 16:40:33 -07:00
|
|
|
if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN))
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::_5;
|
2005-09-19 16:40:33 -07:00
|
|
|
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2005-09-19 16:40:33 -07:00
|
|
|
}
|
|
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
#define CHECK_TEX(t) RETURN_STATUS_IF_ERR((t->validate()))
|
2005-10-23 16:57:59 -07:00
|
|
|
|
2005-09-19 16:40:33 -07:00
|
|
|
|
2005-09-02 11:38:25 -07: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.
|
2011-05-03 05:38:42 -07:00
|
|
|
Status tex_validate_plain_format(size_t bpp, size_t flags)
|
2005-09-02 11:38:25 -07:00
|
|
|
{
|
|
|
|
|
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)
|
2006-09-22 06:19:40 -07:00
|
|
|
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
2005-09-02 11:38:25 -07:00
|
|
|
|
|
|
|
|
// grey must be 8bpp without alpha, or it's invalid.
|
|
|
|
|
if(grey)
|
|
|
|
|
{
|
|
|
|
|
if(bpp == 8 && !alpha)
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
|
|
|
|
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
2005-09-02 11:38:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bpp == 24 && !alpha)
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2005-09-02 11:38:25 -07:00
|
|
|
if(bpp == 32 && alpha)
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2005-09-02 11:38:25 -07:00
|
|
|
|
2006-09-22 06:19:40 -07:00
|
|
|
WARN_RETURN(ERR::TEX_FMT_INVALID);
|
2005-09-02 11:38:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-05-30 21:01:59 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// mipmaps
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
void tex_util_foreach_mipmap(size_t w, size_t h, size_t bpp, const u8* pixels, int levels_to_skip, size_t data_padding, MipmapCB cb, void* RESTRICT cbData)
|
2006-05-30 21:01:59 -07:00
|
|
|
{
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(levels_to_skip >= 0 || levels_to_skip == TEX_BASE_LEVEL_ONLY);
|
2007-12-20 12:14:21 -08:00
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
size_t level_w = w, level_h = h;
|
2007-12-20 12:14:21 -08:00
|
|
|
const u8* level_data = pixels;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
|
|
|
|
// we iterate through the loop (necessary to skip over image data),
|
|
|
|
|
// but do not actually call back until the requisite number of
|
|
|
|
|
// levels have been skipped (i.e. level == 0).
|
2007-12-20 12:14:21 -08:00
|
|
|
int level = (levels_to_skip == TEX_BASE_LEVEL_ONLY)? 0 : -levels_to_skip;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
|
|
|
|
// until at level 1x1:
|
|
|
|
|
for(;;)
|
|
|
|
|
{
|
|
|
|
|
// used to skip past this mip level in <data>
|
2011-08-17 01:38:53 -07:00
|
|
|
const size_t level_dataSize = (size_t)(round_up(level_w, data_padding) * round_up(level_h, data_padding) * bpp/8);
|
2006-05-30 21:01:59 -07:00
|
|
|
|
|
|
|
|
if(level >= 0)
|
2011-08-17 01:38:53 -07:00
|
|
|
cb((size_t)level, level_w, level_h, level_data, level_dataSize, cbData);
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2011-08-17 01:38:53 -07:00
|
|
|
level_data += level_dataSize;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
|
|
|
|
// 1x1 reached - done
|
|
|
|
|
if(level_w == 1 && level_h == 1)
|
|
|
|
|
break;
|
|
|
|
|
level_w /= 2;
|
|
|
|
|
level_h /= 2;
|
|
|
|
|
// if the texture is non-square, one of the dimensions will become
|
|
|
|
|
// 0 before the other. to satisfy OpenGL's expectations, change it
|
|
|
|
|
// back to 1.
|
|
|
|
|
if(level_w == 0) level_w = 1;
|
|
|
|
|
if(level_h == 0) level_h = 1;
|
|
|
|
|
level++;
|
|
|
|
|
|
|
|
|
|
// special case: no mipmaps, we were only supposed to call for
|
|
|
|
|
// the base level
|
|
|
|
|
if(levels_to_skip == TEX_BASE_LEVEL_ONLY)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
struct CreateLevelData
|
|
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
size_t num_components;
|
2005-10-11 21:41:28 -07:00
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
size_t prev_level_w;
|
|
|
|
|
size_t prev_level_h;
|
2005-10-11 21:41:28 -07:00
|
|
|
const u8* prev_level_data;
|
2011-08-17 01:38:53 -07:00
|
|
|
size_t prev_level_dataSize;
|
2005-10-11 21:41:28 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// uses 2x2 box filter
|
2011-08-17 01:38:53 -07:00
|
|
|
static void create_level(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_dataSize, void* RESTRICT cbData)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
2007-09-25 03:43:11 -07:00
|
|
|
CreateLevelData* cld = (CreateLevelData*)cbData;
|
2005-12-28 12:29:22 -08:00
|
|
|
const size_t src_w = cld->prev_level_w;
|
|
|
|
|
const size_t src_h = cld->prev_level_h;
|
2005-10-11 21:41:28 -07:00
|
|
|
const u8* src = cld->prev_level_data;
|
|
|
|
|
u8* dst = (u8*)level_data;
|
|
|
|
|
|
|
|
|
|
// base level - must be copied over from source buffer
|
|
|
|
|
if(level == 0)
|
|
|
|
|
{
|
2011-08-17 01:38:53 -07:00
|
|
|
ENSURE(level_dataSize == cld->prev_level_dataSize);
|
|
|
|
|
memcpy(dst, src, level_dataSize);
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
const size_t num_components = cld->num_components;
|
2005-12-28 12:29:22 -08:00
|
|
|
const size_t dx = num_components, dy = dx*src_w;
|
2005-10-11 21:41:28 -07:00
|
|
|
|
|
|
|
|
// special case: image is too small for 2x2 filter
|
|
|
|
|
if(cld->prev_level_w == 1 || cld->prev_level_h == 1)
|
|
|
|
|
{
|
2005-12-28 12:29:22 -08:00
|
|
|
// image is either a horizontal or vertical line.
|
|
|
|
|
// their memory layout is the same (packed pixels), so no special
|
|
|
|
|
// handling is needed; just pick max dimension.
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t y = 0; y < std::max(src_w, src_h); y += 2)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t i = 0; i < num_components; i++)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
2005-12-28 12:29:22 -08:00
|
|
|
*dst++ = (src[0]+src[dx]+1)/2;
|
2005-10-11 21:41:28 -07:00
|
|
|
src += 1;
|
|
|
|
|
}
|
2005-12-28 12:29:22 -08:00
|
|
|
|
|
|
|
|
src += dx; // skip to next pixel (since box is 2x2)
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// normal
|
|
|
|
|
else
|
|
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t y = 0; y < src_h; y += 2)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t x = 0; x < src_w; x += 2)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t i = 0; i < num_components; i++)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
|
|
|
|
*dst++ = (src[0]+src[dx]+src[dy]+src[dx+dy]+2)/4;
|
|
|
|
|
src += 1;
|
|
|
|
|
}
|
2005-12-28 12:29:22 -08:00
|
|
|
|
|
|
|
|
src += dx; // skip to next pixel (since box is 2x2)
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
2005-12-28 12:29:22 -08:00
|
|
|
|
|
|
|
|
src += dy; // skip to next row (since box is 2x2)
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-17 01:38:53 -07:00
|
|
|
ENSURE(dst == level_data + level_dataSize);
|
|
|
|
|
ENSURE(src == cld->prev_level_data + cld->prev_level_dataSize);
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cld->prev_level_data = level_data;
|
2011-08-17 01:38:53 -07:00
|
|
|
cld->prev_level_dataSize = level_dataSize;
|
2005-10-11 21:41:28 -07:00
|
|
|
cld->prev_level_w = level_w;
|
|
|
|
|
cld->prev_level_h = level_h;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-08-17 01:38:53 -07:00
|
|
|
static Status add_mipmaps(Tex* t, size_t w, size_t h, size_t bpp, void* newData, size_t dataSize)
|
2006-05-30 21:01:59 -07:00
|
|
|
{
|
|
|
|
|
// this code assumes the image is of POT dimension; we don't
|
|
|
|
|
// go to the trouble of implementing image scaling because
|
2022-02-13 14:07:56 -08:00
|
|
|
// the only place this is used (backend textures) requires POT anyway.
|
2006-05-30 21:01:59 -07:00
|
|
|
if(!is_pow2(w) || !is_pow2(h))
|
2006-09-22 06:19:40 -07:00
|
|
|
WARN_RETURN(ERR::TEX_INVALID_SIZE);
|
2014-03-12 19:37:05 -07:00
|
|
|
t->m_Flags |= TEX_MIPMAPS; // must come before tex_img_size!
|
|
|
|
|
const size_t mipmap_size = t->img_size();
|
2021-05-22 12:32:38 -07:00
|
|
|
std::shared_ptr<u8> mipmapData;
|
2011-04-29 12:10:34 -07:00
|
|
|
AllocateAligned(mipmapData, mipmap_size);
|
2011-08-17 01:38:53 -07:00
|
|
|
CreateLevelData cld = { bpp/8, w, h, (const u8*)newData, dataSize };
|
2007-12-20 12:14:21 -08:00
|
|
|
tex_util_foreach_mipmap(w, h, bpp, mipmapData.get(), 0, 1, create_level, &cld);
|
2014-03-12 19:37:05 -07:00
|
|
|
t->m_Data = mipmapData;
|
|
|
|
|
t->m_DataSize = mipmap_size;
|
|
|
|
|
t->m_Ofs = 0;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2006-05-30 21:01:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// pixel format conversion (transformation)
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2005-10-21 00:47:38 -07:00
|
|
|
TIMER_ADD_CLIENT(tc_plain_transform);
|
|
|
|
|
|
2005-09-01 19:56:54 -07:00
|
|
|
// handles BGR and row flipping in "plain" format (see below).
|
2005-01-07 05:48:49 -08:00
|
|
|
//
|
2005-09-01 19:56:54 -07: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 06:23:59 -08:00
|
|
|
//
|
|
|
|
|
// somewhat optimized (loops are hoisted, cache associativity accounted for)
|
2011-05-03 05:38:42 -07:00
|
|
|
static Status plain_transform(Tex* t, size_t transforms)
|
2005-09-01 19:56:54 -07:00
|
|
|
{
|
2022-02-13 14:07:56 -08:00
|
|
|
TIMER_ACCRUE(tc_plain_transform);
|
2005-10-23 16:57:59 -07:00
|
|
|
CHECK_TEX(t);
|
2005-09-19 16:40:33 -07:00
|
|
|
|
2005-09-01 19:56:54 -07:00
|
|
|
// extract texture info
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t w = t->m_Width, h = t->m_Height, bpp = t->m_Bpp;
|
|
|
|
|
const size_t flags = t->m_Flags;
|
|
|
|
|
u8* const srcStorage = t->get_data();
|
2005-09-01 19:56:54 -07:00
|
|
|
|
|
|
|
|
// sanity checks (not errors, we just can't handle these cases)
|
|
|
|
|
// .. unknown transform
|
2010-09-10 13:37:54 -07:00
|
|
|
if(transforms & ~(TEX_BGR|TEX_ORIENTATION|TEX_MIPMAPS|TEX_ALPHA))
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::TEX_CODEC_CANNOT_HANDLE;
|
2005-10-11 21:41:28 -07:00
|
|
|
// .. data is not in "plain" format
|
2011-05-03 05:38:42 -07:00
|
|
|
RETURN_STATUS_IF_ERR(tex_validate_plain_format(bpp, flags));
|
2005-09-01 19:56:54 -07:00
|
|
|
// .. nothing to do
|
2005-09-06 15:44:48 -07:00
|
|
|
if(!transforms)
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2004-08-08 11:07:46 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t srcSize = t->img_size();
|
2011-08-17 01:38:53 -07:00
|
|
|
size_t dstSize = srcSize;
|
2010-09-10 13:37:54 -07:00
|
|
|
|
|
|
|
|
if(transforms & TEX_ALPHA)
|
|
|
|
|
{
|
|
|
|
|
// add alpha channel
|
|
|
|
|
if(bpp == 24)
|
|
|
|
|
{
|
2011-08-17 01:38:53 -07:00
|
|
|
dstSize = (srcSize / 3) * 4;
|
2014-03-12 19:37:05 -07:00
|
|
|
t->m_Bpp = 32;
|
2010-09-10 13:37:54 -07:00
|
|
|
}
|
|
|
|
|
// remove alpha channel
|
|
|
|
|
else if(bpp == 32)
|
|
|
|
|
{
|
|
|
|
|
return INFO::TEX_CODEC_CANNOT_HANDLE;
|
|
|
|
|
}
|
2011-08-17 01:38:53 -07:00
|
|
|
// can't have alpha with grayscale
|
2010-09-10 13:37:54 -07:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return INFO::TEX_CODEC_CANNOT_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-28 14:19:42 -08:00
|
|
|
// allocate copy of the image data.
|
|
|
|
|
// rationale: 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.
|
|
|
|
|
//
|
2007-12-20 12:14:21 -08:00
|
|
|
// this is necessary even when not flipping because the initial data
|
|
|
|
|
// is read-only.
|
2021-05-22 12:32:38 -07:00
|
|
|
std::shared_ptr<u8> dstStorage;
|
2011-08-17 01:38:53 -07:00
|
|
|
AllocateAligned(dstStorage, dstSize);
|
2006-01-28 14:19:42 -08:00
|
|
|
|
2005-01-09 06:23:59 -08:00
|
|
|
// setup row source/destination pointers (simplifies outer loop)
|
2011-08-17 01:38:53 -07:00
|
|
|
u8* dst = (u8*)dstStorage.get();
|
2010-09-10 13:37:54 -07:00
|
|
|
const u8* src;
|
|
|
|
|
const size_t pitch = w * bpp/8; // source bpp (not necessarily dest bpp)
|
2006-01-28 14:19:42 -08:00
|
|
|
// .. avoid y*pitch multiply in row loop; instead, add row_ofs.
|
2005-01-09 06:23:59 -08:00
|
|
|
ssize_t row_ofs = (ssize_t)pitch;
|
2006-01-28 14:19:42 -08:00
|
|
|
|
2005-01-09 06:23:59 -08:00
|
|
|
// flipping rows (0,1,2 -> 2,1,0)
|
2005-09-06 15:44:48 -07:00
|
|
|
if(transforms & TEX_ORIENTATION)
|
2004-08-08 11:07:46 -07:00
|
|
|
{
|
2011-08-17 01:38:53 -07:00
|
|
|
src = (const u8*)srcStorage+srcSize-pitch; // last row
|
2005-01-09 06:23:59 -08:00
|
|
|
row_ofs = -(ssize_t)pitch;
|
2004-08-08 11:07:46 -07:00
|
|
|
}
|
2010-09-10 13:37:54 -07:00
|
|
|
// adding/removing alpha channel (can't convert in-place)
|
|
|
|
|
else if(transforms & TEX_ALPHA)
|
|
|
|
|
{
|
2011-08-17 01:38:53 -07:00
|
|
|
src = (const u8*)srcStorage;
|
2010-09-10 13:37:54 -07:00
|
|
|
}
|
|
|
|
|
// do other transforms in-place
|
|
|
|
|
else
|
|
|
|
|
{
|
2011-08-17 01:38:53 -07:00
|
|
|
src = (const u8*)dstStorage.get();
|
|
|
|
|
memcpy(dstStorage.get(), srcStorage, srcSize);
|
2010-09-10 13:37:54 -07:00
|
|
|
}
|
2005-01-09 06:23:59 -08:00
|
|
|
|
2010-09-10 13:37:54 -07:00
|
|
|
// no conversion necessary
|
|
|
|
|
if(!(transforms & (TEX_BGR | TEX_ALPHA)))
|
|
|
|
|
{
|
|
|
|
|
if(src != dst) // avoid overlapping memcpy if not flipping rows
|
|
|
|
|
{
|
|
|
|
|
for(size_t y = 0; y < h; y++)
|
|
|
|
|
{
|
2010-11-01 04:09:03 -07:00
|
|
|
memcpy(dst, src, pitch);
|
2010-09-10 13:37:54 -07:00
|
|
|
dst += pitch;
|
|
|
|
|
src += row_ofs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// RGB -> BGRA, BGR -> RGBA
|
|
|
|
|
else if(bpp == 24 && (transforms & TEX_ALPHA) && (transforms & TEX_BGR))
|
2005-09-01 19:56:54 -07:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t y = 0; y < h; y++)
|
2005-01-09 06:23:59 -08:00
|
|
|
{
|
2010-09-10 13:37:54 -07:00
|
|
|
for(size_t 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] = 0xFF;
|
|
|
|
|
dst += 4;
|
|
|
|
|
src += 3;
|
|
|
|
|
}
|
|
|
|
|
src += row_ofs - pitch; // flip? previous row : stay
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// RGB -> RGBA, BGR -> BGRA
|
|
|
|
|
else if(bpp == 24 && (transforms & TEX_ALPHA) && !(transforms & TEX_BGR))
|
|
|
|
|
{
|
|
|
|
|
for(size_t y = 0; y < h; y++)
|
|
|
|
|
{
|
|
|
|
|
for(size_t x = 0; x < w; x++)
|
|
|
|
|
{
|
|
|
|
|
// need temporaries in case src == dst (i.e. not flipping)
|
|
|
|
|
const u8 r = src[0], g = src[1], b = src[2];
|
|
|
|
|
dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 0xFF;
|
|
|
|
|
dst += 4;
|
|
|
|
|
src += 3;
|
|
|
|
|
}
|
|
|
|
|
src += row_ofs - pitch; // flip? previous row : stay
|
2005-01-09 06:23:59 -08:00
|
|
|
}
|
2005-09-01 19:56:54 -07:00
|
|
|
}
|
2005-01-09 06:23:59 -08:00
|
|
|
// RGB <-> BGR
|
2010-09-10 13:37:54 -07:00
|
|
|
else if(bpp == 24 && !(transforms & TEX_ALPHA))
|
2005-09-01 19:56:54 -07:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t y = 0; y < h; y++)
|
2005-01-09 06:23:59 -08:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t x = 0; x < w; x++)
|
2005-01-09 06:23:59 -08:00
|
|
|
{
|
|
|
|
|
// 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-01 19:56:54 -07:00
|
|
|
}
|
2005-01-09 06:23:59 -08:00
|
|
|
// RGBA <-> BGRA
|
2010-09-10 13:37:54 -07:00
|
|
|
else if(bpp == 32 && !(transforms & TEX_ALPHA))
|
2005-09-01 19:56:54 -07:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t y = 0; y < h; y++)
|
2005-01-09 06:23:59 -08:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t x = 0; x < w; x++)
|
2005-01-09 06:23:59 -08:00
|
|
|
{
|
|
|
|
|
// 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-01 19:56:54 -07:00
|
|
|
}
|
2010-09-10 13:37:54 -07:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
debug_warn(L"unsupported transform");
|
|
|
|
|
return INFO::TEX_CODEC_CANNOT_HANDLE;
|
|
|
|
|
}
|
2005-01-09 06:23:59 -08:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
t->m_Data = dstStorage;
|
|
|
|
|
t->m_DataSize = dstSize;
|
|
|
|
|
t->m_Ofs = 0;
|
2005-10-11 21:41:28 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
if(!(t->m_Flags & TEX_MIPMAPS) && transforms & TEX_MIPMAPS)
|
2011-08-17 01:38:53 -07:00
|
|
|
RETURN_STATUS_IF_ERR(add_mipmaps(t, w, h, bpp, dstStorage.get(), dstSize));
|
2006-05-30 21:01:59 -07:00
|
|
|
|
|
|
|
|
CHECK_TEX(t);
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2006-05-30 21:01:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TIMER_ADD_CLIENT(tc_transform);
|
|
|
|
|
|
2014-03-13 12:00:22 -07:00
|
|
|
// change the pixel format by flipping the state of all TEX_* flags
|
2006-05-30 21:01:59 -07:00
|
|
|
// that are set in transforms.
|
2014-03-12 19:37:05 -07:00
|
|
|
Status Tex::transform(size_t transforms)
|
2006-05-30 21:01:59 -07:00
|
|
|
{
|
|
|
|
|
TIMER_ACCRUE(tc_transform);
|
2014-03-12 19:37:05 -07:00
|
|
|
CHECK_TEX(this);
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t target_flags = m_Flags ^ transforms;
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
size_t remaining_transforms;
|
2006-05-30 21:01:59 -07:00
|
|
|
for(;;)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
2014-03-12 19:37:05 -07:00
|
|
|
remaining_transforms = target_flags ^ m_Flags;
|
2006-05-30 21:01:59 -07:00
|
|
|
// we're finished (all required transforms have been done)
|
|
|
|
|
if(remaining_transforms == 0)
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
Status ret = tex_codec_transform(this, remaining_transforms);
|
2022-02-13 11:30:28 -08:00
|
|
|
UpdateMIPLevels();
|
2006-09-22 06:19:40 -07:00
|
|
|
if(ret != INFO::OK)
|
2006-05-30 21:01:59 -07:00
|
|
|
break;
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
2005-01-09 06:23:59 -08:00
|
|
|
|
2006-05-30 21:01:59 -07:00
|
|
|
// last chance
|
2014-03-12 19:37:05 -07:00
|
|
|
RETURN_STATUS_IF_ERR(plain_transform(this, remaining_transforms));
|
2022-02-13 11:30:28 -08:00
|
|
|
UpdateMIPLevels();
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2005-01-06 18:00:14 -08:00
|
|
|
}
|
2004-08-08 11:07:46 -07:00
|
|
|
|
|
|
|
|
|
2014-03-13 12:00:22 -07:00
|
|
|
// change the pixel format to the new format specified by <new_flags>.
|
|
|
|
|
// (note: this is equivalent to transform(t, t->flags^new_flags).
|
2014-03-12 19:37:05 -07:00
|
|
|
Status Tex::transform_to(size_t new_flags)
|
2006-05-30 21:01:59 -07:00
|
|
|
{
|
2014-03-13 12:00:22 -07:00
|
|
|
// transform takes care of validating
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t transforms = m_Flags ^ new_flags;
|
|
|
|
|
return transform(transforms);
|
2006-05-30 21:01:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-10-03 05:57:31 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// image orientation
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
// see "Default Orientation" in docs.
|
|
|
|
|
|
2005-10-03 05:57:31 -07:00
|
|
|
static int global_orientation = TEX_TOP_DOWN;
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
// set the orientation (either TEX_BOTTOM_UP or TEX_TOP_DOWN) to which
|
|
|
|
|
// all loaded images will automatically be converted
|
|
|
|
|
// (excepting file formats that don't specify their orientation, i.e. DDS).
|
2005-10-03 05:57:31 -07:00
|
|
|
void tex_set_global_orientation(int o)
|
|
|
|
|
{
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(o == TEX_TOP_DOWN || o == TEX_BOTTOM_UP);
|
2005-10-03 05:57:31 -07:00
|
|
|
global_orientation = o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void flip_to_global_orientation(Tex* t)
|
|
|
|
|
{
|
2005-10-23 16:57:59 -07:00
|
|
|
// (can't use normal CHECK_TEX due to void return)
|
2014-03-12 19:37:05 -07:00
|
|
|
WARN_IF_ERR(t->validate());
|
2005-10-23 16:57:59 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
size_t orientation = t->m_Flags & TEX_ORIENTATION;
|
2005-10-11 21:41:28 -07:00
|
|
|
// if codec knows which way around the image is (i.e. not DDS):
|
2005-10-03 05:57:31 -07:00
|
|
|
if(orientation)
|
|
|
|
|
{
|
2005-10-11 21:41:28 -07:00
|
|
|
// flip image if necessary
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
size_t transforms = orientation ^ global_orientation;
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_IF_ERR(plain_transform(t, transforms));
|
2005-10-03 05:57:31 -07:00
|
|
|
}
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
// indicate image is at global orientation. this is still done even
|
|
|
|
|
// if the codec doesn't know: the default orientation should be chosen
|
|
|
|
|
// to make that work correctly (see "Default Orientation" in docs).
|
2014-03-12 19:37:05 -07:00
|
|
|
t->m_Flags = (t->m_Flags & ~TEX_ORIENTATION) | global_orientation;
|
2005-10-23 16:57:59 -07:00
|
|
|
|
|
|
|
|
// (can't use normal CHECK_TEX due to void return)
|
2014-03-12 19:37:05 -07:00
|
|
|
WARN_IF_ERR(t->validate());
|
2005-10-03 05:57:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// indicate if the orientation specified by <src_flags> matches
|
|
|
|
|
// dst_orientation (if the latter is 0, then the global_orientation).
|
|
|
|
|
// (we ask for src_flags instead of src_orientation so callers don't
|
|
|
|
|
// have to mask off TEX_ORIENTATION)
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
bool tex_orientations_match(size_t src_flags, size_t dst_orientation)
|
2005-10-03 05:57:31 -07:00
|
|
|
{
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 11:48:32 -07:00
|
|
|
const size_t src_orientation = src_flags & TEX_ORIENTATION;
|
2005-10-03 05:57:31 -07:00
|
|
|
if(dst_orientation == 0)
|
|
|
|
|
dst_orientation = global_orientation;
|
|
|
|
|
return (src_orientation == dst_orientation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
2006-05-30 21:01:59 -07:00
|
|
|
// misc. API
|
2005-09-01 19:56:54 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2006-02-22 23:55:32 -08:00
|
|
|
// indicate if <filename>'s extension is that of a texture format
|
2014-03-13 12:00:22 -07:00
|
|
|
// supported by Tex::load. case-insensitive.
|
2006-02-22 23:55:32 -08:00
|
|
|
//
|
2014-03-13 12:00:22 -07:00
|
|
|
// rationale: Tex::load complains if the given file is of an
|
2006-02-22 23:55:32 -08:00
|
|
|
// unsupported type. this API allows users to preempt that warning
|
|
|
|
|
// (by checking the filename themselves), and also provides for e.g.
|
|
|
|
|
// enumerating only images in a file picker.
|
|
|
|
|
// an alternative might be a flag to suppress warning about invalid files,
|
|
|
|
|
// but this is open to misuse.
|
2009-11-03 13:46:35 -08:00
|
|
|
bool tex_is_known_extension(const VfsPath& pathname)
|
2006-02-22 23:55:32 -08:00
|
|
|
{
|
2014-03-12 21:16:20 -07:00
|
|
|
const ITexCodec* dummy;
|
2006-02-22 23:55:32 -08:00
|
|
|
// found codec for it => known extension
|
2011-03-23 06:36:20 -07:00
|
|
|
const OsPath extension = pathname.Extension();
|
2007-12-22 10:15:52 -08:00
|
|
|
if(tex_codec_for_filename(extension, &dummy) == INFO::OK)
|
2006-02-22 23:55:32 -08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
// store the given image data into a Tex object; this will be as if
|
2014-03-13 12:00:22 -07:00
|
|
|
// it had been loaded via Tex::load.
|
2005-10-11 21:41:28 -07:00
|
|
|
//
|
|
|
|
|
// rationale: support for in-memory images is necessary for
|
|
|
|
|
// emulation of glCompressedTexImage2D and useful overall.
|
|
|
|
|
// however, we don't want to provide an alternate interface for each API;
|
|
|
|
|
// these would have to be changed whenever fields are added to Tex.
|
|
|
|
|
// instead, provide one entry point for specifying images.
|
|
|
|
|
//
|
|
|
|
|
// we need only add bookkeeping information and "wrap" it in
|
|
|
|
|
// our Tex struct, hence the name.
|
2021-05-22 12:32:38 -07:00
|
|
|
Status Tex::wrap(size_t w, size_t h, size_t bpp, size_t flags, const std::shared_ptr<u8>& data, size_t ofs)
|
2005-10-03 05:57:31 -07:00
|
|
|
{
|
2014-03-12 19:37:05 -07:00
|
|
|
m_Width = w;
|
|
|
|
|
m_Height = h;
|
|
|
|
|
m_Bpp = bpp;
|
|
|
|
|
m_Flags = flags;
|
|
|
|
|
m_Data = data;
|
|
|
|
|
m_DataSize = ofs + w*h*bpp/8;
|
|
|
|
|
m_Ofs = ofs;
|
|
|
|
|
|
|
|
|
|
CHECK_TEX(this);
|
2022-02-13 11:30:28 -08:00
|
|
|
|
|
|
|
|
UpdateMIPLevels();
|
|
|
|
|
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2005-10-03 05:57:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
// free all resources associated with the image and make further
|
|
|
|
|
// use of it impossible.
|
2014-03-12 19:37:05 -07:00
|
|
|
void Tex::free()
|
2004-08-08 11:07:46 -07:00
|
|
|
{
|
2014-03-13 12:00:22 -07:00
|
|
|
// do not validate - this is called from Tex::load if loading
|
2005-10-11 21:41:28 -07:00
|
|
|
// failed, so not all fields may be valid.
|
|
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
m_Data.reset();
|
2005-10-11 21:41:28 -07:00
|
|
|
|
2022-02-13 14:07:56 -08:00
|
|
|
// TODO: refactor fields zeroing.
|
2005-10-03 05:57:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2006-05-30 21:01:59 -07:00
|
|
|
// getters
|
2005-10-03 05:57:31 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
2005-09-19 15:48:20 -07:00
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
// returns a pointer to the image data (pixels), taking into account any
|
2007-12-20 12:14:21 -08:00
|
|
|
// header(s) that may come before it.
|
2014-03-12 19:37:05 -07:00
|
|
|
u8* Tex::get_data()
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
2005-10-23 16:57:59 -07:00
|
|
|
// (can't use normal CHECK_TEX due to u8* return value)
|
2014-03-12 19:37:05 -07:00
|
|
|
WARN_IF_ERR(validate());
|
2005-10-11 21:41:28 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
u8* p = m_Data.get();
|
2005-10-11 21:41:28 -07:00
|
|
|
if(!p)
|
2022-01-23 23:00:55 -08:00
|
|
|
return nullptr;
|
2014-03-12 19:37:05 -07:00
|
|
|
return p + m_Ofs;
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
|
|
|
|
|
2015-03-15 16:59:48 -07:00
|
|
|
// returns color of 1x1 mipmap level
|
|
|
|
|
u32 Tex::get_average_color() const
|
2010-09-10 13:37:54 -07:00
|
|
|
{
|
|
|
|
|
// require mipmaps
|
2014-03-12 19:37:05 -07:00
|
|
|
if(!(m_Flags & TEX_MIPMAPS))
|
2010-09-10 13:37:54 -07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// find the total size of image data
|
2014-03-12 19:37:05 -07:00
|
|
|
size_t size = img_size();
|
2010-09-10 13:37:54 -07:00
|
|
|
|
|
|
|
|
// compute the size of the last (1x1) mipmap level
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t data_padding = (m_Flags & TEX_DXT)? 4 : 1;
|
|
|
|
|
size_t last_level_size = (size_t)(data_padding * data_padding * m_Bpp/8);
|
2010-09-10 13:37:54 -07:00
|
|
|
|
|
|
|
|
// construct a new texture based on the current one,
|
2014-03-12 19:37:05 -07:00
|
|
|
// but only include the last mipmap level
|
|
|
|
|
// do this so that we can use the general conversion methods for the pixel data
|
2026-04-10 14:03:00 -07:00
|
|
|
Tex basetex;
|
|
|
|
|
std::shared_ptr<uint8_t> data;
|
|
|
|
|
WARN_IF_ERR(AllocateAligned(data, last_level_size));
|
|
|
|
|
memcpy(data.get(), m_Data.get() + m_Ofs + size - last_level_size, last_level_size);
|
|
|
|
|
basetex.wrap(1, 1, m_Bpp, m_Flags, data, 0);
|
2010-09-10 13:37:54 -07:00
|
|
|
|
|
|
|
|
// convert to BGRA
|
2014-03-12 19:37:05 -07:00
|
|
|
WARN_IF_ERR(basetex.transform_to(TEX_BGR | TEX_ALPHA));
|
2010-09-10 13:37:54 -07:00
|
|
|
|
|
|
|
|
// extract components into u32
|
2014-03-12 19:37:05 -07:00
|
|
|
ENSURE(basetex.m_DataSize >= basetex.m_Ofs+4);
|
|
|
|
|
u8 b = basetex.m_Data.get()[basetex.m_Ofs];
|
|
|
|
|
u8 g = basetex.m_Data.get()[basetex.m_Ofs+1];
|
|
|
|
|
u8 r = basetex.m_Data.get()[basetex.m_Ofs+2];
|
|
|
|
|
u8 a = basetex.m_Data.get()[basetex.m_Ofs+3];
|
2010-09-10 13:37:54 -07:00
|
|
|
return b + (g << 8) + (r << 16) + (a << 24);
|
|
|
|
|
}
|
|
|
|
|
|
2005-10-11 21:41:28 -07:00
|
|
|
|
2025-06-03 05:13:41 -07:00
|
|
|
static void add_level_size(size_t /*level*/, size_t /*level_w*/, size_t /*level_h*/,
|
|
|
|
|
const u8* RESTRICT /*level_data*/, size_t level_dataSize, void* RESTRICT cbData)
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
2007-09-25 03:43:11 -07:00
|
|
|
size_t* ptotal_size = (size_t*)cbData;
|
2011-08-17 01:38:53 -07:00
|
|
|
*ptotal_size += level_dataSize;
|
2005-10-11 21:41:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return total byte size of the image pixels. (including mipmaps!)
|
|
|
|
|
// this is preferable to calculating manually because it's
|
|
|
|
|
// less error-prone (e.g. confusing bits_per_pixel with bytes).
|
2014-03-12 19:37:05 -07:00
|
|
|
size_t Tex::img_size() const
|
2005-10-11 21:41:28 -07:00
|
|
|
{
|
2005-10-23 16:57:59 -07:00
|
|
|
// (can't use normal CHECK_TEX due to size_t return value)
|
2014-03-12 19:37:05 -07:00
|
|
|
WARN_IF_ERR(validate());
|
2005-10-11 21:41:28 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
const int levels_to_skip = (m_Flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY;
|
|
|
|
|
const size_t data_padding = (m_Flags & TEX_DXT)? 4 : 1;
|
2005-10-11 21:41:28 -07:00
|
|
|
size_t out_size = 0;
|
2014-03-12 19:37:05 -07:00
|
|
|
tex_util_foreach_mipmap(m_Width, m_Height, m_Bpp, 0, levels_to_skip, data_padding, add_level_size, &out_size);
|
2005-10-11 21:41:28 -07:00
|
|
|
return out_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// return the minimum header size (i.e. offset to pixel data) of the
|
|
|
|
|
// file format indicated by <fn>'s extension (that is all it need contain:
|
|
|
|
|
// e.g. ".bmp"). returns 0 on error (i.e. no codec found).
|
|
|
|
|
// this can be used to optimize calls to tex_write: when allocating the
|
|
|
|
|
// buffer that will hold the image, allocate this much extra and
|
|
|
|
|
// pass the pointer as base+hdr_size. this allows writing the header
|
|
|
|
|
// directly into the output buffer and makes for zero-copy IO.
|
2008-01-07 12:03:19 -08:00
|
|
|
size_t tex_hdr_size(const VfsPath& filename)
|
2005-10-03 05:57:31 -07:00
|
|
|
{
|
2014-03-12 21:16:20 -07:00
|
|
|
const ITexCodec* c;
|
2016-11-23 06:09:58 -08:00
|
|
|
|
2011-03-23 06:36:20 -07:00
|
|
|
const OsPath extension = filename.Extension();
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_RETURN_STATUS_IF_ERR(tex_codec_for_filename(extension, &c));
|
2005-10-03 05:57:31 -07:00
|
|
|
return c->hdr_size(0);
|
2005-09-19 15:48:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-05-30 21:01:59 -07:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// read/write from memory and disk
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2021-05-22 12:32:38 -07:00
|
|
|
Status Tex::decode(const std::shared_ptr<u8>& Data, size_t DataSize)
|
2006-05-30 21:01:59 -07:00
|
|
|
{
|
2014-03-12 21:16:20 -07:00
|
|
|
const ITexCodec* c;
|
2014-03-12 19:37:05 -07:00
|
|
|
RETURN_STATUS_IF_ERR(tex_codec_for_header(Data.get(), DataSize, &c));
|
2006-05-30 21:01:59 -07:00
|
|
|
|
|
|
|
|
// make sure the entire header is available
|
|
|
|
|
const size_t min_hdr_size = c->hdr_size(0);
|
2014-03-12 19:37:05 -07:00
|
|
|
if(DataSize < min_hdr_size)
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::TEX_INCOMPLETE_HEADER;
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t hdr_size = c->hdr_size(Data.get());
|
|
|
|
|
if(DataSize < hdr_size)
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::TEX_INCOMPLETE_HEADER;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
m_Data = Data;
|
|
|
|
|
m_DataSize = DataSize;
|
|
|
|
|
m_Ofs = hdr_size;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2017-01-28 16:06:28 -08:00
|
|
|
RETURN_STATUS_IF_ERR(c->decode(Data.get(), DataSize, this));
|
2006-05-30 21:01:59 -07:00
|
|
|
|
|
|
|
|
// sanity checks
|
2014-03-12 19:37:05 -07:00
|
|
|
if(!m_Width || !m_Height || m_Bpp > 32)
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::TEX_FMT_INVALID;
|
2014-03-12 19:37:05 -07:00
|
|
|
if(m_DataSize < m_Ofs + img_size())
|
2022-04-12 10:39:05 -07:00
|
|
|
return ERR::TEX_INVALID_SIZE;
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
flip_to_global_orientation(this);
|
2006-05-30 21:01:59 -07:00
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
CHECK_TEX(this);
|
2007-12-22 10:15:52 -08:00
|
|
|
|
2022-02-13 11:30:28 -08:00
|
|
|
UpdateMIPLevels();
|
|
|
|
|
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2006-05-30 21:01:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
Status Tex::encode(const OsPath& extension, DynArray* da)
|
2004-07-08 07:40:24 -07:00
|
|
|
{
|
2014-03-12 19:37:05 -07:00
|
|
|
CHECK_TEX(this);
|
|
|
|
|
WARN_RETURN_STATUS_IF_ERR(tex_validate_plain_format(m_Bpp, m_Flags));
|
2005-09-10 07:28:55 -07: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
|
2014-03-13 12:00:22 -07:00
|
|
|
// most likely the case if in_img == get_data() + c->hdr_size(0).
|
2005-10-03 05:57:31 -07:00
|
|
|
// this would make for zero-copy IO.
|
|
|
|
|
|
2014-03-12 19:37:05 -07:00
|
|
|
const size_t max_out_size = img_size()*4 + 256*KiB;
|
2011-05-03 05:38:42 -07:00
|
|
|
RETURN_STATUS_IF_ERR(da_alloc(da, max_out_size));
|
2005-09-06 15:44:48 -07:00
|
|
|
|
2014-03-12 21:16:20 -07:00
|
|
|
const ITexCodec* c;
|
2011-05-03 05:38:42 -07:00
|
|
|
WARN_RETURN_STATUS_IF_ERR(tex_codec_for_filename(extension, &c));
|
2005-09-10 07:28:55 -07:00
|
|
|
|
2005-10-03 05:57:31 -07:00
|
|
|
// encode into <da>
|
2014-03-12 19:37:05 -07:00
|
|
|
Status err = c->encode(this, da);
|
2005-09-10 07:28:55 -07:00
|
|
|
if(err < 0)
|
2004-03-02 15:56:51 -08:00
|
|
|
{
|
2006-05-30 21:01:59 -07:00
|
|
|
(void)da_free(da);
|
|
|
|
|
WARN_RETURN(err);
|
2004-03-02 15:56:51 -08:00
|
|
|
}
|
2005-10-03 05:57:31 -07:00
|
|
|
|
2006-09-22 06:19:40 -07:00
|
|
|
return INFO::OK;
|
2006-05-30 21:01:59 -07:00
|
|
|
}
|
2022-02-13 11:30:28 -08:00
|
|
|
|
|
|
|
|
void Tex::UpdateMIPLevels()
|
|
|
|
|
{
|
|
|
|
|
m_MIPLevels.clear();
|
|
|
|
|
|
|
|
|
|
if (m_Flags & TEX_MIPMAPS)
|
|
|
|
|
{
|
|
|
|
|
// We add one because we need to account the smallest 1x1 level.
|
|
|
|
|
m_MIPLevels.reserve(ceil_log2(std::max(m_Width, m_Height)) + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u8* levelData = m_Data.get();
|
|
|
|
|
levelData += m_Ofs;
|
|
|
|
|
|
|
|
|
|
const u32 dataPadding = (m_Flags & TEX_DXT) != 0 ? 4 : 1;
|
|
|
|
|
u32 levelWidth = m_Width, levelHeight = m_Height;
|
2024-08-29 00:45:59 -07:00
|
|
|
for (;;)
|
2022-02-13 11:30:28 -08:00
|
|
|
{
|
|
|
|
|
const u32 levelDataSize = round_up(levelWidth, dataPadding) * round_up(levelHeight, dataPadding) * m_Bpp / 8;
|
|
|
|
|
m_MIPLevels.emplace_back();
|
|
|
|
|
m_MIPLevels.back().data = levelData;
|
|
|
|
|
m_MIPLevels.back().dataSize = levelDataSize;
|
|
|
|
|
m_MIPLevels.back().width = levelWidth;
|
|
|
|
|
m_MIPLevels.back().height = levelHeight;
|
|
|
|
|
|
|
|
|
|
if (!(m_Flags & TEX_MIPMAPS))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (levelWidth == 1 && levelHeight == 1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
levelData += levelDataSize;
|
|
|
|
|
levelWidth = std::max<u32>(levelWidth / 2, 1);
|
|
|
|
|
levelHeight = std::max<u32>(levelHeight / 2, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|