| // ========================================================== |
| // 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; |
| } |