diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index 56ba4e459..250727917 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -66,13 +66,13 @@ class NAZARA_API NzTexture : public NzResource, NzNonCopyable bool Update(const NzImage& image, nzUInt8 level = 0); bool Update(const NzImage& image, const NzRectui& rect, unsigned int z = 0, nzUInt8 level = 0); bool Update(const NzImage& image, const NzCubeui& cube, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z = 0, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z = 0, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, const NzCubeui& cube, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); bool UpdateFace(nzCubemapFace face, const NzImage& image, nzUInt8 level = 0); bool UpdateFace(nzCubemapFace face, const NzImage& image, const NzRectui& rect, nzUInt8 level = 0); - bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 level = 0); - bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, nzUInt8 level = 0); + bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); + bool UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); void Unlock(); diff --git a/include/Nazara/Utility/Image.hpp b/include/Nazara/Utility/Image.hpp index 629ad1b33..a311f53df 100644 --- a/include/Nazara/Utility/Image.hpp +++ b/include/Nazara/Utility/Image.hpp @@ -97,13 +97,14 @@ class NAZARA_API NzImage : public NzResource bool SetLevelCount(nzUInt8 levelCount); bool SetPixelColor(const NzColor& color, unsigned int x, unsigned int y = 0, unsigned int z = 0); - bool Update(const nzUInt8* pixels, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z = 0, nzUInt8 level = 0); - bool Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z = 0, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); + bool Update(const nzUInt8* pixels, const NzCubeui& cube, unsigned int srcWidth = 0, unsigned int srcHeight = 0, nzUInt8 level = 0); NzImage& operator=(const NzImage& image); NzImage& operator=(NzImage&& image) noexcept; + static void Copy(nzUInt8* destination, const nzUInt8* source, nzUInt8 bpp, unsigned int width, unsigned int height, unsigned int depth = 1, unsigned int dstWidth = 0, unsigned int dstHeight = 0, unsigned int srcWidth = 0, unsigned int srcHeight = 0); static nzUInt8 GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth = 1); struct SharedImage diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp index 0aad8bc1d..446eb139a 100644 --- a/src/Nazara/Renderer/Texture.cpp +++ b/src/Nazara/Renderer/Texture.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -107,9 +108,9 @@ namespace case nzImageType_Cubemap: { - if (glTexStorage2D) + /*if (glTexStorage2D) glTexStorage2D(target, impl->levelCount, openGLFormat.internalFormat, impl->width, impl->height); - else + else*/ { unsigned int size = impl->width; // Les cubemaps ont une longueur et largeur identique for (nzUInt8 level = 0; level < impl->levelCount; ++level) @@ -644,14 +645,33 @@ bool NzTexture::LoadFromImage(const NzImage& image, bool generateMipmaps) return false; } - for (nzUInt8 level = 0; level < levelCount; ++level) + if (type == nzImageType_Cubemap) { - if (!Update(newImage.GetConstPixels(level), level)) + for (nzUInt8 level = 0; level < levelCount; ++level) { - NazaraError("Failed to update texture"); - Destroy(); + for (unsigned int i = 0; i <= nzCubemapFace_Max; ++i) + { + if (!UpdateFace(static_cast(i), newImage.GetConstPixels(0, 0, i, level), level)) + { + NazaraError("Failed to update texture"); + Destroy(); - return false; + return false; + } + } + } + } + else + { + for (nzUInt8 level = 0; level < levelCount; ++level) + { + if (!Update(newImage.GetConstPixels(0, 0, 0, level), level)) + { + NazaraError("Failed to update texture"); + Destroy(); + + return false; + } } } @@ -745,7 +765,14 @@ bool NzTexture::Update(const NzImage& image, nzUInt8 level) } #endif - return Update(image.GetConstPixels(level), level); + const nzUInt8* pixels = image.GetConstPixels(0, 0, 0, level); + if (!pixels) + { + NazaraError("Failed to access image's pixels"); + return false; + } + + return Update(pixels, image.GetWidth(level), image.GetHeight(level), level); } bool NzTexture::Update(const NzImage& image, const NzRectui& rect, unsigned int z, nzUInt8 level) @@ -764,20 +791,14 @@ bool NzTexture::Update(const NzImage& image, const NzRectui& rect, unsigned int } #endif - const nzUInt8* pixels = image.GetConstPixels(level, rect.x, rect.y, z); + const nzUInt8* pixels = image.GetConstPixels(rect.x, rect.y, z, level); if (!pixels) { NazaraError("Failed to access image's pixels"); return false; } - glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); - - bool success = Update(pixels, rect, z, level); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - return success; + return Update(pixels, rect, z, image.GetWidth(level), image.GetHeight(level), level); } bool NzTexture::Update(const NzImage& image, const NzCubeui& cube, nzUInt8 level) @@ -796,25 +817,17 @@ bool NzTexture::Update(const NzImage& image, const NzCubeui& cube, nzUInt8 level } #endif - const nzUInt8* pixels = image.GetConstPixels(level, cube.x, cube.y, cube.z); + const nzUInt8* pixels = image.GetConstPixels(cube.x, cube.y, cube.z, level); if (!pixels) { NazaraError("Failed to access image's pixels"); return false; } - glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); - glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, image.GetHeight(level)); - - bool success = Update(pixels, cube, level); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); - - return success; + return Update(pixels, cube, image.GetWidth(level), image.GetHeight(level), level); } -bool NzTexture::Update(const nzUInt8* pixels, nzUInt8 level) +bool NzTexture::Update(const nzUInt8* pixels, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { #if NAZARA_RENDERER_SAFE if (!m_impl) @@ -824,114 +837,15 @@ bool NzTexture::Update(const nzUInt8* pixels, nzUInt8 level) } #endif - if (m_impl->type == nzImageType_3D || m_impl->type == nzImageType_2D_Array) - return Update(pixels, NzCubeui(0, 0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U), std::max(m_impl->depth >> level, 1U)), level); - else - return Update(pixels, NzRectui(0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U)), 0, level); + return Update(pixels, NzCubeui(0, 0, 0, std::max(m_impl->width >> level, 1U), std::max(m_impl->height >> level, 1U), std::max(m_impl->depth >> level, 1U)), srcWidth, srcHeight, level); } -bool NzTexture::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, nzUInt8 level) +bool NzTexture::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { - #if NAZARA_RENDERER_SAFE - if (!m_impl) - { - NazaraError("Texture must be valid"); - return false; - } - - if (m_impl->renderTexture) - { - NazaraError("Texture is a target, it cannot be updated"); - return false; - } - - if (m_impl->type == nzImageType_Cubemap) - { - NazaraError("Update is not designed for cubemaps, use UpdateFace instead"); - return false; - } - - if (!pixels) - { - NazaraError("Invalid pixel source"); - return false; - } - - if (!rect.IsValid()) - { - NazaraError("Invalid rectangle"); - return false; - } - #endif - - unsigned int height = std::max(m_impl->height >> level, 1U); - - #if NAZARA_RENDERER_SAFE - if (rect.x+rect.width > std::max(m_impl->width >> level, 1U) || rect.y+rect.height > height) - { - NazaraError("Rectangle dimensions are out of bounds"); - return false; - } - - if (z >= std::max(m_impl->depth >> level, 1U)) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(m_impl->depth) + ')'); - return false; - } - - if (level >= m_impl->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_impl->levelCount) + ')'); - return false; - } - #endif - - NzOpenGL::Format format; - if (!NzOpenGL::TranslateFormat(m_impl->format, &format, NzOpenGL::FormatType_Texture)) - { - NazaraError("Failed to get OpenGL format"); - return false; - } - - nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_impl->format); - - // Inversion de la texture pour le repère d'OpenGL - NzImage flipped; - flipped.Create(m_impl->type, m_impl->format, rect.width, rect.height); - flipped.Update(pixels); - - if (!flipped.FlipHorizontally()) - NazaraWarning("Failed to flip image"); - - SetUnpackAlignement(bpp); - - LockTexture(m_impl); - switch (m_impl->type) - { - case nzImageType_1D: - glTexSubImage1D(GL_TEXTURE_1D, level, rect.x, rect.width, format.dataFormat, format.dataType, flipped.GetConstPixels()); - break; - - case nzImageType_1D_Array: - case nzImageType_2D: - glTexSubImage2D(NzOpenGL::TextureTarget[m_impl->type], level, rect.x, height-rect.height-rect.y, rect.width, rect.height, format.dataFormat, format.dataType, flipped.GetConstPixels()); - break; - - case nzImageType_2D_Array: - case nzImageType_3D: - glTexSubImage3D(NzOpenGL::TextureTarget[m_impl->type], level, rect.x, height-rect.height-rect.y, z, rect.width, rect.height, 1, format.dataFormat, format.dataType, flipped.GetConstPixels()); - break; - - case nzImageType_Cubemap: - NazaraError("Update used on a cubemap texture, please enable safe mode"); - break; - } - UnlockTexture(m_impl); - - return true; + return Update(pixels, NzCubeui(rect.x, rect.y, z, rect.width, rect.height, 1), srcWidth, srcHeight, level); } -bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) +bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { #if NAZARA_RENDERER_SAFE if (!m_impl) @@ -992,14 +906,13 @@ bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 leve nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_impl->format); - // Inversion de la texture pour le repère d'OpenGL unsigned int size = cube.width*cube.height*cube.depth*bpp; - nzUInt8* flipped = new nzUInt8[size]; - if (!NzPixelFormat::Flip(nzPixelFlipping_Horizontally, m_impl->format, cube.width, cube.height, cube.depth, pixels, flipped)) - { + std::unique_ptr flipped(new nzUInt8[size]); + NzImage::Copy(flipped.get(), pixels, bpp, cube.width, cube.height, cube.depth, 0, 0, srcWidth, srcHeight); + + // Inversion de la texture pour le repère d'OpenGL + if (!NzPixelFormat::Flip(nzPixelFlipping_Horizontally, m_impl->format, cube.width, cube.height, cube.depth, flipped.get(), flipped.get())) NazaraWarning("Failed to flip image"); - std::memcpy(flipped, pixels, size); - } SetUnpackAlignement(bpp); @@ -1007,17 +920,17 @@ bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 leve switch (m_impl->type) { case nzImageType_1D: - glTexSubImage1D(GL_TEXTURE_1D, level, cube.x, cube.width, format.dataFormat, format.dataType, flipped); + glTexSubImage1D(GL_TEXTURE_1D, level, cube.x, cube.width, format.dataFormat, format.dataType, flipped.get()); break; case nzImageType_1D_Array: case nzImageType_2D: - glTexSubImage2D(NzOpenGL::TextureTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.width, cube.height, format.dataFormat, format.dataType, flipped); + glTexSubImage2D(NzOpenGL::TextureTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.width, cube.height, format.dataFormat, format.dataType, flipped.get()); break; case nzImageType_2D_Array: case nzImageType_3D: - glTexSubImage3D(NzOpenGL::TextureTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.z, cube.width, cube.height, cube.depth, format.dataFormat, format.dataType, flipped); + glTexSubImage3D(NzOpenGL::TextureTarget[m_impl->type], level, cube.x, height-cube.height-cube.y, cube.z, cube.width, cube.height, cube.depth, format.dataFormat, format.dataType, flipped.get()); break; case nzImageType_Cubemap: @@ -1026,8 +939,6 @@ bool NzTexture::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 leve } UnlockTexture(m_impl); - delete[] flipped; - return true; } @@ -1047,7 +958,7 @@ bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, nzUInt8 lev } #endif - return UpdateFace(face, image.GetConstPixels(level), NzRectui(0, 0, image.GetWidth(), image.GetHeight()), level); + return UpdateFace(face, image.GetConstPixels(0, 0, 0, level), NzRectui(0, 0, image.GetWidth(level), image.GetHeight(level)), 0, 0, level); } bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, const NzRectui& rect, nzUInt8 level) @@ -1066,16 +977,10 @@ bool NzTexture::UpdateFace(nzCubemapFace face, const NzImage& image, const NzRec } #endif - glPixelStorei(GL_UNPACK_ROW_LENGTH, image.GetWidth(level)); - - bool success = UpdateFace(face, image.GetConstPixels(level), rect, level); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - return success; + return UpdateFace(face, image.GetConstPixels(0, 0, 0, level), rect, image.GetWidth(level), image.GetHeight(level), level); } -bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 level) +bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { #if NAZARA_RENDERER_SAFE if (!m_impl) @@ -1085,10 +990,10 @@ bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, nzUInt8 le } #endif - return UpdateFace(face, pixels, NzRectui(0, 0, m_impl->width, m_impl->height), level); + return UpdateFace(face, pixels, NzRectui(0, 0, m_impl->width, m_impl->height), srcWidth, srcHeight, level); } -bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, nzUInt8 level) +bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRectui& rect, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { #if NAZARA_RENDERER_SAFE if (!m_impl) @@ -1149,17 +1054,16 @@ bool NzTexture::UpdateFace(nzCubemapFace face, const nzUInt8* pixels, const NzRe // Inversion de la texture pour le repère d'OpenGL unsigned int size = rect.width*rect.height*bpp; - nzUInt8* flipped = new nzUInt8[size]; - if (!NzPixelFormat::Flip(nzPixelFlipping_Horizontally, m_impl->format, rect.width, rect.height, 1, pixels, flipped)) - { + std::unique_ptr flipped(new nzUInt8[size]); + NzImage::Copy(flipped.get(), pixels, bpp, rect.width, rect.height, 1, 0, 0, srcWidth, srcHeight); + + if (!NzPixelFormat::Flip(nzPixelFlipping_Horizontally, m_impl->format, rect.width, rect.height, 1, flipped.get(), flipped.get())) NazaraWarning("Failed to flip image"); - std::memcpy(flipped, pixels, size); - } SetUnpackAlignement(bpp); LockTexture(m_impl); - glTexSubImage2D(NzOpenGL::CubemapFace[face], level, rect.x, height-rect.height-rect.y, rect.width, rect.height, format.dataFormat, format.dataType, flipped); + glTexSubImage2D(NzOpenGL::CubemapFace[face], level, rect.x, height-rect.height-rect.y, rect.width, rect.height, format.dataFormat, format.dataType, flipped.get()); UnlockTexture(m_impl); return true; diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index 5eca49e91..37819bf53 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -6,9 +6,12 @@ #include #include #include +#include #include #include +///TODO: Rajouter des warnings (Formats compressés avec les méthodes Copy/Update, tests taille dans Copy) + namespace { inline unsigned int GetLevelSize(unsigned int size, nzUInt8 level) @@ -66,13 +69,13 @@ m_sharedImage(image.m_sharedImage) NzImage::~NzImage() { - ReleaseImage(); + Destroy(); } bool NzImage::Convert(nzPixelFormat format) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -154,6 +157,12 @@ bool NzImage::Convert(nzPixelFormat format) bool NzImage::Copy(const NzImage& source, const NzCubeui& srcCube, const NzVector3ui& dstPos) { #if NAZARA_UTILITY_SAFE + if (m_sharedImage == &emptyImage) + { + NazaraError("Image must be valid"); + return false; + } + if (!source.IsValid()) { NazaraError("Source image must be valid"); @@ -167,49 +176,19 @@ bool NzImage::Copy(const NzImage& source, const NzCubeui& srcCube, const NzVecto } #endif - const nzUInt8* pixels = source.GetConstPixels(0, srcCube.x, srcCube.y, srcCube.z); - if (!pixels) + const nzUInt8* srcPtr = source.GetConstPixels(0, srcCube.x, srcCube.y, srcCube.z); + if (!srcPtr) { NazaraError("Failed to access pixels"); return false; } - /* - Correctif temporaire : Update veut de la mémoire contigüe - Il est donc nécessaire de prendre la partie de la texture que nous voulons mettre à jour - - ///FIXME: Trouver une interface pour gérer ce genre de problème (Façon OpenGL?) - (Appliquer l'interface à NzTexture également) - */ nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format); - unsigned int dstLineStride = srcCube.width*bpp; - unsigned int dstFaceStride = dstLineStride*srcCube.height; - unsigned int srcLineStride = m_sharedImage->width*bpp; - unsigned int srcFaceStride = srcLineStride*m_sharedImage->height; + nzUInt8* dstPtr = GetPixelPtr(m_sharedImage->pixels[0], bpp, dstPos.x, dstPos.y, dstPos.z, m_sharedImage->width, m_sharedImage->height); - nzUInt8* cube = new nzUInt8[dstFaceStride*srcCube.depth]; - nzUInt8* ptr = cube; + Copy(dstPtr, srcPtr, bpp, srcCube.width, srcCube.height, srcCube.depth, m_sharedImage->width, m_sharedImage->height, source.GetWidth(), source.GetHeight()); - for (unsigned int z = 0; z < srcCube.depth; ++z) - { - nzUInt8* facePixels = ptr; - for (unsigned int y = 0; y < srcCube.height; ++y) - { - std::memcpy(facePixels, pixels, dstLineStride); - - facePixels += dstLineStride; - pixels += srcLineStride; - } - - ptr += dstFaceStride; - pixels += srcFaceStride; - } - - bool success = Update(cube, NzCubeui(dstPos.x, dstPos.y, dstPos.z, srcCube.width, srcCube.height, srcCube.depth)); - - delete[] cube; - - return success; + return true; } bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) @@ -344,7 +323,7 @@ void NzImage::Destroy() bool NzImage::Fill(const NzColor& color) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -358,12 +337,10 @@ bool NzImage::Fill(const NzColor& color) #endif nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format); - nzUInt8* colorBuffer = new nzUInt8[bpp]; - if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, colorBuffer)) + std::unique_ptr colorBuffer(new nzUInt8[bpp]); + if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, colorBuffer.get())) { NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); - delete[] colorBuffer; - return false; } @@ -384,7 +361,7 @@ bool NzImage::Fill(const NzColor& color) while (ptr < end) { - std::memcpy(ptr, colorBuffer, bpp); + std::memcpy(ptr, colorBuffer.get(), bpp); ptr += bpp; } @@ -400,8 +377,6 @@ bool NzImage::Fill(const NzColor& color) depth >>= 1; } - delete[] colorBuffer; - SharedImage* newImage = new SharedImage(1, m_sharedImage->type, m_sharedImage->format, m_sharedImage->levelCount, levels, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth); ReleaseImage(); @@ -413,7 +388,7 @@ bool NzImage::Fill(const NzColor& color) bool NzImage::Fill(const NzColor& color, const NzRectui& rect, unsigned int z) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -442,12 +417,10 @@ bool NzImage::Fill(const NzColor& color, const NzRectui& rect, unsigned int z) EnsureOwnership(); nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format); - nzUInt8* pixels = new nzUInt8[bpp]; - if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) + std::unique_ptr colorBuffer(new nzUInt8[bpp]); + if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, colorBuffer.get())) { NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); - delete[] pixels; - return false; } @@ -461,22 +434,20 @@ bool NzImage::Fill(const NzColor& color, const NzRectui& rect, unsigned int z) nzUInt8* end = dstPixels + srcStride; while (start < end) { - std::memcpy(start, pixels, bpp); + std::memcpy(start, colorBuffer.get(), bpp); start += bpp; } dstPixels += dstStride; } - delete[] pixels; - return true; } bool NzImage::Fill(const NzColor& color, const NzCubeui& cube) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -498,12 +469,10 @@ bool NzImage::Fill(const NzColor& color, const NzCubeui& cube) EnsureOwnership(); nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format); - nzUInt8* pixels = new nzUInt8[bpp]; - if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, pixels)) + std::unique_ptr colorBuffer(new nzUInt8[bpp]); + if (!NzPixelFormat::Convert(nzPixelFormat_RGBA8, m_sharedImage->format, &color.r, colorBuffer.get())) { NazaraError("Failed to convert RGBA8 to " + NzPixelFormat::ToString(m_sharedImage->format)); - delete[] pixels; - return false; } @@ -521,7 +490,7 @@ bool NzImage::Fill(const NzColor& color, const NzCubeui& cube) nzUInt8* end = facePixels + srcStride; while (start < end) { - std::memcpy(start, pixels, bpp); + std::memcpy(start, colorBuffer.get(), bpp); start += bpp; } @@ -531,15 +500,13 @@ bool NzImage::Fill(const NzColor& color, const NzCubeui& cube) dstPixels += faceSize; } - delete[] pixels; - return true; } bool NzImage::FlipHorizontally() { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -577,7 +544,7 @@ bool NzImage::FlipHorizontally() bool NzImage::FlipVertically() { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -620,31 +587,12 @@ nzUInt8 NzImage::GetBytesPerPixel() const const nzUInt8* NzImage::GetConstPixels(unsigned int x, unsigned int y, unsigned int z, nzUInt8 level) const { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return nullptr; } - if (x >= m_sharedImage->width) - { - NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); - return nullptr; - } - - if (y >= m_sharedImage->height) - { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); - return nullptr; - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; - if (z >= depth) - { - NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); - return nullptr; - } - if (level >= m_sharedImage->levelCount) { NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); @@ -652,7 +600,32 @@ const nzUInt8* NzImage::GetConstPixels(unsigned int x, unsigned int y, unsigned } #endif - return GetPixelPtr(m_sharedImage->pixels[level], NzPixelFormat::GetBytesPerPixel(m_sharedImage->format), x, y, z, m_sharedImage->width, m_sharedImage->height); + unsigned int width = GetLevelSize(m_sharedImage->width, level); + #if NAZARA_UTILITY_SAFE + if (x >= width) + { + NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(width) + ')'); + return nullptr; + } + #endif + + unsigned int height = GetLevelSize(m_sharedImage->height, level); + #if NAZARA_UTILITY_SAFE + if (y >= height) + { + NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= (" + NzString::Number(height) + ')'); + return nullptr; + } + + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level); + if (z >= depth) + { + NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); + return nullptr; + } + #endif + + return GetPixelPtr(m_sharedImage->pixels[level], NzPixelFormat::GetBytesPerPixel(m_sharedImage->format), x, y, z, width, height); } unsigned int NzImage::GetDepth(nzUInt8 level) const @@ -699,7 +672,7 @@ nzUInt8 NzImage::GetMaxLevel() const NzColor NzImage::GetPixelColor(unsigned int x, unsigned int y, unsigned int z) const { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return NzColor(); @@ -719,7 +692,7 @@ NzColor NzImage::GetPixelColor(unsigned int x, unsigned int y, unsigned int z) c if (y >= m_sharedImage->height) { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); + NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); return NzColor(); } @@ -743,32 +716,43 @@ NzColor NzImage::GetPixelColor(unsigned int x, unsigned int y, unsigned int z) c nzUInt8* NzImage::GetPixels(unsigned int x, unsigned int y, unsigned int z, nzUInt8 level) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return nullptr; } - if (x >= m_sharedImage->width) + if (level >= m_sharedImage->levelCount) { - NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(m_sharedImage->width) + ')'); + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return nullptr; + } + #endif + + unsigned int width = GetLevelSize(m_sharedImage->width, level); + #if NAZARA_UTILITY_SAFE + if (x >= width) + { + NazaraError("X value exceeds width (" + NzString::Number(x) + " >= (" + NzString::Number(width) + ')'); + return nullptr; + } + #endif + + unsigned int height = GetLevelSize(m_sharedImage->height, level); + #if NAZARA_UTILITY_SAFE + if (y >= height) + { + NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= (" + NzString::Number(height) + ')'); return nullptr; } - if (y >= m_sharedImage->height) - { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); - return nullptr; - } - - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth; + unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->depth, level); if (z >= depth) { NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')'); return nullptr; } - if (level >= m_sharedImage->levelCount) { NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); @@ -875,7 +859,7 @@ bool NzImage::LoadFromStream(NzInputStream& stream, const NzImageParams& params) bool NzImage::SetLevelCount(nzUInt8 levelCount) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -920,7 +904,7 @@ bool NzImage::SetLevelCount(nzUInt8 levelCount) bool NzImage::SetPixelColor(const NzColor& color, unsigned int x, unsigned int y, unsigned int z) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -940,7 +924,7 @@ bool NzImage::SetPixelColor(const NzColor& color, unsigned int x, unsigned int y if (y >= m_sharedImage->height) { - NazaraError("Y value exceeds width (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); + NazaraError("Y value exceeds height (" + NzString::Number(y) + " >= (" + NzString::Number(m_sharedImage->height) + ')'); return false; } @@ -963,10 +947,10 @@ bool NzImage::SetPixelColor(const NzColor& color, unsigned int x, unsigned int y return true; } -bool NzImage::Update(const nzUInt8* pixels, nzUInt8 level) +bool NzImage::Update(const nzUInt8* pixels, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -987,15 +971,20 @@ bool NzImage::Update(const nzUInt8* pixels, nzUInt8 level) EnsureOwnership(); - std::memcpy(m_sharedImage->pixels[level], pixels, GetSize(level)); + Copy(m_sharedImage->pixels[level], pixels, NzPixelFormat::GetBytesPerPixel(m_sharedImage->format), + GetLevelSize(m_sharedImage->width, level), + GetLevelSize(m_sharedImage->height, level), + GetLevelSize(m_sharedImage->depth, level), + 0, 0, + srcWidth, srcHeight); return true; } -bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, nzUInt8 level) +bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -1007,17 +996,17 @@ bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z return false; } - if (level >= m_sharedImage->levelCount) - { - NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); - return false; - } - if (!rect.IsValid()) { NazaraError("Invalid rectangle"); return false; } + + if (level >= m_sharedImage->levelCount) + { + NazaraError("Level out of bounds (" + NzString::Number(level) + " >= " + NzString::Number(m_sharedImage->levelCount) + ')'); + return false; + } #endif unsigned int width = GetLevelSize(m_sharedImage->width, level); @@ -1042,23 +1031,19 @@ bool NzImage::Update(const nzUInt8* pixels, const NzRectui& rect, unsigned int z nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format); nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[level], bpp, rect.x, rect.y, z, width, height); - unsigned int srcStride = rect.width * bpp; - unsigned int dstStride = m_sharedImage->width * bpp; - for (unsigned int y = 0; y < rect.height; ++y) - { - std::memcpy(dstPixels, pixels, srcStride); - pixels += srcStride; - dstPixels += dstStride; - } + + Copy(dstPixels, pixels, bpp, + rect.width, rect.height, 1, + width, height, + srcWidth, srcHeight); return true; } -bool NzImage::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) +bool NzImage::Update(const nzUInt8* pixels, const NzCubeui& cube, unsigned int srcWidth, unsigned int srcHeight, nzUInt8 level) { - ///FIXME: Vérifier que ça fonctionne correctement #if NAZARA_UTILITY_SAFE - if (!m_sharedImage) + if (m_sharedImage == &emptyImage) { NazaraError("Image must be valid"); return false; @@ -1079,7 +1064,6 @@ bool NzImage::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) unsigned int width = GetLevelSize(m_sharedImage->width, level); unsigned int height = GetLevelSize(m_sharedImage->height, level); - unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : GetLevelSize(m_sharedImage->height, level); #if NAZARA_UTILITY_SAFE if (!cube.IsValid()) @@ -1088,7 +1072,8 @@ bool NzImage::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) return false; } - if (cube.x+cube.width > width || cube.y+cube.height > height || cube.z+cube.depth > depth) + // Nous n'autorisons pas de modifier plus d'une face du cubemap à la fois + if (cube.x+cube.width > width || cube.y+cube.height > height || cube.z+cube.depth > GetLevelSize(m_sharedImage->height, level)) { NazaraError("Cube dimensions are out of bounds"); return false; @@ -1099,21 +1084,11 @@ bool NzImage::Update(const nzUInt8* pixels, const NzCubeui& cube, nzUInt8 level) nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format); nzUInt8* dstPixels = GetPixelPtr(m_sharedImage->pixels[level], bpp, cube.x, cube.y, cube.z, width, height); - unsigned int srcStride = cube.width * bpp; - unsigned int dstStride = width * bpp; - unsigned int faceSize = dstStride * height; - for (unsigned int z = 0; z < cube.depth; ++z) - { - nzUInt8* facePixels = dstPixels; - for (unsigned int y = 0; y < cube.height; ++y) - { - std::memcpy(facePixels, pixels, srcStride); - pixels += srcStride; - facePixels += dstStride; - } - dstPixels += faceSize; - } + Copy(dstPixels, pixels, bpp, + cube.width, cube.height, cube.depth, + width, height, + srcWidth, srcHeight); return true; } @@ -1140,13 +1115,55 @@ NzImage& NzImage::operator=(NzImage&& image) noexcept return *this; } +void NzImage::Copy(nzUInt8* destination, const nzUInt8* source, nzUInt8 bpp, unsigned int width, unsigned int height, unsigned int depth, unsigned int dstWidth, unsigned int dstHeight, unsigned int srcWidth, unsigned int srcHeight) +{ + if (dstWidth == 0) + dstWidth = width; + + if (dstHeight == 0) + dstHeight = height; + + if (srcWidth == 0) + srcWidth = width; + + if (srcHeight == 0) + srcHeight = height; + + if ((height == 1 || (dstWidth == width && srcWidth == width)) && (depth == 1 || (dstHeight == height && srcHeight == height))) + std::memcpy(destination, source, width*height*depth*bpp); + else + { + unsigned int lineStride = width * bpp; + unsigned int dstLineStride = dstWidth * bpp; + unsigned int dstFaceStride = dstLineStride * dstHeight; + unsigned int srcLineStride = srcWidth * bpp; + unsigned int srcFaceStride = srcLineStride * srcHeight; + + for (unsigned int i = 0; i < depth; ++i) + { + nzUInt8* dstFacePtr = destination; + const nzUInt8* srcFacePtr = source; + for (unsigned int y = 0; y < height; ++y) + { + std::memcpy(dstFacePtr, srcFacePtr, lineStride); + + dstFacePtr += dstLineStride; + srcFacePtr += srcLineStride; + } + + destination += dstFaceStride; + source += srcFaceStride; + } + } +} + nzUInt8 NzImage::GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth) { - static const float l2 = std::log(2.f); + static const float invLog2 = 1.f/std::log(2.f); - unsigned int widthLevel = std::log(static_cast(width))/l2; - unsigned int heightLevel = std::log(static_cast(height))/l2; - unsigned int depthLevel = std::log(static_cast(depth))/l2; + unsigned int widthLevel = invLog2 * std::log(static_cast(width)); + unsigned int heightLevel = invLog2 * std::log(static_cast(height)); + unsigned int depthLevel = invLog2 * std::log(static_cast(depth)); return std::max(std::max(std::max(widthLevel, heightLevel), depthLevel), 1U); }