Task 604: .RAS File Format

Task 604: .RAS File Format

1. List of Properties for the .RAS File Format

The .RAS file format (Sun Raster) consists of a fixed 32-byte header followed by an optional color map and the image data. All header fields are 32-bit unsigned integers (DWORD) stored in big-endian byte order. The intrinsic properties (header fields) are:

  • MagicNumber: A 32-bit magic identifier, always 0x59A66A95 (used to verify the file type).
  • Width: The width of the image in pixels.
  • Height: The height of the image in pixels.
  • Depth: The number of bits per pixel (common values: 1, 8, 24, 32).
  • Length: The length of the image data in bytes (may be 0 in older files, in which case it's calculated as (Width * Height * Depth) / 8).
  • Type: The encoding type (0: Old/uncompressed, 1: Standard/uncompressed, 2: RLE compressed, 3: RGB order instead of BGR, etc.).
  • ColorMapType: The type of color map (0: None, 1: RGB, 2: Raw).
  • ColorMapLength: The length of the color map in bytes (0 if no color map).

These properties define the core structure. The color map (if present) and image data follow the header but are not fixed "properties" as they vary.

(Note: .SUN is an equivalent extension for the same format.)

3. Embedded HTML JavaScript for Drag-and-Drop .RAS File Property Dump

Here's a self-contained HTML snippet with JavaScript that can be embedded in a blog (e.g., Ghost platform). It creates a drag-and-drop area. When a .RAS file is dropped, it reads the file, parses the 32-byte header using DataView for big-endian values, validates the magic number, and displays the properties on the screen.

Drag and drop a .RAS file here

4. Python Class for .RAS File Handling

import struct
import os

class RasFileHandler:
    def __init__(self):
        self.magic = 0x59A66A95
        self.width = 0
        self.height = 0
        self.depth = 0
        self.length = 0
        self.type = 0
        self.color_map_type = 0
        self.color_map_length = 0

    def read_and_decode(self, file_path):
        if not os.path.exists(file_path):
            print("File not found.")
            return False
        
        with open(file_path, 'rb') as f:
            header = f.read(32)
            if len(header) < 32:
                print("File too small for header.")
                return False
            
            # Unpack big-endian (> for big-endian, I for uint32)
            unpacked = struct.unpack('>8I', header)
            self.magic, self.width, self.height, self.depth, self.length, self.type, self.color_map_type, self.color_map_length = unpacked
            
            if self.magic != 0x59A66A95:
                print("Invalid .RAS file (wrong magic number).")
                return False
            
            self.print_properties()
            return True

    def print_properties(self):
        print("MagicNumber: 0x{:08X}".format(self.magic))
        print("Width: {}".format(self.width))
        print("Height: {}".format(self.height))
        print("Depth: {}".format(self.depth))
        print("Length: {}".format(self.length))
        print("Type: {}".format(self.type))
        print("ColorMapType: {}".format(self.color_map_type))
        print("ColorMapLength: {}".format(self.color_map_length))

    def write(self, file_path, width=100, height=100, depth=8, type=1, color_map_type=0, color_map_length=0, image_data=b''):
        self.width = width
        self.height = height
        self.depth = depth
        self.type = type
        self.color_map_type = color_map_type
        self.color_map_length = color_map_length
        self.length = len(image_data)  # Update length based on provided data
        
        header = struct.pack('>8I', self.magic, self.width, self.height, self.depth, self.length, self.type, self.color_map_type, self.color_map_length)
        
        with open(file_path, 'wb') as f:
            f.write(header)
            # Skip color map for simplicity (assume none)
            f.write(image_data)  # Write provided image data (e.g., raw bytes)
        
        print("File written successfully.")
        self.print_properties()

# Example usage:
# handler = RasFileHandler()
# handler.read_and_decode('example.ras')
# handler.write('output.ras', image_data=b'\x00' * (100*100))  # Simple 100x100 8-bit grayscale

5. Java Class for .RAS File Handling

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class RasFileHandler {
    private static final long MAGIC = 0x59A66A95L;
    private long width;
    private long height;
    private long depth;
    private long length;
    private long type;
    private long colorMapType;
    private long colorMapLength;

    public boolean readAndDecode(String filePath) {
        try (RandomAccessFile raf = new RandomAccessFile(filePath, "r")) {
            byte[] header = new byte[32];
            if (raf.read(header) < 32) {
                System.out.println("File too small for header.");
                return false;
            }

            ByteBuffer bb = ByteBuffer.wrap(header).order(ByteOrder.BIG_ENDIAN);
            long magic = bb.getInt() & 0xFFFFFFFFL;
            this.width = bb.getInt() & 0xFFFFFFFFL;
            this.height = bb.getInt() & 0xFFFFFFFFL;
            this.depth = bb.getInt() & 0xFFFFFFFFL;
            this.length = bb.getInt() & 0xFFFFFFFFL;
            this.type = bb.getInt() & 0xFFFFFFFFL;
            this.colorMapType = bb.getInt() & 0xFFFFFFFFL;
            this.colorMapLength = bb.getInt() & 0xFFFFFFFFL;

            if (magic != MAGIC) {
                System.out.println("Invalid .RAS file (wrong magic number).");
                return false;
            }

            printProperties();
            return true;
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
            return false;
        }
    }

    public void printProperties() {
        System.out.printf("MagicNumber: 0x%08X%n", MAGIC);
        System.out.printf("Width: %d%n", width);
        System.out.printf("Height: %d%n", height);
        System.out.printf("Depth: %d%n", depth);
        System.out.printf("Length: %d%n", length);
        System.out.printf("Type: %d%n", type);
        System.out.printf("ColorMapType: %d%n", colorMapType);
        System.out.printf("ColorMapLength: %d%n", colorMapLength);
    }

    public void write(String filePath, long width, long height, long depth, long type, long colorMapType, long colorMapLength, byte[] imageData) {
        this.width = width;
        this.height = height;
        this.depth = depth;
        this.type = type;
        this.colorMapType = colorMapType;
        this.colorMapLength = colorMapLength;
        this.length = imageData.length;

        try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw")) {
            ByteBuffer bb = ByteBuffer.allocate(32).order(ByteOrder.BIG_ENDIAN);
            bb.putInt((int) MAGIC);
            bb.putInt((int) this.width);
            bb.putInt((int) this.height);
            bb.putInt((int) this.depth);
            bb.putInt((int) this.length);
            bb.putInt((int) this.type);
            bb.putInt((int) this.colorMapType);
            bb.putInt((int) this.colorMapLength);

            raf.write(bb.array());
            // Skip color map for simplicity
            raf.write(imageData);

            System.out.println("File written successfully.");
            printProperties();
        } catch (IOException e) {
            System.out.println("Error writing file: " + e.getMessage());
        }
    }

    // Example usage:
    // public static void main(String[] args) {
    //     RasFileHandler handler = new RasFileHandler();
    //     handler.readAndDecode("example.ras");
    //     byte[] data = new byte[100*100]; // Simple 100x100 8-bit
    //     handler.write("output.ras", 100, 100, 8, 1, 0, 0, data);
    // }
}

6. JavaScript Class for .RAS File Handling

(This is for Node.js, using fs for file I/O. For browser, use FileReader as in part 3.)

const fs = require('fs');

class RasFileHandler {
  constructor() {
    this.magic = 0x59A66A95;
    this.width = 0;
    this.height = 0;
    this.depth = 0;
    this.length = 0;
    this.type = 0;
    this.colorMapType = 0;
    this.colorMapLength = 0;
  }

  readAndDecode(filePath) {
    if (!fs.existsSync(filePath)) {
      console.log('File not found.');
      return false;
    }

    const buffer = fs.readFileSync(filePath);
    if (buffer.length < 32) {
      console.log('File too small for header.');
      return false;
    }

    const dataView = new DataView(buffer.buffer);
    this.magic = dataView.getUint32(0, false); // Big-endian
    if (this.magic !== 0x59A66A95) {
      console.log('Invalid .RAS file (wrong magic number).');
      return false;
    }

    this.width = dataView.getUint32(4, false);
    this.height = dataView.getUint32(8, false);
    this.depth = dataView.getUint32(12, false);
    this.length = dataView.getUint32(16, false);
    this.type = dataView.getUint32(20, false);
    this.colorMapType = dataView.getUint32(24, false);
    this.colorMapLength = dataView.getUint32(28, false);

    this.printProperties();
    return true;
  }

  printProperties() {
    console.log(`MagicNumber: 0x${this.magic.toString(16).toUpperCase()}`);
    console.log(`Width: ${this.width}`);
    console.log(`Height: ${this.height}`);
    console.log(`Depth: ${this.depth}`);
    console.log(`Length: ${this.length}`);
    console.log(`Type: ${this.type}`);
    console.log(`ColorMapType: ${this.colorMapType}`);
    console.log(`ColorMapLength: ${this.colorMapLength}`);
  }

  write(filePath, width = 100, height = 100, depth = 8, type = 1, colorMapType = 0, colorMapLength = 0, imageData = Buffer.alloc(0)) {
    this.width = width;
    this.height = height;
    this.depth = depth;
    this.type = type;
    this.colorMapType = colorMapType;
    this.colorMapLength = colorMapLength;
    this.length = imageData.length;

    const header = Buffer.alloc(32);
    const dataView = new DataView(header.buffer);
    dataView.setUint32(0, this.magic, false);
    dataView.setUint32(4, this.width, false);
    dataView.setUint32(8, this.height, false);
    dataView.setUint32(12, this.depth, false);
    dataView.setUint32(16, this.length, false);
    dataView.setUint32(20, this.type, false);
    dataView.setUint32(24, this.colorMapType, false);
    dataView.setUint32(28, this.colorMapLength, false);

    const fullBuffer = Buffer.concat([header, imageData]); // Skip color map
    fs.writeFileSync(filePath, fullBuffer);

    console.log('File written successfully.');
    this.printProperties();
  }
}

// Example usage:
// const handler = new RasFileHandler();
// handler.readAndDecode('example.ras');
// handler.write('output.ras', 100, 100, 8, 1, 0, 0, Buffer.alloc(100*100));

7. C Implementation for .RAS File Handling

(C uses a struct instead of a class. Includes functions for read/decode/print and write.)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <endian.h> // For big-endian conversion if needed

struct RasHeader {
    uint32_t magic;
    uint32_t width;
    uint32_t height;
    uint32_t depth;
    uint32_t length;
    uint32_t type;
    uint32_t color_map_type;
    uint32_t color_map_length;
};

int read_and_decode(const char* file_path, struct RasHeader* header) {
    FILE* file = fopen(file_path, "rb");
    if (!file) {
        printf("File not found.\n");
        return 0;
    }

    uint32_t raw_header[8];
    if (fread(raw_header, sizeof(uint32_t), 8, file) < 8) {
        printf("File too small for header.\n");
        fclose(file);
        return 0;
    }
    fclose(file);

    // Convert from big-endian to host
    header->magic = be32toh(raw_header[0]);
    if (header->magic != 0x59A66A95) {
        printf("Invalid .RAS file (wrong magic number).\n");
        return 0;
    }

    header->width = be32toh(raw_header[1]);
    header->height = be32toh(raw_header[2]);
    header->depth = be32toh(raw_header[3]);
    header->length = be32toh(raw_header[4]);
    header->type = be32toh(raw_header[5]);
    header->color_map_type = be32toh(raw_header[6]);
    header->color_map_length = be32toh(raw_header[7]);

    print_properties(header);
    return 1;
}

void print_properties(const struct RasHeader* header) {
    printf("MagicNumber: 0x%08X\n", header->magic);
    printf("Width: %u\n", header->width);
    printf("Height: %u\n", header->height);
    printf("Depth: %u\n", header->depth);
    printf("Length: %u\n", header->length);
    printf("Type: %u\n", header->type);
    printf("ColorMapType: %u\n", header->color_map_type);
    printf("ColorMapLength: %u\n", header->color_map_length);
}

void write(const char* file_path, uint32_t width, uint32_t height, uint32_t depth, uint32_t type,
           uint32_t color_map_type, uint32_t color_map_length, const uint8_t* image_data, size_t data_size) {
    struct RasHeader header = {
        .magic = 0x59A66A95,
        .width = width,
        .height = height,
        .depth = depth,
        .length = (uint32_t)data_size,
        .type = type,
        .color_map_type = color_map_type,
        .color_map_length = color_map_length
    };

    FILE* file = fopen(file_path, "wb");
    if (!file) {
        printf("Error opening file for write.\n");
        return;
    }

    // Convert to big-endian
    uint32_t raw_header[8] = {
        htobe32(header.magic),
        htobe32(header.width),
        htobe32(header.height),
        htobe32(header.depth),
        htobe32(header.length),
        htobe32(header.type),
        htobe32(header.color_map_type),
        htobe32(header.color_map_length)
    };

    fwrite(raw_header, sizeof(uint32_t), 8, file);
    // Skip color map for simplicity
    if (image_data) {
        fwrite(image_data, 1, data_size, file);
    }

    fclose(file);
    printf("File written successfully.\n");
    print_properties(&header);
}

// Example usage:
// int main() {
//     struct RasHeader header;
//     read_and_decode("example.ras", &header);
//     uint8_t data[100*100] = {0}; // Simple 100x100 8-bit
//     write("output.ras", 100, 100, 8, 1, 0, 0, data, sizeof(data));
//     return 0;
// }