Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

[SOLVED] Rm2k's XYZ image format

I'm trying to make a program which will load those XYZ image file formats. Here's the information I currently have on this format but it's hard to understand. I hope somebody will have a clarification of this.

RM2k XYZ Graphics Format
Simple documentation file v1.0 by Janus (janusfury@citlink.net)

Header:
4 bytes: XYZ1
2 bytes, unsigned int: Width
2 bytes, unsigned int: Height

Data:
ZLib-compressed. Use ZLib.dll uncompress to decompress it...
The output size of the data is (image and palette):
(Width * Height) + (256 * 3)
The input size of the data is:
FileSize - 8
In english, the data starts after the header and ends at the end of the file, and the image, when decompressed, is just a palette and then image data.
The palette is simply a series of colors in RGB order, no 4th byte for the palette entries.
The data is just a bunch of palette indexes, in standard x,y order.

So far I can read the header easily. I can decompress the data with zlib. But when I try to make an image (for instance the ASCII logo) the letters go to the side and the colors are all not right.

I am converting red, green and blue using this formulae: (r OR (g SHIFT LEFT 8) OR (b SHIFT LEFT 16))

Can anybody explain how these series of colors in rgb order are stored in the file, and ultimately can anyone show me how to load this file normally at all.

Thank you for help. :)
 
While I'm not sure about RM2K, I know that RMXP and RMVX both store images in memory in the very odd and relatively unusual bgr format, as opposed to the simple rgb format.
 
Code to load and save XYZ images using SDL
Code:
 

//////////////////////////////////////// xyz.h /////////////////////////////////////////

#ifndef XYZ_H

#define XYZ_H

 

#include <string>

#include "SDL.h"

 

bool save_XYZ(SDL_Surface* surface, const std::string& filename);

SDL_Surface* load_XYZ(const std::string& filename);

#endif

 

//////////////////////////////////////// xyz.cpp /////////////////////////////////////////

#include <cstdio>

#include "zlib.h"

#include "xyz.h"

 

bool save_XYZ(SDL_Surface* surface, const std::string& filename)

{

        FILE *file;

        size_t return_value;

        uLongf size;

        uLongf destSize;

        char header[4] = {'X', 'Y', 'Z', '1'};

        char *pixel;

        unsigned char *buffer;

        unsigned char *destBuffer;

        unsigned short width;

        unsigned short height;

        int i;

        int zlibErrorValue;

    

        SDL_LockSurface(surface);

        width = surface->w;

        height = surface->h;

        size = 768 + (width * height);

        destSize = (size * (uLongf)1.001) + 12;

        buffer = new unsigned char[size];

        destBuffer = new unsigned char[destSize];

        if ((buffer == NULL) || (destBuffer == NULL))

        {   

            printf("Error XYZ Writer: No memory left");

            return false;

        }

 

        for(i=0;i < 256; i++)

        {

            buffer[i * 3] = surface->format->palette->colors[i].r;

            buffer[i * 3 + 1] = surface->format->palette->colors[i].g;

            buffer[i * 3 + 2] = surface->format->palette->colors[i].b;

        }

        pixel = (char*)surface->pixels;

        for (i=0; i < (width * height); i++)

        {

            buffer[i + 768] = pixel[i];

        }

        SDL_UnlockSurface(surface);

        zlibErrorValue = compress2((Bytef*)destBuffer,  &destSize, (Bytef*)buffer, size, 9);

        delete buffer;

        file = fopen(filename.c_str(), "wb");

        return_value = fwrite(header, 1, sizeof(header), file);

        delete header;

        return_value = fwrite(&width, 1, sizeof(width), file);

        return_value = fwrite(&height, 1, sizeof(height), file);

        fseek(file, 8, SEEK_SET);

        return_value = fwrite(destBuffer, 1, destSize, file);

        fclose(file);

        return true;

}

 

SDL_Surface* load_XYZ(const std::string& filename)

{

    FILE *file;

    unsigned char *buffer;

    unsigned char *destBuffer;

    unsigned int size;

    uLongf destSize;

    unsigned short width;

    unsigned short height;

    int zlibErrorValue;

    SDL_Surface *surface;

    SDL_Palette *palette;

    int i;

    char *pixel;

 

    file = fopen(filename.c_str(), "rb");

    if (file != NULL)

    {

        char* header;

        header = new char[4];

        if (header == NULL)

        {   

            printf("Error XYZ Reader: No memory left");

            return NULL;

        }

        bool return_value;

        return_value = fread(header, 1, 4, file);

        if (strcmp(header, "XYZ1"))

        {

            delete header;

            return_value = fread(&width, 1, 2, file);

            return_value = fread(&height, 1, 2, file);

            fseek(file, 0, SEEK_END);

            size = ftell(file);

            fseek(file, 8, SEEK_SET);

            destSize = 768 + (width * height);

            destBuffer = new unsigned char[destSize];

            buffer = new unsigned char[size - 8];

            if ((buffer == NULL) || (destBuffer == NULL))

            {   

                printf("Error XYZ Reader: No memory left");

                return NULL;

            }

            return_value = fread(buffer, 1, size - 8, file);

            fclose(file);

            zlibErrorValue = uncompress((Bytef*)destBuffer, &destSize, (Bytef*)buffer, (uLongf)(size - 8));

            delete buffer;

            surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0);

    

            SDL_LockSurface(surface);

 

            palette = (surface->format)->palette;

            for (i = 0; i < 256; i++)

            {

                palette->colors[i].r = destBuffer[i * 3];

                palette->colors[i].g = destBuffer[i * 3 + 1];

                palette->colors[i].b = destBuffer[i * 3 + 2];

            }

            pixel = (char*)surface->pixels;

            for (i = 0; i < (width * height); i++)

            {

                pixel[i] = destBuffer[i + 768];

            }

            delete destBuffer;

        }

        else

        {

            perror("XYZ Reader Error: Not a valid XYZ file.");

            delete header;

            fclose(file);

            return NULL;

        }

    }

    else

    {

        perror("XYZ Reader: Error reading file.");

        return NULL;

    }

    SDL_UnlockSurface(surface);

    return surface;

}

 

Explanation:
the file is divided in 3 parts:
- the header which is "XYZ1"
- 2 integers of 2 bytes each with the width and height of the image
- the compressed data

the data size is the files size - the first 8 bytes (header and ints)
when you decompress the data you get:
- a palette
- the bitmap

the palette contains 256 colors, each colors has r, g and b values.
thats 768 bytes.
the rest is the bitmap, which is just an array of (width*height) size with a number that indicates te palette color.

thats all, sorry for the bad english.
 
Thank you for example! You helped a lot. :)

Have one question though. In my place im using delphi/c++ and with image component you have Image.Canvas.Pixel[X,Y] (it reads pixel color as an integer) to set the pixels, well in your case:

Code:
 

 for (i = 0; i < (width * height); i++)

            {

                pixel[i] = destBuffer[i + 768];

            }

 

In my case i thought to use code like this:

Code:
 

  // I'm considering that x and y variables are already declared somewhere up in the program 

  while (y < height) {

    Image.Canvas.Pixel[x,y] = destBuffer[i + 768];

    x++;

    if (x > width) {

     x = 0;

     y++;

    }

    i++;

  }

 

Could this be okay, or you have a better idea? :)
 
are you using an indexed image format?
the xyz pixels just contains the palette position where its the color, they doesnt contain any rgb value. If your canvas is indexed i think that code should be fine. if not, you can always fill every pixel checking for the palette color like:
Code:
 

RGBPixel pixel;

pixel.r = palette[ destBuffer[i + 768] ];

pixel.g = palette[ destBuffer[i + 768] + 1 ];

pixel.b = palette[ destBuffer[i + 768] + 2 ];

 
 
Well I made it indexed. :)
I figured my problem out, I didn't define variables correctly and was using signed instead of unsigned (stupid error i know). XD

Anyways, I made a Delphi version if anyone needs it.

Code:
 

procedure LoadXYZ(filename : string; Image : TImage);

var

 // Header

 head : array [0..3] of AnsiChar;

 wid, hei : Word;

 // Streams

 o_file : TFileStream;

 c_mem, d_mem : TMemoryStream;

 data_dec : TZDecompressionStream;

 // Other

 r, g, b, pixel : Byte;

 i, x, y : integer;

 palette : array [0..255] of Cardinal; // Palette

 OutBitmap : TBitmap; // Output bitmap

begin

 // Works with Zlib version 1.2.3

 o_file := TFileStream.Create(filename, fmOpenRead);

 

 // Create Memory streams

 c_mem := TMemoryStream.Create;

 d_mem := TMemoryStream.Create;

 try

   // Check if file is OK

   o_file.Read(head, 4);

   if (head = 'XYZ1') = False then raise Exception.Create('Invalid format.');

 

   // Load Width and Height

   o_file.Read(wid, sizeof(wid));

   o_file.Read(hei, sizeof(hei));

 

   // Copy rest of image data for decompression

   c_mem.CopyFrom(o_file, o_file.Size - 8);

 

 finally

   o_file.Free;

 end;

 

 data_dec := TZDecompressionStream.Create(c_mem);

 OutBitmap := TBitmap.Create;

 try

   OutBitmap.Width := wid;

   OutBitmap.Height := hei;

 

   d_mem.LoadFromStream(data_dec);

 

   // Fill in Palette

   i := 0;

   while I < 256 do

   begin

    d_mem.Read(r, sizeof(byte));

    d_mem.Read(g, sizeof(byte));

    d_mem.Read(b, sizeof(byte));

    palette[I] := RGB(r, g, b);

    I := I + 1;

   end;

 

   // Fill in Pixels

   x := 0;

   y := 0;

   while y < hei do

   begin

     d_mem.Read(pixel, sizeof(byte));

     OutBitmap.Canvas.Pixels[x, y] := palette[pixel];

     x := x + 1;

     if x > wid - 1 then

     begin

       x := 0;

       y := y + 1;

     end;

   end;

 

   image.Picture.Bitmap.Assign(OutBitmap);

 finally

   data_dec.Free;

   d_mem.Free;

   c_mem.Free;

   OutBitmap.Free;

 end;

end;

 

Thanks again. :)

Here's a demo program too: http://www.mediafire.com/file/mpxvlncun ... 20demo.rar
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top