[comp.graphics] solution: pixel aspect ratio in GIF images

ahg@mentor.cc.purdue.edu (Allen Braunsdorf) (07/17/90)

In article <9894@pt.cs.cmu.edu> tgl@zog.cs.cmu.edu (Tom Lane) writes:
>In article <9866@pt.cs.cmu.edu>, I asked:
>> The GIF documentation that I have makes no mention of the question of
>> pixel shape.  Is there any recognized convention about whether a GIF
>> image is set up for square or non-square pixels, and what the assumed
>> pixel aspect ratio is in the latter case?
>
>There seems to be general agreement that there isn't a recognized
>standard for pixel aspect ratio.

True and unfortunate.  An image format that claims to be for
interchange is worthless for exactly that purpose because of this
fact.

Watch my lips:  Putting an image into GIF without somehow documenting
the aspect ratio destroys the image.  The originally intended image
cannot be recovered with certainty.

>Erik Talvola (talvola@janus.Berkeley.EDU) points out that a GIF picture
>of size 320x200 pixels is almost surely intended for an IBM CGA display,

or a Commodore 64 or an Atari ST or an Amiga....  I know for a fact
that my 64 and my Amiga make different sized 320 x 200 screens on the
same monitor (the 64's is narrower).  Not to mention that the Amiga can
make three screens that are all 320 x 200, but have different aspect
ratios.

>It seems to me that a workable solution for GIF files would be to
>standardize on Brian's suggestion: let the screen dimensions in the
>file header represent a pixel area that fills a 4x3 physical area.
>A GIF reader could then rescale the image appropriately for its own
>screen.
>
>This is something of a hack, but it has several nice properties:
>	1. The file format doesn't change.
>	2. Existing GIF readers don't break (since they almost
>	   surely ignore the screen dimension info).
>	3. A large fraction of the GIF files out there have correct
>	   info in them already (namely, all the IBM-screen files).
>
>The main disadvantage is that readers using this convention would have
>to guard against computing a bogus aspect ratio when given a GIF file
>that doesn't adhere to the convention.  One easy defense is to reject
>any computed ratio that falls outside the expected range (probably
>1.0 to 1.4 would cover it).  A command-line switch to override the
>aspect ratio adjustment would be a good idea too.

Nonsense.  I make lots of images for display on my Amiga that have
pixels nearly twice as wide as they are tall.  Atari 8 bits have modes
where the pixels are far wider than that.  A PostScript printer can
print a picture of >any< size with >any< pixel aspect ratio.

This "solution" is almost as narrow minded and silly as GIF is to begin
with.  That's by no means an attack on any of the people in this
discussion, only on the idea that you can somehow determine the aspect
ratio of an unknown image.  It just can't be done.

The only real solution is to tell the aspect ratio of the image when
you post it or whatever.  If somebody is going to send me a GIF, I
always tell them to send the aspect ratio along with it, or forget it.

Archives typically have a file that shows how many colors each file has,
why not another column for pixel aspect ratio:

clown.gif	657 x 395 x 8 (1:1)
shuttle.gif	657 x 395 x 8 (12:7)
zebra.gif	657 x 395 x 8 (4:13)

That way nobody has to guess.  If you guessed from just the dimensions
of those images, you would have been wrong at least twice, right?

>Any comments?

Yeah, in general, I'm more interested in the pixel aspect ratio (the
shape of the pixels) than in the image aspect ratio.  Either can be
computed from the other for a known image size, but I prefer the pixel
dimensions.

Why?  Because on many displays, the image size is variable, but the
pixel aspect ratio is not.  Again, on my Amiga I'm free to make an
screen of nearly any size, but I only have three (four really)
different kinds of pixels.  Posting the image aspect ratio with each of
my images would be very confusing as it varies so much, but the pixel
aspect ratio stays the same.

I often move images from one of my machines to another.  I use the
pixel aspect ratios to make any necessary corrections.  That way, the
images don't get distorted any more than they need to.  If I didn't
have the aspect ratio of the image documented, or didn't know the
aspect ratio of the target device, I could not do this.

Assumption of aspect ratio is an evil thing.  If you find you do it
often, you should probably consider a different image storage format.
Try IFF ILBM's, coding it up as a PostScript program, or even TIFF.

I don't like the complexities of TIFF, but at least I >can< decode a
TIFF.  No such luck with a GIF.  (Though it is easy to get the
information that is encoded in a GIF, it's impossible to get what's
just not there.)

GIF's a toy, and a dangerous one.  If you want to play with it, use a
warning sticker that contains the (pixel, or at least image) aspect
ratio.  That way, people won't have to slaughter your images.

---
Allen Braunsdorf			Purdue University Computing Center
cc.purdue.edu!ahg			UNIX Systems Programmer

rennie@cs.albany.edu (William A Rennie) (07/17/90)

In article <12198@mentor.cc.purdue.edu> ahg@mentor.cc.purdue.edu (Allen Braunsdorf) writes:


   >It seems to me that a workable solution for GIF files would be to
   >standardize on Brian's suggestion: let the screen dimensions in the
   >file header represent a pixel area that fills a 4x3 physical area.
   >A GIF reader could then rescale the image appropriately for its own
   >screen.
   >
	The GIF standard written by Compuserve has hooks available for
expansion.  Even better, the standard assumes that all existing viewers 
will ignore the expansion information.  (As I recall they have a header code
that is supposed to mean ignore me to current viewers.)  The pixel aspect
ratio problem is a major one.  Perhaps pressure on Compuserve would force a
usable expansion.

Bill Rennie
rennie@cssun.albany.edu

tgl@zog.cs.cmu.edu (Tom Lane) (07/17/90)

In article <12198@mentor.cc.purdue.edu>, ahg@mentor.cc.purdue.edu
(Allen Braunsdorf) writes (in response to a previous post of mine):
> >It seems to me that a workable solution for GIF files would be to
> >standardize on Brian's suggestion: let the screen dimensions in the
> >file header represent a pixel area that fills a 4x3 physical area.
> >A GIF reader could then rescale the image appropriately for its own
> >screen.
>
> This "solution" is almost as narrow minded and silly as GIF is to begin
> with.  That's by no means an attack on any of the people in this
> discussion, only on the idea that you can somehow determine the aspect
> ratio of an unknown image.  It just can't be done.

How about not using "fighting words" when you don't understand the proposal?

What I suggested was that the original creator of a GIF image document the
image's pixel aspect ratio by redefining the meaning of a currently useless
pair of numbers.  It should be obvious that there is no way to regenerate
this info (except by "eyeball adjustment") at any later date.

> Watch my lips:  Putting an image into GIF without somehow documenting
> the aspect ratio destroys the image.  The originally intended image
> cannot be recovered with certainty.

Exactly.  What I'm proposing is a way to include that number without
breaking the existing file format.

> >The main disadvantage is that readers using this convention would have
> >to guard against computing a bogus aspect ratio when given a GIF file
> >that doesn't adhere to the convention.  One easy defense is to reject
> >any computed ratio that falls outside the expected range (probably
> >1.0 to 1.4 would cover it).
> 
> Nonsense.  I make lots of images for display on my Amiga that have
> pixels nearly twice as wide as they are tall.  Atari 8 bits have modes
> where the pixels are far wider than that.

OK, I wasn't aware of that.  That means the range-check couldn't be as
tight as I suggested, but it doesn't render the proposal unworkable.

It would be easy to extend my proposal so that image creators could
unambiguously indicate that they were supplying aspect ratio data per the
new interpretation:  just put small numbers, say less than 64, into the
"screen dimensions" fields.  A reader seeing "4x3" could be certain that it
was computing a valid aspect ratio correction, whereas if it saw "320x200"
it might want to confirm the correction with its user.

> Assumption of aspect ratio is an evil thing.  If you find you do it
> often, you should probably consider a different image storage format.
> Try IFF ILBM's, coding it up as a PostScript program, or even TIFF.

I agree, but GIF is sufficiently popular and storage-efficient that
it's not likely to go away just because I don't like it.  Better to
offer a constructive and reasonably painless proposal for fixing it.

-- 
				tom lane
Internet: tgl@cs.cmu.edu
UUCP: <your favorite internet/arpanet gateway>!cs.cmu.edu!tgl
BITNET: tgl%cs.cmu.edu@cmuccvma
CompuServe: >internet:tgl@cs.cmu.edu

jindak@surfside.esd.sgi.com (Chris Schoeneman) (07/17/90)

In article <12198@mentor.cc.purdue.edu>, ahg@mentor.cc.purdue.edu
(Allen Braunsdorf) writes:
>Assumption of aspect ratio is an evil thing.

And in article <9922@pt.cs.cmu.edu> tgl@zog.cs.cmu.edu (Tom Lane)
writes:
>I agree, but GIF is sufficiently popular and storage-efficient that
>it's not likely to go away just because I don't like it.  Better to
>offer a constructive and reasonably painless proposal for fixing it.

I agree, too.  But instead of messing with the format (at least the
format's meaning), why not extend GIF within it's framework?  GIF
allows for "extension blocks" which all readers must accept.  So I
propose the following extension:

   7 6 5 4 3 2 1 0   Byte #
  +---------------+
  |0 0 1 0 0 0 0 1|    1        '!' - GIF extension block introducer
  +---------------+
  |0 1 0 1 0 0 1 0|    2        'R' - For 'aspect Ratio'
  +---------------+
  |0 0 0 0 0 0 1 0|    3         2  - Two bytes in block
  +---------------+
  |  pixel width  |    4            - First part of ratio (numerator)
  +---------------+
  | pixel height  |    5            - Second part of ratio (denominator)
  +---------------+
  |0 0 0 0 0 0 0 0|    6         0  - extension block end code
  +---------------+

Let byte four equal 'x' and byte five equal 'y'  Then x:y is the _pixel_
aspect ratio.  'x' and 'y' should be relatively prime (ie they should
have no common divisor except one), but they don't have to be.

A 640x480 image for display on a VGA graphics system (IBM), would have
a 1:1 ratio, so bytes four and five would both equal one.  Note that
the _image_ aspect ratio is 4:3, but the pixels are square.

A more extreme example would be an image designed for display on an
TRS-80.  It would have a ratio of 5:12, since twelve pixels are as wide
as 5 pixels are high.

Dividing the image's pixel aspect ratio by your machine's pixel aspect
ratio gives the number of pixels across per pixel down that need to be
drawn to produce the image correctly.  For example, let's assume a
machine with a pixel aspect ratio of 1:2 (twice as high as wide).
For an image with a 1:1 pixel aspect ratio:
  1
  -
  1     1   2   2
 --- =  - x - = -    So we need two pixels across for each one down.
  1     1   1   1
  -
  2

For an image with a 4:3 pixel aspect ratio:
  4
  -
  3     4   2   8
 --- =  - x - = -    We need eight pixels across for three down.
  1     3   1   3    (Or 2.667 pixels across per one down.)
  -
  2

The latter example points out a problem.  Should the decoder draw the
image with each dot made with 8x3 pixels?  This is entirely up to the
decoder.  It could instead draw horizontally three pixels, three
pixels, two pixels for three points in the image.  (On the next line it
might try 3,2,3, then 2,3,3, etc.)  The point is however the image is
drawn, the aspect ratio is correct.

Comments or suggestions?

-Chris Schoeneman
	       Chris Schoeneman | I was neat, clean, shaved and sober,
    jindak@surfside.esd.sgi.com | and I didn't care who knew it.
	 Silicon Graphics, Inc. |		-Raymond Chandler
	      Mountain View, CA |		 (The Big Sleep)

ahg@mentor.cc.purdue.edu (Allen Braunsdorf) (07/18/90)

In article <9922@pt.cs.cmu.edu> tgl@zog.cs.cmu.edu (Tom Lane) writes:
>In article <12198@mentor.cc.purdue.edu>, ahg@mentor.cc.purdue.edu
>(Allen Braunsdorf) writes (in response to a previous post of mine):
>
>How about not using "fighting words" when you don't understand the proposal?
>
>What I suggested was that the original creator of a GIF image document the
>image's pixel aspect ratio by redefining the meaning of a currently useless
>pair of numbers.  It should be obvious that there is no way to regenerate
>this info (except by "eyeball adjustment") at any later date.
>
>> Watch my lips:  Putting an image into GIF without somehow documenting
>> the aspect ratio destroys the image.  The originally intended image
>> cannot be recovered with certainty.
>
>Exactly.  What I'm proposing is a way to include that number without
>breaking the existing file format.

But it does break.  The screen size is in there so that multiple images
stored in one file have a place to go.  My GIF decoder makes a frame as
big as the screen and then renders each image into this frame.  And
it's not just me- that's the right thing to do.  If you record a bogus
screen size, you could really break things.  Even if it didn't break,
the picture might have a really funny border around it.

Multiple images in one file allow you to make a picture with many
colors by breaking it up into little tiles.  That way I could get more
than 256 colors in a picture if I wanted.  It's standard and it's
useful.  It's especially useful for broadcasting several little
"window" pictures to a terminal.  (I'm sure that was the original
idea.)

Since I don't use GIF locally here, my tool (giftorgb) creates three
files (foo.[rgb]) that are just raw byte dumps of the frame.  From
that, I can process the image any way I need and then recode it into
whatever format later.

>I agree, but GIF is sufficiently popular and storage-efficient that
>it's not likely to go away just because I don't like it.  Better to
>offer a constructive and reasonably painless proposal for fixing it.

Another poster proposed adding a new '!' block to the format.  That's
the right way, and a good idea.  My comments about that are under
separate cover.

---
Allen Braunsdorf			Purdue University Computing Center
cc.purdue.edu!ahg			UNIX Systems Programmer

ahg@mentor.cc.purdue.edu (Allen Braunsdorf) (07/18/90)

In article <10454@odin.corp.sgi.com> jindak@surfside.esd.sgi.com (Chris Schoeneman) writes:
>In article <12198@mentor.cc.purdue.edu>, ahg@mentor.cc.purdue.edu
>(Allen Braunsdorf) writes:
>>Assumption of aspect ratio is an evil thing.
>And in article <9922@pt.cs.cmu.edu> tgl@zog.cs.cmu.edu (Tom Lane)
>writes:
>>I agree [but GIF is popular and efficient].
>I agree, too.  But instead of messing with the format (at least the
>format's meaning), why not extend GIF within it's framework?  GIF
>allows for "extension blocks" which all readers must accept.  So I
>propose the following extension:
>
>   7 6 5 4 3 2 1 0   Byte #
>  +---------------+
>  |0 0 1 0 0 0 0 1|    1        '!' - GIF extension block introducer
>  +---------------+
>  |0 1 0 1 0 0 1 0|    2        'R' - For 'aspect Ratio'
>  +---------------+
>  |0 0 0 0 0 0 1 0|    3         2  - Two bytes in block
>  +---------------+
>  |  pixel width  |    4            - First part of ratio (numerator)
>  +---------------+
>  | pixel height  |    5            - Second part of ratio (denominator)
>  +---------------+
>  |0 0 0 0 0 0 0 0|    6         0  - extension block end code
>  +---------------+
>
>Let byte four equal 'x' and byte five equal 'y'  Then x:y is the _pixel_
>aspect ratio.  'x' and 'y' should be relatively prime (ie they should
>have no common divisor except one), but they don't have to be.

That's the right way, and I like it.  One problem: only one of these
should be allowed per file.  That is, it should describe the screen's
pixels and all the images in the file need to comply to it.  Otherwise,
it's a lot messier to decode.  I dump the screen into files and then
aspect ratio correct the whole thing.  If the aspect ratio could change
between images, I'd have to really beef up my GIF decoder.

It seems to me that the whole screen should have the same kind of pixel
anyway, so this limitation is in the spirit of the original idea (of
several images as part of a larger graphic screen on a single device).

How hard is it to amend the GIF definition?  Didn't they do it a while
back to allow Amiga HAM images?

>The latter example points out a problem.  Should the decoder draw the
>image with each dot made with 8x3 pixels?  This is entirely up to the
>decoder.  It could instead draw horizontally three pixels, three
>pixels, two pixels for three points in the image.  (On the next line it
>might try 3,2,3, then 2,3,3, etc.)  The point is however the image is
>drawn, the aspect ratio is correct.

If coloration is not a problem, they should probably filter the rows to
fit their display.  When I convert GIFs to 24 bit color images, I can
do this easily, but for a 256 color display it could be difficult.  Of
course, users with less than a 256 color display have to recolor the
image anyway, so this is probably less additional trouble there.

Some of the programs I've seen don't handle some pictures very well.
Usually it's because they either don't recolor or resize the pictures
nicely.  Do you suppose that if these programs were enhanced to use
accurate aspect ratio information they might improve in other ways as a
side effect?

>Comments or suggestions?

This extension block should come before the first image in the file and
(as I said above) should apply to the whole screen.

Has anybody else proposed this?  It seems that someone in then PC world
(with its many graphic modes with different aspect ratios) would have
demanded this a long time ago.

PC GIF decoders often make you pick the graphic mode to use for
display.  If they had this, they could use the aspect ratio and number
of colors to get a really close mode match (if it came from a machine
with the same graphic modes, it would be exact), or process the image
to make it fit whatever mode you wanted.

It sounds like a good idea to me.  If this were official, I'd probably
start using GIF for some things.

---
Allen Braunsdorf			Purdue University Computing Center
cc.purdue.edu!ahg			UNIX Systems Programmer

tgl@zog.cs.cmu.edu (Tom Lane) (07/18/90)

In article <10454@odin.corp.sgi.com>, jindak@surfside.esd.sgi.com (Chris
Schoeneman) writes:
> ... instead of messing with the format (at least the
> format's meaning), why not extend GIF within it's framework?  GIF
> allows for "extension blocks" which all readers must accept.  So I
> propose the following extension:
> 
> [ straightforward extension block defining assumed pixel aspect ratio ]

That's the obvious solution, and certainly the logically cleanest one.
Why did I propose hacking up the screen size values instead?  Because
*not all GIF readers handle extension blocks*.  In particular, the
two that I have here (Patrick Naughton's gif2ras and xgif) choke on
any extension block.  These particular two seem to be descended from
a common ancestor, which may well have other progeny.  Sure, these
readers violate the published standard; but they are pretty widely used.

Meanwhile, in article <12234@mentor.cc.purdue.edu> ahg@mentor.cc.purdue.edu
(Allen Braunsdorf) writes:
>The screen size is in there so that multiple images
>stored in one file have a place to go.  My GIF decoder makes a frame as
>big as the screen and then renders each image into this frame.

[I assume Allen means "makes a frame as big as the logical screen size
indicated in the file header", not "as big as the display the decoder
is using".  I could argue that it wouldn't be difficult to handle
multiple images this way without using the file's screen size, but
that's really not the point here.]

So what we've got are two different proposals, each of which breaks
a different subset of the GIF readers currently out there.
On aesthetic grounds the extension-block approach is certainly
superior, but I'm not at all sure that it is superior on backwards
compatibility grounds.  It would be nice to get some hard data on the
following points:
	1. How many existing GIF readers pay any attention to the
	   screen size numbers (as opposed to the image size)?
	2. How many existing GIF readers correctly skip over
	   unrecognized extension blocks?
I don't have any easy way of finding out either of these things.
Perhaps someone on the net knows the answers.

Don't get me wrong --- I'd be glad to see Chris's proposal (or *any*
solution) adopted.  I just think that a high degree of backwards
compatibility is going to be necessary to make the thing fly.
I suspect that redefining the screen size fields would create fewer
compatibility problems than adding an extension block.

-- 
				tom lane
Internet: tgl@cs.cmu.edu
UUCP: <your favorite internet/arpanet gateway>!cs.cmu.edu!tgl
BITNET: tgl%cs.cmu.edu@cmuccvma
CompuServe: >internet:tgl@cs.cmu.edu

jef@well.sf.ca.us (Jef Poskanzer) (07/18/90)

In the referenced message, jindak@surfside.esd.sgi.com (Chris Schoeneman) wrote:
}GIF allows for "extension blocks" which all readers must accept.  So I
}propose the following extension:
}
}   7 6 5 4 3 2 1 0   Byte #
}  +---------------+
}  |0 0 1 0 0 0 0 1|    1        '!' - GIF extension block introducer
}  +---------------+
}  |0 1 0 1 0 0 1 0|    2        'R' - For 'aspect Ratio'
}  +---------------+
}  |0 0 0 0 0 0 1 0|    3         2  - Two bytes in block
}  +---------------+
}  |  pixel width  |    4            - First part of ratio (numerator)
}  +---------------+
}  | pixel height  |    5            - Second part of ratio (denominator)
}  +---------------+
}  |0 0 0 0 0 0 0 0|    6         0  - extension block end code
}  +---------------+
}
}Let byte four equal 'x' and byte five equal 'y'  Then x:y is the _pixel_
}aspect ratio.

I like this.  I have appended a quick patch for giftoppm.c that should
(a) make it read and ignore extension blocks, and (b) make it handle
this new aspect ratio extension block by putting out a message advising
the user to do a ppmscale.  This is the same way that ilbmtoppm handles
non-square pixels.

I haven't tested this patch beyond compiling it, but it's pretty simple.
A very similar change should work for any other GIF readers derived from
Patrick Naughton's gif2ras.

If anyone modifies a GIF writer to generate these extension blocks,
drop me a line so we can check interoperability.
---
Jef

  Jef Poskanzer  jef@well.sf.ca.us  {ucbvax, apple, hplabs}!well!jef
  "I'll tell you right out, I'm a man who likes talking to a man who
         likes to talk." -- Kaspar Gutman [The Maltese Falcon]

*** giftoppm.c.old	Tue Jul 17 15:50:09 1990
--- giftoppm.c	Tue Jul 17 16:10:11 1990
***************
*** 65,70 ****
--- 65,72 ----
  #define False (0)
  
  #define NEXTBYTE (*ptr++)
+ #define EXTENSION 0x21
+ #define EXTENSION_ASPECTRATIO 0x52
  #define IMAGESEP 0x2c
  #define INTERLACEMASK 0x40
  #define COLORMAPMASK 0x80
***************
*** 245,253 ****
  	Red[1] = Green[1] = Blue[1] = 255;
      }
    
  /* Check for image seperator */
  
!     if (NEXTBYTE != IMAGESEP)
  	pm_error( "%s is a corrupt GIF file (nosep)", inf, 0,0,0,0 );
  
  /* Now read in values from the image descriptor */
--- 247,287 ----
  	Red[1] = Green[1] = Blue[1] = 255;
      }
    
+ /* Check for extension blocks */
+ 
+     for (;;) {
+ 	ch = NEXTBYTE;
+ 	if ( ch != EXTENSION )
+ 	    break;
+ 	ch = NEXTBYTE;
+ 	if (ch == EXTENSION_ASPECTRATIO) {
+ 	    int pwid, phei;
+ 	    if (NEXTBYTE != 2)
+ 		pm_error( "%s is a corrupt GIF file (bad aspectratio 1)", inf, 0,0,0,0 );
+ 	    pwid = NEXTBYTE;
+ 	    phei = NEXTBYTE;
+ 	    if (NEXTBYTE != 0)
+ 		pm_error( "%s is a corrupt GIF file (bad aspectratio 2)", inf, 0,0,0,0 );
+ 	    if (pwid != phei)
+ 		pm_message(
+ 		    "(Warning: non-square pixels; to fix do a 'ppmscale -%cscale %g'.)\n",
+ 		    pwid > phei ? 'x' : 'y',
+ 		    pwid > phei ? (float) pwid / phei : (float) phei / pwid, 0,0,0 );
+ 	} else {
+ 	    /* Unknown type of extension block - skip past it */
+ 	    for (;;) {
+ 		ch = NEXTBYTE;
+ 		if (ch == 0)
+ 		    break;
+ 		while (--ch >= 0)
+ 		    (void) NEXTBYTE;
+ 	    }
+ 	}
+     }
+ 
  /* Check for image seperator */
  
!     if (ch != IMAGESEP)
  	pm_error( "%s is a corrupt GIF file (nosep)", inf, 0,0,0,0 );
  
  /* Now read in values from the image descriptor */