
ILBM Files: V39 and AA Support Issues
=====================================
Carolyn Scheppner - Technical Manager - CATS U.S.

(c) Copyright 1992 Commodore-Amiga, Inc.  All Rights Reserved


For compatibility with and support of enhanced Amiga graphics
capabilities, both the short-term and long-term possibilities, you must
modify your software to remove any built-in limitations which would
prevent you from growing WITH the Amiga.


I. Get RID of Hardcoded Limits, Write Software that Adapts

Many of the Amiga graphics software packages currently on the market
are hardcoded like the old "DF0: DH0:" file requesters.

Such hardcoded graphics application software limits include:

- offering a fixed set of display modes or sizes
- offering a fixed range of depths or sizes for certain display modes
- loading or handling a maximum of 32 colors
- Dealing with color guns as 4-bit values


The first thing you need to think about when upgrading your application
for V39, is NOT to upgrade it for V39.  You must upgrade your software
so that it can adapt to arbitrary display modes, depths, and sizes.

If you offer different display modes, do not arbitrarily restrict
the modes that you offer.  If 8-bitplane hires, or hires HAM, are
supported by a new chip set, and your software restricts a user to
5-bitplane hires and lores HAM, then your software will be obsolete.

Rewrite your software to use features such as the 2.1 display mode
requester.  Make sure that your software can adapt to larger palettes.
Handle R G and B values internally as at least 8-bits, not 4.



II. Proper IFF ILBM Support

When loading, saving, and processing ILBM files, care must be taken
to properly support the current and future graphics capabiities of
the Amiga.  Hardcoded limits and assumptions often exist in ILBM
handling code.  The NewIFF39 code modules attempt to properly
implement all of the following concepts in a backward (and hopefully
forward) compatible manner.


A. Proper ILBM.CAMG Chunk
   ======================

1. Saving

If running under V36 or higher, save a 32-bit mode id in the CAMG ULONG.
Some of these modes have all zeros in the upper word and are classic
Amiga viewmodes.  Others are more complex but generally provide a
good bit pattern in the low word to allow reasonable results if displayed
with an old viewer.  You may wish to let your user decide if a different
mode id should be saved.  For example, a user may prefer to work in DBLNTSC
but need to save his images as plain HIRES LACE for genlocking.
See V39 graphics/modeid.h for current modes.
Use the 2.1 ASL screen mode requester or the display database if you wish
to offer only all modes available on the user's system.
Use ULONG modeid = GetVPModeID(viewport) if you are saving
the mode id of a display.

2. Loading

Support reading and using full 32-bit modeid's from CAMG, with some
screening for bad ID's, and fallback code if ModeNotAvailable().
Screening is required because there are some CAMG's out there with
garbage in the upper word.  See sample "getcamg" code at end.
Under V39 or higher, the new BestModeIDA() graphics function can
be used to query for a suitable and available replacement mode
when an ILBM CAMG mode is not available.  Stop looking at the
bits of graphics mode id's.  See the NewIFF39 code modules for example
usage of BestModeIDA().



B. Proper ILBM.BMHD X and Y aspect
   ===============================

1. Saving

Use the display database in a simple manner to get the correct
x and y aspect values for the ILBM.BMHD.
See the "getaspect" code below.  This code gets the correct
aspect ratio for any viewport modeid from the display
database.  If running under < V36, it falls back to
updated 2.0-compatible aspect values for old modes.

2. Loading

Perhaps you can start to expect reasonable information
in the ILBM.BMHD x and y aspect fields.



C. Proper 8-bit-per-gun ILBM.CMAP
   ==============================

Problem: the CMAPs of many ILBMs contain only 4-bits-per-gun of of R,
G, and B, each left justified in a CMAP byte - for example, white saved
as F0 F0 F0 rather than FF FF FF.  This made no difference when Amigas
could only display 4-bits-per-gun of color (i.e. 4096 different colors)
since only the upper 4 bits of each CMAP byte were used when setting
colors, and for example, RGB of $F F F, whether stored as F0 F0 F0
or FF FF FF, was 4-bit white.
But AA Amigas can display 8-bits-per-gun of color (16,000,000 different
colors), and F0 F0 F0 is not quite white - FF FF FF is.
To properly display 8-bits of color from an ILBM whose CMAP contains
only 4-bits of color information per gun, you must know to scale the 4 bits
to 8 bits.  But in most ILBM's there is no flag to tell you
whether the CMAP contains 4 left-justified or 8 significant bits per gun.

Here are tips on saving and loading 8-bit-per-gun colors, and some
strategies and a new flag bit for recognizing and
dealing with both 4-bit and 8-bit CMAP colors.

1. Saving

Either always save CMAPs with 8 significant bits-per-gun, or
save 8-bits-per-gun by default and offer a 4-bit palette save option
(for compatibility with some products which may contain containing
broken handcrafted CMAP color handling code).  When saving
from a 4-bit-per-gun source, do NOT left justify each 4-bit gun in
the CMAP R, G, and B bytes, but rather SCALE each 4-bit value to 8 bits
by duplicating the 4-bit value in the upper and lower nibble of its
R, G, or B CMAP byte (e.g. save white as $FF FF FF, etc.).  When saving
under V39 or higher, if you must extract the colors from a display, use
the new 32-bit graphics color getting function GetRGB32().  Do not read
a ColorMap's ColorTable directly.  Save the 8 most significant bits of
each gun in the CMAP.

IMPORTANT:  A new advisory BMHD flag, BMHDF_CMAPOK, has been
defined to indicate that an ILBM contains an 8 bit significant
CMAP, not an old 4-bit left-justified CMAP.  The advisory flag is
the high bit (1 << 7) of the BMHD.Flags byte (formerly named BMHD.Pad1 or
BMHD.Reserved1).  Whenever you save an 8-bit-significant CMAP
(either 4 bits scaled to 8 bits or true 8 bits), we suggest that you set
this flag bit in your BMHD.Flags to advise aware loaders
that your CMAP values are definitely not 4-bit shifted values
and do not need scaling to 8 bits.

#define BMHDB_CMAPOK    7
#define BMHDF_CMAPOK    (1 << BMHDB_CMAPOK)


2. Loading

Detect and use all 8 bits of CMAP color, if provided, when running
under V39 or higher machine.  If you see the new BMHDF_CMAPOK flag
set in BMHD.Flags (described above), then you can be sure that
the CMAP already contains 8 significant bits per gun of color.
If you are running under V39 or higher, scale each 8-bit gun value
to 32-bits (by duplicating it in all 4 bytes of a ULONG), and load
the colors using one of the V39 32-bit color loading functions
(LoadRGB32, SetRGB32, SetRGB32CM).

If the BMHDF_CMAPOK bit is not set in the BMHD, then to display the
CMAP colors correctly on an AA system, you need to determine if the
stored CMAP color gun values are shifted 4-bit or full 8-bit values.
If you do not examine the CMAP and instead just assume that it has 8
significant bits, you will display the colors of many ILBMs incorrectly
dim, while older LoadRGB4() loaders will actually display the correct
colors (because the V39 4-bit color loading functions will scale the
passed 4-bit values properly to 32-bits).

You can try to determine if the CMAP contains all 4-bit left-justified
RGB values by examining the low nibble of every CMAP entry you intend to use
(do not examine additional registers or garbage which may be stored
in the CMAP).  
If every examined low nibble is 0, then you would probably be correct to
assume that the CMAP contains 4-bit left-justified values.  In this case,
you can correctly scale these values to 32-bits by first scaling to
8 bits (i.e. duplicate the upper nibble of each gun into its low nibble),
then scaling to 32-bits (as described above).  If any of the examined CMAP
nibbles is non-zero, then the 8-bit CMAP values are directly scaled to 32-bits.
Then load the colors using one of the V39 32-bit color loading functions.

When loading on a pre-V39 machine, just use the upper (most significant)
nibble of each 8-bit CMAP gun value when calling the old 4-bit-per-gun
pre-V39 graphics functions (LoadRGB4, SetRGB4, SetRGB4CM).
Be very careful to AND out the the low nibble of each gun before shifting
and OR'ing R, G, and B guns to create an xRGB word for pre-V39 functions.


D.  Stop Limiting Color Register Load Counts to 32
    ==============================================

Older IFF code, and even the early V37 NewIFF code would
read any number of colors from an ILBM CMAP, but would
only set a maximum 32 colors in the display.  
Instead, the maximum number of colors set in the display
should be limited by the display Viewport's ColorMap->Count
rather than a hardcoded limit.  The current V37 and V39 NewIFF
have no fixed limit on number of color registers.


E.  Stop Limiting Depth to 5/6
    ==========================

Older IFF code had fixed limits for the maximum allowable
depth for displays and ILBMs.  Remove your limits.
Display as much as the system can handle.  Don't reject
depths and depth/mode combinations arbitrarily.
Also, you may want to stop assuming that a 6-plane ILBM
with no CAMG is HAM or HALFBRITE (although that might still
be a good assumption since only a pretty lame program would
write a HAM or HALFBRITE ILBM with no CAMG chunk).


F. Watch out for BitMap->BytesPerRow
   =================================
We have seen a number of products which have problems with
BitMap->BytesPerRow rounding changes under V39 with AA.

BitMap->BytesPerRow is a modulo - it is the number that must be added to the
address of a bitmap byte to get to the same byte one scan line down.
Prior to V39, the value of BitMap->BytesPerRow always happened to be
the width of a bitmap scan line rounded up to the nearest multiple of 16,
then divided by 8.  And all BitMaps, whether allocated via AllocRaster,
or AllocMem using the RASSIZE macro, or via OpenScreen, had this
same BytesPerRow rounding.  This rounding aligned every scanline on a
word boundary to allow fetching by the word-oriented custom chips.  

In addition, the IFF ILBM spec states that the scan lines of an ILBM BODY
must be saved as width rounded up to the nearest multiple of 16 pixels
(i.e. as an even number of bytes width).
So it is not surprising that assumptions about BitMap->ByesPerRow
crept into ILBM handling code.

ILBM BODY scan lines must still be stored as pixel width rounded up
to a multiple of 16.  But under V39 and AA, the higher bandwidth
required to support new graphics modes requires that the scan lines
of a displayable BitMap be aligned on larger boundaries.  Therefore
under V39 and AA, the BytesPerRow of a BitMap must not be confused
with the correct storage width of an ILBM.

In addition, graphic applications must be very careful not to assume
that all BitMaps of the same width will have the same BytesPerRow.
From V39 onwards, BitMaps allocated by different methods
(e.g. OpenScreen, AllocRaster, AllocMem, AllocBitMap),
or allocated for display by different chipsets (ECS, AA, etc.)
or for different display modes or a promoted display mode, may have
different scanline alignment restrictions and therefore a different
BytesPerRow.  Do not assume that bitplanes allocated by different methods
can be swapped, or processor copied across scanline boundaries.

To test for BitMap->BytesPerRow problems, you generally must test
on an AA machine with either mode promotion or explicit use of higher
bandwidth (greater alignment restriction) modes.

Common symptoms of BitMap->BytesPerRow problems are 1. a skewing of
the display, where the pixels or each successive scan line all appear
shifted to either the left or right, creating a diagonal effect, or
2. excess blank space at the right edge of an ILBM or a display.


G. Watch out for interleaved bitmaps
   =================================

If your application supports capturing any screen, you
must watch out for the new interleaved bitmaps.
An interleaved BitMap's BytesPerRow field is still the
modulo for getting from any one pixel to the pixel directly
below it, BUT it is no longer even related to the rounded up
width of the screen or viewport.  Instead, it is a MUCH
larger value which is actually the rounded up BitMap scan
line width TIMES the depth.  Do not assume that BytesPerRow
is related to the width of the display.

NOTE: The 2.0 Native Developer Update release of the NewIFF
code had 2 major bugs.  The screen.c module had a 1.3
incompatibility, and the ilbmr.c module could not properly
save an interleaved bitmap (such as the V39 Workbench screen).
See the newer version 37.10 of the NewIFF code.  This has
been placed in our listings area on BIX, and sent to ADSP, and
sent to Fred Fish.  For full AA color support, see the NewIFF39
code (available to developers via BIX, ADSP, CIX and Devcon disks,
and planned to be provided on the 3.0 Native Developer Update
and given to Fred Fish).


Under V39, an interleaved bitmap can be detected by:

if(GetBitMapAttr(bitmap_ptr,BMA_FLAGS) & BMF_INTERLEAVED)
printf("is interleaved\n");


H. Proper Printing of new Display Modes
   ====================================

When dumping a rastport to printer under V36 and higher,
the following IORequest field must contain a 32-bit modeid
such as that returned by GetVPModeID(viewport).  You
may want to allow the user the ability to print a display
with a different modeid than it is being displayed in.
Passing the full modeid allows the printer.device to
properly control the aspect of the output, as long as the
mode is available.

   ULONG   io_Modes;               /* graphics viewport modes */


 
-------------------------- getcamg -------------------------------
From: /* ilbmr.c --- ILBM loading routines for use with iffparse */

/*
 * Returns CAMG or computed mode for storage in ilbm->camg
 *
 * ilbm->Bmhd structure must be initialized prior to this call.
 */
ULONG getcamg(struct ILBMInfo *ilbm)
{
struct IFFHandle *iff;
struct StoredProperty *sp;
UWORD  wide,high,deep;
ULONG modeid = 0L;

    if(!(iff=ilbm->ParseInfo.iff))return(0L);

wide = ilbm->Bmhd.pageWidth;
high = ilbm->Bmhd.pageHeight;
deep = ilbm->Bmhd.nPlanes;

D(bug("Getting CAMG for w=%ld h=%ld d=%ld ILBM\n",wide,high,deep));

        /*
         * Grab CAMG's idea of the viewmodes.
         */
        if (sp = FindProp (iff, ID_ILBM, ID_CAMG))
                {
                modeid = (* (ULONG *) sp->sp_Data);

                /* knock bad bits out of old-style 16-bit viewmode CAMGs
                 */
                if((!(modeid & MONITOR_ID_MASK))||
  ((modeid & EXTENDED_MODE)&&(!(modeid & 0xFFFF0000))))
                   modeid &= 
    (~(EXTENDED_MODE|SPRITES|GENLOCK_AUDIO|GENLOCK_VIDEO|VP_HIDE));

                /* check for bogus CAMG like DPaintII brushes
                 * with junk in upper word and extended bit
                 * not set in lower word.
                 */
                if((modeid & 0xFFFF0000)&&(!(modeid & 0x00001000))) sp=NULL;
                }

        if(!sp) {
                /*
                 * No CAMG (or bad CAMG) present; use computed modes.
                 */
		modeid = 0L;
                if (wide >= 640)        modeid = HIRES;
                if (high >= 400)        modeid |= LACE;

/* This 6 planes == HAM or HALFBRITE is not
 * necessarily true anymore, but hopefully
 * all NEW programs are writing a proper CAMG chunk!!
 */
                if (deep == 6)
                        {
                        modeid |= ilbm->EHB ? EXTRA_HALFBRITE : HAM;
                        }

D(bug("No CAMG found - using mode $%08lx\n",modeid));
                }

D(bug("getcamg: modeid = $%08lx\n",modeid));
return(modeid);
}


-------------------------- getaspect -------------------------------

    bmhd->xAspect = 0;  /* So we can tell when we've got it */
    if(GfxBase->lib_Version >=36)
        {
        if(GetDisplayInfoData(NULL, (UBYTE *)&DI,
                sizeof(struct DisplayInfo), DTAG_DISP, modeid))
                {
                bmhd->xAspect =  DI.Resolution.x;
                bmhd->yAspect =  DI.Resolution.y;
                }
        }

    /* If running under 1.3 or GetDisplayInfoData failed, use old method
     * of guessing aspect ratio
     */
    if(! bmhd->xAspect)
        {
        bmhd->xAspect =  44;
        bmhd->yAspect =
                ((struct GfxBase *)GfxBase)->DisplayFlags & PAL ? 44 : 52;
        if(modeid & HIRES)      bmhd->xAspect = bmhd->xAspect >> 1;
        if(modeid & LACE)       bmhd->yAspect = bmhd->yAspect >> 1;
        }

