Utility/Image: Add saver (allowing to save images)
Former-commit-id: df78d657256f8a6b7dad5ab11877aae7402608b3
This commit is contained in:
parent
6f9d778749
commit
d6ff7d065e
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
namespace Nz
|
||||
{
|
||||
|
||||
class 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 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 Update(const Image& image, UInt8 level = 0);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <Nazara/Core/Resource.hpp>
|
||||
#include <Nazara/Core/ResourceLoader.hpp>
|
||||
#include <Nazara/Core/ResourceManager.hpp>
|
||||
#include <Nazara/Core/ResourceSaver.hpp>
|
||||
#include <Nazara/Core/Signal.hpp>
|
||||
#include <Nazara/Core/Stream.hpp>
|
||||
#include <Nazara/Utility/AbstractImage.hpp>
|
||||
|
|
@ -43,12 +44,14 @@ namespace Nz
|
|||
using ImageLoader = ResourceLoader<Image, ImageParams>;
|
||||
using ImageManager = ResourceManager<Image, ImageParams>;
|
||||
using ImageRef = ObjectRef<Image>;
|
||||
using ImageSaver = ResourceSaver<Image, ImageParams>;
|
||||
|
||||
class NAZARA_UTILITY_API Image : public AbstractImage, public RefCounted, public Resource
|
||||
{
|
||||
friend ImageLibrary;
|
||||
friend ImageLoader;
|
||||
friend ImageManager;
|
||||
friend ImageSaver;
|
||||
friend class Utility;
|
||||
|
||||
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 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);
|
||||
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 ImageManager::ManagerMap s_managerMap;
|
||||
static ImageManager::ManagerParams s_managerParameters;
|
||||
static ImageSaver::SaverList s_savers;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -825,6 +825,30 @@ namespace Nz
|
|||
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)
|
||||
{
|
||||
#if NAZARA_RENDERER_SAFE
|
||||
|
|
|
|||
|
|
@ -87,12 +87,12 @@ namespace Nz
|
|||
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterSTB()
|
||||
void RegisterSTBLoader()
|
||||
{
|
||||
ImageLoader::RegisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
||||
void UnregisterSTB()
|
||||
void UnregisterSTBLoader()
|
||||
{
|
||||
ImageLoader::UnregisterLoader(IsSupported, Check, Load);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_LOADERS_STB_HPP
|
||||
#define NAZARA_LOADERS_STB_HPP
|
||||
#ifndef NAZARA_FORMATS_STBLOADER_HPP
|
||||
#define NAZARA_FORMATS_STBLOADER_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
|
||||
|
|
@ -13,9 +13,9 @@ namespace Nz
|
|||
{
|
||||
namespace Loaders
|
||||
{
|
||||
void RegisterSTB();
|
||||
void UnregisterSTB();
|
||||
void RegisterSTBLoader();
|
||||
void UnregisterSTBLoader();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NAZARA_LOADERS_STB_HPP
|
||||
#endif // NAZARA_FORMATS_STBLOADER_HPP
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1066,6 +1066,100 @@ namespace Nz
|
|||
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)
|
||||
{
|
||||
#if NAZARA_UTILITY_SAFE
|
||||
|
|
@ -1377,4 +1471,5 @@ namespace Nz
|
|||
ImageLoader::LoaderList Image::s_loaders;
|
||||
ImageManager::ManagerMap Image::s_managerMap;
|
||||
ImageManager::ManagerParams Image::s_managerParameters;
|
||||
ImageSaver::SaverList Image::s_savers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <Nazara/Utility/Formats/MD5MeshLoader.hpp>
|
||||
#include <Nazara/Utility/Formats/PCXLoader.hpp>
|
||||
#include <Nazara/Utility/Formats/STBLoader.hpp>
|
||||
#include <Nazara/Utility/Formats/STBSaver.hpp>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
|
|
@ -112,7 +113,8 @@ namespace Nz
|
|||
Loaders::RegisterFreeType();
|
||||
|
||||
// Image
|
||||
Loaders::RegisterSTB(); // Loader générique (STB)
|
||||
Loaders::RegisterSTBLoader(); // Generic loader (STB)
|
||||
Loaders::RegisterSTBSaver(); // Generic saver (STB)
|
||||
|
||||
/// Loaders spécialisés
|
||||
// Animation
|
||||
|
|
@ -155,7 +157,8 @@ namespace Nz
|
|||
Loaders::UnregisterMD5Anim();
|
||||
Loaders::UnregisterMD5Mesh();
|
||||
Loaders::UnregisterPCX();
|
||||
Loaders::UnregisterSTB();
|
||||
Loaders::UnregisterSTBLoader();
|
||||
Loaders::UnregisterSTBSaver();
|
||||
|
||||
Window::Uninitialize();
|
||||
VertexDeclaration::Uninitialize();
|
||||
|
|
|
|||
Loading…
Reference in New Issue