bluecore/corona/src/Convert.cpp

277 lines
7.4 KiB
C++

/**
* @file
* @todo allow conversions from direct color images to
* palettized images
*/
#include <map>
#include <utility>
#include <cstring>
#include "corona.h"
#include "Debug.h"
#include "SimpleImage.h"
#include "Utility.h"
namespace corona {
Image* ExpandPalette(Image* image) {
COR_GUARD("ExpandPalette()");
// assert isPalettized(image->getFormat())
const int width = image->getWidth();
const int height = image->getHeight();
const byte* in = (byte*)image->getPixels();
const PixelFormat palette_format = image->getPaletteFormat();
const int pixel_size = GetPixelSize(palette_format);
const byte* palette = (byte*)image->getPalette();
byte* pixels = new byte[width * height * pixel_size];
byte* out = pixels;
for (int i = 0; i < width * height; ++i) {
memcpy(out, palette + (*in) * pixel_size, pixel_size);
out += pixel_size;
++in;
}
delete image;
return new SimpleImage(width, height, palette_format, pixels);
}
struct FormatDesc {
FormatDesc(int r, int g, int b, int a, bool ha) {
r_shift = r;
g_shift = g;
b_shift = b;
a_shift = a;
has_alpha = ha;
}
// shifts are in bytes from the right
// In the case of RGBA, r_shift is 0, g_shift is 1, ...
int r_shift;
int g_shift;
int b_shift;
int a_shift;
bool has_alpha;
};
#define DEFINE_DESC(format, desc) \
case format: { \
COR_LOG(#format); \
static FormatDesc format##_desc desc; \
return &format##_desc; \
}
FormatDesc* GetDescription(PixelFormat format) {
// assert isDirect(image->getFormat())
switch (format) {
DEFINE_DESC(PF_R8G8B8A8, (0, 1, 2, 3, true));
DEFINE_DESC(PF_R8G8B8, (0, 1, 2, 0, false));
DEFINE_DESC(PF_B8G8R8A8, (2, 1, 0, 3, true));
DEFINE_DESC(PF_B8G8R8, (2, 1, 0, 0, false));
default: return 0;
}
}
bool ConvertPixels(byte* out, PixelFormat out_format,
const byte* in, PixelFormat in_format,
int pixel_count)
{
const FormatDesc* out_desc = GetDescription(out_format);
const FormatDesc* in_desc = GetDescription(in_format);
if (!out_desc || !in_desc) {
return false;
}
const int out_size = GetPixelSize(out_format);
const int in_size = GetPixelSize(in_format);
for (int i = 0; i < pixel_count; ++i) {
out[out_desc->r_shift] = in[in_desc->r_shift];
out[out_desc->g_shift] = in[in_desc->g_shift];
out[out_desc->b_shift] = in[in_desc->b_shift];
if (out_desc->has_alpha) {
if (in_desc->has_alpha) {
out[out_desc->a_shift] = in[in_desc->a_shift];
} else {
out[out_desc->a_shift] = 255;
}
}
in += in_size;
out += out_size;
}
return true;
}
Image* DirectConversion(Image* image, PixelFormat target_format) {
COR_GUARD("DirectConversion()");
// assert isDirect(image->getFormat())
const int width = image->getWidth();
const int height = image->getHeight();
const PixelFormat source_format = image->getFormat();
const byte* in = (byte*)image->getPixels();
if (source_format == target_format) {
return image;
}
const int target_size = GetPixelSize(target_format);
byte* out_pixels = new byte[width * height * target_size];
if (!ConvertPixels(out_pixels, target_format,
in, source_format,
width * height))
{
delete[] out_pixels;
delete image;
return 0;
}
delete image;
return new SimpleImage(width, height, target_format, out_pixels);
}
namespace hidden {
COR_EXPORT(Image*) CorConvertImage(
Image* image,
PixelFormat target_format)
{
COR_GUARD("CorConvertImage");
// if we don't have an image, user doesn't care about format, or
// the formats match, don't do any conversion.
if (!image ||
target_format == PF_DONTCARE ||
target_format == image->getFormat())
{
return image;
}
COR_LOG("Doing the conversion...");
// if we have a palettized image, convert it to a direct color
// image and then convert that
if (IsPalettized(image->getFormat())) {
image = ExpandPalette(image);
}
return DirectConversion(image, target_format);
}
COR_EXPORT(Image*) CorConvertPalette(
Image* image,
PixelFormat palette_format)
{
// do we need to convert?
if (!image ||
palette_format == PF_DONTCARE ||
image->getPaletteFormat() == palette_format)
{
return image;
}
// do we have invalid data?
if (!IsPalettized(image->getFormat()) ||
!IsDirect(palette_format))
{
delete image;
return 0;
}
const int width = image->getWidth();
const int height = image->getHeight();
const PixelFormat format = image->getFormat();
const int palette_size = image->getPaletteSize();
// the palette indices don't change, so just make a copy
const int image_size = width * height * GetPixelSize(format);
byte* pixels = new byte[image_size];
memcpy(pixels, image->getPixels(), image_size);
byte* new_palette = new byte[
palette_size * GetPixelSize(palette_format)];
if (!ConvertPixels(new_palette, palette_format,
(byte*)image->getPalette(), image->getPaletteFormat(),
palette_size))
{
delete image;
delete[] pixels;
delete[] new_palette;
return 0;
}
delete image;
return new SimpleImage(
width, height, format, pixels,
new_palette, palette_size, palette_format);
}
COR_EXPORT(Image*) CorFlipImage(
Image* image,
int coordinate_axis)
{
COR_GUARD("CorFlipImage");
// if we don't have an image, don't flip.
if (!image) {
return 0;
}
COR_LOG("Doing the flip...");
const int width = image->getWidth();
const int height = image->getHeight();
byte* pixels = (byte*)image->getPixels();
const PixelFormat pixel_format = image->getFormat();
const int pixel_size = GetPixelSize(pixel_format);
// flip about the X axis
if (coordinate_axis & CA_X) {
byte* row = new byte[width * pixel_size];
for (int h = 0; h < height / 2; ++h) {
byte* top = pixels + h * width * pixel_size;
byte* bot = pixels + (height - h - 1) * width * pixel_size;
memcpy(row, top, width * pixel_size);
memcpy(top, bot, width * pixel_size);
memcpy(bot, row, width * pixel_size);
}
delete[] row;
}
// flip about the Y axis
if (coordinate_axis & CA_Y) {
for (int h = 0; h < height; ++h) {
byte* row = pixels + h * width * pixel_size;
for (int w = 0; w < width / 2; ++w) {
byte* left = row + w * pixel_size;
byte* right = row + (width - w - 1) * pixel_size;
for (int b = 0; b < pixel_size; ++b) {
std::swap(left[b], right[b]);
}
}
}
}
return image;
}
}
}