<commit_before>// Copyright (C) 2014 <NAME>
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/Image.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Utility/Config.hpp>
#include <cmath>
#include <memory>
#include <stdexcept>
#include <Nazara/Utility/Debug.hpp>

///TODO: Rajouter des warnings (Formats compressés avec les méthodes Copy/Update, tests taille dans Copy)
///TODO: Rendre les méthodes exception-safe

namespace
{
	inline unsigned int GetLevelSize(unsigned int size, nzUInt8 level)
	{
		return std::max(size >> level, 1U);
	}

	inline nzUInt8* GetPixelPtr(nzUInt8* base, nzUInt8 bpp, unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height)
	{
		return &base[(width*(height*z + y) + x)*bpp];
	}
}

bool NzImageParams::IsValid() const
{
	return true;
}

NzImage::NzImage() :
m_sharedImage(&emptyImage)
{
}

NzImage::NzImage(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) :
m_sharedImage(&emptyImage)
{
	Create(type, format, width, height, depth, levelCount);

	#ifdef NAZARA_DEBUG
	if (!m_sharedImage)
	{
		NazaraError("Failed to create image");
		throw std::runtime_error("Constructor failed");
	}
	#endif
}

NzImage::NzImage(const NzImage& image) :
NzResource(),
m_sharedImage(image.m_sharedImage)
{
	if (m_sharedImage != &emptyImage)
		m_sharedImage->refCount++;
}

NzImage::NzImage(NzImage&& image) noexcept :
m_sharedImage(image.m_sharedImage)
{
	image.m_sharedImage = &emptyImage;
}

NzImage::NzImage(SharedImage* sharedImage) :
m_sharedImage(sharedImage)
{
}

NzImage::~NzImage()
{
	Destroy();
}

bool NzImage::Convert(nzPixelFormat format)
{
	#if NAZARA_UTILITY_SAFE
	if (m_sharedImage == &emptyImage)
	{
		NazaraError("Image must be valid");
		return false;
	}

	if (!NzPixelFormat::IsValid(format))
	{
		NazaraError("Invalid pixel format");
		return false;
	}

	if (!NzPixelFormat::IsConversionSupported(m_sharedImage->format, format))
	{
		NazaraError("Conversion from " + NzPixelFormat::ToString(m_sharedImage->format) + " to " + NzPixelFormat::ToString(format) + " is not supported");
		return false;
	}
	#endif

	if (format == m_sharedImage->format)
		return true;

	nzUInt8** levels = new nzUInt8*[m_sharedImage->levelCount];

	unsigned int width = m_sharedImage->width;
	unsigned int height = m_sharedImage->height;

	// Les images 3D et cubemaps sont stockés de la même façon
	unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth;

	for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i)
	{
		unsigned int pixelsPerFace = width*height;
		nzUInt8* face = new nzUInt8[pixelsPerFace*depth*NzPixelFormat::GetBytesPerPixel(format)];
		nzUInt8* ptr = face;
		nzUInt8* pixels = m_sharedImage->pixels[i];
		unsigned int srcStride = pixelsPerFace * NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
		unsigned int dstStride = pixelsPerFace * NzPixelFormat::GetBytesPerPixel(format);

		for (unsigned int d = 0; d < depth; ++d)
		{
			if (!NzPixelFormat::Convert(m_sharedImage->format, format, pixels, &pixels[srcStride], ptr))
			{
				NazaraError("Failed to convert image");

				// Nettoyage de la mémoire
				delete[] face; // Permet une optimisation de boucle (GCC)
				for (unsigned int j = 0; j < i; ++j)
					delete[] levels[j];

				delete[] levels;

				return false;
			}

			pixels += srcStride;
			ptr += dstStride;
		}

		levels[i] = face;

		if (width > 1)
			width >>= 1;

		if (height > 1)
			height >>= 1;

		if (depth > 1 && m_sharedImage->type != nzImageType_Cubemap)
			depth >>= 1;
	}

	SharedImage* newImage = new SharedImage(1, m_sharedImage->type, format, m_sharedImage->levelCount, levels, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth);

	ReleaseImage();
	m_sharedImage = newImage;

	return true;
}

void NzImage::Copy(const NzImage& source, const NzBoxui& srcBox, const NzVector3ui& dstPos)
{
	#if NAZARA_UTILITY_SAFE
	if (m_sharedImage == &emptyImage)
	{
		NazaraError("Image must be valid");
		return;
	}

	if (!source.IsValid())
	{
		NazaraError("Source image must be valid");
		return;
	}

	if (source.GetFormat() != m_sharedImage->format)
	{
		NazaraError("Source image format does not match destination image format");
		return;
	}
	#endif

	const nzUInt8* srcPtr = source.GetConstPixels(srcBox.x, srcBox.y, srcBox.z);
	#if NAZARA_UTILITY_SAFE
	if (!srcPtr)
	{
		NazaraError("Failed to access pixels");
		return;
	}
	#endif

	nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
	nzUInt8* dstPtr = GetPixelPtr(m_sharedImage->pixels[0], bpp, dstPos.x, dstPos.y, dstPos.z, m_sharedImage->width, m_sharedImage->height);

	Copy(dstPtr, srcPtr, bpp, srcBox.width, srcBox.height, srcBox.depth, m_sharedImage->width, m_sharedImage->height, source.GetWidth(), source.GetHeight());
}

bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount)
{
	ReleaseImage();

	#if NAZARA_UTILITY_SAFE
	if (!NzPixelFormat::IsValid(format))
	{
		NazaraError("Invalid pixel format");
		return false;
	}
<commit_msg>Fixed Image::Get[Width|Height|Depth] returning 1 on invalid images<commit_after>// Copyright (C) 2014 <NAME>
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Utility/Image.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Utility/Config.hpp>
#include <cmath>
#include <memory>
#include <stdexcept>
#include <Nazara/Utility/Debug.hpp>

///TODO: Rajouter des warnings (Formats compressés avec les méthodes Copy/Update, tests taille dans Copy)
///TODO: Rendre les méthodes exception-safe (Virer toute cette merde de calcul de pointeurs, faire usage du RAII)
///FIXME: Gérer correctement les formats utilisant moins d'un octet par pixel

namespace
{
	inline unsigned int GetLevelSize(unsigned int size, nzUInt8 level)
	{
		if (size == 0) // Possible dans le cas d'une image invalide
			return 0;

		return std::max(size >> level, 1U);
	}

	inline nzUInt8* GetPixelPtr(nzUInt8* base, nzUInt8 bpp, unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height)
	{
		return &base[(width*(height*z + y) + x)*bpp];
	}
}

bool NzImageParams::IsValid() const
{
	return true; // Rien à vérifier
}

NzImage::NzImage() :
m_sharedImage(&emptyImage)
{
}

NzImage::NzImage(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount) :
m_sharedImage(&emptyImage)
{
	Create(type, format, width, height, depth, levelCount);

	#ifdef NAZARA_DEBUG
	if (!m_sharedImage)
	{
		NazaraError("Failed to create image");
		throw std::runtime_error("Constructor failed");
	}
	#endif
}

NzImage::NzImage(const NzImage& image) :
NzResource(),
m_sharedImage(image.m_sharedImage)
{
	if (m_sharedImage != &emptyImage)
		m_sharedImage->refCount++;
}

NzImage::NzImage(NzImage&& image) noexcept :
m_sharedImage(image.m_sharedImage)
{
	image.m_sharedImage = &emptyImage;
}

NzImage::NzImage(SharedImage* sharedImage) :
m_sharedImage(sharedImage)
{
}

NzImage::~NzImage()
{
	Destroy();
}

bool NzImage::Convert(nzPixelFormat format)
{
	#if NAZARA_UTILITY_SAFE
	if (m_sharedImage == &emptyImage)
	{
		NazaraError("Image must be valid");
		return false;
	}

	if (!NzPixelFormat::IsValid(format))
	{
		NazaraError("Invalid pixel format");
		return false;
	}

	if (!NzPixelFormat::IsConversionSupported(m_sharedImage->format, format))
	{
		NazaraError("Conversion from " + NzPixelFormat::ToString(m_sharedImage->format) + " to " + NzPixelFormat::ToString(format) + " is not supported");
		return false;
	}
	#endif

	if (format == m_sharedImage->format)
		return true;

	nzUInt8** levels = new nzUInt8*[m_sharedImage->levelCount];

	unsigned int width = m_sharedImage->width;
	unsigned int height = m_sharedImage->height;

	// Les images 3D et cubemaps sont stockés de la même façon
	unsigned int depth = (m_sharedImage->type == nzImageType_Cubemap) ? 6 : m_sharedImage->depth;

	for (unsigned int i = 0; i < m_sharedImage->levelCount; ++i)
	{
		unsigned int pixelsPerFace = width*height;
		nzUInt8* face = new nzUInt8[pixelsPerFace*depth*NzPixelFormat::GetBytesPerPixel(format)];
		nzUInt8* ptr = face;
		nzUInt8* pixels = m_sharedImage->pixels[i];
		unsigned int srcStride = pixelsPerFace * NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
		unsigned int dstStride = pixelsPerFace * NzPixelFormat::GetBytesPerPixel(format);

		for (unsigned int d = 0; d < depth; ++d)
		{
			if (!NzPixelFormat::Convert(m_sharedImage->format, format, pixels, &pixels[srcStride], ptr))
			{
				NazaraError("Failed to convert image");

				// Nettoyage de la mémoire
				delete[] face; // Permet une optimisation de boucle (GCC)
				for (unsigned int j = 0; j < i; ++j)
					delete[] levels[j];

				delete[] levels;

				return false;
			}

			pixels += srcStride;
			ptr += dstStride;
		}

		levels[i] = face;

		if (width > 1)
			width >>= 1;

		if (height > 1)
			height >>= 1;

		if (depth > 1 && m_sharedImage->type != nzImageType_Cubemap)
			depth >>= 1;
	}

	SharedImage* newImage = new SharedImage(1, m_sharedImage->type, format, m_sharedImage->levelCount, levels, m_sharedImage->width, m_sharedImage->height, m_sharedImage->depth);

	ReleaseImage();
	m_sharedImage = newImage;

	return true;
}

void NzImage::Copy(const NzImage& source, const NzBoxui& srcBox, const NzVector3ui& dstPos)
{
	#if NAZARA_UTILITY_SAFE
	if (m_sharedImage == &emptyImage)
	{
		NazaraError("Image must be valid");
		return;
	}

	if (!source.IsValid())
	{
		NazaraError("Source image must be valid");
		return;
	}

	if (source.GetFormat() != m_sharedImage->format)
	{
		NazaraError("Source image format does not match destination image format");
		return;
	}
	#endif

	const nzUInt8* srcPtr = source.GetConstPixels(srcBox.x, srcBox.y, srcBox.z);
	#if NAZARA_UTILITY_SAFE
	if (!srcPtr)
	{
		NazaraError("Failed to access pixels");
		return;
	}
	#endif

	nzUInt8 bpp = NzPixelFormat::GetBytesPerPixel(m_sharedImage->format);
	nzUInt8* dstPtr = GetPixelPtr(m_sharedImage->pixels[0], bpp, dstPos.x, dstPos.y, dstPos.z, m_sharedImage->width, m_sharedImage->height);

	Copy(dstPtr, srcPtr, bpp, srcBox.width, srcBox.height, srcBox.depth, m_sharedImage->width, m_sharedImage->height, source.GetWidth(), source.GetHeight());
}

bool NzImage::Create(nzImageType type, nzPixelFormat format, unsigned int width, unsigned int height, unsigned int depth, nzUInt8 levelCount)
{
	Destroy();

	#if NAZARA_UTILITY_SAFE
	if (!NzPixelFormat::IsValid(format))
	{
		NazaraError("Invalid pixel format");
		return false;
	}

{'fraction_non_alphanumeric': 0.12735542560103963, 'fraction_numerical': 0.006590550450199573, 'mean_word_length': 4.995548135781859, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4796190', 'n_tokens_mistral': 4081, 'n_tokens_neox': 3807, 'n_words': 942}