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