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 "inc/basics.h"
28 : #include "gimage.h"
29 :
30 6 : GImage *GImageCreate(enum image_type type, int32 width, int32 height) {
31 : /* Prepare to get a bitmap image. Cleanup and return NULL if not enough memory */
32 : GImage *gi;
33 : struct _GImage *base;
34 :
35 6 : if ( type<it_mono || type>it_rgba )
36 0 : return( NULL );
37 :
38 6 : gi = (GImage *) calloc(1,sizeof(GImage));
39 6 : base = (struct _GImage *) malloc(sizeof(struct _GImage));
40 6 : if ( gi==NULL || base==NULL )
41 : goto errorGImageCreate;
42 :
43 6 : gi->u.image = base;
44 6 : base->image_type = type;
45 6 : base->width = width;
46 6 : base->height = height;
47 6 : base->bytes_per_line = (type==it_true || type==it_rgba)?4*width:type==it_index?width:(width+7)/8;
48 6 : base->data = NULL;
49 6 : base->clut = NULL;
50 6 : base->trans = COLOR_UNKNOWN;
51 6 : if ( (base->data = (uint8 *) malloc(height*base->bytes_per_line))==NULL )
52 0 : goto errorGImageCreate;
53 6 : if ( type==it_index ) {
54 2 : if ( (base->clut = (GClut *) calloc(1,sizeof(GClut)))==NULL ) {
55 0 : free(base->data);
56 0 : goto errorGImageCreate;
57 : }
58 2 : base->clut->trans_index = COLOR_UNKNOWN;
59 : }
60 6 : return( gi );
61 :
62 : errorGImageCreate:
63 0 : free(base);
64 0 : free(gi);
65 0 : NoMoreMemMessage();
66 0 : return( NULL );
67 : }
68 :
69 :
70 0 : GImage *_GImage_Create(enum image_type type, int32 width, int32 height) {
71 : GImage *gi;
72 : struct _GImage *base;
73 :
74 0 : if ( type<it_mono || type>it_rgba )
75 0 : return( NULL );
76 :
77 0 : gi = (GImage *) calloc(1,sizeof(GImage));
78 0 : base = (struct _GImage *) malloc(sizeof(struct _GImage));
79 0 : if ( gi==NULL || base==NULL )
80 : goto error_GImage_Create;
81 :
82 0 : gi->u.image = base;
83 0 : base->image_type = type;
84 0 : base->width = width;
85 0 : base->height = height;
86 0 : base->bytes_per_line = (type==it_true || type==it_rgba)?4*width:type==it_index?width:(width+7)/8;
87 0 : base->data = NULL;
88 0 : base->clut = NULL;
89 0 : if ( type==it_index ) {
90 0 : if ( (base->clut = (GClut *) calloc(1,sizeof(GClut)))==NULL )
91 0 : goto error_GImage_Create;
92 : }
93 0 : return( gi );
94 :
95 : error_GImage_Create:
96 0 : free(base);
97 0 : free(gi);
98 0 : NoMoreMemMessage();
99 0 : return( NULL );
100 : }
101 :
102 0 : void GImageDestroy(GImage *gi) {
103 : /* Free memory (if GImage exists) */
104 : int i;
105 :
106 0 : if (gi!=NULL ) {
107 0 : if ( gi->list_len!=0 ) {
108 0 : for ( i=0; i<gi->list_len; ++i ) {
109 0 : free(gi->u.images[i]->clut);
110 0 : free(gi->u.images[i]->data);
111 0 : free(gi->u.images[i]);
112 : }
113 0 : free(gi->u.images);
114 : } else {
115 0 : free(gi->u.image->clut);
116 0 : free(gi->u.image->data);
117 0 : free(gi->u.image);
118 : }
119 0 : free(gi);
120 : }
121 0 : }
122 :
123 0 : GImage *GImageCreateAnimation(GImage **images, int n) {
124 : /* Create an animation using n "images". Return gi and free "images" if */
125 : /* okay, else return NULL and keep "images" if memory error occurred, */
126 : GImage *gi;
127 : struct _GImage **imgs;
128 : int i;
129 :
130 : /* Check if "images" are okay to copy before creating an animation. */
131 : /* We expect to find single images (not an array). Type must match. */
132 0 : for ( i=0; i<n; ++i ) {
133 0 : if ( images[i]->list_len!=0 || \
134 0 : images[i]->u.image->image_type!=images[0]->u.image->image_type ) {
135 0 : fprintf( stderr, "Images are not compatible to make an Animation\n" );
136 0 : return( NULL );
137 : }
138 : }
139 :
140 : /* First, create enough memory space to hold the complete animation */
141 0 : gi = (GImage *) calloc(1,sizeof(GImage));
142 0 : imgs = (struct _GImage **) malloc(n*sizeof(struct _GImage *));
143 0 : if ( gi==NULL || imgs==NULL ) {
144 0 : free(gi);
145 0 : free(imgs);
146 0 : NoMoreMemMessage();
147 0 : return( NULL );
148 : }
149 :
150 : /* Copy images[i] pointer into 'gi', then release each "images[i]". */
151 0 : gi->list_len = n;
152 0 : gi->u.images = imgs;
153 0 : for ( i=0; i<n; ++i ) {
154 0 : imgs[i] = images[i]->u.image;
155 0 : free(images[i]);
156 : }
157 0 : return( gi );
158 : }
159 :
160 : /* -1 => add it at the end */
161 0 : GImage *GImageAddImageBefore(GImage *dest, GImage *src, int pos) {
162 : struct _GImage **imgs;
163 : int n, i, j;
164 : enum image_type it;
165 :
166 0 : n = (src->list_len==0?1:src->list_len) + (dest->list_len==0?1:dest->list_len);
167 0 : imgs = (struct _GImage **) malloc(n*sizeof(struct _GImage *));
168 0 : if ( imgs==NULL ) {
169 0 : NoMoreMemMessage();
170 0 : return( NULL );
171 : }
172 :
173 0 : i = 0;
174 0 : if ( dest->list_len==0 ) {
175 0 : it = dest->u.image->image_type;
176 0 : if ( pos==-1 ) pos = 1;
177 0 : if ( pos!=0 ) imgs[i++] = dest->u.image;
178 : } else {
179 0 : it = dest->u.images[0]->image_type;
180 0 : if ( pos==-1 ) pos = dest->list_len;
181 0 : for ( i=0; i<pos; ++i )
182 0 : imgs[i] = dest->u.images[i];
183 : }
184 0 : j = i;
185 0 : if ( src->list_len==0 ) {
186 0 : if ( src->u.image->image_type!=it ) {
187 0 : free(imgs);
188 0 : return( NULL );
189 : }
190 0 : imgs[j++] = src->u.image;
191 : } else {
192 0 : for ( ; j<i+src->list_len; ++j ) {
193 0 : if ( src->u.images[j-i]->image_type!=it ) {
194 0 : free(imgs);
195 0 : return( NULL );
196 : }
197 0 : imgs[j] = src->u.images[j-i];
198 : }
199 0 : free(src->u.images);
200 : }
201 0 : if ( dest->list_len==0 ) {
202 0 : if ( pos==0 ) imgs[j++] = dest->u.image;
203 : } else {
204 0 : for ( ; j<n; ++j )
205 0 : imgs[j] = dest->u.images[i++];
206 : }
207 0 : dest->u.images = imgs;
208 0 : dest->list_len = n;
209 0 : free(src);
210 0 : return( dest );
211 : }
212 :
213 0 : void GImageDrawRect(GImage *img,GRect *r,Color col) {
214 : struct _GImage *base;
215 : int i;
216 :
217 0 : base = img->u.image;
218 0 : if ( r->y>=base->height || r->x>=base->width )
219 0 : return;
220 :
221 0 : for ( i=0; i<r->width; ++i ) {
222 0 : if ( i+r->x>=base->width )
223 0 : break;
224 0 : base->data[r->y*base->bytes_per_line + i + r->x] = col;
225 0 : if ( r->y+r->height-1<base->height )
226 0 : base->data[(r->y+r->height-1)*base->bytes_per_line + i + r->x] = col;
227 : }
228 0 : for ( i=0; i<r->height; ++i ) {
229 0 : if ( i+r->y>=base->height )
230 0 : break;
231 0 : base->data[(r->y+i)*base->bytes_per_line + r->x] = col;
232 0 : if ( r->x+r->width-1<base->width )
233 0 : base->data[(r->y+i)*base->bytes_per_line + r->x+r->width-1] = col;
234 : }
235 : }
236 :
237 0 : void GImageDrawImage(GImage *dest,GImage *src,GRect *UNUSED(junk),int x, int y) {
238 : struct _GImage *sbase, *dbase;
239 : int i,j, di, sbi, dbi, val, factor, maxpix, sbit;
240 :
241 : /* This is designed to merge images which should be treated as alpha */
242 : /* channels. dest must be indexed, src may be either indexed or mono */
243 0 : dbase = dest->u.image;
244 0 : sbase = src->u.image;
245 :
246 0 : if ( dbase->image_type != it_index ) {
247 0 : fprintf( stderr, "Bad call to GImageMaxImage\n" );
248 0 : return;
249 : }
250 :
251 0 : maxpix = 1;
252 0 : if ( dbase->clut!=NULL )
253 0 : maxpix = dbase->clut->clut_len - 1;
254 :
255 0 : if ( dbase->clut!=NULL && sbase->clut!=NULL && sbase->clut->clut_len>1 ) {
256 0 : factor = (dbase->clut->clut_len - 1) / (sbase->clut->clut_len - 1);
257 0 : if ( factor==0 ) factor=1;
258 : } else
259 0 : factor = 1;
260 :
261 0 : if ( sbase->image_type == it_index ) {
262 0 : for ( i=0; i<sbase->height; ++i ) {
263 0 : di = y + i;
264 0 : if ( di<0 || di>=dbase->height )
265 0 : continue;
266 0 : sbi = i*sbase->bytes_per_line;
267 0 : dbi = di*dbase->bytes_per_line;
268 0 : for ( j=0; j<sbase->width; ++j ) {
269 0 : if ( x+j<0 || x+j>=dbase->width )
270 0 : continue;
271 0 : val = dbase->data[dbi+x+j] + sbase->data[sbi+j]*factor;
272 0 : if ( val>255 ) val = 255;
273 0 : dbase->data[dbi+x+j] = val;
274 : }
275 : }
276 0 : } else if ( sbase->image_type == it_mono ) {
277 0 : for ( i=0; i<sbase->height; ++i ) {
278 0 : di = y + i;
279 0 : if ( di<0 || di>=dbase->height )
280 0 : continue;
281 0 : sbi = i*sbase->bytes_per_line;
282 0 : dbi = di*dbase->bytes_per_line;
283 0 : for ( j=0, sbit=0x80; j<sbase->width; ++j ) {
284 0 : if ( x+j<0 || x+j>=dbase->width )
285 0 : continue;
286 0 : if ( sbase->data[sbi+(j>>3)] & sbit )
287 0 : dbase->data[dbi+x+j] = maxpix;
288 0 : if ( (sbit>>=1) == 0 )
289 0 : sbit = 0x80;
290 : }
291 : }
292 : }
293 : }
294 :
295 : /* Blends src image with alpha channel over dest. Both images must be */
296 : /* 32-bit truecolor. Alpha channel of dest must be all opaque. */
297 0 : void GImageBlendOver(GImage *dest,GImage *src,GRect *from,int x, int y) {
298 : struct _GImage *sbase, *dbase;
299 : int i, j, a, r, g, b;
300 : uint32 *dpt, *spt;
301 :
302 0 : dbase = dest->u.image;
303 0 : sbase = src->u.image;
304 :
305 0 : if ( dbase->image_type != it_true ) {
306 0 : fprintf( stderr, "Bad call to GImageBlendOver\n" );
307 0 : return;
308 : }
309 :
310 0 : if ( sbase->image_type != it_rgba ) {
311 0 : fprintf( stderr, "Bad call to GImageBlendOver\n" );
312 0 : return;
313 : }
314 :
315 0 : for ( i=0; i<from->height; ++i ) {
316 0 : dpt = (uint32 *) (dbase->data + (i+y)*dbase->bytes_per_line + x*sizeof(uint32));
317 0 : spt = (uint32 *) (sbase->data + (i+from->y)*sbase->bytes_per_line + from->x*sizeof(uint32));
318 :
319 0 : for (j=0; j<from->width; j++) {
320 0 : a = COLOR_ALPHA(*spt);
321 0 : r = ((255-a)*COLOR_RED(*dpt) + a*COLOR_RED(*spt))/255;
322 0 : g = ((255-a)*COLOR_GREEN(*dpt)+ a*COLOR_GREEN(*spt))/255;
323 0 : b = ((255-a)*COLOR_BLUE(*dpt) + a*COLOR_BLUE(*spt))/255;
324 0 : spt++;
325 0 : *dpt++ = 0xff000000 | COLOR_CREATE(r,g,b);
326 : }
327 : }
328 : }
329 :
330 5 : int GImageGetWidth(GImage *img) {
331 5 : if ( img->list_len==0 ) {
332 5 : return( img->u.image->width );
333 : } else {
334 0 : return( img->u.images[0]->width );
335 : }
336 : }
337 :
338 5 : int GImageGetHeight(GImage *img) {
339 5 : if ( img->list_len==0 ) {
340 5 : return( img->u.image->height );
341 : } else {
342 0 : return( img->u.images[0]->height );
343 : }
344 : }
345 :
346 0 : void *GImageGetUserData(GImage *img) {
347 0 : return( img->userdata );
348 : }
349 :
350 0 : void GImageSetUserData(GImage *img,void *userdata) {
351 0 : img->userdata = userdata;
352 0 : }
353 :
354 0 : static Color _GImageGetPixelRGBA(struct _GImage *base,int x, int y) {
355 : Color val;
356 :
357 0 : if ( base->image_type==it_rgba ) {
358 0 : val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
359 0 : return( val==base->trans?(val&0xffffff):val );
360 0 : } else if ( base->image_type==it_true ) {
361 0 : val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
362 0 : return( val==base->trans?(val&0xffffff):(val|0xff000000) );
363 0 : } else if ( base->image_type==it_index ) {
364 0 : uint8 pixel = ((uint8*) (base->data + y*base->bytes_per_line))[x];
365 0 : val = base->clut->clut[pixel];
366 0 : return( pixel==base->trans?(val&0xffffff):(val|0xff000000) );
367 : } else {
368 0 : uint8 pixel = (((uint8*) (base->data + y*base->bytes_per_line))[x>>3]&(1<<(7-(x&7))) )?1:0;
369 0 : if ( base->clut==NULL ) {
370 0 : if ( pixel )
371 0 : val = COLOR_CREATE(0xff,0xff,0xff);
372 : else
373 0 : val = COLOR_CREATE(0,0,0);
374 : } else
375 0 : val = base->clut->clut[pixel];
376 0 : return( pixel==base->trans?(val&0xffffff):(val|0xff000000) );
377 : }
378 : }
379 :
380 0 : Color GImageGetPixelRGBA(GImage *image,int x, int y) {
381 0 : struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
382 0 : return( _GImageGetPixelRGBA(base,x,y));
383 : }
384 :
385 : /* This routine is now obsolete. I include it for compatability. See GImageGetPixelRGBA */
386 0 : Color _GImageGetPixelColor(struct _GImage *base,int x, int y) {
387 : Color val;
388 :
389 0 : if ( base->image_type==it_rgba ) {
390 0 : val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
391 0 : return( val==base->trans?~val:val );
392 0 : } else if ( base->image_type==it_true ) {
393 0 : val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
394 0 : return( val==base->trans?~val:val );
395 0 : } else if ( base->image_type==it_index ) {
396 0 : uint8 pixel = ((uint8*) (base->data + y*base->bytes_per_line))[x];
397 0 : val = base->clut->clut[pixel];
398 0 : return( pixel==base->trans?~val:val );
399 : } else {
400 0 : uint8 pixel = (((uint8*) (base->data + y*base->bytes_per_line))[x>>3]&(1<<(7-(x&7))) )?1:0;
401 0 : if ( base->clut==NULL ) {
402 0 : if ( pixel )
403 0 : val = COLOR_CREATE(0xff,0xff,0xff);
404 : else
405 0 : val = COLOR_CREATE(0,0,0);
406 : } else
407 0 : val = base->clut->clut[pixel];
408 0 : return( pixel==base->trans?~val:val );
409 : }
410 : }
411 :
412 0 : Color GImageGetPixelColor(GImage *image,int x, int y) {
413 0 : struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
414 0 : return( _GImageGetPixelColor(base,x,y));
415 : }
|