Line data Source code
1 : /* Copyright (C) 2000-2012 by George Williams */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 : #include "gimage.h"
28 : #include <string.h>
29 :
30 : struct sgiheader {
31 : short magic; /* Magic (SGI identification) number */
32 : char format; /* Storage format (RLE=1 or VERBATIM=0) */
33 : char bpc; /* Bytes per pixel channel (1or2 bytes) */
34 : unsigned short dim; /* Number of dimensions (1,2,3) */
35 : unsigned short width; /* Width of image in pixels X-size */
36 : unsigned short height; /* Height of image in pixels Y-size */
37 : unsigned short chans; /* Number of channels (1,3,4) Z-size */
38 : long pixmin; /* Minimum pixel value (darkest) */
39 : long pixmax; /* Maximum pixel value (brightest) */
40 : char dummy[4]; /* Ignored */
41 : char imagename[80]; /* Image name (0..79chars + '\0') */
42 : long colormap; /* Colormap ID (0,1,2,3) */
43 : char pad[404]; /* Ignored (total=512bytes) */
44 : };
45 :
46 : #define SGI_MAGIC 474
47 : #define VERBATIM 0
48 : #define RLE 1
49 :
50 0 : static int getlong(FILE *fp, long *value) {
51 : /* Get Big-Endian long (32bit int) value. Return 0 if okay, -1 if error */
52 : int ch1, ch2, ch3, ch4;
53 :
54 0 : if ( (ch1=fgetc(fp))<0 || (ch2=fgetc(fp))<0 || \
55 0 : (ch3=fgetc(fp))<0 || (ch4=fgetc(fp))<0 ) {
56 0 : *value=0;
57 0 : return( -1 );
58 : }
59 0 : *value=(long)( (ch1<<24)|(ch2<<16)|(ch3<<8)|ch4 );
60 0 : return( 0 );
61 : }
62 :
63 0 : static int getshort(FILE *fp) {
64 : /* Get Big-Endian short 16bit value. Return value if okay, -1 if error */
65 : int ch1, ch2;
66 :
67 0 : if ( (ch1=fgetc(fp))<0 || (ch2=fgetc(fp))<0 )
68 0 : return( -1 );
69 :
70 0 : return( (ch1<<8) | ch2 );
71 : }
72 :
73 0 : static int getsgiheader(struct sgiheader *head,FILE *fp) {
74 : /* Get Header info. Return 0 if read input file okay, -1 if read error */
75 0 : if ( (head->magic=getshort(fp))<0 || head->magic!=SGI_MAGIC || \
76 0 : (head->format=fgetc(fp))<0 || \
77 0 : (head->bpc=fgetc(fp))<0 || \
78 0 : (head->dim=getshort(fp))==(unsigned short)-1 || \
79 0 : (head->width=getshort(fp))==(unsigned short)-1 || \
80 0 : (head->height=getshort(fp))==(unsigned short)-1|| \
81 0 : (head->chans=getshort(fp))==(unsigned short)-1 || \
82 0 : getlong(fp,&head->pixmin) || \
83 0 : getlong(fp,&head->pixmax) || \
84 0 : fread(head->dummy,sizeof(head->dummy),1,fp)<1 || \
85 0 : fread(head->imagename,sizeof(head->imagename),1,fp)<1 || \
86 0 : getlong(fp,&head->colormap) || \
87 0 : fread(head->pad,sizeof(head->pad),1,fp)<1 )
88 0 : return( -1 );
89 :
90 : /* Check if header information okay (for us to use here ) */
91 0 : if ( (head->format!=VERBATIM && head->format!=RLE) || \
92 0 : (head->bpc!=1 && head->bpc!=2) || \
93 0 : head->dim<1 || head->dim>3 || \
94 0 : head->pixmax>65535 || (head->pixmax>255 && head->bpc==1) || \
95 0 : (head->chans!=1 && head->chans!=3 && head->chans!=4) || \
96 0 : head->pixmax<0 || head->pixmin<0 || head->pixmin>=head->pixmax || \
97 0 : head->colormap!=0 )
98 0 : return( -1 );
99 :
100 0 : return( 0 );
101 : }
102 :
103 0 : static long scalecolor(struct sgiheader *header,long value) {
104 0 : return( (value-header->pixmin)*255L/(header->pixmax-header->pixmin) );
105 : }
106 :
107 0 : static int readlongtab(FILE *fp,unsigned long *tab,long tablen) {
108 : /* RLE table info, exit=0 if okay, return -1 if error */
109 : long i;
110 :
111 0 : for ( i=0; i<tablen; ++i )
112 0 : if ( getlong(fp,(long *)&tab[i]) )
113 0 : return( -1 ); /* had a read error */
114 :
115 0 : return( 0 ); /* read everything okay */
116 : }
117 :
118 0 : static int find_scanline(FILE *fp,struct sgiheader *header,int cur,
119 : unsigned long *starttab,unsigned char **ptrtab) {
120 : /* Find and expand a scanline. Return 0 if okay, else -ve if error */
121 0 : int ch = 0,i,cnt;
122 : long val;
123 : unsigned char *pt;
124 :
125 0 : for ( i=0; i<cur; ++i )
126 0 : if ( starttab[i]==starttab[cur] ) {
127 0 : ptrtab[cur] = ptrtab[i];
128 0 : return( 0 );
129 : }
130 0 : if ( (pt=ptrtab[cur]=(unsigned char *) malloc(header->width))==NULL ) {
131 0 : NoMoreMemMessage();
132 0 : return( -1 );
133 : }
134 0 : if ( fseek(fp,starttab[cur],0)!= 0 ) return( -2 );
135 0 : while ( header->bpc==1 ) {
136 0 : if ( (ch=fgetc(fp))<0 ) return( -2 );
137 0 : if ( (cnt=(ch&0x7f))==0 ) return( 0 );
138 0 : if ( ch&0x80 ) {
139 0 : while ( --cnt>=0 ) {
140 0 : if ( (ch=fgetc(fp))<0 ) return( -2 );
141 0 : *pt++ = scalecolor(header,ch);
142 : }
143 : } else {
144 0 : if ( (ch=fgetc(fp))<0 ) return( -2 );
145 0 : ch = scalecolor(header,ch);
146 0 : while ( --cnt>=0 )
147 0 : *pt++ = ch;
148 : }
149 : }
150 0 : while ( header->bpc!=1 ) {
151 0 : if ( getlong(fp,&val) ) return( -2 );
152 0 : if ( (cnt=(ch&0x7f))==0 ) return( 0 );
153 0 : if ( ch&0x80 ) {
154 0 : while ( --cnt>=0 ) {
155 0 : if ( getlong(fp,&val) ) return( -2 );
156 0 : *pt++ = scalecolor(header,val);
157 : }
158 : } else {
159 0 : if ( getlong(fp,&val) ) return( -2 );
160 0 : val = scalecolor(header,val);
161 0 : while ( --cnt>=0 )
162 0 : *pt++ = val;
163 : }
164 : }
165 0 : return( -2 );
166 : }
167 :
168 0 : static void freeptrtab(unsigned char **ptrtab,long tot) {
169 : long i,j;
170 :
171 0 : if ( ptrtab!=NULL )
172 0 : for ( i=0; i<tot; ++i )
173 0 : if ( ptrtab[i]!=NULL ) {
174 0 : for ( j=i+1; j<tot; ++j )
175 0 : if ( ptrtab[j]==ptrtab[i] )
176 0 : ptrtab[j] = NULL;
177 0 : free(ptrtab[i]);
178 : }
179 0 : }
180 :
181 0 : GImage *GImageReadRgb(char *filename) {
182 : FILE *fp; /* source file */
183 : struct sgiheader header;
184 : int i,j,k;
185 : unsigned char *pt, *end;
186 0 : unsigned char *r=NULL,*g=NULL,*b=NULL,*a=NULL; /* Colors */
187 0 : unsigned long *starttab=NULL /*, *lengthtab=NULL */;
188 0 : unsigned char **ptrtab=NULL;
189 0 : long tablen=0;
190 : unsigned long *ipt, *iend;
191 0 : GImage *ret = NULL;
192 : struct _GImage *base;
193 :
194 0 : if ( (fp=fopen(filename,"rb"))==NULL ) {
195 0 : fprintf(stderr,"Can't open \"%s\"\n", filename);
196 0 : return( NULL );
197 : }
198 :
199 : /* Check, and Get, Header information */
200 0 : if ( getsgiheader(&header,fp) )
201 0 : goto errorGImageReadRgbFile;
202 :
203 : /* Create memory to hold image, exit with NULL if not enough memory */
204 0 : if ( (ret=GImageCreate(header.dim==3?it_true:it_index,header.width,header.height))==NULL ) {
205 0 : fclose(fp);
206 0 : return( NULL );
207 : }
208 0 : base = ret->u.image;
209 :
210 0 : if ( header.format==RLE ) {
211 : /* Working with RLE image data*/
212 :
213 : /* First, get offset table info */
214 0 : tablen = header.height*header.chans;
215 0 : if ( (starttab=(unsigned long *)calloc(1,tablen*sizeof(long)))==NULL || \
216 : /*(lengthtab=(unsigned long *)calloc(1,tablen*sizeof(long)))==NULL || \ */
217 0 : (ptrtab=(unsigned char **)calloc(1,tablen*sizeof(unsigned char *)))==NULL ) {
218 0 : NoMoreMemMessage();
219 0 : goto errorGImageReadRgbMem;
220 : }
221 0 : if ( readlongtab(fp,starttab,tablen) )
222 : /* || readlongtab(fp,lengthtab,tablen) */
223 0 : goto errorGImageReadRgbFile;
224 :
225 : /* Next, get image data */
226 0 : for ( i=0; i<tablen; ++i )
227 0 : if ( (k=find_scanline(fp,&header,i,starttab,ptrtab)) ) {
228 0 : if ( k==-1 ) goto errorGImageReadRgbMem; else goto errorGImageReadRgbFile;
229 : }
230 :
231 : /* Then, build image */
232 0 : if ( header.chans==1 ) {
233 0 : for ( i=0; i<header.height; ++i )
234 0 : memcpy(base->data + (header.height-1-i)*base->bytes_per_line,ptrtab[i],header.width);
235 : } else {
236 : unsigned long *ipt;
237 0 : for ( i=0; i<header.height; ++i ) {
238 0 : ipt = (unsigned long *) (base->data + (header.height-1-i)*base->bytes_per_line);
239 0 : for ( j=0; j<header.width; ++j )
240 0 : *ipt++ = COLOR_CREATE(ptrtab[i][j],
241 : ptrtab[i+header.height][j], ptrtab[i+2*header.height][j]);
242 : }
243 : }
244 0 : freeptrtab(ptrtab,tablen);
245 0 : free(ptrtab); free(starttab); /*free(lengthtab);*/
246 : } else {
247 : /* working with Verbatim image data*/
248 0 : if ( header.chans==1 && header.bpc==1 ) {
249 0 : for ( i=0; i<header.height; ++i ) {
250 0 : fread(base->data + (header.height-1-i)*base->bytes_per_line,header.width,1,fp);
251 0 : if ( header.pixmax!=255 ) {
252 0 : pt = (unsigned char *) (base->data+(header.height-1-i)*base->bytes_per_line);
253 0 : for ( end=pt+header.width; pt<end; ++pt )
254 0 : *pt = (*pt*255)/header.pixmax;
255 : }
256 : }
257 0 : } else if ( header.chans==1 ) {
258 0 : for ( i=0; i<header.height; ++i ) {
259 0 : pt = (unsigned char *) (base->data + (header.height-1-i)*base->bytes_per_line);
260 0 : for ( end=pt+header.width; pt<end; ) {
261 0 : if ( (k=getshort(fp))<0 ) goto errorGImageReadRgbFile;
262 0 : *pt++ = (k*255L)/header.pixmax;
263 : }
264 : }
265 : } else {
266 : /* import RGB=3 or RGBA=4 Verbatim images */
267 : unsigned char *rpt, *gpt, *bpt;
268 0 : if ( (r=(unsigned char *) malloc(header.width*sizeof(unsigned char)))==NULL || \
269 0 : (g=(unsigned char *) malloc(header.width*sizeof(unsigned char)))==NULL || \
270 0 : (b=(unsigned char *) malloc(header.width*sizeof(unsigned char)))==NULL || \
271 0 : (header.chans==4 && \
272 0 : (a=(unsigned char *) malloc(header.width*sizeof(unsigned char)))==NULL) ) {
273 0 : NoMoreMemMessage();
274 0 : goto errorGImageReadRgbMem;
275 : }
276 0 : if ( header.bpc==1 ) {
277 0 : for ( i=0; i<header.height; ++i ) {
278 0 : if ( (fread(r,header.width,1,fp))<1 || \
279 0 : (fread(g,header.width,1,fp))<1 || \
280 0 : (fread(b,header.width,1,fp))<1 || \
281 0 : (header.chans==4 && (fread(a,header.width,1,fp))<1) )
282 : goto errorGImageReadRgbFile;
283 0 : ipt = (unsigned long *) (base->data + (header.height-1-i)*base->bytes_per_line);
284 0 : rpt = r; gpt = g; bpt = b;
285 0 : for ( iend=ipt+header.width; ipt<iend; )
286 0 : *ipt++ = COLOR_CREATE(*rpt++*255L/header.pixmax,
287 : *gpt++*255L/header.pixmax,*bpt++*255L/header.pixmax);
288 : }
289 : } else {
290 0 : for ( i=0; i<header.height; ++i ) {
291 0 : for ( j=0; j<header.width; ++j ) {
292 0 : if ( (k=getshort(fp))<0 ) goto errorGImageReadRgbFile;
293 0 : r[j] = k*255L/header.pixmax;
294 : }
295 0 : for ( j=0; j<header.width; ++j ) {
296 0 : if ( (k=getshort(fp))<0 ) goto errorGImageReadRgbFile;
297 0 : g[j] = k*255L/header.pixmax;
298 : }
299 0 : for ( j=0; j<header.width; ++j ) {
300 0 : if ( (k=getshort(fp))<0 ) goto errorGImageReadRgbFile;
301 0 : b[j] = k*255L/header.pixmax;
302 : }
303 0 : if ( header.chans==4 ) {
304 0 : fread(a,header.width,1,fp);
305 0 : fread(a,header.width,1,fp);
306 : }
307 0 : ipt = (unsigned long *) (base->data + (header.height-1-i)*base->bytes_per_line);
308 0 : rpt = r; gpt = g; bpt = b;
309 0 : for ( iend=ipt+header.width; ipt<iend; )
310 0 : *ipt++ = COLOR_CREATE(*rpt++,*gpt++,*bpt++);
311 : }
312 : }
313 0 : free(r); free(g); free(b); free(a);
314 : }
315 : }
316 0 : return( ret );
317 :
318 : errorGImageReadRgbFile:
319 0 : fprintf(stderr,"Bad input file \"%s\"\n",filename );
320 : errorGImageReadRgbMem:
321 0 : freeptrtab(ptrtab,tablen);
322 0 : free(ptrtab); free(starttab); /*free(lengthtab);*/
323 0 : free(r); free(g); free(b); free(a);
324 0 : GImageDestroy(ret);
325 0 : fclose(fp);
326 0 : return( NULL );
327 : }
|