261 lines
7.0 KiB
C
261 lines
7.0 KiB
C
/**
|
|
* Displaying an uncompressed IFF ILBM image
|
|
* straight from RAM.
|
|
*
|
|
* Since ILBM images are interleaved bitmaps, if
|
|
* your display mode is set to be the same size as the
|
|
* image, you can treat that raw image data as the
|
|
* bitmap source for Agnus.
|
|
*
|
|
* Copyright 2024 John Bintz. Licensed under the MIT
|
|
* License. Visit https://theindustriousrabbit.com
|
|
* for more!
|
|
*/
|
|
|
|
#include <clib/dos_protos.h>
|
|
#include <clib/exec_protos.h>
|
|
#include <clib/intuition_protos.h>
|
|
#include <clib/graphics_protos.h>
|
|
#include <exec/memory.h>
|
|
#include <graphics/GfxBase.h>
|
|
#include <stdio.h>
|
|
#include <hardware/cia.h>
|
|
#include <hardware/dmabits.h>
|
|
|
|
#define BPLCON0_COLOR (0x200)
|
|
|
|
// We only need four fields from the image header
|
|
typedef struct BMHDHeader {
|
|
UWORD width;
|
|
UWORD height;
|
|
UWORD _a1;
|
|
UWORD _a2;
|
|
UBYTE planes;
|
|
UBYTE _a3;
|
|
UBYTE compression;
|
|
UBYTE _a4;
|
|
UWORD _a5;
|
|
UBYTE _a6;
|
|
UBYTE _a7;
|
|
UWORD _a8;
|
|
UWORD _a9;
|
|
};
|
|
|
|
// Make it very easy to load all color info at
|
|
// once.
|
|
typedef struct ILBMColor {
|
|
UBYTE red;
|
|
UBYTE green;
|
|
UBYTE blue;
|
|
};
|
|
|
|
extern struct GfBase *GfxBase;
|
|
extern struct Custom far custom;
|
|
extern struct CIA far ciaa,ciab;
|
|
|
|
// The code below follows the same pattern as the
|
|
// PC DOS IFF ILBM reader.
|
|
UBYTE charBuffer[8];
|
|
ULONG chunkLength;
|
|
|
|
struct ILBMColor palette[32];
|
|
struct BMHDHeader bmhdHeader;
|
|
|
|
void readChunk(BPTR fh) {
|
|
FRead(fh, &charBuffer, 8, 1);
|
|
chunkLength = *(ULONG *)(charBuffer + 4);
|
|
}
|
|
|
|
BYTE checkChunkType(char type[]) {
|
|
BYTE i;
|
|
for (i = 0; i < 4; ++i) {
|
|
if (type[i] != charBuffer[i]) {
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int main(void) {
|
|
int color, plane, imageModulo;
|
|
BYTE keepReading = 1;
|
|
void *imageData, *copperlist;
|
|
UWORD *copperlist_ptr;
|
|
|
|
struct View *OldView = ((struct GfxBase *)GfxBase)->ActiView;
|
|
ULONG OldCopper = (ULONG)((struct GfxBase *)GfxBase)->copinit;
|
|
ULONG OldDMACON, OldINTENA, OldINTREQ, OldADKCON;
|
|
|
|
// this is a 320x200x32 uncompressed IFF ILBM image.
|
|
BPTR fh = Open("chicken art 32 uncompressed.iff", MODE_OLDFILE);
|
|
|
|
readChunk(fh);
|
|
|
|
if (!checkChunkType("FORM")) {
|
|
printf("Not IFF\n");
|
|
Close(fh);
|
|
return 1;
|
|
}
|
|
|
|
FRead(fh, &charBuffer, 4, 1);
|
|
if (!checkChunkType("ILBM")) {
|
|
printf("Not ILBM\n");
|
|
Close(fh);
|
|
return 1;
|
|
}
|
|
|
|
while (keepReading) {
|
|
keepReading = 0;
|
|
|
|
readChunk(fh);
|
|
printf("Chunk length: %d\n", chunkLength);
|
|
if (checkChunkType("BMHD")) {
|
|
FRead(fh, &bmhdHeader, sizeof(struct BMHDHeader), 1);
|
|
printf("Width: %d\n", bmhdHeader.width);
|
|
printf("Height: %d\n", bmhdHeader.height);
|
|
printf("Bitplanes: %d\n", bmhdHeader.planes);
|
|
printf("Compression: %d\n", bmhdHeader.compression);
|
|
|
|
if (bmhdHeader.compression == 1) {
|
|
printf("Can't read compressed files\n");
|
|
Close(fh);
|
|
return 1;
|
|
}
|
|
|
|
keepReading = 1;
|
|
}
|
|
|
|
if (checkChunkType("CMAP")) {
|
|
printf("Colors: %d\n", 1 << bmhdHeader.planes);
|
|
FRead(fh, &palette, sizeof(struct ILBMColor), 1 << bmhdHeader.planes);
|
|
|
|
for (color = 0; color < (1 << bmhdHeader.planes); ++color) {
|
|
printf("Color %d: %d %d %d\n", color, palette[color].red, palette[color].green, palette[color].blue);
|
|
}
|
|
keepReading = 1;
|
|
}
|
|
|
|
if (checkChunkType("BODY")) {
|
|
printf("Made it to BODY\n");
|
|
|
|
// read raw image data into Chip RAM for Agnus
|
|
imageData = AllocMem(chunkLength, MEMF_CHIP | MEMF_CLEAR);
|
|
FRead(fh, imageData, chunkLength, 1);
|
|
|
|
// The modulo is what makes this work. While Agnus is
|
|
// reading data from the bitplane pointers, those pointer
|
|
// values are increasing (See "Amiga Rasterbars are Cool"
|
|
// to observe this in action). At the end of the display
|
|
// line, Agnus will then add the modulo vaule to the
|
|
// bitplane pointer. This allows you to interleave image
|
|
// data, row by row, in memory, rather than having each
|
|
// bitplane be a separate chunk of memory.
|
|
imageModulo = (bmhdHeader.width + 8 - 1) / 8;
|
|
printf("Modulo: %d\n", imageModulo);
|
|
|
|
OldDMACON = custom.dmaconr | 0x8000;
|
|
OldINTENA = custom.intenar | 0x8000;
|
|
OldINTREQ = custom.intreqr | 0x8000;
|
|
OldADKCON = custom.adkconr | 0x8000;
|
|
|
|
// disable interrupts
|
|
custom.intena = 0xc000;
|
|
custom.intena = 0x3fff;
|
|
|
|
// set up dma
|
|
custom.dmacon = DMAF_SETCLR | DMAF_COPPER | DMAF_RASTER;
|
|
custom.dmacon = DMAF_AUDIO | DMAF_DISK | DMAF_SPRITE | DMAF_BLITTER;
|
|
|
|
// take control
|
|
LoadView(NULL);
|
|
WaitTOF();
|
|
WaitTOF();
|
|
OwnBlitter();
|
|
WaitBlit();
|
|
Forbid();
|
|
|
|
// Activate color rendering for as many bitplanes in the file
|
|
custom.bplcon0 = BPLCON0_COLOR | (bmhdHeader.planes << 12);
|
|
custom.bplcon1 = 0;
|
|
|
|
// The modulo is being added to the pointer once a display line
|
|
// has been read. We need to skip (number of planes - 1) rows down
|
|
// to get to the start of the next line for the bitplane in question.
|
|
custom.bpl1mod = imageModulo * (bmhdHeader.planes - 1);
|
|
custom.bpl2mod = imageModulo * (bmhdHeader.planes - 1);
|
|
|
|
// 320x256 display
|
|
custom.diwstrt = 0x2c21;
|
|
custom.diwstop = 0x2cc1;
|
|
custom.ddfstrt = 0x0038;
|
|
custom.ddfstop = 0x00d0;
|
|
|
|
// create our copperlist
|
|
copperlist = AllocMem(2000, MEMF_CHIP | MEMF_CLEAR);
|
|
copperlist_ptr = (UWORD *)copperlist;
|
|
|
|
// bitplane pointers
|
|
for (plane = 0; plane < bmhdHeader.planes; ++plane) {
|
|
// We use the modulos here as well, to start each bitplane pointer on
|
|
// the correct row of data.
|
|
*(copperlist_ptr++) = 0x0E0 + (plane * 4);
|
|
*(copperlist_ptr++) = (((ULONG)imageData + plane * imageModulo) >> 16) & 0xffff;
|
|
|
|
*(copperlist_ptr++) = 0x0E2 + (plane * 4);
|
|
*(copperlist_ptr++) = (((ULONG)imageData + plane * imageModulo)) & 0xffff;
|
|
}
|
|
|
|
// palette
|
|
for (color = 0; color < (1 << bmhdHeader.planes); ++color) {
|
|
*(copperlist_ptr++) = 0x180 + color * 2;
|
|
*(copperlist_ptr++) = (palette[color].red >> 4) << 8 |
|
|
(palette[color].green >> 4) << 4 |
|
|
(palette[color].blue >> 4);
|
|
}
|
|
|
|
*(copperlist_ptr++) = 0xffff;
|
|
*(copperlist_ptr++) = 0xfffe;
|
|
|
|
custom.cop1lc = (ULONG)copperlist;
|
|
|
|
// wait for a mouse click
|
|
while ((ciaa.ciapra >> 6) == 3) {}
|
|
|
|
// put everything back
|
|
custom.dmacon = 0x7FFF;
|
|
custom.dmacon = OldDMACON;
|
|
custom.intena = 0x7FFF;
|
|
custom.intena = OldINTENA;
|
|
custom.intreq = 0x7FFF;
|
|
custom.intreq = OldINTREQ;
|
|
custom.adkcon = 0x7FFF;
|
|
custom.adkcon = OldADKCON;
|
|
|
|
custom.cop1lc = OldCopper;
|
|
LoadView(OldView);
|
|
WaitTOF();
|
|
WaitTOF();
|
|
WaitBlit();
|
|
DisownBlitter();
|
|
Permit();
|
|
|
|
// free the RAM we allocated
|
|
FreeMem(copperlist, 2000);
|
|
FreeMem(imageData, chunkLength);
|
|
} else {
|
|
if (!keepReading) {
|
|
// ignore every other chunk
|
|
if (chunkLength & 1 == 1) chunkLength++;
|
|
if (Seek(fh, chunkLength, OFFSET_CURRENT) != -1) {
|
|
keepReading = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Close(fh);
|
|
}
|
|
|