initial commit
This commit is contained in:
16
corona/Makefile
Normal file
16
corona/Makefile
Normal 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
275
corona/src/Convert.cpp
Normal 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
321
corona/src/Corona.cpp
Normal 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
52
corona/src/Debug.cpp
Normal 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
70
corona/src/Debug.h
Normal 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
|
55
corona/src/DefaultFileSystem.cpp
Normal file
55
corona/src/DefaultFileSystem.cpp
Normal 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
93
corona/src/MemoryFile.cpp
Normal 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
36
corona/src/MemoryFile.h
Normal 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
16
corona/src/Open.h
Normal 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
678
corona/src/OpenBMP.cpp
Normal 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
190
corona/src/OpenPCX.cpp
Normal 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
255
corona/src/OpenPNG.cpp
Normal 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
147
corona/src/OpenTGA.cpp
Normal 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
16
corona/src/Save.h
Normal 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
168
corona/src/SavePNG.cpp
Normal 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
49
corona/src/SaveTGA.cpp
Normal 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
101
corona/src/SimpleImage.h
Normal 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
27
corona/src/Types.h
Normal 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
127
corona/src/Utility.h
Normal 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
728
corona/src/corona.h
Normal 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
|
Reference in New Issue
Block a user