Utility/Image: Add saver (allowing to save images)

Former-commit-id: df78d657256f8a6b7dad5ab11877aae7402608b3
This commit is contained in:
Lynix 2016-03-08 13:11:09 +01:00
parent 6f9d778749
commit d6ff7d065e
9 changed files with 413 additions and 10 deletions

View File

@ -22,7 +22,6 @@
namespace Nz namespace Nz
{ {
class Texture; class Texture;
using TextureConstRef = ObjectRef<const Texture>; using TextureConstRef = ObjectRef<const Texture>;
@ -94,6 +93,10 @@ namespace Nz
bool LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params = ImageParams()); bool LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params = ImageParams());
bool LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params = ImageParams()); bool LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params = ImageParams());
// Save
bool SaveToFile(const String& filePath, const ImageParams& params = ImageParams());
bool SaveToStream(Stream& stream, const String& format, const ImageParams& params = ImageParams());
bool SetMipmapRange(UInt8 minLevel, UInt8 maxLevel); bool SetMipmapRange(UInt8 minLevel, UInt8 maxLevel);
bool Update(const Image& image, UInt8 level = 0); bool Update(const Image& image, UInt8 level = 0);

View File

@ -15,6 +15,7 @@
#include <Nazara/Core/Resource.hpp> #include <Nazara/Core/Resource.hpp>
#include <Nazara/Core/ResourceLoader.hpp> #include <Nazara/Core/ResourceLoader.hpp>
#include <Nazara/Core/ResourceManager.hpp> #include <Nazara/Core/ResourceManager.hpp>
#include <Nazara/Core/ResourceSaver.hpp>
#include <Nazara/Core/Signal.hpp> #include <Nazara/Core/Signal.hpp>
#include <Nazara/Core/Stream.hpp> #include <Nazara/Core/Stream.hpp>
#include <Nazara/Utility/AbstractImage.hpp> #include <Nazara/Utility/AbstractImage.hpp>
@ -43,12 +44,14 @@ namespace Nz
using ImageLoader = ResourceLoader<Image, ImageParams>; using ImageLoader = ResourceLoader<Image, ImageParams>;
using ImageManager = ResourceManager<Image, ImageParams>; using ImageManager = ResourceManager<Image, ImageParams>;
using ImageRef = ObjectRef<Image>; using ImageRef = ObjectRef<Image>;
using ImageSaver = ResourceSaver<Image, ImageParams>;
class NAZARA_UTILITY_API Image : public AbstractImage, public RefCounted, public Resource class NAZARA_UTILITY_API Image : public AbstractImage, public RefCounted, public Resource
{ {
friend ImageLibrary; friend ImageLibrary;
friend ImageLoader; friend ImageLoader;
friend ImageManager; friend ImageManager;
friend ImageSaver;
friend class Utility; friend class Utility;
public: public:
@ -107,6 +110,17 @@ namespace Nz
bool LoadCubemapFromMemory(const void* data, std::size_t size, const ImageParams& imageParams = ImageParams(), const CubemapParams& cubemapParams = CubemapParams()); bool LoadCubemapFromMemory(const void* data, std::size_t size, const ImageParams& imageParams = ImageParams(), const CubemapParams& cubemapParams = CubemapParams());
bool LoadCubemapFromStream(Stream& stream, const ImageParams& imageParams = ImageParams(), const CubemapParams& cubemapParams = CubemapParams()); bool LoadCubemapFromStream(Stream& stream, const ImageParams& imageParams = ImageParams(), const CubemapParams& cubemapParams = CubemapParams());
// LoadFace
bool LoadFaceFromFile(CubemapFace face, const String& filePath, const ImageParams& params = ImageParams());
bool LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params = ImageParams());
bool LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params = ImageParams());
// Save
bool SaveToFile(const String& filePath, const ImageParams& params = ImageParams());
bool SaveToStream(Stream& stream, const String& format, const ImageParams& params = ImageParams());
//TODO: SaveArray, SaveCubemap, SaveFace
void SetLevelCount(UInt8 levelCount); void SetLevelCount(UInt8 levelCount);
bool SetPixelColor(const Color& color, unsigned int x, unsigned int y = 0, unsigned int z = 0); bool SetPixelColor(const Color& color, unsigned int x, unsigned int y = 0, unsigned int z = 0);
@ -165,6 +179,7 @@ namespace Nz
static ImageLoader::LoaderList s_loaders; static ImageLoader::LoaderList s_loaders;
static ImageManager::ManagerMap s_managerMap; static ImageManager::ManagerMap s_managerMap;
static ImageManager::ManagerParams s_managerParameters; static ImageManager::ManagerParams s_managerParameters;
static ImageSaver::SaverList s_savers;
}; };
} }

View File

@ -825,6 +825,30 @@ namespace Nz
return Update(image, Rectui(0, 0, faceSize, faceSize), face); return Update(image, Rectui(0, 0, faceSize, faceSize), face);
} }
bool Texture::SaveToFile(const String& filePath, const ImageParams& params)
{
Image image;
if (!Download(&image))
{
NazaraError("Failed to download texture");
return false;
}
return image.SaveToFile(filePath, params);
}
bool Texture::SaveToStream(Stream& stream, const String& format, const ImageParams& params)
{
Image image;
if (!Download(&image))
{
NazaraError("Failed to download texture");
return false;
}
return image.SaveToStream(stream, format, params);
}
bool Texture::SetMipmapRange(UInt8 minLevel, UInt8 maxLevel) bool Texture::SetMipmapRange(UInt8 minLevel, UInt8 maxLevel)
{ {
#if NAZARA_RENDERER_SAFE #if NAZARA_RENDERER_SAFE

View File

@ -87,12 +87,12 @@ namespace Nz
namespace Loaders namespace Loaders
{ {
void RegisterSTB() void RegisterSTBLoader()
{ {
ImageLoader::RegisterLoader(IsSupported, Check, Load); ImageLoader::RegisterLoader(IsSupported, Check, Load);
} }
void UnregisterSTB() void UnregisterSTBLoader()
{ {
ImageLoader::UnregisterLoader(IsSupported, Check, Load); ImageLoader::UnregisterLoader(IsSupported, Check, Load);
} }

View File

@ -4,8 +4,8 @@
#pragma once #pragma once
#ifndef NAZARA_LOADERS_STB_HPP #ifndef NAZARA_FORMATS_STBLOADER_HPP
#define NAZARA_LOADERS_STB_HPP #define NAZARA_FORMATS_STBLOADER_HPP
#include <Nazara/Prerequesites.hpp> #include <Nazara/Prerequesites.hpp>
@ -13,9 +13,9 @@ namespace Nz
{ {
namespace Loaders namespace Loaders
{ {
void RegisterSTB(); void RegisterSTBLoader();
void UnregisterSTB(); void UnregisterSTBLoader();
} }
} }
#endif // NAZARA_LOADERS_STB_HPP #endif // NAZARA_FORMATS_STBLOADER_HPP

View File

@ -0,0 +1,242 @@
// Copyright (C) 2015 Jérôme Leclercq
// 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/Formats/STBLoader.hpp>
#include <stb/stb_image_write.h>
#include <Nazara/Utility/Image.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <map>
#include <stdexcept>
#include <Nazara/Utility/Debug.hpp>
namespace Nz
{
namespace
{
using FormatHandler = bool(*)(const Image& image, const ImageParams& parameters, Stream& stream);
std::map<String, FormatHandler> s_formatHandlers;
int ConvertToFloatFormat(Image& image)
{
switch (image.GetFormat())
{
case PixelFormatType_R32F:
return 1;
case PixelFormatType_RG32F:
return 2;
case PixelFormatType_RGB32F:
return 3;
case PixelFormatType_RGBA32F:
return 4;
default:
{
if (PixelFormat::HasAlpha(image.GetFormat()))
{
if (!image.Convert(PixelFormatType_RGBA32F))
break;
return 4;
}
else
{
if (!image.Convert(PixelFormatType_RGB32F))
break;
return 3;
}
}
}
return 0;
}
int ConvertToIntegerFormat(Image& image)
{
switch (image.GetFormat())
{
case PixelFormatType_L8:
case PixelFormatType_R8:
return 1;
case PixelFormatType_LA8:
case PixelFormatType_RG8:
return 2;
case PixelFormatType_RGB8:
return 3;
case PixelFormatType_RGBA8:
return 4;
default:
{
if (PixelFormat::HasAlpha(image.GetFormat()))
{
if (!image.Convert(PixelFormatType_RGBA8))
break;
return 4;
}
else
{
if (!image.Convert(PixelFormatType_RGB8))
break;
return 3;
}
}
}
return 0;
}
void WriteToStream(void* userdata, void* data, int size)
{
Stream* stream = static_cast<Stream*>(userdata);
if (stream->Write(data, size) != size)
throw std::runtime_error("Failed to write to stream");
}
bool FormatQuerier(const String& extension)
{
return s_formatHandlers.find(extension) != s_formatHandlers.end();
}
bool SaveToStream(const Image& image, const String& format, Stream& stream, const ImageParams& parameters)
{
NazaraUnused(parameters);
if (!image.IsValid())
{
NazaraError("Invalid image");
return false;
}
ImageType type = image.GetType();
if (type != ImageType_1D && type != ImageType_2D)
{
NazaraError("Image type 0x" + String::Number(type, 16) + " is not ");
return false;
}
auto it = s_formatHandlers.find(format);
NazaraAssert(it != s_formatHandlers.end(), "Invalid handler");
const FormatHandler& handler = it->second;
try
{
return handler(image, parameters, stream);
}
catch (const std::exception& e)
{
NazaraError(e.what());
return false;
}
}
bool SaveBMP(const Image& image, const ImageParams& parameters, Stream& stream)
{
Image tempImage(image); //< We're using COW here to prevent Image copy unless required
int componentCount = ConvertToIntegerFormat(tempImage);
if (componentCount == 0)
{
NazaraError("Failed to convert image to suitable format");
return false;
}
if (!stbi_write_bmp_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, tempImage.GetConstPixels()))
{
NazaraError("Failed to write BMP to stream");
return false;
}
return true;
}
bool SaveHDR(const Image& image, const ImageParams& parameters, Stream& stream)
{
Image tempImage(image); //< We're using COW here to prevent Image copy unless required
int componentCount = ConvertToFloatFormat(tempImage);
if (componentCount == 0)
{
NazaraError("Failed to convert image to suitable format");
return false;
}
if (!stbi_write_hdr_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, reinterpret_cast<const float*>(tempImage.GetConstPixels())))
{
NazaraError("Failed to write BMP to stream");
return false;
}
return true;
}
bool SavePNG(const Image& image, const ImageParams& parameters, Stream& stream)
{
Image tempImage(image); //< We're using COW here to prevent Image copy unless required
int componentCount = ConvertToIntegerFormat(tempImage);
if (componentCount == 0)
{
NazaraError("Failed to convert image to suitable format");
return false;
}
if (!stbi_write_png_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, tempImage.GetConstPixels(), 0))
{
NazaraError("Failed to write BMP to stream");
return false;
}
return true;
}
bool SaveTGA(const Image& image, const ImageParams& parameters, Stream& stream)
{
Image tempImage(image); //< We're using COW here to prevent Image copy unless required
int componentCount = ConvertToIntegerFormat(tempImage);
if (componentCount == 0)
{
NazaraError("Failed to convert image to suitable format");
return false;
}
if (!stbi_write_tga_to_func(&WriteToStream, &stream, tempImage.GetWidth(), tempImage.GetHeight(), componentCount, tempImage.GetConstPixels()))
{
NazaraError("Failed to write BMP to stream");
return false;
}
return true;
}
}
namespace Loaders
{
void RegisterSTBSaver()
{
s_formatHandlers["bmp"] = &SaveBMP;
s_formatHandlers["hdr"] = &SaveHDR;
s_formatHandlers["png"] = &SavePNG;
s_formatHandlers["tga"] = &SaveTGA;
ImageSaver::RegisterSaver(FormatQuerier, SaveToStream);
}
void UnregisterSTBSaver()
{
ImageSaver::UnregisterSaver(FormatQuerier, SaveToStream);
s_formatHandlers.clear();
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Utility module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_FORMATS_STBSAVER_HPP
#define NAZARA_FORMATS_STBSAVER_HPP
#include <Nazara/Prerequesites.hpp>
namespace Nz
{
namespace Loaders
{
void RegisterSTBSaver();
void UnregisterSTBSaver();
}
}
#endif // NAZARA_FORMATS_STBSAVER_HPP

View File

@ -1066,6 +1066,100 @@ namespace Nz
return LoadCubemapFromImage(image, cubemapParams); return LoadCubemapFromImage(image, cubemapParams);
} }
bool Image::LoadFaceFromFile(CubemapFace face, const String& filePath, const ImageParams& params)
{
NazaraAssert(IsValid() && IsCubemap(), "Texture must be a valid cubemap");
Image image;
if (!image.LoadFromFile(filePath, params))
{
NazaraError("Failed to load image");
return false;
}
if (!image.Convert(GetFormat()))
{
NazaraError("Failed to convert image to texture format");
return false;
}
unsigned int faceSize = GetWidth();
if (image.GetWidth() != faceSize || image.GetHeight() != faceSize)
{
NazaraError("Image size must match texture face size");
return false;
}
Copy(image, Rectui(0, 0, faceSize, faceSize), Vector3ui(0, 0, face));
return true;
}
bool Image::LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params)
{
NazaraAssert(IsValid() && IsCubemap(), "Texture must be a valid cubemap");
Image image;
if (!image.LoadFromMemory(data, size, params))
{
NazaraError("Failed to load image");
return false;
}
if (!image.Convert(GetFormat()))
{
NazaraError("Failed to convert image to texture format");
return false;
}
unsigned int faceSize = GetWidth();
if (image.GetWidth() != faceSize || image.GetHeight() != faceSize)
{
NazaraError("Image size must match texture face size");
return false;
}
Copy(image, Rectui(0, 0, faceSize, faceSize), Vector3ui(0, 0, face));
return true;
}
bool Image::LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params)
{
NazaraAssert(IsValid() && IsCubemap(), "Texture must be a valid cubemap");
Image image;
if (!image.LoadFromStream(stream, params))
{
NazaraError("Failed to load image");
return false;
}
if (!image.Convert(GetFormat()))
{
NazaraError("Failed to convert image to texture format");
return false;
}
unsigned int faceSize = GetWidth();
if (image.GetWidth() != faceSize || image.GetHeight() != faceSize)
{
NazaraError("Image size must match texture face size");
return false;
}
Copy(image, Rectui(0, 0, faceSize, faceSize), Vector3ui(0, 0, face));
return true;
}
bool Image::SaveToFile(const String& filePath, const ImageParams& params)
{
return ImageSaver::SaveToFile(*this, filePath, params);
}
bool Image::SaveToStream(Stream& stream, const String& format, const ImageParams& params)
{
return ImageSaver::SaveToStream(*this, stream, format, params);
}
void Image::SetLevelCount(UInt8 levelCount) void Image::SetLevelCount(UInt8 levelCount)
{ {
#if NAZARA_UTILITY_SAFE #if NAZARA_UTILITY_SAFE
@ -1377,4 +1471,5 @@ namespace Nz
ImageLoader::LoaderList Image::s_loaders; ImageLoader::LoaderList Image::s_loaders;
ImageManager::ManagerMap Image::s_managerMap; ImageManager::ManagerMap Image::s_managerMap;
ImageManager::ManagerParams Image::s_managerParameters; ImageManager::ManagerParams Image::s_managerParameters;
ImageSaver::SaverList Image::s_savers;
} }

View File

@ -26,6 +26,7 @@
#include <Nazara/Utility/Formats/MD5MeshLoader.hpp> #include <Nazara/Utility/Formats/MD5MeshLoader.hpp>
#include <Nazara/Utility/Formats/PCXLoader.hpp> #include <Nazara/Utility/Formats/PCXLoader.hpp>
#include <Nazara/Utility/Formats/STBLoader.hpp> #include <Nazara/Utility/Formats/STBLoader.hpp>
#include <Nazara/Utility/Formats/STBSaver.hpp>
#include <Nazara/Utility/Debug.hpp> #include <Nazara/Utility/Debug.hpp>
namespace Nz namespace Nz
@ -112,7 +113,8 @@ namespace Nz
Loaders::RegisterFreeType(); Loaders::RegisterFreeType();
// Image // Image
Loaders::RegisterSTB(); // Loader générique (STB) Loaders::RegisterSTBLoader(); // Generic loader (STB)
Loaders::RegisterSTBSaver(); // Generic saver (STB)
/// Loaders spécialisés /// Loaders spécialisés
// Animation // Animation
@ -155,7 +157,8 @@ namespace Nz
Loaders::UnregisterMD5Anim(); Loaders::UnregisterMD5Anim();
Loaders::UnregisterMD5Mesh(); Loaders::UnregisterMD5Mesh();
Loaders::UnregisterPCX(); Loaders::UnregisterPCX();
Loaders::UnregisterSTB(); Loaders::UnregisterSTBLoader();
Loaders::UnregisterSTBSaver();
Window::Uninitialize(); Window::Uninitialize();
VertexDeclaration::Uninitialize(); VertexDeclaration::Uninitialize();