mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
ImageData (and Images loaded from them) now support different data formats. Resolves issue #1048.
Currently exposed formats are rgba8 and rgba16 (normalized), and rgba16f and rgba32f (floating-point). Some systems, especially mobile ones, won't support every format when creating a love.graphics Image. Use love.graphics.getRawImageFormats to check for support. love.image.newImageData now takes an optional format parameter as its third argument when creating an empty sized ImageData. It defaults to rgba8. 16-bit PNGs, .hdr images, and floating-point OpenEXR images can now be loaded via love.image.newImageData and love.graphics.newImage. --HG-- branch : minor
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2016 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
// LOVE
|
||||
#include "EXRHandler.h"
|
||||
|
||||
// tinyexr
|
||||
#define TINYEXR_IMPLEMENTATION
|
||||
#include "libraries/tinyexr/tinyexr.h"
|
||||
|
||||
// C
|
||||
#include <cstdlib>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
bool EXRHandler::canDecode(love::filesystem::FileData *data)
|
||||
{
|
||||
const char *err;
|
||||
|
||||
EXRImage exrImage;
|
||||
InitEXRImage(&exrImage);
|
||||
|
||||
if (ParseMultiChannelEXRHeaderFromMemory(&exrImage, (const unsigned char *) data->getData(), &err) != 0)
|
||||
return false;
|
||||
|
||||
FreeEXRImage(&exrImage);
|
||||
|
||||
return exrImage.width > 0 && exrImage.height > 0;
|
||||
}
|
||||
|
||||
bool EXRHandler::canEncode(ImageData::Format /*rawFormat*/, ImageData::EncodedFormat /*encodedFormat*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void getEXRChannels(const EXRImage &exrImage, T *rgba[4])
|
||||
{
|
||||
for (int i = 0; i < exrImage.num_channels; i++)
|
||||
{
|
||||
if (exrImage.channel_names[i] == nullptr)
|
||||
continue;
|
||||
|
||||
switch (*exrImage.channel_names[i])
|
||||
{
|
||||
case 'R':
|
||||
rgba[0] = (T *) exrImage.images[i];
|
||||
break;
|
||||
case 'G':
|
||||
rgba[1] = (T *) exrImage.images[i];
|
||||
break;
|
||||
case 'B':
|
||||
rgba[2] = (T *) exrImage.images[i];
|
||||
break;
|
||||
case 'A':
|
||||
rgba[3] = (T *) exrImage.images[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T *loadEXRChannels(int width, int height, T *rgba[4], T one)
|
||||
{
|
||||
T *data = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
data = new T[width * height * 4];
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
throw love::Exception("Out of memory.");
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
size_t offset = y * width + x;
|
||||
|
||||
data[offset * 4 + 0] = rgba[0] != nullptr ? rgba[0][offset] : 0;
|
||||
data[offset * 4 + 1] = rgba[1] != nullptr ? rgba[1][offset] : 0;
|
||||
data[offset * 4 + 2] = rgba[2] != nullptr ? rgba[2][offset] : 0;
|
||||
data[offset * 4 + 3] = rgba[3] != nullptr ? rgba[3][offset] : one;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
FormatHandler::DecodedImage EXRHandler::decode(love::filesystem::FileData *data)
|
||||
{
|
||||
const char *err;
|
||||
auto mem = (const unsigned char *) data->getData();
|
||||
DecodedImage img;
|
||||
|
||||
EXRImage exrImage;
|
||||
InitEXRImage(&exrImage);
|
||||
|
||||
if (ParseMultiChannelEXRHeaderFromMemory(&exrImage, mem, &err) != 0)
|
||||
throw love::Exception("Could not parse EXR image: %s", err);
|
||||
|
||||
if (LoadMultiChannelEXRFromMemory(&exrImage, mem, &err) != 0)
|
||||
throw love::Exception("Could not decode EXR image: %s", err);
|
||||
|
||||
int pixelType = exrImage.pixel_types[0];
|
||||
|
||||
for (int i = 1; i < exrImage.num_channels; i++)
|
||||
{
|
||||
if (pixelType != exrImage.pixel_types[i])
|
||||
{
|
||||
FreeEXRImage(&exrImage);
|
||||
throw love::Exception("Could not decode EXR image: all channels must have the same data type.");
|
||||
}
|
||||
}
|
||||
|
||||
img.width = exrImage.width;
|
||||
img.height = exrImage.height;
|
||||
|
||||
if (pixelType == TINYEXR_PIXELTYPE_HALF)
|
||||
{
|
||||
img.format = ImageData::FORMAT_RGBA16F;
|
||||
|
||||
half *rgba[4] = {nullptr};
|
||||
getEXRChannels(exrImage, rgba);
|
||||
|
||||
try
|
||||
{
|
||||
img.data = (unsigned char *) loadEXRChannels(img.width, img.height, rgba, floatToHalf(1.0f));
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
FreeEXRImage(&exrImage);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (pixelType == TINYEXR_PIXELTYPE_FLOAT)
|
||||
{
|
||||
img.format = ImageData::FORMAT_RGBA32F;
|
||||
|
||||
float *rgba[4] = {nullptr};
|
||||
getEXRChannels(exrImage, rgba);
|
||||
|
||||
try
|
||||
{
|
||||
img.data = (unsigned char *) loadEXRChannels(img.width, img.height, rgba, 1.0f);
|
||||
}
|
||||
catch (love::Exception &)
|
||||
{
|
||||
FreeEXRImage(&exrImage);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeEXRImage(&exrImage);
|
||||
throw love::Exception("Could not decode EXR image: unknown pixel format.");
|
||||
}
|
||||
|
||||
img.size = img.width * img.height * ImageData::getPixelSize(img.format);
|
||||
|
||||
FreeEXRImage(&exrImage);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
FormatHandler::EncodedImage EXRHandler::encode(const DecodedImage & /*img*/, ImageData::EncodedFormat /*encodedFormat*/)
|
||||
{
|
||||
throw love::Exception("Invalid format.");
|
||||
}
|
||||
|
||||
void EXRHandler::free(unsigned char *mem)
|
||||
{
|
||||
delete[] mem;
|
||||
}
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2016 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#ifndef LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
|
||||
#define LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
|
||||
|
||||
#include "FormatHandler.h"
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace image
|
||||
{
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
/**
|
||||
* Interface between ImageData and TinyEXR library, for decoding exr files.
|
||||
**/
|
||||
class EXRHandler : public FormatHandler
|
||||
{
|
||||
public:
|
||||
|
||||
// Implements FormatHandler.
|
||||
|
||||
virtual bool canDecode(love::filesystem::FileData *data);
|
||||
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
|
||||
|
||||
virtual DecodedImage decode(love::filesystem::FileData *data);
|
||||
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
|
||||
|
||||
virtual void free(unsigned char *mem);
|
||||
|
||||
}; // EXRHandler
|
||||
|
||||
} // magpie
|
||||
} // image
|
||||
} // love
|
||||
|
||||
#endif // LOVE_IMAGE_MAGPIE_EXR_HANDLER_H
|
||||
@@ -42,7 +42,7 @@ bool FormatHandler::canDecode(love::filesystem::FileData* /*data*/)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FormatHandler::canEncode(ImageData::EncodedFormat /*format*/)
|
||||
bool FormatHandler::canEncode(ImageData::Format /*rawFormat*/, ImageData::EncodedFormat /*encodedFormat*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
// Raw RGBA pixel data.
|
||||
struct DecodedImage
|
||||
{
|
||||
ImageData::Format format = ImageData::FORMAT_RGBA8;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
size_t size = 0;
|
||||
@@ -75,7 +76,7 @@ public:
|
||||
/**
|
||||
* Whether this format handler can encode to a particular format.
|
||||
**/
|
||||
virtual bool canEncode(ImageData::EncodedFormat format);
|
||||
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
|
||||
|
||||
/**
|
||||
* Decodes an image from its encoded form into raw pixel data.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "PNGHandler.h"
|
||||
#include "STBHandler.h"
|
||||
#include "EXRHandler.h"
|
||||
|
||||
#include "ddsHandler.h"
|
||||
#include "PVRHandler.h"
|
||||
@@ -43,9 +44,12 @@ namespace magpie
|
||||
|
||||
Image::Image()
|
||||
{
|
||||
halfInit(); // Makes sure half-float conversions can be used.
|
||||
|
||||
formatHandlers = {
|
||||
new PNGHandler,
|
||||
new STBHandler,
|
||||
new EXRHandler,
|
||||
};
|
||||
|
||||
compressedFormatHandlers = {
|
||||
@@ -78,14 +82,14 @@ love::image::ImageData *Image::newImageData(love::filesystem::FileData *data)
|
||||
return new ImageData(formatHandlers, data);
|
||||
}
|
||||
|
||||
love::image::ImageData *Image::newImageData(int width, int height)
|
||||
love::image::ImageData *Image::newImageData(int width, int height, ImageData::Format format)
|
||||
{
|
||||
return new ImageData(formatHandlers, width, height);
|
||||
return new ImageData(formatHandlers, width, height, format);
|
||||
}
|
||||
|
||||
love::image::ImageData *Image::newImageData(int width, int height, void *data, bool own)
|
||||
love::image::ImageData *Image::newImageData(int width, int height, ImageData::Format format, void *data, bool own)
|
||||
{
|
||||
return new ImageData(formatHandlers, width, height, data, own);
|
||||
return new ImageData(formatHandlers, width, height, format, data, own);
|
||||
}
|
||||
|
||||
love::image::CompressedImageData *Image::newCompressedData(love::filesystem::FileData *data)
|
||||
|
||||
@@ -52,8 +52,8 @@ public:
|
||||
const char *getName() const;
|
||||
|
||||
love::image::ImageData *newImageData(love::filesystem::FileData *data);
|
||||
love::image::ImageData *newImageData(int width, int height);
|
||||
love::image::ImageData *newImageData(int width, int height, void *data, bool own = false);
|
||||
love::image::ImageData *newImageData(int width, int height, ImageData::Format format = ImageData::FORMAT_RGBA8);
|
||||
love::image::ImageData *newImageData(int width, int height, ImageData::Format format, void *data, bool own = false);
|
||||
|
||||
love::image::CompressedImageData *newCompressedData(love::filesystem::FileData *data);
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ namespace image
|
||||
namespace magpie
|
||||
{
|
||||
|
||||
ImageData::ImageData(std::list<FormatHandler *> formats, love::filesystem::FileData *data)
|
||||
: formatHandlers(formats)
|
||||
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, love::filesystem::FileData *data)
|
||||
: formatHandlers(formatHandlers)
|
||||
, decodeHandler(nullptr)
|
||||
{
|
||||
for (FormatHandler *handler : formatHandlers)
|
||||
@@ -38,8 +38,8 @@ ImageData::ImageData(std::list<FormatHandler *> formats, love::filesystem::FileD
|
||||
decode(data);
|
||||
}
|
||||
|
||||
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height)
|
||||
: formatHandlers(formats)
|
||||
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format)
|
||||
: formatHandlers(formatHandlers)
|
||||
, decodeHandler(nullptr)
|
||||
{
|
||||
for (FormatHandler *handler : formatHandlers)
|
||||
@@ -47,15 +47,16 @@ ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height)
|
||||
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->format = format;
|
||||
|
||||
create(width, height);
|
||||
create(width, height, format);
|
||||
|
||||
// Set to black/transparency.
|
||||
memset(data, 0, width*height*sizeof(pixel));
|
||||
memset(data, 0, getSize());
|
||||
}
|
||||
|
||||
ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height, void *data, bool own)
|
||||
: formatHandlers(formats)
|
||||
ImageData::ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format, void *data, bool own)
|
||||
: formatHandlers(formatHandlers)
|
||||
, decodeHandler(nullptr)
|
||||
{
|
||||
for (FormatHandler *handler : formatHandlers)
|
||||
@@ -63,11 +64,12 @@ ImageData::ImageData(std::list<FormatHandler *> formats, int width, int height,
|
||||
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->format = format;
|
||||
|
||||
if (own)
|
||||
this->data = (unsigned char *) data;
|
||||
else
|
||||
create(width, height, data);
|
||||
create(width, height, format, data);
|
||||
}
|
||||
|
||||
ImageData::~ImageData()
|
||||
@@ -81,11 +83,13 @@ ImageData::~ImageData()
|
||||
handler->release();
|
||||
}
|
||||
|
||||
void ImageData::create(int width, int height, void *data)
|
||||
void ImageData::create(int width, int height, Format format, void *data)
|
||||
{
|
||||
size_t datasize = width * height * getPixelSize(format);
|
||||
|
||||
try
|
||||
{
|
||||
this->data = new unsigned char[width*height*sizeof(pixel)];
|
||||
this->data = new unsigned char[datasize];
|
||||
}
|
||||
catch(std::bad_alloc &)
|
||||
{
|
||||
@@ -93,9 +97,10 @@ void ImageData::create(int width, int height, void *data)
|
||||
}
|
||||
|
||||
if (data)
|
||||
memcpy(this->data, data, width*height*sizeof(pixel));
|
||||
memcpy(this->data, data, datasize);
|
||||
|
||||
decodeHandler = nullptr;
|
||||
this->format = format;
|
||||
}
|
||||
|
||||
void ImageData::decode(love::filesystem::FileData *data)
|
||||
@@ -121,8 +126,7 @@ void ImageData::decode(love::filesystem::FileData *data)
|
||||
throw love::Exception("Could not decode file '%s' to ImageData: unsupported file format", name.c_str());
|
||||
}
|
||||
|
||||
// The decoder *must* output a 32 bits-per-pixel image.
|
||||
if (decodedimage.size != decodedimage.width*decodedimage.height*sizeof(pixel))
|
||||
if (decodedimage.size != decodedimage.width * decodedimage.height * getPixelSize(decodedimage.format))
|
||||
{
|
||||
decoder->free(decodedimage.data);
|
||||
throw love::Exception("Could not convert image!");
|
||||
@@ -134,14 +138,15 @@ void ImageData::decode(love::filesystem::FileData *data)
|
||||
else
|
||||
delete[] this->data;
|
||||
|
||||
this->width = decodedimage.width;
|
||||
this->width = decodedimage.width;
|
||||
this->height = decodedimage.height;
|
||||
this->data = decodedimage.data;
|
||||
this->data = decodedimage.data;
|
||||
this->format = decodedimage.format;
|
||||
|
||||
decodeHandler = decoder;
|
||||
}
|
||||
|
||||
love::filesystem::FileData *ImageData::encode(EncodedFormat format, const char *filename)
|
||||
love::filesystem::FileData *ImageData::encode(EncodedFormat encodedFormat, const char *filename)
|
||||
{
|
||||
FormatHandler *encoder = nullptr;
|
||||
FormatHandler::EncodedImage encodedimage;
|
||||
@@ -149,12 +154,12 @@ love::filesystem::FileData *ImageData::encode(EncodedFormat format, const char *
|
||||
|
||||
rawimage.width = width;
|
||||
rawimage.height = height;
|
||||
rawimage.size = width*height*sizeof(pixel);
|
||||
rawimage.size = getSize();
|
||||
rawimage.data = data;
|
||||
|
||||
for (FormatHandler *handler : formatHandlers)
|
||||
{
|
||||
if (handler->canEncode(format))
|
||||
if (handler->canEncode(format, encodedFormat))
|
||||
{
|
||||
encoder = handler;
|
||||
break;
|
||||
@@ -164,7 +169,7 @@ love::filesystem::FileData *ImageData::encode(EncodedFormat format, const char *
|
||||
if (encoder != nullptr)
|
||||
{
|
||||
thread::Lock lock(mutex);
|
||||
encodedimage = encoder->encode(rawimage, format);
|
||||
encodedimage = encoder->encode(rawimage, encodedFormat);
|
||||
}
|
||||
|
||||
if (encoder == nullptr || encodedimage.data == nullptr)
|
||||
|
||||
@@ -39,18 +39,18 @@ class ImageData : public love::image::ImageData
|
||||
{
|
||||
public:
|
||||
|
||||
ImageData(std::list<FormatHandler *> formats, love::filesystem::FileData *data);
|
||||
ImageData(std::list<FormatHandler *> formats, int width, int height);
|
||||
ImageData(std::list<FormatHandler *> formats, int width, int height, void *data, bool own);
|
||||
ImageData(std::list<FormatHandler *> formatHandlers, love::filesystem::FileData *data);
|
||||
ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format = FORMAT_RGBA8);
|
||||
ImageData(std::list<FormatHandler *> formatHandlers, int width, int height, Format format, void *data, bool own);
|
||||
virtual ~ImageData();
|
||||
|
||||
// Implements image::ImageData.
|
||||
virtual love::filesystem::FileData *encode(EncodedFormat format, const char *filename);
|
||||
virtual love::filesystem::FileData *encode(EncodedFormat encodedFormat, const char *filename);
|
||||
|
||||
private:
|
||||
|
||||
// Create imagedata. Initialize with data if not null.
|
||||
void create(int width, int height, void *data = 0);
|
||||
void create(int width, int height, Format format, void *data = nullptr);
|
||||
|
||||
// Decode and load an encoded format.
|
||||
void decode(love::filesystem::FileData *data);
|
||||
|
||||
@@ -140,9 +140,10 @@ bool PNGHandler::canDecode(love::filesystem::FileData *data)
|
||||
return status == 0 && width > 0 && height > 0;
|
||||
}
|
||||
|
||||
bool PNGHandler::canEncode(ImageData::EncodedFormat format)
|
||||
bool PNGHandler::canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat)
|
||||
{
|
||||
return format == ImageData::ENCODED_PNG;
|
||||
return encodedFormat == ImageData::ENCODED_PNG
|
||||
&& (rawFormat == ImageData::FORMAT_RGBA8 || rawFormat == ImageData::FORMAT_RGBA16);
|
||||
}
|
||||
|
||||
PNGHandler::DecodedImage PNGHandler::decode(love::filesystem::FileData *fdata)
|
||||
@@ -154,14 +155,23 @@ PNGHandler::DecodedImage PNGHandler::decode(love::filesystem::FileData *fdata)
|
||||
DecodedImage img;
|
||||
|
||||
lodepng::State state;
|
||||
unsigned status = lodepng_inspect(&width, &height, &state, indata, insize);
|
||||
|
||||
state.info_raw.colortype = LCT_RGBA;
|
||||
state.info_raw.bitdepth = 8;
|
||||
if (status != 0)
|
||||
{
|
||||
const char *err = lodepng_error_text(status);
|
||||
throw love::Exception("Could not decode PNG image (%s)", err);
|
||||
}
|
||||
|
||||
state.decoder.zlibsettings.custom_zlib = zlibDecompress;
|
||||
state.info_raw.colortype = LCT_RGBA;
|
||||
|
||||
unsigned status = lodepng_decode(&img.data, &width, &height,
|
||||
&state, indata, insize);
|
||||
if (state.info_png.color.bitdepth == 16)
|
||||
state.info_raw.bitdepth = 16;
|
||||
else
|
||||
state.info_raw.bitdepth = 8;
|
||||
|
||||
status = lodepng_decode(&img.data, &width, &height, &state, indata, insize);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
@@ -171,14 +181,27 @@ PNGHandler::DecodedImage PNGHandler::decode(love::filesystem::FileData *fdata)
|
||||
|
||||
img.width = (int) width;
|
||||
img.height = (int) height;
|
||||
img.size = width * height * 4;
|
||||
img.size = width * height * (state.info_raw.bitdepth * 4 / 8);
|
||||
img.format = state.info_raw.bitdepth == 16 ? ImageData::FORMAT_RGBA16 : ImageData::FORMAT_RGBA8;
|
||||
|
||||
// LodePNG keeps raw 16 bit images stored as big-endian.
|
||||
#ifndef LOVE_BIG_ENDIAN
|
||||
if (state.info_raw.bitdepth == 16)
|
||||
{
|
||||
uint16 *pixeldata = (uint16 *) img.data;
|
||||
uint16 numpixelcomponents = img.size / sizeof(uint16);
|
||||
|
||||
for (size_t i = 0; i < numpixelcomponents; i++)
|
||||
pixeldata[i] = swapuint16(pixeldata[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
PNGHandler::EncodedImage PNGHandler::encode(const DecodedImage &img, ImageData::EncodedFormat format)
|
||||
PNGHandler::EncodedImage PNGHandler::encode(const DecodedImage &img, ImageData::EncodedFormat encodedFormat)
|
||||
{
|
||||
if (format != ImageData::ENCODED_PNG)
|
||||
if (!canEncode(img.format, encodedFormat))
|
||||
throw love::Exception("PNG encoder cannot encode to non-PNG format.");
|
||||
|
||||
EncodedImage encimg;
|
||||
@@ -186,11 +209,10 @@ PNGHandler::EncodedImage PNGHandler::encode(const DecodedImage &img, ImageData::
|
||||
lodepng::State state;
|
||||
|
||||
state.info_raw.colortype = LCT_RGBA;
|
||||
state.info_raw.bitdepth = 8;
|
||||
state.info_raw.bitdepth = img.format == ImageData::FORMAT_RGBA16 ? 16 : 8;
|
||||
|
||||
// TODO: support plain RGB (24-bit) encoding in the future?
|
||||
state.info_png.color.colortype = LCT_RGBA;
|
||||
state.info_png.color.bitdepth = 8;
|
||||
state.info_png.color.bitdepth = state.info_raw.bitdepth;
|
||||
|
||||
state.encoder.zlibsettings.custom_zlib = zlibCompress;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
// Implements FormatHandler.
|
||||
|
||||
virtual bool canDecode(love::filesystem::FileData *data);
|
||||
virtual bool canEncode(ImageData::EncodedFormat format);
|
||||
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
|
||||
|
||||
virtual DecodedImage decode(love::filesystem::FileData *data);
|
||||
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
|
||||
|
||||
@@ -32,6 +32,7 @@ static void loveSTBIAssert(bool test, const char *teststr)
|
||||
// #define STBI_ONLY_PNG
|
||||
#define STBI_ONLY_BMP
|
||||
#define STBI_ONLY_TGA
|
||||
#define STBI_ONLY_HDR
|
||||
#define STBI_NO_STDIO
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ASSERT(A) loveSTBIAssert((A), #A)
|
||||
@@ -59,20 +60,31 @@ bool STBHandler::canDecode(love::filesystem::FileData *data)
|
||||
return status == 1 && w > 0 && h > 0;
|
||||
}
|
||||
|
||||
bool STBHandler::canEncode(ImageData::EncodedFormat format)
|
||||
bool STBHandler::canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat)
|
||||
{
|
||||
return format == ImageData::ENCODED_TGA;
|
||||
return encodedFormat == ImageData::ENCODED_TGA && rawFormat == ImageData::FORMAT_RGBA8;
|
||||
}
|
||||
|
||||
FormatHandler::DecodedImage STBHandler::decode(love::filesystem::FileData *data)
|
||||
{
|
||||
DecodedImage img;
|
||||
|
||||
const stbi_uc *buffer = (const stbi_uc *) data->getData();
|
||||
int bufferlen = (int) data->getSize();
|
||||
int comp = 0;
|
||||
img.data = stbi_load_from_memory((const stbi_uc *) data->getData(),
|
||||
(int) data->getSize(),
|
||||
&img.width, &img.height,
|
||||
&comp, 4);
|
||||
|
||||
if (stbi_is_hdr_from_memory(buffer, bufferlen))
|
||||
{
|
||||
img.data = (unsigned char *) stbi_loadf_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4);
|
||||
img.size = img.width * img.height * 4 * sizeof(float);
|
||||
img.format = ImageData::FORMAT_RGBA32F;
|
||||
}
|
||||
else
|
||||
{
|
||||
img.data = stbi_load_from_memory(buffer, bufferlen, &img.width, &img.height, &comp, 4);
|
||||
img.size = img.width * img.height * 4;
|
||||
img.format = ImageData::FORMAT_RGBA8;
|
||||
}
|
||||
|
||||
if (img.data == nullptr || img.width <= 0 || img.height <= 0)
|
||||
{
|
||||
@@ -82,14 +94,12 @@ FormatHandler::DecodedImage STBHandler::decode(love::filesystem::FileData *data)
|
||||
throw love::Exception("Could not decode image with stb_image (%s).", err);
|
||||
}
|
||||
|
||||
img.size = img.width * img.height * 4;
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, ImageData::EncodedFormat format)
|
||||
FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, ImageData::EncodedFormat encodedFormat)
|
||||
{
|
||||
if (!canEncode(format))
|
||||
if (!canEncode(img.format, encodedFormat))
|
||||
throw love::Exception("Invalid format.");
|
||||
|
||||
// We don't actually use stb_image for encoding, but this code is small
|
||||
@@ -113,13 +123,13 @@ FormatHandler::EncodedImage STBHandler::encode(const DecodedImage &img, ImageDat
|
||||
throw love::Exception("Out of memory.");
|
||||
|
||||
// here's the header for the Targa file format.
|
||||
encimg.data[0] = 0; // ID field size
|
||||
encimg.data[1] = 0; // colormap type
|
||||
encimg.data[2] = 2; // image type
|
||||
encimg.data[3] = encimg.data[4] = 0; // colormap start
|
||||
encimg.data[5] = encimg.data[6] = 0; // colormap length
|
||||
encimg.data[7] = 32; // colormap bits
|
||||
encimg.data[8] = encimg.data[9] = 0; // x origin
|
||||
encimg.data[0] = 0; // ID field size
|
||||
encimg.data[1] = 0; // colormap type
|
||||
encimg.data[2] = 2; // image type
|
||||
encimg.data[3] = encimg.data[4] = 0; // colormap start
|
||||
encimg.data[5] = encimg.data[6] = 0; // colormap length
|
||||
encimg.data[7] = 32; // colormap bits
|
||||
encimg.data[8] = encimg.data[9] = 0; // x origin
|
||||
encimg.data[10] = encimg.data[11] = 0; // y origin
|
||||
// Targa is little endian, so:
|
||||
encimg.data[12] = img.width & 255; // least significant byte of width
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
// Implements FormatHandler.
|
||||
|
||||
virtual bool canDecode(love::filesystem::FileData *data);
|
||||
virtual bool canEncode(ImageData::EncodedFormat format);
|
||||
virtual bool canEncode(ImageData::Format rawFormat, ImageData::EncodedFormat encodedFormat);
|
||||
|
||||
virtual DecodedImage decode(love::filesystem::FileData *data);
|
||||
virtual EncodedImage encode(const DecodedImage &img, ImageData::EncodedFormat format);
|
||||
|
||||
Reference in New Issue
Block a user