Example Pictor Decoder
Encyclopedia

Pictor PCPaint PIC image format

PICtor
PICtor PIC image format
PICtor is an image file format developed by John Bridges, the principal author of PCPaint, the first Paintbrush program for the PC. It was also the native file format for Pictor Paint and GRASP and became the first widely accepted DOS imaging standard.-Typical file format:The PICtor format is a...

is an image file format developed by John Bridges
John Bridges
John Bridges may refer to:*John Bridges * John E. Bridges, Chelan County Superior Court Judge in Washington state* John Bridges , British archer who competed at the 1908 Summer Olympics...

, the principal author of PCPaint
PCPaint
PCPaint was the first IBM PC-based mouse driven GUI paint program . It was developed by John Bridges and Doug Wolfgram.The hardware manufacturer Mouse Systems bundled PCPaint with millions of computer mice that they sold, making PCPaint also the best-selling MS-DOS-based paint program of the late...

, the first Paintbrush program for the PC. It was also the native file format for Pictor Paint
Pictor Paint
Pictor Paint was an improved version of PCPaint, the first IBM PC-based mouse driven GUI paint program. It was written by John Bridges, the primary author of PCPaint, and bundled with GRASP GRaphical System for Presentation also written by John Bridges...

 and GRASP (multimedia authoring software)  (also by Bridges) and became the first widely accepted DOS
MS-DOS
MS-DOS is an operating system for x86-based personal computers. It was the most commonly used member of the DOS family of operating systems, and was the main operating system for IBM PC compatible personal computers during the 1980s to the mid 1990s, until it was gradually superseded by operating...

imaging standard.

The PICtor format is quite well documented, but lacks a programming example like the one below.

Typical Pictor Decoder


// This source code is herewith released by me into the Public Domain.
// Bill Buckels, May 21, 2007
// You have a royalty-free right to use, modify, reproduce and distribute this
// source code and the binaries that it produces in any way you find useful

// this decoder is only a portion of the actual code required to
// display a Pictor PIC.

// It is based on the Pseudo-Code in the Pictor PC Paint File Format Summary
// http://www.fileformat.info/format/pictor/
// Reading of header information etc. is outside of here.

// The maximum display size in this particular example is 640 x 400
// Decoded Data is translated to 8bpp during the decoding term
// Doing so highly simplifies color translation.
// The usual amount of fiddling with data outside of here may not be
// entirely clear to someone who has never written an image loader.
// Other than that, reading this example in addition to the Pseudo-Code
// should clarify what John Bridges wrote.

// Any display considerations would be exactly the same, whether we
// were decoding Pictor PIC, a PCX or whatever if we were translating from
// older native modes to a GUI display. 'Nuff said.

// The code below works for monochrome, 2 bit CGA, 4 bit EGA, and 8 bit MCGA

// I am assuming a 32 bit Windows platform with a BOOL and a TRUE and a FALSE
// no hardship to compile this in 'nix tho'
  1. define PICTOR_Int unsigned short // for 32 bit Windows
  2. define SUCCESS 0
  3. define FAILURE -1


unsigned char *picBlockBuf=NULL;
unsigned char *picScreenBuf=NULL;

int UnpackPictorData(FILE *fpPictor, int *scale, BOOL *spread)
{

// this routine uses a triple buffer method before handing-off to our
// standard MCGA image loader.

// 1. The block buffer expands the encoded data to raw data specific to the native mode.

// - for 4 plane EGA images this is a contiguous block of monochrome data.

// - for single plane images (all the other images that we handle), the bytes are stored
// similarly to a windows BMP in their respective packed pixel format which contains
// the color indices according to bit depths. Unlike the windows BMP these blocks
// are not padded on scanline boundaries to a DWORD value (thank goodness).

// 2. After unpacking each block, the block buffer is expanded into an 1 byte per pixel
// contiguous buffer. Each subsequent block unpacks at the offset where the previous
// block left-off.

// - for single plane images this results in a buffer of width x height x 1 byte per pixel

// - for 4 plane EGA images this results in a quadruple buffer width x height x 1 byte x 4,
// with the entire image of each of the 4 planes' monochrome pixels repeated once
// for each plane.

// 3. If the image is a 4 plane EGA it is then given an extra massage, and the planes are
// combined and expanded to an EGA indices into a width x height x 1 byte per pixel.
// 4. By that time all pixels are 8 bit values and according to how we will be
// positioning and scaling the image for display in our standard MCGA mode routines
// the image is prepared for display by moving into position in the rawbuffer.
// 5. The image is still upside-down but will be flipped, and CGA Mode monochrome images
// will be corrected for aspect before final display by the standard MCGA loader.

unsigned char buf[256], RunValue;
PICBLOCKHEADER h;
int c, status = FAILURE;
unsigned width;
unsigned long offset, y, yoff, ymax, x, xoff, xmax, ystart, realx;
unsigned long bufx, bufy, target;
unsigned char backcolor = 0;

PICTOR_Int byteCount, byteRunLength, blockCount, idx;

status = SUCCESS;

blockCount = 0;

free(picBlockBuf);
picBlockBuf = NULL;

free(picScreenBuf);
picScreenBuf = NULL;

offset = 0;
realx = (unsigned long)Pictor_head.xsize;

// put other exceptions here or allow for uneven boundaries
switch(Pictor_head.pictortype)
{
case Pictor_MONO: while (Pictor_head.xsize%8 != 0)Pictor_head.xsize++;
break;
case Pictor_CGA: while (Pictor_head.xsize%4 != 0)Pictor_head.xsize++;
break;
case Pictor_EGA1: while (Pictor_head.xsize%2 != 0)Pictor_head.xsize++;
break;
case Pictor_EGA4: while (Pictor_head.xsize%8 != 0)Pictor_head.xsize++;
break;
case Pictor_VGA: break; // never a problem with these
}

bufx = (unsigned long)Pictor_head.xsize;
bufy = (unsigned long)Pictor_head.ysize;
target = bufx;
target *= bufy;
target *= (unsigned long) Pictor_head.planes;

// safety
picScreenBuf = malloc(target + 640);

if (NULL picScreenBuf)return FAILURE;

// set-up complete
// start unpacking here
StartOfDataBlock:

if (NULL != picBlockBuf) {

// unpack the block segment into the raster-aligned 8 bit screen buffer
// by now we have ended-up with contiguous data in the block
// that can be unpacked 1 pixel at a time for each plane of color data

width = (unsigned)Pictor_head.pixeldivisor;
width *= (unsigned)h.RunLength;

// safety play
if ((offset + width) > target) width = (unsigned)target - offset;

// we are handling specific formats only
// everything is expanded from the picBlockBuf to 8bpp into the picScreenBuf
switch(Pictor_head.pictortype)
{
case Pictor_MONO:
Mono2Svga(picBlockBuf,(unsigned char *)&picScreenBuf[offset],(unsigned)h.RunLength);
break;
case Pictor_CGA:
Cga2Svga(picBlockBuf,(unsigned char *)&picScreenBuf[offset],(unsigned)h.RunLength);
break;
case Pictor_EGA1:
Vga2Svga(picBlockBuf,(unsigned char *)&picScreenBuf[offset],(unsigned)h.RunLength);
break;
case Pictor_EGA4:
Mono2Svga(picBlockBuf,(unsigned char *)&picScreenBuf[offset],(unsigned)h.RunLength);
break;
case Pictor_VGA:
memcpy((unsigned char *)&picScreenBuf[offset], picBlockBuf,(unsigned)h.RunLength);
break;
}

width = (unsigned)Pictor_head.pixeldivisor;
width *= (unsigned)h.RunLength;

offset += width;
blockCount++;

// free the last block we used after putting the pixels into the 8 bit buffer
free(picBlockBuf);
picBlockBuf = NULL;

}

// safety plays
if (offset > target)goto EndOfRead; // done

if (Pictor_head.numblocks <= blockCount)
goto EndOfRead; // done

memset(&h,0,sizeof(PICBLOCKHEADER));

// Read BlockSize value from data block header
// Read RunLength value from data block header
// Read RunMarker value from data block header

if (1 != fread(buf,5,1,fpPictor))goto EndOfRead; // done

h.BlockSize = Shorty((unsigned char *)&buf[0]);
h.RunLength = Shorty((unsigned char *)&buf[2]);
h.RunMarker = buf[4]; /* Start-of-run indicator */

byteCount = 0;

if (h.RunLength 0 || h.BlockSize

0) {
goto EndOfRead; // something is wrong
}

picBlockBuf = malloc((unsigned)h.RunLength);
if (NULL

picBlockBuf)goto EndOfRead;

StartOfRun:

if ((c = fgetc(fpPictor))

EOF)goto EndOfRead; // done
RunValue = (unsigned char)c;

// If the byte is a RunMarker
if (RunValue

h.RunMarker) {

if ((c = fgetc(fpPictor))EOF)goto EndOfRead; // done
byteRunLength = (PICTOR_Int)c;

// If the byte following the RunMarker is not 0 it is the ByteRunLength
if (byteRunLength !=0) {
// Read this byte as the ByteRunLength
// Read the next byte as the RunValue
if ((c = fgetc(fpPictor))EOF)goto EndOfRead; // done
// Write the RunValue `RunLength' times.
RunValue = (unsigned char)c;
for (idx = 0; idx < byteRunLength; idx++) {
picBlockBuf[byteCount] = RunValue;
byteCount++;
if (byteCount >= h.RunLength)break;
}
}
// If the byte following the RunMarker is 0
else {
// Read the next word as the ByteRunLength
// Read the next byte as the RunValue
if (1 != fread(buf,2,1,fpPictor))goto EndOfRead; // done
byteRunLength = Shorty((unsigned char *)&buf[0]);
if ((c = fgetc(fpPictor))EOF)goto EndOfRead; // done
// Write the RunValue `RunLength' times.
RunValue = (unsigned char)c;
for (idx = 0; idx < byteRunLength; idx++) {
picBlockBuf[byteCount] = RunValue;
byteCount++;
if (byteCount >= h.RunLength)break;
}
}

}
else {
// If the byte following the header is not a RunMarker
// Write the byte as a literal RunValue
picBlockBuf[byteCount] = RunValue;
byteCount++;
}

// now check the byte count
// If the number of bytes written so far does not equal the RunLength
if (byteCount < h.RunLength) goto StartOfRun;

// If the number of bytes written so far equals the RunLength
goto StartOfDataBlock;

EndOfRead:

free(picBlockBuf);
picBlockBuf = NULL;

// clear the background with something that makes sense
// subject to fine-tuning
switch(Pictor_head.pictortype)
{
case Pictor_MONO:
if (picScreenBuf[0] 0)backcolor = 1;
else backcolor = 0;
break;
case Pictor_CGA:
if (picScreenBuf[0] 3)backcolor = 0;
else backcolor = 3;
break;
case Pictor_EGA1:
case Pictor_EGA4:
backcolor = 0xf;
break;
case Pictor_VGA:
backcolor = 0xff;
break;
}

for (y = 0; y < 400; y++)
memset(&rawbuffer[y*640], backcolor, 640);

// planar EGA's have been expanded to 4 times the buffered size of a single plane image
// these are now stored like a mono image and must be consolidated into an EGA value
// and then repacked as an 8 bit indices at the start of the buffer
if (Pictor_head.pictortype Pictor_EGA4) {

unsigned char c0,c1,c2,c3;
unsigned long offset1, offset2, offset3;

// combine the 4 bit planes into an MCGA byte and use the normal routines from here
offset = 0;
offset1 = bufx * bufy;
offset2 = offset1 * 2;
offset3 = offset1 * 3;
for (y = 0; y < bufy; y++) {
for (x = 0; x < bufx; x++) {
c0 = picScreenBuf[offset];
c1 = picScreenBuf[offset1];
c2 = picScreenBuf[offset2];
c3 = picScreenBuf[offset3];
picScreenBuf[offset] = ((c3<<3) | (c2<<2) | (c1<<1) | c0);
offset++;
offset1++;
offset2++;
offset3++;
}
}
}

// move the pictor image into the rawbuffer
switch(scale[0]) {
case 1:
spread[0] = FALSE;
yoff = (long) 200 - Pictor_head.ysize;
yoff = (yoff/2);
xoff = (long) 320 - Pictor_head.xsize;
xoff = (xoff/2);
ystart = 0;
xmax = 320;
ymax = 200;
break;

case 2:
default:

if (spread[0] TRUE && Pictor_head.ysize < 201) {
yoff = (long) 200 - Pictor_head.ysize;
yoff = (yoff/2);
ystart = 0;
}
else {
spread[0] = FALSE;
if (Pictor_head.ysize > 400) {
yoff = 0;
ystart = bufy - 400;
}
else {
yoff = (long) 400 - Pictor_head.ysize;
yoff = (yoff/2);
ystart = 0;
}
}
xoff = (long) 640 - Pictor_head.xsize;
xoff = (xoff/2);
xmax = 640;
ymax = 400;
break;
}

for (y = ystart; y < bufy; y++, yoff++) {
offset = y * bufx;
target = (yoff * xmax) + xoff;
for (x = 0; x < bufx; x++) {
rawbuffer[target] = picScreenBuf[offset];
offset++;
target++;
}
}

if (Pictor_head.pictortype Pictor_MONO || Pictor_head.pictortype

Pictor_CGA)
{
// remap CGA and Mono to VGA 16 Color Order Now
offset = 0;
for (y = 0; y < 400; y++) {
for (x = 0; x < 640; x++) {
switch(rawbuffer[offset])
{
case 1:
if (Pictor_head.pictortype

Pictor_MONO)
rawbuffer[offset] = BWHITE;
else
rawbuffer[offset] = BLUE;
break;
case 2:
rawbuffer[offset] = RED;
break;
case 3:
rawbuffer[offset] = BWHITE;
break;
}
offset++;
}
}
}

free(picScreenBuf);
picScreenBuf = NULL;

return SUCCESS;

}
The source of this article is wikipedia, the free encyclopedia.  The text of this article is licensed under the GFDL.
 
x
OK