1
0
forked from 0ad/0ad

Texture system refactoring and cleanup, fixes #2455, patch by IronNerd.

This was SVN commit r14835.
This commit is contained in:
JoshuaJB 2014-03-13 02:37:05 +00:00
parent a068935789
commit a5639631ee
21 changed files with 387 additions and 438 deletions

View File

@ -232,7 +232,7 @@ void CHeightMipmap::DumpToDisk(const VfsPath& filename) const
AllocateAligned(buf, hdr_size+img_size, maxSectorSize); AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
void* img = buf.get() + hdr_size; void* img = buf.get() + hdr_size;
Tex t; Tex t;
WARN_IF_ERR(tex_wrap(w, h, bpp, flags, buf, hdr_size, &t)); WARN_IF_ERR(t.wrap(w, h, bpp, flags, buf, hdr_size));
memset(img, 0x00, img_size); memset(img, 0x00, img_size);
size_t yoff = 0; size_t yoff = 0;
@ -253,9 +253,7 @@ void CHeightMipmap::DumpToDisk(const VfsPath& filename) const
} }
DynArray da; DynArray da;
WARN_IF_ERR(tex_encode(&t, filename.Extension(), &da)); WARN_IF_ERR(t.encode(filename.Extension(), &da));
g_VFS->CreateFile(filename, DummySharedPtr(da.base), da.pos); g_VFS->CreateFile(filename, DummySharedPtr(da.base), da.pos);
(void)da_free(&da); (void)da_free(&da);
tex_free(&t);
} }

View File

@ -333,7 +333,7 @@ void CTerrainTextureEntry::LoadAlphaMaps(VfsPath &amtype)
// upload the composite texture // upload the composite texture
Tex t; Tex t;
(void)tex_wrap(total_w, total_h, 8, TEX_GREY, data, 0, &t); (void)t.wrap(total_w, total_h, 8, TEX_GREY, data, 0);
// uncomment the following to save a png of the generated texture // uncomment the following to save a png of the generated texture
// in the public/ directory, for debugging // in the public/ directory, for debugging

View File

@ -331,22 +331,21 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
} }
Tex tex; Tex tex;
if (tex_decode(file, fileSize, &tex) < 0) if (tex.decode(file, fileSize) < 0)
{ {
LOGERROR(L"Failed to decode texture \"%ls\"", src.string().c_str()); LOGERROR(L"Failed to decode texture \"%ls\"", src.string().c_str());
return false; return false;
} }
// Check whether there's any alpha channel // Check whether there's any alpha channel
bool hasAlpha = ((tex.flags & TEX_ALPHA) != 0); bool hasAlpha = ((tex.m_Flags & TEX_ALPHA) != 0);
if (settings.format == FMT_ALPHA) if (settings.format == FMT_ALPHA)
{ {
// Convert to uncompressed 8-bit with no mipmaps // Convert to uncompressed 8-bit with no mipmaps
if (tex_transform_to(&tex, (tex.flags | TEX_GREY) & ~(TEX_DXT | TEX_MIPMAPS | TEX_ALPHA)) < 0) if (tex.transform_to((tex.m_Flags | TEX_GREY) & ~(TEX_DXT | TEX_MIPMAPS | TEX_ALPHA)) < 0)
{ {
LOGERROR(L"Failed to transform texture \"%ls\"", src.string().c_str()); LOGERROR(L"Failed to transform texture \"%ls\"", src.string().c_str());
tex_free(&tex);
return false; return false;
} }
} }
@ -355,17 +354,16 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
// TODO: grayscale images will fail on some systems // TODO: grayscale images will fail on some systems
// see http://trac.wildfiregames.com/ticket/1640 // see http://trac.wildfiregames.com/ticket/1640
// (plain_transform doesn't know how to construct the alpha channel) // (plain_transform doesn't know how to construct the alpha channel)
if (tex.flags & TEX_GREY) if (tex.m_Flags & TEX_GREY)
{ {
LOGERROR(L"Failed to convert grayscale texture \"%ls\" - only RGB textures are currently supported", src.string().c_str()); LOGERROR(L"Failed to convert grayscale texture \"%ls\" - only RGB textures are currently supported", src.string().c_str());
return false; return false;
} }
// Convert to uncompressed BGRA with no mipmaps // Convert to uncompressed BGRA with no mipmaps
if (tex_transform_to(&tex, (tex.flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) < 0) if (tex.transform_to((tex.m_Flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) < 0)
{ {
LOGERROR(L"Failed to transform texture \"%ls\"", src.string().c_str()); LOGERROR(L"Failed to transform texture \"%ls\"", src.string().c_str());
tex_free(&tex);
return false; return false;
} }
} }
@ -375,8 +373,8 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
if (hasAlpha) if (hasAlpha)
{ {
hasAlpha = false; hasAlpha = false;
u8* data = tex_get_data(&tex); u8* data = tex.get_data();
for (size_t i = 0; i < tex.w * tex.h; ++i) for (size_t i = 0; i < tex.m_Width * tex.m_Height; ++i)
{ {
if (data[i*4+3] != 0xFF) if (data[i*4+3] != 0xFF)
{ {
@ -454,29 +452,26 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath
// TODO: normal maps, gamma, etc // TODO: normal maps, gamma, etc
// Load the texture data // Load the texture data
request->inputOptions.setTextureLayout(nvtt::TextureType_2D, tex.w, tex.h); request->inputOptions.setTextureLayout(nvtt::TextureType_2D, tex.m_Width, tex.m_Height);
if (tex.bpp == 32) if (tex.m_Bpp == 32)
{ {
request->inputOptions.setMipmapData(tex_get_data(&tex), tex.w, tex.h); request->inputOptions.setMipmapData(tex.get_data(), tex.m_Width, tex.m_Height);
} }
else // bpp == 8 else // bpp == 8
{ {
// NVTT requires 32-bit input data, so convert // NVTT requires 32-bit input data, so convert
const u8* input = tex_get_data(&tex); const u8* input = tex.get_data();
u8* rgba = new u8[tex.w * tex.h * 4]; u8* rgba = new u8[tex.m_Width * tex.m_Height * 4];
u8* p = rgba; u8* p = rgba;
for (size_t i = 0; i < tex.w * tex.h; i++) for (size_t i = 0; i < tex.m_Width * tex.m_Height; i++)
{ {
p[0] = p[1] = p[2] = p[3] = *input++; p[0] = p[1] = p[2] = p[3] = *input++;
p += 4; p += 4;
} }
request->inputOptions.setMipmapData(rgba, tex.w, tex.h); request->inputOptions.setMipmapData(rgba, tex.m_Width, tex.m_Height);
delete[] rgba; delete[] rgba;
} }
// NVTT copies the texture data so we can free it now
tex_free(&tex);
pthread_mutex_lock(&m_WorkerMutex); pthread_mutex_lock(&m_WorkerMutex);
m_RequestQueue.push_back(request); m_RequestQueue.push_back(request);
pthread_mutex_unlock(&m_WorkerMutex); pthread_mutex_unlock(&m_WorkerMutex);

View File

@ -94,7 +94,7 @@ public:
data.get()[1] = 64; data.get()[1] = 64;
data.get()[2] = 64; data.get()[2] = 64;
Tex t; Tex t;
(void)tex_wrap(1, 1, 24, 0, data, 0, &t); (void)t.wrap(1, 1, 24, 0, data, 0);
m_DefaultHandle = ogl_tex_wrap(&t, m_VFS, L"(default texture)"); m_DefaultHandle = ogl_tex_wrap(&t, m_VFS, L"(default texture)");
(void)ogl_tex_set_filter(m_DefaultHandle, GL_LINEAR); (void)ogl_tex_set_filter(m_DefaultHandle, GL_LINEAR);
@ -111,7 +111,7 @@ public:
data.get()[1] = 0; data.get()[1] = 0;
data.get()[2] = 255; data.get()[2] = 255;
Tex t; Tex t;
(void)tex_wrap(1, 1, 24, 0, data, 0, &t); (void)t.wrap(1, 1, 24, 0, data, 0);
m_ErrorHandle = ogl_tex_wrap(&t, m_VFS, L"(error texture)"); m_ErrorHandle = ogl_tex_wrap(&t, m_VFS, L"(error texture)");
(void)ogl_tex_set_filter(m_ErrorHandle, GL_LINEAR); (void)ogl_tex_set_filter(m_ErrorHandle, GL_LINEAR);

View File

@ -78,11 +78,11 @@ public:
TS_ASSERT_OK(m_VFS->LoadFile(dest, file, fileSize)); TS_ASSERT_OK(m_VFS->LoadFile(dest, file, fileSize));
Tex tex; Tex tex;
TS_ASSERT_OK(tex_decode(file, fileSize, &tex)); TS_ASSERT_OK(tex.decode(file, fileSize));
TS_ASSERT_OK(tex_transform_to(&tex, (tex.flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS))); TS_ASSERT_OK(tex.transform_to((tex.m_Flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)));
u8* texdata = tex_get_data(&tex); u8* texdata = tex.get_data();
// The source texture is repeated after 4 pixels, so the compressed texture // The source texture is repeated after 4 pixels, so the compressed texture
// should be identical after 4 pixels // should be identical after 4 pixels
@ -97,7 +97,5 @@ public:
// if (i % 4 == 0) printf("\n"); // if (i % 4 == 0) printf("\n");
// printf("%02x ", texdata[i]); // printf("%02x ", texdata[i]);
// } // }
tex_free(&tex);
} }
}; };

View File

@ -60,17 +60,17 @@ static Status load_sys_cursor(const PIVFS& vfs, const VfsPath& pathname, int hx,
shared_ptr<u8> file; size_t fileSize; shared_ptr<u8> file; size_t fileSize;
RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize)); RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize));
ScopedTex t; Tex t;
RETURN_STATUS_IF_ERR(tex_decode(file, fileSize, &t)); RETURN_STATUS_IF_ERR(t.decode(file, fileSize));
// convert to required BGRA format. // convert to required BGRA format.
const size_t flags = (t.flags | TEX_BGR) & ~TEX_DXT; const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT;
RETURN_STATUS_IF_ERR(tex_transform_to(&t, flags)); RETURN_STATUS_IF_ERR(t.transform_to(flags));
void* bgra_img = tex_get_data(&t); void* bgra_img = t.get_data();
if(!bgra_img) if(!bgra_img)
WARN_RETURN(ERR::FAIL); WARN_RETURN(ERR::FAIL);
RETURN_STATUS_IF_ERR(sys_cursor_create((int)t.w, (int)t.h, bgra_img, hx, hy, cursor)); RETURN_STATUS_IF_ERR(sys_cursor_create((int)t.m_Width, (int)t.m_Height, bgra_img, hx, hy, cursor));
return INFO::OK; return INFO::OK;
#endif #endif
} }

View File

@ -449,7 +449,7 @@ static void OglTex_dtor(OglTex* ot)
{ {
if(ot->flags & OT_TEX_VALID) if(ot->flags & OT_TEX_VALID)
{ {
tex_free(&ot->t); ot->t.free();
ot->flags &= ~OT_TEX_VALID; ot->flags &= ~OT_TEX_VALID;
} }
@ -473,7 +473,7 @@ static Status OglTex_reload(OglTex* ot, const PIVFS& vfs, const VfsPath& pathnam
{ {
shared_ptr<u8> file; size_t fileSize; shared_ptr<u8> file; size_t fileSize;
RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize)); RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize));
if(tex_decode(file, fileSize, &ot->t) >= 0) if(ot->t.decode(file, fileSize) >= 0)
ot->flags |= OT_TEX_VALID; ot->flags |= OT_TEX_VALID;
} }
@ -491,13 +491,13 @@ static Status OglTex_validate(const OglTex* ot)
{ {
if(ot->flags & OT_TEX_VALID) if(ot->flags & OT_TEX_VALID)
{ {
RETURN_STATUS_IF_ERR(tex_validate(&ot->t)); RETURN_STATUS_IF_ERR(ot->t.validate());
// width, height // width, height
// (note: this is done here because tex.cpp doesn't impose any // (note: this is done here because tex.cpp doesn't impose any
// restrictions on dimensions, while OpenGL does). // restrictions on dimensions, while OpenGL does).
size_t w = ot->t.w; size_t w = ot->t.m_Width;
size_t h = ot->t.h; size_t h = ot->t.m_Height;
// .. == 0; texture file probably not loaded successfully. // .. == 0; texture file probably not loaded successfully.
if(w == 0 || h == 0) if(w == 0 || h == 0)
WARN_RETURN(ERR::_11); WARN_RETURN(ERR::_11);
@ -794,12 +794,12 @@ static Status get_mipmaps(Tex* t, GLint filter, int q_flags, int* plevels_to_ski
{ {
// decisions: // decisions:
// .. does filter call for uploading mipmaps? // .. does filter call for uploading mipmaps?
const bool need_mipmaps = are_mipmaps_needed(t->w, t->h, filter); const bool need_mipmaps = are_mipmaps_needed(t->m_Width, t->m_Height, filter);
// .. does the image data include mipmaps? (stored as separate // .. does the image data include mipmaps? (stored as separate
// images after the regular texels) // images after the regular texels)
const bool includes_mipmaps = (t->flags & TEX_MIPMAPS) != 0; const bool includes_mipmaps = (t->m_Flags & TEX_MIPMAPS) != 0;
// .. is this texture in S3TC format? (more generally, "compressed") // .. is this texture in S3TC format? (more generally, "compressed")
const bool is_s3tc = (t->flags & TEX_DXT) != 0; const bool is_s3tc = (t->m_Flags & TEX_DXT) != 0;
*plevels_to_skip = TEX_BASE_LEVEL_ONLY; *plevels_to_skip = TEX_BASE_LEVEL_ONLY;
if(!need_mipmaps) if(!need_mipmaps)
@ -833,7 +833,7 @@ static Status get_mipmaps(Tex* t, GLint filter, int q_flags, int* plevels_to_ski
// we will generate mipmaps in software. // we will generate mipmaps in software.
else else
{ {
RETURN_STATUS_IF_ERR(tex_transform_to(t, t->flags|TEX_MIPMAPS)); RETURN_STATUS_IF_ERR(t->transform_to(t->m_Flags|TEX_MIPMAPS));
*plevels_to_skip = 0; // t contains mipmaps *plevels_to_skip = 0; // t contains mipmaps
} }
@ -843,7 +843,7 @@ static Status get_mipmaps(Tex* t, GLint filter, int q_flags, int* plevels_to_ski
// if OpenGL's texture dimension limit is too small, use the // if OpenGL's texture dimension limit is too small, use the
// higher mipmap levels. NB: the minimum guaranteed size is // higher mipmap levels. NB: the minimum guaranteed size is
// far too low, and menu background textures may be large. // far too low, and menu background textures may be large.
GLint w = (GLint)t->w, h = (GLint)t->h; GLint w = (GLint)t->m_Width, h = (GLint)t->m_Height;
while(w > ogl_max_tex_size || h > ogl_max_tex_size) while(w > ogl_max_tex_size || h > ogl_max_tex_size)
{ {
(*plevels_to_skip)++; (*plevels_to_skip)++;
@ -893,13 +893,13 @@ static void upload_compressed_level(size_t level, size_t level_w, size_t level_h
// pre: <t> is valid for OpenGL use; texture is bound. // pre: <t> is valid for OpenGL use; texture is bound.
static void upload_impl(Tex* t, GLenum fmt, GLint int_fmt, int levels_to_skip, u32* uploaded_size) static void upload_impl(Tex* t, GLenum fmt, GLint int_fmt, int levels_to_skip, u32* uploaded_size)
{ {
const GLsizei w = (GLsizei)t->w; const GLsizei w = (GLsizei)t->m_Width;
const GLsizei h = (GLsizei)t->h; const GLsizei h = (GLsizei)t->m_Height;
const size_t bpp = t->bpp; const size_t bpp = t->m_Bpp;
const u8* data = (const u8*)tex_get_data(t); const u8* data = (const u8*)t->get_data();
const UploadParams up = { fmt, int_fmt, uploaded_size }; const UploadParams up = { fmt, int_fmt, uploaded_size };
if(t->flags & TEX_DXT) if(t->m_Flags & TEX_DXT)
tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 4, upload_compressed_level, (void*)&up); tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 4, upload_compressed_level, (void*)&up);
else else
tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 1, upload_level, (void*)&up); tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 1, upload_level, (void*)&up);
@ -932,11 +932,11 @@ Status ogl_tex_upload(const Handle ht, GLenum fmt_ovr, int q_flags_ovr, GLint in
if(ot->flags & OT_TEX_VALID) if(ot->flags & OT_TEX_VALID)
{ {
// decompress S3TC if that's not supported by OpenGL. // decompress S3TC if that's not supported by OpenGL.
if((t->flags & TEX_DXT) && !have_s3tc) if((t->m_Flags & TEX_DXT) && !have_s3tc)
(void)tex_transform_to(t, t->flags & ~TEX_DXT); (void)t->transform_to(t->m_Flags & ~TEX_DXT);
// determine fmt and int_fmt, allowing for user override. // determine fmt and int_fmt, allowing for user override.
ot->fmt = choose_fmt(t->bpp, t->flags); ot->fmt = choose_fmt(t->m_Bpp, t->m_Flags);
if(fmt_ovr) ot->fmt = fmt_ovr; if(fmt_ovr) ot->fmt = fmt_ovr;
if(q_flags_ovr) ot->q_flags = q_flags_ovr; if(q_flags_ovr) ot->q_flags = q_flags_ovr;
ot->int_fmt = choose_int_fmt(ot->fmt, ot->q_flags); ot->int_fmt = choose_int_fmt(ot->fmt, ot->q_flags);
@ -964,13 +964,10 @@ Status ogl_tex_upload(const Handle ht, GLenum fmt_ovr, int q_flags_ovr, GLint in
ot->flags |= OT_IS_UPLOADED; ot->flags |= OT_IS_UPLOADED;
// see rationale for <refs> at declaration of OglTex. // see rationale for <refs> at declaration of OglTex.
// note: tex_free is safe even if this OglTex was wrapped -
// the Tex contains a mem handle.
intptr_t refs = h_get_refcnt(ht); intptr_t refs = h_get_refcnt(ht);
if(refs == 1) if(refs == 1)
{ {
// note: we verify above that OT_TEX_VALID is set // note: we verify above that OT_TEX_VALID is set
tex_free(t);
ot->flags &= ~OT_TEX_VALID; ot->flags &= ~OT_TEX_VALID;
} }
} }
@ -992,11 +989,11 @@ Status ogl_tex_get_size(Handle ht, size_t* w, size_t* h, size_t* bpp)
H_DEREF(ht, OglTex, ot); H_DEREF(ht, OglTex, ot);
if(w) if(w)
*w = ot->t.w; *w = ot->t.m_Width;
if(h) if(h)
*h = ot->t.h; *h = ot->t.m_Height;
if(bpp) if(bpp)
*bpp = ot->t.bpp; *bpp = ot->t.m_Bpp;
return INFO::OK; return INFO::OK;
} }
@ -1009,7 +1006,7 @@ Status ogl_tex_get_format(Handle ht, size_t* flags, GLenum* fmt)
H_DEREF(ht, OglTex, ot); H_DEREF(ht, OglTex, ot);
if(flags) if(flags)
*flags = ot->t.flags; *flags = ot->t.m_Flags;
if(fmt) if(fmt)
{ {
ENSURE(ot->flags & OT_IS_UPLOADED); ENSURE(ot->flags & OT_IS_UPLOADED);
@ -1030,7 +1027,7 @@ Status ogl_tex_get_data(Handle ht, u8** p)
{ {
H_DEREF(ht, OglTex, ot); H_DEREF(ht, OglTex, ot);
*p = tex_get_data(&ot->t); *p = ot->t.get_data();
return INFO::OK; return INFO::OK;
} }
@ -1048,7 +1045,7 @@ extern Status ogl_tex_get_average_colour(Handle ht, u32* p)
H_DEREF(ht, OglTex, ot); H_DEREF(ht, OglTex, ot);
warn_if_uploaded(ht, ot); warn_if_uploaded(ht, ot);
*p = tex_get_average_colour(&ot->t); *p = ot->t.get_average_colour();
return INFO::OK; return INFO::OK;
} }
@ -1111,7 +1108,7 @@ Status ogl_tex_get_texture_id(Handle ht, GLuint* id)
Status ogl_tex_transform(Handle ht, size_t transforms) Status ogl_tex_transform(Handle ht, size_t transforms)
{ {
H_DEREF(ht, OglTex, ot); H_DEREF(ht, OglTex, ot);
Status ret = tex_transform(&ot->t, transforms); Status ret = ot->t.transform(transforms);
return ret; return ret;
} }
@ -1121,7 +1118,7 @@ Status ogl_tex_transform(Handle ht, size_t transforms)
Status ogl_tex_transform_to(Handle ht, size_t new_flags) Status ogl_tex_transform_to(Handle ht, size_t new_flags)
{ {
H_DEREF(ht, OglTex, ot); H_DEREF(ht, OglTex, ot);
Status ret = tex_transform_to(&ot->t, new_flags); Status ret = ot->t.transform_to(new_flags);
return ret; return ret;
} }

View File

@ -35,27 +35,31 @@ class TestTex : public CxxTest::TestSuite
shared_ptr<u8> img(new u8[size], ArrayDeleter()); shared_ptr<u8> img(new u8[size], ArrayDeleter());
std::generate(img.get(), img.get()+size, rand); std::generate(img.get(), img.get()+size, rand);
// wrap in Tex // create the DynArray that will be wrapped in a Tex Object
Tex t;
TS_ASSERT_OK(tex_wrap(w, h, bpp, flags, img, 0, &t));
// encode to file format
DynArray da; DynArray da;
TS_ASSERT_OK(tex_encode(&t, extension, &da));
memset(&t, 0, sizeof(t));
// decode from file format // Once the Tex created here goes out of scope, the DynArray should be freed
shared_ptr<u8> ptr = DummySharedPtr(da.base); {
TS_ASSERT_OK(tex_decode(ptr, da.cur_size, &t)); // wrap in Tex
Tex t;
TS_ASSERT_OK(t.wrap(w, h, bpp, flags, img, 0));
// make sure pixel format gets converted completely to plain // encode to file format
TS_ASSERT_OK(tex_transform_to(&t, 0)); TS_ASSERT_OK(t.encode(extension, &da));
memset(&t, 0, sizeof(t));
// compare img // decode from file format
TS_ASSERT_SAME_DATA(tex_get_data(&t), img.get(), size); shared_ptr<u8> ptr = DummySharedPtr(da.base);
TS_ASSERT_OK(t.decode(ptr, da.cur_size));
// make sure pixel format gets converted completely to plain
TS_ASSERT_OK(t.transform_to(0));
// compare img
TS_ASSERT_SAME_DATA(t.get_data(), img.get(), size);
}
// cleanup // cleanup
tex_free(&t);
TS_ASSERT_OK(da_free(&da)); TS_ASSERT_OK(da_free(&da));
} }
@ -136,10 +140,10 @@ public:
// assumes 2x2 box filter algorithm with rounding // assumes 2x2 box filter algorithm with rounding
static const u8 mipmap[] = { 0x6C,0x79,0x87 }; static const u8 mipmap[] = { 0x6C,0x79,0x87 };
Tex t; Tex t;
TS_ASSERT_OK(tex_wrap(2, 2, 24, 0, img, 0, &t)); TS_ASSERT_OK(t.wrap(2, 2, 24, 0, img, 0));
TS_ASSERT_OK(tex_transform_to(&t, TEX_MIPMAPS)); TS_ASSERT_OK(t.transform_to(TEX_MIPMAPS));
const u8* const out_img = tex_get_data(&t); const u8* const out_img = t.get_data();
TS_ASSERT_EQUALS((int)tex_img_size(&t), 12+3); TS_ASSERT_EQUALS((int)t.img_size(), 12+3);
TS_ASSERT_SAME_DATA(out_img, imgData, 12); TS_ASSERT_SAME_DATA(out_img, imgData, 12);
TS_ASSERT_SAME_DATA(out_img+12, mipmap, 3); TS_ASSERT_SAME_DATA(out_img+12, mipmap, 3);
} }
@ -149,13 +153,13 @@ public:
shared_ptr<u8> img(new u8[100*100*4], ArrayDeleter()); shared_ptr<u8> img(new u8[100*100*4], ArrayDeleter());
Tex t; Tex t;
TS_ASSERT_OK(tex_wrap(100, 100, 32, TEX_ALPHA, img, 0, &t)); TS_ASSERT_OK(t.wrap(100, 100, 32, TEX_ALPHA, img, 0));
TS_ASSERT_EQUALS((int)tex_img_size(&t), 40000); TS_ASSERT_EQUALS((int)t.img_size(), 40000);
// DXT rounds up to 4x4 blocks; DXT1a is 4bpp // DXT rounds up to 4x4 blocks; DXT1a is 4bpp
Tex t2; Tex t2;
TS_ASSERT_OK(tex_wrap(97, 97, 4, DXT1A, img, 0, &t2)); TS_ASSERT_OK(t2.wrap(97, 97, 4, DXT1A, img, 0));
TS_ASSERT_EQUALS((int)tex_img_size(&t2), 5000); TS_ASSERT_EQUALS((int)t2.img_size(), 5000);
} }
void test_s3tc_decode() void test_s3tc_decode()
@ -176,17 +180,15 @@ public:
// wrap in Tex // wrap in Tex
Tex t; Tex t;
TS_ASSERT_OK(tex_wrap(w, h, bpp, flags, img, 0, &t)); TS_ASSERT_OK(t.wrap(w, h, bpp, flags, img, 0));
// decompress S3TC // decompress S3TC
TS_ASSERT_OK(tex_transform_to(&t, 0)); TS_ASSERT_OK(t.transform_to(0));
// compare img // compare img
TS_ASSERT_SAME_DATA(tex_get_data(&t), expected, 48); TS_ASSERT_SAME_DATA(t.get_data(), expected, 48);
// cleanup // cleanup
tex_free(&t);
tex_codec_unregister_all(); tex_codec_unregister_all();
} }
}; };

View File

@ -57,41 +57,41 @@ STATUS_ADD_DEFINITIONS(texStatusDefinitions);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// be careful not to use other tex_* APIs here because they call us. // be careful not to use other tex_* APIs here because they call us.
Status tex_validate(const Tex* t) Status Tex::validate() const
{ {
if(t->flags & TEX_UNDEFINED_FLAGS) if(m_Flags & TEX_UNDEFINED_FLAGS)
WARN_RETURN(ERR::_1); WARN_RETURN(ERR::_1);
// pixel data (only check validity if the image is still in memory; // pixel data (only check validity if the image is still in memory;
// ogl_tex frees the data after uploading to GL) // ogl_tex frees the data after uploading to GL)
if(t->data) if(m_Data)
{ {
// file size smaller than header+pixels. // file size smaller than header+pixels.
// possible causes: texture file header is invalid, // possible causes: texture file header is invalid,
// or file wasn't loaded completely. // or file wasn't loaded completely.
if(t->dataSize < t->ofs + t->w*t->h*t->bpp/8) if(m_DataSize < m_Ofs + m_Width*m_Height*m_Bpp/8)
WARN_RETURN(ERR::_2); WARN_RETURN(ERR::_2);
} }
// bits per pixel // bits per pixel
// (we don't bother checking all values; a sanity check is enough) // (we don't bother checking all values; a sanity check is enough)
if(t->bpp % 4 || t->bpp > 32) if(m_Bpp % 4 || m_Bpp > 32)
WARN_RETURN(ERR::_3); WARN_RETURN(ERR::_3);
// flags // flags
// .. DXT value // .. DXT value
const size_t dxt = t->flags & TEX_DXT; const size_t dxt = m_Flags & TEX_DXT;
if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5) if(dxt != 0 && dxt != 1 && dxt != DXT1A && dxt != 3 && dxt != 5)
WARN_RETURN(ERR::_4); WARN_RETURN(ERR::_4);
// .. orientation // .. orientation
const size_t orientation = t->flags & TEX_ORIENTATION; const size_t orientation = m_Flags & TEX_ORIENTATION;
if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN)) if(orientation == (TEX_BOTTOM_UP|TEX_TOP_DOWN))
WARN_RETURN(ERR::_5); WARN_RETURN(ERR::_5);
return INFO::OK; return INFO::OK;
} }
#define CHECK_TEX(t) RETURN_STATUS_IF_ERR(tex_validate(t)) #define CHECK_TEX(t) RETURN_STATUS_IF_ERR((t->validate()))
// check if the given texture format is acceptable: 8bpp grey, // check if the given texture format is acceptable: 8bpp grey,
@ -257,15 +257,15 @@ static Status add_mipmaps(Tex* t, size_t w, size_t h, size_t bpp, void* newData,
// the only place this is used (ogl_tex_upload) requires POT anyway. // the only place this is used (ogl_tex_upload) requires POT anyway.
if(!is_pow2(w) || !is_pow2(h)) if(!is_pow2(w) || !is_pow2(h))
WARN_RETURN(ERR::TEX_INVALID_SIZE); WARN_RETURN(ERR::TEX_INVALID_SIZE);
t->flags |= TEX_MIPMAPS; // must come before tex_img_size! t->m_Flags |= TEX_MIPMAPS; // must come before tex_img_size!
const size_t mipmap_size = tex_img_size(t); const size_t mipmap_size = t->img_size();
shared_ptr<u8> mipmapData; shared_ptr<u8> mipmapData;
AllocateAligned(mipmapData, mipmap_size); AllocateAligned(mipmapData, mipmap_size);
CreateLevelData cld = { bpp/8, w, h, (const u8*)newData, dataSize }; CreateLevelData cld = { bpp/8, w, h, (const u8*)newData, dataSize };
tex_util_foreach_mipmap(w, h, bpp, mipmapData.get(), 0, 1, create_level, &cld); tex_util_foreach_mipmap(w, h, bpp, mipmapData.get(), 0, 1, create_level, &cld);
t->data = mipmapData; t->m_Data = mipmapData;
t->dataSize = mipmap_size; t->m_DataSize = mipmap_size;
t->ofs = 0; t->m_Ofs = 0;
return INFO::OK; return INFO::OK;
} }
@ -293,9 +293,9 @@ TIMER_ACCRUE(tc_plain_transform);
CHECK_TEX(t); CHECK_TEX(t);
// extract texture info // extract texture info
const size_t w = t->w, h = t->h, bpp = t->bpp; const size_t w = t->m_Width, h = t->m_Height, bpp = t->m_Bpp;
const size_t flags = t->flags; const size_t flags = t->m_Flags;
u8* const srcStorage = tex_get_data(t); u8* const srcStorage = t->get_data();
// sanity checks (not errors, we just can't handle these cases) // sanity checks (not errors, we just can't handle these cases)
// .. unknown transform // .. unknown transform
@ -307,7 +307,7 @@ TIMER_ACCRUE(tc_plain_transform);
if(!transforms) if(!transforms)
return INFO::OK; return INFO::OK;
const size_t srcSize = tex_img_size(t); const size_t srcSize = t->img_size();
size_t dstSize = srcSize; size_t dstSize = srcSize;
if(transforms & TEX_ALPHA) if(transforms & TEX_ALPHA)
@ -316,7 +316,7 @@ TIMER_ACCRUE(tc_plain_transform);
if(bpp == 24) if(bpp == 24)
{ {
dstSize = (srcSize / 3) * 4; dstSize = (srcSize / 3) * 4;
t->bpp = 32; t->m_Bpp = 32;
} }
// remove alpha channel // remove alpha channel
else if(bpp == 32) else if(bpp == 32)
@ -448,11 +448,11 @@ TIMER_ACCRUE(tc_plain_transform);
return INFO::TEX_CODEC_CANNOT_HANDLE; return INFO::TEX_CODEC_CANNOT_HANDLE;
} }
t->data = dstStorage; t->m_Data = dstStorage;
t->dataSize = dstSize; t->m_DataSize = dstSize;
t->ofs = 0; t->m_Ofs = 0;
if(!(t->flags & TEX_MIPMAPS) && transforms & TEX_MIPMAPS) if(!(t->m_Flags & TEX_MIPMAPS) && transforms & TEX_MIPMAPS)
RETURN_STATUS_IF_ERR(add_mipmaps(t, w, h, bpp, dstStorage.get(), dstSize)); RETURN_STATUS_IF_ERR(add_mipmaps(t, w, h, bpp, dstStorage.get(), dstSize));
CHECK_TEX(t); CHECK_TEX(t);
@ -464,38 +464,38 @@ TIMER_ADD_CLIENT(tc_transform);
// change <t>'s pixel format by flipping the state of all TEX_* flags // change <t>'s pixel format by flipping the state of all TEX_* flags
// that are set in transforms. // that are set in transforms.
Status tex_transform(Tex* t, size_t transforms) Status Tex::transform(size_t transforms)
{ {
TIMER_ACCRUE(tc_transform); TIMER_ACCRUE(tc_transform);
CHECK_TEX(t); CHECK_TEX(this);
const size_t target_flags = t->flags ^ transforms; const size_t target_flags = m_Flags ^ transforms;
size_t remaining_transforms; size_t remaining_transforms;
for(;;) for(;;)
{ {
remaining_transforms = target_flags ^ t->flags; remaining_transforms = target_flags ^ m_Flags;
// we're finished (all required transforms have been done) // we're finished (all required transforms have been done)
if(remaining_transforms == 0) if(remaining_transforms == 0)
return INFO::OK; return INFO::OK;
Status ret = tex_codec_transform(t, remaining_transforms); Status ret = tex_codec_transform(this, remaining_transforms);
if(ret != INFO::OK) if(ret != INFO::OK)
break; break;
} }
// last chance // last chance
RETURN_STATUS_IF_ERR(plain_transform(t, remaining_transforms)); RETURN_STATUS_IF_ERR(plain_transform(this, remaining_transforms));
return INFO::OK; return INFO::OK;
} }
// change <t>'s pixel format to the new format specified by <new_flags>. // change <t>'s pixel format to the new format specified by <new_flags>.
// (note: this is equivalent to tex_transform(t, t->flags^new_flags). // (note: this is equivalent to tex_transform(t, t->flags^new_flags).
Status tex_transform_to(Tex* t, size_t new_flags) Status Tex::transform_to(size_t new_flags)
{ {
// tex_transform takes care of validating <t> // tex_transform takes care of validating <t>
const size_t transforms = t->flags ^ new_flags; const size_t transforms = m_Flags ^ new_flags;
return tex_transform(t, transforms); return transform(transforms);
} }
@ -520,9 +520,9 @@ void tex_set_global_orientation(int o)
static void flip_to_global_orientation(Tex* t) static void flip_to_global_orientation(Tex* t)
{ {
// (can't use normal CHECK_TEX due to void return) // (can't use normal CHECK_TEX due to void return)
WARN_IF_ERR(tex_validate(t)); WARN_IF_ERR(t->validate());
size_t orientation = t->flags & TEX_ORIENTATION; size_t orientation = t->m_Flags & TEX_ORIENTATION;
// if codec knows which way around the image is (i.e. not DDS): // if codec knows which way around the image is (i.e. not DDS):
if(orientation) if(orientation)
{ {
@ -534,10 +534,10 @@ static void flip_to_global_orientation(Tex* t)
// indicate image is at global orientation. this is still done even // indicate image is at global orientation. this is still done even
// if the codec doesn't know: the default orientation should be chosen // if the codec doesn't know: the default orientation should be chosen
// to make that work correctly (see "Default Orientation" in docs). // to make that work correctly (see "Default Orientation" in docs).
t->flags = (t->flags & ~TEX_ORIENTATION) | global_orientation; t->m_Flags = (t->m_Flags & ~TEX_ORIENTATION) | global_orientation;
// (can't use normal CHECK_TEX due to void return) // (can't use normal CHECK_TEX due to void return)
WARN_IF_ERR(tex_validate(t)); WARN_IF_ERR(t->validate());
} }
@ -590,29 +590,29 @@ bool tex_is_known_extension(const VfsPath& pathname)
// //
// we need only add bookkeeping information and "wrap" it in // we need only add bookkeeping information and "wrap" it in
// our Tex struct, hence the name. // our Tex struct, hence the name.
Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr<u8>& data, size_t ofs, Tex* t) Status Tex::wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr<u8>& data, size_t ofs)
{ {
t->w = w; m_Width = w;
t->h = h; m_Height = h;
t->bpp = bpp; m_Bpp = bpp;
t->flags = flags; m_Flags = flags;
t->data = data; m_Data = data;
t->dataSize = ofs + w*h*bpp/8; m_DataSize = ofs + w*h*bpp/8;
t->ofs = ofs; m_Ofs = ofs;
CHECK_TEX(t); CHECK_TEX(this);
return INFO::OK; return INFO::OK;
} }
// free all resources associated with the image and make further // free all resources associated with the image and make further
// use of it impossible. // use of it impossible.
void tex_free(Tex* t) void Tex::free()
{ {
// do not validate <t> - this is called from tex_load if loading // do not validate <t> - this is called from tex_load if loading
// failed, so not all fields may be valid. // failed, so not all fields may be valid.
t->data.reset(); m_Data.reset();
// do not zero out the fields! that could lead to trouble since // do not zero out the fields! that could lead to trouble since
// ogl_tex_upload followed by ogl_tex_free is legit, but would // ogl_tex_upload followed by ogl_tex_free is legit, but would
@ -626,47 +626,49 @@ void tex_free(Tex* t)
// returns a pointer to the image data (pixels), taking into account any // returns a pointer to the image data (pixels), taking into account any
// header(s) that may come before it. // header(s) that may come before it.
u8* tex_get_data(const Tex* t) u8* Tex::get_data()
{ {
// (can't use normal CHECK_TEX due to u8* return value) // (can't use normal CHECK_TEX due to u8* return value)
WARN_IF_ERR(tex_validate(t)); WARN_IF_ERR(validate());
u8* p = t->data.get(); u8* p = m_Data.get();
if(!p) if(!p)
return 0; return 0;
return p + t->ofs; return p + m_Ofs;
} }
// returns colour of 1x1 mipmap level // returns colour of 1x1 mipmap level
u32 tex_get_average_colour(const Tex* t) u32 Tex::get_average_colour() const
{ {
// require mipmaps // require mipmaps
if(!(t->flags & TEX_MIPMAPS)) if(!(m_Flags & TEX_MIPMAPS))
return 0; return 0;
// find the total size of image data // find the total size of image data
size_t size = tex_img_size(t); size_t size = img_size();
// compute the size of the last (1x1) mipmap level // compute the size of the last (1x1) mipmap level
const size_t data_padding = (t->flags & TEX_DXT)? 4 : 1; const size_t data_padding = (m_Flags & TEX_DXT)? 4 : 1;
size_t last_level_size = (size_t)(data_padding * data_padding * t->bpp/8); size_t last_level_size = (size_t)(data_padding * data_padding * m_Bpp/8);
// construct a new texture based on the current one, // construct a new texture based on the current one,
// but set its data pointer offset to the last mipmap level's data // but only include the last mipmap level
Tex basetex = *t; // do this so that we can use the general conversion methods for the pixel data
basetex.w = 1; Tex basetex = *this;
basetex.h = 1; uint8_t *data = new uint8_t[last_level_size];
basetex.ofs += size - last_level_size; memcpy(data, m_Data.get() + m_Ofs + size - last_level_size, last_level_size);
boost::shared_ptr<uint8_t> sdata(data);
basetex.wrap(1, 1, m_Bpp, m_Flags, sdata, 0);
// convert to BGRA // convert to BGRA
WARN_IF_ERR(tex_transform_to(&basetex, TEX_BGR | TEX_ALPHA)); WARN_IF_ERR(basetex.transform_to(TEX_BGR | TEX_ALPHA));
// extract components into u32 // extract components into u32
ENSURE(basetex.dataSize >= basetex.ofs+4); ENSURE(basetex.m_DataSize >= basetex.m_Ofs+4);
u8 b = basetex.data.get()[basetex.ofs]; u8 b = basetex.m_Data.get()[basetex.m_Ofs];
u8 g = basetex.data.get()[basetex.ofs+1]; u8 g = basetex.m_Data.get()[basetex.m_Ofs+1];
u8 r = basetex.data.get()[basetex.ofs+2]; u8 r = basetex.m_Data.get()[basetex.m_Ofs+2];
u8 a = basetex.data.get()[basetex.ofs+3]; u8 a = basetex.m_Data.get()[basetex.m_Ofs+3];
return b + (g << 8) + (r << 16) + (a << 24); return b + (g << 8) + (r << 16) + (a << 24);
} }
@ -680,15 +682,15 @@ static void add_level_size(size_t UNUSED(level), size_t UNUSED(level_w), size_t
// return total byte size of the image pixels. (including mipmaps!) // return total byte size of the image pixels. (including mipmaps!)
// this is preferable to calculating manually because it's // this is preferable to calculating manually because it's
// less error-prone (e.g. confusing bits_per_pixel with bytes). // less error-prone (e.g. confusing bits_per_pixel with bytes).
size_t tex_img_size(const Tex* t) size_t Tex::img_size() const
{ {
// (can't use normal CHECK_TEX due to size_t return value) // (can't use normal CHECK_TEX due to size_t return value)
WARN_IF_ERR(tex_validate(t)); WARN_IF_ERR(validate());
const int levels_to_skip = (t->flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY; const int levels_to_skip = (m_Flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY;
const size_t data_padding = (t->flags & TEX_DXT)? 4 : 1; const size_t data_padding = (m_Flags & TEX_DXT)? 4 : 1;
size_t out_size = 0; size_t out_size = 0;
tex_util_foreach_mipmap(t->w, t->h, t->bpp, 0, levels_to_skip, data_padding, add_level_size, &out_size); tex_util_foreach_mipmap(m_Width, m_Height, m_Bpp, 0, levels_to_skip, data_padding, add_level_size, &out_size);
return out_size; return out_size;
} }
@ -714,57 +716,57 @@ size_t tex_hdr_size(const VfsPath& filename)
// read/write from memory and disk // read/write from memory and disk
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
Status tex_decode(const shared_ptr<u8>& data, size_t dataSize, Tex* t) Status Tex::decode(const shared_ptr<u8>& Data, size_t DataSize)
{ {
const TexCodecVTbl* c; const TexCodecVTbl* c;
RETURN_STATUS_IF_ERR(tex_codec_for_header(data.get(), dataSize, &c)); RETURN_STATUS_IF_ERR(tex_codec_for_header(Data.get(), DataSize, &c));
// make sure the entire header is available // make sure the entire header is available
const size_t min_hdr_size = c->hdr_size(0); const size_t min_hdr_size = c->hdr_size(0);
if(dataSize < min_hdr_size) if(DataSize < min_hdr_size)
WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER); WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);
const size_t hdr_size = c->hdr_size(data.get()); const size_t hdr_size = c->hdr_size(Data.get());
if(dataSize < hdr_size) if(DataSize < hdr_size)
WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER); WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER);
t->data = data; m_Data = Data;
t->dataSize = dataSize; m_DataSize = DataSize;
t->ofs = hdr_size; m_Ofs = hdr_size;
RETURN_STATUS_IF_ERR(c->decode((rpU8)data.get(), dataSize, t)); RETURN_STATUS_IF_ERR(c->decode((rpU8)Data.get(), DataSize, this));
// sanity checks // sanity checks
if(!t->w || !t->h || t->bpp > 32) if(!m_Width || !m_Height || m_Bpp > 32)
WARN_RETURN(ERR::TEX_FMT_INVALID); WARN_RETURN(ERR::TEX_FMT_INVALID);
if(t->dataSize < t->ofs + tex_img_size(t)) if(m_DataSize < m_Ofs + img_size())
WARN_RETURN(ERR::TEX_INVALID_SIZE); WARN_RETURN(ERR::TEX_INVALID_SIZE);
flip_to_global_orientation(t); flip_to_global_orientation(this);
CHECK_TEX(t); CHECK_TEX(this);
return INFO::OK; return INFO::OK;
} }
Status tex_encode(Tex* t, const OsPath& extension, DynArray* da) Status Tex::encode(const OsPath& extension, DynArray* da)
{ {
CHECK_TEX(t); CHECK_TEX(this);
WARN_RETURN_STATUS_IF_ERR(tex_validate_plain_format(t->bpp, t->flags)); WARN_RETURN_STATUS_IF_ERR(tex_validate_plain_format(m_Bpp, m_Flags));
// we could be clever here and avoid the extra alloc if our current // 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 // memory block ensued from the same kind of texture file. this is
// most likely the case if in_img == tex_get_data() + c->hdr_size(0). // most likely the case if in_img == tex_get_data() + c->hdr_size(0).
// this would make for zero-copy IO. // this would make for zero-copy IO.
const size_t max_out_size = tex_img_size(t)*4 + 256*KiB; const size_t max_out_size = img_size()*4 + 256*KiB;
RETURN_STATUS_IF_ERR(da_alloc(da, max_out_size)); RETURN_STATUS_IF_ERR(da_alloc(da, max_out_size));
const TexCodecVTbl* c; const TexCodecVTbl* c;
WARN_RETURN_STATUS_IF_ERR(tex_codec_for_filename(extension, &c)); WARN_RETURN_STATUS_IF_ERR(tex_codec_for_filename(extension, &c));
// encode into <da> // encode into <da>
Status err = c->encode(t, da); Status err = c->encode(this, da);
if(err < 0) if(err < 0)
{ {
(void)da_free(da); (void)da_free(da);

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2010 Wildfire Games /* Copyright (c) 2014 Wildfire Games
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@ -201,7 +201,6 @@ enum TexFlags
TEX_UNDEFINED_FLAGS = ~0x1FF TEX_UNDEFINED_FLAGS = ~0x1FF
}; };
/** /**
* stores all data describing an image. * stores all data describing an image.
* we try to minimize size, since this is stored in OglTex resources * we try to minimize size, since this is stored in OglTex resources
@ -214,9 +213,9 @@ struct Tex
* (which may occur when being loaded), this may be replaced with * (which may occur when being loaded), this may be replaced with
* a new buffer (e.g. if decompressing file contents). * a new buffer (e.g. if decompressing file contents).
**/ **/
shared_ptr<u8> data; shared_ptr<u8> m_Data;
size_t dataSize; size_t m_DataSize;
/** /**
* offset to image data in file. this is required since * offset to image data in file. this is required since
@ -224,26 +223,141 @@ struct Tex
* returns the actual file buffer. zero-copy load and * returns the actual file buffer. zero-copy load and
* write-back to file is also made possible. * write-back to file is also made possible.
**/ **/
size_t ofs; size_t m_Ofs;
size_t w; size_t m_Width;
size_t h; size_t m_Height;
size_t bpp; size_t m_Bpp;
/// see TexFlags and "Format Conversion" in docs. /// see TexFlags and "Format Conversion" in docs.
size_t flags; size_t m_Flags;
~Tex()
{
free();
}
/**
* Is the texture object valid and self-consistent?
*
* @return Status
**/
Status validate() const;
/**
* free all resources associated with the image and make further
* use of it impossible.
*
* @return Status
**/
void free();
/**
* decode an in-memory texture file into texture object.
*
* FYI, currently BMP, TGA, JPG, JP2, PNG, DDS are supported - but don't
* rely on this (not all codecs may be included).
*
* @param data Input data.
* @param data_size Its size [bytes].
* @return Status.
**/
Status decode(const shared_ptr<u8>& data, size_t data_size);
/**
* encode a texture into a memory buffer in the desired file format.
*
* @param extension (including '.').
* @param da Output memory array. Allocated here; caller must free it
* when no longer needed. Invalid unless function succeeds.
* @return Status
**/
Status encode(const OsPath& extension, DynArray* da);
/**
* store the given image data into a Tex object; this will be as if
* it had been loaded via tex_load.
*
* 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.
* note: since we do not know how \<img\> was allocated, the caller must free
* it themselves (after calling tex_free, which is required regardless of
* alloc type).
*
* we need only add bookkeeping information and "wrap" it in
* our Tex struct, hence the name.
*
* @param w,h Pixel dimensions.
* @param bpp Bits per pixel.
* @param flags TexFlags.
* @param data Img texture data. note: size is calculated from other params.
* @param ofs
* @return Status
**/
Status wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr<u8>& data, size_t ofs);
//
// modify image
//
/**
* Change \<t\>'s pixel format.
*
* @param transforms TexFlags that are to be flipped.
* @return Status
**/
Status transform(size_t transforms);
/**
* Change \<t\>'s pixel format (2nd version)
* (note: this is equivalent to tex_transform(t, t-\>flags^new_flags).
*
* @param new_flags desired new value of TexFlags.
* @return Status
**/
Status transform_to(size_t new_flags);
//
// return image information
//
/**
* rationale: since Tex is a struct, its fields are accessible to callers.
* this is more for C compatibility than convenience; the following should
* be used instead of direct access to the corresponding fields because
* they take care of some dirty work.
**/
/**
* return a pointer to the image data (pixels), taking into account any
* header(s) that may come before it.
*
* @return pointer to data returned by mem_get_ptr (holds reference)!
**/
u8* get_data();
/**
* return the ARGB value of the 1x1 mipmap level of the texture.
*
* @return ARGB value (or 0 if texture does not have mipmaps)
**/
u32 get_average_colour() const;
/**
* return total byte size of the image pixels. (including mipmaps!)
* rationale: this is preferable to calculating manually because it's
* less error-prone (e.g. confusing bits_per_pixel with bytes).
*
* @return size [bytes]
**/
size_t img_size() const;
}; };
/**
* Is the texture object valid and self-consistent?
*
* @param t
* @return Status
**/
extern Status tex_validate(const Tex* t);
/** /**
* Set the orientation to which all loaded images will * Set the orientation to which all loaded images will
* automatically be converted (excepting file formats that don't specify * automatically be converted (excepting file formats that don't specify
@ -269,129 +383,6 @@ extern void tex_codec_register_all();
**/ **/
extern void tex_codec_unregister_all(); extern void tex_codec_unregister_all();
/**
* decode an in-memory texture file into texture object.
*
* FYI, currently BMP, TGA, JPG, JP2, PNG, DDS are supported - but don't
* rely on this (not all codecs may be included).
*
* @param data Input data.
* @param data_size Its size [bytes].
* @param t Output texture object.
* @return Status.
**/
extern Status tex_decode(const shared_ptr<u8>& data, size_t data_size, Tex* t);
/**
* encode a texture into a memory buffer in the desired file format.
*
* @param t Input texture object.
* @param extension (including '.').
* @param da Output memory array. Allocated here; caller must free it
* when no longer needed. Invalid unless function succeeds.
* @return Status
**/
extern Status tex_encode(Tex* t, const OsPath& extension, DynArray* da);
/**
* store the given image data into a Tex object; this will be as if
* it had been loaded via tex_load.
*
* 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.
* note: since we do not know how \<img\> was allocated, the caller must free
* it themselves (after calling tex_free, which is required regardless of
* alloc type).
*
* we need only add bookkeeping information and "wrap" it in
* our Tex struct, hence the name.
*
* @param w,h Pixel dimensions.
* @param bpp Bits per pixel.
* @param flags TexFlags.
* @param data Img texture data. note: size is calculated from other params.
* @param ofs
* @param t output texture object.
* @return Status
**/
extern Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr<u8>& data, size_t ofs, Tex* t);
/**
* free all resources associated with the image and make further
* use of it impossible.
*
* @param t texture object (note: not zeroed afterwards; see impl)
* @return Status
**/
extern void tex_free(Tex* t);
//
// modify image
//
/**
* Change \<t\>'s pixel format.
*
* @param t Input texture object.
* @param transforms TexFlags that are to be flipped.
* @return Status
**/
extern Status tex_transform(Tex* t, size_t transforms);
/**
* Change \<t\>'s pixel format (2nd version)
* (note: this is equivalent to tex_transform(t, t-\>flags^new_flags).
*
* @param t Input texture object.
* @param new_flags desired new value of TexFlags.
* @return Status
**/
extern Status tex_transform_to(Tex* t, size_t new_flags);
//
// return image information
//
/**
* rationale: since Tex is a struct, its fields are accessible to callers.
* this is more for C compatibility than convenience; the following should
* be used instead of direct access to the corresponding fields because
* they take care of some dirty work.
**/
/**
* return a pointer to the image data (pixels), taking into account any
* header(s) that may come before it.
*
* @param t input texture object
* @return pointer to data returned by mem_get_ptr (holds reference)!
**/
extern u8* tex_get_data(const Tex* t);
/**
* return the ARGB value of the 1x1 mipmap level of the texture.
*
* @param t input texture object
* @return ARGB value (or 0 if texture does not have mipmaps)
**/
extern u32 tex_get_average_colour(const Tex* t);
/**
* return total byte size of the image pixels. (including mipmaps!)
* rationale: this is preferable to calculating manually because it's
* less error-prone (e.g. confusing bits_per_pixel with bytes).
*
* @param t input texture object
* @return size [bytes]
**/
extern size_t tex_img_size(const Tex* t);
/** /**
* special value for levels_to_skip: the callback will only be called * special value for levels_to_skip: the callback will only be called
* for the base mipmap level (i.e. 100%) * for the base mipmap level (i.e. 100%)
@ -464,13 +455,4 @@ extern bool tex_is_known_extension(const VfsPath& pathname);
**/ **/
extern size_t tex_hdr_size(const VfsPath& filename); extern size_t tex_hdr_size(const VfsPath& filename);
// RAII wrapper
struct ScopedTex : public Tex
{
~ScopedTex()
{
tex_free(this);
}
};
#endif // INCLUDED_TEX #endif // INCLUDED_TEX

View File

@ -116,10 +116,10 @@ static Status bmp_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t)
if(compress != BI_RGB) if(compress != BI_RGB)
WARN_RETURN(ERR::TEX_COMPRESSED); WARN_RETURN(ERR::TEX_COMPRESSED);
t->w = w; t->m_Width = w;
t->h = h; t->m_Height = h;
t->bpp = bpp; t->m_Bpp = bpp;
t->flags = flags; t->m_Flags = flags;
return INFO::OK; return INFO::OK;
} }
@ -127,11 +127,11 @@ static Status bmp_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t)
static Status bmp_encode(Tex* RESTRICT t, DynArray* RESTRICT da) static Status bmp_encode(Tex* RESTRICT t, DynArray* RESTRICT da)
{ {
const size_t hdr_size = sizeof(BmpHeader); // needed for BITMAPFILEHEADER const size_t hdr_size = sizeof(BmpHeader); // needed for BITMAPFILEHEADER
const size_t img_size = tex_img_size(t); const size_t img_size = t->img_size();
const size_t file_size = hdr_size + img_size; const size_t file_size = hdr_size + img_size;
const i32 h = (t->flags & TEX_TOP_DOWN)? -(i32)t->h : (i32)t->h; const i32 h = (t->m_Flags & TEX_TOP_DOWN)? -(i32)t->m_Height : (i32)t->m_Height;
size_t transforms = t->flags; size_t transforms = t->m_Flags;
transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit. transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit.
transforms ^= TEX_BGR; // BMP is native BGR. transforms ^= TEX_BGR; // BMP is native BGR.
@ -145,10 +145,10 @@ static Status bmp_encode(Tex* RESTRICT t, DynArray* RESTRICT da)
// BITMAPINFOHEADER // BITMAPINFOHEADER
40, // biSize = sizeof(BITMAPINFOHEADER) 40, // biSize = sizeof(BITMAPINFOHEADER)
(i32)t->w, (i32)t->m_Width,
h, h,
1, // biPlanes 1, // biPlanes
(u16)t->bpp, (u16)t->m_Bpp,
BI_RGB, // biCompression BI_RGB, // biCompression
(u32)img_size, // biSizeImage (u32)img_size, // biSizeImage
0, 0, 0, 0 // unused (bi?PelsPerMeter, biClr*) 0, 0, 0, 0 // unused (bi?PelsPerMeter, biClr*)

View File

@ -175,9 +175,9 @@ std::vector<RowPtr> tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
Status tex_codec_write(Tex* t, size_t transforms, const void* hdr, size_t hdr_size, DynArray* da) Status tex_codec_write(Tex* t, size_t transforms, const void* hdr, size_t hdr_size, DynArray* da)
{ {
RETURN_STATUS_IF_ERR(tex_transform(t, transforms)); RETURN_STATUS_IF_ERR(t->transform(transforms));
void* img_data = tex_get_data(t); const size_t img_size = tex_img_size(t); void* img_data = t->get_data(); const size_t img_size = t->img_size();
RETURN_STATUS_IF_ERR(da_append(da, hdr, hdr_size)); RETURN_STATUS_IF_ERR(da_append(da, hdr, hdr_size));
RETURN_STATUS_IF_ERR(da_append(da, img_data, img_size)); RETURN_STATUS_IF_ERR(da_append(da, img_data, img_size));
return INFO::OK; return INFO::OK;

View File

@ -279,22 +279,22 @@ static Status s3tc_decompress(Tex* t)
// - adding or stripping alpha channels during transform is not // - adding or stripping alpha channels during transform is not
// our job; we merely output the same pixel format as given // our job; we merely output the same pixel format as given
// (tex.cpp's plain transform could cover it, if ever needed). // (tex.cpp's plain transform could cover it, if ever needed).
const size_t dxt = t->flags & TEX_DXT; const size_t dxt = t->m_Flags & TEX_DXT;
const size_t out_bpp = (dxt != 1)? 32 : 24; const size_t out_bpp = (dxt != 1)? 32 : 24;
const size_t out_size = tex_img_size(t) * out_bpp / t->bpp; const size_t out_size = t->img_size() * out_bpp / t->m_Bpp;
shared_ptr<u8> decompressedData; shared_ptr<u8> decompressedData;
AllocateAligned(decompressedData, out_size, pageSize); AllocateAligned(decompressedData, out_size, pageSize);
const size_t s3tc_block_size = (dxt == 3 || dxt == 5)? 16 : 8; const size_t s3tc_block_size = (dxt == 3 || dxt == 5)? 16 : 8;
S3tcDecompressInfo di = { dxt, s3tc_block_size, out_bpp/8, decompressedData.get() }; S3tcDecompressInfo di = { dxt, s3tc_block_size, out_bpp/8, decompressedData.get() };
const u8* s3tc_data = tex_get_data(t); const u8* s3tc_data = t->get_data();
const int levels_to_skip = (t->flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY; const int levels_to_skip = (t->m_Flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY;
tex_util_foreach_mipmap(t->w, t->h, t->bpp, s3tc_data, levels_to_skip, 4, s3tc_decompress_level, &di); tex_util_foreach_mipmap(t->m_Width, t->m_Height, t->m_Bpp, s3tc_data, levels_to_skip, 4, s3tc_decompress_level, &di);
t->data = decompressedData; t->m_Data = decompressedData;
t->dataSize = out_size; t->m_DataSize = out_size;
t->ofs = 0; t->m_Ofs = 0;
t->bpp = out_bpp; t->m_Bpp = out_bpp;
t->flags &= ~TEX_DXT; t->m_Flags &= ~TEX_DXT;
return INFO::OK; return INFO::OK;
} }
@ -607,7 +607,7 @@ static size_t dds_hdr_size(const u8* UNUSED(file))
static Status dds_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t) static Status dds_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t)
{ {
const DDS_HEADER* sd = (const DDS_HEADER*)(data+4); const DDS_HEADER* sd = (const DDS_HEADER*)(data+4);
RETURN_STATUS_IF_ERR(decode_sd(sd, t->w, t->h, t->bpp, t->flags)); RETURN_STATUS_IF_ERR(decode_sd(sd, t->m_Width, t->m_Height, t->m_Bpp, t->m_Flags));
return INFO::OK; return INFO::OK;
} }
@ -626,8 +626,8 @@ static Status dds_transform(Tex* t, size_t transforms)
{ {
TIMER_ACCRUE(tc_dds_transform); TIMER_ACCRUE(tc_dds_transform);
size_t mipmaps = t->flags & TEX_MIPMAPS; size_t mipmaps = t->m_Flags & TEX_MIPMAPS;
size_t dxt = t->flags & TEX_DXT; size_t dxt = t->m_Flags & TEX_DXT;
ENSURE(is_valid_dxt(dxt)); ENSURE(is_valid_dxt(dxt));
const size_t transform_mipmaps = transforms & TEX_MIPMAPS; const size_t transform_mipmaps = transforms & TEX_MIPMAPS;
@ -637,7 +637,7 @@ static Status dds_transform(Tex* t, size_t transforms)
{ {
// we don't need to actually change anything except the flag - the // we don't need to actually change anything except the flag - the
// mipmap levels will just be treated as trailing junk // mipmap levels will just be treated as trailing junk
t->flags &= ~TEX_MIPMAPS; t->m_Flags &= ~TEX_MIPMAPS;
return INFO::OK; return INFO::OK;
} }
// requesting decompression // requesting decompression

View File

@ -503,16 +503,8 @@ static Status jpg_decode_impl(rpU8 data, size_t size, jpeg_decompress_struct* ci
if(cinfo->err->num_warnings != 0) if(cinfo->err->num_warnings != 0)
ret = WARN::TEX_INVALID_DATA; ret = WARN::TEX_INVALID_DATA;
// store image info // store image info and validate
t->data = img; return ret | t->wrap(w,h,bpp,flags,img,0);
t->dataSize = imgSize;
t->ofs = 0;
t->w = w;
t->h = h;
t->bpp = bpp;
t->flags = flags;
return ret;
} }
@ -522,10 +514,10 @@ static Status jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, DynArray* da)
// describe image format // describe image format
// required: // required:
cinfo->image_width = (JDIMENSION)t->w; cinfo->image_width = (JDIMENSION)t->m_Width;
cinfo->image_height = (JDIMENSION)t->h; cinfo->image_height = (JDIMENSION)t->m_Height;
cinfo->input_components = (int)t->bpp / 8; cinfo->input_components = (int)t->m_Bpp / 8;
cinfo->in_color_space = (t->bpp == 8)? JCS_GRAYSCALE : JCS_RGB; cinfo->in_color_space = (t->m_Bpp == 8)? JCS_GRAYSCALE : JCS_RGB;
// defaults depend on cinfo->in_color_space already having been set! // defaults depend on cinfo->in_color_space already having been set!
jpeg_set_defaults(cinfo); jpeg_set_defaults(cinfo);
// (add optional settings, e.g. quality, here) // (add optional settings, e.g. quality, here)
@ -535,16 +527,16 @@ static Status jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, DynArray* da)
jpeg_start_compress(cinfo, TRUE); jpeg_start_compress(cinfo, TRUE);
// if BGR, convert to RGB. // if BGR, convert to RGB.
WARN_IF_ERR(tex_transform_to(t, t->flags & ~TEX_BGR)); WARN_IF_ERR(t->transform_to(t->m_Flags & ~TEX_BGR));
const size_t pitch = t->w * t->bpp / 8; const size_t pitch = t->m_Width * t->m_Bpp / 8;
u8* data = tex_get_data(t); u8* data = t->get_data();
std::vector<RowPtr> rows = tex_codec_alloc_rows(data, t->h, pitch, t->flags, TEX_TOP_DOWN); std::vector<RowPtr> rows = tex_codec_alloc_rows(data, t->m_Height, pitch, t->m_Flags, TEX_TOP_DOWN);
// could use cinfo->output_scanline to keep track of progress, // could use cinfo->output_scanline to keep track of progress,
// but we need to count lines_left anyway (paranoia). // but we need to count lines_left anyway (paranoia).
JSAMPARRAY row = (JSAMPARRAY)&rows[0]; JSAMPARRAY row = (JSAMPARRAY)&rows[0];
JDIMENSION lines_left = (JDIMENSION)t->h; JDIMENSION lines_left = (JDIMENSION)t->m_Height;
while(lines_left != 0) while(lines_left != 0)
{ {
JDIMENSION lines_read = jpeg_write_scanlines(cinfo, row, lines_left); JDIMENSION lines_read = jpeg_write_scanlines(cinfo, row, lines_left);

View File

@ -153,16 +153,8 @@ static Status png_decode_impl(MemoryStream* stream, png_structp png_ptr, png_inf
// success; make sure all data was consumed. // success; make sure all data was consumed.
ENSURE(stream->RemainingSize() == 0); ENSURE(stream->RemainingSize() == 0);
// store image info // store image info and validate
t->data = data; return t->wrap(w,h,bpp,flags,data,0);
t->dataSize = img_size;
t->ofs = 0;
t->w = w;
t->h = h;
t->bpp = bpp;
t->flags = flags;
return INFO::OK;
} }
@ -170,11 +162,11 @@ static Status png_decode_impl(MemoryStream* stream, png_structp png_ptr, png_inf
// "dtor / setjmp interaction" warning. // "dtor / setjmp interaction" warning.
static Status png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, DynArray* da) static Status png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, DynArray* da)
{ {
const png_uint_32 w = (png_uint_32)t->w, h = (png_uint_32)t->h; const png_uint_32 w = (png_uint_32)t->m_Width, h = (png_uint_32)t->m_Height;
const size_t pitch = w * t->bpp / 8; const size_t pitch = w * t->m_Bpp / 8;
int colour_type; int colour_type;
switch(t->flags & (TEX_GREY|TEX_ALPHA)) switch(t->m_Flags & (TEX_GREY|TEX_ALPHA))
{ {
case TEX_GREY|TEX_ALPHA: case TEX_GREY|TEX_ALPHA:
colour_type = PNG_COLOR_TYPE_GRAY_ALPHA; colour_type = PNG_COLOR_TYPE_GRAY_ALPHA;
@ -194,11 +186,11 @@ static Status png_encode_impl(Tex* t, png_structp png_ptr, png_infop info_ptr, D
png_set_IHDR(png_ptr, info_ptr, w, h, 8, colour_type, png_set_IHDR(png_ptr, info_ptr, w, h, 8, colour_type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
u8* data = tex_get_data(t); u8* data = t->get_data();
std::vector<RowPtr> rows = tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN); std::vector<RowPtr> rows = tex_codec_alloc_rows(data, h, pitch, t->m_Flags, TEX_TOP_DOWN);
// PNG is native RGB. // PNG is native RGB.
const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY; const int png_transforms = (t->m_Flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
png_set_rows(png_ptr, info_ptr, (png_bytepp)&rows[0]); png_set_rows(png_ptr, info_ptr, (png_bytepp)&rows[0]);
png_write_png(png_ptr, info_ptr, png_transforms, 0); png_write_png(png_ptr, info_ptr, png_transforms, 0);

View File

@ -136,10 +136,10 @@ static Status tga_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t)
if(desc & TGA_RIGHT_TO_LEFT) if(desc & TGA_RIGHT_TO_LEFT)
WARN_RETURN(ERR::TEX_INVALID_LAYOUT); WARN_RETURN(ERR::TEX_INVALID_LAYOUT);
t->w = w; t->m_Width = w;
t->h = h; t->m_Height = h;
t->bpp = bpp; t->m_Bpp = bpp;
t->flags = flags; t->m_Flags = flags;
return INFO::OK; return INFO::OK;
} }
@ -147,13 +147,13 @@ static Status tga_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t)
static Status tga_encode(Tex* RESTRICT t, DynArray* RESTRICT da) static Status tga_encode(Tex* RESTRICT t, DynArray* RESTRICT da)
{ {
u8 img_desc = 0; u8 img_desc = 0;
if(t->flags & TEX_TOP_DOWN) if(t->m_Flags & TEX_TOP_DOWN)
img_desc |= TGA_TOP_DOWN; img_desc |= TGA_TOP_DOWN;
if(t->bpp == 32) if(t->m_Bpp == 32)
img_desc |= 8; // size of alpha channel img_desc |= 8; // size of alpha channel
TgaImgType img_type = (t->flags & TEX_GREY)? TGA_GREY : TGA_TRUE_COLOUR; TgaImgType img_type = (t->m_Flags & TEX_GREY)? TGA_GREY : TGA_TRUE_COLOUR;
size_t transforms = t->flags; size_t transforms = t->m_Flags;
transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit. transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit.
transforms ^= TEX_BGR; // TGA is native BGR. transforms ^= TEX_BGR; // TGA is native BGR.
@ -164,9 +164,9 @@ static Status tga_encode(Tex* RESTRICT t, DynArray* RESTRICT da)
(u8)img_type, (u8)img_type,
{0,0,0,0,0}, // unused (colour map) {0,0,0,0,0}, // unused (colour map)
0, 0, // unused (origin) 0, 0, // unused (origin)
(u16)t->w, (u16)t->m_Width,
(u16)t->h, (u16)t->m_Height,
(u8)t->bpp, (u8)t->m_Bpp,
img_desc img_desc
}; };
const size_t hdr_size = sizeof(hdr); const size_t hdr_size = sizeof(hdr);

View File

@ -173,7 +173,7 @@ const wchar_t* ErrorString(int err)
Status tex_write(Tex* t, const VfsPath& filename) Status tex_write(Tex* t, const VfsPath& filename)
{ {
DynArray da; DynArray da;
RETURN_STATUS_IF_ERR(tex_encode(t, filename.Extension(), &da)); RETURN_STATUS_IF_ERR(t->encode(filename.Extension(), &da));
// write to disk // write to disk
Status ret = INFO::OK; Status ret = INFO::OK;
@ -230,7 +230,7 @@ void WriteScreenshot(const VfsPath& extension)
AllocateAligned(buf, hdr_size+img_size, maxSectorSize); AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
GLvoid* img = buf.get() + hdr_size; GLvoid* img = buf.get() + hdr_size;
Tex t; Tex t;
if(tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0) if(t.wrap(w, h, bpp, flags, buf, hdr_size) < 0)
return; return;
glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img); glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img);
@ -242,8 +242,6 @@ void WriteScreenshot(const VfsPath& extension)
} }
else else
LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str()); LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str());
tex_free(&t);
} }
@ -294,7 +292,7 @@ void WriteBigScreenshot(const VfsPath& extension, int tiles)
Tex t; Tex t;
GLvoid* img = img_buf.get() + hdr_size; GLvoid* img = img_buf.get() + hdr_size;
if(tex_wrap(img_w, img_h, bpp, flags, img_buf, hdr_size, &t) < 0) if(t.wrap(img_w, img_h, bpp, flags, img_buf, hdr_size) < 0)
{ {
free(tile_data); free(tile_data);
return; return;
@ -378,6 +376,5 @@ void WriteBigScreenshot(const VfsPath& extension, int tiles)
else else
LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str()); LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str());
tex_free(&t);
free(tile_data); free(tile_data);
} }

View File

@ -1929,7 +1929,7 @@ int CRenderer::LoadAlphaMaps()
// upload the composite texture // upload the composite texture
Tex t; Tex t;
(void)tex_wrap(total_w, total_h, 8, TEX_GREY, data, 0, &t); (void)t.wrap(total_w, total_h, 8, TEX_GREY, data, 0);
/*VfsPath filename("blendtex.png"); /*VfsPath filename("blendtex.png");

View File

@ -132,37 +132,35 @@ void SkyManager::LoadSkyTextures()
} }
Tex tex; Tex tex;
tex_decode(file, fileSize, &tex); tex.decode(file, fileSize);
tex_transform_to(&tex, (tex.flags | TEX_BOTTOM_UP | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)); tex.transform_to((tex.m_Flags | TEX_BOTTOM_UP | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS));
u8* data = tex_get_data(&tex); u8* data = tex.get_data();
if (types[i] == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || types[i] == GL_TEXTURE_CUBE_MAP_POSITIVE_Y) if (types[i] == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || types[i] == GL_TEXTURE_CUBE_MAP_POSITIVE_Y)
{ {
std::vector<u8> rotated(tex.dataSize); std::vector<u8> rotated(tex.m_DataSize);
for (size_t y = 0; y < tex.h; ++y) for (size_t y = 0; y < tex.m_Height; ++y)
{ {
for (size_t x = 0; x < tex.w; ++x) for (size_t x = 0; x < tex.m_Width; ++x)
{ {
size_t invx = y, invy = tex.w-x-1; size_t invx = y, invy = tex.m_Width-x-1;
rotated[(y*tex.w + x) * 4 + 0] = data[(invy*tex.w + invx) * 4 + 0]; rotated[(y*tex.m_Width + x) * 4 + 0] = data[(invy*tex.m_Width + invx) * 4 + 0];
rotated[(y*tex.w + x) * 4 + 1] = data[(invy*tex.w + invx) * 4 + 1]; rotated[(y*tex.m_Width + x) * 4 + 1] = data[(invy*tex.m_Width + invx) * 4 + 1];
rotated[(y*tex.w + x) * 4 + 2] = data[(invy*tex.w + invx) * 4 + 2]; rotated[(y*tex.m_Width + x) * 4 + 2] = data[(invy*tex.m_Width + invx) * 4 + 2];
rotated[(y*tex.w + x) * 4 + 3] = data[(invy*tex.w + invx) * 4 + 3]; rotated[(y*tex.m_Width + x) * 4 + 3] = data[(invy*tex.m_Width + invx) * 4 + 3];
} }
} }
glTexImage2D(types[i], 0, GL_RGB, tex.w, tex.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &rotated[0]); glTexImage2D(types[i], 0, GL_RGB, tex.m_Width, tex.m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &rotated[0]);
} }
else else
{ {
glTexImage2D(types[i], 0, GL_RGB, tex.w, tex.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexImage2D(types[i], 0, GL_RGB, tex.m_Width, tex.m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
} }
tex_free(&tex);
} }
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

View File

@ -336,7 +336,7 @@ public:
shared_ptr<u8> buf; shared_ptr<u8> buf;
AllocateAligned(buf, hdr_size+img_size, maxSectorSize); AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
Tex t; Tex t;
if (tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0) if (t.wrap(w, h, bpp, flags, buf, hdr_size) < 0)
return; return;
u8* img = buf.get() + hdr_size; u8* img = buf.get() + hdr_size;
@ -344,7 +344,6 @@ public:
img[i] = (u8)((data[i] * 255) / max); img[i] = (u8)((data[i] * 255) / max);
tex_write(&t, filename); tex_write(&t, filename);
tex_free(&t);
} }
bool TryLoadSharedComponent(bool hasTechs) bool TryLoadSharedComponent(bool hasTechs)

View File

@ -172,22 +172,21 @@ MESSAGEHANDLER(ImportHeightmap)
// decode to a raw pixel format // decode to a raw pixel format
Tex tex; Tex tex;
if (tex_decode(fileData, fileSize, &tex) < 0) if (tex.decode(fileData, fileSize) < 0)
{ {
LOGERROR(L"Failed to decode heightmap."); LOGERROR(L"Failed to decode heightmap.");
return; return;
} }
// Convert to uncompressed BGRA with no mipmaps // Convert to uncompressed BGRA with no mipmaps
if (tex_transform_to(&tex, (tex.flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) < 0) if (tex.transform_to((tex.m_Flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) < 0)
{ {
LOGERROR(L"Failed to transform heightmap."); LOGERROR(L"Failed to transform heightmap.");
tex_free(&tex);
return; return;
} }
// pick smallest side of texture; truncate if not divisible by PATCH_SIZE // pick smallest side of texture; truncate if not divisible by PATCH_SIZE
ssize_t terrainSize = std::min(tex.w, tex.h); ssize_t terrainSize = std::min(tex.m_Width, tex.m_Height);
terrainSize -= terrainSize % PATCH_SIZE; terrainSize -= terrainSize % PATCH_SIZE;
// resize terrain to heightmap size // resize terrain to heightmap size
@ -198,9 +197,9 @@ MESSAGEHANDLER(ImportHeightmap)
u16* heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap(); u16* heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap();
ssize_t hmSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); ssize_t hmSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
u8* mapdata = tex_get_data(&tex); u8* mapdata = tex.get_data();
ssize_t bytesPP = tex.bpp / 8; ssize_t bytesPP = tex.m_Bpp / 8;
ssize_t mapLineSkip = tex.w * bytesPP; ssize_t mapLineSkip = tex.m_Width * bytesPP;
for (ssize_t y = 0; y < terrainSize; ++y) for (ssize_t y = 0; y < terrainSize; ++y)
{ {
@ -215,8 +214,6 @@ MESSAGEHANDLER(ImportHeightmap)
} }
} }
tex_free(&tex);
// update simulation // update simulation
CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY); CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
if (cmpTerrain) cmpTerrain->ReloadTerrain(); if (cmpTerrain) cmpTerrain->ReloadTerrain();