home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 24 / CD_ASCQ_24_0995.iso / vrac / imagefaq.zip / FAQ04.TXT < prev    next >
Internet Message Format  |  1995-06-10  |  21KB

  1. From news.alpha.net!news.mathworks.com!gatech!news.sprintlink.net!noc.netcom.net!netcom.com!jdm Sat Jun 10 11:31:34 1995
  2. Newsgroups: comp.graphics,comp.answers,news.answers
  3. Path: news.alpha.net!news.mathworks.com!gatech!news.sprintlink.net!noc.netcom.net!netcom.com!jdm
  4. From: jdm@netcom.com
  5. Subject: Graphics File Formats FAQ (Part 4 of 4): Tips and Tricks of the Trade
  6. Message-ID: <graphics/fileformats-faq-4-802022477@netcom.com>
  7. Followup-To: poster
  8. Summary: This document answers many of the most frequently asked 
  9.     questions about graphics file formats on Usenet.
  10. Keywords: FAQ, GRAPHICS, FORMAT, IMAGE
  11. Sender: jdm@netcom9.netcom.com
  12. Supersedes: <graphics/fileformats-faq-4-799731112@netcom.com>
  13. Reply-To: jdm@netcom.com (James D. Murray)
  14. Organization: None Whatsoever
  15. References: <graphics/fileformats-faq-1-802022477@netcom.com> 
  16. Date: Thu, 1 Jun 1995 16:01:37 GMT
  17. Approved: news-answers-request@MIT.EDU
  18. Expires: Sat, 1 Jul 1995 16:01:17 GMT
  19. Lines: 527
  20. Xref: news.alpha.net comp.graphics:77824 comp.answers:12192 news.answers:45355
  21.  
  22. Posted-By: auto-faq 3.1.1.2
  23. Archive-name: graphics/fileformats-faq/part4
  24. Posting-Frequency: monthly
  25. Last-modified: 01Jun95
  26.  
  27. This FAQ (Frequently Asked Questions) list contains information on graphics
  28. file formats, including, raster, vector, metafile, Page Description Language,
  29. 3D object, animation, and multimedia formats.
  30.  
  31. This FAQ is divided into four parts, each covering a different area of
  32. graphics file format information:
  33.  
  34.   Graphics File Formats FAQ: General Graphics Format Questions (Part 1 of 4)
  35.   Graphics File Formats FAQ: Image Conversion and Display Programs (Part 2 of 4)
  36.   Graphics File Formats FAQ: Where to Get File Format Specifications (Part 3 of 4)
  37.   Graphics File Formats FAQ: Tips and Tricks of the Trade (Part 4 of 4)
  38.  
  39. Please email contributions, corrections, and suggestions about this FAQ to
  40. jdm@netcom.com. Relevant information posted to newsgroups will not
  41. automatically make it into this FAQ.
  42.  
  43. -- James D. Murray <jdm@netcom.com>  ;-{)>>>>
  44.  
  45. ----------------------------------------------------------------------
  46.  
  47. Subject: 0. Contents of Tips and Tricks of the Trade
  48.  
  49. Subjects marked with <NEW> are new to this FAQ.
  50. Subjects marked with <UPD> have been updated since the last release
  51.  of this FAQ.
  52.  
  53. I. General questions about this FAQ
  54.  
  55. 0. Maintainer's Comments
  56. 1. What's new in this latest FAQ release?
  57.  
  58. II. Programming Tips for Graphics File Formats
  59.  
  60. 0. What's the best way to read a file header?
  61. 1. What's this business about endianness?
  62. 2. How can I determine the byte-order of a system at run-time?
  63. 3. How can I identify the format of a graphics file?
  64. 4. What are the format identifiers of some popular file formats?
  65.  
  66. III. Kudos and Assertions
  67.  
  68. 0. Acknowledgments 
  69. 1. About The Author 
  70. 2. Disclaimer 
  71. 3. Copyright Notice
  72.  
  73. ------------------------------
  74.  
  75. Subject: I. General questions about this FAQ
  76.  
  77. ------------------------------
  78.  
  79. Subject: 0. Maintainer's Comments
  80.  
  81. Programmer's are code-hungry people. They just want the secrets and they want
  82. them to work NOW! But always in the back of a hack's mind there are the
  83. questions: "Is this really the best way to do this? Could it be better?".
  84.  
  85. This FAQ is to share ideas on the implementation details of reading, writing,
  86. converting, and displaying graphics file formats. You'll probably get some
  87. good ideas here, find a few things you didn't know about, and even have a few
  88. suggestions and improvements of you own to add (send them to jdm@netcom.com).
  89.  
  90. If you need to know the best way to do something with file formats, or just
  91. find it embarrassing to implement a chunk of some other programmer's code and
  92. then have to admit you really don't understand how it works, then this FAQ is
  93. for you.
  94.  
  95. ------------------------------
  96.  
  97. Subject: 1. What's new in this latest FAQ release?
  98.  
  99. A few more file identifiers have been added, but that's all. This is truly
  100. the most neglected part of this FAQ. I'm gonna hafta start stifting through
  101. comp.graphics more frequently to fill this part out, I guess.
  102.  
  103. ------------------------------
  104.  
  105. Subject: II. Programming Tips for Graphics File Formats
  106.  
  107. ------------------------------
  108.  
  109. Subject: 0. What's the best way to read a file header?
  110.  
  111. You wouldn't think there's a lot of mystery about reading a few bytes from a
  112. disk file, eh? Programmer's, however, are constantly loosing time because
  113. they don't consider a few problems that may occur and cause them to loose
  114. time. Consider the following code:
  115.  
  116.   typedef struct _Header
  117.   {
  118.     BYTE Id;
  119.     WORD Height;
  120.     WORD Width;
  121.     BYTE Colors;
  122.   } HEADER;
  123.  
  124.   HEADER Header;
  125.  
  126.   void ReadHeader(FILE *fp)
  127.   {
  128.     if (fp != (FILE *)NULL)
  129.       fread(&Header, sizeof(HEADER), 1, fp);
  130.   }
  131.  
  132. Looks good, right? The fread() will read the next sizeof(HEADER) bytes from a
  133. valid FILE pointer into the Header data structure. So what could go wrong?
  134.  
  135. The problem often encountered with this method is one of element alignment
  136. within structures. Compilers may pad structures with "invisible" elements to
  137. allow each "visible" element to align on a 2- or 4-byte address boundary.
  138. This is done for efficiency in accessing the element while in memory. Padding
  139. may also be added to the end of the structure to bring it's total length to
  140. an even number of bytes. This is done so the data following the structure in
  141. memory will also align on a proper address boundary.
  142.  
  143. If the above code is compiled with no (or 1-byte) structure alignment the
  144. code will operate as expected. With 2-byte alignment an extra two bytes would
  145. be added to the HEADER structure in memory and make it appear as such:
  146.  
  147.   typedef struct _Header
  148.   {
  149.     BYTE Id;
  150.     BYTE Pad1;      // Added padding
  151.     WORD Height;
  152.     WORD Width;
  153.     BYTE Colors;
  154.     BYTE Pad2;      // Added padding
  155.   } HEADER;
  156.  
  157. As you can see the fread() will store the correct value in Id, but the first
  158. byte of Height will be stored in the padding byte. This will throw off the
  159. correct storage of data in the remaining part of the structure causing the
  160. values to be garbage.
  161.  
  162. A compiler using 4-byte alignment would change the HEADER in memory as such:
  163.  
  164.   typedef struct _Header
  165.   {
  166.     BYTE Id;
  167.     BYTE Pad1;      // Added padding
  168.     BYTE Pad2;      // Added padding
  169.     BYTE Pad3;      // Added padding
  170.     WORD Height;
  171.     WORD Width;
  172.     BYTE Colors;
  173.     BYTE Pad4;      // Added padding
  174.     BYTE Pad5;      // Added padding
  175.     BYTE Pad6;      // Added padding
  176.   } HEADER;
  177.  
  178. What started off as a 6-byte header increased to 8 and 12 bytes thanks to
  179. alignment. But what can you do? All the documentation and makefiles you write
  180. will not prevent someone from compiling with the wrong options flag and then
  181. pulling their (or your) hair out when your software appears not to work
  182. correctly.
  183.  
  184. Now considering this alternative to the ReadHeader() function:
  185.  
  186.   HEADER Header;
  187.  
  188.   void ReadHeader(FILE *fp)
  189.   {
  190.     if (fp != (FILE *)NULL)
  191.     {
  192.       fread(&Header.Id, sizeof(Header.Id), 1, fp);
  193.       fread(&Header.Height, sizeof(Header.Height), 1, fp);
  194.       fread(&Header.Width, sizeof(Header.Width), 1, fp);
  195.       fread(&Header.Colors, sizeof(Header.Colors), 1, fp);
  196.     }
  197.   }
  198.  
  199. What both you and your compiler now see is a lot more code. Rather than
  200. reading the entire structure in one, elegant shot, you read in each element
  201. separately using multiple calls to fread(). The trade-off here is increased
  202. code size for not caring what the structure alignment option of the compiler
  203. is set to. These cases are also true for writing structures to files using
  204. fwrite(). Write only the data and not the padding please.
  205.  
  206. But is there still anything we've yet over looked? Will fread() (fscanf(),
  207. fgetc(), and so forth) always return the data we expect?  Will fwrite()
  208. (fprintf(), fputc(), and so forth) ever write data that we don't want, or in
  209. a way we don't expect? Read on to the next section...
  210.  
  211. ------------------------------
  212.  
  213. Subject: 1. What's this business about endianness?
  214.  
  215. So you've been pulling you hair out trying to discover why your elegant and
  216. perfect-beyond-reproach code, running on your Macintosh or Sun, is reading
  217. garbage from PCX and TGA files. Or perhaps your MS-DOS or Windows
  218. application just can't seem to make heads or tails out of that Sun Raster
  219. file. And, to make matters even more mysterious, it seems your most
  220. illustrious creation will read some TIFF files, but not others.
  221.  
  222. As was hinted at in the previous section, just reading the header of a
  223. graphics file one field is not enough to insure data is always read correctly
  224. (not enough for portable code, anyway). In addition to structure, we must also
  225. consider the endianness of the file's data, and the endianness of the
  226. system's architecture our code is running on.
  227.  
  228. Here's are some baseline rules to follow:
  229.  
  230.   1) Graphics files typically use a fixed byte-ordering scheme. For example, 
  231.      PCX and TGA files are always little-endian; Sun Raster and Macintosh
  232.      PICT are always big-endian.
  233.   2) Graphics files that may contain data using either byte-ordering scheme
  234.      (for example TIFF) will have an identifier that indicates the
  235.      endianness of the data.
  236.   3) ASCII-based graphics files (such as DXF and most 3D object files),
  237.      have no endianness and are always read in the same way on any system.
  238.   4) Most CPUs use a fixed byte-ordering scheme. For example, the 80486
  239.      is little-endian and the 68040 is big-endian.
  240.   5) You can test for the type of endianness a system using software.
  241.   6) There are many systems that are neither big- nor little-endian; these
  242.      middle-endian systems will possibly cause such byte-order detection
  243.      tests to return erroneous results.
  244.  
  245. Now we know that using fread() on a big-endian system to read data from a
  246. file that was originally written in little-endian order will return incorrect
  247. data. Actually, the data is correct, but the bytes that make up the data are
  248. arranged in the wrong order. If we attempt to read the 16-bit value 1234h
  249. from a little-endian file, it would be stored in memory using the big-endian
  250. byte-ordering scheme and the value 3412h would result. What we need is a swap
  251. function to change the resulting position of the bytes:
  252.  
  253.   WORD SwapTwoBytes(WORD w)
  254.   {
  255.       register WORD tmp;
  256.       tmp =  (w & 0x00FF);
  257.       tmp = ((w & 0xFF00) >> 0x08) | (tmp << 0x08);
  258.       return(tmp);
  259.   }
  260.   
  261. Now we can read a two-byte header value and swap the bytes as such:
  262.  
  263.   fread(&Header.Height, sizeof(Header.Height), 1, fp);
  264.   Header.Height = SwapTwoBytes(Header.Height);
  265.  
  266. But what about four-byte values? The value 12345678h would be stored as
  267. 78563412h. What we need is a swap function to handle four-byte values:
  268.  
  269.   DWORD SwapFourBytes(DWORD dw)
  270.   {
  271.       register DWORD tmp;
  272.       tmp =  (dw & 0x000000FF);
  273.       tmp = ((dw & 0x0000FF00) >> 0x08) | (tmp << 0x08);
  274.       tmp = ((dw & 0x00FF0000) >> 0x10) | (tmp << 0x08);
  275.       tmp = ((dw & 0xFF000000) >> 0x18) | (tmp << 0x08);
  276.       return(tmp);
  277.   }
  278.  
  279. But how do we know when to swap and when not to swap? We always know the
  280. byte-order of a graphics file that we are reading, but how do we check what
  281. the endianness of system we are running on is? Using the C language, we might
  282. use preprocessor switches to cause a conditional compile based on a system
  283. definition flag:
  284.  
  285.   #define MSDOS     1
  286.   #define WINDOWS   2
  287.   #define MACINTOSH 3
  288.   #define AMIGA     4
  289.   #define SUNUNIX   5
  290.   
  291.   #define SYSTEM    MSDOS
  292.   
  293.   #if defined(SYSTEM == MSDOS)  
  294.     // Little-endian code here
  295.   #elif defined(SYSTEM == WINDOWS)  
  296.     // Little-endian code here
  297.   #elif defined(SYSTEM == MACINTOSH)  
  298.     // Big-endian code here
  299.   #elif defined(SYSTEM == AMIGA)  
  300.     // Big-endian code here
  301.   #elif defined(SYSTEM == SUNUNIX)  
  302.     // Big-endian code here
  303.   #else
  304.   #error Unknown SYSTEM definition
  305.   #endif
  306.  
  307. My reaction to the above code was *YUCK!* (and I hope yours was too!).  A
  308. snarl of fread(), fwrite(), SwapTwoBytes(), and SwapFourBytes() functions
  309. laced between preprocessor statements is hardly elegant code, although
  310. sometimes it is our best choice. Fortunately, this is not one of those times.
  311.  
  312. What we first need is a set of functions to read the data from a file using
  313. the byte-ordering scheme of the data. This effectively combines the read\write
  314. and swap operations into one set of functions. Considering the following:
  315.  
  316.   WORD GetBigWord(FILE *fp)
  317.   {
  318.       register WORD w;
  319.       w =  (WORD) (fgetc(fp) & 0xFF);
  320.       w = ((WORD) (fgetc(fp) & 0xFF)) | (w << 0x08);
  321.       return(w);
  322.   }
  323.   
  324.   WORD GetLittleWord(FILE *fp)
  325.   {
  326.       register WORD w;
  327.       w =  (WORD) (fgetc(fp) & 0xFF);
  328.       w = ((WORD) (fgetc(fp) & 0xFF) << 0x08);
  329.       return(w);
  330.   }
  331.   
  332.   DWORD GetBigDoubleWord(FILE *fp)
  333.   {
  334.       register WORD dw;
  335.       dw =  (DWORD) (fgetc(fp) & 0xFF);
  336.       dw = ((DWORD) (fgetc(fp) & 0xFF)) | (dw << 0x08);
  337.       dw = ((DWORD) (fgetc(fp) & 0xFF)) | (dw << 0x08);
  338.       dw = ((DWORD) (fgetc(fp) & 0xFF)) | (dw << 0x08);
  339.       return(dw);
  340.   }
  341.   
  342.   DWORD GetLittleDoubleWord(FILE *fp)
  343.   {
  344.       register WORD dw;
  345.       dw =  (DWORD) (fgetc(fp) & 0xFF);
  346.       dw = ((DWORD) (fgetc(fp) & 0xFF) << 0x08);
  347.       dw = ((DWORD) (fgetc(fp) & 0xFF) << 0x10);
  348.       dw = ((DWORD) (fgetc(fp) & 0xFF) << 0x18);
  349.       return(dw);
  350.   }
  351.   
  352.   void PutBigWord(WORD w, FILE *fp)
  353.   {
  354.       fputc((w >> 0x08) & 0xFF, fp);
  355.       fputc(w & 0xFF, fp);
  356.   }
  357.   
  358.   void PutLittleWord(WORD w, FILE *fp)
  359.   {
  360.       fputc(w & 0xFF, fp);
  361.       fputc((w >> 0x08) & 0xFF, fp);
  362.   }
  363.   
  364.   void PutBigDoubleWord(DWORD dw, FILE *fp)
  365.   {
  366.       fputc((dw >> 0x08) & 0xFF, fp);
  367.       fputc((dw >> 0x10) & 0xFF, fp);
  368.       fputc((dw >> 0x18) & 0xFF, fp);
  369.       fputc(dw & 0xFF, fp);
  370.   }
  371.   
  372.   void PutLittleDoubleWord(DWORD dw, FILE *fp)
  373.   {
  374.       fputc(w & 0xFF, fp);
  375.       fputc((w >> 0x08) & 0xFF, fp);
  376.       fputc((w >> 0x10) & 0xFF, fp);
  377.       fputc((w >> 0x18) & 0xFF, fp);
  378.   }
  379.  
  380. If we were reading a little-endian file on a big-endian system (or visa
  381. versa), the previous code:
  382.  
  383.   fread(&Header.Height, sizeof(Header.Height), 1, fp);
  384.   Header.Height = SwapTwoBytes(Header.Height);
  385.  
  386. Would be replaced by:
  387.   
  388.   Header.Height = GetLittleWord(fp);
  389.  
  390. The code to write the same value to a file would be changed from:
  391.  
  392.   Header.Height = SwapTwoBytes(Header.Height);
  393.   fwrite(&Header.Height, sizeof(Header.Height), 1, fp);
  394.  
  395. To the slightly more readable:
  396.  
  397.   PutLittleWord(Header.Height, fp);
  398.  
  399. Note that these functions are the same regardless of the endianness of a
  400. system. For example, the ReadLittleWord() will always read a two-byte value
  401. from a little-endian file regardless of the endianness of the system;
  402. PutBigDoubleWord() will always write a four-byte big-endian value, and so
  403. forth.
  404.  
  405. ------------------------------
  406.  
  407. Subject: 2. How can I determine the byte-order of a system at run-time?
  408.  
  409. You may wish to optimize how you read (or write) data from a graphics file
  410. based on the endianness of your system. Using the GetBigDoubleWord() function
  411. mentioned in the previous section to read big-endian data from a file on a
  412. big-endian system imposes extra overhead we don't really need (although if
  413. the actual number of read/write operations in your program is small you might
  414. not consider this overhead to be too bad).
  415.  
  416. If our code could tell what the endianness of the system was at run-time, it
  417. could choose (using function pointers) what set of read/write functions to
  418. use. Look at the following function:
  419.  
  420.   #define BIG_ENDIAN      0
  421.   #define LITTLE_ENDIAN   1
  422.  
  423.   int TestByteOrder(void)
  424.   {
  425.       short int w = 0x0001;
  426.       char *byte = (char *) &word;
  427.       return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
  428.   }
  429.  
  430. This code assigns the value 0001h to a 16-bit integer. A char pointer is then
  431. assigned to point at the first (least-significant) byte of the integer value.
  432. If the first byte of the integer is 01h, then the system is little-endian
  433. (the 01h is in the lowest, or least-significant, address). If it is 00h then
  434. the system is big-endian.
  435.  
  436. ------------------------------
  437.  
  438. Subject: 3. How can I identify the format of a graphics file?
  439.  
  440. When writing any type of file or data stream reader it is very important to
  441. implement some sort of method for verifying that the input data is in the
  442. format you expect. Here are a few methods:
  443.  
  444. 1) Trust the user of your program to always supply the correct data, thereby
  445. freeing you from the tedious task of writing any type of format
  446. identification routines. Choose this method and you will provide solid proof
  447. that contradicts the popular claim that users are inherently far more stupid
  448. than programmers.
  449.  
  450. 2) Read the file extension or descriptor. A GIF file will always have the
  451. extension .GIF, right? Targa files .TGA, yes?  And TIFF files will have
  452. an extension of .TIF or a descriptor of TIFF. So no problem?
  453.  
  454. Well, for the most part, this is true. This method certainly isn't
  455. bulletproof, however.  Your reader will occasionally be fed the odd-batch of
  456. mis-label files ("I thought they were PCX files!"). Or files with
  457. unrecognized mangled extensions  (.TAR rather than .TGA or .JFI rather than
  458. .JPG) that your reader knows how to read, but won't read because it doesn't
  459. recognize the extensions. File extensions also won't usually tell you the
  460. revision of the file format you are reading (with some revisions creating an
  461. almost entirely new format). And more than one file format share the more
  462. common file extensions (such as .IMG and .PIC). And last of all, data streams
  463. have no file extensions or descriptors to read at all.
  464.  
  465. 3) Read the file and attempt to recognize the format by specific patterns in
  466. the data. Most file formats contain some sort of identifying pattern of data
  467. that is identical in all files. In some cases this pattern gives and
  468. indication of the revision of the format (such as GIF87a and GIF89a) or
  469. the endianness of the data format.
  470.  
  471. Nothing is easy, however. Not all formats contain such identifiers (such as
  472. PCX). And those that do don't necessarily put it at the beginning of the
  473. file. This means if the data is in the format of a stream you many have to
  474. read (and buffer) most or all of the data before you can determine the
  475. format. Of course, not all graphics formats are suitable to be read as a data
  476. stream anyway.
  477.  
  478. Your best bet for a method of format detection is a combination of methods
  479. two and three. First believe the file extension or descriptor, read some
  480. data, and check for identifying data patterns. If this test fails, then
  481. attempt to recognize all other known patterns.
  482.  
  483. Run-time file format identification a black-art at best.
  484.  
  485. ------------------------------
  486.  
  487. Subject: 4. What are the format identifiers of some popular file formats?
  488.  
  489. Here are a few algorithms that you can use to determine the format of a
  490. graphics file at run-time.
  491.  
  492.  
  493. GIF: The first six bytes of a GIF file will be the byte pattern of 
  494.      474946383761h ("GIF87a") or 474946383961h ("GIF89a").
  495.  
  496. PNG: The first eight bytes of all PNG files are 89504e470d0a1a0ah.
  497.  
  498. Sun: The first four bytes of a Sun Rasterfile are 59a66a95h. If you have
  499.      accidentally read this identifier using the little-endian byte order
  500.      this value will will be read as 956aa659h.
  501.  
  502. TIFF: The first four bytes of a big-endian TIFF files are 4d4d002ah and
  503.       49492a00h for little-endian TIFF files.
  504.  
  505. ------------------------------
  506.  
  507. Subject: III. Kudos and Assertions
  508.  
  509. ------------------------------
  510.  
  511. Subject: 0. Acknowledgments
  512.  
  513. Nobody yet. 
  514.  
  515. Doesn't anybody have any neat tricks to share?
  516.  
  517. ------------------------------
  518.  
  519. Subject: 1. About The Author
  520.  
  521. The author of this FAQ, James D. Murray, lives in the City of Orange, Orange
  522. County, California, USA. He is the co-author of the book Encyclopedia of
  523. Graphics File Formats published by O'Reilly and Associates, makes a passable
  524. living writing Microsoft Windows applications in C++, and may be reached as
  525. jdm@netcom.com, or via U.S. Snail at: P.O. Box 70, Orange, CA 92666-0070 USA.
  526.  
  527. GCS d-- H++ s g- p? au+ a w+ v++ C+++(++++) US+++ p++>++++ L>++ 3 E--- N++ K-
  528. W---$ M-@ V-- po Y+ t++ 5-- j>x R+>-- G' tv-->! b+++ D++ B e- u* h- f r-->+++
  529. n++ y*(**)
  530.  
  531. ------------------------------
  532.  
  533. Subject: 2. Disclaimer
  534.  
  535. While every effort has been taken to insure the accuracy of the information
  536. contained in this FAQ list compilation, the author and contributors assume no
  537. responsibility for errors or omissions, or for damages resulting from the use
  538. of the information contained herein.
  539.  
  540. ------------------------------
  541.  
  542. Subject: 3. Copyright Notice
  543.  
  544. This FAQ is Copyright (C) 1994-95 by James D. Murray. All Rights Reserved. 
  545. This work may be reproduced, in whole or in part, using any medium,
  546. including, but not limited to, electronic transmission, CD-ROM, or published
  547. in print, under the condition that this copyright notice remains intact.
  548.  
  549.  
  550.