1 #include "img.h"
2 #include "img_conv.h"
3 #include "Icon.h"
4 #include <stdlib.h>
5 
6 #ifdef __cplusplus
7 extern "C" {
8 #endif
9 
10 static Bool initialized = false;
11 
12 void
apc_img_init(void)13 apc_img_init( void)
14 {
15 	if ( initialized) croak("Attempt to initialize image subsystem twice");
16 	list_create( &imgCodecs, 8, 8);
17 	initialized = true;
18 }
19 
20 #define CHK if ( !initialized) croak("Image subsystem is not initialized");
21 
22 void
apc_img_done(void)23 apc_img_done( void)
24 {
25 	int i;
26 
27 	CHK;
28 	for ( i = 0; i < imgCodecs. count; i++) {
29 		PImgCodec c = ( PImgCodec)( imgCodecs. items[ i]);
30 		if ( c-> instance)
31 			c-> vmt-> done( c);
32 		free( c);
33 	}
34 	list_destroy( &imgCodecs);
35 	initialized = false;
36 }
37 
38 Bool
apc_img_register(PImgCodecVMT codec,void * initParam)39 apc_img_register( PImgCodecVMT codec, void * initParam)
40 {
41 	PImgCodec c;
42 
43 	CHK;
44 	if ( !codec) return false;
45 	c = ( PImgCodec) malloc( sizeof( struct ImgCodec) + codec-> size);
46 	if ( !c) return false;
47 
48 	memset( c, 0, sizeof( struct ImgCodec));
49 	c-> vmt = ( PImgCodecVMT) ((( Byte *) c) + sizeof( struct ImgCodec));
50 	c-> initParam = initParam;
51 	memcpy( c-> vmt, codec, codec-> size);
52 	list_add( &imgCodecs, ( Handle) c);
53 	return true;
54 }
55 
56 static void *
init(PImgCodecInfo * info,void * param)57 init( PImgCodecInfo * info, void * param)
58 {
59 	return NULL;
60 }
61 
62 static void
done(PImgCodec instance)63 done( PImgCodec instance)
64 {
65 }
66 
67 static HV *
defaults(PImgCodec instance)68 defaults(  PImgCodec instance)
69 {
70 	return newHV();
71 }
72 
73 static void
check_in(PImgCodec instance,HV * system,HV * user)74 check_in(  PImgCodec instance, HV * system, HV * user)
75 {
76 }
77 
78 static void *
open_load(PImgCodec instance,PImgLoadFileInstance fi)79 open_load(  PImgCodec instance, PImgLoadFileInstance fi)
80 {
81 	return NULL;
82 }
83 
84 static Bool
load(PImgCodec instance,PImgLoadFileInstance fi)85 load( PImgCodec instance, PImgLoadFileInstance fi)
86 {
87 	return false;
88 }
89 
90 static void
close_load(PImgCodec instance,PImgLoadFileInstance fi)91 close_load( PImgCodec instance, PImgLoadFileInstance fi)
92 {
93 	free( fi-> instance);
94 }
95 
96 static void *
open_save(PImgCodec instance,PImgSaveFileInstance fi)97 open_save( PImgCodec instance, PImgSaveFileInstance fi)
98 {
99 	return NULL;
100 }
101 
102 static Bool
save(PImgCodec instance,PImgSaveFileInstance fi)103 save( PImgCodec instance, PImgSaveFileInstance fi)
104 {
105 	return false;
106 }
107 
108 static void
close_save(PImgCodec instance,PImgSaveFileInstance fi)109 close_save( PImgCodec instance, PImgSaveFileInstance fi)
110 {
111 	free( fi-> instance);
112 }
113 
114 
115 List imgCodecs;
116 struct ImgCodecVMT CNullImgCodecVMT = {
117 	sizeof( struct ImgCodecVMT),
118 	init,
119 	done,
120 	defaults,
121 	check_in,
122 	open_load,
123 	load,
124 	close_load,
125 	defaults,
126 	check_in,
127 	open_save,
128 	save,
129 	close_save
130 };
131 
132 void
apc_img_profile_add(HV * to,HV * from,HV * keys)133 apc_img_profile_add( HV * to, HV * from, HV * keys)
134 {
135 	HE *he;
136 	hv_iterinit(( HV*) keys);
137 	for (;;)
138 	{
139 		char *key;
140 		int  keyLen;
141 		SV ** holder;
142 		if (( he = hv_iternext( keys)) == NULL)
143 			return;
144 		key    = (char*) HeKEY( he);
145 		keyLen = HeKLEN( he);
146 		if ( !hv_exists( from, key, keyLen))
147 			continue;
148 		holder = hv_fetch( from, key, keyLen, 0);
149 		if ( holder)
150 			(void) hv_store( to, key, keyLen, newSVsv( *holder), 0);
151 	}
152 }
153 
154 static ssize_t
stdio_read(void * f,size_t bufsize,void * buffer)155 stdio_read( void * f, size_t bufsize, void * buffer)
156 {
157 	return fread( buffer, 1, bufsize, ( FILE*) f);
158 }
159 
160 static ssize_t
stdio_write(void * f,size_t bufsize,void * buffer)161 stdio_write( void * f, size_t bufsize, void * buffer)
162 {
163 	return fwrite( buffer, 1, bufsize, ( FILE*) f);
164 }
165 
166 static int
stdio_seek(void * f,long offset,int whence)167 stdio_seek( void * f, long offset, int whence)
168 {
169 	return fseek( ( FILE*) f, offset, whence);
170 }
171 
172 static long
stdio_tell(void * f)173 stdio_tell( void * f)
174 {
175 	return ftell( ( FILE*) f);
176 }
177 
178 
179 static ImgIORequest std_ioreq = {
180 	stdio_read,
181 	stdio_write,
182 	stdio_seek,
183 	stdio_tell,
184 	(void*) fflush,
185 	(void*) ferror
186 };
187 
188 PList
apc_img_load(Handle self,char * fileName,Bool is_utf8,PImgIORequest ioreq,HV * profile,char * error)189 apc_img_load( Handle self, char * fileName, Bool is_utf8, PImgIORequest ioreq,  HV * profile, char * error)
190 {
191 	dPROFILE;
192 	int i, profiles_len = 0, lastFrame = -2, codecID = -1;
193 	PList ret;
194 	PImgCodec c = NULL;
195 	ImgLoadFileInstance fi;
196 	AV * profiles = NULL;
197 	HV * def = NULL, * firstObjectExtras = NULL, * commonHV = NULL;
198 	Bool err = false;
199 	Bool loadExtras = false, noImageData = false;
200 	Bool incrementalLoad = false;
201 	Bool iconUnmask = false;
202 	Bool blending = true;
203 	Bool noIncomplete = false;
204 	char * baseClassName = "Prima::Image";
205 	ImgIORequest sioreq;
206 	int  load_mask;
207 	char dummy_error_buf[256];
208 
209 
210 #define out(x){ err = true;\
211 	strncpy( fi.errbuf, x, 256);\
212 	goto EXIT_NOW;}
213 
214 #define outd(x,d){ err = true;\
215 	snprintf( fi.errbuf, 256, x, d);\
216 	goto EXIT_NOW;}
217 
218 	CHK;
219 	memset( &fi, 0, sizeof( fi));
220 	ret = plist_create( 8, 8);
221 	if ( !ret) out("Not enough memory")
222 
223 	fi. errbuf = error ? error : dummy_error_buf;
224 	fi. errbuf[0] = 0;
225 
226 	/* open file */
227 	if ( ioreq == NULL) {
228 		memcpy( &sioreq, &std_ioreq, sizeof( sioreq));
229 		if (( sioreq. handle = prima_open_file( fileName, is_utf8, "rb")) == NULL)
230 			out( strerror( errno));
231 		fi. req = &sioreq;
232 		fi. req_is_stdio = true;
233 		load_mask = IMG_LOAD_FROM_FILE;
234 	} else {
235 		fi. req = ioreq;
236 		fi. req_is_stdio = false;
237 		load_mask = IMG_LOAD_FROM_STREAM;
238 	}
239 	fi. fileName = fileName;
240 	fi. is_utf8  = is_utf8;
241 	fi. stop = false;
242 
243 	/* assigning user file profile */
244 	if ( pexist( index)) {
245 		fi. frameMapSize = 1;
246 		if ( !( fi. frameMap  = (int*) malloc( sizeof( int))))
247 			out("Not enough memory");
248 		if ((*fi. frameMap = pget_i( index)) < 0)
249 			out("Invalid index");
250 	} else if ( pexist( map)) {
251 		SV * sv = pget_sv( map);
252 		if ( SvOK( sv)) {
253 			if ( SvROK( sv) && SvTYPE( SvRV( sv)) == SVt_PVAV) {
254 				AV * av = ( AV*) SvRV( sv);
255 				int len = av_len( av) + 1;
256 				if ( !( fi. frameMap = ( int *) malloc( sizeof( int) * len)))
257 					out("Not enough memory");
258 				for ( i = 0; i < len; i++) {
259 					SV ** holder = av_fetch( av, i, 0);
260 					if ( !holder) out("Array panic on 'map' property");
261 					if (( fi. frameMap[ i] = SvIV( *holder)) < 0)
262 						out("Invalid index on 'map' property");
263 				}
264 				fi. frameMapSize = len;
265 			} else
266 				out("Not an array passed to 'map' property");
267 		}
268 	} else if ( pexist( loadAll) && pget_B( loadAll)) {
269 		fi. loadAll = true;
270 	} else {
271 		fi. frameMapSize = 1;
272 		if ( ! (fi. frameMap = ( int*) malloc( sizeof( int))))
273 			out("Not enough memory");
274 	*fi. frameMap = 0;
275 	}
276 
277 	if ( pexist( loadExtras) && pget_B( loadExtras))
278 		fi. loadExtras = loadExtras = true;
279 
280 	if ( pexist( noImageData) && pget_B( noImageData))
281 		fi. noImageData = noImageData = true;
282 
283 	if ( pexist( iconUnmask) && pget_B( iconUnmask))
284 		fi. iconUnmask = iconUnmask = true;
285 
286 	if ( pexist( blending) && !pget_B( blending))
287 		fi. blending = blending = false;
288 
289 	if ( pexist( noIncomplete) && pget_B( noIncomplete))
290 		fi. noIncomplete = noIncomplete = true;
291 
292 	if ( pexist( eventMask))
293 		fi. eventMask = pget_i( eventMask);
294 
295 	if ( pexist( eventDelay))
296 		fi. eventDelay = 1000.0 * pget_f( eventDelay);
297 	if ( fi. eventDelay <= 0)
298 		fi. eventDelay = 100; /* 100 ms. reasonable? */
299 	EVENT_SCANLINES_RESET(&fi);
300 
301 	if ( pexist( profiles)) {
302 		SV * sv = pget_sv( profiles);
303 		if ( SvOK( sv) && SvROK( sv) && SvTYPE( SvRV( sv)) == SVt_PVAV) {
304 			profiles = ( AV *) SvRV( sv);
305 			profiles_len = av_len( profiles);
306 		} else
307 			out("Not an array passed to 'profiles' property");
308 	}
309 
310 	if ( pexist( className)) {
311 		PVMT vmt;
312 		baseClassName = pget_c( className);
313 		vmt = gimme_the_vmt( baseClassName);
314 		while ( vmt && vmt != (PVMT)CImage)
315 			vmt = vmt-> base;
316 		if ( !vmt)
317 			outd("class '%s' is not a Prima::Image descendant", baseClassName);
318 	}
319 
320 	/* all other properties to be parsed by codec */
321 	fi. extras = profile;
322 
323 	fi. fileProperties = newHV();
324 	fi. frameCount = -1;
325 
326 	/* finding codec */
327 	{
328 		Bool * loadmap = ( Bool *) malloc( sizeof( Bool) * imgCodecs. count);
329 
330 		if ( !loadmap)
331 			out("Not enough memory");
332 		memset( loadmap, 0, sizeof( Bool) * imgCodecs. count);
333 		for ( i = 0; i < imgCodecs. count; i++) {
334 			c = ( PImgCodec ) ( imgCodecs. items[ i]);
335 			if ( !c-> instance)
336 				c-> instance = c-> vmt-> init( &c->info, c-> initParam);
337 			if ( !c-> instance) { /* failed to initialize, retry next time */
338 				loadmap[ i] = true;
339 				continue;
340 			}
341 		}
342 		c = NULL;
343 
344 		/* finding by extension first */
345 		if ( fileName) {
346 			int fileNameLen = strlen( fileName);
347 			for ( i = 0; i < imgCodecs. count; i++) {
348 				int j = 0, found = false;
349 				if ( loadmap[ i]) continue;
350 				c = ( PImgCodec ) ( imgCodecs. items[ i]);
351 				while ( c-> info-> fileExtensions[ j]) {
352 					char * ext = c-> info-> fileExtensions[ j];
353 					int extLen = strlen( ext);
354 					if ( extLen < fileNameLen && stricmp( fileName + fileNameLen - extLen, ext) == 0) {
355 						found = true;
356 						break;
357 					}
358 					j++;
359 				}
360 				if ( found) {
361 					loadmap[ i] = true;
362 
363 					if ( !( c-> info-> IOFlags & load_mask)) {
364 						c = NULL;
365 						continue;
366 					}
367 					if (( fi. instance = c-> vmt-> open_load( c, &fi)) != NULL) {
368 						codecID = i;
369 						break;
370 					}
371 
372 					if ( fi. stop) {
373 						err = true;
374 						free( loadmap);
375 						goto EXIT_NOW;
376 					}
377 				}
378 				c = NULL;
379 			}
380 		}
381 
382 		/* use first suitable codec */
383 		if ( c == NULL) {
384 			for ( i = 0; i < imgCodecs. count; i++) {
385 				if ( loadmap[ i]) continue;
386 				c = ( PImgCodec ) ( imgCodecs. items[ i]);
387 				if ( !( c-> info-> IOFlags & load_mask)) {
388 						c = NULL;
389 						continue;
390 				}
391 				if (( fi. instance = c-> vmt-> open_load( c, &fi)) != NULL) {
392 					codecID = i;
393 					break;
394 				}
395 				if ( fi. stop) {
396 					err = true;
397 					free( loadmap);
398 					goto EXIT_NOW;
399 				}
400 				c = NULL;
401 			}
402 		}
403 		free( loadmap);
404 		if ( !c) out("No appropriate codec found");
405 	}
406 
407 	if ( fi. loadAll) {
408 		if ( fi. frameCount >= 0) {
409 			fi. frameMapSize = fi. frameCount;
410 			if ( !( fi. frameMap  = (int*) malloc( fi. frameCount * sizeof(int))))
411 				out("Not enough memory");
412 			for ( i = 0; i < fi. frameCount; i++)
413 				fi. frameMap[i] = i;
414 		} else {
415 			fi. frameMapSize = INT_MAX;
416 			incrementalLoad = true;
417 		}
418 	}
419 
420 
421 	/* use common profile */
422 	def = c-> vmt-> load_defaults( c);
423 	commonHV = newHV();
424 	if ( profile) {
425 		c-> vmt-> load_check_in( c, commonHV, profile);
426 		apc_img_profile_add( commonHV, profile, def);
427 	}
428 
429 	if ( fi. loadExtras && c-> info-> fileType)
430 		(void) hv_store( fi. fileProperties, "codecID", 7, newSViv( codecID), 0);
431 
432 	/* loading */
433 	for ( i = 0; i < fi. frameMapSize; i++) {
434 		HV * profile = commonHV;
435 		char * className = baseClassName;
436 
437 		fi. frame = incrementalLoad ? i : fi. frameMap[ i];
438 		if (
439 			( fi. frameCount >= 0 && fi. frame >= fi. frameCount) ||
440 			( !(c-> info-> IOFlags & IMG_LOAD_MULTIFRAME) && fi. frame > 0)
441 		) {
442 			if ( !(c-> info-> IOFlags & IMG_LOAD_MULTIFRAME) && fi. frameCount < 0)
443 				fi. frameCount = i;
444 			c-> vmt-> close_load( c, &fi);
445 			if ( incrementalLoad)
446 				/* that means, codec bothered to set frameCount at last - report no error then */
447 				goto EXIT_NOW;
448 			out("Frame index out of range");
449 		}
450 
451 		fi. loadExtras   = loadExtras;
452 		fi. noImageData  = noImageData;
453 		fi. iconUnmask   = iconUnmask;
454 		fi. blending     = blending;
455 		fi. noIncomplete = noIncomplete;
456 		fi. wasTruncated = false;
457 
458 		/* query profile */
459 		if ( profiles && ( i <= profiles_len)) {
460 			HV * hv;
461 			SV ** holder = av_fetch( profiles, i, 0);
462 			if ( !holder) outd("Array panic on 'profiles[%d]' property", i);
463 			if ( SvOK( *holder)) {
464 				if ( SvROK( *holder) && SvTYPE( SvRV( *holder)) == SVt_PVHV)
465 					hv = ( HV*) SvRV( *holder);
466 				else
467 					outd("Not a hash passed to 'profiles[%d]' property", i);
468 				profile = newHV();
469 				apc_img_profile_add( profile, commonHV, commonHV);
470 				c-> vmt-> load_check_in( c, profile, hv);
471 				apc_img_profile_add( profile, hv, def);
472 				{
473 					HV * profile = hv;
474 					if ( pexist( loadExtras))
475 						fi. loadExtras  = pget_B( loadExtras);
476 					if ( pexist( noImageData))
477 						fi. noImageData = pget_B( noImageData);
478 					if ( pexist( iconUnmask))
479 						fi. iconUnmask = pget_B( iconUnmask);
480 					if ( pexist( blending))
481 						fi. blending = pget_B( blending);
482 				}
483 			}
484 		}
485 
486 		fi. jointFrame = ( fi. frame == lastFrame + 1);
487 		fi. profile    = profile;
488 		lastFrame = fi. frame;
489 
490 		/* query className */
491 		if ( pexist( className)) {
492 			PVMT vmt;
493 			className = pget_c( className);
494 			vmt = gimme_the_vmt( className);
495 			while ( vmt && vmt != (PVMT)CImage)
496 				vmt = vmt-> base;
497 			if ( !vmt) {
498 				if ( fi. profile != commonHV) sv_free(( SV *) fi. profile);
499 				outd("class '%s' is not a Prima::Image descendant", className);
500 			}
501 		}
502 
503 		/* create storage */
504 		if (( i > 0) || ( self == NULL_HANDLE)) {
505 			HV * profile = newHV();
506 			fi. object = Object_create( className, profile);
507 			sv_free(( SV *) profile);
508 			if ( !fi. object) {
509 				if ( fi. profile != commonHV) sv_free(( SV *) fi. profile);
510 				outd("Failed to create object '%s'", className);
511 			}
512 		} else
513 			fi. object = self;
514 
515 		if ( fi. iconUnmask && kind_of( fi. object, CIcon))
516 			PIcon( fi. object)-> autoMasking = amNone;
517 
518 		fi. frameProperties = newHV();
519 		if ( fi. loadExtras && c-> info-> fileType)
520 			(void) hv_store( fi. frameProperties, "codecID", 7, newSViv( codecID), 0);
521 
522 		/* loading image */
523 		if ( !c-> vmt-> load( c, &fi)) {
524 			c-> vmt-> close_load( c, &fi);
525 			sv_free(( SV *) fi. frameProperties);
526 			if ( fi. object != self)
527 				Object_destroy( fi. object);
528 			if ( fi. profile != commonHV) sv_free(( SV *) fi. profile);
529 			if ( incrementalLoad) {
530 				if ( fi. frameCount < 0)
531 					fi. frameCount = fi. frame;
532 				else if ( fi.frame < fi.frameCount )
533 					err = true;
534 				/* or it is EOF, report no error then */
535 				goto EXIT_NOW;
536 			}
537 			err = true;
538 			goto EXIT_NOW;
539 		}
540 
541 		if ( fi. loadExtras && fi. wasTruncated)
542 			(void) hv_store( fi. frameProperties, "truncated", 9, newSVpv( fi.errbuf, 0 ), 0);
543 
544 		/* checking for grayscale */
545 		{
546 			PImage i = ( PImage) fi. object;
547 			if ( !( i-> type & imGrayScale))
548 				switch ( i-> type & imBPP) {
549 				case imbpp1:
550 					if ( i-> palSize == 2 && memcmp( i-> palette, stdmono_palette, sizeof( stdmono_palette)) == 0)
551 						i-> type |= imGrayScale;
552 					break;
553 				case imbpp4:
554 					if ( i-> palSize == 16 && memcmp( i-> palette, std16gray_palette, sizeof( std16gray_palette)) == 0)
555 						i-> type |= imGrayScale;
556 					break;
557 				case imbpp8:
558 					if ( i-> palSize == 256 && memcmp( i-> palette, std256gray_palette, sizeof( std256gray_palette)) == 0)
559 						i-> type |= imGrayScale;
560 					break;
561 				}
562 		}
563 
564 		/* updating image */
565 		if ( !fi. noImageData) {
566 			CImage( fi. object)-> update_change( fi. object);
567 			/* loaders are ok to be lazy and use autoMasking for post-creation of the mask */
568 			if ( fi. iconUnmask && kind_of( fi. object, CIcon))
569 				PIcon( fi. object)-> autoMasking = amNone;
570 		}
571 
572 		/* applying extras */
573 		if ( fi. loadExtras) {
574 			HV * extras = newHV();
575 			SV * sv = newRV_noinc(( SV *) extras);
576 
577 			apc_img_profile_add( extras, fi. fileProperties,  fi. fileProperties);
578 			apc_img_profile_add( extras, fi. frameProperties, fi. frameProperties);
579 			if ( i == 0) firstObjectExtras = extras;
580 			(void) hv_store(( HV* )SvRV((( PAnyObject) fi. object)-> mate), "extras", 6, newSVsv( sv), 0);
581 			sv_free( sv);
582 		} else if ( fi. noImageData) { /* no extras, report dimensions only */
583 			HV * extras = newHV();
584 			SV * sv = newRV_noinc(( SV *) extras), **item;
585 			if (( item = hv_fetch( fi. frameProperties, "width", 5, 0)) && SvOK( *item))
586 				(void) hv_store( extras, "width", 5, newSVsv( *item), 0);
587 			else
588 				(void) hv_store( extras, "width", 5, newSViv(PImage(fi.object)-> w), 0);
589 			if (( item = hv_fetch( fi. frameProperties, "height", 6, 0)) && SvOK( *item))
590 				(void) hv_store( extras, "height", 6, newSVsv( *item), 0);
591 			else
592 				(void) hv_store( extras, "height", 6, newSViv(PImage(fi.object)-> h), 0);
593 			(void) hv_store(( HV* )SvRV((( PAnyObject) fi. object)-> mate), "extras", 6, newSVsv( sv), 0);
594 			sv_free( sv);
595 		}
596 
597 		sv_free(( SV *) fi. frameProperties);
598 		if ( fi. profile != commonHV) sv_free(( SV *) fi. profile);
599 
600 		list_add( ret, fi. object);
601 	}
602 
603 	c-> vmt-> close_load( c, &fi);
604 
605 	/* returning info for null load request  */
606 	if ( self && loadExtras && fi. frameMapSize == 0) {
607 		HV * extras = newHV();
608 		SV * sv = newRV_noinc(( SV *) extras);
609 		apc_img_profile_add( extras, fi. fileProperties,  fi. fileProperties);
610 		firstObjectExtras = extras;
611 		(void) hv_store(( HV* )SvRV((( PAnyObject) self)-> mate), "extras", 6, newSVsv( sv), 0);
612 		sv_free( sv);
613 	}
614 
615 EXIT_NOW:;
616 	if ( err && fi.errbuf[0] == 0)
617 		strcpy(fi.errbuf, fi.wasTruncated ? "Truncated file" : "Internal error");
618 	if ( fi. frameCount < 0 && pexist( wantFrames) && pget_i( wantFrames)) {
619 		if ( ioreq != NULL)
620 			req_seek( ioreq, 0, SEEK_SET);
621 		fi. frameCount = apc_img_frame_count( fileName, is_utf8, ioreq);
622 	}
623 	if ( firstObjectExtras)
624 		(void) hv_store( firstObjectExtras, "frames", 6, newSViv( fi. frameCount), 0);
625 	if ( err && ret)
626 		list_add( ret, NULL_HANDLE); /* indicate the error */
627 	if ( def)
628 		sv_free(( SV *) def);
629 	if ( commonHV)
630 		sv_free(( SV *) commonHV);
631 	if ( fi. fileProperties)
632 		sv_free((SV *) fi. fileProperties);
633 	if ( ioreq == NULL && fi.req != NULL && fi. req-> handle != NULL)
634 		fclose(( FILE*) fi. req-> handle);
635 	free( fi. frameMap);
636 	return ret;
637 #undef out
638 #undef outd
639 }
640 
641 int
apc_img_frame_count(char * fileName,Bool is_utf8,PImgIORequest ioreq)642 apc_img_frame_count( char * fileName, Bool is_utf8, PImgIORequest ioreq )
643 {
644 	PImgCodec c = NULL;
645 	ImgLoadFileInstance fi;
646 	int i, frameMap, ret = 0;
647 	char error[256];
648 	ImgIORequest sioreq;
649 	int load_mask;
650 
651 	CHK;
652 	memset( &fi, 0, sizeof( fi));
653 	/* open file */
654 	if ( ioreq == NULL) {
655 		memcpy( &sioreq, &std_ioreq, sizeof( sioreq));
656 		if (( sioreq. handle = prima_open_file( fileName, is_utf8, "rb")) == NULL)
657 			goto EXIT_NOW;
658 		fi. req = &sioreq;
659 		fi. req_is_stdio = true;
660 		load_mask = IMG_LOAD_FROM_FILE;
661 	} else {
662 		fi. req = ioreq;
663 		fi. req_is_stdio = false;
664 		load_mask = IMG_LOAD_FROM_STREAM;
665 	}
666 
667 	/* assigning request */
668 	fi. fileName = fileName;
669 	fi. is_utf8  = is_utf8;
670 	fi. frameMapSize   = frameMap = 0;
671 	fi. frameMap       = &frameMap;
672 	fi. loadExtras     = true;
673 	fi. noImageData    = true;
674 	fi. iconUnmask     = false;
675 	fi. blending       = false;
676 	fi. noIncomplete   = false;
677 	fi. extras         = newHV();
678 	fi. fileProperties = newHV();
679 	fi. frameCount = -1;
680 	fi. errbuf     = error;
681 	fi. stop       = false;
682 
683 	/* finding codec */
684 	{
685 		Bool * loadmap = ( Bool*) malloc( sizeof( Bool) * imgCodecs. count);
686 
687 		if ( !loadmap)
688 			return 0;
689 		memset( loadmap, 0, sizeof( Bool) * imgCodecs. count);
690 		for ( i = 0; i < imgCodecs. count; i++) {
691 			c = ( PImgCodec ) ( imgCodecs. items[ i]);
692 			if ( !c-> instance)
693 				c-> instance = c-> vmt-> init( &c->info, c-> initParam);
694 			if ( !c-> instance) { /* failed to initialize, retry next time */
695 				loadmap[ i] = true;
696 				continue;
697 			}
698 		}
699 
700 		c = NULL;
701 
702 		/* finding by extension first */
703 		if ( fileName) {
704 			int fileNameLen = strlen( fileName);
705 			for ( i = 0; i < imgCodecs. count; i++) {
706 				int j = 0, found = false;
707 				if ( loadmap[ i]) continue;
708 				c = ( PImgCodec ) ( imgCodecs. items[ i]);
709 				while ( c-> info-> fileExtensions[ j]) {
710 					char * ext = c-> info-> fileExtensions[ j];
711 					int extLen = strlen( ext);
712 					if ( extLen < fileNameLen && stricmp( fileName + fileNameLen - extLen, ext) == 0) {
713 						found = true;
714 						break;
715 					}
716 					j++;
717 				}
718 				if ( found) {
719 					loadmap[ i] = true;
720 
721 					if ( !( c-> info-> IOFlags & load_mask)) {
722 						c = NULL;
723 						continue;
724 					}
725 					if (( fi. instance = c-> vmt-> open_load( c, &fi)) != NULL)
726 						break;
727 
728 					if ( fi. stop) {
729 						free( loadmap);
730 						goto EXIT_NOW;
731 					}
732 				}
733 				c = NULL;
734 			}
735 		}
736 
737 		if ( c == NULL) {
738 			for ( i = 0; i < imgCodecs. count; i++) {
739 				if ( loadmap[ i]) continue;
740 				c = ( PImgCodec ) ( imgCodecs. items[ i]);
741 				if ( !( c-> info-> IOFlags & load_mask)) {
742 						c = NULL;
743 						continue;
744 				}
745 				if (( fi. instance = c-> vmt-> open_load( c, &fi)) != NULL)
746 					break;
747 				if ( fi. stop) {
748 					free( loadmap);
749 					goto EXIT_NOW;
750 				}
751 				c = NULL;
752 			}
753 		}
754 		free( loadmap);
755 		if ( !c) goto EXIT_NOW;
756 	}
757 
758 	/* can tell now? */
759 
760 	if ( fi. frameCount >= 0) {
761 		c-> vmt-> close_load( c, &fi);
762 		ret = fi. frameCount;
763 		goto EXIT_NOW;
764 	}
765 
766 	if ( !( c-> info-> IOFlags & IMG_LOAD_MULTIFRAME)) {
767 		c-> vmt-> close_load( c, &fi);
768 		ret = 1; /* single-framed file. what else? */
769 		goto EXIT_NOW;
770 	}
771 
772 	/* if can't, trying to load huge index, hoping that if */
773 	/* codec have a sequential access, it eventually meet the  */
774 	/* EOF and report the frame count */
775 	{
776 		HV * profile = newHV();
777 		fi. object = Object_create( "Prima::Image", profile);
778 		sv_free(( SV *) profile);
779 		frameMap = fi. frame = INT_MAX;
780 		fi. frameProperties = newHV();
781 	}
782 
783 	/* loading image */
784 	if ( c-> vmt-> load( c, &fi) || fi. frameCount >= 0) {
785 		/* well, INT_MAX frame is ok, and maybe more, but can't report more anyway */
786 		c-> vmt-> close_load( c, &fi);
787 		ret = ( fi. frameCount < 0) ? INT_MAX : fi. frameCount;
788 		goto EXIT_NOW;
789 	}
790 
791 	/* can't report again - so loading as may as we can */
792 	fi. loadAll = true;
793 	for ( i = 0; i < INT_MAX; i++) {
794 		fi. jointFrame = i > 0;
795 		frameMap = fi. frame = i;
796 		if ( !( c-> info-> IOFlags & IMG_LOAD_MULTIFRAME)) {
797 			c-> vmt-> close_load( c, &fi);
798 			if ( !( fi. instance = c-> vmt-> open_load( c, &fi))) {
799 				ret = i;
800 				goto EXIT_NOW;
801 			}
802 		}
803 		if ( !c-> vmt-> load( c, &fi) || fi. frameCount >= 0) {
804 			c-> vmt-> close_load( c, &fi);
805 			ret = ( fi. frameCount < 0) ? i : fi. frameCount;
806 			goto EXIT_NOW;
807 		}
808 	}
809 
810 	c-> vmt-> close_load( c, &fi);
811 
812 EXIT_NOW:;
813 	if ( fi. object)
814 		Object_destroy( fi. object);
815 	if ( fi. extras)
816 		sv_free(( SV *) fi. extras);
817 	if ( fi. frameProperties)
818 		sv_free(( SV *) fi. frameProperties);
819 	if ( fi. fileProperties)
820 		sv_free(( SV *) fi. fileProperties);
821 	if ( ioreq == NULL && fi.req != NULL && fi. req-> handle != NULL)
822 		fclose(( FILE*) fi. req-> handle);
823 	return ret;
824 }
825 
826 int
apc_img_save(Handle self,char * fileName,Bool is_utf8,PImgIORequest ioreq,HV * profile,char * error)827 apc_img_save( Handle self, char * fileName, Bool is_utf8, PImgIORequest ioreq, HV * profile, char * error)
828 {
829 	dPROFILE;
830 	int i;
831 	PImgCodec c = NULL;
832 	ImgSaveFileInstance fi;
833 	AV * images = NULL;
834 	HV * def = NULL, * commonHV = NULL;
835 	Bool err = false;
836 	int codecID = -1;
837 	int xself = self ? 1 : 0;
838 	int ret = 0;
839 	Bool autoConvert = true;
840 	ImgIORequest sioreq;
841 	int save_mask;
842 	char dummy_error_buf[256];
843 
844 #define out(x){ err = true;\
845 	strncpy( fi.errbuf, x, 256);\
846 	goto EXIT_NOW;}
847 
848 #define outd(x,d){ err = true;\
849 	snprintf( fi.errbuf, 256, x, d);\
850 	goto EXIT_NOW;}
851 
852 	CHK;
853 	memset( &fi, 0, sizeof( fi));
854 
855 	if ( pexist( append) && pget_B( append))
856 		fi. append = true;
857 
858 	if ( pexist( autoConvert))
859 		autoConvert = pget_B( autoConvert);
860 
861 	/* open file */
862 	if ( fi. append && ioreq == NULL) {
863 		FILE * f = ( FILE *) prima_open_file( fileName, is_utf8, "rb");
864 		if ( !f)
865 			fi. append = false;
866 		else
867 			fclose( f);
868 	}
869 
870 	fi. errbuf = error ? error : dummy_error_buf;
871 	if ( ioreq == NULL) {
872 		memcpy( &sioreq, &std_ioreq, sizeof( sioreq));
873 		if (( sioreq. handle = prima_open_file( fileName, is_utf8, fi. append ? "rb+" : "wb+" )) == NULL)
874 			out( strerror( errno));
875 		fi. req = &sioreq;
876 		fi. req_is_stdio = true;
877 		save_mask = IMG_SAVE_TO_FILE;
878 	} else {
879 		fi. req = ioreq;
880 		fi. req_is_stdio = false;
881 		save_mask = IMG_SAVE_TO_STREAM;
882 	}
883 
884 	fi. fileName     = fileName;
885 	fi. is_utf8      = is_utf8;
886 
887 	fi. frameMapSize = xself;
888 	if ( pexist( images)) {
889 		SV * sv = pget_sv( images);
890 		if ( SvOK( sv) && SvROK( sv) && SvTYPE( SvRV( sv)) == SVt_PVAV) {
891 			images = ( AV *) SvRV( sv);
892 			fi. frameMapSize += av_len( images) + 1;
893 		} else
894 			out("Not an array passed to 'images' property");
895 	}
896 	if ( fi. frameMapSize == 0)
897 		out("Nothing to save");
898 
899 	/* fill array of objects */
900 	if ( !( fi. frameMap = ( Handle *) malloc( sizeof( Handle) * fi. frameMapSize)))
901 		out("Not enough memory");
902 	memset( fi. frameMap, 0, sizeof( Handle) * fi. frameMapSize);
903 
904 	for ( i = 0; i < fi. frameMapSize; i++) {
905 		Handle obj = NULL_HANDLE;
906 
907 		/* query profile */
908 		if ( self && (i == 0)) {
909 			obj = self;
910 			if ( !kind_of( obj, CImage))
911 				out("Not a Prima::Image descendant passed");
912 			if ( PImage(obj)-> w == 0 || PImage(obj)-> h == 0)
913 				out("Cannot save a null image");
914 		} else if ( images) {
915 			SV ** holder = av_fetch( images, i - xself, 0);
916 			if ( !holder) outd("Array panic on 'images[%d]' property", i - xself);
917 			obj = gimme_the_mate( *holder);
918 			if ( !obj)
919 				outd("Invalid object reference passed in 'images[%d]'", i - xself);
920 			if ( !kind_of( obj, CImage))
921 				outd("Not a Prima::Image descendant passed in 'images[%d]'", i - xself);
922 			if ( PImage(obj)-> w == 0 || PImage(obj)-> h == 0)
923 				out("Cannot save a null image");
924 		} else
925 			out("Logic error");
926 		fi. frameMap[ i] = obj;
927 	}
928 
929 	/* all other properties to be parsed by codec */
930 	fi. extras = profile;
931 
932 	/* finding codec */
933 	strcpy( fi.errbuf, "No appropriate codec found");
934 	{
935 		Bool * savemap = ( Bool*) malloc( sizeof( Bool) * imgCodecs. count);
936 
937 		if ( !savemap)
938 			out("Not enough memory");
939 		memset( savemap, 0, sizeof( Bool) * imgCodecs. count);
940 
941 		for ( i = 0; i < imgCodecs. count; i++) {
942 			c = ( PImgCodec ) ( imgCodecs. items[ i]);
943 			if ( !c-> instance)
944 				c-> instance = c-> vmt-> init( &c->info, c-> initParam);
945 			if ( !c-> instance) { /* failed to initialize, retry next time */
946 				savemap[ i] = true;
947 				continue;
948 			}
949 		}
950 
951 		/* checking 'codecID', if available */
952 		{
953 			SV * c = NULL;
954 			if ( pexist( codecID))
955 				c = pget_sv( codecID);
956 			else if ( self &&
957 				hv_exists(( HV*)SvRV((( PAnyObject) self)-> mate), "extras", 6)
958 			) {
959 				SV ** sv = hv_fetch(( HV*)SvRV((( PAnyObject) self)-> mate), "extras", 6, 0);
960 				if ( sv && SvOK( *sv) && SvROK( *sv) && SvTYPE( SvRV( *sv)) == SVt_PVHV) {
961 					HV * profile = ( HV *) SvRV( *sv);
962 					if ( pexist( codecID))
963 						c = pget_sv( codecID);
964 				}
965 			}
966 			if ( c && SvOK( c)) { /* accept undef */
967 				codecID = SvIV( c);
968 				if ( codecID < 0) codecID = imgCodecs. count - codecID;
969 			}
970 		}
971 
972 		/* find codec */
973 		c = NULL;
974 		if ( codecID >= 0) {
975 			if ( codecID >= imgCodecs. count)
976 				out("Codec index out of range");
977 
978 			c = ( PImgCodec ) ( imgCodecs. items[ codecID]);
979 			if ( !( c-> info-> IOFlags & save_mask))
980 					out( ioreq ?
981 						"Codec cannot save images to streams" :
982 						"Codec cannot save images");
983 
984 			if ( fi. frameMapSize > 1 &&
985 					!( c-> info-> IOFlags & IMG_SAVE_MULTIFRAME))
986 				out("Codec cannot save mutiframe images");
987 			if ( fi. append &&
988 					!( c-> info-> IOFlags & IMG_SAVE_APPEND))
989 				out("Codec cannot append frames");
990 
991 			if (( fi. instance = c-> vmt-> open_save( c, &fi)) == NULL)
992 				out("Codec cannot handle this file");
993 
994 			if ( !autoConvert) {
995 				int j, *k = c-> info-> saveTypes, ok = 0;
996 				for ( j = 0; j < fi. frameMapSize; j++) {
997 					int type = PImage( fi. frameMap[j])-> type;
998 					while ( *k) {
999 						if ( type == *k) {
1000 							ok = 1;
1001 							break;
1002 						}
1003 						k++;
1004 					}
1005 				}
1006 				if ( !ok)
1007 					out("Image type(s) not supported by the codec specified");
1008 			}
1009 		}
1010 
1011 		if ( !c && fileName) {
1012 			int fileNameLen = strlen( fileName);
1013 			/* finding codec by extension  */
1014 			for ( i = 0; i < imgCodecs. count; i++) {
1015 				int j = 0, found = false;
1016 				if ( savemap[ i]) continue;
1017 				c = ( PImgCodec ) ( imgCodecs. items[ i]);
1018 				while ( c-> info-> fileExtensions[ j]) {
1019 					char * ext = c-> info-> fileExtensions[ j];
1020 					int extLen = strlen( ext);
1021 					if ( extLen < fileNameLen && stricmp( fileName + fileNameLen - extLen, ext) == 0) {
1022 						found = true;
1023 						break;
1024 					}
1025 					j++;
1026 				}
1027 
1028 				if ( found) {
1029 					savemap[ i] = true;
1030 					if ( !( c-> info-> IOFlags & save_mask)) {
1031 						c = NULL;
1032 						continue;
1033 					}
1034 
1035 					if ( fi. frameMapSize > 1
1036 						&& !( c-> info-> IOFlags & IMG_SAVE_MULTIFRAME)) {
1037 						c = NULL;
1038 						continue;
1039 					}
1040 					if ( fi. append
1041 						&& !( c-> info-> IOFlags & IMG_SAVE_APPEND)) {
1042 						c = NULL;
1043 						continue;
1044 					}
1045 
1046 					if ( !autoConvert) {
1047 						int j, *k = c-> info-> saveTypes, ok = 0;
1048 						for ( j = 0; j < fi. frameMapSize; j++) {
1049 							int type = PImage( fi. frameMap[j])-> type;
1050 							while ( *k) {
1051 								if ( type == *k) {
1052 									ok = 1;
1053 									break;
1054 								}
1055 								k++;
1056 							}
1057 						}
1058 						if ( !ok) {
1059 							c = NULL;
1060 							continue;
1061 						}
1062 					}
1063 
1064 					if (( fi. instance = c-> vmt-> open_save( c, &fi)) != NULL)
1065 						break;
1066 				}
1067 				c = NULL;
1068 			}
1069 		}
1070 
1071 		free( savemap);
1072 		if ( !c) { /* use pre-formatted error string */
1073 			err = true;
1074 			goto EXIT_NOW;
1075 		}
1076 	}
1077 
1078 	snprintf( fi.errbuf, 256, "Error saving %s", fileName ? fileName : "to stream");
1079 
1080 	/* use common profile */
1081 	def = c-> vmt-> save_defaults( c);
1082 	commonHV = newHV();
1083 	if ( profile) {
1084 		c-> vmt-> save_check_in( c, commonHV, profile);
1085 		apc_img_profile_add( commonHV, profile, def);
1086 	}
1087 
1088 	/* saving */
1089 	for ( i = 0; i < fi. frameMapSize; i++) {
1090 		HV * profile = commonHV;
1091 		PImage im;
1092 
1093 		im = ( PImage) fi. frameMap[ i];
1094 		if ( hv_exists(( HV*)SvRV( im-> mate), "extras", 6)) {
1095 			SV ** sv = hv_fetch(( HV*)SvRV( im-> mate), "extras", 6, 0);
1096 			if ( sv && SvOK( *sv) && SvROK( *sv) && SvTYPE( SvRV( *sv)) == SVt_PVHV) {
1097 				HV * hv = ( HV *) SvRV( *sv);
1098 				profile = newHV();
1099 				apc_img_profile_add( profile, commonHV, commonHV);
1100 				c-> vmt-> save_check_in( c, profile, hv);
1101 				apc_img_profile_add( profile, hv, def);
1102 			}
1103 		}
1104 
1105 		fi. frame  = i;
1106 		fi. object = fi. frameMap[ i];
1107 		fi. objectExtras = profile;
1108 
1109 		/* converting image to format with maximum bit depth and category flags match */
1110 		if ( autoConvert) {
1111 			int *k = c-> info-> saveTypes;
1112 			int max = *k & imBPP, best = *k, supported = false;
1113 			int flags = im-> type & imCategory, bestflags = *k & imCategory, bestmatch;
1114 #define dBITS(a) int i = 0x80, match = ( flags & (a)) >> 8
1115 #define CALCBITS(x) { \
1116 	x = 0;\
1117 	while ( i >>= 1 ) if ( match & i ) x++; \
1118 }
1119 			{
1120 				dBITS( bestflags );
1121 				CALCBITS( bestmatch )
1122 			}
1123 			while ( *k) {
1124 				if ( im-> type == *k) {
1125 					supported = true;
1126 					break;
1127 				}
1128 				if ( max < ( *k & imBPP)) {
1129 					dBITS( bestflags = ( *k & imCategory));
1130 					max       = *k & imBPP;
1131 					best      = *k;
1132 					CALCBITS( bestmatch );
1133 				} else if ( max == ( *k & imBPP)) {
1134 					dBITS( *k );
1135 					int testmatch;
1136 					CALCBITS( testmatch );
1137 					if ( testmatch > bestmatch ) {
1138 						best      = *k;
1139 						bestflags = *k & imCategory;
1140 						bestmatch = testmatch;
1141 					}
1142 				}
1143 				k++;
1144 			}
1145 			if ( !supported) {
1146 				im-> self-> set_type(( Handle) im, best);
1147 				if ( best != im-> type) outd("Failed converting image to type '%04x'", best);
1148 			}
1149 		}
1150 
1151 		/* saving image */
1152 		if ( !c-> vmt-> save( c, &fi)) {
1153 			c-> vmt-> close_save( c, &fi);
1154 			if ( fi. objectExtras != commonHV) sv_free(( SV *) fi. objectExtras);
1155 			err = true;
1156 			goto EXIT_NOW;
1157 		}
1158 
1159 		if ( fi. objectExtras != commonHV) sv_free(( SV *) fi. objectExtras);
1160 		ret++;
1161 	}
1162 
1163 	c-> vmt-> close_save( c, &fi);
1164 
1165 EXIT_NOW:;
1166 	free( fi. frameMap);
1167 	if ( ioreq == NULL && fi. req != NULL && fi. req-> handle != NULL)
1168 		fclose(( FILE*) fi. req-> handle);
1169 	if ( err && fileName)
1170 		apc_fs_unlink( fileName, is_utf8 );
1171 	if ( def)
1172 		sv_free(( SV *) def);
1173 	if ( commonHV)
1174 		sv_free(( SV *) commonHV);
1175 	return err ? -ret : ret;
1176 #undef out
1177 #undef outd
1178 }
1179 void
apc_img_codecs(PList ret)1180 apc_img_codecs( PList ret)
1181 {
1182 	int i;
1183 	PImgCodec c;
1184 
1185 	CHK;
1186 	for ( i = 0; i < imgCodecs. count; i++) {
1187 		c = ( PImgCodec ) ( imgCodecs. items[ i]);
1188 		if ( !c-> instance)
1189 			c-> instance = c-> vmt-> init( &c->info, c-> initParam);
1190 		if ( !c-> instance)  /* failed to initialize, retry next time */
1191 			continue;
1192 		list_add( ret, ( Handle) c);
1193 	}
1194 }
1195 
1196 int
apc_img_read_palette(PRGBColor palBuf,SV * palette,Bool triplets)1197 apc_img_read_palette( PRGBColor palBuf, SV * palette, Bool triplets)
1198 {
1199 	AV * av;
1200 	int i, count;
1201 	Byte buf[768];
1202 
1203 	if ( !SvROK( palette) || ( SvTYPE( SvRV( palette)) != SVt_PVAV))
1204 		return 0;
1205 	av = (AV *) SvRV( palette);
1206 	count = av_len( av) + 1;
1207 
1208 	if ( triplets) {
1209 		if ( count > 768) count = 768;
1210 		count -= count % 3;
1211 
1212 		for ( i = 0; i < count; i++)
1213 		{
1214 			SV **itemHolder = av_fetch( av, i, 0);
1215 			if ( itemHolder == NULL) return 0;
1216 			buf[ i] = SvIV( *itemHolder);
1217 		}
1218 		memcpy( palBuf, buf, count);
1219 		return count/3;
1220 	} else {
1221 		int j;
1222 		if ( count > 256) count = 256;
1223 
1224 		for ( i = 0, j = 0; i < count; i++)
1225 		{
1226 			Color c;
1227 			SV **itemHolder = av_fetch( av, i, 0);
1228 			if ( itemHolder == NULL) return 0;
1229 			c = (Color)(SvIV( *itemHolder));
1230 			buf[j++] = c & 0xFF;
1231 			buf[j++] = (c >> 8) & 0xFF;
1232 			buf[j++] = (c >> 16) & 0xFF;
1233 		}
1234 		memcpy( palBuf, buf, j);
1235 		return count;
1236 	}
1237 }
1238 
1239 
fill_plist(char * key,char ** list,HV * profile)1240 static AV * fill_plist( char * key, char ** list, HV * profile)
1241 {
1242 	AV * av = newAV();
1243 	if ( !list) list = imgPVEmptySet;
1244 	while ( *list) {
1245 		av_push( av, newSVpv( *list, 0));
1246 		list++;
1247 	}
1248 	(void) hv_store( profile, key, strlen( key), newRV_noinc(( SV *) av), 0);
1249 	return av;
1250 }
1251 
fill_ilist(char * key,int * list,HV * profile)1252 static void fill_ilist( char * key, int * list, HV * profile)
1253 {
1254 	AV * av = newAV();
1255 	if ( !list) list = imgIVEmptySet;
1256 	while ( *list) {
1257 		av_push( av, newSViv( *list));
1258 		list++;
1259 	}
1260 	(void) hv_store( profile, key, strlen( key), newRV_noinc(( SV *) av), 0);
1261 }
1262 
1263 
1264 HV *
apc_img_info2hash(PImgCodec codec)1265 apc_img_info2hash( PImgCodec codec)
1266 {
1267 	HV * profile, * hv;
1268 	AV * av;
1269 	PImgCodecInfo c;
1270 
1271 	CHK;
1272 	profile = newHV();
1273 	if ( !codec) return profile;
1274 
1275 	if ( !codec-> instance)
1276 		codec-> instance = codec-> vmt-> init( &codec->info, codec-> initParam);
1277 	if ( !codec-> instance)
1278 		return profile;
1279 	c = codec-> info;
1280 
1281 	pset_c( name,   c-> name);
1282 	pset_c( vendor, c-> vendor);
1283 	pset_i( versionMajor, c-> versionMaj);
1284 	pset_i( versionMinor, c-> versionMin);
1285 	fill_plist( "fileExtensions", c-> fileExtensions, profile);
1286 	pset_c( fileType, c-> fileType);
1287 	pset_c( fileShortType, c-> fileShortType);
1288 	fill_plist( "featuresSupported", c-> featuresSupported, profile);
1289 	pset_c( module,  c-> primaModule);
1290 	pset_c( package, c-> primaPackage);
1291 	pset_i( canLoad,         c-> IOFlags & IMG_LOAD_FROM_FILE);
1292 	pset_i( canLoadStream  , c-> IOFlags & IMG_LOAD_FROM_STREAM);
1293 	pset_i( canLoadMultiple, c-> IOFlags & IMG_LOAD_MULTIFRAME);
1294 	pset_i( canSave        , c-> IOFlags & IMG_SAVE_TO_FILE);
1295 	pset_i( canSaveStream  , c-> IOFlags & IMG_SAVE_TO_STREAM);
1296 	pset_i( canSaveMultiple, c-> IOFlags & IMG_SAVE_MULTIFRAME);
1297 	pset_i( canAppend,       c-> IOFlags & IMG_SAVE_APPEND);
1298 
1299 	fill_ilist( "types",  c-> saveTypes, profile);
1300 
1301 	if ( c-> IOFlags & ( IMG_LOAD_FROM_FILE|IMG_LOAD_FROM_STREAM)) {
1302 		hv = codec-> vmt-> load_defaults( codec);
1303 		if ( c-> IOFlags & IMG_LOAD_MULTIFRAME) {
1304 			(void) hv_store( hv, "index",        5, newSViv(0),     0);
1305 			(void) hv_store( hv, "map",          3, newSVsv(NULL_SV), 0);
1306 			(void) hv_store( hv, "loadAll",      7, newSViv(0),     0);
1307 			(void) hv_store( hv, "wantFrames",  10, newSViv(0),     0);
1308 		}
1309 		(void) hv_store( hv, "loadExtras",   10, newSViv(0),     0);
1310 		(void) hv_store( hv, "noImageData",  11, newSViv(0),     0);
1311 		(void) hv_store( hv, "iconUnmask",   10, newSViv(0),     0);
1312 		(void) hv_store( hv, "blending",      8, newSViv(0),     0);
1313 		(void) hv_store( hv, "noIncomplete", 12, newSViv(0),     0);
1314 		(void) hv_store( hv, "className",     9, newSVpv("Prima::Image", 0), 0);
1315 	} else
1316 		hv = newHV();
1317 	pset_sv_noinc( loadInput, newRV_noinc(( SV *) hv));
1318 
1319 	av = fill_plist( "loadOutput", c-> loadOutput, profile);
1320 	if ( c-> IOFlags & ( IMG_LOAD_FROM_FILE|IMG_LOAD_FROM_STREAM)) {
1321 		if ( c-> IOFlags & IMG_LOAD_MULTIFRAME)
1322 			av_push( av, newSVpv( "frames", 0));
1323 		av_push( av, newSVpv( "height", 0));
1324 		av_push( av, newSVpv( "width",  0));
1325 		av_push( av, newSVpv( "codecID", 0));
1326 		av_push( av, newSVpv( "truncated", 0));
1327 	}
1328 
1329 	if ( c-> IOFlags & ( IMG_SAVE_TO_FILE|IMG_SAVE_TO_STREAM)) {
1330 		hv = codec-> vmt-> save_defaults( codec);
1331 		if ( c-> IOFlags & IMG_SAVE_MULTIFRAME)
1332 			(void) hv_store( hv, "append",       6, newSViv(0), 0);
1333 		(void) hv_store( hv, "autoConvert", 11, newSViv(1), 0);
1334 		(void) hv_store( hv, "codecID",     7,  newSVsv( NULL_SV), 0);
1335 	} else
1336 		hv = newHV();
1337 	pset_sv_noinc( saveInput, newRV_noinc(( SV *) hv));
1338 
1339 	fill_plist( "mime", c-> mime, profile);
1340 
1341 	return profile;
1342 }
1343 
1344 char * imgPVEmptySet[] = { NULL };
1345 int    imgIVEmptySet[] = { 0 };
1346 
1347 void
apc_img_notify_header_ready(PImgLoadFileInstance fi)1348 apc_img_notify_header_ready( PImgLoadFileInstance fi)
1349 {
1350 	Event e = { cmImageHeaderReady };
1351 	e. gen. p = fi-> frameProperties;
1352 	CImage( fi-> object)-> message( fi-> object, &e);
1353 }
1354 
1355 void
apc_img_notify_scanlines_ready(PImgLoadFileInstance fi,int scanlines,int direction)1356 apc_img_notify_scanlines_ready( PImgLoadFileInstance fi, int scanlines, int direction)
1357 {
1358 	Event e;
1359 	int height, width;
1360 	unsigned int dt;
1361 	struct timeval t;
1362 
1363 	fi-> lastCachedScanline += scanlines;
1364 	gettimeofday( &t, NULL);
1365 	dt =
1366 		t.tv_sec * 1000 + t.tv_usec / 1000 -
1367 		fi-> lastEventTime.tv_sec * 1000 - fi-> lastEventTime.tv_usec / 1000;
1368 
1369 	if ( dt < fi-> eventDelay) return;
1370 	if ( fi-> lastEventScanline == fi-> lastCachedScanline) return;
1371 
1372 	e. cmd = cmImageDataReady;
1373 	height = PImage( fi-> object)-> h;
1374 	width  = PImage( fi-> object)-> w;
1375 
1376 	switch ( direction ) {
1377 	case SCANLINES_DIR_TOP_TO_BOTTOM:
1378 		e. gen. R. left   = 0;
1379 		e. gen. R. right  = width - 1;
1380 		e. gen. R. bottom = height - fi-> lastCachedScanline;
1381 		e. gen. R. top    = height - fi-> lastEventScanline  - 1;
1382 		break;
1383 	case SCANLINES_DIR_BOTTOM_TO_TOP:
1384 		e. gen. R. left   = 0;
1385 		e. gen. R. right  = width - 1;
1386 		e. gen. R. bottom = fi-> lastEventScanline;
1387 		e. gen. R. top    = fi-> lastCachedScanline - 1;
1388 		break;
1389 	case SCANLINES_DIR_LEFT_TO_RIGHT:
1390 		e. gen. R. left   = fi-> lastEventScanline;
1391 		e. gen. R. right  = fi-> lastCachedScanline - 1;
1392 		e. gen. R. bottom = 0;
1393 		e. gen. R. top    = height - 1;
1394 		break;
1395 	case SCANLINES_DIR_RIGHT_TO_LEFT:
1396 		e. gen. R. left    = width - fi-> lastCachedScanline;
1397 		e. gen. R. right   = width - fi-> lastEventScanline - 1;
1398 		e. gen. R. bottom  = 0;
1399 		e. gen. R. top     = height - 1;
1400 		break;
1401 	}
1402 	CImage( fi-> object)-> message( fi-> object, &e);
1403 
1404 	gettimeofday( &fi-> lastEventTime, NULL);
1405 	fi-> lastEventScanline = fi-> lastCachedScanline;
1406 }
1407 #ifdef __cplusplus
1408 }
1409 #endif
1410