From a5639631eefd180093cd14339dd428056633f963 Mon Sep 17 00:00:00 2001 From: JoshuaJB Date: Thu, 13 Mar 2014 02:37:05 +0000 Subject: [PATCH] Texture system refactoring and cleanup, fixes #2455, patch by IronNerd. This was SVN commit r14835. --- source/graphics/HeightMipmap.cpp | 6 +- source/graphics/TerrainTextureEntry.cpp | 2 +- source/graphics/TextureConverter.cpp | 33 +- source/graphics/TextureManager.cpp | 4 +- source/graphics/tests/test_TextureConverter.h | 8 +- source/lib/res/graphics/cursor.cpp | 12 +- source/lib/res/graphics/ogl_tex.cpp | 55 ++-- source/lib/res/graphics/tests/test_tex.h | 58 ++-- source/lib/tex/tex.cpp | 180 +++++------ source/lib/tex/tex.h | 282 ++++++++---------- source/lib/tex/tex_bmp.cpp | 18 +- source/lib/tex/tex_codec.cpp | 4 +- source/lib/tex/tex_dds.cpp | 28 +- source/lib/tex/tex_jpg.cpp | 30 +- source/lib/tex/tex_png.cpp | 24 +- source/lib/tex/tex_tga.cpp | 22 +- source/ps/Util.cpp | 9 +- source/renderer/Renderer.cpp | 2 +- source/renderer/SkyManager.cpp | 30 +- .../simulation2/components/CCmpAIManager.cpp | 3 +- .../GameInterface/Handlers/MapHandlers.cpp | 15 +- 21 files changed, 387 insertions(+), 438 deletions(-) diff --git a/source/graphics/HeightMipmap.cpp b/source/graphics/HeightMipmap.cpp index 286af1b996..92b1da3909 100644 --- a/source/graphics/HeightMipmap.cpp +++ b/source/graphics/HeightMipmap.cpp @@ -232,7 +232,7 @@ void CHeightMipmap::DumpToDisk(const VfsPath& filename) const AllocateAligned(buf, hdr_size+img_size, maxSectorSize); void* img = buf.get() + hdr_size; 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); size_t yoff = 0; @@ -253,9 +253,7 @@ void CHeightMipmap::DumpToDisk(const VfsPath& filename) const } 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); (void)da_free(&da); - - tex_free(&t); } diff --git a/source/graphics/TerrainTextureEntry.cpp b/source/graphics/TerrainTextureEntry.cpp index 18dab5fe49..7429dce27c 100644 --- a/source/graphics/TerrainTextureEntry.cpp +++ b/source/graphics/TerrainTextureEntry.cpp @@ -333,7 +333,7 @@ void CTerrainTextureEntry::LoadAlphaMaps(VfsPath &amtype) // upload the composite texture 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 // in the public/ directory, for debugging diff --git a/source/graphics/TextureConverter.cpp b/source/graphics/TextureConverter.cpp index 1620506be4..aadca03a90 100644 --- a/source/graphics/TextureConverter.cpp +++ b/source/graphics/TextureConverter.cpp @@ -331,22 +331,21 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath } 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()); return false; } // 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) { // 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()); - tex_free(&tex); return false; } } @@ -355,17 +354,16 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath // TODO: grayscale images will fail on some systems // see http://trac.wildfiregames.com/ticket/1640 // (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()); return false; } // 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()); - tex_free(&tex); return false; } } @@ -375,8 +373,8 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath if (hasAlpha) { hasAlpha = false; - u8* data = tex_get_data(&tex); - for (size_t i = 0; i < tex.w * tex.h; ++i) + u8* data = tex.get_data(); + for (size_t i = 0; i < tex.m_Width * tex.m_Height; ++i) { if (data[i*4+3] != 0xFF) { @@ -454,29 +452,26 @@ bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath // TODO: normal maps, gamma, etc // Load the texture data - request->inputOptions.setTextureLayout(nvtt::TextureType_2D, tex.w, tex.h); - if (tex.bpp == 32) + request->inputOptions.setTextureLayout(nvtt::TextureType_2D, tex.m_Width, tex.m_Height); + 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 { // NVTT requires 32-bit input data, so convert - const u8* input = tex_get_data(&tex); - u8* rgba = new u8[tex.w * tex.h * 4]; + const u8* input = tex.get_data(); + u8* rgba = new u8[tex.m_Width * tex.m_Height * 4]; 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 += 4; } - request->inputOptions.setMipmapData(rgba, tex.w, tex.h); + request->inputOptions.setMipmapData(rgba, tex.m_Width, tex.m_Height); delete[] rgba; } - // NVTT copies the texture data so we can free it now - tex_free(&tex); - pthread_mutex_lock(&m_WorkerMutex); m_RequestQueue.push_back(request); pthread_mutex_unlock(&m_WorkerMutex); diff --git a/source/graphics/TextureManager.cpp b/source/graphics/TextureManager.cpp index 64440f6ee9..bfccc8ba95 100644 --- a/source/graphics/TextureManager.cpp +++ b/source/graphics/TextureManager.cpp @@ -94,7 +94,7 @@ public: data.get()[1] = 64; data.get()[2] = 64; 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)"); (void)ogl_tex_set_filter(m_DefaultHandle, GL_LINEAR); @@ -111,7 +111,7 @@ public: data.get()[1] = 0; data.get()[2] = 255; 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)"); (void)ogl_tex_set_filter(m_ErrorHandle, GL_LINEAR); diff --git a/source/graphics/tests/test_TextureConverter.h b/source/graphics/tests/test_TextureConverter.h index 5c20d79329..28f2f9e225 100644 --- a/source/graphics/tests/test_TextureConverter.h +++ b/source/graphics/tests/test_TextureConverter.h @@ -78,11 +78,11 @@ public: TS_ASSERT_OK(m_VFS->LoadFile(dest, file, fileSize)); 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 // should be identical after 4 pixels @@ -97,7 +97,5 @@ public: // if (i % 4 == 0) printf("\n"); // printf("%02x ", texdata[i]); // } - - tex_free(&tex); } }; diff --git a/source/lib/res/graphics/cursor.cpp b/source/lib/res/graphics/cursor.cpp index 167e0ea967..970164c862 100644 --- a/source/lib/res/graphics/cursor.cpp +++ b/source/lib/res/graphics/cursor.cpp @@ -60,17 +60,17 @@ static Status load_sys_cursor(const PIVFS& vfs, const VfsPath& pathname, int hx, shared_ptr file; size_t fileSize; RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize)); - ScopedTex t; - RETURN_STATUS_IF_ERR(tex_decode(file, fileSize, &t)); + Tex t; + RETURN_STATUS_IF_ERR(t.decode(file, fileSize)); // convert to required BGRA format. - const size_t flags = (t.flags | TEX_BGR) & ~TEX_DXT; - RETURN_STATUS_IF_ERR(tex_transform_to(&t, flags)); - void* bgra_img = tex_get_data(&t); + const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT; + RETURN_STATUS_IF_ERR(t.transform_to(flags)); + void* bgra_img = t.get_data(); if(!bgra_img) 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; #endif } diff --git a/source/lib/res/graphics/ogl_tex.cpp b/source/lib/res/graphics/ogl_tex.cpp index 623e330d74..f1a7d4019a 100644 --- a/source/lib/res/graphics/ogl_tex.cpp +++ b/source/lib/res/graphics/ogl_tex.cpp @@ -449,7 +449,7 @@ static void OglTex_dtor(OglTex* ot) { if(ot->flags & OT_TEX_VALID) { - tex_free(&ot->t); + ot->t.free(); ot->flags &= ~OT_TEX_VALID; } @@ -473,7 +473,7 @@ static Status OglTex_reload(OglTex* ot, const PIVFS& vfs, const VfsPath& pathnam { shared_ptr file; size_t 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; } @@ -491,13 +491,13 @@ static Status OglTex_validate(const OglTex* ot) { if(ot->flags & OT_TEX_VALID) { - RETURN_STATUS_IF_ERR(tex_validate(&ot->t)); + RETURN_STATUS_IF_ERR(ot->t.validate()); // width, height // (note: this is done here because tex.cpp doesn't impose any // restrictions on dimensions, while OpenGL does). - size_t w = ot->t.w; - size_t h = ot->t.h; + size_t w = ot->t.m_Width; + size_t h = ot->t.m_Height; // .. == 0; texture file probably not loaded successfully. if(w == 0 || h == 0) WARN_RETURN(ERR::_11); @@ -794,12 +794,12 @@ static Status get_mipmaps(Tex* t, GLint filter, int q_flags, int* plevels_to_ski { // decisions: // .. 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 // 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") - 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; 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. 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 } @@ -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 // higher mipmap levels. NB: the minimum guaranteed size is // 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) { (*plevels_to_skip)++; @@ -893,13 +893,13 @@ static void upload_compressed_level(size_t level, size_t level_w, size_t level_h // pre: 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) { - const GLsizei w = (GLsizei)t->w; - const GLsizei h = (GLsizei)t->h; - const size_t bpp = t->bpp; - const u8* data = (const u8*)tex_get_data(t); + const GLsizei w = (GLsizei)t->m_Width; + const GLsizei h = (GLsizei)t->m_Height; + const size_t bpp = t->m_Bpp; + const u8* data = (const u8*)t->get_data(); 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); else 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) { // decompress S3TC if that's not supported by OpenGL. - if((t->flags & TEX_DXT) && !have_s3tc) - (void)tex_transform_to(t, t->flags & ~TEX_DXT); + if((t->m_Flags & TEX_DXT) && !have_s3tc) + (void)t->transform_to(t->m_Flags & ~TEX_DXT); // 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(q_flags_ovr) ot->q_flags = q_flags_ovr; 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; // see rationale for 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); if(refs == 1) { // note: we verify above that OT_TEX_VALID is set - tex_free(t); 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); if(w) - *w = ot->t.w; + *w = ot->t.m_Width; if(h) - *h = ot->t.h; + *h = ot->t.m_Height; if(bpp) - *bpp = ot->t.bpp; + *bpp = ot->t.m_Bpp; return INFO::OK; } @@ -1009,7 +1006,7 @@ Status ogl_tex_get_format(Handle ht, size_t* flags, GLenum* fmt) H_DEREF(ht, OglTex, ot); if(flags) - *flags = ot->t.flags; + *flags = ot->t.m_Flags; if(fmt) { ENSURE(ot->flags & OT_IS_UPLOADED); @@ -1030,7 +1027,7 @@ Status ogl_tex_get_data(Handle ht, u8** p) { H_DEREF(ht, OglTex, ot); - *p = tex_get_data(&ot->t); + *p = ot->t.get_data(); return INFO::OK; } @@ -1048,7 +1045,7 @@ extern Status ogl_tex_get_average_colour(Handle ht, u32* p) H_DEREF(ht, OglTex, ot); warn_if_uploaded(ht, ot); - *p = tex_get_average_colour(&ot->t); + *p = ot->t.get_average_colour(); 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) { H_DEREF(ht, OglTex, ot); - Status ret = tex_transform(&ot->t, transforms); + Status ret = ot->t.transform(transforms); 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) { H_DEREF(ht, OglTex, ot); - Status ret = tex_transform_to(&ot->t, new_flags); + Status ret = ot->t.transform_to(new_flags); return ret; } diff --git a/source/lib/res/graphics/tests/test_tex.h b/source/lib/res/graphics/tests/test_tex.h index ec8c6a1816..984ca13100 100644 --- a/source/lib/res/graphics/tests/test_tex.h +++ b/source/lib/res/graphics/tests/test_tex.h @@ -35,27 +35,31 @@ class TestTex : public CxxTest::TestSuite shared_ptr img(new u8[size], ArrayDeleter()); std::generate(img.get(), img.get()+size, rand); - // wrap in Tex - Tex t; - TS_ASSERT_OK(tex_wrap(w, h, bpp, flags, img, 0, &t)); - - // encode to file format + // create the DynArray that will be wrapped in a Tex Object DynArray da; - TS_ASSERT_OK(tex_encode(&t, extension, &da)); - memset(&t, 0, sizeof(t)); - // decode from file format - shared_ptr ptr = DummySharedPtr(da.base); - TS_ASSERT_OK(tex_decode(ptr, da.cur_size, &t)); + // Once the Tex created here goes out of scope, the DynArray should be freed + { + // 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 - TS_ASSERT_OK(tex_transform_to(&t, 0)); + // encode to file format + TS_ASSERT_OK(t.encode(extension, &da)); + memset(&t, 0, sizeof(t)); - // compare img - TS_ASSERT_SAME_DATA(tex_get_data(&t), img.get(), size); + // decode from file format + shared_ptr 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 - tex_free(&t); TS_ASSERT_OK(da_free(&da)); } @@ -136,10 +140,10 @@ public: // assumes 2x2 box filter algorithm with rounding static const u8 mipmap[] = { 0x6C,0x79,0x87 }; Tex t; - TS_ASSERT_OK(tex_wrap(2, 2, 24, 0, img, 0, &t)); - TS_ASSERT_OK(tex_transform_to(&t, TEX_MIPMAPS)); - const u8* const out_img = tex_get_data(&t); - TS_ASSERT_EQUALS((int)tex_img_size(&t), 12+3); + TS_ASSERT_OK(t.wrap(2, 2, 24, 0, img, 0)); + TS_ASSERT_OK(t.transform_to(TEX_MIPMAPS)); + const u8* const out_img = t.get_data(); + TS_ASSERT_EQUALS((int)t.img_size(), 12+3); TS_ASSERT_SAME_DATA(out_img, imgData, 12); TS_ASSERT_SAME_DATA(out_img+12, mipmap, 3); } @@ -149,13 +153,13 @@ public: shared_ptr img(new u8[100*100*4], ArrayDeleter()); Tex t; - TS_ASSERT_OK(tex_wrap(100, 100, 32, TEX_ALPHA, img, 0, &t)); - TS_ASSERT_EQUALS((int)tex_img_size(&t), 40000); + TS_ASSERT_OK(t.wrap(100, 100, 32, TEX_ALPHA, img, 0)); + TS_ASSERT_EQUALS((int)t.img_size(), 40000); // DXT rounds up to 4x4 blocks; DXT1a is 4bpp Tex t2; - TS_ASSERT_OK(tex_wrap(97, 97, 4, DXT1A, img, 0, &t2)); - TS_ASSERT_EQUALS((int)tex_img_size(&t2), 5000); + TS_ASSERT_OK(t2.wrap(97, 97, 4, DXT1A, img, 0)); + TS_ASSERT_EQUALS((int)t2.img_size(), 5000); } void test_s3tc_decode() @@ -176,17 +180,15 @@ public: // wrap in Tex 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 - TS_ASSERT_OK(tex_transform_to(&t, 0)); + TS_ASSERT_OK(t.transform_to(0)); // compare img - TS_ASSERT_SAME_DATA(tex_get_data(&t), expected, 48); + TS_ASSERT_SAME_DATA(t.get_data(), expected, 48); // cleanup - tex_free(&t); - tex_codec_unregister_all(); } }; diff --git a/source/lib/tex/tex.cpp b/source/lib/tex/tex.cpp index 52658c026c..443fdbb448 100644 --- a/source/lib/tex/tex.cpp +++ b/source/lib/tex/tex.cpp @@ -57,41 +57,41 @@ STATUS_ADD_DEFINITIONS(texStatusDefinitions); //----------------------------------------------------------------------------- // 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); // pixel data (only check validity if the image is still in memory; // ogl_tex frees the data after uploading to GL) - if(t->data) + if(m_Data) { // file size smaller than header+pixels. // possible causes: texture file header is invalid, // 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); } // bits per pixel // (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); // flags // .. 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) WARN_RETURN(ERR::_4); // .. 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)) WARN_RETURN(ERR::_5); 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, @@ -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. if(!is_pow2(w) || !is_pow2(h)) WARN_RETURN(ERR::TEX_INVALID_SIZE); - t->flags |= TEX_MIPMAPS; // must come before tex_img_size! - const size_t mipmap_size = tex_img_size(t); + t->m_Flags |= TEX_MIPMAPS; // must come before tex_img_size! + const size_t mipmap_size = t->img_size(); shared_ptr mipmapData; AllocateAligned(mipmapData, mipmap_size); CreateLevelData cld = { bpp/8, w, h, (const u8*)newData, dataSize }; tex_util_foreach_mipmap(w, h, bpp, mipmapData.get(), 0, 1, create_level, &cld); - t->data = mipmapData; - t->dataSize = mipmap_size; - t->ofs = 0; + t->m_Data = mipmapData; + t->m_DataSize = mipmap_size; + t->m_Ofs = 0; return INFO::OK; } @@ -293,9 +293,9 @@ TIMER_ACCRUE(tc_plain_transform); CHECK_TEX(t); // extract texture info - const size_t w = t->w, h = t->h, bpp = t->bpp; - const size_t flags = t->flags; - u8* const srcStorage = tex_get_data(t); + 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(); // sanity checks (not errors, we just can't handle these cases) // .. unknown transform @@ -307,7 +307,7 @@ TIMER_ACCRUE(tc_plain_transform); if(!transforms) return INFO::OK; - const size_t srcSize = tex_img_size(t); + const size_t srcSize = t->img_size(); size_t dstSize = srcSize; if(transforms & TEX_ALPHA) @@ -316,7 +316,7 @@ TIMER_ACCRUE(tc_plain_transform); if(bpp == 24) { dstSize = (srcSize / 3) * 4; - t->bpp = 32; + t->m_Bpp = 32; } // remove alpha channel else if(bpp == 32) @@ -448,11 +448,11 @@ TIMER_ACCRUE(tc_plain_transform); return INFO::TEX_CODEC_CANNOT_HANDLE; } - t->data = dstStorage; - t->dataSize = dstSize; - t->ofs = 0; + t->m_Data = dstStorage; + t->m_DataSize = dstSize; + 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)); CHECK_TEX(t); @@ -464,38 +464,38 @@ TIMER_ADD_CLIENT(tc_transform); // change 's pixel format by flipping the state of all TEX_* flags // that are set in transforms. -Status tex_transform(Tex* t, size_t transforms) +Status Tex::transform(size_t transforms) { 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; for(;;) { - remaining_transforms = target_flags ^ t->flags; + remaining_transforms = target_flags ^ m_Flags; // we're finished (all required transforms have been done) if(remaining_transforms == 0) return INFO::OK; - Status ret = tex_codec_transform(t, remaining_transforms); + Status ret = tex_codec_transform(this, remaining_transforms); if(ret != INFO::OK) break; } // last chance - RETURN_STATUS_IF_ERR(plain_transform(t, remaining_transforms)); + RETURN_STATUS_IF_ERR(plain_transform(this, remaining_transforms)); return INFO::OK; } // change 's pixel format to the new format specified by . // (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 - const size_t transforms = t->flags ^ new_flags; - return tex_transform(t, transforms); + const size_t transforms = m_Flags ^ new_flags; + return transform(transforms); } @@ -520,9 +520,9 @@ void tex_set_global_orientation(int o) static void flip_to_global_orientation(Tex* t) { // (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(orientation) { @@ -534,10 +534,10 @@ static void flip_to_global_orientation(Tex* t) // 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). - 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) - 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 // our Tex struct, hence the name. -Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr& data, size_t ofs, Tex* t) +Status Tex::wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr& data, size_t ofs) { - t->w = w; - t->h = h; - t->bpp = bpp; - t->flags = flags; - t->data = data; - t->dataSize = ofs + w*h*bpp/8; - t->ofs = ofs; + 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(t); + CHECK_TEX(this); return INFO::OK; } // free all resources associated with the image and make further // use of it impossible. -void tex_free(Tex* t) +void Tex::free() { // do not validate - this is called from tex_load if loading // 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 // 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 // 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) - WARN_IF_ERR(tex_validate(t)); + WARN_IF_ERR(validate()); - u8* p = t->data.get(); + u8* p = m_Data.get(); if(!p) return 0; - return p + t->ofs; + return p + m_Ofs; } // returns colour of 1x1 mipmap level -u32 tex_get_average_colour(const Tex* t) +u32 Tex::get_average_colour() const { // require mipmaps - if(!(t->flags & TEX_MIPMAPS)) + if(!(m_Flags & TEX_MIPMAPS)) return 0; // 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 - const size_t data_padding = (t->flags & TEX_DXT)? 4 : 1; - size_t last_level_size = (size_t)(data_padding * data_padding * t->bpp/8); + 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); // construct a new texture based on the current one, - // but set its data pointer offset to the last mipmap level's data - Tex basetex = *t; - basetex.w = 1; - basetex.h = 1; - basetex.ofs += size - last_level_size; + // but only include the last mipmap level + // do this so that we can use the general conversion methods for the pixel data + Tex basetex = *this; + uint8_t *data = new uint8_t[last_level_size]; + memcpy(data, m_Data.get() + m_Ofs + size - last_level_size, last_level_size); + boost::shared_ptr sdata(data); + basetex.wrap(1, 1, m_Bpp, m_Flags, sdata, 0); // 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 - ENSURE(basetex.dataSize >= basetex.ofs+4); - u8 b = basetex.data.get()[basetex.ofs]; - u8 g = basetex.data.get()[basetex.ofs+1]; - u8 r = basetex.data.get()[basetex.ofs+2]; - u8 a = basetex.data.get()[basetex.ofs+3]; + 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]; 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!) // this is preferable to calculating manually because it's // 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) - 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 size_t data_padding = (t->flags & TEX_DXT)? 4 : 1; + 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; 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; } @@ -714,57 +716,57 @@ size_t tex_hdr_size(const VfsPath& filename) // read/write from memory and disk //----------------------------------------------------------------------------- -Status tex_decode(const shared_ptr& data, size_t dataSize, Tex* t) +Status Tex::decode(const shared_ptr& Data, size_t DataSize) { 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 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); - const size_t hdr_size = c->hdr_size(data.get()); - if(dataSize < hdr_size) + const size_t hdr_size = c->hdr_size(Data.get()); + if(DataSize < hdr_size) WARN_RETURN(ERR::TEX_INCOMPLETE_HEADER); - t->data = data; - t->dataSize = dataSize; - t->ofs = hdr_size; + m_Data = Data; + m_DataSize = DataSize; + 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 - if(!t->w || !t->h || t->bpp > 32) + if(!m_Width || !m_Height || m_Bpp > 32) 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); - flip_to_global_orientation(t); + flip_to_global_orientation(this); - CHECK_TEX(t); + CHECK_TEX(this); return INFO::OK; } -Status tex_encode(Tex* t, const OsPath& extension, DynArray* da) +Status Tex::encode(const OsPath& extension, DynArray* da) { - CHECK_TEX(t); - WARN_RETURN_STATUS_IF_ERR(tex_validate_plain_format(t->bpp, t->flags)); + CHECK_TEX(this); + 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 // 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). // 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)); const TexCodecVTbl* c; WARN_RETURN_STATUS_IF_ERR(tex_codec_for_filename(extension, &c)); // encode into - Status err = c->encode(t, da); + Status err = c->encode(this, da); if(err < 0) { (void)da_free(da); diff --git a/source/lib/tex/tex.h b/source/lib/tex/tex.h index 317be3c800..27ce4e1033 100644 --- a/source/lib/tex/tex.h +++ b/source/lib/tex/tex.h @@ -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 * a copy of this software and associated documentation files (the @@ -201,7 +201,6 @@ enum TexFlags TEX_UNDEFINED_FLAGS = ~0x1FF }; - /** * stores all data describing an image. * 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 * a new buffer (e.g. if decompressing file contents). **/ - shared_ptr data; + shared_ptr m_Data; - size_t dataSize; + size_t m_DataSize; /** * 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 * write-back to file is also made possible. **/ - size_t ofs; + size_t m_Ofs; - size_t w; - size_t h; - size_t bpp; + size_t m_Width; + size_t m_Height; + size_t m_Bpp; /// 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& 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 \ 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& data, size_t ofs); + + // + // modify image + // + + /** + * Change \'s pixel format. + * + * @param transforms TexFlags that are to be flipped. + * @return Status + **/ + Status transform(size_t transforms); + + /** + * Change \'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 * 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(); -/** - * 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& 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 \ 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& 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 \'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 \'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 * 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); -// RAII wrapper -struct ScopedTex : public Tex -{ - ~ScopedTex() - { - tex_free(this); - } -}; - #endif // INCLUDED_TEX diff --git a/source/lib/tex/tex_bmp.cpp b/source/lib/tex/tex_bmp.cpp index d4d899c609..2ef4ba71e3 100644 --- a/source/lib/tex/tex_bmp.cpp +++ b/source/lib/tex/tex_bmp.cpp @@ -116,10 +116,10 @@ static Status bmp_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t) if(compress != BI_RGB) WARN_RETURN(ERR::TEX_COMPRESSED); - t->w = w; - t->h = h; - t->bpp = bpp; - t->flags = flags; + t->m_Width = w; + t->m_Height = h; + t->m_Bpp = bpp; + t->m_Flags = flags; 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) { 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 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_BGR; // BMP is native BGR. @@ -145,10 +145,10 @@ static Status bmp_encode(Tex* RESTRICT t, DynArray* RESTRICT da) // BITMAPINFOHEADER 40, // biSize = sizeof(BITMAPINFOHEADER) - (i32)t->w, + (i32)t->m_Width, h, 1, // biPlanes - (u16)t->bpp, + (u16)t->m_Bpp, BI_RGB, // biCompression (u32)img_size, // biSizeImage 0, 0, 0, 0 // unused (bi?PelsPerMeter, biClr*) diff --git a/source/lib/tex/tex_codec.cpp b/source/lib/tex/tex_codec.cpp index 38f09e67af..dbc4849096 100644 --- a/source/lib/tex/tex_codec.cpp +++ b/source/lib/tex/tex_codec.cpp @@ -175,9 +175,9 @@ std::vector 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) { - 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, img_data, img_size)); return INFO::OK; diff --git a/source/lib/tex/tex_dds.cpp b/source/lib/tex/tex_dds.cpp index 8e17b22571..9ca46778d6 100644 --- a/source/lib/tex/tex_dds.cpp +++ b/source/lib/tex/tex_dds.cpp @@ -279,22 +279,22 @@ static Status s3tc_decompress(Tex* t) // - adding or stripping alpha channels during transform is not // our job; we merely output the same pixel format as given // (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_size = tex_img_size(t) * out_bpp / t->bpp; + const size_t out_size = t->img_size() * out_bpp / t->m_Bpp; shared_ptr decompressedData; AllocateAligned(decompressedData, out_size, pageSize); const size_t s3tc_block_size = (dxt == 3 || dxt == 5)? 16 : 8; S3tcDecompressInfo di = { dxt, s3tc_block_size, out_bpp/8, decompressedData.get() }; - const u8* s3tc_data = tex_get_data(t); - const int levels_to_skip = (t->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); - t->data = decompressedData; - t->dataSize = out_size; - t->ofs = 0; - t->bpp = out_bpp; - t->flags &= ~TEX_DXT; + const u8* s3tc_data = t->get_data(); + const int levels_to_skip = (t->m_Flags & TEX_MIPMAPS)? 0 : TEX_BASE_LEVEL_ONLY; + tex_util_foreach_mipmap(t->m_Width, t->m_Height, t->m_Bpp, s3tc_data, levels_to_skip, 4, s3tc_decompress_level, &di); + t->m_Data = decompressedData; + t->m_DataSize = out_size; + t->m_Ofs = 0; + t->m_Bpp = out_bpp; + t->m_Flags &= ~TEX_DXT; 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) { 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; } @@ -626,8 +626,8 @@ static Status dds_transform(Tex* t, size_t transforms) { TIMER_ACCRUE(tc_dds_transform); - size_t mipmaps = t->flags & TEX_MIPMAPS; - size_t dxt = t->flags & TEX_DXT; + size_t mipmaps = t->m_Flags & TEX_MIPMAPS; + size_t dxt = t->m_Flags & TEX_DXT; ENSURE(is_valid_dxt(dxt)); 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 // mipmap levels will just be treated as trailing junk - t->flags &= ~TEX_MIPMAPS; + t->m_Flags &= ~TEX_MIPMAPS; return INFO::OK; } // requesting decompression diff --git a/source/lib/tex/tex_jpg.cpp b/source/lib/tex/tex_jpg.cpp index afba9ff157..61c883ec21 100644 --- a/source/lib/tex/tex_jpg.cpp +++ b/source/lib/tex/tex_jpg.cpp @@ -503,16 +503,8 @@ static Status jpg_decode_impl(rpU8 data, size_t size, jpeg_decompress_struct* ci if(cinfo->err->num_warnings != 0) ret = WARN::TEX_INVALID_DATA; - // store image info - t->data = img; - t->dataSize = imgSize; - t->ofs = 0; - t->w = w; - t->h = h; - t->bpp = bpp; - t->flags = flags; - - return ret; + // store image info and validate + return ret | t->wrap(w,h,bpp,flags,img,0); } @@ -522,10 +514,10 @@ static Status jpg_encode_impl(Tex* t, jpeg_compress_struct* cinfo, DynArray* da) // describe image format // required: - cinfo->image_width = (JDIMENSION)t->w; - cinfo->image_height = (JDIMENSION)t->h; - cinfo->input_components = (int)t->bpp / 8; - cinfo->in_color_space = (t->bpp == 8)? JCS_GRAYSCALE : JCS_RGB; + cinfo->image_width = (JDIMENSION)t->m_Width; + cinfo->image_height = (JDIMENSION)t->m_Height; + cinfo->input_components = (int)t->m_Bpp / 8; + cinfo->in_color_space = (t->m_Bpp == 8)? JCS_GRAYSCALE : JCS_RGB; // defaults depend on cinfo->in_color_space already having been set! jpeg_set_defaults(cinfo); // (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); // 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; - u8* data = tex_get_data(t); - std::vector rows = tex_codec_alloc_rows(data, t->h, pitch, t->flags, TEX_TOP_DOWN); + const size_t pitch = t->m_Width * t->m_Bpp / 8; + u8* data = t->get_data(); + std::vector 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, // but we need to count lines_left anyway (paranoia). JSAMPARRAY row = (JSAMPARRAY)&rows[0]; - JDIMENSION lines_left = (JDIMENSION)t->h; + JDIMENSION lines_left = (JDIMENSION)t->m_Height; while(lines_left != 0) { JDIMENSION lines_read = jpeg_write_scanlines(cinfo, row, lines_left); diff --git a/source/lib/tex/tex_png.cpp b/source/lib/tex/tex_png.cpp index 32d34a37b9..49de395443 100644 --- a/source/lib/tex/tex_png.cpp +++ b/source/lib/tex/tex_png.cpp @@ -153,16 +153,8 @@ static Status png_decode_impl(MemoryStream* stream, png_structp png_ptr, png_inf // success; make sure all data was consumed. ENSURE(stream->RemainingSize() == 0); - // store image info - t->data = data; - t->dataSize = img_size; - t->ofs = 0; - t->w = w; - t->h = h; - t->bpp = bpp; - t->flags = flags; - - return INFO::OK; + // store image info and validate + return t->wrap(w,h,bpp,flags,data,0); } @@ -170,11 +162,11 @@ static Status png_decode_impl(MemoryStream* stream, png_structp png_ptr, png_inf // "dtor / setjmp interaction" warning. 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 size_t pitch = w * t->bpp / 8; + const png_uint_32 w = (png_uint_32)t->m_Width, h = (png_uint_32)t->m_Height; + const size_t pitch = w * t->m_Bpp / 8; int colour_type; - switch(t->flags & (TEX_GREY|TEX_ALPHA)) + switch(t->m_Flags & (TEX_GREY|TEX_ALPHA)) { case TEX_GREY|TEX_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_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - u8* data = tex_get_data(t); - std::vector rows = tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN); + u8* data = t->get_data(); + std::vector rows = tex_codec_alloc_rows(data, h, pitch, t->m_Flags, TEX_TOP_DOWN); // 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_write_png(png_ptr, info_ptr, png_transforms, 0); diff --git a/source/lib/tex/tex_tga.cpp b/source/lib/tex/tex_tga.cpp index 5e43905a91..83811abd91 100644 --- a/source/lib/tex/tex_tga.cpp +++ b/source/lib/tex/tex_tga.cpp @@ -136,10 +136,10 @@ static Status tga_decode(rpU8 data, size_t UNUSED(size), Tex* RESTRICT t) if(desc & TGA_RIGHT_TO_LEFT) WARN_RETURN(ERR::TEX_INVALID_LAYOUT); - t->w = w; - t->h = h; - t->bpp = bpp; - t->flags = flags; + t->m_Width = w; + t->m_Height = h; + t->m_Bpp = bpp; + t->m_Flags = flags; 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) { u8 img_desc = 0; - if(t->flags & TEX_TOP_DOWN) + if(t->m_Flags & TEX_TOP_DOWN) img_desc |= TGA_TOP_DOWN; - if(t->bpp == 32) + if(t->m_Bpp == 32) 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_BGR; // TGA is native BGR. @@ -164,9 +164,9 @@ static Status tga_encode(Tex* RESTRICT t, DynArray* RESTRICT da) (u8)img_type, {0,0,0,0,0}, // unused (colour map) 0, 0, // unused (origin) - (u16)t->w, - (u16)t->h, - (u8)t->bpp, + (u16)t->m_Width, + (u16)t->m_Height, + (u8)t->m_Bpp, img_desc }; const size_t hdr_size = sizeof(hdr); diff --git a/source/ps/Util.cpp b/source/ps/Util.cpp index 5d9db1ccfa..6fa78a4ace 100644 --- a/source/ps/Util.cpp +++ b/source/ps/Util.cpp @@ -173,7 +173,7 @@ const wchar_t* ErrorString(int err) Status tex_write(Tex* t, const VfsPath& filename) { DynArray da; - RETURN_STATUS_IF_ERR(tex_encode(t, filename.Extension(), &da)); + RETURN_STATUS_IF_ERR(t->encode(filename.Extension(), &da)); // write to disk Status ret = INFO::OK; @@ -230,7 +230,7 @@ void WriteScreenshot(const VfsPath& extension) AllocateAligned(buf, hdr_size+img_size, maxSectorSize); GLvoid* img = buf.get() + hdr_size; 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; glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img); @@ -242,8 +242,6 @@ void WriteScreenshot(const VfsPath& extension) } else 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; 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); return; @@ -378,6 +376,5 @@ void WriteBigScreenshot(const VfsPath& extension, int tiles) else LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str()); - tex_free(&t); free(tile_data); } diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 235d59b466..863e0a250c 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -1929,7 +1929,7 @@ int CRenderer::LoadAlphaMaps() // upload the composite texture 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"); diff --git a/source/renderer/SkyManager.cpp b/source/renderer/SkyManager.cpp index 1e36ea632b..98d68d84c9 100644 --- a/source/renderer/SkyManager.cpp +++ b/source/renderer/SkyManager.cpp @@ -88,7 +88,7 @@ void SkyManager::LoadSkyTextures() texture->Prefetch(); m_SkyTexture[i] = texture; }*/ - + /////////////////////////////////////////////////////////////////////////// // HACK: THE HORRIBLENESS HERE IS OVER 9000. The following code is a HUGE hack and will be removed completely // as soon as all the hardcoded GL_TEXTURE_2D references are corrected in the TextureManager/OGL/tex libs. @@ -132,37 +132,35 @@ void SkyManager::LoadSkyTextures() } 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) { - std::vector rotated(tex.dataSize); + std::vector 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.w + x) * 4 + 1] = data[(invy*tex.w + invx) * 4 + 1]; - rotated[(y*tex.w + x) * 4 + 2] = data[(invy*tex.w + invx) * 4 + 2]; - rotated[(y*tex.w + x) * 4 + 3] = data[(invy*tex.w + invx) * 4 + 3]; + rotated[(y*tex.m_Width + x) * 4 + 0] = data[(invy*tex.m_Width + invx) * 4 + 0]; + rotated[(y*tex.m_Width + x) * 4 + 1] = data[(invy*tex.m_Width + invx) * 4 + 1]; + rotated[(y*tex.m_Width + x) * 4 + 2] = data[(invy*tex.m_Width + invx) * 4 + 2]; + 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 { - 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); diff --git a/source/simulation2/components/CCmpAIManager.cpp b/source/simulation2/components/CCmpAIManager.cpp index fa49e2d4e0..31ac7674a5 100644 --- a/source/simulation2/components/CCmpAIManager.cpp +++ b/source/simulation2/components/CCmpAIManager.cpp @@ -336,7 +336,7 @@ public: shared_ptr buf; AllocateAligned(buf, hdr_size+img_size, maxSectorSize); 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; u8* img = buf.get() + hdr_size; @@ -344,7 +344,6 @@ public: img[i] = (u8)((data[i] * 255) / max); tex_write(&t, filename); - tex_free(&t); } bool TryLoadSharedComponent(bool hasTechs) diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp index 26111335bc..fd89b6111d 100644 --- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -172,22 +172,21 @@ MESSAGEHANDLER(ImportHeightmap) // decode to a raw pixel format Tex tex; - if (tex_decode(fileData, fileSize, &tex) < 0) + if (tex.decode(fileData, fileSize) < 0) { LOGERROR(L"Failed to decode heightmap."); return; } // 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."); - tex_free(&tex); return; } // 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; // resize terrain to heightmap size @@ -198,9 +197,9 @@ MESSAGEHANDLER(ImportHeightmap) u16* heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap(); ssize_t hmSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); - u8* mapdata = tex_get_data(&tex); - ssize_t bytesPP = tex.bpp / 8; - ssize_t mapLineSkip = tex.w * bytesPP; + u8* mapdata = tex.get_data(); + ssize_t bytesPP = tex.m_Bpp / 8; + ssize_t mapLineSkip = tex.m_Width * bytesPP; for (ssize_t y = 0; y < terrainSize; ++y) { @@ -215,8 +214,6 @@ MESSAGEHANDLER(ImportHeightmap) } } - tex_free(&tex); - // update simulation CmpPtr cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY); if (cmpTerrain) cmpTerrain->ReloadTerrain();