1 /* spctoppm.c - read a compressed Spectrum file and produce a portable pixmap
2 **
3 ** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11 */
12 
13 #include "ppm.h"
14 
15 #define ROWS 200
16 #define COLS 320
17 #define MAXVAL 7
18 
19 static void DoBitmap ARGS(( FILE* ifp ));
20 static void DoChar ARGS(( int n, char c ));
21 static void DoColormap ARGS(( FILE* ifp ));
22 
23 static char screen[ROWS*COLS/2];
24 static short sscreen[ROWS*COLS/4];
25 static pixel pal[ROWS][48];
26 static long colormap_length, bitmap_length;
27 
28 int
main(argc,argv)29 main( argc, argv )
30     int argc;
31     char* argv[];
32     {
33     FILE* ifp;
34     char c1, c2;
35     pixel* pixelrow;
36     register pixel* pP;
37     int row, col;
38 
39 
40     ppm_init( &argc, argv );
41 
42     /* Check args. */
43     if ( argc > 2 )
44 	pm_usage( "[spcfile]" );
45 
46     if ( argc == 2 )
47 	ifp = pm_openr( argv[1] );
48     else
49 	ifp = stdin;
50 
51     /* Check SPC file header. */
52     c1 = getc( ifp );
53     c2 = getc( ifp );
54 
55     if ( ( c1 != 'S' ) || ( c2 != 'P' ) )
56 	pm_error( "not a Spectrum picture" );
57 
58     /* Skip reserved bytes. */
59     getc( ifp );
60     getc( ifp );
61 
62     /* Get length of bitmap data. */
63     (void) pm_readbiglong( ifp, &bitmap_length );
64 
65     /* and colormap */
66     (void) pm_readbiglong( ifp, &colormap_length );
67 
68     /* Process bitmap. */
69     DoBitmap( ifp );
70 
71     /* Process colormap. */
72     DoColormap( ifp );
73 
74     pm_close( ifp );
75 
76     /* Write the PPM file. */
77     ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 );
78     pixelrow = ppm_allocrow( COLS );
79 
80     for ( row = 0; row < ROWS; ++row )
81 	{
82 	for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP )
83 	    {
84 	    int c, ind, b, plane, x1;
85 
86 	    /* Compute pixel value. */
87 	    ind = ( 80 * row ) + ( ( col >> 4 ) << 2 );
88 	    b = 0x8000 >> (col & 0xf);
89 	    c = 0;
90 	    for ( plane = 0; plane < 4; ++plane )
91 		if ( b & sscreen[ind+plane] )
92 		    c |= (1 << plane);
93 
94 	    /* Compute palette index. */
95 	    x1 = 10 * c;
96 	    if ( c & 1 )
97 		x1 -= 5;
98 	    else
99 		++x1;
100 	    if ( ( col >= x1 ) && ( col < ( x1 + 160 ) ) )
101 		c += 16;
102 	    if ( col >= ( x1 + 160 ) )
103 		c += 32;
104 
105 	    /* Store the proper color. */
106 	    *pP = pal[row][c];
107 	    }
108 	ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 );
109 	}
110 
111     pm_close( stdout );
112 
113     exit( 0 );
114     }
115 
116 static void
DoBitmap(ifp)117 DoBitmap( ifp )
118     FILE* ifp;
119     {
120     int i;
121     long count, data;
122     signed char h, c;
123 
124     /* Zero out first scan line. */
125     for ( i = 0; i < 160; ++i )
126 	screen[i] = 0;
127 
128     /* 'count' counts number of input bytes. */
129     count = 0;
130 
131     /* 'data' counts just data bytes. */
132     data = 0;
133 
134     while ( count < bitmap_length )
135 	{
136 	/* Get next record header. */
137 	h = getc( ifp );
138 	++count;
139 
140 	if ( ( h >= 0 ) && ( count < bitmap_length ) )
141 	    {
142 	    for ( i = 0; i <= h; ++i )
143 		{
144 		c = getc( ifp );
145 		++count;
146 		DoChar( data, c );
147 		++data;
148 		}
149 	    }
150 	else if ( ( h < 0 ) && ( count < bitmap_length ) )
151 	    {
152 	    c = getc( ifp );
153 	    ++count;
154 
155 	    for ( i = 0; i < ( 2 - h ); ++i )
156 		{
157 		DoChar( data, c );
158 		++data;
159 		}
160 	    }
161     }
162 
163     /* Convert the char version of the screen to short. */
164     for ( i = 0; i < ROWS*COLS/4; ++i )
165 	sscreen[i] = ( screen[i<<1] << 8 ) + ( 0xff & screen[(i<<1)+1] );
166     }
167 
168 static void
DoChar(int n,char c)169 DoChar( int n, char c )
170     {
171     int i;
172 
173     /* Compute screen index. */
174     i = 160 + 2 * ( n / 7960 ) + 8 * ( ( n % 7960 ) / 2 ) + ( n & 1 );
175     screen[i] = c;
176     }
177 
178 static void
DoColormap(ifp)179 DoColormap( ifp )
180     FILE* ifp;
181     {
182     int i, j, b;
183     short mask;
184 
185     /* Clear first three palettes. */
186     for ( j = 0; j < 48; ++j )
187 	PPM_ASSIGN( pal[0][j], 0, 0, 0 );
188 
189     /* Read the palettes. */
190     for ( i = 1; i < ROWS; ++i )
191 	for ( j = 0; j < 3; ++j )
192 	    {
193 	    (void) pm_readbigshort( ifp, &mask );
194 	    for ( b = 0; b < 15; ++b )
195 		if ( mask & ( 1 << b ) )
196 		    {
197 		    short k;
198 		    (void) pm_readbigshort( ifp, &k );
199 		    PPM_ASSIGN( pal[i][(j*16)+b],
200 			( k & 0x700 ) >> 8,
201 			( k & 0x070 ) >> 4,
202 			( k & 0x007 ) );
203 		    }
204 	    }
205     }
206