art2bmp

2008.02.08
art2bmp
Recently when digging through my old floppies. I came across a bunch of art work I did in the early nineties in an obscure freeware art program called "VGA Art Studio". It generated it's own proprietary ".art" file extension, so here is a script to convert them to bmps. If you have an "art" file, it's probably a different format.

But in case there is another person on the planet with this need, here you go!

#!/usr/bin/perl
=head1 NAME

art2bmp - Converts "VGA Art Studio files" to BMP

=head1 SYNOPSIS

B I

=head1 DESCRIPTION

Converts "VGA Art Studio" (1991) files to BMP files. Not to be confused with the 
numerous other formats using the ".art" extension. If you have a .art file, it's 
almost certainly a different format.

Does not handle any mask data if present.

=head1 AUTHOR

Lee Pumphret
=cut

use strict;
use warnings;


my @files = glob(shift @ARGV);

foreach my $file (@files) {
    next and warn "Mismatched extension in filename [$file]... skipping\n" 
      unless $file=~/\.art$/i;
    print "processing $file...\n";
    open( IN, "<$file" ) or die "Couldn't open $file :$!";
    binmode(IN);
    my $buffer;

    my $ART_HEADER_SIZE = 8;    # .art file header size

    # Read the header
    ( read( IN, $buffer, $ART_HEADER_SIZE ) == $ART_HEADER_SIZE )
      or die "Error reading header data!";

    my ( $ident, $mask, $x, $y, $im_compressed, $mask_compressed, ) =
      unpack( "CCssCC", $buffer );

    unless ( $ident == 123 ) {
        die "Doesn't look like an ART file! [$ident]";
    }

    # Read the palette data (rgb triplets)
    my @pal;
    for ( 0 .. 255 ) {
        read( IN, $buffer, 3 ) == 3 or die "Error reading palette!";
        my @rgb = unpack( "CCC", $buffer );
        push @pal, pack( "CCC", reverse @rgb );
    }

    # Read image data
    my @imdata;
    my $pixel_count = 0;

    if ($im_compressed) {
        warn "Decompressing image [$x x $y]....\n";

        while ( $pixel_count < $x * $y ) {
            read( IN, $buffer, 1 ) == 1
              or die "Couldn't read! (have $pixel_count pixels $#imdata) $!";
            my $byte = unpack( "C", $buffer );

            if ( $byte < 128 ) {
                $imdata[ $pixel_count++ ] = $pal[$byte];
            }
            else {

                # It's RLE
                my $repeat = $byte - 128;
                read( IN, $buffer, 1 ) == 1 or die "Couldn't read! :$!";
                $byte = unpack( "C", $buffer );
                push @imdata, ( $pal[$byte] ) x ( $repeat + 1 );
                $pixel_count += $repeat + 1;
            }

        }

    }
    else {
        warn "$file is Uncompressed!";
        read( IN, $buffer, $x * $y ) == $x * $y or die "Short read on $file";
        @imdata = map { $pal[$_] } unpack( "C*", $buffer );

    }

    if ($mask){
      warn "Image has mask data, ignoring...";
    }


    # Write out the bmp file
    ( my $outfile = $file ) =~ s/art$/bmp/i;

    my $written = $#imdata;
    open( OUT, ">$outfile" ) or die "Couldn't open output!";
    binmode(OUT);

    # BMP 3.1 Header
    print OUT pack "C C l a a a a l l l l s s l l l l l l", 0x42, 0x4D,
      54 + ( $x * 3 ) * $y, 'l', 'e', 'e', 'p', 54, 0x28, $x, $y, 1, 24, 0, 0,
      0, 0, 0, 0;

    # Write out the image data, bottom to top
    while ( $written > 0 ) {
        print OUT @imdata[ $written - ( $x - 1 ) .. $written ];

        $written -= $x;
    }

    close OUT;

}

__END__
A snippet of documentation from "VGA Art Studio"
* Note, there were some typos, errors in the doc which I have corrected.

Appendix A:     VGA Art Studio .ART and .PAL file formats
=========================================================


The .ART file
-------------

All VGA Art Studio picture/cutout files are stored in this format. In fact,
there is no distinction between a picture or a cutout - a picture can be pasted
in as a cutting, and a cutting can be loaded in as a file.

There are essentially two image formats - compressed and uncompressed. Before
saving a file, VGA Art Studio works out which format will actually result in a
smaller file, and then saves the file in this format. Actually, it is not quite
as simple as that - each image may need a 'mask',  and whether or not they the
mask is compressed is independent of whether or not the picture itself is
compressed. However, the file format is still relatively easy to use, and is as
follows :

+----------+-----------------------------------------------------+-------+
|  Offset  | Description                                         | Bytes |
+----------+-----------------------------------------------------+-------+
|      0   | 123 dec. (acts as a file identifier)                |   1   |
|      1   | Mask included?           (1=yes, 0=no)              |   1   |
|      2   | Size (x) in pixels of the image - INTEL format      |   2   |
|      4   | Size (y) in pixels of the image - INTEL format      |   2   |
|      6   | Image data compressed?   (1=yes, 0=no)              |   1   |
|      7   | Mask data compressed ?   (1=yes, 0=no)              |   1   |
|      8   | Palette data - 256 x (R,G,B) triplets, with         |  768  |
|          | individual intensities from 0..255                  |       |
|    776   | Image data               (may be compressed)        |  ???  |
|    ???   | (optional) Mask data     (may be compressed)        |  ???  |
+----------------------------------------------------------------+-------+

As mentioned, both the image data and the mask data may be compressed depending
on the values in bytes 6 and 7 of the header. The two formats are the same for
both the mask and the image, and work as follows:

The data in its uncompressed form consists of 64000 bytes, in a straight
forward raster format - with 1 byte per pixel - (acting as an index into the
palette in the case of image data - in the case of mask data, a non-zero value
indicates that the corresponding image pixel IS to be displayed). The bytes are
stored row by row, with each row running from left to right, in standard raster
format.

In the compressed format, the algorithm for uncompressing the data runs as
follows :

+-----------------------------+
| Read the next byte of data  |
| from the file, (unless we   |<-------------------------------------------+
| now have data for 64000     |                                            |
| pixels, in which case stop).|                                            |
+-----------------------------+                                            |
        |                                                            |
       \ /                                                           |
   +--------------------+    Y     +----------------------------------+    |
   | Is the byte <128 ? |--------->| Use this byte for the next pixel |----+
   +--------------------+          +----------------------------------+    ^
        |  N                                                         |
       \ /                                                           |
+--------------------------------------------------------------------+     |
| Subtract 128 from the value of the byte - call this C. Then read   |     |
| the next byte - this gives you the colour of the next (C+1)        |-----+
| pixels.                                                            |
+--------------------------------------------------------------------+

Note:

Compressed data is stored a line at a time. Compression using a byte >128 as a
repeat count MUST NOT wrap round from one line to the beginning of the next.



The .PAL file
-------------

This is very very straightforward. Each file is 769 bytes long, and consists of
the following data:

+----------+-----------------------------------------------------+-------+
|  Offset  | Description                                         | Bytes |
+----------+-----------------------------------------------------+-------+
|      0   | 124 dec. (acts as a file identifier)                |   1   |
|      1   | Palette data - 256 x (R,G,B) triplets, with         |  768  |
|          | individual intensities from 0..255                  |       |
+----------------------------------------------------------------+-------+


----------------------------------------------------------------------------
Appendix D : About Mooose Software
==================================

"Founded" in 1990 by :

Ben Stragnell
Graham Sanderson
Samer Abdallah

       \     \ /            \/    ___//                                        
     \_ /    //             \]   //~~~                                         
       \\    ]]            //   //                                             
      \__\ _]_\_          _\\ __/\//                                           
    __ _____\        /_\//  _                                            
      __ _/     \/~~~~~~\/ \__ //                                              
       _/       ]        ]    \/                                               
         /]  /  \  ]                                                     
        /  ](0  0)]                                                      
       /   ]      ]                                                      
____________~   ]        ]                                                     
    \ <    > /                                                     
         / \______/                                                      
         ]      ]                                                        
         ]                                                               
        ]                                                                

The moose is without doubt one of the finest animals ever invented, its
majestic antlers towering skyward, as it stands proudly, one of Nature's
most cunning creations. It's also got a really cool name, and you can put
an extra 'o' in it, to make it last just that little bit longer. Oh yes.

Keep an eye out for more releases from Mooose Software, and please support
the Public Domain!
pnt2bmp < previous
Scroll to top