blob: 08fd558450c42075c46094ea7bad387f9c724dfe [file] [log] [blame]
// ==========================================================
// Sun rasterfile Loader
//
// Design and implementation by
// - Hervé Drolon (drolon@infonie.fr)
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================
#include "FreeImage.h"
#include "Utilities.h"
// ----------------------------------------------------------
// Constants + headers
// ----------------------------------------------------------
#ifdef _WIN32
#pragma pack(push, 1)
#else
#pragma pack(1)
#endif
typedef struct tagSUNHEADER {
DWORD magic; // Magic number
DWORD width; // Image width in pixels
DWORD height; // Image height in pixels
DWORD depth; // Depth (1, 8, 24 or 32 bits) of each pixel
DWORD length; // Image length (in bytes)
DWORD type; // Format of file (see RT_* below)
DWORD maptype; // Type of colormap (see RMT_* below)
DWORD maplength; // Length of colormap (in bytes)
} SUNHEADER;
#ifdef _WIN32
#pragma pack(pop)
#else
#pragma pack()
#endif
// ----------------------------------------------------------
// Following the header is the colormap, for maplength bytes (unless maplength is zero),
// then the image. Each row of the image is rounded to 2 bytes.
#define RAS_MAGIC 0x59A66A95 // Magic number for Sun rasterfiles
// Sun supported type's
#define RT_OLD 0 // Old format (raw image in 68000 byte order)
#define RT_STANDARD 1 // Raw image in 68000 byte order
#define RT_BYTE_ENCODED 2 // Run-length encoding of bytes
#define RT_FORMAT_RGB 3 // XRGB or RGB instead of XBGR or BGR
#define RT_FORMAT_TIFF 4 // TIFF <-> standard rasterfile
#define RT_FORMAT_IFF 5 // IFF (TAAC format) <-> standard rasterfile
#define RT_EXPERIMENTAL 0xffff // Reserved for testing
// These are the possible colormap types.
// if it's in RGB format, the map is made up of three byte arrays
// (red, green, then blue) that are each 1/3 of the colormap length.
#define RMT_NONE 0 // maplength is expected to be 0
#define RMT_EQUAL_RGB 1 // red[maplength/3], green[maplength/3], blue[maplength/3]
#define RMT_RAW 2 // Raw colormap
#define RESC 128 // Run-length encoding escape character
// ----- NOTES -----
// Each line of the image is rounded out to a multiple of 16 bits.
// This corresponds to the rounding convention used by the memory pixrect
// package (/usr/include/pixrect/memvar.h) of the SunWindows system.
// The ras_encoding field (always set to 0 by Sun's supported software)
// was renamed to ras_length in release 2.0. As a result, rasterfiles
// of type 0 generated by the old software claim to have 0 length; for
// compatibility, code reading rasterfiles must be prepared to compute the
// TRUE length from the width, height, and depth fields.
// ==========================================================
// Internal functions
// ==========================================================
static void
ReadData(FreeImageIO *io, fi_handle handle, BYTE *buf, DWORD length, BOOL rle) {
// Read either Run-Length Encoded or normal image data
static BYTE repchar, remaining= 0;
if (rle) {
// Run-length encoded read
while(length--) {
if (remaining) {
remaining--;
*(buf++)= repchar;
} else {
io->read_proc(&repchar, 1, 1, handle);
if (repchar == RESC) {
io->read_proc(&remaining, 1, 1, handle);
if (remaining == 0) {
*(buf++)= RESC;
} else {
io->read_proc(&repchar, 1, 1, handle);
*(buf++)= repchar;
}
} else {
*(buf++)= repchar;
}
}
}
} else {
// Normal read
io->read_proc(buf, length, 1, handle);
}
}
// ==========================================================
// Plugin Interface
// ==========================================================
static int s_format_id;
// ==========================================================
// Plugin Implementation
// ==========================================================
static const char * DLL_CALLCONV
Format() {
return "RAS";
}
static const char * DLL_CALLCONV
Description() {
return "Sun Raster Image";
}
static const char * DLL_CALLCONV
Extension() {
return "ras";
}
static const char * DLL_CALLCONV
RegExpr() {
return NULL;
}
static const char * DLL_CALLCONV
MimeType() {
return "image/x-cmu-raster";
}
static BOOL DLL_CALLCONV
Validate(FreeImageIO *io, fi_handle handle) {
BYTE ras_signature[] = { 0x59, 0xA6, 0x6A, 0x95 };
BYTE signature[4] = { 0, 0, 0, 0 };
io->read_proc(signature, 1, sizeof(ras_signature), handle);
return (memcmp(ras_signature, signature, sizeof(ras_signature)) == 0);
}
static BOOL DLL_CALLCONV
SupportsExportDepth(int depth) {
return FALSE;
}
static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type) {
return FALSE;
}
static BOOL DLL_CALLCONV
SupportsNoPixels() {
return TRUE;
}
// ----------------------------------------------------------
static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
SUNHEADER header; // Sun file header
WORD linelength; // Length of raster line in bytes
WORD fill; // Number of fill bytes per raster line
BOOL rle; // TRUE if RLE file
BOOL isRGB; // TRUE if file type is RT_FORMAT_RGB
BYTE fillchar;
FIBITMAP *dib = NULL;
BYTE *bits; // Pointer to dib data
WORD x, y;
if(!handle) {
return NULL;
}
BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
try {
// Read SUN raster header
io->read_proc(&header, sizeof(SUNHEADER), 1, handle);
#ifndef FREEIMAGE_BIGENDIAN
// SUN rasterfiles are big endian only
SwapLong(&header.magic);
SwapLong(&header.width);
SwapLong(&header.height);
SwapLong(&header.depth);
SwapLong(&header.length);
SwapLong(&header.type);
SwapLong(&header.maptype);
SwapLong(&header.maplength);
#endif
// Verify SUN identifier
if (header.magic != RAS_MAGIC) {
throw FI_MSG_ERROR_MAGIC_NUMBER;
}
// Allocate a new DIB
switch(header.depth) {
case 1:
case 8:
dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth);
break;
case 24:
dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
break;
case 32:
dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
break;
}
if (dib == NULL) {
throw FI_MSG_ERROR_DIB_MEMORY;
}
// Check the file format
rle = FALSE;
isRGB = FALSE;
switch(header.type) {
case RT_OLD:
case RT_STANDARD:
case RT_FORMAT_TIFF: // I don't even know what these format are...
case RT_FORMAT_IFF: //The TIFF and IFF format types indicate that the raster
//file was originally converted from either of these file formats.
//so lets at least try to process them as RT_STANDARD
break;
case RT_BYTE_ENCODED:
rle = TRUE;
break;
case RT_FORMAT_RGB:
isRGB = TRUE;
break;
default:
throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
}
// set up the colormap if needed
switch(header.maptype) {
case RMT_NONE :
{
if (header.depth < 24) {
// Create linear color ramp
RGBQUAD *pal = FreeImage_GetPalette(dib);
int numcolors = 1 << header.depth;
for (int i = 0; i < numcolors; i++) {
pal[i].rgbRed = (BYTE)((255 * i) / (numcolors - 1));
pal[i].rgbGreen = (BYTE)((255 * i) / (numcolors - 1));
pal[i].rgbBlue = (BYTE)((255 * i) / (numcolors - 1));
}
}
break;
}
case RMT_EQUAL_RGB:
{
BYTE *r, *g, *b;
// Read SUN raster colormap
int numcolors = 1 << header.depth;
if((DWORD)(3 * numcolors) > header.maplength) {
// some RAS may have less colors than the full palette
numcolors = header.maplength / 3;
} else {
throw "Invalid palette";
}
r = (BYTE*)malloc(3 * numcolors * sizeof(BYTE));
g = r + numcolors;
b = g + numcolors;
RGBQUAD *pal = FreeImage_GetPalette(dib);
io->read_proc(r, 3 * numcolors, 1, handle);
for (int i = 0; i < numcolors; i++) {
pal[i].rgbRed = r[i];
pal[i].rgbGreen = g[i];
pal[i].rgbBlue = b[i];
}
free(r);
break;
}
case RMT_RAW:
{
BYTE *colormap;
// Read (skip) SUN raster colormap.
colormap = (BYTE *)malloc(header.maplength * sizeof(BYTE));
io->read_proc(colormap, header.maplength, 1, handle);
free(colormap);
break;
}
}
if(header_only) {
// header only mode
return dib;
}
// Calculate the line + pitch
// Each row is multiple of 16 bits (2 bytes).
if (header.depth == 1) {
linelength = (WORD)((header.width / 8) + (header.width % 8 ? 1 : 0));
} else {
linelength = (WORD)header.width;
}
fill = (linelength % 2) ? 1 : 0;
unsigned pitch = FreeImage_GetPitch(dib);
// Read the image data
switch(header.depth) {
case 1:
case 8:
{
bits = FreeImage_GetBits(dib) + (header.height - 1) * pitch;
for (y = 0; y < header.height; y++) {
ReadData(io, handle, bits, linelength, rle);
bits -= pitch;
if (fill) {
ReadData(io, handle, &fillchar, fill, rle);
}
}
break;
}
case 24:
{
BYTE *buf, *bp;
buf = (BYTE*)malloc(header.width * 3);
for (y = 0; y < header.height; y++) {
bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
ReadData(io, handle, buf, header.width * 3, rle);
bp = buf;
if (isRGB) {
for (x = 0; x < header.width; x++) {
bits[FI_RGBA_RED] = *(bp++); // red
bits[FI_RGBA_GREEN] = *(bp++); // green
bits[FI_RGBA_BLUE] = *(bp++); // blue
bits += 3;
}
} else {
for (x = 0; x < header.width; x++) {
bits[FI_RGBA_RED] = *(bp + 2); // red
bits[FI_RGBA_GREEN] = *(bp + 1);// green
bits[FI_RGBA_BLUE] = *bp; // blue
bits += 3; bp += 3;
}
}
if (fill) {
ReadData(io, handle, &fillchar, fill, rle);
}
}
free(buf);
break;
}
case 32:
{
BYTE *buf, *bp;
buf = (BYTE*)malloc(header.width * 4);
for (y = 0; y < header.height; y++) {
bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
ReadData(io, handle, buf, header.width * 4, rle);
bp = buf;
if (isRGB) {
for (x = 0; x < header.width; x++) {
bits[FI_RGBA_ALPHA] = *(bp++); // alpha
bits[FI_RGBA_RED] = *(bp++); // red
bits[FI_RGBA_GREEN] = *(bp++); // green
bits[FI_RGBA_BLUE] = *(bp++); // blue
bits += 4;
}
}
else {
for (x = 0; x < header.width; x++) {
bits[FI_RGBA_RED] = *(bp + 3); // red
bits[FI_RGBA_GREEN] = *(bp + 2); // green
bits[FI_RGBA_BLUE] = *(bp + 1); // blue
bits[FI_RGBA_ALPHA] = *bp; // alpha
bits += 4;
bp += 4;
}
}
if (fill) {
ReadData(io, handle, &fillchar, fill, rle);
}
}
free(buf);
break;
}
}
return dib;
} catch (const char *text) {
if(dib) {
FreeImage_Unload(dib);
}
FreeImage_OutputMessageProc(s_format_id, text);
}
return NULL;
}
// ==========================================================
// Init
// ==========================================================
void DLL_CALLCONV
InitRAS(Plugin *plugin, int format_id) {
s_format_id = format_id;
plugin->format_proc = Format;
plugin->description_proc = Description;
plugin->extension_proc = Extension;
plugin->regexpr_proc = RegExpr;
plugin->open_proc = NULL;
plugin->close_proc = NULL;
plugin->pagecount_proc = NULL;
plugin->pagecapability_proc = NULL;
plugin->load_proc = Load;
plugin->save_proc = NULL;
plugin->validate_proc = Validate;
plugin->mime_proc = MimeType;
plugin->supports_export_bpp_proc = SupportsExportDepth;
plugin->supports_export_type_proc = SupportsExportType;
plugin->supports_icc_profiles_proc = NULL;
plugin->supports_no_pixels_proc = SupportsNoPixels;
}