1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
|
This file: README for gzf
Contents
--------
1. Introduction
2. Why an alternative GIF format?
3. GZF Compressed Raster Data
4. GIF -> GZF conversion
5. GIF LZW -> compress LZW Translation
6. Decoding GIF LZW raster data using the LZWStream package
7. Programming Example
8. Using the LZWStream package in your own applications
9. Using gif2gzf to convert gif images to gzf images
10. Reading GZF Images
11. License
12. Author, contact information and where to get it
1. Introduction
---------------
GZF is a graphics format very similar to CompuServe's GIF graphics format.
The Gif graphics format has been around since 1987, when CompuServe published
the Graphics Interchange Format definition (also known as GIF87a). It defines
an open standard for exchanging graphics, in which the image data is stored in
a compressed raster format. The problem with the GIF specification lies in the
compression method that is used to compress and decompress the image data. The
Lempel-Ziv-Welch algorithm used for both compression *and* decompression is
patented by Unisys under US patent no. 4,558,302.
Unisys claims to only have found out of the use of LZW in the gif specification
in 1993 (although there are strong indications they have known since 1989) and
since then they are denying the use of LZW without a license in all types of
both freeware and commercial software. Their point of view regarding the use of
LZW in freeware is a totally outrageous one: it requires writers of freeware to
obtain a license (at the price of 1,000$ in 1997) and to give Unisys $0.10 for
every copy of their software that is used. The situation for commercial
software doesn't differ much and is in some aspects even worse. If you are
writing a commercial software library, you must obtain a 1,000$ license from
Unisys before you can even begin to think about selling your product. And even
if you have a license, you can *only* sell your product to other users when
they have also obtained a license from Unisys at the same price...
Clearly, Unisys doesn't know what it is doing. If they pursue this policy it
will mean the end of GIF.
The GZF format described in this document does not suffer from this decrepancy
in the GIF specification, while keeping the required changes to the GIF format
to a minimum. In fact, the only difference between GZF and GIF is the format of
the compressed data. The GZF format stores the compressed raster data in the
deflate format (as described in RFC 1951). This format is used in various
public domain compression packages such as zlib, gzip and Info-Zip.
We have chosen to use zlib as a way to achieve compression and decompression of
the raster data, since zlib is the only library with the most open license. The
zlib license allows usage of zlib in both freeware and commercial software
without requiring any license or licensing fees. Both the gzip and Info-Zip
license allow use of their algorithms in freeware without a license but
restrict the use for commercial purposes.
2. Why an alternative GIF format?
---------------------------------
Since the GIF controversy first surfaced, there have been several attempts to
define a new GIF format, but all have been abandoned in favor of the PNG
graphics format. This PNG format is superior to the GIF format, but it is not
a replacement for the GIF format. Therefore a number of features of the GIF
format are not supported by the PNG format. One of the most eye-catching
features of the GIF specification which is not present in the current PNG
specification is that a GIF image can contain multiple images in a single file.
When these images are displayed sequentially, an animation results.
Since Netscape introduced support for these GIF animations, they have come to
become a widely used element on the World Wide Web, where they are used to
flourish otherwise static HTML documents.
While Netscape made a strong marketing move by adding animated GIF support to
their browser, it has also prevented a rapid replacement of the GIF image
format with the PNG image format. Netscape is probably a company which can
afford a LZW license, but many software developers can not. The only way in
which many software developers can still support GIF images while not required
a LZW license is an alternative GIF format. The GZF format described in this
document provides this alternative.
The routines contained with the GZF code allow developers to create and read
GZF images, and, more important, it also provides a way to read GIF images
without requiring a LZW license.
3. GZF Compressed Raster Data
-----------------------------
The raster data in the GZF image format is compressed into the ``deflate'' data
format. This format is described in rfc1951.txt, of which the Abstract is shown
below:
This specification defines a lossless compressed data format that is
compatible with the widely used GZIP utility. The format includes a
cyclic redundancy check value for detecting data corruption. The
format presently uses the DEFLATE method of compression but can be
easily extended to use other compression methods. The format can be
implemented readily in a manner not covered by patents.
Especially the last sentence is of importance, it means that any software
developer can write a deflate compressor/decompressor without having to fear
a lawsuit.
To remain compliant with the Gif specification, the compressed raster data is
stored in blocks of 0 to 255 bytes preceeded with a character count. A block
with a zero byte count terminates the raster data stream.
4. GIF -> GZF conversion
------------------------
To convert existing gif images to gzf images, the following conversion method
is proposed:
1. Compare the first six bytes of the file to either GIF87a or GIF89a. Gif
images are always identified by one of these magic numbers. If the file
is a gif file, proceed and else terminate;
2. read and write the screen descriptor;
3. read and write a possibly global colormap;
4. read and write all extension blocks up to the first compressed raster data
5. read a compressed raster data block
6. convert the compressed raster data to a format understood by the standard
Unix "compress" utility;
7. uncompress the converted, but still compressed, raster data by feeding it
to the "compress" utility;
8. compress the uncompressed raster data using the zlib function compress and
store the returned, compressed raster data in a temporary buffer;
9. repeat steps 4 till 8 until a block with a zero byte count is encountered;
10.write the temporary buffer in blocks of 256 bytes;
11.write the gif terminator character and terminate conversion.
The "compress" utility mentioned in steps 6 and 7 uses a slightly modified
version of the LZW algorithm, which is also patented. By converting the
compressed data rather than uncompressing it using a LZW algorithm introduces
a performance penalty, but it *ensures* that your application does not have to
use the dreaded LZW algorithm but can instead rely on the presence of the
"compress" utility. You are almost guarenteed of the presence of this utility
since it is a standard tool on *every* Unix distribution. If for some reason,
compress should not be present on your system, the public domain compress and
gzip compression tools can mimic uncompress. See the Translation section below
for more details.
The gif2gzf source code implements the above method.
5. GIF LZW -> compress LZW Translation
--------------------------------------
The translation process takes care of some minor differences between the GIF
compressed data and the compress compressed data.
The GIF LZW format uses dynamic clear code and end-of-data code whereas
compress uses a static clear code and doesn't have an end-of-data code.
The dynamic stream codes in GIF are determined by the value of the "code size".
This code size is a value indicating the minimum number of bits required
to represent the set of actual pixel values. The value of clear code is given
by <code size>^2, and the end-of-data code is defined as <clear code>+1.
The first available compression code value is <clear code>+2.
The main difference however concerns the minimum code size specification in
compressed data. This number controls the generation of the LZW codes in the
compressed data. While GIF images use anything between 3 to 12 bits, compress
only works between 8 and 16 (maximum codeSize for compress, the minimum-maximum
is 12). This means that the conversion process must make a distinction between
the code size: anything below 8 and 8 or above.
The latter case is quite easy: the gif LZW codes are simply copied to compress
output with possible adjustment of the aforementioned codes. The first case
involves a lot more work as the 2 to 7 bits LZW codes need to be transformed
to 8 bit codes. This conversion process actually comes pretty close to
violating the LZW patent, but since only the LZW codes are read, adjusted and
packed again the gzf gif decoder is patent-safe.
6. Decoding GIF LZW raster data using the LZWStream package
-----------------------------------------------------------
The gzf source code comes with a package that uncompresses the LZW compressed
raster data in gif images by using the "compress" utility. Proper use of this
package requires you to understand the GIF format (the Gif87a and Gif89a
specification can be found in the docs directory).
The LZWStream package only decodes gif raster data and does not provide a
way to read an entire gif image.
An LZWStream is created by using the LZWStreamCreate function:
LZWStream *LZWStreamCreate(ib, zCmd)
ImageBuffer *ib;
char *zCmd;
ImageBuffer is a memory object which is used to mimic file access but in
memory, which can provide a substantial speedup. An ImageBuffer is created
by using the ImageFileToBuffer(char *file) function, where "file" represents
the file that is to be loaded.
zCmd is the name of the "uncompress" program that is to be used. If NULL is
specified, the LZWStream package will default to "uncompress". This argument
can be used to specify an alternative LZW decoder, such as gzip.
When LZWStreamCreate returns successfully, you need to set two read functions:
readOK and getData.
The proto's for these functions are:
size_t (*readOK)(ImageBuffer*, unsigned char*, int);
size_t (*getData)(ImageBuffer*, unsigned char*);
The code in buffer.c offers two default functions:
size_t ReadOK(ib, buf, len)
ImageBuffer *ib;
unsigned char *buf;
int len;
This function copies len characters from ib to buf and returns the number of
characters copied. It returns 0 when the end of the current ImageBuffer is
reached.
size_t GifGetDataBlock(ib, buf)
ImageBuffer *ib;
unsigned char *buf;
This function retrieves an the contents of an entire block of data found in
a gif image. Gif data blocks consist of a single byte with a character count,
followed by the specified number of bytes. The above function reads the
character count and copies the requested number of bytes in the given
destination buffer. An ImageBuffer is destroyed by using the
FreeImageBuffer() macro.
Feel free to replace these default functions with your own readers.
When you have reached the point were you want to obtain the decompressed
raster data, you *must* call the following function:
int LZWStreamInit(lzw)
LZWStream *lzw;
lzw is the LZWStream returned by LZWStreamCreate. This function returns
1 when the stream was successfully initialized or something else when an
error occurs.
The next step is to convert the gif LZW compressed raster data to the
LZW format used by "compress". This is done by the following function:
void LZWStreamConvert(lzw)
LZWStream *lzw;
When this function returns, the data has been converted but not yet
uncompressed. The LZWStream package offers two functions to do this:
unsigned char *LZWStreamUncompress(lzw, len)
LZWStream *lzw;
int *len;
int LZWStreamFillBuffer(lzw, data, size)
LZWStream *lzw;
unsigned char *data;
int size;
The first function decompress the converted data and returns it in an
allocated buffer. "len" is the size of the uncompressed data.
The second function also decompresses the converted data but allows you to
read it in seperate chunks. "size" is the number of bytes that should be read
and "data" is the destination buffer. You should make sure that the specified
destination buffer is large enough to hold "size" bytes. This function returns
the number of bytes copied into "data".
The final step is to destroy the LZWStream:
void LZWStreamDestroy(lzw)
LZWStream *lzw;
This function removes any temporary files that were created and cleans up
the memory used by the given stream. It does not destroy the ImageBuffer
contained in it. When this function returns, "lzw" is no longer valid.
As a final note: the LZWStream structure contains a field called err_msg.
This field contains an error message whenever an error occured. You can
choose to display this message or ignore it. You may *not* free this
message, it points to a static data space.
7. Programming Example
----------------------
The following example demonstrates a possible use of the ImageBuffer and
LZWStream objects.
#include "ImBuffer.h" /* ImageBuffer structure & functions */
#include "LZWStream.h" /* LZWStream functions */
unsigned char*
readGifImage(char *file)
{
ImageBuffer *ib;
LZWStream *lzw;
unsigned char c, buf[280];
ib = ImageFileToBuffer(file);
/* read and check gif magic */
ReadOK(ib, &buf, 6);
{
[ verify gif magic ]
}
/* read logical screen descriptor */
ReadOK(ib, buf, 7);
[ pull out necessary image attributes ]
[ read the global colormap using the current ImageBuffer ]
while(1)
{
/* read block identifier */
ReadOK(ib, &c, 1);
/* gif terminator */
if(c == ';')
break;
/* a gif extension block */
if(c == '!')
{
/* get extension type */
ReadOK(ib, &c, 1);
[ deal with it ]
continue;
}
if(c != ',')
continue; /* not a valid start character */
/* read image descriptor */
ReadOK(ib, buf, 9);
[ pull out necessary information ]
[ read a possible local colormap, again using the ImageBuffer ]
/* create a new stream object */
lzw = LZWStreamCreate(ib, NULL);
/* initialize uncompression */
LZWStreamInit(lzw);
/* set read functions */
lzw->readOK = readOK;
lzw->getData = GifGetDataBlock;
/* convert data */
LZWStreamConvert(lzw);
/* get uncompressed data */
data = LZWStreamUncompress(lzw, &len);
/* destroy stream */
LZWStreamDestroy(lzw);
/* we have our image */
break;
}
/* free the ImageBuffer */
FreeImageBuffer(ib);
/* and return the image data */
return(data);
}
The above is a very simple example which doesn't care about errors and
interlaced images. Obvious enhancements would be to add error checking on
every ReadOK call, dealing with multiple images and comparing the size of
the uncompressed raster data with the dimensions of this image. See
gif2gzf.c for a much more extensive example.
8. Using the LZWStream package in your own applications
-------------------------------------------------------
To use the LZWStream package in your own application, you need the following
files:
- LZWStream.h: required typedefs and function prototypes
- LZWStream.c: the LZW decompressor
- ImBuffer.h : ImageBuffer typedefs and function prototypes
- ImBuffer.c : ImageBuffer routines.
Compile LZWStream.c with -DNO_XMHTML defined.
Just add these files to your Makefile and you're done.
9. Using gif2gzf to convert gif images to gzf images
----------------------------------------------------
The gif2gzf utility converts a gif image to a gzf image. This tool is capable
of converting GIF87a, GIF89a, multi-image GIF87a, multi-image GIF89a and
multi-image GIF89a with the NETSCAPE2.0 loop extension.
The syntax to gif2gzf is as follows:
gif2gzf <giffile> <gzffile> [-v -p]
Where:
giffile: name of giffile to convert
gzffile: name of destination file
-v: verify deflate compression by inflating the deflate compressed data
-p: show progress count. Can be usefull when converting animated gifs.
gif2gzf is not capable of doing batch conversion, but you can write a simple
script that does just this.
If you are using a shell which knows the ``foreach'' command, you can do batch
conversion on the command line using the following sequence of commands:
hobbes [87][13:10] [/home/newt/src/libs/gzf/pics] >foreach file ( gif/*gif )
foreach? ../gif2gzf $file $file:r.gzf
foreach? end
gif/emailed.gif -> gif/emailed.gzf
Converted frame: 53
Size reduction: 29.72%
gif/techsup.gif -> gif/techsup.gzf
Converted frame: 0
Size reduction: 4.21%
....
hobbes [88][13:10] [/home/newt/src/libs/gzf/pics] >
That's about it.
10. Reading GZF Images
----------------------
Reading a GZF image is very similar to reading a GIF image. As was mentioned
in the introduction, the only difference between these formats is the way
in which the raster data is stored.
The following piece of code is based on giftoppm and shows how to decompress
and convert the GZF raster data to the actual image data.
static unsigned char *
ReadGZFImage(ImageBuffer *ib, int len, int height, int interlace)
{
int xpos = 0, ypos = 0, nread, foo;
static unsigned char *image, *data;
register unsigned char *dp, *dPtr;
/* uncompress GZF image data */
data = UncompressGZFData(ib, len*height, &nread);
/* sanity check */
if(data == NULL || nread == 0)
return(NULL);
dPtr = data;
/*
* interlaced image. Need to alternate uncompressed data to create the
* actual image.
*/
if(interlace)
{
int pass = 0, step = 8;
int pass = 0, step = 8;
register int i;
/* allocate image storage */
image = (unsigned char *)calloc(len * height, sizeof(char));
for(i = 0; i < height; i++)
{
if(ypos < height)
{
dp = &image[len * ypos];
for(xpos = 0; xpos < len; xpos++)
*dp++ = *dPtr++;
}
if((ypos += step) >= height)
{
if (pass++ > 0)
step /= 2;
ypos = step / 2;
}
}
/* no longer needed */
free(data);
return(image);
}
/* uncompressed data is same as image data */
return(data);
}
The following routine does the actual decompression:
/*
* dsize is the size of the decompressed image data and nread will be
* updated to reflect the actual no of bytes uncompressed.
*/
static unsigned char*
UncompressData(ImageBuffer *ib, int dsize, int *nread)
{
static unsigned char *output_buf;
unsigned char buf[256], c;
z_stream stream;
int err;
*nread = 0;
/* Skip code size, it's never used */
(void)ReadOK(ib, &c, 1);
/* allocate output buffer */
output_buf = (unsigned char*)calloc(dsize+1, sizeof(char));
/* initialize inflate stuff */
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
if((err = inflateInit(&stream)) != Z_OK)
{
fprintf(stderr, "inflateInit failed: %s\n", stream.msg);
free(output_buf);
return(NULL);
}
/* keep uncompressing until we reach the end of the compressed stream */
while(True)
{
/* next compressed block of data */
stream.avail_in = GifGetDataBlock(ib, buf);
stream.next_in = buf;
/* update output buffer */
stream.next_out = output_buf + stream.total_out;
stream.avail_out = dsize - stream.total_out;
/* uncompress it */
err = inflate(&stream, Z_PARTIAL_FLUSH);
/* check return value */
if(err != Z_OK && err != Z_STREAM_END)
{
fprintf(stderr, "inflate failed: %s\n", stream.msg);
free(output_buf);
return(NULL);
}
/* and break if inflate has finished uncompressing our data. */
if(err == Z_STREAM_END)
break;
}
/*
* Skip remaining data. The deflate format signals the end of compressed
* data itself, so we never reach the zero-data block in the above loop.
*/
while((GifGetDataBlock(ib, buf)) > 0);
*nread = stream.total_out;
/* successfull uncompress, return uncompressed image data */
return(output_buf);
}
11. License
-----------
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. This software is provided "as is" without express or
implied warranty.
Copyright (C) 1994-1997 by Ripley Software Development
All Rights Reserved
12. Author, contact information and where to get it.
----------------------------------------------------
The LZWStream package is written and maintained by Koen D'Hondt and is
originally part of the XmHTML Widget, a LGPL'd Motif widget capable of
displaying HTML 3.2 documents. This widget has built-in support for various
types of images including gif and animated gif images.
If you have any comments, encounter problems, suggestions or just want to tell
us how great this package is ;-) feel free to contact me. ripley@xs4all.nl
Koen D'Hondt
Ripley Software Development.
email: ripley@xs4all.nl
URL : http://www.xs4all.nl/~ripley
|