[comp.sys.acorn] 8-bit: PPM file converter/viewer

ian@ob1.uws.EDU.AU (Ian Walsh) (02/13/91)

	The following programs will allow PPM files to be viewed on a BBC Model
B (or any machine with BBC BASIC 2 - probably!).

	I didn't know whether to uuencode them or not, so I haven't. Sorry about
the size of this file! (I was going to wait till the binary group started).

	A full-colour PPM file will have to be 'quantised' to 8 colours,
using ppmquant. The colour map provided in the ppmquant man entry will
be used (I called mine ttl.map).

	eg. ppmquant -map ttl.map -fs image.ppm | ppmtobbc > p.image
				      ^^^^^^^^^    	     ^^^^^^^
				     input file           output file

	The resulting file (p.image from above) should be transfered to
your BBC. Then use the BBC BASIC program (at end of this article) to view
and save the image on your Beeb.

	It's probably a good idea to put the ppmquant stuff into a shell
script. I use one to convert GIF files:

	giftoppm $1.gif | ppmquant -map ttl.map -fs | ppmtobbc > p.$1

	Using the PBMPLUS package you can convert GIF/PCX/IFF/TIFF/BMP files
to the PPM format. If there's any interest I could post BBC versions of some
of these files for downloading (maybe when comp.binaries.acorn gets going).

					Andrew Leahy
Univeristy Of Western Sydney - Nepean
email: a.leahy@nepean.uws.edu.au

C Source for ppmtobbc:
======================

	Compile & run this program on the same machine which has the
PBMPLUS package. I haven't used any of the PBM library routines, I 
should really! I've only compiled it on one machine (Apollo DN3000),
but there shouldn't be any machine-specific code.
	Some checks are made on the input file: it must be a P6 file
(ie. PPM RAWBITS), the max value must be 255 (usually is anyway).
	The image dimensions are read, if they are greater than 160x256
a warning will be displayed, but it will still be converted.
	I've only just started programming in C, so if you've got any
comments or questions about my code please email me!

/* Convert PPM files to compressed R..R,G..G,B..B format,
	for displaying on a BBC micro in Mode 2 (160x256x8)

	by Andrew Leahy 12/2/91
	email: a.leahy@nepean.uws.edu.au
                            
	Usage: ppmtobbc [file]
	Input: stdin or file
	Output: stdout

	eg. ppmtobbc image.ppm > p.image
*/
         
#include <stdio.h>                    
               
#define BAD 0
#define OK 1
#define LO_MASK 0x80

FILE	*fp_in, *fp_out;                      
int	width, height;

main( int argc, char *argv[] )
{
char	dummy;

	if (argc == 1)
		fp_in = stdin;
	else
		if ((fp_in = fopen( argv[1], "r" )) == NULL) {
			fprintf( stderr, "Couldn't open %s\n", argv[1] );
			exit(0);
		}

	if (!PPM_ok()) exit(0);		/* if PPM file unacceptable die	*/

	fp_out = stdout;		/* all o/p to stdout		*/

	dummy = fgetc( fp_in );		/* read past linefeed character */		

	write_dim();			/* o/p image size		*/
                         
	write_map();			/* compress & reorder the image	*/

	fclose (fp_in );		/* close-up shop		*/
	fclose( fp_out );
}

int PPM_ok()/* is the PPM file reasonable? */
{              
#define PTYPE "P6"                                            
#define MAX_WIDTH 160
#define MAX_HEIGHT 512
#define MAX_COL 255
                                                    
char	ptype[2];
int	max_col, return_val = BAD;

	fscanf( fp_in, "%s", ptype );	/* get PPM info from file	*/
	fscanf( fp_in, "%d %d", &width, &height );
	fscanf( fp_in, "%d", &max_col );

	fprintf( stderr, "PPM info: %s, %dx%d, %d.\n", ptype, width, height, max_col );

	if (!strcmp( ptype, PTYPE ))
		if (max_col == MAX_COL)
			return_val = OK;
		else	
			fprintf( stderr, "ppmtobbc: PPM max colour value must be 255\n" );
	else
		fprintf( stderr, "ppmtobbc: PPM file type not P6 (PPM RAWBITS)\n" );

	if (width > MAX_WIDTH || height > MAX_HEIGHT)
		fprintf( stderr, "ppmtobbc: image may be too large!\n" );

	return return_val;
}

int write_dim()	/* write out the image type and size */ 
{                       
	fprintf( fp_out, "%s", "M2" ); /* for a Mode 2 screen */
	fputc( width /256, fp_out ); fputc( width %256, fp_out);   /* width  */
	fputc( height /256, fp_out ); fputc( height %256, fp_out); /* height */
}

int write_map()
{                                  
#define RED 0
#define GREEN 1
#define BLUE 2

char	row [80] [3]; /* should use malloc here to setup array */
int	y, x, byte, shift, colour;
           
	for( y=0; y < height; y++ ) {
                 
		/* Initialise array */

		for( byte=0; byte < width / 8; byte++ )
			row [byte] [RED] = row [byte] [GREEN] = row [byte] [BLUE] = 0;

		/* Read PPM row into array, masking lower 7 bits */

		for( x=0; x < width; x++ ) {

			byte = x / 8; shift = x % 8;
			row [byte] [RED]   += (fgetc( fp_in ) & 0x80) >> shift;
			row [byte] [GREEN] += (fgetc( fp_in ) & 0x80) >> shift;
			row [byte] [BLUE]  += (fgetc( fp_in ) & 0x80) >> shift;
		}
                          
		/* Write out each row in turn */
                             
	  	for( colour=0; colour < 3; colour++ )
			for( byte=0; byte < width / 8; byte++ )
				fputc( row [byte] [colour], fp_out );
	}
}

BBC BASIC 2 program:
====================

	Use this program to view the resulting file on your Beeb. The
image will be centred on a MODE 2 screen. If you want to save the image
to disc un-comment line 130 (replace the SAVE with SVPIC if you wish).
	I give all my PPM M2 files the prefix 'P', so when an image is
saved it is given the 'S' prefix. eg. P.PORSCHE becomes S.PORSCHE.
	The machine code is an attempt to speed up the loading, all it
basically does is VDU23,128,BGET#Y%;0;0;0;128 in a loop.
	Again, any problems/questions/improvements please email me!

 10 REM PPM M2 file viewer, by Andrew Leahy 6/2/91
 20 REM email: a.leahy@nepean.uws.edu.au
 30 ONERRORCLOSE#0:VDU4:REPORT:PRINT" at ";ERL:END
 40 INPUT"Enter RGB File to view : P."C$
 50 Y%=OPENIN("P."+C$):IF Y%=0 PROCerr("No Such File!")
 60 IF FNid<>"M2" PROCerr("Not a recognised file type!")
 70 W%=(BGET#Y%*256+BGET#Y%)DIV8:H%=BGET#Y%*256+BGET#Y%-1:REM Get size
 80 ?&70=W%:code=&900:PROCass
 90 MODE2:VDU5,29,640-32*W%;510-H%*2;:REM Centre image
100 FORI%=H%*4 TO0STEP-4:?&71=1
110 FORJ%=0TO2:MOVE0,I%:CALLcode:NEXT,
120 VDU4:CLOSE#0
130 REMOSCLI("SAVE S."+C$+" 3000 8000")
140 END
150 DEF PROCass
160 P%=code:oswrch=&FFEE:osbget=&FFD7
170 FOR pass=0TO2STEP2:[OPT pass
180 LDA #18:JSR oswrch:LDA #1:JSR oswrch:LDA &71:JSR oswrch \ GCOL1,?&71
190 ASL A:STA &71 \ ?&71= 2*?&71
200 LDX &70 \ Get width
210 .put_byte LDA#23:JSR oswrch \ VDU23,128,BGET#Y%;0;0;0;
220 LDA #128:JSR oswrch:JSR osbget:JSR oswrch
230 LDA #0:]:FOR I%=0TO6:[OPT pass:JSR oswrch:]:NEXT
240 [OPT pass:LDA #128:JSR oswrch \ VDU128
250 DEX:BNE put_byte:RTS:]
260 NEXT:ENDPROC
270 DEF FNid:=CHR$BGET#Y%+CHR$BGET#Y%
280 DEF PROCerr(A$):PRINT'A$:CLOSE#0:END