initial commit

This commit is contained in:
cirdan
2008-01-16 11:45:17 +00:00
commit 8f17a3a819
1068 changed files with 384278 additions and 0 deletions

16
corona/Makefile Normal file
View File

@ -0,0 +1,16 @@
include ../Makefile.common
OBJ = $(patsubst %.cpp,%.o,$(wildcard src/*.cpp))
LIBNAME = libcorona.a
CXXFLAGS += -Isrc -I../libpng -I../zlib -I../physfs
all: $(LIBNAME)
$(LIBNAME): $(OBJ)
@echo Creating archive $@
@ar -csru $@ $(OBJ)
@echo
clean:
-@$(RM) src$(SLASH)*.o
-@$(RM) src$(SLASH)*.d
-@$(RM) $(LIBNAME)

275
corona/src/Convert.cpp Normal file
View File

@ -0,0 +1,275 @@
/**
* @file
* @todo allow conversions from direct color images to
* palettized images
*/
#include <map>
#include <utility>
#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;
}
}
}

321
corona/src/Corona.cpp Normal file
View File

@ -0,0 +1,321 @@
#include <memory>
#include <string>
#include <vector>
#include <string.h>
#include <ctype.h>
#include "corona.h"
#include "MemoryFile.h"
#include "Open.h"
#include "Save.h"
#include "SimpleImage.h"
namespace corona {
namespace hidden {
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(const char*) CorGetVersion() {
return "1.0.2";
}
///////////////////////////////////////////////////////////////////////////
class FFDImpl : public FileFormatDesc {
public:
FFDImpl(FileFormat format, const char* description, const char* exts) {
m_format = format;
m_description = description;
const char* ext = exts;
while (*ext) {
m_extensions.push_back(ext);
ext += strlen(ext) + 1;
}
}
FileFormat getFormat() { return m_format; }
const char* getDescription() { return m_description.c_str(); }
size_t getExtensionCount() { return m_extensions.size(); }
const char* getExtension(size_t i) { return m_extensions[i].c_str(); }
private:
FileFormat m_format;
std::string m_description;
std::vector<std::string> m_extensions;
};
FFDImpl ffPNG (FF_PNG, "PNG Files", "png\0");
FFDImpl ffJPEG(FF_JPEG, "JPEG Files", "jpeg\0jpg\0");
FFDImpl ffPCX (FF_PCX, "PCX Files", "pcx\0");
FFDImpl ffBMP (FF_BMP, "BMP Files", "bmp\0");
FFDImpl ffTGA (FF_TGA, "TGA Files", "tga\0");
FFDImpl ffGIF (FF_GIF, "GIF Files", "gif\0");
const int MAX_FORMAT_COUNT = 64;
FileFormatDesc** g_read_formats = 0;
FileFormatDesc** g_write_formats = 0;
FileFormatDesc* g_read_array[MAX_FORMAT_COUNT + 1] = {0};
FileFormatDesc* g_write_array[MAX_FORMAT_COUNT + 1] = {0};
COR_EXPORT(FileFormatDesc**) CorGetSupportedReadFormats() {
if (!g_read_formats) {
g_read_formats = g_read_array;
FileFormatDesc** f = g_read_formats;
*f++ = &ffPNG;
*f++ = &ffPCX;
*f++ = &ffBMP;
*f++ = &ffTGA;
}
return g_read_formats;
}
COR_EXPORT(FileFormatDesc**) CorGetSupportedWriteFormats() {
if (!g_write_formats) {
g_write_formats = g_write_array;
FileFormatDesc** f = g_write_formats;
*f++ = &ffPNG;
*f++ = &ffTGA;
}
return g_write_formats;
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(Image*) CorCreateImage(
int width,
int height,
PixelFormat format)
{
return CreateImage(width, height, format, 0);
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(Image*) CorCreateImageWithPixels(
int width,
int height,
PixelFormat format,
void* pixels)
{
// this function only supports creation of non-palettized images
if (!IsDirect(format)) {
return 0;
}
int size = width * height * GetPixelSize(format);
byte* p = new byte[size];
if (pixels) {
memcpy(p, pixels, size);
} else {
memset(p, 0, size);
}
return new SimpleImage(width, height, format, p);
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(Image*) CorCreatePalettizedImage(
int width,
int height,
PixelFormat format,
int palette_size,
PixelFormat palette_format)
{
// only support creation of palettized images
if (!IsPalettized(format) || !IsDirect(palette_format)) {
return 0;
}
// make sure the palette is the right size
if (palette_size != GetPaletteSize(format)) {
return 0;
}
int size = width * height * GetPixelSize(format);
byte* pixels = new byte[size];
memset(pixels, 0, size);
int palette_bytes = palette_size * GetPixelSize(palette_format);
byte* palette = new byte[palette_bytes];
memset(palette, 0, palette_bytes);
return new SimpleImage(width, height, format, pixels,
palette, palette_size, palette_format);
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(Image*) CorCloneImage(
Image* source,
PixelFormat format)
{
if (!source) {
// we need an image to clone :)
return 0;
}
const int width = source->getWidth();
const int height = source->getHeight();
const PixelFormat source_format = source->getFormat();
const int source_pixel_size = GetPixelSize(source_format);
if (source_pixel_size == 0) {
// unknown pixel size?
return 0;
}
// duplicate the image
int image_size = width * height * source_pixel_size;
byte* pixels = new byte[image_size];
memcpy(pixels, source->getPixels(), image_size);
if (IsPalettized(source_format)) {
// clone palette
int palette_size = source->getPaletteSize();
PixelFormat palette_format = source->getPaletteFormat();
int palette_bytes = palette_size * GetPixelSize(palette_format);
byte* palette = new byte[palette_bytes];
memcpy(palette, source->getPalette(), palette_bytes);
Image* image = new SimpleImage(width, height, source_format, pixels,
palette, palette_size, palette_format);
return ConvertImage(image, format);
}
Image* image = new SimpleImage(width, height, source_format, pixels);
return ConvertImage(image, format);
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(Image*) CorOpenImage(
const char* filename,
FileFormat file_format)
{
if (!filename) {
return 0;
}
std::auto_ptr<File> file(OpenFile(filename, false));
return CorOpenImageFromFile(file.get(), file_format);
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(Image*) CorOpenImageFromFile(
File* file,
FileFormat file_format)
{
if (!file) {
return 0;
}
#define TRY_TYPE(type) \
{ \
Image* image = CorOpenImageFromFile(file, (type)); \
if (image) { return image; } \
}
file->seek(0, File::BEGIN);
switch (file_format) {
case FF_AUTODETECT: {
TRY_TYPE(FF_PNG);
TRY_TYPE(FF_PCX);
TRY_TYPE(FF_BMP);
TRY_TYPE(FF_TGA);
return 0;
}
case FF_PNG: return OpenPNG(file);
case FF_PCX: return OpenPCX(file);
case FF_BMP: return OpenBMP(file);
case FF_TGA: return OpenTGA(file);
default: return 0;
}
}
///////////////////////////////////////////////////////////////////////////
int strcmp_ci(const char* a, const char* b) {
while (*a && *b) {
const int diff = tolower(*a) - tolower(*b);
if (diff != 0) {
return diff;
}
++a;
++b;
}
return tolower(*a) - tolower(*b);
}
bool ends_with(const char* str, const char* ext) {
const int str_len = strlen(str);
const int ext_len = strlen(ext);
return (str_len >= ext_len &&
strcmp_ci(str + str_len - ext_len, ext) == 0);
}
COR_EXPORT(bool) CorSaveImage(
const char* filename,
FileFormat file_format,
Image* image)
{
if (!filename) {
return false;
}
if (file_format == FF_AUTODETECT) {
if (ends_with(filename, ".png")) {
file_format = FF_PNG;
} else if (ends_with(filename, ".tga")) {
file_format = FF_TGA;
} else {
return false;
}
}
std::auto_ptr<File> file(OpenFile(filename, true));
return CorSaveImageToFile(file.get(), file_format, image);
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(bool) CorSaveImageToFile(
File* file,
FileFormat file_format,
Image* image)
{
if (!file || !image) {
return false;
}
switch (file_format) {
case FF_PNG: return SavePNG(file, image);
case FF_JPEG: return false;
case FF_PCX: return false;
case FF_BMP: return false;
case FF_TGA: return SaveTGA(file, image);
case FF_GIF: return false;
default: return false;
}
}
///////////////////////////////////////////////////////////////////////////
COR_EXPORT(int) CorGetPixelSize(PixelFormat format) {
switch (format) {
case PF_R8G8B8A8: return 4;
case PF_R8G8B8: return 3;
case PF_B8G8R8A8: return 4;
case PF_B8G8R8: return 3;
case PF_I8: return 1;
default: return 0;
}
}
///////////////////////////////////////////////////////////////////////////
}
}

52
corona/src/Debug.cpp Normal file
View File

@ -0,0 +1,52 @@
#include "Debug.h"
#ifdef CORONA_DEBUG
FILE* Log::handle;
int Log::indent_count;
////////////////////////////////////////////////////////////////////////////////
void
Log::Write(const char* str)
{
EnsureOpen();
if (handle) {
std::string s(std::string(indent_count * 2, ' ') + str + "\n");
fputs(s.c_str(), handle);
fflush(handle);
}
}
////////////////////////////////////////////////////////////////////////////////
void
Log::EnsureOpen()
{
if (!handle) {
#ifdef WIN32
handle = 0; //fopen("C:/corona_debug.log", "w");
#else
//std::string home(getenv("HOME"));
handle = 0; //fopen((home + "/corona_debug.log").c_str(), "w");
#endif
atexit(Close);
}
}
////////////////////////////////////////////////////////////////////////////////
void
Log::Close()
{
if (handle) {
fclose(handle);
}
}
////////////////////////////////////////////////////////////////////////////////
#endif

70
corona/src/Debug.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef DEBUG_HPP
#define DEBUG_HPP
#ifdef _DEBUG
# ifndef CORONA_DEBUG
# define CORONA_DEBUG
# endif
#endif
#ifdef CORONA_DEBUG
#include <stdio.h>
#include <string>
class Log {
public:
static void Write(const char* str);
static void IncrementIndent() { ++indent_count; }
static void DecrementIndent() { --indent_count; }
private:
static void EnsureOpen();
static void Close();
private:
static FILE* handle;
static int indent_count;
};
class Guard {
public:
Guard(const char* label)
: m_label(label) {
Write("+");
Log::IncrementIndent();
}
~Guard() {
Log::DecrementIndent();
Write("-");
}
void Write(const char* prefix) {
Log::Write((prefix + m_label).c_str());
}
private:
std::string m_label;
};
#define COR_GUARD(label) Guard guard_obj__(label)
#define COR_LOG(label) (Log::Write(label))
#define COR_IF_DEBUG if (true)
#define COR_ASSERT(condition, label) if (!(condition)) { __asm int 3 }
#else
#define COR_GUARD(label)
#define COR_LOG(label)
#define COR_IF_DEBUG if (false)
#define COR_ASSERT(condition, label)
#endif
#endif

View File

@ -0,0 +1,55 @@
//#include <stdio.h>
#include <physfs.h>
#include "Utility.h"
namespace corona {
class CFile : public DLLImplementation<File> {
public:
CFile(PHYSFS_File* file) {
m_file = file;
}
~CFile() {
PHYSFS_close(m_file);
}
int COR_CALL read(void* buffer, int size) {
return (int)PHYSFS_read(m_file, buffer, 1, size );
}
int COR_CALL write(const void* buffer, int size) {
return (int)PHYSFS_write(m_file, buffer, 1, size );
}
bool COR_CALL seek(int position, SeekMode mode) {
PHYSFS_uint64 pos;
switch (mode) {
case BEGIN: pos = position; break;
case CURRENT: pos = PHYSFS_tell(m_file) + position; break;
case END: pos = PHYSFS_fileLength( m_file ) + position; break;
default: return false;
}
return PHYSFS_seek(m_file, pos) != 0;
}
int COR_CALL tell() {
return (int)PHYSFS_tell(m_file);
}
private:
PHYSFS_File* m_file;
};
COR_EXPORT(File*) CorOpenFile(const char* filename, bool writeable) {
PHYSFS_File* file;
if( writeable )
file = PHYSFS_openWrite(filename);
else
file = PHYSFS_openRead(filename);
return (file ? new CFile(file) : 0);
}
}

93
corona/src/MemoryFile.cpp Normal file
View File

@ -0,0 +1,93 @@
#include <string.h>
#include "MemoryFile.h"
#include "Utility.h"
namespace corona {
COR_EXPORT(File*) CorCreateMemoryFile(const void* buffer, int size) {
if (size && !buffer) {
return 0;
}
if (size < 0) {
return 0;
}
return new MemoryFile(buffer, size);
}
int getNextPowerOfTwo(int value) {
int i = 1;
while (i < value) {
i *= 2;
}
return i;
}
MemoryFile::MemoryFile(const void* buffer, int size) {
m_capacity = getNextPowerOfTwo(size);
m_size = size;
m_buffer = new byte[m_capacity];
memcpy(m_buffer, buffer, size);
m_position = 0;
}
MemoryFile::~MemoryFile() {
delete[] m_buffer;
}
int COR_CALL MemoryFile::read(void* buffer, int size) {
int real_read = std::min((m_size - m_position), size);
memcpy(buffer, m_buffer + m_position, real_read);
m_position += real_read;
return real_read;
}
int COR_CALL MemoryFile::write(const void* buffer, int size) {
ensureSize(m_position + size);
memcpy(m_buffer + m_position, buffer, size);
m_position += size;
return size;
}
bool COR_CALL MemoryFile::seek(int position, SeekMode mode) {
int real_pos;
switch (mode) {
case BEGIN: real_pos = position; break;
case CURRENT: real_pos = m_position + position; break;
case END: real_pos = m_size + position; break;
default: return false;
}
if (real_pos < 0 || real_pos > m_size) {
m_position = 0;
return false;
} else {
m_position = real_pos;
return true;
}
}
int COR_CALL MemoryFile::tell() {
return m_position;
}
void MemoryFile::ensureSize(int min_size) {
bool realloc_needed = false;
while (m_capacity < min_size) {
m_capacity *= 2;
realloc_needed = true;
}
if (realloc_needed) {
byte* new_buffer = new byte[m_capacity];
memcpy(new_buffer, m_buffer, m_size);
delete[] m_buffer;
m_buffer = new_buffer;
}
m_size = min_size;
}
};

36
corona/src/MemoryFile.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef MEMORY_FILE_H
#define MEMORY_FILE_H
#include "corona.h"
#include "Types.h"
#include "Utility.h"
namespace corona {
class MemoryFile : public DLLImplementation<File> {
public:
MemoryFile(const void* buffer, int size);
~MemoryFile();
int COR_CALL read(void* buffer, int size);
int COR_CALL write(const void* buffer, int size);
bool COR_CALL seek(int position, SeekMode mode);
int COR_CALL tell();
private:
void ensureSize(int min_size);
byte* m_buffer;
int m_position;
int m_size;
/// The actual size of m_buffer. Always a power of two.
int m_capacity;
};
}
#endif

16
corona/src/Open.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef CORONA_OPEN_H
#define CORONA_OPEN_H
#include "corona.h"
namespace corona {
Image* OpenBMP (File* file); // OpenBMP.cpp
Image* OpenPCX (File* file); // OpenPCX.cpp
Image* OpenPNG (File* file); // OpenPNG.cpp
Image* OpenTGA (File* file); // OpenTGA.cpp
}
#endif

678
corona/src/OpenBMP.cpp Normal file
View File

@ -0,0 +1,678 @@
// format information gleaned from
// http://www.daubnet.com/formats/BMP.html
// and
// http://www.edm2.com/0107/os2bmp.html
//
// If you have Visual Studio.NET:
// ms-help://MS.VSCC/MS.MSDNVS/gdi/bitmaps_7c36.htm
#include <string.h>
#include "corona.h"
#include "SimpleImage.h"
#include "Utility.h"
namespace corona {
struct Header {
bool os2;
int file_size;
int data_offset;
int width;
int height;
int bpp;
int compression;
int pitch; // number of bytes in each scanline
int image_size;
auto_array<BGR> palette;
int palette_size;
// for bitfield specification...
// /*- 16-bit only -*/
u32 bf_red_mask, bf_red_shift, bf_red_rshift;
u32 bf_green_mask, bf_green_shift, bf_green_rshift;
u32 bf_blue_mask, bf_blue_shift, bf_blue_rshift;
};
bool ReadHeader(File* file, Header& h);
bool ReadInfoHeader(File* file, Header& h);
bool ReadPalette(File* file, Header& h);
Image* DecodeBitmap(File* file, const Header& h);
Image* OpenBMP(File* file) {
Header h;
if (ReadHeader(file, h) &&
ReadInfoHeader(file, h) &&
ReadPalette(file, h)) {
return DecodeBitmap(file, h);
} else {
return 0;
}
}
bool ReadHeader(File* file, Header& h) {
byte header[14];
if (file->read(header, 14) != 14) {
return false;
}
// check signature
if (header[0] != 'B' || header[1] != 'M') {
return false;
}
h.file_size = read32_le(header + 2);
h.data_offset = read32_le(header + 10);
return true;
}
bool ReadInfoHeader(File* file, Header& h) {
const int HEADER_READ_SIZE = 24;
// read the only part of the header we need
byte header[HEADER_READ_SIZE];
if (file->read(header, HEADER_READ_SIZE) != HEADER_READ_SIZE) {
return false;
}
int size = read32_le(header + 0);
int width;
int height;
int planes;
int bpp;
int compression;
int image_size;
if (size < 40) { // assume OS/2 bitmap
if (size < 12) {
return false;
}
h.os2 = true;
width = read16_le(header + 4);
height = read16_le(header + 6);
planes = read16_le(header + 8);
bpp = read16_le(header + 10);
compression = 0;
image_size = 0;
} else {
h.os2 = false;
width = read32_le(header + 4);
height = read32_le(header + 8);
planes = read16_le(header + 12);
bpp = read16_le(header + 14);
compression = read32_le(header + 16);
image_size = read32_le(header + 20);
}
// sanity check the info header
if (planes != 1) {
return false;
}
// adjust image_size
// (if compression == 0 or 3, manually calculate image size)
int line_size = 0;
if (compression == 0 || compression == 3) {
line_size = (width * bpp + 7) / 8;
line_size = (line_size + 3) / 4 * 4; // 32-bit-aligned
image_size = line_size * height;
}
h.width = width;
h.height = height;
h.bpp = bpp;
h.compression = compression;
h.pitch = line_size;
h.image_size = image_size;
// jump forward (backward in the OS/2 case :) to the palette data
file->seek(size - HEADER_READ_SIZE, File::CURRENT);
return true;
}
// count the number of consecutive zeroes on the right side of a
// binary number
// 0x00F0 will return 4
int count_right_zeroes(u32 n) {
int total = 0;
u32 c = 1;
while ((total < 32) && ((n & c) == 0)) {
c <<= 1;
++total;
}
return total;
}
// count the number of ones in a binary number
// 0x00F1 will return 5
int count_ones(u32 n) {
int total = 0;
u32 c = 1;
for (int i = 0; i < 32; ++i) {
if (n & c) {
++total;
}
c <<= 1;
}
return total;
}
bool ReadPalette(File* file, Header& h) {
// initialize bit masks/shifts... just in case
h.bf_red_mask = h.bf_red_shift = h.bf_red_rshift = 0;
h.bf_green_mask = h.bf_green_shift = h.bf_green_rshift = 0;
h.bf_blue_mask = h.bf_blue_shift = h.bf_blue_rshift = 0;
// if we're not a palettized image, don't even read a palette
if (h.bpp > 8) {
h.palette_size = 0;
// do we have bitfields?
if (h.compression == 3) {
auto_array<byte> bitfields(new byte[12]);
if (file->read(bitfields, 12) != 12) {
return false;
}
h.bf_red_mask = read32_le((byte*)bitfields);
h.bf_green_mask = read32_le((byte*)bitfields + 4);
h.bf_blue_mask = read32_le((byte*)bitfields + 8);
// calculate shifts
h.bf_red_shift = count_right_zeroes(h.bf_red_mask);
h.bf_green_shift = count_right_zeroes(h.bf_green_mask);
h.bf_blue_shift = count_right_zeroes(h.bf_blue_mask);
h.bf_red_rshift = 8 - count_ones(h.bf_red_mask);
h.bf_green_rshift = 8 - count_ones(h.bf_green_mask);
h.bf_blue_rshift = 8 - count_ones(h.bf_blue_mask);
// otherwise, set default bitfield entries
} else {
if (h.bpp == 16) {
h.bf_red_mask = 0x7C00;
h.bf_red_shift = 10;
h.bf_red_rshift = 3;
h.bf_green_mask = 0x03E0;
h.bf_green_shift = 5;
h.bf_green_rshift = 3;
h.bf_blue_mask = 0x001F;
h.bf_blue_shift = 0;
h.bf_blue_rshift = 3;
} else if (h.bpp == 32) {
// these don't need any rshift
h.bf_red_mask = 0x00FF0000; h.bf_red_shift = 16;
h.bf_green_mask = 0x0000FF00; h.bf_green_shift = 8;
h.bf_blue_mask = 0x000000FF; h.bf_blue_shift = 0;
}
}
return true;
}
if (h.bpp <= 8) {
h.palette_size = 1 << h.bpp;
} else {
h.palette_size = 0;
return true;
}
h.palette = new BGR[h.palette_size];
// read the BMP color table
const int buffer_size = h.palette_size * (h.os2 ? 3 : 4);
auto_array<byte> buffer(new byte[buffer_size]);
if (file->read(buffer, buffer_size) != buffer_size) {
return false;
}
byte* in = buffer;
BGR* out = h.palette;
for (int i = 0; i < h.palette_size; ++i) {
out->blue = *in++;
out->green = *in++;
out->red = *in++;
if (!h.os2) {
++in; // skip alpha
}
++out;
}
return true;
}
bool advance(int& x, int& y, const Header& h) {
if (++x >= h.width) {
x = 0;
if (++y >= h.height) {
return false;
}
}
return true;
}
Image* ReadBitmap1(const byte* raster_data, const Header& h) {
auto_array<byte> pixels(new byte[h.width * h.height]);
auto_array<BGR> palette(new BGR[256]);
memset(palette, 0, 256 * sizeof(BGR));
memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
for (int i = 0; i < h.height; ++i) {
const byte* in = raster_data + i * h.pitch;
byte* out = pixels + (h.height - i - 1) * h.width;
int mask = 128;
for (int j = 0; j < h.width; ++j) {
*out++ = (*in & mask) > 0;
mask >>= 1;
if (mask == 0) {
++in;
mask = 128;
}
}
}
return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
(byte*)palette.release(), 256, PF_B8G8R8);
}
Image* ReadBitmap4(const byte* raster_data, const Header& h) {
auto_array<byte> pixels(new byte[h.width * h.height]);
auto_array<BGR> palette(new BGR[256]);
memset(palette, 0, 256 * sizeof(BGR));
memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
for (int i = 0; i < h.height; ++i) {
const byte* in = raster_data + i * h.pitch;
byte* out = pixels + (h.height - i - 1) * h.width;
for (int j = 0; j < h.width / 2; ++j) {
*out++ = (*in >> 4);
*out++ = (*in & 0x0F);
++in;
}
if (h.width % 2) {
*out++ = (*in >> 4);
}
}
return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
(byte*)palette.release(), 256, PF_B8G8R8);
}
Image* ReadBitmapRLE4(const byte* raster_data, const Header& h) {
auto_array<byte> pixels(new byte[h.width * h.height]);
auto_array<BGR> palette(new BGR[256]);
memset(palette, 0, 256 * sizeof(BGR));
memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
// by default, we have an empty bitmap
memset(pixels, 0, h.width * h.height);
// we read the image from the bottom down, and then flip it when
// we're done
int x = 0;
int y = 0;
const byte* in = raster_data;
while (in - raster_data < h.image_size - 1) {
byte n = *in++;
byte c = *in++;
if (n == 0) { // escape code
if (c == 0) { // end of line
x = 0;
//++y; // XXXaegis uhhh... uhhh... :) it works this way...
if (y >= h.height) {
// did we go too far?
break;
}
} else if (c == 1) { // end of bitmap
break;
} else if (c == 2) { // delta
// do we have enough space?
if (in - raster_data >= h.image_size - 1) {
break;
}
// I have no idea how I'm supposed to do this...
// Let's take a guess!
int dx = *in++;
int dy = *in++;
x = (x + dx) % h.width;
y += dy + (x + dx) / h.width;
if (y >= h.height) {
// if we went too far, stop now
break;
}
} else { // read uncompressed
// the input raster data is padded on DWORD boundaries
// c == num_pixels
int num_bytes = (c + 3) / 4 * 2;
// make sure we have enough space
if (in - raster_data > h.image_size - num_bytes) {
break;
}
// nasty decoding loop...
int i = 0;
int j = 0;
while (true) {
byte l = (in[j] & 0xF0) >> 4;
byte r = (in[j] & 0x0F);
++j;
pixels[y * h.width + x] = l;
if (!advance(x, y, h) || ++i >= c) {
break;
}
pixels[y * h.width + x] = r;
if (!advance(x, y, h) || ++i >= c) {
break;
}
}
// make SURE we move forward the right number of bytes
in += num_bytes;
}
} else {
// a less nasty decoding loop...
byte lc = (c & 0xF0) >> 4;
byte rc = c & 0x0F;
int i = 0;
while (true) {
pixels[y * h.width + x] = lc;
if (!advance(x, y, h) || ++i >= n) {
break;
}
pixels[y * h.width + x] = rc;
if (!advance(x, y, h) || ++i >= n) {
break;
}
}
} // end if
} // end while
// flippy flippy!
int pitch = h.width;
auto_array<byte> row(new byte[pitch]);
for (int i = 0; i < h.height / 2; ++i) {
int j = h.height - i - 1;
memcpy((byte*)row, pixels + i * pitch, pitch);
memcpy(pixels + i * pitch, pixels + j * pitch, pitch);
memcpy(pixels + j * pitch, (byte*)row, pitch);
}
return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
(byte*)palette.release(), 256, PF_B8G8R8);
}
Image* ReadBitmap8(const byte* raster_data, const Header& h) {
auto_array<byte> pixels(new byte[h.width * h.height]);
auto_array<BGR> palette(new BGR[256]);
memset(palette, 0, 256 * sizeof(BGR));
memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
for (int i = 0; i < h.height; ++i) {
const byte* in = raster_data + i * h.pitch;
byte* out = pixels + (h.height - i - 1) * h.width;
for (int j = 0; j < h.width; ++j) {
*out++ = *in++;
}
}
return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
(byte*)palette.release(), 256, PF_B8G8R8);
}
Image* ReadBitmapRLE8(const byte* raster_data, const Header& h) {
auto_array<byte> pixels(new byte[h.width * h.height]);
auto_array<BGR> palette(new BGR[256]);
memset(palette, 0, 256 * sizeof(BGR));
memcpy(palette, h.palette, h.palette_size * sizeof(BGR));
// by default, we have an empty bitmap
memset(pixels, 0, h.width * h.height);
// we read the image from the bottom down, and then flip it when
// we're done
int x = 0;
int y = 0;
const byte* in = raster_data;
while (in - raster_data < h.image_size - 1) {
byte n = *in++;
byte c = *in++;
if (n == 0) { // escape code
if (c == 0) { // end of line
x = 0;
//++y; // XXXaegis uhhh... uhhh... :) it works this way...
if (y >= h.height) {
// did we go too far?
break;
}
} else if (c == 1) { // end of bitmap
break;
} else if (c == 2) { // delta
// do we have enough space?
if (in - raster_data >= h.image_size - 1) {
break;
}
// I have no idea how I'm supposed to do this...
// Let's take a guess!
int dx = *in++;
int dy = *in++;
x = (x + dx) % h.width;
y += dy + (x + dx) / h.width;
if (y >= h.height) {
// if we went too far, stop now
break;
}
} else { // read uncompressed
// c == num_pixels
int num_bytes = (c + 1) / 2 * 2;
// make sure we have enough space
if (in - raster_data > h.image_size - num_bytes) {
break;
}
// decoding loop...
int i = 0;
int j = 0;
while (true) {
pixels[y * h.width + x] = in[j++];
if (!advance(x, y, h) || ++i >= c) {
break;
}
}
// make SURE we move forward the right number of bytes
in += num_bytes;
}
} else {
int i = 0;
while (true) {
pixels[y * h.width + x] = c;
if (!advance(x, y, h) || ++i >= n) {
break;
}
}
} // end if
} // end while
// flippy flippy!
int pitch = h.width;
auto_array<byte> row(new byte[pitch]);
for (int i = 0; i < h.height / 2; ++i) {
int j = h.height - i - 1;
memcpy((byte*)row, pixels + i * pitch, pitch);
memcpy(pixels + i * pitch, pixels + j * pitch, pitch);
memcpy(pixels + j * pitch, (byte*)row, pitch);
}
return new SimpleImage(h.width, h.height, PF_I8, pixels.release(),
(byte*)palette.release(), 256, PF_B8G8R8);
}
Image* ReadBitmap16(const byte* raster_data, const Header& h) {
auto_array<RGB> pixels(new RGB[h.width * h.height]);
for (int i = 0; i < h.height; ++i) {
const byte* in = raster_data + i * h.pitch;
RGB* out = pixels + (h.height - i - 1) * h.width;
for (int j = 0; j < h.width; ++j) {
int clr = read16_le(in);
in += 2;
#define C16(C) \
(byte)( ((clr & h.bf_##C##_mask) >> h.bf_##C##_shift) << h.bf_##C##_rshift);
out->red = C16(red);
out->green = C16(green);
out->blue = C16(blue);
++out;
#undef C16
}
}
return new SimpleImage(h.width, h.height, PF_R8G8B8,
(byte*)pixels.release());
}
Image* ReadBitmap24(const byte* raster_data, const Header& h) {
auto_array<BGR> pixels(new BGR[h.width * h.height]);
for (int i = 0; i < h.height; ++i) {
const byte* in = raster_data + i * h.pitch;
BGR* out = pixels + (h.height - i - 1) * h.width;
for (int j = 0; j < h.width; ++j) {
out->blue = *in++;
out->green = *in++;
out->red = *in++;
++out;
}
}
return new SimpleImage(h.width, h.height, PF_B8G8R8,
(byte*)pixels.release());
}
Image* ReadBitmap32(const byte* raster_data, const Header& h) {
auto_array<RGB> pixels(new RGB[h.width * h.height]);
for (int i = 0; i < h.height; ++i) {
const byte* in = raster_data + i * h.pitch;
RGB* out = pixels + (h.height - i - 1) * h.width;
for (int j = 0; j < h.width; ++j) {
u32 pixel = read32_le(in);
in += 4;
out->red = (byte)((pixel & h.bf_red_mask) >> h.bf_red_shift);
out->green = (byte)((pixel & h.bf_green_mask) >> h.bf_green_shift);
out->blue = (byte)((pixel & h.bf_blue_mask) >> h.bf_blue_shift);
++out;
}
}
return new SimpleImage(h.width, h.height, PF_R8G8B8,
(byte*)pixels.release());
}
Image* DecodeBitmap(File* file, const Header& h) {
if (!file->seek(h.data_offset, File::BEGIN)) {
return 0;
}
// the raster data stored in the file
auto_array<byte> raster_data(new byte[h.image_size]);
if (file->read(raster_data, h.image_size) != h.image_size) {
return 0;
}
// the output pixel buffer (parameter to new SimpleImage)
auto_array<byte> pixels(new byte[h.width * h.height * 3]);
typedef Image* (*Decoder)(const byte* raster_data, const Header& h);
Decoder decoder = 0;
if (h.bpp == 1 && h.compression == 0) { decoder = ReadBitmap1; }
else if (h.bpp == 4 && h.compression == 0) { decoder = ReadBitmap4; }
else if (h.bpp == 4 && h.compression == 2) { decoder = ReadBitmapRLE4; }
else if (h.bpp == 8 && h.compression == 0) { decoder = ReadBitmap8; }
else if (h.bpp == 8 && h.compression == 1) { decoder = ReadBitmapRLE8; }
else if (h.bpp == 16 && (h.compression == 0 ||
h.compression == 3)) { decoder = ReadBitmap16; }
else if (h.bpp == 24 && h.compression == 0) { decoder = ReadBitmap24; }
else if (h.bpp == 32 && (h.compression == 0 ||
h.compression == 3)) { decoder = ReadBitmap32; }
if (decoder) {
return decoder(raster_data.get(), h);
} else {
return 0;
}
}
}

190
corona/src/OpenPCX.cpp Normal file
View File

@ -0,0 +1,190 @@
#include <stdio.h>
#include <string.h>
#include "Debug.h"
#include "Open.h"
#include "SimpleImage.h"
#include "Utility.h"
namespace corona {
/*
struct PCX_HEADER {
byte manufacturer;
byte version;
byte encoding;
byte bits_per_pixel;
word xmin;
word ymin;
word xmax;
word ymax;
word hdpi;
word vdpi;
byte colormap[48];
byte reserved;
byte num_planes;
word bytes_per_line;
word palette_info;
word h_screen_size;
word v_screen_size;
byte filler[54];
};
*/
//////////////////////////////////////////////////////////////////////////////
bool ReadScanline(File* file, int scansize, byte* scanline) {
byte* out = scanline;
while (out - scanline < scansize) {
// read a byte!
byte data;
if (file->read(&data, 1) != 1) {
return false;
}
if ((data & 0xC0) != 0xC0) { // non RLE
*out++ = data;
} else { // RLE
// read the repeated byte
int numbytes = data & 0x3F;
if (file->read(&data, 1) != 1) {
return false;
}
while (numbytes-- && out - scanline < scansize) {
*out++ = data;
}
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////////
Image* OpenPCX(File* file) {
COR_GUARD("OpenPCX");
// read the header block
byte pcx_header[128];
int read = file->read(pcx_header, 128);
if (read != 128) {
return 0;
}
// parse the header...
//int manufacturer = pcx_header[0];
//int version = pcx_header[1];
int encoding = pcx_header[2];
int bpp = pcx_header[3];
int xmin = read16_le(pcx_header + 4);
int ymin = read16_le(pcx_header + 6);
int xmax = read16_le(pcx_header + 8);
int ymax = read16_le(pcx_header + 10);
int num_planes = pcx_header[65];
int bytes_per_line = read16_le(pcx_header + 66);
// verify the header
// we only support RLE encoding
if (encoding != 1) {
COR_LOG("Unsupported encoding");
return 0;
}
COR_IF_DEBUG {
char str[100];
sprintf(str, "bits per pixel - %d", bpp);
COR_LOG(str);
sprintf(str, "bytes per line - %d", bytes_per_line);
COR_LOG(str);
}
// we only support 8 bits per pixel
if (bpp != 8) {
COR_LOG("Unsupported bpp");
return 0;
}
// create the image structure
int width = xmax - xmin + 1;
int height = ymax - ymin + 1;
auto_array<byte> scanline(new byte[bytes_per_line]);
auto_array<byte> pixels(new byte[width * height * 3]);
// decode the pixel data
if (num_planes == 1) { // 256 colors
auto_array<RGB> palette(new RGB[256]);
auto_array<byte> image(new byte[width * height]);
// read all of the scanlines
for (int iy = 0; iy < height; ++iy) {
if (!ReadScanline(file, bytes_per_line, scanline)) {
COR_LOG("Failure reading scanline");
return 0;
}
memcpy((byte*)image + iy * width, scanline, width);
}
// seek back from the end 769 bytes
if (!file->seek(-769, File::END)) {
COR_LOG("Failure seeking to palette");
return 0;
}
// do we have a palette?
byte has_palette;
if (file->read(&has_palette, 1) != 1 || has_palette != 12) {
COR_LOG("Failure testing for existence of palette");
return 0;
}
// read palette
if (file->read(palette, 3 * 256) != 3 * 256) {
COR_LOG("Failure reading palette");
return 0;
}
return new SimpleImage(width, height, PF_I8, image.release(),
(byte*)palette.release(), 256, PF_R8G8B8);
} else if (num_planes == 3) { // 24-bit color
auto_array<byte> scanline(new byte[3 * bytes_per_line]);
byte* out = pixels;
for (int iy = 0; iy < height; ++iy) {
if (!ReadScanline(file, 3 * bytes_per_line, scanline)) {
COR_LOG("Failure reading scanline");
return 0;
}
byte* r = scanline;
byte* g = scanline + bytes_per_line;
byte* b = scanline + bytes_per_line * 2;
for (int ix = 0; ix < width; ++ix) {
*out++ = *r++;
*out++ = *g++;
*out++ = *b++;
}
}
return new SimpleImage(width, height, PF_R8G8B8, pixels.release());
} else {
COR_LOG("Unknown number of planes");
return 0;
}
}
//////////////////////////////////////////////////////////////////////////////
}

255
corona/src/OpenPNG.cpp Normal file
View File

@ -0,0 +1,255 @@
/**
* @todo use our own longjmp instead of libpng's. this way we don't need
* to use PNG_SETJMP_SUPPORTED in Windows, and don't depend on
* png_ptr->jmpbuf in older versions of libpng.
*/
#include <png.h>
#include "Debug.h"
#include "Open.h"
#include "SimpleImage.h"
#include "Utility.h"
namespace corona {
typedef unsigned char byte;
//////////////////////////////////////////////////////////////////////////////
void PNG_read_function(png_structp png_ptr,
png_bytep data,
png_size_t length) {
File* file = (File*)png_get_io_ptr(png_ptr);
if (file->read(data, length) != int(length)) {
png_error(png_ptr, "Read error");
}
}
//////////////////////////////////////////////////////////////////////////////
void PNG_warning_function(png_structp png_ptr, png_const_charp error) {
// no warnings
}
//////////////////////////////////////////////////////////////////////////////
void PNG_error_function(png_structp png_ptr, png_const_charp warning) {
// copied from libpng's pngerror.cpp, but without the fprintf
jmp_buf jmpbuf;
memcpy(jmpbuf, png_ptr->jmpbuf, sizeof(jmp_buf));
longjmp(jmpbuf, 1);
}
//////////////////////////////////////////////////////////////////////////////
void fill_palette(png_structp png, png_infop info, png_color palette[256]) {
COR_GUARD("fill_palette");
// by default, the palette is greyscale
for (int i = 0; i < 256; ++i) {
palette[i].red = i;
palette[i].green = i;
palette[i].blue = i;
}
// do we have a palette and is it big enough?
png_colorp png_palette;
int num_palette = 0;
png_get_PLTE(png, info, &png_palette, &num_palette);
COR_IF_DEBUG {
char str[80];
sprintf(str, "palette size: %d", num_palette);
COR_LOG(str);
}
if (num_palette >= 256) {
#if 0
COR_IF_DEBUG {
for (int i = 0; i < 256; ++i) {
char str[80];
sprintf(str, "r(%d) g(%d) b(%d)",
int(palette[i].red),
int(palette[i].green),
int(palette[i].blue));
COR_LOG(str);
}
}
#endif
memcpy(palette, png_palette, 256 * sizeof(png_color));
}
}
//////////////////////////////////////////////////////////////////////////////
Image* OpenPNG(File* file) {
COR_GUARD("OpenPNG");
// verify PNG signature
byte sig[8];
file->read(sig, 8);
if (png_sig_cmp(sig, 0, 8)) {
return 0;
}
COR_LOG("Signature verified");
// read struct
png_structp png_ptr = png_create_read_struct(
PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (!png_ptr) {
return 0;
}
COR_LOG("png struct created");
// info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
return 0;
}
COR_LOG("info struct created");
// the PNG error function calls longjmp(png_ptr->jmpbuf)
if (setjmp(png_jmpbuf(png_ptr))) {
COR_LOG("Error loading PNG");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return 0;
}
COR_LOG("setjmp() succeeded");
// set the error function
png_set_error_fn(png_ptr, 0, PNG_error_function, PNG_warning_function);
// read the image
png_set_read_fn(png_ptr, file, PNG_read_function);
png_set_sig_bytes(png_ptr, 8); // we already read 8 bytes for the sig
// always give us 8-bit samples (strip 16-bit and expand <8-bit)
int png_transform = PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND;
png_read_png(png_ptr, info_ptr, png_transform, NULL);
COR_LOG("PNG read");
if (!png_get_rows(png_ptr, info_ptr)) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return 0;
}
int width = png_get_image_width(png_ptr, info_ptr);
int height = png_get_image_height(png_ptr, info_ptr);
byte* pixels = 0; // allocate when we know the format
PixelFormat format;
byte* palette = 0;
PixelFormat palette_format;
// decode based on pixel format
int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
int num_channels = png_get_channels(png_ptr, info_ptr);
png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr);
// 32-bit RGBA
if (bit_depth == 8 && num_channels == 4) {
COR_LOG("32-bit RGBA: bit_depth = 8 && num_channels = 4");
format = PF_R8G8B8A8;
pixels = new byte[width * height * 4];
for (int i = 0; i < height; ++i) {
memcpy(pixels + i * width * 4, row_pointers[i], width * 4);
}
// 24-bit RGB
} else if (bit_depth == 8 && num_channels == 3) {
COR_LOG("24-bit RGB: bit_depth = 8 && num_channels = 3");
format = PF_R8G8B8;
pixels = new byte[width * height * 3];
for (int i = 0; i < height; ++i) {
memcpy(pixels + i * width * 3, row_pointers[i], width * 3);
}
// palettized or greyscale with alpha
} else if (bit_depth == 8 && (num_channels == 2 || num_channels == 1)) {
png_color png_palette[256];
fill_palette(png_ptr, info_ptr, png_palette);
if (num_channels == 2) {
COR_LOG("bit_depth = 8 && num_channels = 2");
format = PF_R8G8B8A8;
pixels = new byte[width * height * 4];
byte* out = pixels;
for (int i = 0; i < height; ++i) {
byte* in = row_pointers[i];
for (int j = 0; j < width; ++j) {
byte c = *in++;
*out++ = png_palette[c].red;
*out++ = png_palette[c].green;
*out++ = png_palette[c].blue;
*out++ = *in++; // alpha
}
}
} else { // (num_channels == 1)
COR_LOG("bit_depth = 8 && num_channels = 1");
pixels = new byte[width * height];
format = PF_I8;
palette = new byte[256 * 4];
palette_format = PF_R8G8B8A8;
// get the transparent palette flags
png_bytep trans;
int num_trans = 0;
png_color_16p trans_values; // XXX not used - should be?
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
// copy the palette from the PNG
for (int i = 0; i < 256; ++i) {
palette[i * 4 + 0] = png_palette[i].red;
palette[i * 4 + 1] = png_palette[i].green;
palette[i * 4 + 2] = png_palette[i].blue;
palette[i * 4 + 3] = 255;
}
// apply transparency to palette entries
for (int i = 0; i < num_trans; ++i) {
palette[trans[i] * 4 + 3] = 0;
}
byte* out = pixels;
for (int i = 0; i < height; ++i) {
memcpy(out, row_pointers[i], width);
out += width;
}
}
} else { // unknown format
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return 0;
}
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if (palette) {
return new SimpleImage(width, height, format, pixels,
palette, 256, palette_format);
} else {
return new SimpleImage(width, height, format, pixels);
}
}
//////////////////////////////////////////////////////////////////////////////
}

147
corona/src/OpenTGA.cpp Normal file
View File

@ -0,0 +1,147 @@
#include <algorithm>
#include "Debug.h"
#include "Open.h"
#include "SimpleImage.h"
#include "Utility.h"
namespace corona {
Image* OpenTGA(File* file) {
COR_GUARD("OpenTGA");
// read header
byte header[18];
if (file->read(header, 18) != 18) {
return 0;
}
// decode header
int id_length = header[0];
int cm_type = header[1];
int image_type = header[2];
//int cm_first = read16_le(header + 3);
int cm_length = read16_le(header + 5);
int cm_entry_size = header[7]; // in bits
//int x_origin = read16_le(header + 8);
//int y_origin = read16_le(header + 10);
int width = read16_le(header + 12);
int height = read16_le(header + 14);
int pixel_depth = header[16];
int image_descriptor = header[17];
bool mirrored = (image_descriptor & (1 << 4)) != 0; // left-to-right?
bool flipped = (image_descriptor & (1 << 5)) == 0; // bottom-to-top?
/*
* image types
* 0 = no image data
* 1 = uncompressed, color-mapped
* 2 = uncompressed, true-color
* 3 = uncompressed, black and white
* 9 = RLE, color-mapped
* 10 = RLE, true-color
* 11 = RLE, black and white
*/
// make sure we support the image
if (image_type != 2 || (pixel_depth != 24 && pixel_depth != 32)) {
return 0;
}
// skip image id
byte unused[255];
if (file->read(unused, id_length) != id_length) {
return 0;
}
// skip color map
if (cm_type != 0) {
// allocate color map
int cm_entry_bytes = (cm_entry_size + 7) / 8;
int cm_size = cm_entry_bytes * cm_length;
auto_array<byte> color_map(new byte[cm_size]);
if (file->read(color_map, cm_size) != cm_size) {
return 0;
}
}
// read image data
PixelFormat format;
auto_array<byte> pixels;
if (pixel_depth == 24) {
COR_LOG("24-bit image");
format = PF_B8G8R8;
int image_size = width * height * 3;
pixels = new byte[image_size];
if (file->read(pixels, image_size) != image_size) {
return 0;
}
} else if (pixel_depth == 32) {
COR_LOG("32-bit image");
format = PF_B8G8R8A8;
int image_size = width * height * 4;
pixels = new byte[image_size];
if (file->read(pixels, image_size) != image_size) {
return 0;
}
} else {
return 0;
}
// reverse each row
if (mirrored) {
COR_LOG("Image is mirrored");
const int bpp = pixel_depth / 8; // bytes per pixel
for (int y = 0; y < height; ++y) {
// points to the first pixel of the row
byte* start = pixels.get() + y * width * bpp;
// points to the last pixel of the row
byte* end = start + (width - 1) * bpp;
while (start < end) {
for (int b = 0; b < bpp; ++b) {
std::swap(start[b], end[b]);
}
start += bpp;
end -= bpp;
}
}
}
// reverse rows as a whole
if (flipped) {
COR_LOG("Image is flipped");
const int bpp = pixel_depth / 8; // bytes per pixel
const int row_size = width * bpp;
auto_array<byte> temp(new byte[row_size]); // for the swap
// points to the beginning of the first row
byte* start = pixels.get();
// points to the beginning of the last row
byte* end = start + (height - 1) * width * bpp;
while (start < end) {
memcpy(temp.get(), start, row_size);
memcpy(start, end, row_size);
memcpy(end, temp.get(), row_size);
start += row_size;
end -= row_size;
}
}
return new SimpleImage(width, height, format, pixels.release());
}
}

16
corona/src/Save.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef CORONA_SAVE_H
#define CORONA_SAVE_H
#include "corona.h"
namespace corona {
#ifndef NO_PNG
bool SavePNG(File* file, Image* image); // SavePNG.cpp
#endif
bool SaveTGA(File* file, Image* image); // SaveTGA.cpp
}
#endif

168
corona/src/SavePNG.cpp Normal file
View File

@ -0,0 +1,168 @@
#include <memory>
#include <png.h>
#include "Debug.h"
#include "Save.h"
#include "Types.h"
namespace corona {
void PNG_write(png_structp png_ptr, png_bytep data, png_size_t length) {
File* file = (File*)png_get_io_ptr(png_ptr);
if (file->write(data, length) != int(length)) {
png_error(png_ptr, "Write error");
}
}
void PNG_flush(png_structp png_ptr) {
// assume that files always flush
}
bool SavePNG(File* file, Image* image) {
COR_GUARD("SavePNG");
if (!image) {
return false;
}
// If the image format isn't supported directly by this function,
// clone to a supported format and try to save with that.
switch (image->getFormat()) {
case PF_R8G8B8A8:
case PF_R8G8B8:
case PF_I8:
break;
default: {
COR_LOG("Unsupported pixel format... cloning");
std::auto_ptr<Image> cloned(CloneImage(image, PF_R8G8B8A8));
return SavePNG(file, cloned.get());
}
}
// create write struct
png_structp png_ptr = png_create_write_struct(
PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
return false;
}
// error handling!
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, NULL);
return false;
}
// create info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr, NULL);
return false;
}
int width = image->getWidth();
int height = image->getHeight();
// set image characteristics
png_set_write_fn(png_ptr, file, PNG_write, PNG_flush);
int color_format = 0; // png output format
int color_format_bpp = 0; // png bytes per pixel
bool color_format_paletted = false; // png palette needed flag
// figure out output format
switch (image->getFormat()) {
case PF_R8G8B8A8:
color_format = PNG_COLOR_TYPE_RGB_ALPHA;
color_format_bpp = 4;
break;
case PF_R8G8B8:
color_format = PNG_COLOR_TYPE_RGB;
color_format_bpp = 3;
break;
case PF_I8:
color_format = PNG_COLOR_TYPE_PALETTE;
color_format_bpp = 1;
color_format_paletted = true;
break;
default:
// Unsupported format. This should already be taken care of
// by the test at the beginning of this function.
png_destroy_write_struct(&png_ptr, &info_ptr);
return false;
}
png_set_IHDR(
png_ptr, info_ptr,
width, height,
8,
color_format,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_color* png_palette = 0;
if (color_format_paletted) {
COR_LOG("Saving palettized image...");
int image_palette_format = image->getPaletteFormat(); // palette format
int image_palette_size = image->getPaletteSize(); // palette size
// allocate png palette and get pointer to image palette
png_palette = (png_color*)png_malloc(
png_ptr, sizeof(png_color) * image_palette_size);
byte* image_palette = (byte*)image->getPalette();
if (image_palette_format == PF_R8G8B8) {
// 24 bit source palette
for (int i = 0; i < image_palette_size; i++) {
// copy entry directly
png_palette[i].red = *image_palette++;
png_palette[i].green = *image_palette++;
png_palette[i].blue = *image_palette++;
}
} else if (image_palette_format == PF_R8G8B8A8) {
// 32 bit source palette
for (int i = 0; i < image_palette_size; i++) {
// copy entry, skip alpha
png_palette[i].red = *image_palette++;
png_palette[i].green = *image_palette++;
png_palette[i].blue = *image_palette++;
image_palette++;
}
}
// write palette
png_set_PLTE(png_ptr, info_ptr, png_palette, image_palette_size);
}
byte* pixels = (byte*)image->getPixels();
// build rows
void** rows = (void**)png_malloc(png_ptr, sizeof(void*) * height);
for (int i = 0; i < height; ++i) {
rows[i] = png_malloc(png_ptr, color_format_bpp * width);
memcpy(rows[i], pixels, color_format_bpp * width);
pixels += width * color_format_bpp;
}
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
info_ptr->valid |= PNG_INFO_IDAT;
// actually write the image
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
// clean up memory
for (int i = 0; i < height; ++i) {
png_free(png_ptr, rows[i]);
}
png_free(png_ptr, rows);
if (png_palette) {
png_free(png_ptr, png_palette);
}
png_destroy_write_struct(&png_ptr, &info_ptr);
return true;
}
}

49
corona/src/SaveTGA.cpp Normal file
View File

@ -0,0 +1,49 @@
#include <memory>
#include <png.h>
#include "Debug.h"
#include "Save.h"
#include "Utility.h"
namespace corona {
bool SaveTGA(File* file, Image* source) {
COR_GUARD("SaveTGA");
std::auto_ptr<Image> image(CloneImage(source, PF_B8G8R8A8));
if (!image.get()) {
return false;
}
const int width = image->getWidth();
const int height = image->getHeight();
// generate the header
byte header[18];
header[0] = 0; // id_length
header[1] = 0; // cm_type
header[2] = 2; // image_type (uncompressed true color)
write16_le(header + 3, 0); // cm_first
write16_le(header + 5, 0); // cm_length
header[7] = 0; // cm_entry_size
write16_le(header + 8, 0); // x_origin
write16_le(header + 10, 0); // y_origin
write16_le(header + 12, width);
write16_le(header + 14, height);
header[16] = 32; // pixel_depth
header[17] = (1 << 5) | 7; // origin at upper/left, 8 bits of alpha data
if (file->write(header, 18) != 18) {
return false;
}
// write pixels
const int data_size = width * height * 4;
if (file->write(image->getPixels(), data_size) != data_size) {
return false;
}
return true;
}
}

101
corona/src/SimpleImage.h Normal file
View File

@ -0,0 +1,101 @@
#ifndef CORONA_SIMPLE_IMAGE_H
#define CORONA_SIMPLE_IMAGE_H
#include "corona.h"
#include "Types.h"
#include "Utility.h"
namespace corona {
/**
* Basic, flat, simple image. Has a width, a height, a pixel
* format, and a 2D array of pixels (one-byte packing).
*
* The constructor takes a pixel buffer (and optionally a palette)
* which it then owns and delete[]'s when the image is destroyed.
*/
class SimpleImage : public DLLImplementation<Image> {
public:
/**
* Creates a new image, setting all properties.
*
* @param width width of the new image
* @param height height of the new image
* @param format format that the pixels are stored in
* @param pixels pixel buffer that the SimpleImage takes
ownership of. it should be
width*height*sizeof(pixel) bytes.
* @param palette palette color buffer
* @param palette_size number of entries in palette
* @param palette_format color format palette is stored as
*/
SimpleImage(int width,
int height,
PixelFormat format,
byte* pixels,
byte* palette = 0,
int palette_size = 0,
PixelFormat palette_format = PF_DONTCARE) {
m_width = width;
m_height = height;
m_format = format;
m_pixels = pixels;
m_palette = palette;
m_palette_size = palette_size;
m_palette_format = palette_format;
}
/**
* Destroys the image, freeing the owned pixel buffer and palette.
*/
~SimpleImage() {
delete[] m_pixels;
delete[] m_palette;
}
int COR_CALL getWidth() {
return m_width;
}
int COR_CALL getHeight() {
return m_height;
}
PixelFormat COR_CALL getFormat() {
return m_format;
}
void* COR_CALL getPixels() {
return m_pixels;
}
void* COR_CALL getPalette() {
return m_palette;
}
int COR_CALL getPaletteSize() {
return m_palette_size;
}
PixelFormat COR_CALL getPaletteFormat() {
return m_palette_format;
}
private:
int m_width;
int m_height;
PixelFormat m_format;
byte* m_pixels;
byte* m_palette;
int m_palette_size;
PixelFormat m_palette_format;
};
}
#endif

27
corona/src/Types.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef CORONA_TYPES_H
#define CORONA_TYPES_H
namespace corona {
// VC++-specific types
#ifdef _MSC_VER
typedef unsigned char byte;
typedef unsigned __int16 u16;
typedef unsigned __int32 u32;
// reasonable defaults
// should work on any 32-bit platform
#else
typedef unsigned char byte;
typedef unsigned short u16;
typedef unsigned long u32;
#endif
}
#endif

127
corona/src/Utility.h Normal file
View File

@ -0,0 +1,127 @@
#ifndef CORONA_UTILITY_H
#define CORONA_UTILITY_H
#include <algorithm>
#include "corona.h"
#include "Types.h"
#define COR_EXPORT(ret) COR_FUNCTION(ret)
#if defined(_MSC_VER) && _MSC_VER <= 1200
// define our own std::min and std::max in VC6
namespace std {
template<typename T>
T min(T a, T b) {
return a < b ? a : b;
}
template<typename T>
T max(T a, T b) {
return a > b ? a : b;
}
}
#endif
namespace corona {
template<typename T>
class auto_array {
public:
explicit auto_array(T* initial = 0) {
array = initial;
}
~auto_array() {
delete[] array;
}
operator T*() const {
return array;
}
T* get() const {
return array;
}
T* release() {
T* old = array;
array = 0;
return old;
}
auto_array<T>& operator=(T* a) {
if (array != a) {
delete array;
array = a;
}
return *this;
}
private:
T* array;
};
inline u16 read16_le(const byte* b) {
return b[0] + (b[1] << 8);
}
inline void write16_le(byte* b, u16 value) {
b[0] = value & 0xFF;
b[1] = value >> 8;
}
inline u16 read16_be(const byte* b) {
return (b[0] << 8) + b[1];
}
inline void write16_be(byte* b, u16 value) {
b[0] = value >> 8;
b[1] = value & 0xFF;
}
inline u32 read32_le(const byte* b) {
return read16_le(b) + (read16_le(b + 2) << 16);
}
inline u32 read32_be(const byte* b) {
return (read16_be(b) << 16) + read16_be(b + 2);
}
struct RGB {
byte red;
byte green;
byte blue;
};
struct RGBA {
byte red;
byte green;
byte blue;
byte alpha;
};
struct BGR {
byte blue;
byte green;
byte red;
};
struct BGRA {
byte blue;
byte green;
byte red;
byte alpha;
};
}
#endif

728
corona/src/corona.h Normal file
View File

@ -0,0 +1,728 @@
/**
* Corona Image I/O Library
* Version 1.0.2
* (c) 2003 Chad Austin
*
* This API uses principles explained at
* http://aegisknight.org/cppinterface.html
*
* This code licensed under the terms of the zlib license. See
* license.txt.
*
*
* Note: When compiling this header in gcc, you may want to use the
* -Wno-non-virtual-dtor flag to get rid of those annoying "class has
* virtual functions but no virtual destructor" warnings.
*/
#ifndef CORONA_H
#define CORONA_H
#ifndef __cplusplus
#error Corona requires C++
#endif
#include <stddef.h>
// DLLs in Windows should use the standard calling convention
#ifndef COR_CALL
# if defined(WIN32) || defined(_WIN32)
# define COR_CALL __stdcall
# else
# define COR_CALL
# endif
#endif
// Export functions from the DLL
#ifndef COR_DECL
# if defined(WIN32) || defined(_WIN32)
# ifdef CORONA_EXPORTS
# define COR_DECL __declspec(dllexport)
# else
# define COR_DECL
# endif
# else
# define COR_DECL
# endif
#endif
// evil "identifier is too long in debug information" warning
#ifdef _MSC_VER
#pragma warning(disable : 4786)
#endif
#define COR_FUNCTION(ret) extern "C" COR_DECL ret COR_CALL
namespace corona {
/**
* File formats supported for reading or writing.
*/
enum FileFormat {
FF_AUTODETECT = 0x0100,
FF_PNG = 0x0101,
FF_JPEG = 0x0102,
FF_PCX = 0x0103,
FF_BMP = 0x0104,
FF_TGA = 0x0105,
FF_GIF = 0x0106,
};
/**
* Pixel format specifications. Pixel data can be packed in one of
* the following ways.
*/
enum PixelFormat {
PF_DONTCARE = 0x0200, /**< special format used when specifying a
desired pixel format */
PF_R8G8B8A8 = 0x0201, /**< RGBA, channels have eight bits of precision */
PF_R8G8B8 = 0x0202, /**< RGB, channels have eight bits of precision */
PF_I8 = 0x0203, /**< Palettized, 8-bit indices into palette */
PF_B8G8R8A8 = 0x0204, /**< BGRA, channels have eight bits of precision */
PF_B8G8R8 = 0x0205, /**< BGR, channels have eight bits of precision */
};
/**
* Axis specifications. The image can be flipped along the following
* axes.
*/
enum CoordinateAxis {
CA_X = 0x0001,
CA_Y = 0x0002,
};
/**
* A helper class for DLL-compatible interfaces. Derive your cross-DLL
* interfaces from this class.
*
* When deriving from this class, do not declare a virtual destructor
* on your interface.
*/
class DLLInterface {
private:
/**
* Destroy the object, freeing all associated memory. This is
* the same as a destructor.
*/
virtual void COR_CALL destroy() = 0;
public:
/**
* "delete image" should actually call image->destroy(), thus putting the
* burden of calling the destructor and freeing the memory on the image
* object, and thus on Corona's side of the DLL boundary.
*/
void operator delete(void* p) {
if (p) {
DLLInterface* i = static_cast<DLLInterface*>(p);
i->destroy();
}
}
};
/**
* A helper class for DLL-compatible interface implementations. Derive
* your implementations from DLLImplementation<YourInterface>.
*/
template<class Interface>
class DLLImplementation : public Interface {
public:
/**
* So the implementation can put its destruction logic in the destructor,
* as natural C++ code does.
*/
virtual ~DLLImplementation() { }
/**
* Call the destructor in a Win32 ABI-compatible way.
*/
virtual void COR_CALL destroy() {
delete this;
}
/**
* So destroy()'s "delete this" doesn't go into an infinite loop,
* calling the interface's operator delete, which calls destroy()...
*/
void operator delete(void* p) {
::operator delete(p);
}
};
/**
* An image object represents a rectangular collections of pixels.
* They have a width, a height, and a pixel format. Images cannot
* be resized.
*/
class Image : public DLLInterface {
public:
/**
* Get image width.
* @return image width
*/
virtual int COR_CALL getWidth() = 0;
/**
* Get image height.
* @return image height
*/
virtual int COR_CALL getHeight() = 0;
/**
* Get pixel format.
* @return pixel format
*/
virtual PixelFormat COR_CALL getFormat() = 0;
/**
* Get pixel buffer. The pixels are packed in the format defined
* by the image's pixel format.
*
* @return pointer to first element in pixel buffer
*/
virtual void* COR_CALL getPixels() = 0;
/**
* Get the palette. Pixels are packed in the format defined by
* getPaletteFormat().
*
* @return pointer to first palette entry
*/
virtual void* COR_CALL getPalette() = 0;
/**
* Get the number of entries in the palette.
*
* @return number of palette entries
*/
virtual int COR_CALL getPaletteSize() = 0;
/**
* Get the format of the colors in the palette.
*
* @return pixel format of palette entries
*/
virtual PixelFormat COR_CALL getPaletteFormat() = 0;
};
/**
* Represents a random-access file, usually stored on a disk. Files
* are always binary: that is, they do no end-of-line
* transformations. File objects are roughly analogous to ANSI C
* FILE* objects.
*/
class File : public DLLInterface {
public:
/**
* The different ways you can seek within a file.
*/
enum SeekMode {
BEGIN, /**< relative to the beginning of the file */
CURRENT, /**< relative to the current position in the file */
END /**< relative to the end of the file: position should
be negative*/
};
/**
* Read size bytes from the file, storing them in buffer.
*
* @param buffer buffer to read into
* @param size number of bytes to read
*
* @return number of bytes successfully read
*/
virtual int COR_CALL read(void* buffer, int size) = 0;
/**
* Write size bytes from buffer to the file.
*
* @param buffer buffer that contains the data to write
* @param size number of bytes to write
*
* @return number of bytes successfully written
*/
virtual int COR_CALL write(const void* buffer, int size) = 0;
/**
* Jump to a new position in the file, using the specified seek
* mode. Remember: if mode is END, the position must be negative,
* to seek backwards from the end of the file into its contents.
* If the seek fails, the current position is undefined.
*
* @param position position relative to the mode
* @param mode where to seek from in the file
*
* @return true on success, false otherwise
*/
virtual bool COR_CALL seek(int position, SeekMode mode) = 0;
/**
* Get current position within the file.
*
* @return current position
*/
virtual int COR_CALL tell() = 0;
};
/// Describes a file format that Corona supports.
class FileFormatDesc {
protected:
virtual ~FileFormatDesc() { }
public:
/// Actual FileFormat being described.
virtual FileFormat getFormat() = 0;
/// Short description of format, such as "PNG Files" or "JPEG Files"
virtual const char* getDescription() = 0;
/// @{
/// List of supported extensions, such as {"bmp", "rle", "dib"}
virtual size_t getExtensionCount() = 0;
virtual const char* getExtension(size_t i) = 0;
/// @}
};
/// PRIVATE API - for internal use only
namespace hidden {
// these are extern "C" so we don't mangle the names
// API information
COR_FUNCTION(const char*) CorGetVersion();
COR_FUNCTION(FileFormatDesc**) CorGetSupportedReadFormats();
COR_FUNCTION(FileFormatDesc**) CorGetSupportedWriteFormats();
// creation
COR_FUNCTION(Image*) CorCreateImage(
int width,
int height,
PixelFormat format);
COR_FUNCTION(Image*) CorCreateImageWithPixels(
int width,
int height,
PixelFormat format,
void* pixels);
COR_FUNCTION(Image*) CorCreatePalettizedImage(
int width,
int height,
PixelFormat format, // must be a palettized format
int palette_size,
PixelFormat palette_format);
COR_FUNCTION(Image*) CorCloneImage(
Image* source,
PixelFormat format);
// loading
COR_FUNCTION(Image*) CorOpenImage(
const char* filename,
FileFormat file_format);
COR_FUNCTION(Image*) CorOpenImageFromFile(
File* file,
FileFormat file_format);
// saving
COR_FUNCTION(bool) CorSaveImage(
const char* filename,
FileFormat file_format,
Image* image);
COR_FUNCTION(bool) CorSaveImageToFile(
File* file,
FileFormat file_format,
Image* image);
// conversion
COR_FUNCTION(Image*) CorConvertImage(
Image* image,
PixelFormat format);
COR_FUNCTION(Image*) CorConvertPalette(
Image* image,
PixelFormat palette_format);
COR_FUNCTION(Image*) CorFlipImage(
Image* image,
int coordinate_axis);
// files
COR_FUNCTION(File*) CorOpenFile(const char* name, bool writeable);
COR_FUNCTION(File*) CorCreateMemoryFile(const void* buffer, int size);
// utility
COR_FUNCTION(int) CorGetPixelSize(PixelFormat format);
}
/* PUBLIC API */
/**
* Return the Corona version string.
*
* @return Corona version information
*/
inline const char* GetVersion() {
return hidden::CorGetVersion();
}
/**
* Returns a null-terminated array of FileFormatDesc* pointers that
* describe the file formats Corona can read. The array is owned by
* Corona, so do not delete it when you are done using it.
*/
inline FileFormatDesc** GetSupportedReadFormats() {
return hidden::CorGetSupportedReadFormats();
}
/**
* Returns a null-terminated array of FileFormatDesc* pointers that
* describe the file formats Corona can write. The array is owned
* by Corona, so do not delete it when you are done using it.
*/
inline FileFormatDesc** GetSupportedWriteFormats() {
return hidden::CorGetSupportedWriteFormats();
}
/**
* Create a new, blank image with a specified width, height, and
* format. If pixels is specified, Corona uses them to initialize
* the contents of the image. Corona does *not* take ownership of
* the pixel memory, so the caller is responsible for cleaning up
* after itself. If pixels is not specified, the new image is
* filled with zeroes.
*
* @param width width of the new image
* @param height height of the new image
* @param format format the pixels are stored in, cannot be PF_DONTCARE
* @param pixels pixel buffer used to initialize the new image
*
* @return newly created blank image
*/
inline Image* CreateImage(
int width,
int height,
PixelFormat format,
void* pixels = 0)
{
return hidden::CorCreateImageWithPixels(width, height, format, pixels);
}
/**
* Create a new, blank image with a specified width, height, format,
* and palette.
*
* @param width width of image
* @param height height of image
* @param format format of palette indices, should be PF_I8
* @param palette_size number of colors in palette
* @param palette_format pixel format of palette entries
*/
inline Image* CreateImage(
int width,
int height,
PixelFormat format,
int palette_size,
PixelFormat palette_format)
{
return hidden::CorCreatePalettizedImage(
width, height, format,
palette_size, palette_format);
}
/**
* Create a new image from an old one. If format is specified, the
* new image is converted to that pixel format. If format is not
* specified, the new image simply uses the same format as the
* source. If the image could not be cloned or the pixel format is
* invalid, CloneImage returns 0.
*
* @param source image to clone
* @param format format the new image is stored in, defaults to PF_DONTCARE
*
* @return new image cloned from the source, 0 if failure
*/
inline Image* CloneImage(
Image* source,
PixelFormat format = PF_DONTCARE)
{
return hidden::CorCloneImage(source, format);
}
/**
* Opens an image from the default filesystem. This function simply
* forwards the call to OpenImage(file, file_format, pixel_format)
* with a standard C library file.
*
* See OpenImage(fs, filename, file_format, pixel_format) for more
* information.
*
* @param filename image filename to open
* @param file_format file format the image is stored in, or FF_AUTODETECT
* to try all loaders
* @param pixel_format desired pixel format, or PF_DONTCARE to use image's
* native format
*
* @return the image loaded from the disk, or 0 if it cannot be opened
*/
inline Image* OpenImage(
const char* filename,
PixelFormat pixel_format = PF_DONTCARE,
FileFormat file_format = FF_AUTODETECT)
{
return hidden::CorConvertImage(
hidden::CorOpenImage(filename, file_format),
pixel_format);
}
/**
* Opens an image from the specified file.
*
* If file_format is FF_AUTODETECT, the loader tries
* to load each format until it finds one that succeeds. Otherwise,
* it tries the specific loader specified.
*
* If pixel_format is PF_DONTCARE, the new image object has the
* pixel format closest to the image's format on disk. Otherwise,
* the pixels are converted to the specified format before the image
* is returned.
*
* @param file name of the file that contains the image
* @param file_format file format the image is stored in, or FF_AUTODETECT
* to try all loaders
* @param pixel_format desired pixel format, or PF_DONTCARE to use image's
* native format
*
* @return the image loaded from the file, or 0 if it cannot be opened
*/
inline Image* OpenImage(
File* file,
PixelFormat pixel_format = PF_DONTCARE,
FileFormat file_format = FF_AUTODETECT)
{
return hidden::CorConvertImage(
hidden::CorOpenImageFromFile(file, file_format),
pixel_format);
}
/// For compatibility. This function may be deprecated.
inline Image* OpenImage(
const char* filename,
FileFormat file_format,
PixelFormat pixel_format = PF_DONTCARE)
{
return OpenImage(filename, pixel_format, file_format);
}
/// For compatibility. This function may be deprecated.
inline Image* OpenImage(
File* file,
FileFormat file_format,
PixelFormat pixel_format = PF_DONTCARE)
{
return OpenImage(file, pixel_format, file_format);
}
/**
* Saves an image to a file in the default filesystem. This
* function simply calls SaveImage(file, file_format, image)
* with a standard C library file.
*
* See SaveImage(fs, filename, file_format, image) for more information.
*
* @param filename name of the file to save the image to
* @param file_format file format in which to save image. if FF_AUTODETECT,
* SaveImage guesses the type from the file extension
* @param image image to save
*
* @return true if save succeeds, false otherwise
*/
inline bool SaveImage(
const char* filename,
FileFormat file_format,
Image* image)
{
return hidden::CorSaveImage(filename, file_format, image);
}
/**
* Saves an image to the specified file. This function saves image
* to a file of type file_format. If file_format is not a supported
* output type, the function fails. As of now, Corona only supports
* saving images of type FF_PNG and FF_TGA.
*
* @note This function may create the file even if the save does not
* succeed, so users of this library should remove the file after
* the call to SaveImage().
*
* @param file file in which to save the image
* @param file_format file format in which to save image -- must not be
* FF_AUTODETECT
* @param image image to save
*
* @return true if the save succeeds, false otherwise
*/
inline bool SaveImage(
File* file,
FileFormat file_format,
Image* image)
{
return hidden::CorSaveImageToFile(file, file_format, image);
}
/**
* Converts an image from one format to another, destroying the old
* image. If source is 0, the function returns 0. If format is
* PF_DONTCARE or the source and target formats match, returns the
* unmodified source image. If a valid conversion is not found,
* ConvertImage destroys the old image and returns 0. For example,
* ConvertImage does not support creating a palettized image from a
* direct color image yet.
*
* @param source image to convert
* @param format desired format -- can be PF_DONTCARE
*
* @return valid image object if conversion succeeds, 0 otherwise
*/
inline Image* ConvertImage(Image* source, PixelFormat format) {
return hidden::CorConvertImage(source, format);
}
/**
* Converts the palette of a palettized image from one format to
* another, destroying the old image. If the source is 0, the
* palette_format is PF_DONTCARE, or the source and target formats
* match, the function returns the unmodified source image. If a
* valid conversion is not found or invalid inputs are given (such
* as a direct-color source image), this function destroys the old
* image and returns 0.
*
* @param source palettized image to convert
* @param palette_format desired pixel format of palette
*
* @return valid image object if conversion succeeds, 0 otherwise
*/
inline Image* ConvertPalette(Image* source, PixelFormat palette_format) {
return hidden::CorConvertPalette(source, palette_format);
}
/**
* Flips the pixels in the image around the given axis.
*
* @param source image to flip
* @param coordinate_axis Axis around which to flip. Both CA_X and CA_Y
* can be specified by ORing them together.
*
* @return the image passed in
*/
inline Image* FlipImage(Image* source, int coordinate_axis) {
return hidden::CorFlipImage(source, coordinate_axis);
}
/**
* Returns a default File implementation.
*
* @param filename name of the file on local filesystem
* @param writeable whether the file can be written to
*/
inline File* OpenFile(const char* filename, bool writeable) {
return hidden::CorOpenFile(filename, writeable);
}
/**
* Creates a File implementation that reads from a buffer in memory.
* It stores a copy of the buffer that is passed in.
*
* The File object does <i>not</i> take ownership of the memory buffer.
* When the file is destroyed, it will not free the memory.
*
* @param buffer Pointer to the beginning of the data.
* @param size Size of the buffer in bytes.
*
* @return 0 if size is non-zero and buffer is null. Otherwise,
* returns a valid File object.
*/
inline File* CreateMemoryFile(const void* buffer, int size) {
return hidden::CorCreateMemoryFile(buffer, size);
}
/**
* Returns the number of bytes needed to store a pixel of a gixen format.
*
* @param format The format to query.
*
* @return Number of bytes each pixel takes, or 0 if the format is invalid.
*/
inline int GetPixelSize(PixelFormat format) {
return hidden::CorGetPixelSize(format);
}
/**
* Returns true if the pixel format does not require a palette; that
* is, if each pixel itself contains color data.
*
* @param format The format to query.
*
* @return True if format is direct color, false otherwise.
*/
inline bool IsDirect(PixelFormat format) {
return (format == PF_R8G8B8A8 || format == PF_R8G8B8 ||
format == PF_B8G8R8A8 || format == PF_B8G8R8);
}
/**
* Returns true if the pixel format requires a palette; that
* is, if each pixel is an index into a separate palette.
*
* @param format The format to query.
*
* @return True if format is palettized, false otherwise.
*/
inline bool IsPalettized(PixelFormat format) {
return format == PF_I8;
}
/**
* Returns the number of color entries in a palette for an image
* of the given format.
*
* @param format The format to query.
*
* @return Number of color entries, or 0 if the format is not palettized.
*/
inline int GetPaletteSize(PixelFormat format) {
return (format == PF_I8 ? 256 : 0);
}
}
#endif