/** * @file * @todo allow conversions from direct color images to * palettized images */ #include #include #include #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; } } }