|
1 /****************************************************************************** |
|
2 * |
|
3 * $Id$ |
|
4 * |
|
5 * |
|
6 * Copyright (C) 1997-2009 by Dimitri van Heesch. |
|
7 * |
|
8 * Permission to use, copy, modify, and distribute this software and its |
|
9 * documentation under the terms of the GNU General Public License is hereby |
|
10 * granted. No representations are made about the suitability of this software |
|
11 * for any purpose. It is provided "as is" without express or implied warranty. |
|
12 * See the GNU General Public License for more details. |
|
13 * |
|
14 * All output generated with Doxygen is not covered by this license. |
|
15 * |
|
16 * The GIF compression code below is based on the file ppmtogif.c of the |
|
17 * netpbm package. The original copyright message follows: |
|
18 * |
|
19 * --------------------------------------------------------------------------- |
|
20 * |
|
21 * Copyright (C) 1989 by Jef Poskanzer. |
|
22 * |
|
23 * Permission to use, copy, modify, and distribute this software and its |
|
24 * documentation for any purpose and without fee is hereby granted, provided |
|
25 * that the above copyright notice appear in all copies and that both that |
|
26 * copyright notice and this permission notice appear in supporting |
|
27 * documentation. This software is provided "as is" without express or |
|
28 * implied warranty. |
|
29 * |
|
30 * --------------------------------------------------------------------------- |
|
31 * |
|
32 * The Graphics Interchange Format(c) is the Copyright property of |
|
33 * CompuServe Incorporated. GIF(sm) is a Service Mark property of |
|
34 * CompuServe Incorporated. |
|
35 */ |
|
36 |
|
37 #include "gifenc.h" |
|
38 |
|
39 static const unsigned int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, |
|
40 0x001F, 0x003F, 0x007F, 0x00FF, |
|
41 0x01FF, 0x03FF, 0x07FF, 0x0FFF, |
|
42 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; |
|
43 |
|
44 GifEncoder::GifEncoder(Byte *rawBytes,Color *p,int w,int h, Byte d, |
|
45 int t) |
|
46 : colorResolution(8),globalPaletteFlag(0x80),bits(12), |
|
47 maxMaxCode(1<<bits) |
|
48 { |
|
49 width = w; |
|
50 height = h; |
|
51 depth = d; |
|
52 transIndex = t; |
|
53 palette = p; |
|
54 data = rawBytes; |
|
55 dataPtr = data; |
|
56 } |
|
57 |
|
58 GifEncoder::~GifEncoder() |
|
59 { |
|
60 } |
|
61 |
|
62 void GifEncoder::writeGIF(QFile &fp) |
|
63 { |
|
64 // Write the Magic header |
|
65 fp.writeBlock( transIndex < 0 ? "GIF87a" : "GIF89a", 6 ); |
|
66 |
|
67 // Write the logical screen descriptor |
|
68 putWord( width, fp ); |
|
69 putWord( height, fp ); |
|
70 Byte pack = globalPaletteFlag | ((colorResolution-1)<<4) | (depth-1); |
|
71 putByte( pack, fp ); |
|
72 putByte( 0, fp ); // the background color |
|
73 putByte( 0, fp ); // no aspect ration defined |
|
74 |
|
75 // Write global color table |
|
76 int i; for ( i=0 ; i< (1<<depth) ; i++) |
|
77 { |
|
78 putByte(palette[i].red, fp); |
|
79 putByte(palette[i].green,fp); |
|
80 putByte(palette[i].blue, fp); |
|
81 } |
|
82 |
|
83 if ( transIndex >= 0) |
|
84 { |
|
85 // Write graphic control extension (needed for GIF transparancy) |
|
86 putByte( 0x21, fp); // extension introducer |
|
87 putByte( 0xf9, fp); // graphic control label |
|
88 putByte( 4, fp); // block size |
|
89 putByte( 1, fp); // announce transparacy value |
|
90 putWord( 0, fp); // zero delay time |
|
91 putByte( transIndex, fp); // write transparant index |
|
92 putByte( 0, fp); // end block |
|
93 } |
|
94 |
|
95 // Write the image descriptor |
|
96 putByte( 0x2c, fp); // image separator |
|
97 putWord( 0, fp); // image left position |
|
98 putWord( 0, fp); // image top position |
|
99 putWord( width, fp); // image width |
|
100 putWord( height, fp); // image height |
|
101 putByte( 0, fp); // no local color table, no interlacing |
|
102 |
|
103 // Write table based image data |
|
104 Byte initCodeSize = depth<=1 ? 2 : depth; |
|
105 putByte( initCodeSize, fp); // LZW Minimum Code Size |
|
106 compress( initCodeSize+1, fp); |
|
107 putByte( 0, fp); // end of blocks |
|
108 |
|
109 // Write GIF Trailer |
|
110 putByte( 0x3b, fp); |
|
111 } |
|
112 |
|
113 void GifEncoder::compress( int ibits, QFile &outfile ) |
|
114 { |
|
115 int i; |
|
116 int entry; |
|
117 |
|
118 initBits = ibits; |
|
119 numPixels = width*height; |
|
120 dataPtr = data; |
|
121 clearFlag = FALSE; |
|
122 nBits = initBits; |
|
123 maxCode = (1<<nBits) -1; |
|
124 ClearCode = (1 << (initBits - 1)); |
|
125 EOFCode = ClearCode + 1; |
|
126 freeEntry = ClearCode + 2; |
|
127 aCount = 0; |
|
128 curAccum = 0; |
|
129 curBits = 0; |
|
130 |
|
131 entry = nextPixel(); |
|
132 |
|
133 int hshift = 0; |
|
134 int fcode; |
|
135 for ( fcode = hashTableSize; fcode < 65536L; fcode *= 2L ) ++hshift; |
|
136 hshift = 8 - hshift; /* set hash code range bound */ |
|
137 |
|
138 clearHashTable(); /* clear hash table */ |
|
139 |
|
140 writeCode( ClearCode,outfile ); |
|
141 |
|
142 int c; |
|
143 while ( (c = nextPixel()) != EOF ) |
|
144 { |
|
145 fcode = (c << bits) + entry; |
|
146 i = (c << hshift) ^ entry; /* xor hashing */ |
|
147 |
|
148 bool found=FALSE; |
|
149 if (htab[i]==fcode) |
|
150 { |
|
151 entry = codetab[i]; |
|
152 found=TRUE; |
|
153 } |
|
154 else if (htab[i]>=0) |
|
155 { |
|
156 int disp = hashTableSize - i; |
|
157 if (i==0) disp=1; |
|
158 do |
|
159 { |
|
160 if ((i-=disp)<0) i+=hashTableSize; |
|
161 if (htab[i]==fcode) |
|
162 { |
|
163 entry=codetab[i]; |
|
164 found=TRUE; |
|
165 } |
|
166 } while (htab[i]>0 && !found); |
|
167 } |
|
168 if (!found) |
|
169 { |
|
170 writeCode( entry, outfile ); |
|
171 entry = c; |
|
172 if ( freeEntry < maxMaxCode ) |
|
173 { |
|
174 codetab[i] = freeEntry++; /* code -> hashtable */ |
|
175 htab[i] = fcode; |
|
176 } |
|
177 else |
|
178 { |
|
179 clearHashTable(); |
|
180 freeEntry = ClearCode + 2; |
|
181 clearFlag = TRUE; |
|
182 writeCode( ClearCode, outfile ); |
|
183 } |
|
184 } |
|
185 } |
|
186 writeCode( entry, outfile ); |
|
187 writeCode( EOFCode, outfile ); |
|
188 } |
|
189 |
|
190 void GifEncoder::putWord( Word w, QFile &fp ) |
|
191 { |
|
192 fp.putch( w & 0xff ); |
|
193 fp.putch( (w>>8) & 0xff ); |
|
194 } |
|
195 |
|
196 void GifEncoder::putByte( Byte b, QFile &fp ) |
|
197 { |
|
198 fp.putch( b ); |
|
199 } |
|
200 |
|
201 void GifEncoder::writeCode( int code, QFile &outfile ) |
|
202 { |
|
203 curAccum &= masks[ curBits ]; |
|
204 |
|
205 if ( curBits > 0 ) |
|
206 { |
|
207 curAccum |= (code << curBits); |
|
208 } |
|
209 else |
|
210 { |
|
211 curAccum = code; |
|
212 } |
|
213 |
|
214 curBits += nBits; |
|
215 |
|
216 while( curBits >= 8 ) |
|
217 { |
|
218 writeChar( (Byte)(curAccum & 0xff),outfile ); |
|
219 curAccum >>= 8; |
|
220 curBits -= 8; |
|
221 } |
|
222 |
|
223 /* |
|
224 * If the next entry is going to be too big for the code size, |
|
225 * then increase it, if possible. |
|
226 */ |
|
227 if ( freeEntry > maxCode || clearFlag ) |
|
228 { |
|
229 if( clearFlag ) |
|
230 { |
|
231 nBits = initBits; |
|
232 maxCode = (1<<nBits)-1; |
|
233 clearFlag = FALSE; |
|
234 } |
|
235 else |
|
236 { |
|
237 ++nBits; |
|
238 if ( nBits == bits ) |
|
239 maxCode = maxMaxCode; |
|
240 else |
|
241 maxCode = (1<<nBits)-1; |
|
242 } |
|
243 } |
|
244 |
|
245 if ( code == EOFCode ) |
|
246 { |
|
247 /* At EOF, write the rest of the buffer. */ |
|
248 while( curBits > 0 ) |
|
249 { |
|
250 writeChar( (Byte)(curAccum & 0xff), outfile ); |
|
251 curAccum >>= 8; |
|
252 curBits -= 8; |
|
253 } |
|
254 |
|
255 writePacket(outfile); |
|
256 } |
|
257 } |
|
258 |
|
259 /* |
|
260 * Add a character to the end of the current packet, and if it is 254 |
|
261 * characters, flush the packet to disk. |
|
262 */ |
|
263 void GifEncoder::writeChar( Byte c, QFile &fp ) |
|
264 { |
|
265 accum[ aCount++ ] = c; |
|
266 if( aCount >= 254 ) writePacket(fp); |
|
267 } |
|
268 |
|
269 /* |
|
270 * Flush the packet to disk, and reset the accumulator |
|
271 */ |
|
272 void GifEncoder::writePacket(QFile &fp) |
|
273 { |
|
274 if ( aCount > 0 ) |
|
275 { |
|
276 fp.putch( aCount ); |
|
277 fp.writeBlock( (const char *)accum, aCount ); |
|
278 aCount = 0; |
|
279 } |
|
280 } |
|
281 |
|
282 void GifEncoder::clearHashTable() /* reset code table */ |
|
283 { |
|
284 int *htab_p = htab; |
|
285 int i; for (i=0;i<hashTableSize;i++) *htab_p++ = -1; |
|
286 } |
|
287 |