Jump to content
  • 0

Pixobjects to Sprite images


rje
 Share

Question

Back in my first job, we represented raster images in a text format we called "pixobjects".  Our data was monochrome, so an asterisk represented an "on" pixel, and a space or dot was "off".  So a file representing a house would look sort of like this:

=house1 4bpp 16x8


     ******    
   **********        
 **************
   **********
   *** ** ***   
   ****** ***   

I've embellished the header line, which used to only contain the name of the object and (optionally) its dimensions, to include bits per pixel, because I feel the need for this sort of command line tool again.  ALTHOUGH I remember that someone already WROTE a REAL sprite editor.

Nevertheless, I dashed out a Perl script that would handle 4bpp images in this textual format.  It can also handle a subset of 8bpp -- values 0 through 61.

use strict;
#
#  Convert pixobjects to CX16 sprites
#  USAGE: perl pix2bin.pl <input file>
#
=pod
  The pixobject file format is as follows:

  A header line, containing the object's name, its bits per pixel, and its dimensions.
  Data lines following.  Hexadecimal digits are interpreted properly.  

  Values from 0 up to 61 are handled as 0-9, A-Z, and a-z.
  The full 8 bit range of values are not handled correctly.

  Example:

=house1 4bpp 16x8


     ******    
   **********        
 **************
   **********
   *** ** ***   
   ****** ***   

This should be useful for creating small 4bpp sprites for the CX16.
I naturally want to write a complementary utility that will unpack 
a CX16 4bpp sprite into a pixobject file.

=cut


my $infile = shift || die "SYNOPSIS: $0 pixobj\n";

open my $in, '<', $infile;
my $header = <$in>;
my @data = <$in>;
close $in;

chomp @data;
my ($name, $bpp, $width, $height) = $header =~ /=(\w+) (4bpp|8bpp)\s+(\d\d?)x(\d\d?)/;
die "Cannot read header\n" unless $bpp && $width && $height;

my $dimensions = $3 . 'x' . $4;

my $outfile = sprintf("%s_%s_%s.bin", $bpp, $name, $dimensions);
print "===> $outfile <=== $bpp, $width x $height\n";

open my $out, '>', $outfile;
print $out pack 'xx';             # initial two null bytes

foreach my $line (@data)
{
   my @bits = split '', $line;
   my @line = ();

   my $odd   = 1;    # This is how we'll handle 4 bits per pixel.
   my $value = 0;

   for (0..$width-1) 
   {
       my $charval = getCharval($bits[$_]);

       if ($bpp =~ /4bpp/i && $odd)
       {
           $value = $charval << 4; # initial value = upper nybble
       }
       else                        # 8 bits OR we're even
       {
           $value += $charval;
           print $out pack 'C', $value;
           $value = 0;             # and reset
       }
       $odd = 1 - $odd;            # toggle for nybbles
   }
}
close $out;

sub getCharval
{
    my $c = shift;
    return ord($c)-48 if $c =~ /\d/;        # 0-9
    return ord($c)-55 if $c =~ /[A-Z]/;     # 10-35
    return ord($c)-64 if $c =~ /[a-z]/;     # 35-61 
    return 1 if $c eq '*';
    return 0; # everything else is a zero
}


 

Edited by rje
Link to comment
Share on other sites

6 answers to this question

Recommended Posts

  • 0

And here's the corresponding script to deconstruct a sprite into a pixobject.

 

use strict;
#
#  Convert CX16 sprites to pixobjects
#  USAGE: perl bin2pix.pl <4bpp | 8bpp> <width> <sprite file>
#
=pod
  The VERA sprite format has two varieties:

  4 bits per pixel
  8 bits per pixel

  This script is not smart enough to know the color depth;
  therefore, the command line has to specify "4bpp" or "8bpp".

  Similarly, the script is not smart enough to know the image width.

  The output is a pixobject with this format:

=NAME 4bpp|8bpp WIDTHxHEIGHT

  "NAME" is the filename minus the extension.
  WIDTH and HEIGHT are in pixels.

  What follows is the lines of data.  These values are supported:

  0-9 colors 0-9
  A-Z colors 10-35
  a-z colors 36-61

=cut
my $bpp    = shift || die "USAGE: $0 <4bpp | 8bpp> <width> <sprite file>\n";
my $width  = shift || die "USAGE: $0 $bpp <width> <sprite file>\n";
my $infile = shift || die "USAGE: $0 $bpp $width <sprite file>\n";

my ($name) = $infile =~ /^(\w+)\./;
my $line = '';
my @data = ();

open my $in, '<:raw', $infile || die "ERROR: cannot open sprite file [$infile]\n";
read $in, my $nullbytes, 2;

while (read($in, my $c, 1) != 0)
{
    my $v = unpack 'C', $c;
    if ($bpp eq '4bpp')  # two chars per byte
    {
       $line .= decodeByte($v >> 4);
       $line .= decodeByte($v & 0x0f);
    }
    else                 # one byte per byte
    {
       $line .= decodeByte($v); 
    }

    if (length($line) == $width)
    {
        push @data, $line;
        $line = '';
    }
}
close $in;

my $height = scalar @data;
my $dimensions = $width . 'x' . $height;
my $data = join "\n", @data;

print<<EOPIXOBJ;
=$name $bpp $dimensions
$data
EOPIXOBJ

sub decodeByte
{
    my $val = shift;
    return '.' if $val == 0;
    return '*' if $val == 1;
    return chr(48+$val) if $val < 10; # '0'..'9'
    return chr(55+$val) if $val < 36; # 'A'..'Z'
    return chr(61+$val) if $val < 61; # 'a'..'z'
    return '.'; # everything else is a dot
}

 

 

 

Edited by rje
Link to comment
Share on other sites

  • 0

Interesting. I used a similar technique as part of a program to draw a bitmap using only geometric shapes (rectangles) a few years ago as part of a programming challenge / puzzle.

In my case, I took the original bitmap converted it to 24 of these pixel maps. 8 channels for each of the three primary colors. I discarded the low order bits and searched for all the rectangles in each bitplane. It was an interesting approach, and analysis of the original image was a lot easier in text.

  • Like 1
Link to comment
Share on other sites

  • 0

Ha!  I had completely forgotten about the computer vision assignments that used text-bitmaps to trace edges, sharpen, compute moments, determine shape, etc.  Thanks for the reminder.

 

Edited by rje
Link to comment
Share on other sites

  • 0

Attached is a very old example of a pixobject, which I created at work using our handwriting analysis code which chops up connected writing based on best guesses.  As a smoke test, I scanned in pages from the Codex Serafinianus and forced our code to try to segment it.  The result is a fair pile of little snippets like this one.

The header is a bit opaque to me.  

=C: 64 105 1012 3682 0 0 

C :  means it thinks it's a character, but can't tell what character it is.  If for example it thought it was the letter "E", it would have shown C:E.  If on the other hand it thought it was a symbol, it would have used an S: and the guessed symbol name.

Then come the rows and columns.

Those three fields are mandatory, always.  The rest are optional.

 

The next two numbers are -- most likely -- the original raster position of the image on the page.  This allows the GUI to overlay the pixobject selection over the original raster image for human editing.

I'm not sure what those last two numbers were for.   They're always zero in the samples I've got.

 

 

codex-test.pix

Edited by rje
Link to comment
Share on other sites

  • 0

The 7F is interesting.  I wonder if 127 is the place-holder for "no known character".  If the character is guessed as a non-word character, the ASCII value would show up, but that's text (e.g. C:42 for the * character).

The 0A sounds like a Windows artifact, but it was produced on Solaris so it can't be that... unless the file transfer to Windows added it in (unlikely).  The C code itself could have written \r\n to the header to be friendlier to Windows-based code...

Edited by rje
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use