1 /* CommonPal.c */
2
3 /* Routines for optimising animations' palettes
4 * (K) All Rites Reversed - Copy What You Like (see file Copying)
5 *
6 * Authors:
7 * Peter Hartley <pdh@chaos.org.uk>
8 *
9 * History:
10 * 01-Mar-97 pdh Created
11 * 07-Apr-97 *** Release 6beta1
12 * 20-May-97 *** Release 6beta2
13 * 24-Aug-97 pdh Fixed problem with blank frame 1, 2-colour frame 2
14 * Made palette entry for transparent colour black :-((((
15 * 24-Aug-97 *** Release 6
16 * 27-Sep-97 *** Release 6.01
17 * 07-Nov-97 pdh Anim_Trim
18 * 08-Nov-97 *** Release 6.02
19 * 15-Feb-98 pdh Move palette mapping stuff to palettemap.c
20 * 21-Feb-98 *** Release 6.03
21 * 07-Jun-98 *** Release 6.04
22 * 21-Aug-98 *** Release 6.05
23 * 05-Oct-98 *** Release 6.06
24 * 19-Feb-99 *** Release 6.07
25 * 26-Mar-00 *** Release 6.10
26 *
27 */
28
29 #include <string.h>
30 #include <stdarg.h>
31
32 #include "animlib.h"
33 #include "utils.h"
34 #include "gifencode.h"
35 #include "workspace.h"
36 #include "frame.h"
37
38 #if 0
39 #define debugf printf
40 #define DEBUG 1
41 #else
42 #define debugf 1?0:printf
43 #define DEBUG 0
44 #endif
45
46 #if 0
47 #define HEAPTEST HeapTest(__LINE__)
48
49 static void HeapTest( int line )
50 {
51 void *foo;
52
53 Anim_CheckHeap(__FILE__,line);
54
55 foo = Anim_Allocate(20000);
56 if ( foo ) Anim_Free(&foo);
57 }
58 #else
59 #define HEAPTEST
60 #endif
61
62 static int FindColour( unsigned int *pColours, unsigned int *pnColours,
63 unsigned int colour );
64
Anim_CommonPalette(anim a)65 BOOL Anim_CommonPalette( anim a )
66 {
67 unsigned int colours[256];
68 unsigned int nColours = 0;
69 unsigned int nThisFrame;
70 pixel *image, *mask;
71 pixel map[256];
72 unsigned char used[256];
73 unsigned char dstused[256];
74 unsigned int i, j;
75 unsigned int len = a->nWidth * a->nHeight;
76 BOOL result = TRUE;
77 BOOL compatible;
78 frame f;
79 unsigned int mincolours = 0;
80 unsigned int nUsedOnFrame;
81
82 image = Anim_Allocate( len );
83 mask = Anim_Allocate( len );
84 if ( !image || !mask )
85 {
86 Anim_NoMemory( "commonpal" );
87 result = FALSE;
88 }
89 else
90 {
91 Workspace_Claim( gifcompress_WORKSIZE );
92
93 for ( i=0; i < a->nFrames; i++ )
94 {
95 f = a->pFrames + i;
96
97 if ( !Anim_Decompress( f->pImageData, f->nImageSize, len, image ) )
98 {
99 result = FALSE;
100 break;
101 }
102
103 if ( f->pMaskData )
104 {
105 if ( !Anim_Decompress( f->pMaskData, f->nMaskSize, len, mask ) )
106 {
107 result = FALSE;
108 break;
109 }
110 }
111 else
112 memset( mask, 1, len ); /* all solid */
113
114 memset( used, 0, 256 );
115
116 for ( j=0; j<len; j++ )
117 if ( mask[j] )
118 used[ image[j] ] = 1;
119
120 compatible = TRUE;
121
122 nThisFrame = nColours;
123
124 memset( dstused, 0, 256 );
125
126 for ( j=0; j<256; j++ )
127 {
128 if ( used[j] )
129 {
130 int n = FindColour( colours, &nColours,
131 f->pal->pColours[j] );
132
133 if ( n<0 )
134 {
135 compatible = FALSE;
136 break;
137 }
138 map[j] = (pixel) n;
139 dstused[n] = 1;
140 }
141 }
142
143 if ( compatible )
144 {
145 debugf( "commonpal: frame %d compatible\n", i );
146
147 #if DEBUG
148 debugf( "map" );
149 for ( j=0; j<f->pal->nColours; j++ )
150 {
151 if ( used[j] )
152 debugf( " %d", map[j] );
153 else
154 debugf( " -" );
155 }
156 debugf( "\n" );
157 #endif
158
159 Palette_Destroy( &f->pal ); /* sets to NULL */
160
161 /* Remap the frame to the new palette */
162 for ( j=0; j<len; j++ )
163 image[j] = map[image[j]];
164 Anim_Free( &f->pImageData );
165
166 f->pImageData = Anim_Compress( image, len, &f->nImageSize );
167
168 if ( !f->pImageData )
169 {
170 result = FALSE;
171 break;
172 }
173
174 nUsedOnFrame = 0;
175
176 for ( j=0; j<256; j++ )
177 if ( dstused[j] )
178 nUsedOnFrame++;
179
180 if ( nUsedOnFrame >= mincolours && (i || f->pMaskData) )
181 {
182 mincolours = nUsedOnFrame + 1;
183 }
184 }
185 else
186 {
187 debugf( "commonpal: frame %d INcompatible!\n", i );
188
189 nColours = nThisFrame;
190 }
191 }
192
193 Workspace_Release();
194 }
195
196 Anim_Free( &image );
197 Anim_Free( &mask );
198
199 if ( result )
200 {
201 /* Allow an extra colour for transparency if possible */
202 if ( mincolours > nColours && nColours < 256 )
203 {
204 /* And make it black, to avoid BUGS in the Microsoft Java VM */
205 colours[nColours] = 0;
206 nColours++;
207 }
208
209 debugf( "mincolours=%d nColours=%d\n", mincolours, nColours );
210
211 for ( i=0; i < a->nFrames; i++ )
212 {
213 f = a->pFrames+i;
214
215 if ( !f->pal )
216 {
217 f->pal = Palette_Create( colours, nColours );
218 if ( !f->pal )
219 {
220 result = FALSE;
221 break;
222 }
223 }
224 }
225 }
226 return result;
227 }
228
FindColour(unsigned int * pColours,unsigned int * pnColours,unsigned int colour)229 static int FindColour( unsigned int *pColours, unsigned int *pnColours,
230 unsigned int colour )
231 {
232 int i;
233
234 for ( i=0; i < (int)*pnColours; i++ )
235 {
236 if ( pColours[i] == colour )
237 {
238 return i;
239 }
240 }
241
242 if ( *pnColours == 256 )
243 return -1;
244
245 pColours[ *pnColours ] = colour;
246 return (*pnColours)++;
247 }
248
249
250 /*===========================================*
251 * Stuff that doesn't really belong here *
252 *===========================================*/
253
254 #if 0
255 BOOL Anim_AddToHistogram( anim a )
256 {
257 pixel *buffer;
258 pixel *mask;
259 unsigned int size = a->nWidth * a->nHeight;
260 unsigned int i, j;
261
262 debugf( "in Anim_AddToHistogram(%d)\n", a->nFrames );
263
264 buffer = Anim_Allocate( size );
265 mask = Anim_Allocate( size );
266
267 if ( !buffer
268 || !mask )
269 {
270 Anim_NoMemory( "addhist" );
271 Anim_Free( &buffer );
272 Anim_Free( &mask );
273 return FALSE;
274 }
275
276 Workspace_Claim(0);
277
278 for ( i=0; i<a->nFrames; i++ )
279 {
280 frame f = a->pFrames + i;
281 unsigned int *pal = f->pal->pColours;
282
283 memset( mask, 1, size );
284
285 if ( !Anim_Decompress( f->pImageData, f->nImageSize, size, buffer )
286 || ( f->pMaskData
287 && !Anim_Decompress( f->pMaskData, f->nMaskSize, size, mask )
288 )
289 )
290 {
291 Anim_Free( &buffer );
292 Anim_Free( &mask );
293 Workspace_Release();
294 return FALSE;
295 }
296
297 for ( j=0; j<size; j++ )
298 if ( mask[j] )
299 if ( !Histogram_Pixel( pal[ buffer[j] ] ) )
300 {
301 Anim_Free( &buffer );
302 Anim_Free( &mask );
303 Workspace_Release();
304 return FALSE;
305 }
306 }
307
308 Workspace_Release();
309
310 Anim_Free( &buffer );
311 Anim_Free( &mask );
312
313 debugf( "Anim_AddToHistogram exits\n" );
314
315 return TRUE;
316 }
317 #endif
318
Anim_Trim(anim a)319 BOOL Anim_Trim( anim a )
320 {
321 pixel *buffer;
322 pixel *mask;
323 unsigned int size = a->nWidth * a->nHeight;
324 unsigned int i;
325 rect rcFrame, rcMin;
326
327 /* quick exit if all solid */
328 for ( i=0; i < a->nFrames; i++ )
329 if ( a->pFrames[i].pMaskData == NULL )
330 return TRUE;
331
332 buffer = Anim_Allocate( size );
333 mask = Anim_Allocate( size );
334
335 if ( !buffer || !mask )
336 {
337 Anim_NoMemory( "trim" );
338 Anim_Free( &buffer );
339 Anim_Free( &mask );
340 return FALSE;
341 }
342
343 Workspace_Claim(0);
344
345 /* discover smallest rect */
346 for ( i=0; i<a->nFrames; i++ )
347 {
348 framestr *f = a->pFrames + i;
349
350 if ( !Anim_Decompress( f->pMaskData, f->nMaskSize, size, mask )
351 || !BitMaskTrimTransparentBorders( a, mask, &rcFrame ) )
352 {
353 Anim_Free( &buffer );
354 Anim_Free( &mask );
355 Workspace_Release();
356 return FALSE;
357 }
358
359 if ( i==0 )
360 rcMin = rcFrame; /* structure copy */
361 else
362 Rect_Union( &rcMin, &rcFrame );
363 }
364
365 if ( rcMin.xoff > 0 || rcMin.yoff > 0
366 || rcMin.xsize < a->nWidth || rcMin.ysize < a->nHeight )
367 {
368 /* Scissors out, Penfold */
369 pixel *imgstart = buffer + rcMin.xoff + rcMin.yoff*a->nWidth;
370 pixel *maskstart = mask + rcMin.xoff + rcMin.yoff*a->nWidth;
371
372 for ( i=0; i<a->nFrames; i++ )
373 {
374 framestr *f = a->pFrames + i;
375
376 if ( !Anim_Decompress( f->pImageData, f->nImageSize, size, buffer )
377 || !Anim_Decompress( f->pMaskData, f->nMaskSize, size, mask ))
378 {
379 Anim_Free( &buffer );
380 Anim_Free( &mask );
381 Workspace_Release();
382 return FALSE;
383 }
384 Anim_Free( &f->pImageData );
385 Anim_Free( &f->pMaskData );
386
387 f->pImageData = Anim_CompressAligned( imgstart, rcMin.xsize,
388 rcMin.ysize, a->nWidth,
389 &f->nImageSize );
390 f->pMaskData = Anim_CompressAligned( maskstart, rcMin.xsize,
391 rcMin.ysize, a->nWidth,
392 &f->nMaskSize );
393 if ( !f->pImageData || !f->pMaskData )
394 {
395 Anim_Free( &buffer );
396 Anim_Free( &mask );
397 Workspace_Release();
398 return FALSE;
399 }
400 }
401 a->nWidth = rcMin.xsize;
402 a->nHeight = rcMin.ysize;
403 }
404 Anim_Free( &buffer );
405 Anim_Free( &mask );
406 Workspace_Release();
407 return TRUE;
408 }
409
410 /* eof */
411