`8NNNNWp NNNuTHIS DISK HAS BEEN IMMUNIZED AGAINST MOST VIRUSES BY THE 'ULTIMATE VIRUS KILLER' VERSION 6.0 BY RICHARD KARSMAKERS, *THE* ATARI VIRUS KILLER!!!puke'(*** S` @!#@')+-/1 3@5`79;=?A C@E`IKMOQ S@U`WY[]_a c@e`gikmoq s@u`wy @` @ @ ` @ @ ` ǀ ` ׀ ٠ /@` @`/Aa   !A!#A%a')+-/1!3A5a79;=ACAEaIKMOQ!SAUaWY[]_a!cAeagioq!sAuaw{}!o!Aa!Aa!Aa!Aaǁɡ!Aaׁ١!Aa!Aa " B b  !"!B!b!!!!!"!""#B"%b"'")"-"/#1"#3B#5b#7#9#;#=#?$A"$CB$Eb$G$I$$M$O%Q"%SB%Ub%W%Y%]%_&a"&cB&eb&g&&k&m&o'O'ub'w'y'{'("(B(b((((()")B)b))))*"*B*b*****+"+B+b++` @!#@')+-/1 3@5`79;=?A C@E`IKMOQ S@U`WY[]_a c@e`gikmoq s@u`wy @` @ @ ` @ @ ` ǀ ` ׀ ٠ /@` @`/Aa   !A!#A%a')+-/1!3A5a79;=ACAEaIKMOQ!SAUaWY[]_a!cAeagioq!sAuaw{}!o!Aa!Aa!Aa!Aaǁɡ!Aaׁ١!Aa!Aa " B b  !"!B!b!!!!!"!""#B"%b"'")"-"/#1"#3B#5b#7#9#;#=#?$A"$CB$Eb$G$I$$M$O%Q"%SB%Ub%W%Y%]%_&a"&cB&eb&g&&k&m&o'O'ub'w'y'{'("(B(b((((()")B)b))))*"*B*b*****+"+B+b++ASSEMBLY nAUTO HnzC Kn}GFA tnMISC nlSTOS MnqDESKTOP INF ICTARI TXT nԇICTARI 016. n.. TINY  nCONV_PAC #n CONV_IMG 'nCONV_TNY ,nCONV_DEG 0nCONV_PCX 4nPIC_PAC 8n%. !n.. nTINY S *These routines were passed to me for some reason or another,they are *related to the TNY graphic format.One of them is a decompression routine *which could be very useful.None of them have been tested or used by me. * CHRIS - EMERALD CITY SECTION TEXT *===============================================================* * TNY file routines * * tny_move - Move tny files to odd address * * set_palette - Set palette from a tny file * * pic_expand - Expands a TNY file * *===============================================================* ***************************************************************** * Move TNY files down by one byte so that the palette is on * * an even boundary, as the first byte of a TNY file is the * * screen mode indicator. * * The TNY files must be from (A0) to (A1) * ***************************************************************** tny_move MOVEM.L D0/D1/A0/A1,-(SP) MOVE.L A1,D0 SUB.L A0,D0 ;LENGTH IN D0 SUB.L #1,D0 ;In case over 1 word CLR.L D1 ;put upper bit in d1 MOVE.L D0,D1 SWAP D1 MOVE.L A0,A1 SUB.L #1,A1 .lp MOVE.B (A0)+,(A1)+ DBF D0,.lp DBF D1,.lp MOVEM.L (SP)+,D0/D1/A0/A1 RTS ***************************************************************** * Sets the palette from TNY data in (A5) * * So can be called directly after pic_expand as registers are * * preserved and will be pointing to the correct place. * * To call as stand-alone.... * * LEA picture,A5 * * BSR set_palette * ***************************************************************** set_palette MOVEM.L D0-D7/A0-A6,-(SP) MOVE.L A5,-(SP) ;Address of palette MOVE.W #6,-(SP) TRAP #14 ADDQ.L #6,SP MOVEM.L (SP)+,D0-D7/A0-A6 RTS ***************************************************************** * pic_expand takes a tiny file pointed to by A5 * * and expands it into address in A3. * * eg. * * BSR tny_move * * * * LEA pic_0,A5 Address of picture to expand * * MOVE.L scr_work,A3 Put at address in scr_work * * BSR pic_expand Expand it * * BSR set_palette and set the correct palette * * * * dummy DC.W 0 Dummy required as files moved down * * pic_0 INCBIN "ascreen.tny" * * pic_2 INCBIN "bscreen.tny" * * etc..... * * pic_E * This marks end of screen files for TNY_MOVE * ***************************************************************** pic_expand MOVEM.L D0-D7/A0-A6,-(SP) SUB.L #1,A5 ;Subtract 1 as TNY files on odd boundary! LEA tny_work_area,A4 ;A4 IS work area BSR.W TNY_CONVERT MOVEM.L (SP)+,D0-D7/A0-A6 RTS ************************************************************* * Expand a TNY format low rez file into 'screen data' * ************************************************************* * TNY_CONVERT ;EXPAND FROM (A5) TNY FILE ;TO (A3) SCREEN USING (A4) WORK AREA * LEA $25(A5),A0 ;ADDR OF 1ST DATA AREA MOVE.W $21(A5),D0 ;LENGTH OF 1ST DATA AREA EXT.L D0 ;LONG ADD.L D0,A0 ;ADD 'EM GIVING START OF 2ND CLR.L D0 MOVE.W $23(A5),D0 ASL.L #1,D0 MOVE.L A4,-(SP) .LOOP MOVE.B (A0)+,(A4)+ DBF D0,.LOOP MOVE.L (SP)+,A4 * clr.l d0 clr.l d2 clr.l d3 LEA $25(A5),A2 * bra.b NEXT LOOP1 BSR.W GET_WORD tst.w d1 bge.b LOOP3 neg.w d1 bra.b LOOP2 MOVE2 move.w $0(a4,d2.w),$0(a3,d0.w) addq.w #2,d2 BSR.S INC_D0 LOOP2 dbf d1,MOVE2 bra.b NEXT MOVE3 move.w $0(a4,d2.w),$0(a3,d0.w) BSR.S INC_D0 LOOP3 dbf d1,MOVE3 addq.w #2,d2 NEXT CMP.W $21(A5),D3 blt.b LOOP1 rts ***************************************** GET_WORD clr.l d1 tst.b $0(a2,d3.w) beq.b .OK1 cmpi.b #$1,$0(a2,d3.w) beq.b .OK1 move.b $0(a2,d3.w),d1 ext.w d1 addq.w #1,d3 bra.b .EXIT .OK1 lea $0(a2,d3.w),a0 move.b (a0)+,d1 move.b (a0)+,TNY_WRK1 move.b (a0)+,TNY_WRK1+1 addq.w #3,d3 cmp.b #$1,d1 bne.b .OK2 neg.w TNY_WRK1 .OK2 move.w TNY_WRK1,d1 .EXIT rts ******************************** INC_D0 add.l #$A0,d0 cmp.l #$7D00,d0 blt.b .EXIT sub.l #$7CF8,d0 cmp.l #$A0,d0 blt.b .EXIT sub.l #$9E,d0 .EXIT rts SECTION BSS TNY_WRK1 DS.B 2 tny_work_area DS.B 30000 ;Work area to expand tiny files (could ;use a lot less?) EVEN . #n .. nCONV_PACS W CONV_PACDOC aX ;Convert PAC compressed file to bit image (high rez only) ;ENTRY a0=source buffer address ; a1=destination buffer address ;EXIT destination buffer holds picture image ; d0=0 OK, d0<>0 error conv_pac movem.l a0-a2/d1-d7,-(sp) move #1,d0 set error flag clr d7 set vertical format cmp.b #'p',(a0)+ check for pM85 or pM86 bne conv_pac9 cmp.b #'M',(a0)+ bne conv_pac9 cmp.b #'8',(a0)+ bne conv_pac9 cmp.b #'6',(a0)+ beq conv_pac0 move #1,d7 set horiz format conv_pac0 clr d3 zero column counter clr d2 zero line counter move.b (a0)+,d4 fetch ID byte move.b (a0)+,d5 fetch pack byte move.b (a0)+,d6 fetch special byte conv_pac1 tst d7 check flag bne conv_pac7 cmp #80,d3 80 columns done beq conv_pac6 bra if yes conv_pac8 move.b (a0)+,d0 fetch next byte cmp.b d0,d4 =ID byte ? beq conv_pac2 cmp.b d0,d6 =special byte ? beq conv_pac3 bsr op_pac output byte bra conv_pac1 repeat conv_pac2 move.b (a0)+,d1 fetch count and #$ff,d1 conv_pac5 move.b d5,d0 fetch pack byte bsr op_pac output byte dbra d1,conv_pac5 repeat d1+1 times bra conv_pac1 conv_pac3 move.b (a0)+,d0 fetch data byte move.b (a0)+,d1 fetch count byte and #$ff,d1 conv_pac4 bsr op_pac output byte dbra d1,conv_pac4 repeat d1+1 times bra conv_pac1 conv_pac7 cmp #400,d3 end of screen ? bne conv_pac8 conv_pac6 clr d0 no errors conv_pac9 movem.l (sp)+,a0-a2/d1-d7 rts ;Output byte data to buffer in Vertical or Horizontal format ;ENTRY d7 =0 Vertical format, =1 Horizontal format ; a1=destination address ; d0.b=data byte ; d2=line/column counter ; d3=column/line counter ;EXIT d3=80 or 400 when picture complete op_pac tst d7 bne op_pac2 move.b d0,(a1) add #80,a1 add #1,d2 cmp #400,d2 bne op_pac1 clr d2 sub #400*80,a1 add #1,a1 add #1,d3 op_pac1 rts op_pac2 move.b d0,(a1) add #1,a1 add #1,d2 cmp #80,d2 bne op_pac1 clr d2 sub #80,a1 add #80,a1 add #1,d3 rts SUB-ROUTINE NAME conv_pac BRIEF DESCRIPTION Converts a STAD (.PAC) compressed file to normal. FILENAME CONV_PAC OTHER RESOURCES None LANGUAGE Assembler (Devpac) AUTHOR Peter Hibbs ENTRY PARAMETERS a0 points at start of buffer holding picture data. a1 points at destination buffer. EXIT PARAMETERS Destination buffer holds picture image. Register d0=0 OK, d0=1 error. DETAILS - This sub-routine converts a compressed (.PAC) STAD file into a picture image. To use it first copy the .PAC file to a buffer and set a0 to the start of the buffer. Allocate a 32000 byte output buffer (which could be the screen) and set a1 to the start address. Call the routine which will decompress the file and copy it to the output buffer. These image files are high resolution only. Register d0=0 on return to indicate no errors or 1 if the file is not a STAD type file. . (n.. nCONV_IMGS 7W CONV_IMGDOC `X;Convert .IMG file to normal (high rez only) ;ENTRY a0=source buffer address ; a1=destination buffer address ; (m_buffer) defined ;EXIT destination buffer holds picture image ; d0=0 OK, d0<>0 invalid picture format ; d1=width (bytes) ; d2=height (scanlines) conv_img movem.l a0-a2/d3-d7,-(sp) move #1,d0 set error cmp #1,4(a0) check No planes bne conv_img1 bra if not 1 (high rez) move 14(a0),d3 move d3,d2 d2=height (scanlines) sub #1,d3 move 6(a0),d6 sub #1,d6 d6=pattern length-1 move 2(a0),d0 fetch header size lsl #1,d0 d0*2 move 12(a0),d7 fetch width (pixels) and.l #$0000ffff,d7 divu #8,d7 divide by 8 move.l d7,d1 and.l #$ffff0000,d7 beq conv_img2 add #1,d1 d1=width (bytes) conv_img2 add d0,a0 skip header conv_img3 tst.b (a0) check next byte bne conv_img4 tst.b 1(a0) bne conv_img4 cmp.b #-1,2(a0) bne conv_img1 move.b 3(a0),d4 and #$00ff,d4 add #4,a0 move.l a0,a2 conv_img5 move.l a2,a0 bsr img_line process one line add d1,a1 sub #1,d3 cmp #-1,d3 check flag byte (FF) beq conv_img7 bra if error sub #1,d4 bne conv_img5 bra conv_img3 conv_img4 bsr img_line process one line add d1,a1 next output addr dbra d3,conv_img3 repeat for all lines clr d0 no errors conv_img1 movem.l (sp)+,a0-a2/d3-d7 rts conv_img7 move #1,d0 flag error bra conv_img1 & exit ;Display one line of data ;ENTRY d1=width (bytes) ; a0=source pointer ; a1=destination pointer ; d6=byte pattern length ; (m_buffer) defined ;EXIT a0=next source address img_line movem.l d1/a1-a2,-(sp) img_line1 tst.b (a0) bra if pattern run beq img_line3 cmp.b #$80,(a0) bra if bit string beq img_line4 tst.b (a0) d7=bit 7 (a0) 00 or FF smi.b d7 img_line5 move.b (a0)+,d0 set counter and #$007f,d0 sub #1,d0 img_line6 move.b d7,(a1)+ copy d7 to O/P tst d1 beq img_line0 sub #1,d1 img_line0 dbra d0,img_line6 repaet n times img_line9 tst d1 bne img_line1 movem.l (sp)+,d1/a1-a2 rts img_line4 add #1,a0 bit string move.b (a0)+,d0 set byte counter and #$00ff,d0 sub #1,d0 img_line7 move.b (a0)+,d7 copy data to O/P move.b d7,(a1)+ tst d1 beq img_line2 sub #1,d1 img_line2 dbra d0,img_line7 repeat n times bra img_line9 img_line3 add #1,a0 next byte pattern run move.b (a0)+,d0 set d0 to count and #$00ff,d0 sub #1,d0 move d6,d5 fetch pattern length lea m_buffer,a2 img_line14 move.b (a0)+,(a2)+ copy pattern to buffer dbra d5,img_line14 img_line8 move d6,d5 lea m_buffer,a2 img_line15 move.b (a2)+,(a1)+ copy buffer to O/P tst d1 beq img_line16 sub #1,d1 img_line16 dbra d5,img_line15 repeat 'pattern' times dbra d0,img_line8 bra img_line9 m_buffer ds.b 8 SUB-ROUTINE NAME conv_img BRIEF DESCRIPTION Converts an IMG compressed picture file to normal. FILENAME CONV_IMG OTHER RESOURCES None LANGUAGE Assembler (Devpac) AUTHOR Peter Hibbs ENTRY PARAMETERS a0 points at start of buffer holding picture data. a1 points at destination buffer. m_buffer 8 byte buffer defined. EXIT PARAMETERS Destination buffer holds picture image. Register d0=0 OK, d0=1 error. Register d1=width of image in bytes. Register d2=height of image in scanlines. DETAILS - This sub-routine converts a compressed .IMG picture file into a bitmap image. To use it first copy the .IMG file to a buffer and set a0 to the start of the buffer. Allocate an output buffer and set a1 to the start address. Call the routine which will decompress the file and copy it to the output buffer. Unfortunately the size of the output image may be larger than the normal high resolution screen so it is normal to decompress the file to a RAM buffer. The size of the RAM buffer can be determined by multiplying the width (in bytes) by the height (in scanlines), in a practical application this should be done within the routine just before the decompression section. The m_alloc BIOS function could then allocate a suitable amount of RAM and the a1 register set to the start address. This routine will handle high resolution images only although the XIMG format also incorporates a colour palette. (A mod by any member to add this facility would be very welcome). An 8 byte buffer should also be defined in RAM for the pattern run option. Register d0=0 on return to indicate no errors or 1 if the file is not a high resolution file or that there is a flag error. Registers d1 and d2 hold the width and height of the final picture image for use by the calling program. . ,n.. nCONV_TNYS W!CONV_TNYDOC 2aX;Converts .TNY or .TN1-TN3 file to normal picture ;ENTRY a0 points at source buffer (must start on odd address) ; a1 points at destination buffer ;EXIT destination buffer holds picture image ; d0=0 OK, no other registers changed conv_tny movem.l a0-a3/d1-d5,-(sp) move.b (a0)+,d0 check animation code cmp.b #3,d0 blt conv_tny0 skip if none add #4,a0 skip animatiom bytes conv_tny0 movem.l a0-a1/d0,-(sp) set palette colours move.l a0,-(sp) move #6,-(sp) trap #14 addq #6,sp movem.l (sp)+,a0-a1/d0 add #32,a0 skip colour palette move #200-1,d5 init column count move #200*20-1,d4 init plane count move.l a1,a3 save a1 clr.l d0 move.b (a0)+,d0 lsl #8,d0 move.b (a0)+,d0 add #2,a0 move.l a0,a2 add d0,a2 conv_tny1 subq #1,d0 move.b (a0)+,d2 and #$00ff,d2 btst #7,d2 bne conv_tny2 tst.b d2 beq conv_tny3 cmp.b #1,d2 beq conv_tny4 and #$00ff,d2 conv_tny8 sub #1,d2 move.b (a2)+,d3 lsl #8,d3 move.b (a2)+,d3 conv_tny5 bsr out_tny dbra d2,conv_tny5 conv_tny6 tst d0 bmi conv_tny10 cmp #1,d0 bne conv_tny1 conv_tny10 clr d0 movem.l (sp)+,a0-a3/d1-d5 rts conv_tny2 neg.b d2 and #$00ff,d2 conv_tny9 subq #1,d2 conv_tny7 move.b (a2)+,d3 lsl #8,d3 move.b (a2)+,d3 bsr out_tny dbra d2,conv_tny7 bra conv_tny6 conv_tny3 move.b (a0)+,d2 lsl #8,d2 move.b (a0)+,d2 subq #2,d0 bra conv_tny8 conv_tny4 move.b (a0)+,d2 lsl #8,d2 move.b (a0)+,d2 subq #2,d0 bra conv_tny9 ;Outputs value in D3.W to next dest address ;ENTRY d3=value ; a1 points at address ; d5=column count ; d4=plane count out_tny move d3,(a1) add #160,a1 dbra d5,out_tny1 move #199,d5 sub #160*200-8,a1 out_tny1 dbra d4,out_tny2 move #200*20-1,d4 addq #2,a3 move.l a3,a1 out_tny2 rts SUB-ROUTINE NAME conv_tny BRIEF DESCRIPTION Converts any Tiny compressed file to normal. FILENAME CONV_TNY OTHER RESOURCES None LANGUAGE Assembler (Devpac) AUTHOR Peter Hibbs ENTRY PARAMETERS a0 points at start of buffer holding picture data. a1 points at destination buffer. EXIT PARAMETERS Destination buffer holds picture image. Colour palette set up. Register d0=0. DETAILS - This sub-routine converts a compressed (TN1-3 or TNY) Tiny file into a picture image. Files with the TNY extension may be any of the three resolutions. To use it first copy the Tiny file to a buffer and set a0 to the start of the buffer (which must be an odd address). Allocate a 32000 byte output buffer (which could be the screen) and set a1 to the start address. Call the routine which will decompress the file and copy it to the output buffer. The colour palette will also be set up although, of course, if the file type does not match the current screen resolution, this will not work correctly. The reason the file must start on an odd address is that the palette information starts at byte number 1 (or 5) so that if this address is passed to the set_palette BIOS function, the CPU generates an address error. If the file must be stored at an even address, the palette information should first be copied to another buffer and the address of this buffer used to set up the colour palette. The animation facilities (bytes 1-4 if present) are ignored by the routine. Register d0=0 on return to indicate no errors (for this routine it always returns with a value of 0). . 1n.. nCONV_DEGS WCONV_DEGDOC `X;Converts a 'Degas' picture (PI1-3 or PC1-3) to normal format (all resolutions) ;ENTRY a0=source buffer address ; a1=destination buffer address ; k1 & k2 stores defined ;EXIT destination buffer holds picture image ; d0=0 OK, no other regs changed conv_degas movem.l a0-a2/d1-d4,-(sp) move (a0)+,d0 movem.l a0-a1/d0,-(sp) set palette colours move.l a0,-(sp) move #6,-(sp) trap #14 addq #6,sp movem.l (sp)+,a0-a1/d0 add #32,a0 skip palette data btst #15,d0 check if compressed beq conv_degas6 bra if not and #3,d0 mask high bits move #2,k1 set planes value move #80,k2 set columns cmp #2,d0 high rez beq conv_degas7 yes move #4,k1 4 bytes/plane cmp #1,d0 med rez ? beq conv_degas7 yes move #8,k1 8 bytes/plane move #40,k2 40 columns conv_degas7 clr d2 clr d3 move.l a1,d1 calc end addr add.l #32000,d1 conv_degas1 clr.l d0 fetch cntrl byte move.b (a0)+,d0 cmp.b #128,d0 beq conv_degas1 loop if 128 btst #7,d0 bne conv_degas3 bra if neg conv_degas2 move.b (a0)+,(a1) copy data to O/P buffer bsr degas_next calc next addr dbra d0,conv_degas2 repeat d0 times conv_degas5 cmp.l d1,a1 blt conv_degas1 repeat till all done conv_degas9 clr d0 no errors movem.l (sp)+,a0-a2/d1-d4 rts conv_degas3 neg.b d0 conv to positive conv_degas4 move.b (a0),(a1) copy data to output bsr degas_next calc next addr dbra d0,conv_degas4 repeat d0 times addq #1,a0 inc source pointer bra conv_degas5 & repeat conv_degas6 move #8000-1,d0 copy picture to buffer conv_degas8 move.l (a0)+,(a1)+ for uncompressed image dbra d0,conv_degas8 bra conv_degas9 ;Calc next output address degas_next add #1,a1 addq #1,d2 btst #0,d2 bne degas_next1 add k1,a1 subq #2,a1 cmp k2,d2 bne degas_next1 sub #160-2,a1 clr d2 addq #1,d3 move k1,d4 lsr #1,d4 cmp d4,d3 bne degas_next1 clr d3 add #160,a1 sub k1,a1 degas_next1 rts k1 ds.w 1 k2 ds.w 1 SUB-ROUTINE NAME conv_deg BRIEF DESCRIPTION Converts any Degas file to a normal picture FILENAME CONV_DEG OTHER RESOURCES None LANGUAGE Assembler (Devpac) AUTHOR Peter Hibbs ENTRY PARAMETERS a0 points at start of buffer holding picture data. a1 points at destination buffer. k1 and k2 stores defined. EXIT PARAMETERS Destination buffer holds picture image. Colour palette set up. Register d0=0. DETAILS - This sub-routine converts a compressed (PC1-3) or uncompressed (PI1-3) Degas file into a picture image. To use it first copy the Degas file to a buffer and set a0 to the start of the buffer (which must be an even address). Allocate a 32000 byte output buffer (which could be the screen) and set a1 to the start address. Call the routine which will decompress the file (if it is a PC1-3 type) and copy it to the output buffer. The colour palette will also be set up although, of course, if the file type does not match the current screen resolution, this will not work correctly. Two word-size stores (k1 and k2) are used for temporary storage by the routines. Register d0=0 on return to indicate no errors (for this routine it always returns with a value of 0). . 5n.. nCONV_PCXS W qCONV_PCXDOC aX"T;Convert .PCX files to image (high rez only) ;ENTRY a0=source buffer address ; a1=destination buffer address ;EXIT destination buffer holds picture image ; d0=0 OK, d0<>0 error ; d1=width (bytes) ; d2=height (scanlines) conv_pcx movem.l a0-a3/d3-d5,-(sp) move #1,d0 set error cmp.b #1,3(a0) check if mono image bne conv_pcx1 exit if not cmp.b #10,(a0) check ident code=10 bne conv_pcx1 bra if not move 4(a0),d0 calc width rol #8,d0 move 8(a0),d3 get Xmax rol #8,d3 sub d0,d3 add #1,d3 move 6(a0),d0 calc height rol #8,d0 move 10(a0),d3 get Xmax rol #8,d3 sub d0,d3 add #1,d3 move d3,d2 save picture height sub #1,d3 correct for dbra move 66(a0),d5 fetch width (bytes) rol #8,d5 move d5,d1 save width add #128,a0 a0=data start conv_pcx4 bsr pcx_line convert one line add d5,a1 locate next line addr dbra d3,conv_pcx4 repeat for all lines clr d0 no error conv_pcx1 movem.l (sp)+,a0-a3/d3-d5 rts ;Convert one .PCX line ;ENTRY a0=source address ; a1=dest address ; d5=line width (bytes) ;EXIT pcx_line clr d4 reset count move.l a1,-(sp) pcx_line7 move.b (a0),d0 fetch next byte and.b #%11000000,d0 cmp.b #%11000000,d0 beq pcx_line3 move.b (a0)+,(a1)+ copy to dest not.b -1(a1) and invert add #1,d4 inc chr count cmp d4,d5 bne pcx_line7 pcx_line1 move.l (sp)+,a1 rts pcx_line3 move.b (a0)+,d0 and #%00111111,d0 sub #1,d0 pcx_line5 move.b (a0),(a1)+ not.b -1(a1) add #1,d4 cmp d4,d5 beq pcx_line6 dbra d0,pcx_line5 add #1,a0 bra pcx_line7 pcx_line6 add #1,a0 bra pcx_line1 SUB-ROUTINE NAME conv_pcx BRIEF DESCRIPTION Converts a PCX compressed picture file to normal. FILENAME CONV_PCX OTHER RESOURCES None LANGUAGE Assembler (Devpac) AUTHOR Peter Hibbs ENTRY PARAMETERS a0 points at start of buffer holding picture data. a1 points at destination buffer. EXIT PARAMETERS Destination buffer holds picture image. Register d0=0 OK, d0=1 error. Register d1=width of image in bytes. Register d2=height of image in scanlines. DETAILS - This sub-routine converts a compressed .PCX picture file into a bitmap image. PCX picture files are used extensively on the PC by programs such as PC Paintbrush Plus and since they are not screen dependant, they can also be used on the Atari in high resolution. Like the IMG format, pictures can be any size. To use it first copy the .PCX file to a buffer and set a0 to the start of the buffer. Allocate an output buffer and set a1 to the start address. Call the routine which will decompress the file and copy it to the output buffer. As the size of the output image may be larger than the normal high resolution screen it is normal to decompress the file to a RAM buffer first. The size of the RAM buffer can be determined by multiplying the width (in bytes) by the height (in scanlines), in a practical application this should be done within the routine just before the decompression section. The m_alloc BIOS function could then allocate a suitable amount of RAM and the a1 register set to the start address. This routine will handle high resolution images only although the PC version does also incorporates colour information. (A mod by any member to add this facility would be very welcome). Register d0=0 on return to indicate no errors or 1 if the file is not a high resolution file. Registers d1 and d2 hold the width and height of the final picture image for use by the calling program. . 9n%.. nPICPAC DOC }h&^VDI_COPYTXT g>!PICPAC S W|bG -- PICPAC ------ By: Mrten Lindstrm -------------------- A collection of sub-routines for picture un/packing primarily written for VDI image copying with VR_TRNFM + VRO_CPYFM/VRT_CPYFM, but also including routines to replace the VDI ones, thus making the package as usable without GEM. The formats handled are IMG and IFF ILBM (plus, unpacking only, Degas and NEO). General notes on all of my sub-routines: 1) No registers are affected except those specified as exit parameters. 2) At exit the processor condition flags will be set according to the contents of register D0 if this is an exit parameter. This means that you don't have to perform a test of D0 to, for example, determine if there was an error. Example :- bsr routine ble error if zero or negative signals error The routines for un/packing picture files have names XXXUNP and XXXPAC where XXX stands for either of IMG, LBM (=IFF ILBM), DEG (=Degas) or NEO(chrome). IMGUNP unpacks any IMG including PC imported, colour IMGs of types XIMG and HyperPaint and with any pattern length. LBMUNP unpacks IFF ILBM of any compression type 0-2, skipping any mask plane. DEGUNP unpacks/converts files of types PI1,PC1,PI2,PC2,PI3,PC3,PI5,PC5, PI7,PC7 (but not PI8,PC8 since I'm unsure about the organization of TT low rez screen memory - word- or byte-interleaved). NEOUNP reads the resolution word in the NEO file header and then branches to DEGUNP - i.e. can handle the same image resolutions. IMGPAC can pack with pattern length 1 or 2. (2 is 'standard' but 1 is usually slightly more effective /less ineffective. Any palette will be stored according to the XIMG standard. IMGPAC uses all available IMG compression methods about as effectively as can be done I think. (For every IMG file that I have tried to re-pack with IMGPAC, it managed to save a few bytes at least - in some extreme cases KILObytes - compared to the original size. Specifically it is much more effective than either HyperPaint or Fleet Street Publisher.) LBMPAC can pack according to compression types 0-2 where 0 = Uncompressed 1 = Packbits (as Degas compressed): Standard and the best for pictures with regular patterns. 2 = Vertical word (almost as TINY): usually by far the most effective type, except with patterns. Not as standard as Packbits (but used by Deluxe Paint). (I have not bothered to write corresponding Degas/NEO packing routines.) The un/packing of picture files is done NOT directly to/from screen memory (as would admittedly have been the quickest and most space-saving) but in all cases to/from an image in 'device independent' (i.e. plane separated) format. This is the format between which and the screen all 100% compatible programs must do their image copying, and in addition allows more flexibility for instance with images of differing size or number of colour planes. The copying can be done with VDI (VR_TRNFM + VRO_CPYFM/VRT_CPYFM) or - if the screen is known to be word-interleaved (as are all ST resolutions) - it can be done with my non-VDI routines PUTFM and GETFM. The unpacking routines create (preceding the picture data) a VDI MFDB followed by extra parameters (including palette). And the packing routines expect a pointer to a corresponding structure as input: MFDB (20 bytes): L: Pointer to image W: Width in pixels W: Height W: Width in words W: Format flag = 1 for device independent (0: device specific) W: Number of planes. (Initiated to actual number, but can be changed) 3W: 0 (reserved) + 6 words (12 bytes): W: Actual number of planes (Fill planes = requested number minus this) 2W: Relative width and height of a pixel W: Palette format -1 = 24-bit, 0 = VDI W: 0 (or Start colour number if # image colours < # screen colours) W: Number of colours in palette After this any palette You have the option to have the unpacking routines present the palette in either VDI or 24-bit format. In a GEM program the VDI format would be natural of course (but you must create a colour number translation table to use it - see my IMG_IFF file). If you are not using GEM you can use my SETTRU routine to set screen colours according to a 24-bit palette. In PICPAC.S a set of routines to convert palettes between different formats are included, some of which are internally called by the picture un/packing routines. They have names XXX_YYY where XXX stands for source and YYY for destination format given as "VDI", "STE" or "TRU" (=24-bits). E.g. STE_VDI translates ST(E) hardware palette into VDI colours. HOW TO USE THE UNPACKING ROUTINES --------------------------------- (described with a mixture of plain text and assembler instructions that I hope you'll excuse.) With VDI: 1) Load picture file and set A3 to point to it 2) moveq #0,D3 ;for a VDI palette move.w nplanes,D3 ;if 'nplanes' labels # screen planes (probably * got from WORK_OUT array) bsr IMGCALC ;for IMG file (or LBMCALC, DEGCALC, NEOCALC) ble error 3) Use size in D0 or D1 to allocate memory for unpacking (D1 if you intend to give any mono image special treatment with VRT_CPYFM rather than the general VRO_CPYFM). Set A4 to point to address of this block. 4) bsr IMGUNP ;(or LBMUNP, DEGUNP, NEOUNP) ble error * ( tst.l D1 ;If you want to check for 'minor error' i.e.) * ( bmi minorerr ;file header OK but error during unpacking ) 5) If you intend to use VRT_CPYFM for a possible mono image: move.w D1,D3 In word at 28(A4) you can also, optionally, set a start screen colour number where you want a possible palette shorter than the screen palette to go. (Any start number not divisible by the number of colours in image will be replaced with the next lower number that is.) 6) bsr PICFILL 7) A4 is already pointing to an MFDB for the device independent image. Now create an MFDB for the transformed image (if you do not want to do the transformation in place, which is slow). A simple copy - 5 longs - from (A4) will do fine. Use size given in D0 (by the unpacking routine; PICFILL hasn't touched it) to allocate memory for the transformed image and put the address of this as the first long in the new MFDB. Then call VR_TRNFM (A4 -> source) 8) In the word at 30(A4) is the number of colours in palette. Directly following is the palette itself as VDI colours (3 words colour) which can be set with VS_COLOR (after translating the colour numbers via a table created with the help of V_GET_PIXEL - see the file IMG_IFF) 9) Now you can copy the picture (or rectangles of it) to screen with VRO_CPYFM/VRT_TRNFM (I have also written some general notes about VDI copying in the file VDI_COPY.TXT, sent to ICTARI together with this one.) Without VDI: 1) Load picture file and set A3 to point to it 2) moveq #-1,D3 ;for 24-bit palette and no fill planes bsr IMGCALC for IMG file (or LBMCALC, DEGCALC, NEOCALC) ble error 3) Use size in D0 to allocate memory for unpacking. Set A4 to point to address of this block. 4) bsr IMGUNP (or LBMUNP, DEGUNP, NEOUNP) ble error * ( tst.l D1 ;If you want to check for 'minor error' i.e.) * ( bmi minorerr ;file header OK but error during unpacking ) 5) In word at 28(A4) you can, optionally, set a start screen colour number where you want a possible palette shorter than the screen palette to go. (Any start number not divisible by the number of colours in image will be replaced with the next lower number that is.) 6) lea 28(A4),A0 move.l (A0)+,D0 ;Load start colour & number of colours bsr SETTRU ;Set screen palette 7) Copy the picture (or rectangles of it) to screen with my PUTFM. In case # image planes < # screen planes, first clear D0.W or move the same word you used at 28(A4) to D0. FOREIGN SCREEN FORMATS: ----------------------- SIZE: The unpacking to a device independent form, and copying - of selected rectangles - to screen from there, makes it easy to deal with pictures of any size. Large images can be scrolled by simply moving the rectangle from where the copy is made. COLOUR: Images of more colours than the current screen is a tougher problem. The algorithm described above will simply skip any extra colour planes, which can produce acceptable results depending on how the picture was made. I think for instance you would agree that the (5 plane) CIVILIZATION images look acceptable to say the least, even on the (4 plane) ST. To make more general pictures look good you would have to devise an emulation routine of some kind (probably replacing colours with patterns of two or more colours). PIXEL SHAPE: The unpacking routines takes care of the pixel shape info deducible from the file headers and store it as two words at 22(A4). None of my routines use this info however, so again it is up to others to write appropriate emulation routines (perhaps just doubling/halving lines/columns). ROUTINE DESCRIPTIONS: --------------------- SUB-ROUTINE NAME TT_VDO? BRIEF DESCRIPTION Compares VDO_ cookie value MSW with 2 FILENAME PICPAC.S OTHER RESOURCES - LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS None EXIT PARAMETERS None except processor flags set according to comparison DETAILS No registers are affected, only the processor flags, which can be used by a conditional branch. Example: bsr TT_VDO? bhi falcon if 'falcon' labels a routine specific for Falcon030 video hardware. SUB-ROUTINE NAMES VDI_TRU,TRU_VDI,STE_TRU,STE_VDI,TRU_STE,VDI_STE BRIEF DESCRIPTION Transform palette between three different formats FILENAME PICPAC.S OTHER RESOURCES - LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A0 -> Source, A1 -> Destination, D0: # colours EXIT PARAMETERS None (No registers affected) DETAILS These routines transform palettes between three formats: ST(E) 2 bytes/colour (As used by XBIOS SETCOLOR) 24-bit 3 bytes/colour (1 byte/component 0-255) VDI 6 bytes/colour (1 word/component 0-1000) E.g. STE_VDI transforms an ST(E) hardware palette into a VDI palette. STE_TRU transforms the 16 STE colour intensities $0,$1,$2, etc. to $F into $00,$11,$22 etc. up to $FF i.e. evenly among the 256 possible true colour levels. Conversions between VDI and 24-bit are made according to: TRU = VDI*256/1001 VDI = TRU*1000/255 Internally I have let conversions between VDI and STE be made via the 24- bit ('TRU') format (e.g. STE_VDI is calling STE_TRU and TRU_VDI). SUB-ROUTINE NAME SETTRU BRIEF DESCRIPTION Sets palette given as 24-bit (no dummy bytes) FILENAME PICPAC.S OTHER RESOURCES None (But calls tt_vdo? in same file, see above) LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A0->Palette, D0 MSW: 0 (hardware start colour), LSW: # colours EXIT PARAMETERS None (No registers affected) DETAILS Sets screen colours according to palette given in 24-bit format (as used for instance in IFF ILBM pictures). When the number of colours in the palette is less than the number of screen colours, a start colour can be set in the Most Significant Word of D0. Note that the 24-bit format used by this and other of my routines is not identical to the palette format of Falcon VSETRGB/VGETRGB. The latter uses leading dummy fill bytes to make each colour take up a full longword. FALCON NOTE: I don't have a Falcon (yet?), but have been bold enough to let this routine call the Falcon VSETRGB function. I would be very interested to hear from a Falcon owner if it works. I am counting on VSETRGB to copy the palette immediately to hardware without waiting for a VBL. Otherwise, if you have reserved a large stack (over 1K), you can change the routine to transform and set the whole palette in one go (possibly with VSYNC). The other, less neat, solution is to replace with direct copy to hardware addresses (at $FF9C00 +, fill byte between green and blue of each colour). SUB-ROUTINE NAME GETCHNK BRIEF DESCRIPTION Searches IFF FORM for specified chunk FILENAME PICPAC.S OTHER RESOURCES None LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A3 -> FORM chunk to search, D0: ID to look for EXIT PARAMETERS A0 -> Data (after 8 byte header) of found chunk D0.L: Length or -1 at error DETAILS Searches IFF FORM (or other structural IFF chunk), pointed to by A3, for (the first occurring) chunk the ID of which is specified in D0. Example (assuming A3 points to FORM i.e. usually file start): move.l #'BODY',D0 search for BODY chunk bsr GETCHNK ble error after which A0 points to the data of the (1st) BODY chunk. SUB-ROUTINE NAMES IMGCALC, LBMCALC, DEGCALC, NEOCALC BRIEF DESCRIPTION Calculate space needed for picture unpacking with corresponding routines - see below. FILENAME PICPAC.S OTHER RESOURCES None LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A3->Loaded picture file D3 MSW: Type of palette wanted: -1= 24-bit, 0=VDI D3 LSW: >0 Min number of planes (= # screen planes) 0: Use no fill planes EXIT PARAMETERS D0.L: Required size or -1 for error D1.L = D0.L except when #planes=1 and D3.W>0: Size required without fill. (Intended for VRT_CPYFM.) DETAILS These routines can be called in preparation of picture unpacking with the routines below to calculate the space that needs to be allocated. 'Min. number of planes' (in D3 LSW) needs to be used should the picture contain less colour planes than the screen, and you intend to use VDI function VRO_CPYFM to copy it to screen. Since VRO_CPYFM (or VR_TRNFM) isn't capable of copying between forms with different numbers of planes, extra fill planes have to be added. Should the loaded picture turn out to be a mono image you can use VRT_CPYFM instead of VRO_CPYFM, which saves you the need for fill planes. In this case you can use the size given in D1.L instead of D0.L. SUB-ROUTINE NAMES IMGUNP, LBMUNP, DEGUNP, NEOUNP BRIEF DESCRIPTION Unpack pictures to device independent format FILENAME PICPAC.S OTHER RESOURCES None (but above calc and palette routines called) LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A4 -> destination, A3/D3: See above calc routines EXIT PARAMETERS D0.L: Size of image data or -1 for error D1 MSW: <0 for minor error, LSW: =D3.W or =1 for one plane image Additional parameters found at (A4). See below. DETAILS These routines unpack/convert a picture file to a form that can be easily used with VDI functions VR_TRNFM and VRO_CPYFM/VRT_CPYFM, or alternatively with my PUTFM routine below. If you use the VDI you should first call PICFILL (see below). (If you intend to give mono images special treatment with VRT_CPYFM rather than VRO_CPYFM, you should move D1 to D3 before calling PICFILL.) The image size returned in D0 can be used to allocate memory for VR_TRNFM if you don't want to make the transformation in place (slow). At (A4) will be found a VDI MFDB for the unpacked image, followed - at 20(A4) - by an extra 6-word structure of my own (see below). Then comes - at 32(A4) - a palette in either VDI or 24-bit format, and finally the image itself in 'device independent' format i.e. plane separated. MFDB (20 bytes): L: Pointer to image W: Width in pixels W: Height W: Width in words W: Format flag = 1 for device independent (0: device specific) W: Number of planes. (Initiated to actual number, but can be changed) 3W: 0 (reserved) + 6 words (12 bytes): W: Actual number of planes (Fill planes = requested number minus this) 2W: Relative width and height of a pixel W: Palette format -1 = 24-bit, 0 = VDI W: 0 (or Start colour number if # image colours < # screen colours) W: Number of colours in palette After this any palette SUB-ROUTINE NAME PICFILL BRIEF DESCRIPTION Sets #planes in MFDB and fills extra planes FILENAME PICPAC.S OTHER RESOURCES None LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A4 -> unpacked image (see above unp routines) D3.W: Requested number of planes EXIT PARAMETERS None DETAILS This function should be called to prepare the data output from the above unpacking routines, before using VDI to copy it to screen. It sets the number of planes in the MFDB and fills any extra planes. Should you not want the image palette to start at screen colour 0, you can specify another start colour in the word at 28(A4) before calling PICFILL. E.g. for a 4-plane image and an 8-plane screen you could set the start colour to $F0 if you want to use the last 16 colours rather than the first 16. (Note that any start number not divisible by the number of colours in image will be interpreted as the next lower number that is. So in the above example $F1, $FF etc. would be interpreted as $F0.) SUB-ROUTINE NAME PUTFM BRIEF DESCRIPTION Copies rectangle from device indep. form to screen FILENAME PICPAC.S OTHER RESOURCES None LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A4 -> MFDB A3 ->8 W: source X1,Y1,X2,Y2, screen X1,Y1,X2,Y2 D0: Start colour if #image planes< #screen planes (Start colour = -1: Repeat last plane) EXIT PARAMETERS None DETAILS This non-GEM routine can be used to replace VR_TRNFM + VRO_CPYFM/VRT_CPYFM for plain copy operations if screen is known to be word-interleaved. I.e. it copies rectangles DIRECTLY from a 'device independent' (plane separated) form to the screen without the need for transformation as a separate step. (This is how I would have wished that VRO_CPYFM had worked, and how it probably was/is intended to work one day.) In addition the source form does NOT need to have as many planes as the screen. If not you can, in D0, specify a start colour for the hardware palette where you want the image palette to go. (Any start number not divisible by the number of colours in image will be interpreted as the next lower number that is. So for a 4-plane image a start colour $FF is equivalent to $F0.) OR you can set D0=-1 in which case last plane will be repeated (so that a mono image will use the first and last colours - by default white and black). A3 should point to 8 words (as used with VDI copy routines): X,Y of source rectangle upper left corner X,Y of source rectangle lower right corner X,Y of screen rect. upper left corner (0,0 for full screen) X,Y of screen rect. lower right corner (319,199 for full ST low) PUTFM does some error checking: it will shrink rectangles to equal size and to fit within screen/source boundaries. Only straight copy operations are possible (no fancy 'write modes' logically combining source and destination in various ways). And again the screen must be word-interleaved, which includes all standard ST screens and, if the Atari Compendium is to be believed, all other Atari screen modes as well except 16 bit Falcon high colour. I have my doubts though regarding the TT low rez at least, which may be BYTE-interleaved according to an article in a German ST Magazine. PUTFM can obviously not be a general replacement for VDI copying if you want your program to be 100% compatible including graphic cards. SUB-ROUTINE NAME GETFM BRIEF DESCRIPTION Copies rectangle from screen to device indep. form FILENAME PICPAC.S OTHER RESOURCES None (but - part of - PUTFM called) LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A4 -> MFDB A3 ->8 W: screen X1,Y1,X2,Y2, dest. X1,Y1,X2,Y2 EXIT PARAMETERS None (MFDB #planes set to min of screen and form) DETAILS This routine is the reverse of PUTFM (see above). A difference is that any extra planes are ignored, and that GETFM sets the MFDB number of planes to the minimum of screen and form. (Also note that screen coordinates are now the FIRST four words pointed to by A3. Since source coordinates are always first.) SUB-ROUTINE NAMES IMGPAC, LBMPAC BRIEF DESCRIPTION Pack pictures to files of types IMG & IFF ILBM FILENAME PICPAC.S OTHER RESOURCES None (but above palette routines called) LANGUAGE Assembler (Devpac 2) AUTHOR Mrten Lindstrm ENTRY PARAMETERS A4 -> MFDB + xtra param. (see 'unp' routines above) A3 -> Space for file D0 for LBM: compr. type; for IMG: pattern length EXIT PARAMETERS D0.L: File length or -1 for error DETAILS These routines pack pictures (in the format produced by the above 'unp' routines) to files of types IMG and IFF ILBM. IMGPAC lets you specify a pattern length of 1 or 2 - only. 2 is 'standard' for some reason while a pattern length of 1 should practically always be more effective - or, rather, less ineffective. (Where the IMG pattern run is at all applicable it is usually outdone by the simple Packbits - of IFF ILBM and Degas compressed. IMG is best with solid lines /colour fields, especially where the IMG line repeat feature can be used.) For IFF ILBM files a compression type can be specified: 0: Uncompressed 1: Packbits - Standard and the best for pictures with regular patterns. 2: Vertical word - usually by far the most effective type, except with patterns. Not as standard as Packbits (but used by Deluxe Paint). VDI IMAGE COPYING ------------------- By: Mrten Lindstrm ----------------------- When writing the documentation for my image un/packing sub-routines (in assembler) designed to be used with the VDI copy functions, I realised/remembered that, to a newcomer to GEM, the VDI functions themselves need a bit of explanation too. This is a description not on HOW to pass parameters or call VDI functions, but on WHAT parameters to pass and functions to call and WHY. Screen dimensions and number of planes -------------------------------------- As with all (compatible) work on the screen you should never assume it is of a particular size or number of colour planes. So just as a quick reminder, you get the screen dimensions (coordinates of lower right corner) as the first two words of the WORK_OUT array returned when you open a (virtual) workstation (with V_OPNVWK). To get the number of planes you call VQ_EXTND after which the number can be found as WORK_OUT[4] (i.e. the word at offset 8 from WORK_OUT start.) The MFDB (Memory Form Definition Block) -------- As parameters, all the VDI copy functions use pointers NOT to the image/screen data but to MFDBs: MFDB (20 bytes): L: Pointer to image (or 0 = screen) W: Width in pixels W: Height W: Width in words = (width in pixels +15)/16 truncated W: Format flag: 1 for device independent 0 for device specific W: Number of planes 3W: 0 (reserved) An MFDB for the screen needs only to consist of a single zeroed longword. Picture formats --------------- The format flag describes how the image data are arranged: 0 - DEVICE SPECIFIC: This is how the screen memory is organized and depends on where the program happens to be running. For all standard ST resolutions it is "word interleaved" (if you don't understand this term don't worry about it), but in a general program, written to be 100% compatible, you cannot make any assumptions at all regarding the organization of a device specific image - including the screen. And therefore you should not manipulate it directly in any way. 1 - DEVICE INDEPENDENT: Here the image data are separated according to 'colour planes', i.e. first comes colour plane 0 for the complete image, then colour plane 1 for the full image etc. (Colour plane 0 contains bit 0 for all pixels, plane 1 contains bit 1 etc. Together these bits form a colour number for each pixel.) In each plane the pixel data come in the 'natural order', line-wise 'left to right' (bytes in address number order and more significant bits of byte before the lesser ones) beginning with the top line and ending with the bottom line. Each new pixel line always starts at next word boundary. An image in this format can safely be manipulated without 'dirtying' the code one bit. In practice you should load all images as device independent forms, then transform them to device specific format (with VR_TRNFM) after which you can copy (rectangles of) them to screen (with VRO_CPYFM/VRT_CPYFM). In the opposite direction you can copy from screen to a device specific form, which can then be transformed before it is exported as an image file. VR_TRNFM (Raster TRaNsForM) -------- This function transforms a device independent form to device specific format or vice versa. As parameters, it takes TWO pointers to source and destination MFDBs. These CAN be the same (to save space) but that makes the transformation very slow. VR_TRNFM will look at the format flag for the source MFDB only and invert this in the destination MFDB. NOTE that VR_TRNFM must be used on EVERY image you import (load as a file) before you can copy it to screen. Even mono images need to be transformed if you want your program to be 100% compatible (in spite of the fact that transformations for Atari standard mono resolutions perform nothing but a plain copy operation). VR_TRNFM does NOT (contrary to what is said in the Atari Compendium) transform colour numbers in any way. VRO_CPYFM (Raster Opaque CoPY ForM) --------- Copies a rectangle from a form to another. Both forms must (in present versions of TOS) be DEVICE SPECIFIC. And they both must contain the same number of planes. Since VR_TRNFM too demands forms with equal number of planes, the latter requirement means that if the actual number of image planes is less than the screen planes, then you must add extra memory space to (the end of) the device independent image as fill planes, before transformation is effected. In the opposite case (# screen planes less than # image planes) you only have to change the number of planes in the MFDB (which will make VR_TRNFM ignore the extra planes). Parameters of VRO_CPYFM: Copy mode: word = 3 for plain copy Pointer to rectangle definition Pointer to source MFDB Pointer to destination MFDB If you use other values than 3 for copy mode, you can logically combine source and destination images in various ways. Of the 16 possible - more or less peculiar - modes, 1 ANDs and 7 ORs source with destination (possibly usable for sprite copying). The rectangle definition is an array of 8 words: Source X1,Y1, X2,Y2, Destination X1,Y1, X2,Y2 where X1,Y2 are the coordinates for the upper left and X2,Y2 the lower right corner of the rectangle. Obviously you should make sure that the width and height are the same for source and destination rectangles. The present versions of VDI uses only the source size I think, but a future version could implement scaling. If you are unfamiliar to VDI you should note that the difference between first and second X/Y of each rectangle is ONE LESS than the width/height of the rectangle. For example a rectangle covering a complete ST low rez screen would be defined as 0,0,319,199 (but again you should use screen dimensions got from WORK_OUT - see above.) VRT_CPYFM (Raster Transparent CoPY ForM) --------- This function works as VRO_CPYFM except that the source form should be single plane (regardless of number of planes in destination). For one-plane (i.e. mono) images, VRT_CPYFM can thus be used as an alternative to VRO_CPYFM, with the advantage that no fill planes have to be added before transformation (saving memory). The disadvantage seems to be (without blitter at least) a somewhat slower speed of operation. A difference that should be noted is that the copy mode word is defined differently for VRO_CPYFM and VRT_CPYFM. While the former uses its own 16 modes, VRT_CPYFM uses the standard four VDI write modes. This means that for plain copy you should use 3 with VRO_CPYFM but 1 with VRT_CPYFM. As a special further parameter with VRT_CPYFM are added VDI colour numbers for the ones and zeros of the image data. Parameters of VRT_CPYFM: Write mode: word = 1 for plain copy NOTE!!! Pointer to rectangle definition Pointer to source MFDB Pointer to destination MFDB (Pointer to) VDI colour numbers for ones and zeros. Colour numbers -------------- As is well known the (Atari) VDI uses its own mixed-up colour numbers. These numbers should never be used as indexes to the colours of an image file palette (which are ordered according the numbers formed - for each pixel - by the colour plane bits). Instead you should create a translation table: bitplane numbers -> VDI numbers. For this the function V_GET_PIXEL can be used - See the file IMG_IFF for a further discussion. * -- * PICPAC * ------------ * By: Mrten Lindstrm * -------------------------- * A collection of sub-routines for picture un/packing, including palette * handling routines, plus non-GEM routines to replace VDI image copy. * 1 tab per field. * Suggested tab length - constant: 10, variable: Double operand field *************************** * TT_VDO? Compares VDO_ value MSW with 2 *~~~~~~~~~~~~~~~~~~~~~~~~~~ (Called by SETTRU) * Affects NO REGISTERS. Only processor condition flags are set * according to the comparison of the MSW of the VDO_ value with 2. *~~~~~~~~~~~~~~~~~~~~~~~~~~ * Short version: (requires that the value of the _VDO cookie has been * copied to label '_vdo' earlier) *TT_VDO? cmpi.w #2,_vdo * rts *-------------------------- * Long version: TT_VDO? movem.l D0-D2/A0-A2/A6,-(SP) subq.w #2,SP Reserve space for word value lea (SP),A6 bsr.s tt_vdo?3 Push following address on stack and jump move.l $5A0.W,D0 beq.s tt_vdo?2 movea.l D0,A0 subq.l #4,A0 tt_vdo?1 addq.l #4,A0 move.l (A0)+,D0 beq.s tt_vdo?2 cmpi.l #'_VDO',D0 bne.s tt_vdo?1 Try next coookie move.w (A0),D0 Cookie Most Significant Word tt_vdo?2 move.w D0,(A6) rts tt_vdo?3 move.w #38,-(SP) SUPEXEC trap #14 subq.w #2,(A6) Compare cookie value MSW with 2 lea 2(A6),SP movem.l (SP)+,D0-D2/A0-A2/A6 rts *************************** * VDI_TRU TRU_VDI Routines to transform palettes * STE_TRU STE_VDI between three different formats: * TRU_STE VDI_STE ST(E) hardware, VDI and 24-bit *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A0->source, A1->dest, * D0.W: Number of colours *~~~~~~~~~~~~~~~~~~~~~~~~~~ STE_TRU movem.l D0-D3/A0-A1,-(SP) bra.s ste_tru3 ste_tru1 move.w (A0)+,D2 lsl.w #5,D2 First bit of Red -> X-bit moveq #2,D1 ste_tru2 rol.w #3,D2 Let next 3 bits slip by. addx.w D2,D2 First bit in from behind, and first move.b D2,D3 of next RGB-component out to X-bit. rol.b #4,D2 or.b D2,D3 To make full use of all 8 bits, copy move.b D3,(A1)+ high nybble to low nybble. dbf D1,ste_tru2 Next RGB component ste_tru3 dbf D0,ste_tru1 Next colour movem.l (SP)+,D0-D3/A0-A1 rts * VDI_TRU movem.l D0-D1/A0-A1,-(SP) mulu #3,D0 3 RGB components/colour bra.s vdi_tru2 vdi_tru1 move.w (A0)+,D1 ext.l D1 lsl.l #8,D1 Multiply by 256 divu #1001,D1 move.b D1,(A1)+ vdi_tru2 dbf D0,vdi_tru1 Next RGB component movem.l (SP)+,D0-D1/A0-A1 rts * STE_VDI movem.l D0-D1/A0-A1,-(SP) moveq #3,D1 mulu D0,D1 adda.w D1,A1 bsr.s STE_TRU lea (A1),A0 suba.l D1,A1 bra.s tru_vdi1 *========================== TRU_VDI movem.l D0-D1/A0-A1,-(SP) tru_vdi1 mulu #3,D0 bra.s tru_vdi3 tru_vdi2 moveq #0,D1 move.b (A0)+,D1 mulu #1000,D1 divu #255,D1 move.w D1,(A1)+ tru_vdi3 dbf D0,tru_vdi2 Next RGB component movem.l (SP)+,D0-D1/A0-A1 rts * VDI_STE movem.l D0-D1/A0-A2,-(SP) subq.l #4,SP move.w D0,D1 D1.W: Number of colours move.l A1,A2 A2 -> Destination bra.s vdi_ste2 vdi_ste1 lea (SP),A1 Set destination to stack moveq #1,D0 One colour bsr.s VDI_TRU lea 6(A0),A1 Save address to next source colour lea (SP),A0 Set source to stack bsr.s tru_ste3 Convert one colour to STE move.w D0,(A2)+ lea (A1),A0 Restore source address vdi_ste2 dbf D1,vdi_ste1 addq.l #4,SP movem.l (SP)+,D0-D1/A0-A2 rts * TRU_STE movem.l D0-D1/A0-A1,-(SP) move.l D0,D1 bra.s tru_ste2 tru_ste1 bsr.s tru_ste3 Convert one colour to STE addq.l #3,A0 move.w D0,(A1)+ STE colour tru_ste2 dbf D1,tru_ste1 movem.l (SP)+,D0-D1/A0-A1 rts *-------------------------- Convert 1 colour to STE. Also called by VDI_STE * and SETTRU. IN: A0->source 24 bit colour, OUT: D0.W: STE colour tru_ste3 addq.l #3,A0 Do components backwards: B,G,R move.l D1,-(SP) Save D1 moveq #2,D1 tru_ste4 move.b -(A0),D0 lsr.b #5,D0 Ignore 4 last bits. Next one to X ror.w #3,D0 roxr.w #1,D0 X-bit first dbf D1,tru_ste4 Next RGB component move.l (SP)+,D1 Restore D1 lsr.w #4,D0 rts *************************** * SETTRU Sets palette given in 24-bit format *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A0 -> source, * D0 MSW: Hardware start colour, * LSW: Number of colours *~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTRU movem.l D0-D4/A0-A3,-(SP) lea (A0),A3 A3 -> Source Palette move.l D0,D3 swap D3 D3.W: Start colour move.w D0,D4 D4.W: Number of colours bsr TT_VDO? Falcon Video? bls.s settru2 NO: ST/TT bhi.s settru5 YES * ST/TT settru1 lea (A3),A0 A0 is input for tru_ste3 addq.l #3,A3 bsr.s tru_ste3 Do one colour move.w D0,-(SP) Colour move.w D3,-(SP) Colour number addq.w #1,D3 move.w #7,-(SP) SETCOLOR trap #14 addq.l #6,SP settru2 dbf D4,settru1 Next colour bra.s settru6 EXIT * FALCON Set palette in rounds of 16 colours each * Depending on how much/little space you think you have on stack, the * number 16 can be changed (throughout routine) to any other number. settru3 lea -16*4(SP),SP Reserve space for palette lea (SP),A1 pea (A1) Address move.w D0,-(SP) Number of colours move.w D3,-(SP) Start colour add.w D0,D3 move.w #93,-(SP) VSETRGB (Falcon) subq.w #1,D0 settru4 clr.b (A1)+ Dummy byte move.b (A3)+,(A1)+ R move.b (A3)+,(A1)+ G move.b (A3)+,(A1)+ B dbf D0,settru4 trap #14 lea 16*4+10(SP),SP settru5 moveq #16,D0 sub.w D0,D4 bpl.s settru3 add.w D4,D0 bgt.s settru3 settru6 movem.l (SP)+,D0-D4/A0-A3 rts *************************** * IMGCALC LBMCALC DEGCALC NEOCALC Calculate space needed for unpacking *~~~~~~~~~~~~~~~~~~~~~~~~~~ (IMGCALC also converts PC IMG header to ST) * IMGUNP LBMUNP DEGUNP NEOUNP Unpacks image file *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A3 -> Loaded image file * D3 MSW: Type of palette wanted: -1 = 24-bit, 0=VDI * LSW: >0 = 'Min number of planes' (= # planes on screen). Absent * planes to be filled in. 0 = No fill planes to be used. * A4 -> Space for unpacked image (Only needed for the UNP routines) * OUT (CALC): * D0.L: Required size of block to be reserved for 'UNP' or -1 for error * D1.L = D0.L except for one plane pic. and D3.W>0 when D1.L is size * required without fill. Intended for VRT_CPYFM. * OUT (UNP): * D0.L: Size of image data or -1 for major error * D1 MSW <0 for minor error, LSW: =D3.W or =1 for one plane image * At (A4) a VDI MFDB for the unpacked image + extra info and palette *~~~~~~~~~~~~~~~~~~~~~~~~~~ IMGCALC movem.l D2-D7/A0-A3/A5-A6,-(SP) bsr.s imgcalc3 move.l D0,D1 Size of MFDB +xtra pars +palette bmi.s imgcalc2 error subq.w #1,D6 Number of planes? beq.s imgcalc1 movea.l A1,A5 imgcalc1 add.l A5,D1 add.l A1,D0 imgcalc2 movem.l (SP)+,D2-D7/A0-A3/A5-A6 rts * * Sub-routine to load registers with parameters read or calculated from * image file header. (In addition converts PC header to ST format.) * A4 untouched. See ___unp0 for further info. Also called by IMGUNP. imgcalc3 tst.b 4(A3) PC format? beq.s imgcalc6 NO Motorola * - - - - - - - - - - - - - YES: Transform header words from Intel lea (A3),A0 moveq #7,D1 imgcalc4 move.w (A0),D0 ror.w #8,D0 move.w D0,(A0)+ dbf D1,imgcalc4 moveq #-13,D1 add.w 2(A3),D1 bmi.s imgcalc6 addq.l #4,A0 Skip possible 'XIMG' byte string imgcalc5 move.w (A0),D0 ror.w #8,D0 move.w D0,(A0)+ dbf D1,imgcalc5 * - - - - - - - - - - - - - imgcalc6 lea 2(A3),A0 adda.w (A0),A3 adda.w (A0)+,A3 A3 -> Image data move.w (A0)+,D6 D6: Number of planes ble.s imgcalc7 move.w (A0)+,D7 D7: Pattern length ble.s imgcalc7 move.l (A0)+,A2 A2: Pixel dimensions move.l (A0)+,D5 D5 LSW: Number of lines bra ___unp0 MSW: Width in pixels * After this A0 points to possible source palette imgcalc7 moveq #-1,D0 error rts *========================== IMGUNP movem.l D1-D7/A0-A6,-(SP) bsr.s imgcalc3 Read header bmi imgunp27 error => exit move.l D3,(SP) st (SP) Minor error: Initiate to yes pea (A1) Save picture size in bytes bsr ___unp7 Fill in MFDB and extra parameters * - - - - - - - - - - - - - Do palette move.l D1,D0 Number of colours beq.s imgunp7 No palette cmpi.l #'XIMG',(A0)+ bne.s imgunp3 tst.w (A0)+ bne.s imgunp4 tst.l D3 Palette format wanted? bpl.s imgunp1 bsr VDI_TRU 24 bit palette bra.s imgunp7 imgunp1 subq.w #1,D0 VDI palette imgunp2 move.w (A0)+,(A1)+ move.w (A0)+,(A1)+ move.w (A0)+,(A1)+ dbf D0,imgunp2 bra.s imgunp7 imgunp3 subq.l #4,A0 cmpi.w #$80,(A0)+ HyperPaint? bne.s imgunp4 NO add.w D1,D1 YES, apparently add.l A0,D1 cmp.l A3,D1 'Palette' ranges into data? bls.s imgunp5 No, palette OK! imgunp4 lea imgunp28(PC),A0 No recognizable palette: Use the subq.w #1,D0 default one instead andi.w #$F,D0 addq.w #1,D0 Max 16 colours move.w D0,D1 add.w D1,D1 clr.w -2(A0,D1.W) Last colour = black imgunp5 tst.l D3 Source palette in STE format. bpl.s imgunp6 Convert to: bsr STE_TRU 24 bit bra.s imgunp7 imgunp6 bsr STE_VDI VDI * - - - - - - - - - - - - - Do the image data imgunp7 subq.l #4,SP Reserve room for A3 save lea (SP),A1 Save stack pointer moveq #1,D0 and.w D7,D0 add.w D7,D0 Pattern length evened up suba.w D0,SP Reserve! SP-> Pattern buffer subq.w #1,D7 D7: Bytes/pattern - 1 subq.w #1,D6 D6: Number of planes - 1 add.w D2,D2 sub.w D2,A6 A6: Pic Size - (#b/line evened up) lea (A4),A2 adda.w D4,A2 A2: End of line in destination move.w D6,D2 D2: Plane counter imgunp8 moveq #0,D3 Read first command of line moveq #0,D1 imgunp9 move.b (A3)+,D1 1st command byte bne.s imgunp12 0 move.b (A3)+,D1 2nd command byte bne.s imgunp16 0 0 move.b (A3)+,D1 3rd ditto not.b D1 should be $FF bne.s imgunp9 move.b (A3)+,D3 D3: Number of line repeats subq.b #1,D3 bcs.s imgunp9 If D3 was 0, skip command move.l A3,(A1) Save source pointer A3 imgunp10 movea.l (A1),A3 Next line repeat: Restore A3 imgunp11 moveq #0,D1 Read command within line move.b (A3)+,D1 1st command byte beq.s imgunp15 Pattern imgunp12 bgt.s imgunp22 Solid run with 0 andi.w #$7F,D1 bne.s imgunp21 Solid run with $FF move.b (A3)+,D1 Copy bytes: D1 = Number of bytes bra.s imgunp14 imgunp13 move.b (A3)+,(A4)+ imgunp14 dbf D1,imgunp13 bra.s imgunp25 imgunp15 move.b (A3)+,D1 Repeat pattern: D1 = times imgunp16 lea (SP),A0 move.w D7,D0 Pattern length - 1 imgunp17 move.b (A3)+,(A0)+ Read pattern dbf D0,imgunp17 bra.s imgunp20 imgunp18 lea (SP),A0 move.w D7,D0 Pattern length - 1 imgunp19 move.b (A0)+,(A4)+ Write pattern dbf D0,imgunp19 imgunp20 dbf D1,imgunp18 bra.s imgunp25 imgunp21 moveq #-1,D0 Solid run with $FF bra.s imgunp23 imgunp22 moveq #0,D0 Solid run with 0 imgunp23 subq.w #1,D1 imgunp24 move.b D0,(A4)+ dbf D1,imgunp24 imgunp25 cmpa.l A2,A4 Line end reached? bcs.s imgunp11 No => continue with next command bhi.s imgunp26 ERROR (Command broke line/plane limit) adda.l A5,A2 A2-> end of same line in next plane lea (A2),A4 suba.w D4,A4 A4 -> its beginning dbf D2,imgunp11 Do same line in next plane suba.l A6,A4 Back to plane 0 for new line lea (A4),A2 adda.w D4,A2 move.w D6,D2 subq.w #1,D5 Decrease line counter dbeq D3,imgunp10 Next line repeat bne imgunp8 Next line sf 8(A1) Done without error imgunp26 lea 4(A1),SP move.l (SP)+,D0 Return image length imgunp27 movem.l (SP)+,D1-D7/A0-A6 rts *-------------------------- imgunp28 dc.w $FFF,$F00,$0F0,$FF0,$00F,$F0F,$0FF,$555 default palette dc.w $333,$F33,$3F3,$FF3,$33F,$F3F,$3FF,$000 *~~~~~~~~~~~~~~~~~~~~~~~~~~ * ___unp0: branched to from IMGCALC, DEGCALC, LBMCALC etc. * calculates parameters * ___unp7: called from IMGUNP, DEGUNP, LBMUNP etc. * writes parameters in MFDB * ------------------------- * IN (___unp0): * D3 MSW: -1 = 24-bit palette, 0=VDI; LSW: Requested number of planes * D5 MSW: Width in pixels, LSW: Number of lines * D6.W: Number of planes * OUT (___unp0): * D0.L: Size of MFDB + extra block + palette, or -1 for error * D1.L: Number of colours, or 0 if more than 8 planes * D2.W: Width in words * D3: As input except LSW=1 if D6=1 * D4.W: Width in bytes * A1: Total picture size, incl. fill planes * A5: Size in bytes of one plane * A6: Total picture size, not incl. fill planes * Remaining register (ie D5,D6,D7, A0,A2,A3,A4) untouched * * IN (___unp7): Same as input AND OUTPUT from ___unp0 + * A4 -> Area for unpacked image * A2 MSW: Pixel width, LSW: Pixel height * OUT: A1-> area for palette, A4-> area for image data, D3.W cleared ___unp0 move.l D5,D4 swap D4 addq.w #7,D4 lsr.w #3,D4 D4: Width in bytes move.w D4,D0 addq.w #1,D0 lsr.w #1,D0 move.w D0,D2 D2: Width in words add.w D0,D0 mulu D5,D0 beq.s ___unp6 error movea.l D0,A5 A5: Size in bytes of one plane move.w D3,D1 # requested planes move.w D6,D0 # actual planes ble.s ___unp6 error subq.w #1,D0 bne.s ___unp1 move.w D6,D3 If # actual planes=1 then #req.pl=1 ___unp1 suba.l A6,A6 ___unp2 adda.l A5,A6 Total planes dbf D0,___unp2 movea.l A6,A1 A6: Total size of actual planes sub.w D6,D1 bls.s ___unp4 subq.w #1,D1 ___unp3 adda.l A5,A1 Add size of fill planes dbf D1,___unp3 A1: Total size incl. fill planes ___unp4 moveq #0,D0 moveq #0,D1 cmpi.w #8,D6 bhi.s ___unp5 direct colour RGB (no palette) moveq #1,D1 lsl.w D6,D1 D1: Number of colours move.w D1,D0 mulu #3,D0 tst.l D3 bmi.s ___unp5 add.w D0,D0 D0: Size of palette ___unp5 add.w #20+12,D0 + size of MFDB + extra info rts ___unp6 moveq #-1,D0 moveq #-1,D3 rts *~~~~~~~~~~~~~~~~~~~~~~~~~~ ___unp7 lea (A4),A1 adda.l D0,A4 A4 -> destination data * - - - - - - - - - - - - - Fill in MFDB move.l A4,(A1)+ Address move.l D5,(A1)+ Width in pixels, Height move.w D2,(A1)+ Width in words move.w #1,(A1)+ Flag for device independent format move.w D6,(A1)+ Initiate to actual number of planes clr.w (A1)+ clr.l (A1)+ * - - - - - - - - - - - - - and extra parameters move.w D6,(A1)+ Actual number of planes in file move.l A2,(A1)+ Pixel dimensions clr.w D3 move.l D3,(A1)+ Palette format; Start colour move.w D1,(A1)+ Number of colours rts * NEOCALC movem.l D2-D7/A0-A3/A5-A6,-(SP) bsr.s neocalc1 bra.s degcalc1 neocalc1 move.l (A3)+,D7 bra.s degcalc5 *========================== DEGCALC movem.l D2-D7/A0-A3/A5-A6,-(SP) bsr.s degcalc4 degcalc1 move.l D0,D1 Size of MFDB +xtra pars +palette bmi.s degcalc3 error subq.w #1,D6 Number of planes? beq.s degcalc2 movea.l A1,A5 degcalc2 add.l A5,D1 add.l A1,D0 degcalc3 movem.l (SP)+,D2-D7/A0-A3/A5-A6 rts * * Load registers with parameters read/calculated from file header. A4 un- * touched. ___unp0 for further info. Also called by DEGUNP/NEOCALC/NEOUNP. degcalc4 move.w (A3)+,D7 M.S.Bit of D7.W: Compression flag degcalc5 moveq #7,D0 and.w D7,D0 D0.W: Resolution move.b degcalc7(PC,D0.W),D0 bmi.s degcalc6 error (unknown resolution) lea degcalc8(PC),A0 adda.w D0,A0 move.w (A0)+,D6 D6.W: # planes move.l (A0)+,D5 D5.L: image dimensions move.l (A0)+,A2 A2: pixel dimensions lea (A3),A0 A0 -> palette bra ___unp0 degcalc6 moveq #-1,D0 error rts * ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Data for standard ST/TT resolutions degcalc7 dc.b 0,10,20,-1,30,-1,40,-1 (or replace last -1 with 50) degcalc8 dc.w 4,320,200,338,372 #planes,imagew,imageh,pixw,pixh dc.w 2,640,200,169,372 dc.w 1,640,400,372,372 dc.w 4,640,480,372,372 dc.w 1,1280,960,372,372 * dc.w 8,320,480,744,372 TT low * To use last line for TT low you would have to change routine below for * uncompressed image. (And TT low rez screen may be BYTE interleaved ?) * Pixel dimensions (for the ST) as given by the VDI (supposedly in microns) *========================== NEOUNP movem.l D1-D7/A0-A6,-(SP) bsr.s neocalc1 Read header lea 124(A3),A3 A3->image data bra.s degunp1 *========================== DEGUNP movem.l D1-D7/A0-A6,-(SP) bsr.s degcalc4 Read header lea 32(A3),A3 A3->image data degunp1 bmi degunp12 error => exit move.l D3,(SP) st (SP) initiate to error pea (A1) Save picture size bsr ___unp7 Fill in MFDB and extra parameters * - - - - - - - - - - - - - Do palette move.w D1,D0 Number of colours tst.l D3 bpl.s degunp2 bsr STE_TRU 24-bit palette bra.s degunp3 degunp2 bsr STE_VDI VDI palette * - - - - - - - - - - - - - Do the image data degunp3 subq.w #1,D6 D6: Number of planes - 1 tst.w D7 Compressed? bpl.s degunp13 *-------------------------- COMPRESSED DEGAS subq.w #1,D5 D5: Number of lines - 1 add.w D2,D2 sub.w D2,A6 A6: Pic Size - (#B/line evened up) degunp4 lea (A4),A2 adda.w D4,A2 A2-> End of line in destination move.w D6,D2 D2: Plane counter degunp5 moveq #0,D1 degunp6 move.b (A3)+,D1 1st command byte bpl.s degunp8 neg.b D1 bmi.s degunp6 $80 not used move.b (A3)+,D0 degunp7 move.b D0,(A4)+ dbf D1,degunp7 bra.s degunp9 degunp8 move.b (A3)+,(A4)+ dbf D1,degunp8 degunp9 cmpa.l A2,A4 Line end reached? bcs.s degunp5 No => next command bhi.s degunp11 Passed => EXIT adda.l A5,A2 A2-> end of same line in next plane lea (A2),A4 suba.w D4,A4 A4 -> its beginning dbf D2,degunp5 Do same line in next plane suba.l A6,A4 Back to plane 0 for new line dbf D5,degunp4 Next line degunp10 sf 4(SP) No error degunp11 move.l (SP)+,D0 Return image length degunp12 movem.l (SP)+,D1-D7/A0-A6 rts *-------------------------- UNCOMPRESSED DEGAS/NEO * As written can only handle up to 4 planes (i.e. not TT low) degunp13 mulu D5,D2 words per plane lea degunp16(PC),A6 sub.w D6,A6 sub.w D6,A6 bra.s degunp15 degunp14 adda.l A5,A4 degunp15 dbf D6,degunp14 lea (A4),A2 suba.l A5,A2 lea (A2),A1 suba.l A5,A1 lea (A1),A0 suba.l A5,A0 jmp (A6) move.w (A3)+,(A0)+ move.w (A3)+,(A1)+ move.w (A3)+,(A2)+ degunp16 move.w (A3)+,(A4)+ subq.l #1,D2 beq.s degunp10 jmp (A6) * LBMCALC movem.l D2-D7/A0-A3/A5-A6,-(SP) bsr.s lbmcalc3 move.l D0,D1 Size of MFDB +xtra pars +palette bmi.s lbmcalc2 error subq.w #1,D6 Number of planes? beq.s lbmcalc1 movea.l A1,A5 lbmcalc1 add.l A5,D1 add.l A1,D0 lbmcalc2 movem.l (SP)+,D2-D7/A0-A3/A5-A6 rts * * Load registers with parameters read/calculated from image file header. * A4 untouched. See ___unp0 for further info. Also called by LBMUNP. lbmcalc3 cmpi.l #'FORM',(A3) bne.s lbmcalc4 cmpi.l #'ILBM',8(A3) bne.s lbmcalc4 move.l #'BMHD',D0 bsr.s GETCHNK ble.s lbmcalc4 move.l (A0)+,D5 moveq #0,D6 addq.l #4,A0 move.b (A0),D6 move.l (A0)+,D7 moveq #0,D0 addq.l #2,A0 move.b (A0)+,D0 swap D0 move.b (A0)+,D0 movea.l D0,A2 bra ___unp0 lbmcalc4 moveq #-1,D0 error rts *~~~~~~~~~~~~~~~~~~~~~~~~~~ * GETCHNK Searches IFF FORM for chunk *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A3 -> FORM chunk to search, D0: ID to look for * OUT: A0 -> Found chunk data, D0: Length or -1 at error *~~~~~~~~~~~~~~~~~~~~~~~~~~ GETCHNK movem.l D1/A1,-(SP) lea 12(A3),A0 A0 -> start of FORM chunk data lea 4(A3),A1 adda.l (A1)+,A1 A1 -> end of it getchnk1 cmp.l (A0)+,D0 movem.l (A0)+,D1 beq.s getchnk2 found addq.l #1,D1 andi.b #$FE,D1 adda.l D1,A0 cmpa.l A1,A0 bcs.s getchnk1 next chunk moveq #-1,D1 getchnk2 move.l D1,D0 movem.l (SP)+,D1/A1 rts *========================== LBMUNP movem.l D1-D7/A0-A6,-(SP) bsr.s lbmcalc3 Read header bmi lbmunp21 error => exit move.l D3,(SP) sf (SP) Initiate to NO error pea (A1) Save picture size bsr ___unp7 Fill in MFDB and extra parameters * - - - - - - - - - - - - - Do palette move.l #'CMAP',D0 bsr.s GETCHNK ble lbmunp23 error move.w D1,D0 Number of colours tst.l D3 Palette format wanted? bpl.s lbmunp3 lbmunp1 subq.w #1,D0 lbmunp2 move.b (A0)+,(A1)+ 24 bit palette move.b (A0)+,(A1)+ move.b (A0)+,(A1)+ dbf D0,lbmunp2 bra.s lbmunp4 lbmunp3 bsr TRU_VDI VDI palette * - - - - - - - - - - - - - Do the image data lbmunp4 move.l #'BODY',D0 bsr.s GETCHNK A0 -> source image data ble lbmunp23 error subq.w #1,D6 D6: Number of planes - 1 subq.w #1,D5 D5: Number of lines - 1 sub.w D2,A6 sub.w D2,A6 A6: Pic Size - (#B/line evened up) lsr.w #8,D7 move.w D7,D0 swap D7 subq.b #1,D7 D7.B: 0 if there is mask subq.b #1,D0 Compression type? beq.s lbmunp10 1 : Line Byterun bhi lbmunp24 2+: Vertical? *-------------------------- 0 : UNCOMPRESSED IFF ILBM lbmunp5 sub.w D2,A5 sub.w D2,A5 Bytes per plane - 1 row subq.w #1,D2 words per row -1 lbmunp6 move.w D6,D1 lbmunp7 move.w D2,D0 lbmunp8 move.w (A0)+,(A4)+ dbf D0,lbmunp8 next word adda.l A5,A4 Same line, new plane dbf D1,lbmunp7 next plane tst.b D7 bne.s lbmunp9 No mask adda.w D4,A0 lbmunp9 suba.l A6,A4 Back to plane 0 for new line dbf D5,lbmunp6 next line bra.s lbmunp20 *-------------------------- PACKBITS COMPRESSED lbmunp10 lea (A4),A2 adda.w D4,A2 move.w D6,D2 lbmunp11 moveq #0,D1 lbmunp12 move.b (A0)+,D1 Command byte bpl.s lbmunp14 neg.b D1 bmi.s lbmunp12 $80 not used move.b (A0)+,D0 lbmunp13 move.b D0,(A4)+ dbf D1,lbmunp13 bra.s lbmunp15 lbmunp14 move.b (A0)+,(A4)+ dbf D1,lbmunp14 lbmunp15 cmpa.l A2,A4 Line end reached? bcs.s lbmunp11 No => continue with next command bhi.s lbmunp22 Passed => EXIT adda.l A5,A2 A2-> end of same line in next plane lea (A2),A4 suba.w D4,A4 A4 -> its beginning dbf D2,lbmunp11 Do same line in next plane * - - - - - - - - - - - - - Skip mask tst.b D7 bne.s lbmunp19 No mask move.w D4,D2 Bytes of one line moveq #0,D1 lbmunp16 move.b (A0)+,D1 Command byte bpl.s lbmunp17 neg.b D1 bmi.s lbmunp16 $80 not used bra.s lbmunp18 lbmunp17 add.w D1,A0 Instead of copy: Add D1+1 to A0 lbmunp18 addq.l #1,A0 Instead of repeat: Add 1 to A0 addq.w #1,D1 sub.w D1,D2 Subtract |D1+1| from bytes on line bhi.s lbmunp16 bcs.s lbmunp22 Line end passed => EXIT * - - - - - - - - - - - - - lbmunp19 suba.l A6,A4 Back to plane 0 for new line dbf D5,lbmunp10 Next line lbmunp20 move.l (SP)+,D0 Return image length lbmunp21 movem.l (SP)+,D1-D7/A0-A6 rts lbmunp22 st 4(SP) 'Minor error' bra.s lbmunp20 lbmunp23 addq.l #4,SP Skip saved image size moveq #-1,D0 Major error bra.s lbmunp21 *-------------------------- VERTICAL WORD COMPRESSED lbmunp24 subq.b #1,D0 Compression type 2? bne.s lbmunp23 No, "error" (higher than 2) lea -2(A5),A6 A6: Plane size less one word move.w D5,A3 A3: Line counter save move.w D2,D4 add.w D4,D4 D4: # Bytes/line evened up lbmunp25 cmpi.l #'VDAT',(A0)+ New plane bne.s lbmunp22 error move.l (A0)+,D7 add.l A0,D7 D7 -> End of VDAT lea (A0),A1 adda.w (A1)+,A0 A1 -> control bytes movea.l A4,A2 A0 -> word data adda.l A5,A2 A2 -> Next destination plane move.w D2,D3 D3: Word column counter lbmunp26 moveq #0,D1 Get command byte move.b (A1)+,D1 bmi.s lbmunp30 beq.s lbmunp29 subq.w #1,D1 bne.s lbmunp27 move.w (A0)+,D1 subq.w #1,D1 lbmunp27 move.w (A0)+,D0 lbmunp28 move.w D0,(A4) Repeat word adda.w D4,A4 subq.w #1,D5 dbcs D1,lbmunp28 bcc.s lbmunp34 suba.l A6,A4 Next word column move.w A3,D5 subq.w #1,D3 dbeq D1,lbmunp28 bra.s lbmunp33 lbmunp29 move.w (A0)+,D1 bra.s lbmunp31 lbmunp30 neg.b D1 lbmunp31 subq.w #1,D1 lbmunp32 move.w (A0)+,(A4) Copy word adda.w D4,A4 subq.w #1,D5 dbcs D1,lbmunp32 bcc.s lbmunp34 suba.l A6,A4 Next word column move.w A3,D5 subq.w #1,D3 dbeq D1,lbmunp32 lbmunp33 beq.s lbmunp35 lbmunp34 cmpa.l D7,A0 bcs.s lbmunp26 bra.s lbmunp36 lbmunp35 tst.w D1 beq.s lbmunp37 lbmunp36 st 4(SP) lbmunp37 movea.l D7,A0 and.w #1,D7 adda.w D7,A0 A0 -> Next VDAT lea (A2),A4 dbf D6,lbmunp25 Next plane bra lbmunp20 *************************** Updates number of planes in MFDB * PICFILL (at 12(A4)) and fills any absent *~~~~~~~~~~~~~~~~~~~~~~~~~~ (compared to D3 ie screen) planes * IN: A4-> unpacked image, D3: requested number of planes * Planes will be filled according to start colour at 28(A4) PICFILL movem.l D0-D4/A0,-(SP) move.w 20(A4),D0 D0: Actual number of planes move.w D3,12(A4) Requested number of planes sub.w D0,D3 D3: Number of fill planes ble.s picfill7 move.w 28(A4),D4 Start colour movem.w 6(A4),D1-D2 mulu D1,D2 D2: size of 1 plane in words move.l D2,D1 add.l D1,D1 D1: ditto in bytes movea.l (A4),A0 bra.s picfill2 picfill1 adda.l D1,A0 Skip actual planes lsr.w #1,D4 picfill2 dbf D0,picfill1 bra.s picfill6 picfill3 swap D1 picfill4 move.w D0,(A0)+ picfill5 dbf D1,picfill4 next word swap D1 dbf D1,picfill3 next 64 K words picfill6 lsr.w #1,D4 Shift last bit of colour number out scs D0 and extend it to word ext.w D0 move.l D2,D1 D1: size of 1 plane in words dbf D3,picfill5 next plane picfill7 movem.l (SP)+,D0-D4/A0 rts *************************** * PUTFM Non-VDI copy to screen *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A4 -> MFDB * A3 -> 8 Words: source X1,Y1,X2,Y2, dest X1,Y1,X2,Y2 (as VDI) * D0: Start colour (when # image planes < # screen planes * Only M.S.Bits, in excess of # image planes, noted) *~~~~~~~~~~~~~~~~~~~~~~~~~~ PUTFM movem.l D0-D7/A0-A6,-(SP) movem.l (A3),D3-D6 moveq #0,D7 Flag for PUT form move.l #$40FFFF,-(SP) BLITMODE, don't change trap #14 addq.l #4,SP and.b #1,D0 bne putfm30 Use blitter bsr putfm46 Calculate parameters beq putfm29 error: exit addq.l #2,A5 tst.w D1 bne.s putfm10 * FAST COPY WITH NO SHIFT putfm1 move.l D0,-(SP) M.S.Bit: Odd word flag movem.l A4,-(SP) MSW: # extra screen planes, LSW: Colour lea putfm7(PC),A4 bpl.s putfm2 lea putfm6-putfm7(A4),A4 putfm2 movem.l D6-D7/A5-A6,-(SP) Next plane putfm3 move.w (A5)+,D0 Next line and.w D2,D0 and.w D4,(A6) or.w D0,(A6) adda.w A3,A6 move.w (SP),D6 bra.s putfm5 putfm4 move.w (A5)+,(A6) adda.w A3,A6 move.w (A5)+,(A6) adda.w A3,A6 putfm5 dbf D6,putfm4 jmp (A4) putfm6 move.l (A5)+,(A6) adda.w A3,A6 putfm7 move.w (A5)+,D0 and.w D3,D0 and.w D5,(A6) or.w D0,(A6) putfm8 adda.l A1,A5 adda.l A2,A6 dbf D7,putfm3 movem.l (SP)+,D6-D7/A5-A6 adda.l A0,A5 addq.l #2,A6 putfm9 dbf D6,putfm2 move.w (SP),D6 # extra planes ble putfm28 suba.l A0,A5 suba.l A0,A0 move.w A0,(SP) Clear extra planes move.w 2(SP),D0 M.S.Bits of start colour bmi.s putfm9 Extra planes = last plane repeated bra.s putfm19 Extra planes filled with D0 bits * SHIFTED COPY putfm10 move.l D0,-(SP) M.S.Bit: Odd word flag movem.l A4,-(SP) MSW: # extra screen planes, LSW: Colour lea putfm16(PC),A4 bpl.s putfm11 lea putfm15-putfm16(A4),A4 putfm11 movem.l D6-D7/A0/A5-A6,-(SP) Next plane putfm12 lea -2(A5),A0 Next line move.l (A0)+,D0 lsr.l D1,D0 and.w D2,D0 and.w D4,(A6) or.w D0,(A6) adda.w A3,A6 move.w (SP),D6 bra.s putfm14 putfm13 move.l (A5)+,D0 lsr.l D1,D0 move.w D0,(A6) adda.w A3,A6 move.l (A0)+,D0 lsr.l D1,D0 move.w D0,(A6) adda.w A3,A6 putfm14 dbf D6,putfm13 jmp (A4) putfm15 move.l (A5),D0 lea (A0),A5 lsr.l D1,D0 move.w D0,(A6) adda.w A3,A6 putfm16 move.l (A5)+,D0 lsr.l D1,D0 and.w D3,D0 and.w D5,(A6) or.w D0,(A6) putfm17 adda.l A1,A5 adda.l A2,A6 dbf D7,putfm12 movem.l (SP)+,D6-D7/A0/A5-A6 adda.l A0,A5 addq.l #2,A6 putfm18 dbf D6,putfm11 move.w (SP),D6 # extra planes ble.s putfm28 suba.l A0,A5 suba.l A0,A0 move.w A0,(SP) Clear extra planes move.w 2(SP),D0 M.S.Bits of start colour bmi.s putfm18 Repeat last plane for extra planes * Fill extra planes according to D0 bits putfm19 move.w D6,(SP) # planes move.w 6(SP),A4 # words to do per line subq.w #2,A4 movea.w D7,A5 putfm20 move.w A5,D7 lsr.w #1,D0 scs D1 ext.w D1 beq.s putfm24 putfm21 or.w D2,(A6) adda.w A3,A6 move.w A4,D6 bra.s putfm23 putfm22 move.w D1,(A6) adda.w A3,A6 putfm23 dbf D6,putfm22 or.w D3,(A6) adda.l A2,A6 dbf D7,putfm21 bra.s putfm27 putfm24 and.w D4,(A6) adda.w A3,A6 move.w A4,D6 bra.s putfm26 putfm25 move.w D1,(A6) adda.w A3,A6 putfm26 dbf D6,putfm25 and.w D5,(A6) adda.l A2,A6 dbf D7,putfm24 putfm27 addq.l #2,A6 subq.w #1,(SP) bgt.s putfm20 * putfm28 addq #8,SP putfm29 movem.l (SP)+,D0-D7/A0-A6 rts * Blitter version of PUTFM putfm30 move.l (SP),A6 Get start colour bsr putfm38 (Push next address on stack and jump) move.l A6,-(SP) and save it bsr putfm46 beq putfm37 addq.w #1,D7 Y count move.w #$C080,D5 tst.w D1 bne.s putfm31 clr.b D5 addq.l #2,A5 addq.w #2,A1 putfm31 or.b D1,D5 cmp.w #1,D0 bne.s putfm32 addq.w #2,A1 adda.w A3,A2 putfm32 swap D3 move.w A3,D3 swap D0 move.w D7,D0 move.l A4,D4 MSW: Xtra planes LSW: Start colour lea $FFFF8A20.W,A3 move.w #$203,D1 putfm33 bsr.s putfm39 adda.l A0,A5 addq.l #2,A6 dbf D6,putfm33 move.l D4,D6 swap D6 Extra screen planes subq.w #1,D6 bmi putfm37 suba.l A0,A5 tst.w D4 bpl.s putfm35 putfm34 bsr.s putfm39 addq.l #2,A6 dbf D6,putfm34 bra.s putfm37 putfm35 lsr.w D4 scs D1 ext.w D1 ext.l D1 lea -32(A3),A4 moveq #7,D7 putfm36 move.l D1,(A4)+ dbf D7,putfm36 move.w #$103,D1 bsr.s putfm39 addq.l #2,A6 dbf D6,putfm35 putfm37 addq.l #4,SP rts putfm38 move.w #38,-(SP) SUPEXEC trap #14 addq.l #6,SP bra putfm29 * putfm39 lea (A3),A4 LOAD BLITTER: move.w #2,(A4)+ Source X increment move.w A1,(A4)+ Source Y increment move.l A5,(A4)+ Source address move.w D2,(A4)+ Endmask 1 move.w #-1,(A4)+ Endmask 2 move.l D3,(A4)+ Endmask 3, Dest X incr move.w A2,(A4)+ Destination Y increment move.l A6,(A4)+ Destination address move.l D0,(A4)+ X count, Y count move.w D1,(A4)+ HOP op, logical op move.w D5,(A4)+ Busy+HOG, FXSR+Rshift rts * Check rectangle dimension putfm40 addq.w #1,D6 X2/Y2 on screen cmp.w A3,D6 Compare with W/H of screen bls.s putfm41 move.w A3,D6 putfm41 tst.w D5 X1/Y1 on screen bpl.s putfm42 clr.w D5 putfm42 sub.w D5,D6 X2/Y2 - X1/Y1 on screen = W/H addq.w #1,D4 X2/Y2 in raster cmp.w A1,D4 Compare with W/H of raster bls.s putfm43 move.w A1,D4 putfm43 tst.w D3 X1/Y1 in raster bpl.s putfm44 clr.w D3 putfm44 sub.w D3,D4 X2/Y2 - X1/Y1 in raster = W/H cmp.w D4,D6 bls.s putfm45 move.w D4,D6 D6: Min. W/H putfm45 rts * putfm46 move.w #3,-(SP) LOGBASE trap #14 addq.l #2,SP move.l D0,A6 A6 -> Logical screen dc.w $A000 Line-A Init * (A0): #planes, -2(A0) Bytes per line, -4(A0) # screen lines * -12(A0): width in pixels movea.l (A4)+,A5 Raster pointer * - - - - - - - - - - - - - move.w (A4)+,D0 W of raster move.w (A4)+,A1 H of raster move.w -4(A0),A3 H of screen bsr.s putfm40 move.w D6,D7 Height beq putfm53 subq.w #1,D7 move.w -2(A0),A2 Bytes per screen line move.w A2,D2 mulu D5,D2 times Y on screen add.l D2,A6 added to screen address move.w (A4)+,D2 words per raster line tst.w (A4)+ beq putfm53 Abort if MFDB 'device specific' add.w D2,D2 D2: bytes per raster line move.w D3,D1 D1: Y i raster mulu D2,D1 Y-offs (B/linje * Y in raster) add.l D1,A5 * - - - - - - - - - - - - - swap D3 swap D4 swap D5 swap D6 exg D0,A1 W of raster in A1 move.w -12(A0),A3 W of screen bsr.s putfm40 move.w D5,D1 X on screen - X in raster sub.w D3,D1 D1: X of raster rel. to screen asr.w #4,D3 X in raster in words, truncated add.w D3,D3 X in bytes, rounded down to word adda.w D3,A5 move.w D5,D4 lsr.w #4,D4 X on screen in words, truncated add.w D4,D4 X in bytes, rounded down to word tst.l D7 bpl.s putfm47 neg.w D1 For GETFM (from screen to raster) move.w D3,D5 make D1 and D5 raster relative. putfm47 move.w (A0),D3 D3: nplanes mulu D3,D4 adda.w D4,A6 A6 -> start address on screen moveq #15,D4 add.w D4,D6 Width+15 and.w D5,D4 D4: Xfrac add.w D4,D6 Width + Xfrac + 15 moveq #15,D5 and.w D5,D1 D1: Right shift cmp.w D4,D1 bhi.s putfm49 tst.l D7 bpl.s putfm48 sub.w D3,A6 sub.w D3,A6 bra.s putfm49 putfm48 subq.l #2,A5 putfm49 and.w D6,D5 D5: X2frac lsr.w #4,D6 D6: Number of words to do per row beq.s putfm53 * - - - - - - - - - - - - - move.w D2,A1 A1: Bytes per raster line mulu D0,D2 times raster height (in D0) movea.l D2,A0 A0: Bytes per raster plane move.w (A4),D2 D2: # raster planes move.w D3,D0 sub.w D2,D0 D0: # screen planes - # raster ones bpl.s putfm50 add.w D0,D2 Less screen planes than raster ones moveq #0,D0 tst.l D7 bpl.s putfm50 move.w D2,(A4) For GETFM set new #planes in MFDB putfm50 swap D0 D0 MSW: Extra screen planes move.w 6(SP),D0 LSW: Start colour asr.w D2,D0 shifted with # raster planes move.l D0,A4 add.w D3,D3 move.w D3,A3 A3: number of screen planes *2 mulu D6,D3 # Screen planes * (# Bytes to do) sub.w A3,D3 - # Screen planes * 2 suba.w D3,A2 A2: Screen line offset suba.w D6,A1 suba.w D6,A1 A1: Raster line offset add.w D4,D4 move.w putfm54(PC,D4.W),D4 Left mask add.w D5,D5 move.w putfm55(PC,D5.W),D5 Right mask moveq #0,D0 move.w D6,D0 lsr.w #1,D6 Number of longwords to do per row bne.s putfm52 putfm51 subq.w #2,A1 1 word sub.w A3,A2 or.w D5,D4 moveq #-1,D5 addq.w #1,D6 (This should clear X-bit) putfm52 roxr.l #1,D0 D0 M.S.Bit: Flag for odd word addx.w D0,D0 subq.w #1,D6 swap D6 move.w D2,D6 Number of planes subq.w #1,D6 move.w D5,D3 not.w D3 move.w D4,D2 not.w D2 Non-zero if OK putfm53 rts Zero-flag set if error *-------------------------- putfm54 dc.w $0000,$8000,$C000,$E000,$F000,$F800,$FC00,$FE00 dc.w $FF00,$FF80,$FFC0,$FFE0,$FFF0,$FFF8,$FFFC,$FFFE putfm55 dc.w $7FFF,$3FFF,$1FFF,$0FFF,$07FF,$03FF,$01FF,$00FF dc.w $007F,$003F,$001F,$000F,$0007,$0003,$0001,$0000 *~~~~~~~~~~~~~~~~~~~~~~~~~~ * GETFM Non-VDI copy from screen *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: A4 -> MFDB * A3 -> 8 Words: source X1,Y1,X2,Y2, dest X1,Y1,X2,Y2 (as VDI) *~~~~~~~~~~~~~~~~~~~~~~~~~~ GETFM movem.l D0-D7/A0-A6,-(SP) move.l (A3)+,D5 move.l (A3)+,D6 move.l (A3)+,D3 move.l (A3)+,D4 moveq #-1,D7 Flag for GET form move.l #$40FFFF,-(SP) BLITMODE, don't change trap #14 addq.l #4,SP and.b #1,D0 bne.s getfm6 Use blitter bsr putfm46 beq.s getfm5 Exit suba.l A3,A2 subq.w #2,D0 words to do per line -2 move.w D0,A4 getfm1 movem.l D6-D7/A0/A5-A6,-(SP) Next plane getfm2 move.l (A6),D0 Next line adda.w A3,A6 move.w (A6),D0 lsr.l D1,D0 and.w D2,D0 and.w D4,(A5) or.w D0,(A5)+ move.w A4,D6 bra.s getfm4 getfm3 move.l (A6),D0 Next 'middle' (unmasked) word adda.w A3,A6 move.w (A6),D0 lsr.l D1,D0 move.w D0,(A5)+ getfm4 dbf D6,getfm3 move.l (A6),D0 adda.w A3,A6 move.w (A6),D0 lsr.l D1,D0 and.w D3,D0 and.w D5,(A5) or.w D0,(A5)+ adda.l A1,A5 adda.l A2,A6 dbf D7,getfm2 movem.l (SP)+,D6-D7/A0/A5-A6 adda.l A0,A5 addq.l #2,A6 dbf D6,getfm1 getfm5 movem.l (SP)+,D0-D7/A0-A6 rts * Blitter version of GETFM getfm6 bsr.s getfm11 bsr putfm46 beq.s getfm10 addq.w #1,D7 Y count move.l #$203C080,D5 tst.w D1 bne.s getfm7 clr.b D5 add.w A3,A6 add.w A3,A2 getfm7 or.b D1,D5 cmp.w #1,D0 bhi.s getfm8 addq.w #2,A1 adda.w A3,A2 getfm8 swap D0 move.w D7,D0 getfm9 lea $FFFF8A20.W,A4 LOAD BLITTER: move.w A3,(A4)+ Source X increment move.w A2,(A4)+ Source Y increment move.l A6,(A4)+ Source address move.w D2,(A4)+ Endmask 1 move.w #-1,(A4)+ Endmask 2 move.w D3,(A4)+ Endmask 3 move.w #2,(A4)+ Dest X incr move.w A1,(A4)+ Destination Y increment move.l A5,(A4)+ Destination address move.l D0,(A4)+ X count, Y count move.l D5,(A4)+ HOP,log,Busy+HOG,FXSR+Rshft adda.l A0,A5 addq.l #2,A6 dbf D6,getfm9 getfm10 rts getfm11 move.w #38,-(SP) SUPEXEC trap #14 addq.l #6,SP bra.s getfm5 *************************** * IMGPAC LBMPAC Pack images to files of types *~~~~~~~~~~~~~~~~~~~~~~~~~~ IMG and IFF ILBM * IN: * A4 -> MFDB + extra parameters * A3 -> Space for file * D0 for LBM: Compression type (0-2), for IMG: Pattern length (1 or 2) * OUT: D0: File length or -1 for error *~~~~~~~~~~~~~~~~~~~~~~~~~~ ___pac lea 20(A4),A0 READ PARAMETERS move.w (A0)+,D6 D6: # planes movea.l (A4)+,A2 A2: Image data move.w (A4),D4 addq.w #7,D4 lsr.w #3,D4 D4: Image width in bytes move.l (A4)+,D5 D5: Image height (and width in MSW) move.w (A4)+,D2 D2: Width in words move.w D2,D1 add.w D1,D1 Width in bytes evened up move.w D1,D3 mulu D5,D1 movea.l D1,A5 A5: Bytes per plane mulu D6,D3 mulu D5,D3 movea.l D3,A6 A6: Bytes of whole picture movem.l (A0)+,D1/D3 D1: pixel dimensions rts D3: Palette format (and start colour) * A0 -> #colours and palette *========================== IMGPAC movem.l D0-D7/A0-A6,-(SP) bsr.s ___pac Read parameters bgt.s imgpac2 addq.l #4,SP imgpac1 moveq #-1,D0 Error: quick exit movem.l (SP)+,D1-D7/A0-A6 * - - - - - - - - - - - - - imgpac2 move.l #$10008,(A3)+ (File v1); Palette-less header: 8 words move.w D6,(A3)+ # planes move.w D0,(A3)+ pattern length move.l D1,(A3)+ Pixel dimensions move.l D5,(A3)+ Image width and height move.w (A0)+,D0 Number of colours beq.s imgpac7 * - - - - - - - - - - - - - SKIP PALETTE FOR WHITE-BLACK IMG cmp.b #2,D0 If # colours is 2, a palette is bne.s imgpac4 probably best left out of an lea (A0),A1 IMG file tst.l D3 bpl.s imgpac3 addq.l #3,A1 move.b (A1)+,D1 or.b (A1)+,D1 or.b (A1)+,D1 beq.s imgpac7 at least if 2nd colour is black bra.s imgpac4 imgpac3 addq.l #6,A1 move.w (A1)+,D1 or.w (A1)+,D1 or.w (A1)+,D1 beq.s imgpac7 * - - - - - - - - - - - - - DO PALETTE imgpac4 move.w D0,D1 add.w D0,D1 add.w D0,D1 # words in palette addq.w #3,D1 + XIMG palette header add.w D1,-14(A3) Add to file header length move.l #'XIMG',(A3)+ clr.w (A3)+ tst.l D3 bpl.s imgpac5 lea (A3),A1 bsr TRU_VDI subq.w #3,D1 add.w D1,D1 # bytes in palette add.w D1,A3 bra.s imgpac7 imgpac5 subq.w #1,D0 imgpac6 move.w (A0)+,(A3)+ move.w (A0)+,(A3)+ move.w (A0)+,(A3)+ dbf D0,imgpac6 * - - - - - - - - - - - - - imgpac7 move.l (SP)+,D0 sub.w D4,A5 Plane size minus one line add.w D2,D2 sub.w D2,A6 Image size minus one evened up line move.w D2,-(SP) Save line offset within plane subq.w #1,D6 # planes -1 subq.w #1,D5 # lines -1 subq.w #1,D4 # bytes/line -1 move.w D4,A4 subq.b #2,D0 bhi imgpac1 Can't handle pattern length > 2 * - - - - - - - - - - - - - Check IMG line repeat imgpac8 lea (A2),A0 lea (A2),A1 add.w (SP),A1 move.w D5,D7 moveq #1,D1 bra.s imgpac11 imgpac9 move.w A4,D4 imgpac10 cmpm.b (A0)+,(A1)+ dbne D4,imgpac10 bne.s imgpac12 adda.l A5,A0 Same line, next plane adda.l A5,A1 dbf D2,imgpac9 suba.l A6,A0 suba.l A6,A1 lea (A0),A2 imgpac11 move.w D6,D2 addq.b #1,D1 dbcs D7,imgpac9 imgpac12 subq.b #2,D1 beq.s imgpac13 No repeat sub.w D1,D5 addq.b #1,D1 sf (A3)+ sf (A3)+ st (A3)+ move.b D1,(A3)+ * - - - - - - - - - - - - - imgpac13 move.w D6,D2 imgpac14 move.w A4,D4 imgpac15 lea (A3),A1 A1 -> Start of literal sequence addq.l #2,A3 moveq #0,D3 D3: Bytes in current sequence * imgpac16 move.b D1,D7 LITERAL COPY RUN: move.b (A2)+,D1 beq.s imgpac17 If D1 = %00000000 cmp.b #-1,D1 or %11111111 bne.s imgpac18 imgpac17 tst.b D3 and if current literal sequence beq.s imgpac31 was just started cmp.b D1,D7 bne.s imgpac18 cmpi.b #2,D3 bcs.s imgpac30 tst.b D4 or about to be finished beq.s imgpac30 cmp.b (A2),D1 or if 3+ of 'solid' bytes beq.s imgpac30 then use 'solid run'! imgpac18 cmp.b -4(A2),D7 Try possible pattern match: 1st bne.s imgpac21 cmp.b -3(A2),D1 and 2nd byte. bne.s imgpac21 cmpi.b #2,D3 At least 3 bytes must have preceded bls.s imgpac21 for this to be valid cmp.b (A2),D7 Try a further pattern repeat bne.s imgpac21 (A pattern usually needs to be tst.b D0 bne.s imgpac36 (1 byte pattern repeated 5 times) cmp.b 1(A2),D1 repeated 3 times to be economical) bne.s imgpac21 bsr.s imgpac27 End literal copy sequence moveq #1,D3 Include previous 2-byte pattern subq.l #3,A3 addq.w #1,D4 bra imgpac48 To pattern run! imgpac19 bsr.s imgpac35 imgpac20 lea (A3),A1 A1 -> Start of literal sequence addq.l #2,A3 moveq #0,D3 imgpac21 move.b D1,(A3)+ addq.b #1,D3 imgpac22 dbcs D4,imgpac16 Next byte (LITERAL) bcs.s imgpac23 bsr.s imgpac26 bra imgpac49 * Break off sequence at $7F/$FF bytes imgpac23 subq.w #1,D3 Back 1 byte to $FF bsr.s imgpac26 End literal copy run subq.l #1,A3 imgpac24 subq.l #1,A2 Back 1 byte imgpac25 bra.s imgpac15 Skip dbf * End literal copy sequence imgpac26 addq.b #3,D3 Include last bytes in sequence imgpac27 subq.b #2,D3 or deduct three imgpac28 subq.b #1,D3 or one bls.s imgpac29 Cancel sequence if empty move.b #$80,(A1)+ move.b D3,(A1) rts imgpac29 subq.l #2,A3 rts * Solid run imgpac30 subq.l #1,A3 imgpac31 bsr.s imgpac28 End literal sequence move.b D1,D7 addq.b #1,D3 beq.s imgpac33 moveq #1,D3 bra.s imgpac33 imgpac32 move.b (A2)+,D1 SOLID RUN cmp.b D1,D7 bne.s imgpac19 imgpac33 addq.b #1,D3 dbmi D4,imgpac32 Next byte bmi.s imgpac34 $80 bytes are more than allowed bsr.s imgpac35 bra.s imgpac49 imgpac34 subq.w #1,D3 Back 1 byte to $7F bsr.s imgpac35 bra.s imgpac24 imgpac35 andi.b #$80,D7 End solid run or.b D7,D3 move.b D3,(A3)+ rts * Pattern run - 1-byte pattern imgpac36 cmp.b D1,D7 bne.s imgpac21 bsr.s imgpac27 End literal copy sequence moveq #3,D3 Include previous 3 bytes subq.l #3,A3 bra.s imgpac41 imgpac37 bsr.s imgpac38 bra.s imgpac20 imgpac38 clr.b (A3)+ End pattern run move.b D3,(A3)+ move.b D7,(A3)+ rts imgpac39 subq.w #1,D3 Back 1 pattern to $FF bsr.s imgpac38 End pattern run bra.s imgpac24 imgpac40 move.b (A2)+,D1 1-BYTE PATTERN RUN cmp.b D1,D7 bne.s imgpac37 imgpac41 addq.b #1,D3 dbcs D4,imgpac40 Next byte bcs.s imgpac39 $100 bytes are more than allowed bsr.s imgpac38 bra.s imgpac49 * Pattern run - 2-byte pattern imgpac42 subq.w #1,D3 Back 1 pattern to $FF subq.l #2,A2 = 2 bytes addq.w #1,D4 Total 2 regained by adding 1 + skipping dbf imgpac43 bsr.s imgpac44 bra.s imgpac25 imgpac44 clr.b (A3)+ End pattern run move.b D3,(A3)+ move.b D7,(A3)+ move.b D1,(A3)+ rts imgpac45 subq.l #1,A2 imgpac46 bsr.s imgpac44 move.b -1(A2),D1 bra imgpac20 imgpac47 cmp.b (A2)+,D7 PATTERN RUN: Compare 1st bne.s imgpac46 cmp.b (A2)+,D1 and 2nd byte of pattern bne.s imgpac45 imgpac48 addq.b #1,D3 bcs.s imgpac42 subq.w #2,D4 bhi.s imgpac47 Next pattern beq.s imgpac43 bsr.s imgpac44 * imgpac49 adda.l A5,A2 Same line, next plane dbf D2,imgpac14 suba.l A6,A2 dbf D5,imgpac8 addq.l #2,SP move.l A3,D0 movem.l (SP)+,D1-D7/A0-A6 sub.l A3,D0 rts *========================== LBMPAC movem.l D0-D7/A0-A6,-(SP) move.l #'FORM',(A3)+ addq.l #4,A3 move.l #'ILBM',(A3)+ move.l #'BMHD',(A3)+ moveq #20,D1 move.l D1,(A3)+ bsr ___pac Read parameters bgt.s lbmpac2 addq.l #4,SP lbmpac1 moveq #-1,D0 Error: quick exit movem.l (SP)+,D1-D7/A0-A6 * - - - - - - - - - - - - - lbmpac2 move.l D5,(A3)+ Image width and height clr.l (A3)+ Image X & Y offset move.b D6,(A3)+ # planes clr.b (A3)+ No mask move.b D0,(A3)+ Compression type clr.b (A3)+ Reserved dummy byte clr.w (A3)+ transparent colour bsr WBRATIO Make 2-word ratio fit in 2 bytes move.b D0,(A3)+ Pixel width move.b D1,(A3)+ Pixel height move.l D5,(A3)+ Set page dimensions = image dim. move.w (A0)+,D0 Number of colours beq.s lbmpac5 move.l #'CMAP',(A3)+ move.w D0,D1 mulu #3,D1 move.l D1,(A3)+ tst.l D3 bmi.s lbmpac3 lea (A3),A1 bsr VDI_TRU add.l D1,A3 bra.s lbmpac5 lbmpac3 subq.w #1,D0 lbmpac4 move.b (A0)+,(A3)+ move.b (A0)+,(A3)+ move.b (A0)+,(A3)+ dbf D0,lbmpac4 lbmpac5 move.l #'BODY',(A3)+ move.l (SP)+,D0 addq.l #4,A3 move.l A3,-(SP) Save address to BODY start subq.w #1,D5 # lines -1 subq.w #1,D6 # planes -1 subq.w #2,D0 bhi.s lbmpac1 beq lbmpac27 2 = Vertical word compression add.w D2,D2 sub.w D2,A6 Image size minus one evened up line addq.w #1,D0 bne lbmpac23 0 = No compression sub.w D4,A5 Plane size minus one line subq.w #1,D4 # bytes/line -1 move.w D4,A4 *-------------------------- 1 = PACKBITS COMPRESSION lbmpac6 move.w D6,D2 lbmpac7 move.w A4,D4 move.b (A2)+,D1 bra.s lbmpac18 * lbmpac8 move.b D1,D7 move.b (A2)+,D1 cmp.b D1,D7 bne.s lbmpac19 cmp.b (A2),D7 You usually need three bytes in a bne.s lbmpac19 repeat seq. to make it economical tst.b D3 beq.s lbmpac19 * When a byte = previous (and subsequent) byte: bsr.s lbmpac12 moveq #1,D3 lbmpac9 addq.b #1,D3 dbmi D4,lbmpac17 bmi.s lbmpac14 bsr.s lbmpac10 bra.s lbmpac21 * End repeat sequence lbmpac10 subq.b #1,D3 neg.b D3 move.b D3,-1(A3) move.b D7,(A3)+ rts * End literal sequence lbmpac11 addq.b #1,D3 Include last byte in sequence lbmpac12 subq.b #2,D3 or don't (and subtract 1 for dbf) bmi.s lbmpac13 Cancel sequence if empty move.b D3,(A1) rts lbmpac13 subq.l #1,A3 rts * Cut sequence at $80 bytes lbmpac14 bsr.s lbmpac10 bra.s lbmpac16 lbmpac15 bsr.s lbmpac11 lbmpac16 lea (A3),A1 A1 -> Start of literal sequence addq.l #1,A3 moveq #0,D3 bra.s lbmpac20 * lbmpac17 move.b (A2)+,D1 cmp.b D1,D7 beq.s lbmpac9 * When a byte <> previous byte: bsr.s lbmpac10 lbmpac18 lea (A3),A1 A1 -> Start of literal sequence addq.l #1,A3 moveq #0,D3 D3: Bytes in current sequence lbmpac19 move.b D1,(A3)+ addq.b #1,D3 lbmpac20 dbmi D4,lbmpac8 bmi.s lbmpac15 bsr.s lbmpac11 * lbmpac21 adda.l A5,A2 Same line, next plane dbf D2,lbmpac7 suba.l A6,A2 dbf D5,lbmpac6 *-------------------------- lbmpac22 move.l (SP)+,A0 Start of BODY move.l A3,D0 suba.l A0,A3 move.l A3,-(A0) Length of BODY chunk movem.l (SP)+,D1-D7/A0-A6 sub.l A3,D0 subq.l #8,D0 move.l D0,4(A3) Length of whole ILBM FORM addq.l #8,D0 Length of file rts *-------------------------- 0 = UNCOMPRESSED IFF ILBM lbmpac23 sub.w D2,A5 Plane size minus one evened up line subq.w #1,D4 lsr.w #1,D4 D4: # words/line -1 lbmpac24 move.w D6,D2 plane counter lbmpac25 move.w D4,D1 word counter lbmpac26 move.w (A2)+,(A3)+ dbf D1,lbmpac26 adda.l A5,A2 Same line, next plane dbf D2,lbmpac25 suba.l A6,A2 dbf D5,lbmpac24 Next line bra.s lbmpac22 *-------------------------- 2 = VERTICAL WORD COMPRESSION lbmpac27 move.w D5,A6 move.w D2,D4 add.w D4,D4 Image width in bytes evened up subq.w #1,D2 move.l A5,D7 addq.l #5,D7 Calculate worst case space needed divs #3,D7 for control bytes bvc.s lbmpac28 moveq #-1,D7 Max. = lbmpac28 andi.w #$7FFE,D7 32K - 2 subq.w #2,A5 # Bytes/plane - 2 movem.w D2/D7,-(SP) *>>>>>>>>>>>>>>>>>>>>>>>>>> VDAT lbmpac29 movem.w (SP),D2/A4 #words/line, space for ctrl bytes move.l #"VDAT",(A3)+ move.l A3,-(SP) addq.l #6,A3 A3 -> control bytes adda.l A3,A4 A4 -> word data move.l A4,-(SP) move.w (A2),D1 Read first word bra.s lbmpac45 * Literal copy loop lbmpac30 suba.l A5,A2 Move A2 to next column start lbmpac31 move.w D1,D7 D7: previous word adda.w D4,A2 move.w (A2),D1 Read word cmp.w D1,D7 bne.s lbmpac46 Write it if previous word <> it tst.w D3 beq.s lbmpac46 ... or not valid * When a word = previous word: bsr.s lbmpac35 End literal sequence and start moveq #1,D3 repeat seq. counted from prev. word lbmpac32 addq.w #1,D3 dbcs D5,lbmpac44 Next word (REPEAT) bcs.s lbmpac40 At $10000 words, cut sequence move.w A6,D5 dbf D2,lbmpac43 Next column (REPEAT) bsr.s lbmpac33 bra.s lbmpac48 * End repeat sequence lbmpac33 cmp.w #128,D3 bhs.s lbmpac34 If count > 127: word format move.b D3,(A3)+ rts lbmpac34 move.b #1,(A3)+ Repeated word, count in word format move.w D3,-2(A4) Count move.w D7,(A4)+ and data word rts * End literal sequence lbmpac35 subq.w #1,D3 Prev. word to new repeat sequence lbmpac36 beq.s lbmpac37 Exit if no words in old sequence cmp.w #128,D3 bhi.s lbmpac38 If count > 128: word format neg.b D3 move.b D3,(A3)+ lbmpac37 rts lbmpac38 clr.b (A3)+ Literal run, count in word format addq.l #2,A4 lea (A4),A0 move.w D3,D0 lbmpac39 move.w -4(A0),-(A0) Move whole sequence one word ... dbf D0,lbmpac39 move.w D3,(A1) ... so that count can be inserted rts * Cut sequence at $10000 words lbmpac40 bsr.s lbmpac34 bra.s lbmpac42 lbmpac41 bsr.s lbmpac38 lbmpac42 lea (A4),A1 A1 -> Start of literal sequence bra.s lbmpac47 * lbmpac43 suba.l A5,A2 Repeat loop New column (REPEAT) lbmpac44 adda.w D4,A2 New word (REPEAT) cmp.w (A2),D7 beq.s lbmpac32 * When a word <> previous word: move.w (A2),D1 bsr.s lbmpac33 End repeat sequence and start lbmpac45 moveq #0,D3 literal seq. counted from this word lea (A4),A1 A1 -> Start of literal sequence lbmpac46 move.w D1,(A4)+ Write word addq.w #1,D3 lbmpac47 dbcs D5,lbmpac31 Next word (LITERAL) bcs.s lbmpac41 At $10000 words, cut sequence move.w A6,D5 dbf D2,lbmpac30 Next column (LITERAL) tst.w D3 bsr.s lbmpac36 * End of literal copy lbmpac48 move.l A3,D1 D1 -> End of control bytes addq.l #1,D1 rounded up andi.b #-2,D1 movea.l D1,A3 move.l (SP)+,A0 Move all data words back to lbmpac49 move.w (A0)+,(A3)+ where control bytes end cmpa.l A0,A4 bne.s lbmpac49 move.l (SP)+,A0 addq.l #4,A0 A0 -> VDAT start (excl header) move.l A3,D0 D0 -> VDAT end sub.l A0,D0 Length of VDAT chunk sub.l A0,D1 Offset to data words move.w D1,(A0) move.l D0,-(A0) addq.l #2,A2 A2 -> Next source plane dbf D6,lbmpac29 *>>>>>>>>>>>>>>>>>>>>>>>>>> addq.l #4,SP bra lbmpac22 *************************** * WBRATIO Called by LBMPAC * translates two-WORD ratio into an approximately equivalent two-BYTE ratio * (May seem a big routine for a small task, but I thought it necessary) *~~~~~~~~~~~~~~~~~~~~~~~~~~ * IN: D1 MSW: Width, LSW: Height. OUT: D0.B: Width, D1.B: Height *~~~~~~~~~~~~~~~~~~~~~~~~~~ WBRATIO movem.l D2-D7,-(SP) Make 2-word ratio fit in 2 bytes move.w D1,D3 D3: Height beq.s wbratio4 swap D1 moveq #0,D2 move.w D1,D2 D2: Width beq.s wbratio4 move.w D2,D7 sub.w D3,D7 bpl.s wbratio1 exg D2,D3 D2:max D3:min wbratio1 moveq #1,D0 First try 1 for out-component 1 wbratio2 move.w D0,D4 mulu D3,D4 In2*Out1 move.l D2,D5 lsr.w #1,D5 move.l D5,D6 add.l D4,D5 divu D2,D5 /In1 move.w D5,D1 =Out2 swap D5 sub.w D5,D6 bpl.s wbratio3 neg.w D6 wbratio3 lsl.l #8,D6 cmp.l D4,D6 bcs.s wbratio5 Acceptable match addq.b #1,D0 Try other out-component 1 bcc.s wbratio2 wbratio4 moveq #1,D0 Default: 1 for both width moveq #1,D1 and height wbratio5 tst.w D7 bpl.s wbratio6 exg D0,D1 wbratio6 movem.l (SP)+,D2-D7 rts . Hnz.. STE_FIX PRG 3`{`:` f`dJxfA`A P=f" hmBCzp f8QP`, P?f J9g##/|vByN"m y #f EgQ`R( yNJDESKTOP.INF&o k  Ј<.@// ?<?<JNAO aBg/<?<1NAJ/ / B@rC02 x$f*`0 _INFf2` _MCHf fpRPH$fJ@f2`p$x )g2`\o _INF!|PHBpЁ!@`: xC!` $HPH&IPI&& f"_INF#|PIB#|(&_$_NuA8NA NZ090g A#NH090D@HA pN0A+N&090gNAp!N#rNu?<NA/?< NA\ONu/ /??<NMPO @$_Nu/ /?<&NN\O$_NuDESKTOP.INF patch installedalready installedthis machine is not an STEthis STE ROM version does not need the patch ERROR: . mytrap1main_StkSizeerrno0Pterm0dmsgCconwsjstartt1savercookie_jvalidate:SetexcvSupexecMADMAClongframjmp_abs_NFopen=opcodeFO_filenFO_modeFread?FR_handlFR_countFR_bufstartupsuptrp gheadplstkchkfnckreadDdeskinftstb4gotopennoreadjreadbufsaveretmungeitvxmunggetbytnxbytgoteresendTpaStartTpaEndTextSegSTextSegS DataSegSDataSegSBssSegStBssSegSiDtaPtr PntPrcPt$Reserved(EnvStrPt,Reserved0CurDrv7Reserved8CmdLineBasePage> h""   <. Kn}.. LZW Ln~READ_TNY UnGEM_TUT 006YnIMG_LOAD anNEO_SHOW fn. Mn~.. Kn}LZW C o^+LZW H gpTIFF MSG t UNLZW C /******************************************************************************* ** ** Name : LZW.C ** Date : 21st May 1994 ** Author : Kevin Preece ** Title : LZW Compression & Decompression Routines ** Compiler : Lattice C V5.51 ** **--- DESCRIPTION -------------------------------------------------------------- ** ** This source contains modules for compressing and decompressing files using ** the Lempel-Ziv-Welch (LZW) compression scheme, as described in the TIFF 5.0 ** specification document. The scheme described in this document is essentially ** the same as the GIF standard for LZW compression and decompression. ** ** There are two major entry points: ** ** (1) PackLZW( ... ); ** (2) UnpackLZW( ... ); ** ** (1) PackLZW( unsigned char *ipb, void *opb, long len ); ** ipb a pointer to the input buffer to be compressed ** opb a pointer to the buffer to receive packed data ** len the length of the uncompressed data ** ******************************************************************************** */ #include #include #include #include #include #include "lzw.h" /* ------------------------------ */ typedef struct node { const unsigned char *s; long slen; short code; struct node *next; } NODE; /* ------------------------------ */ short LastCode; NODE **HashTab = NULL; NODE *MemBlk = NULL; short NodeNo; unsigned char *IBuffP = NULL; long IBuffLen; void *OBuffP = NULL; long OBuffLen; long BufferSize = BUFFER_SIZE; jmp_buf _pksave; long ByteCount = 0; short NextCode = 1; const short BitsPerChar[] = { 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12 }; const unsigned char CharTab[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; /* -- LZW Compression Routines -- */ /* ClearHashTable() * -------------- * * Clears the hash table of all strings it contains. On the first call to this * function a block of memory is allocated to it. * * INPUTS: ** NONE ** * OUTPUTS: ** NONE ** */ void ClearHashTable( void ) { if ( HashTab == NULL ) { if (( HashTab = (NODE**) calloc( HASHMAX, sizeof( NODE** ))) == NULL ) { fprintf( stderr, "Cannot clear hash table\n" ); longjmp( _pksave, ERROR_CODE ); } } else { memset( HashTab, 0, HASHMAX * sizeof( NODE** )); } NodeNo = 0; } /* NewNode() * ------- * * This function is called to obtain storage for a NODE structure. * * On the first call to this function, a chunk of memory large enough to hold HASHMAX * NODEs (HASHMAX is the largest value returned by the hashing function). * * On subsequent calls, a block (the size of a NODE structure) is carved from this * block. * * INPUTS: ** NONE ** * * OUTPUTS: returns a pointer to the new node. */ NODE *NewNode( void ) { if ( MemBlk == NULL ) { MemBlk = malloc(( HASHMAX - 1 ) * sizeof( NODE )); if ( MemBlk == NULL ) { fprintf( stderr, "Cannot allocte NewNode()\n" ); longjmp( _pksave, ERROR_CODE ); } } if ( NodeNo < ( HASHMAX - 1 )) { return ( &MemBlk[ NodeNo++ ] ); } fprintf( stderr, "Cannot allocte NewNode() call 2, NodeNo = %ld\n" ); errno = ENOMEM; longjmp( _pksave, ERROR_CODE ); return NULL; } long mhash( const unsigned char *b, size_t len ) { long d1; long d3; d1 = 0; while ( len ) { --len; d1 <<= 4; d1 += *b++; d3 = d1; d3 &= 0xF0000000L; if ( !d3 ) continue; d1 ^= d3; d3 >>= 24; d3 &= 0x00FF; d1 ^= d3; } return d1; } /* AddTableEntry() * ------------- * * Adds a new string to the table * * INPUTS: s the string to be added * slen the length of string s * * OUTPUTS: ** NONE ** */ void AddTableEntry( const unsigned char *s, const long slen ) { NODE *t; NODE **p; t = NewNode(); t->s = s; t->slen = slen; t->code = NextCode++; p = &HashTab[ mhash( s, slen ) % HASHMAX ]; t->next = ( *p ) ? *p : NULL; /* link to next string with same hash code */ *p = t; } /* InitStringTable() * --------------- * * Sets the string table to its virgin state. * * INPUTS: ** NONE ** * OUTPUTS: ** NONE ** */ void InitStringTable( void ) { short c; ClearHashTable(); for ( NextCode = c = 0; c < 256; ++c ) { AddTableEntry( &CharTab[c], 1 ); } NextCode = END_OF_INFORMATION + 1; } #ifdef DEBUG void ShowCode( short code ) { static short pos = 0; if (( code > 32 ) && ( code < 256 )) { pos += printf( "%c", (char) code ); } else { pos += printf( "<%d>", (int) code ); } if ( pos > 140 ) { pos = 0; printf( "\n" ); } } #endif void PutByte( unsigned char c ) { char *s; s = (char*) OBuffP; *s++ = c; OBuffP = s; ++ByteCount; if ( ByteCount == IBuffLen ) { fprintf( stderr, "Unable to compress file\n" ); longjmp( _pksave, -1 ); } } /* WriteCode() * ---------- * * Writes the given code to the output stream * * INPUTS: code the code to be written * * OUTPUTS: ** NONE ** */ void WriteCode( short code ) { static long byte = 0; static short bits = 0; short bpc; short shift_amt; unsigned short accum; static const short mask[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x0000, 0x01FF, 0x03FF, 0x07FF, 0x0FFF }; bpc = BitsPerChar[ (( NextCode - 1 ) >> 8 ) & 0xf ]; bits += bpc; byte <<= bpc; byte |= code AND mask[ bpc ]; shift_amt = bits AND 7; accum = (short) ( byte >> shift_amt ); byte &= mask[ shift_amt ]; if ( bits >= 16 ) { PutByte( ( accum >> 8 ) & 0xFF ); } PutByte( accum AND 0xFF ); bits = shift_amt; #ifdef DEBUG ShowCode( code ); #endif switch ( code ) { case CLEAR_CODE : InitStringTable(); break; case END_OF_INFORMATION : ClearHashTable(); byte <<= 8 - bits; PutByte( (char) byte ); bits = 0; byte = 0; break; case MAX_CODE : WriteCode( CLEAR_CODE ); break; } } /* FindString() * ----------- * * Given a string, this function searches the hash table to see whether it has been * seen already. * * INPUTS: s The string we wish to find * slen The length of the string s * * OUTPUTS returns 1 if the string was found, or zero if not. */ short FindString( const unsigned char *s, const long slen ) { NODE *p; p = HashTab[ mhash( s, slen ) % HASHMAX ]; if ( p != NULL ) { do { if (( slen == p->slen ) && !memcmp( s, p->s, slen )) { LastCode = p->code; return ( 1 ); } p = p->next; } while ( p ); } return ( 0 ); } /* PackStrip() * ---------- * * Packs a single strip of the image. Note that a strip here is not the same as a * strip in packbits or CCITT compression schemes. Here it is a block of upto * BufferSize bytes in length. * * INPUTS: _ a pointer to the buffer holding the strip * length the total length of the data in the input buffer * * OUTPUTS: compressed data is added to the output stream */ void PackStrip( register unsigned char *_, register long length ) { register long _len; register short code; WriteCode( CLEAR_CODE ); for ( _len = 0; length; --length ) { ++_len; if ( FindString( _, _len )) { code = LastCode; } else { WriteCode( code ); AddTableEntry( _, _len ); --_len; _ += _len; code = *_; _len = 1; } } WriteCode( code ); } void ReleaseMem( const void *addr ) { } /* lzw() * --- * * Packs an image according to the specification for the LZW scheme described in the * ALDUS/MICROSOFT document for version 5.0 of TIFF (Tagged Image File Format). * * INPUTS: buf_ a pointer to the buffer holding the image to be compressed * buf_o a pointer or file handle for the destination * length the total size of the image in bytes * * OUTPUTS: output buffer contains compressed data * return value is the size of the compressed image in bytes or -1 on error */ long PackLZW( void ) { unsigned char *buf_i; void *buf_o; long len; long ret; if (( ret = setjmp( _pksave )) == 0 ) { buf_i = IBuffP; buf_o = OBuffP; len = IBuffLen; ByteCount = 0; for ( ; len > 0; buf_i += BufferSize, len -= BufferSize ) { PackStrip( buf_i, min( len, BufferSize )); } WriteCode( END_OF_INFORMATION ); OBuffP = buf_o; ret = ByteCount; } else { fprintf( stderr, "program aborted\n" ); } ReleaseMem( HashTab ); ReleaseMem( MemBlk ); return ( ret ); } /***** ReadFile ***** ** */ BOOL ReadFile( const char *name ) { FILE *fp; long len; fp = fopen( name, "rb" ); if ( fp ) { fseek( fp, 0, SEEK_END ); len = ftell( fp ); fseek( fp, 0, SEEK_SET ); IBuffP = malloc( len ); if ( IBuffP ) { if ( fread( IBuffP, len, 1, fp ) == 1 ) { /* BufferSize = len; */ IBuffLen = len; fclose( fp ); return TRUE; } fprintf( stderr, "Cannot read file %s\n", name ); } else { fprintf( stderr, "Out of memory\n" ); } fclose( fp ); } else { fprintf( stderr, "cannot open file %s\n", name ); } return FALSE; } void WriteFile( const char *name ) { FILE *fp; fp = fopen( name, "wb" ); if ( fp ) { if ( fwrite( OBuffP, OBuffLen, 1, fp ) != 1 ) { fprintf( stderr, "cannot write output\n" ); } fclose( fp ); } else { fprintf( stderr, "Cannot open %s for output\n" ); } } /* **** main() */ int main( int argc, char **argv ) { if ( argc == 3 ) { if ( ReadFile( argv[1] )) { OBuffP = malloc( IBuffLen ); if ( OBuffP ) { OBuffLen = PackLZW(); WriteFile( argv[2] ); printf( "Original = %ld bytes, packed = %ld bytes\n", IBuffLen, OBuffLen ); } return 0; } } else { fprintf( stderr, "Usage: lzw input output\n" ); } return 1; } #ifdef min #undef min #endif #define min(a,b) ((a) <= (b) ? (a) : (b)) #define AND & #define OR | #define XOR ^ #define EOR XOR #define CLEAR_CODE 256 #define END_OF_INFORMATION 257 #define ERROR_CODE 2 #define BUFFER_SIZE 8192 #define MAX_CODE 8192 #define HASHMAX 8209 typedef int BOOL; #define TRUE -1; #define FALSE 0; Title: Tag Image File Format (TIFF) Specification Author: Aldus/MicroSoft Version: 5.0 Title From: Documentation Author From: Documentation Version From: Documentation Publisher: Aldus/MicroSoft Date: 08/08/88 Keywords: Shareware: No Machine: IBM PC Memory requirements: Peripherals needed: Other s/ware needed: Local id. code: ibmpc/e130 Date mounted/updated: 10/12/89 File names: e130tiff.boo Unarchived files: 1 Unarchived size: 183313 Omissions: See also: ------------------------------------------------------------------------------- Acquiring the package: Download the BOO-format files and run the DEBOO utility to convert each to a .ARC file. Then unpack each of these with the PKUNPAK utility to generate the original files for the package. (The utilities needed are online in directory micros/ibmpc/h99) Description: This is a document describing TIFF, a tag based file format that is designed to promote the interchange of digital image data. TIFF was devised by Aldus and MicroSoft in conjunction with scanner and printer manufacturers. The fields were defined primarily with desktop publishing and related applications in mind, although it is conceivable that other sorts of imaging applications may find TIFF to be useful. The general scenario for which TIFF was invented assumes that applications software for scanning or painting creates a TIFF file, which can then be read and incorporated into a document or publication by an application such as a desktop publishing package. The intent of TIFF is to organize and codify existing practice with respect to the definition and usage of desktop digital data, not to blaze new paths or promote unproven techniques. Yet a very high priority has been given to structuring the data in such a way as to minimize the pain of future additions. TIFF was designed to be a very extensible interchange format. TIFF is not a printer language or page description language, nor is it intended to be a general document interchange standard. It may be useful as is for some image editing applications, but is probably inappropriate for and would thus need to be translated into some intermediate data structures by other image editing applications. The primary design goal was to provide a rich environment within which the exchange of image data between application programs can be accomplished. This richness is required in order to take advantage of the varying capabilities of scanners and similar devices. TIFF is therefore designed to be a superset of existing image file formats for desktop scanners (and paint programs and anything else that produces images with pixels in them) and will be enhanced on a continuing basis as new capabilities arise. #include #include #include #include #include "lzw.h" #define ERROR_CODE -1 /* - LZW Decompression Routines - */ #define string_from_code(c) (((c)<256) ? &CharTab[(c)] : s_tab[(c)].str) #define len_from_code(c) (((c)<256) ? 1 : s_tab[(c)].len) #define is_in_table(c) (c) < next_code typedef struct { char *str; long len; } STR_ENTRY; const short BitsPerChar[] = { 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12 }; const unsigned char CharTab[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; short bits_per_char = 9; char *buf_o; STR_ENTRY *s_tab; long next_code; jmp_buf _pksave; char *IBuffP = NULL; FILE *OutFile = NULL; long OutFileLen = 0; char *OutBuffer[ BUFFER_SIZE ]; long OutBufferLen = 0; long ByteCount = 0; /* get_next_code() * ------------- * * Gets the next code from the input stream. Bit boundaries are catered for here. * * INPUTS: buf_i the file pointer, or pointer to memory where input will come from * OUTPUTS: returns the code it found. */ short get_next_code( void ) { short code; short bpc; static char msk = 0; static char byte = 0; for ( code = 0, bpc = bits_per_char; bpc; msk >>= 1, --bpc ) { if ( msk == 0 ) { msk = 0x80; byte = *IBuffP++; } code += code; /* fast shift left */ if ( byte AND msk ) { ++code; } } #ifdef DEBUG if (( code > 31 ) _AND_ ( code < 256 )) { printf( "%c", (char) code ); } else { printf( "<%d>", (int) code ); } #endif return ( code ); } /* initialise_table() * ---------------- * * Sets the string pointer table to its virgin state. * * INPUTS: ** NONE ** * OUTPUTS: ** NONE ** */ void initialise_table( void ) { if ( s_tab == NULL ) { if (( s_tab = (STR_ENTRY*) malloc(( HASHMAX - 256 ) * sizeof( STR_ENTRY ))) == NULL ) { longjmp( _pksave, ERROR_CODE ); } } memset( s_tab, 0, ( HASHMAX - 256 ) * sizeof( STR_ENTRY )); next_code = END_OF_INFORMATION + 1; bits_per_char = 9; } /* write_string() * ------------ * * writes the given string to the output buffer. * * INPUTS: str Pointer to the string to be written * buf_o Pointer to the output buffer * len The length of str * * OUTPUTS: returns a pointer to the start of the string just written. */ unsigned char *write_string( const unsigned char *str, const unsigned char *buf_o, register long len ) { static unsigned char *buf; unsigned char *ptr; if ( buf == NULL ) { buf = buf_o; } memmove( buf, str, len ); ptr = buf; buf += len; ByteCount += len; OutBufferLen += len; return( ptr ); } /* add_string_to_table() * ------------------- * * Adds a new string to the table, and updates the code number of the next strin to * be added. * * INPUTS: str The string to be added * len The length of str * * OUTPUTS: ** NONE ** */ void add_string_to_table( char *str, long len ) { STR_ENTRY *p; p = &s_tab[ next_code++ ]; p->str = str; p->len = len; bits_per_char = BitsPerChar[ (( next_code ) >> 8 ) & 0xf ]; } void WriteOutBuffer( void ) { if ( fwrite( OutBuffer, OutBufferLen, 1, OutFile ) != 1 ) { fprintf( stderr, "Cannot write to output file\n" ); longjmp( _pksave, ERROR_CODE ); } } /* unlzw() * ----- * * De-compress an image that has been compresed by the LZW compression scheme implimented * in the function lzw(). * * INPUTS: buf_i A pointer to the buffer, or the file pointer, of the compressed data * buf_o A pointer to the memory area to hold the un-compressed image * * OUTPUTS: returns the total size of the image */ long UnLZW( void ) { unsigned char *ptr; unsigned char *tptr; short code; short old_code; long len; long ret; if (( ret = (long) setjmp( _pksave )) == 0 ) { ptr = buf_o = OutBuffer; ByteCount = 0; OutBufferLen = 0; while (( code = get_next_code()) != END_OF_INFORMATION ) { if ( code == CLEAR_CODE ) { WriteOutBuffer(); initialise_table(); buf_o = OutBuffer; OutBufferLen = 0; if (( code = get_next_code()) == END_OF_INFORMATION ) { break; } write_string( string_from_code( code ), buf_o, len_from_code( code )); old_code = code; } else { len = len_from_code( old_code ); if ( is_in_table( code )) { tptr = write_string( string_from_code( code ), buf_o, len_from_code( code )); add_string_to_table( ptr, ++len ); ptr = tptr; } else { ptr = write_string( string_from_code( old_code ), buf_o, len ); write_string( string_from_code( old_code ), buf_o, 1 ); add_string_to_table( ptr, ++len ); } old_code = code; } } ret = ByteCount; } else { fprintf( stderr, "Program aborted by longjmp()\n" ); } /* release_mem( s_tab ); */ return ( ret ); } BOOL ReadInputFile( const char *name ) { FILE *fp; long len; BOOL rc = FALSE; fp = fopen( name, "rb" ); if ( fp ) { fseek( fp, 0, SEEK_END ); len = ftell( fp ); fseek( fp, 0, SEEK_SET ); IBuffP = malloc( len ); if ( IBuffP ) { if ( fread( IBuffP, len, 1, fp ) != 1 ) { fprintf( stderr, "Unable to read input file\n" ); } else { rc = TRUE; } } else { fprintf( stderr, "Out of memory\n" ); } fclose( fp ); } else { fprintf( stderr, "Unable to open file %s\n", name ); } return rc; } BOOL OpenOutputFile( const char *name ) { OutFile = fopen( name, "wb" ); if ( !OutFile ) { fprintf( stderr, "Cannot open output file %s\n" ); return FALSE; } return TRUE; } int main( int argc, char **argv ) { if ( argc == 3 ) { if ( ReadInputFile( argv[1] )) { if ( OpenOutputFile( argv[2] )) { UnLZW(); fclose( OutFile ); } } } else { fprintf( stderr, "Usage: unlzw input output\n" ); } } . Vn.. Kn}READ_TNYC bint read_tiny_file( char *pathname, char *char_ptr, int *colours ) /*=======================================================================*/ /* */ /* Reads the contents of the TINY format file given in pathname into */ /* memory begining at the byte pointed to by char_ptr. The appropriate */ /* colour palette is not set up, instead it is passed back in the */ /* array colours so that Setpallete [ xbios(6) ] can be used later. */ /* This means that the picture can be loaded into a background display */ /* display screen without altering the colours of the currently shown */ /* picture. */ /* */ /* The function returns the following values :- */ /* */ /* 0 - File loaded sucessfully */ /* 1 - Couldn't open the file */ /* 2 - TINY file is not for the current resolution */ /* 3 - Couldn't grab enough memory to read file contents */ /* 4 - File isn't a TINY file */ /* */ /* */ /* This function uses the following external functions :- */ /* */ /* fopen - stdio.h */ /* fread - stdio.h */ /* fclose - stdio.h */ /* Getres [xbios(4)] - osbind.h */ /* malloc - stdlib.h */ /* free - stdlib.h */ /* memcpy - string.h */ /* */ /* (Compiles with Lattice C, other systems may have different */ /* include file names.) */ /*-----------------------------------------------------------------------*/ /* Written by Geoff Smith for the Seaton Shareware Cooperative */ /*=======================================================================*/ { int count, offset, data_count, end; int size[2]; char res, data_type; FILE *t_file; char *control_start, *control_ptr, *end_of_control; int *data_start, *data_ptr, *mem_loc; /* open file */ if ( (t_file = fopen( pathname, "rb" )) == NULL ) return 1; /* find out what resolution the file is for */ fread( &res, sizeof(char), 1, t_file); /* check if a valid resolution code */ if ( res < 0 || res > 5 ) return 4; /* check if resolution is the same as the current resolution */ if ( res%3 != Getrez() ) return 2; /* if there is animation information ignore it */ if (res > 2) fread( size, sizeof(int), 2, t_file); /* read the colour palette */ fread( colours, sizeof(int), 16, t_file); /* check the colour palette */ for (count=0; count<16; count++) if (colours[count]<0 || colours[count]>0x777) return 4; /* get size of control and data sections */ fread( size, sizeof(int), 2, t_file); /* grab some memory to read the data into */ control_start = (char *)malloc(size[0]); if (!control_start) return 3; data_start = (int *)malloc(sizeof(int)*size[1]); if (!data_start) return 3; /* read data into memory and then we can close the file */ fread( control_start, sizeof(char), size[0], t_file); fread( data_start, sizeof(int), size[1], t_file); fclose( t_file ); /* convert the byte pointer supplied to a word pointer & set pointers */ mem_loc = (int *)char_ptr; control_ptr = control_start; data_ptr = data_start; /* initialise count of number of words written to memory */ count = 0; /* calculate end of control data */ end_of_control = control_start + size[0]; /* read through each control byte */ while (control_ptr < end_of_control) { /* check if copying data or repeating data */ if ( *control_ptr < 0 || *control_ptr == 1 ) data_type = 'c'; else data_type = 'r'; /* get number of words to copy or repeat */ if ( *control_ptr < 0 || *control_ptr > 1 ) { data_count = abs( *control_ptr ); control_ptr++; } else { /* copy two bytes to form an integer */ memcpy( &data_count, control_ptr+1, 2 ); control_ptr += 3; } end = count + data_count; for ( ; count8) { form_alert(1,"[3][Skipping Header Bytes][OK]"); cntr1 = img_header.header_length-8; for (cntr = 0; cntr < cntr1; cntr ++) dummy_word = fgetw(img_file); } /* Define Storage Requirements */ /* Define buffer for Pattern Run */ pattern_buffer = malloc(img_header.pattern_length); if (pattern_buffer == NULL) { error_bell(); form_alert(1,"[3][File Load.|No Room For Pattern|Buffer.][OK]"); fclose(img_file); close_window(win_handle, GENL); return; } pattern_pointer = pattern_buffer; /* Define buffer for scan line run */ line_length = div(img_header.line_width, 8); if (line_length.rem != 0) { bytes_per_line = (img_header.line_width+(8-line_length.rem))/8; } else { bytes_per_line = img_header.line_width / 8; } scan_buffer = malloc(bytes_per_line); if (scan_buffer == NULL) { error_bell(); form_alert(1,"[3][File Load.|No Memory For Line Buffer][OK]"); fclose(img_file); free(pattern_buffer); close_window(win_handle, GENL); return; } scan_pointer = scan_buffer; /* Define area for image data */ length = bytes_per_line * img_header.number_lines; img_data = malloc(length); if (img_data == NULL) { error_bell(); form_alert(1,"[3][File Load.|No Memory For Picture][OK]"); fclose(img_file); free(pattern_buffer); free(scan_buffer); close_window(win_handle, GENL); return; } img_pointer = img_data; /* Prepare the status display in window GENL */ dummy_word = work_out[47] * 1.5; wind_update(BEG_UPDATE); x_coord = xwork[GENL]; y_coord = ywork[GENL]; mess_text[0] = '\0'; strcat(mess_text, "Loading File : "); strcat(mess_text, filename); v_gtext(virt_handle, x_coord, y_coord, mess_text); y_coord = y_coord + dummy_word; cvtitos(img_header.line_width); mess_text[0] = '\0'; strcat(mess_text, "This image is "); strcat(mess_text, outstr); cvtitos(img_header.number_lines); strcat(mess_text, " pixels by "); strcat(mess_text, outstr); strcat(mess_text, " lines."); y_coord = y_coord + dummy_word; v_gtext(virt_handle, x_coord, y_coord, mess_text); cvtltos(length); mess_text[0] = '\0'; strcat(mess_text, "The image is "); strcat(mess_text, outstr); strcat(mess_text, " bytes long."); y_coord = y_coord + dummy_word; v_gtext(virt_handle, x_coord, y_coord, mess_text); mess_text[0] = '\0'; y_coord = y_coord + dummy_word; /* Now Read In The Image Data */ byte_count = 0; while(! feof(img_file)) { mess_text[0] = '\0'; strcat(mess_text, "Bytes Processed : "); cvtltos(byte_count); strcat(mess_text, outstr); v_gtext(virt_handle, x_coord, y_coord, mess_text); if (! feof(img_file)) byte_in = fgetc(img_file); else { read_error(); return; } if (byte_in == 0) { if (! feof(img_file)) patt_repeat = fgetc(img_file); else { read_error(); return; } if (patt_repeat == 0) result = scan_run(); else result = pattern_run(); if (result == -1) { error_bell(); form_alert(1,"[3][File Load.|Read Error|Scan/Patt][STOP]"); fclose(img_file); free(pattern_buffer); free(scan_buffer); free(img_data); close_window(win_handle, GENL); return; } byte_in = 0; } if (byte_in == 128) { result = literal_read(); if (result == -1) { error_bell(); form_alert(1,"[3][File Load.|Error|Literal Data][STOP]"); fclose(img_file); free(pattern_buffer); free(scan_buffer); free(img_data); close_window(win_handle, GENL); return; } byte_in = 128; } if (byte_in != 0 && byte_in != 128) { temp_byte_in = byte_in & 128; if (temp_byte_in == 128) result = solid_run_set(); else result = solid_run_clear(); if (result == -1) { error_bell(); form_alert(1,"[3][File Load.|Solid Run Error][STOP]"); fclose(img_file); free(pattern_buffer); free(scan_buffer); free(img_data); close_window(win_handle, GENL); return; } } } fclose(img_file); free(pattern_buffer); free(scan_buffer); mess_text[0] = '\0'; y_coord = y_coord + dummy_word; y_coord = y_coord + dummy_word; strcat(mess_text, "FILE LOADED."); v_gtext(virt_handle, x_coord, y_coord, mess_text); wind_update(END_UPDATE); return; } int open_file(void) { /* Open An .IMG File */ short button, pathlen; fname[0] = '\0'; path[0] = '\0'; filename[0] = '\0'; result = form_dial(FMD_START, 0, 0, 0, 0, xdesk, ydesk, wdesk, hdesk); if (result == 0) { error_bell(); form_alert(1,"[3][Open File.|Can't reserve area for|file selector][STOP]"); men_title_change(f_opt, 1); return -1; } *fname = 0; getcd(0, path); /* Get current drive & path */ strcat(path, "\\*.IMG"); result = fsel_exinput(path, fname, &button, "Load An .IMG File"); if (result == 0) { error_bell(); form_alert(1,"[3][Open File.|Can't select file][STOP]"); men_title_change(f_opt, 1); return -1; } if (button == 0) { men_title_change(f_opt, 1); return -1; } pathlen = strlen(path); for (cntr = 0; cntr < pathlen; cntr++) if (path[cntr] == '*') break; strncpy(filename, path, cntr); strcat(filename, fname); img_file = fopen(filename, "rb"); if (img_file == NULL) { error_bell(); form_alert(1,"[3][Open File.|Can't Open IMG File][STOP]"); men_title_change(f_opt, 1); return -1; } return 0; } int scan_run(void) { /* Read In Data - Scan Run Compression */ unsigned char scan_run; unsigned short scan_length; if (! feof(img_file)) byte_in = fgetc(img_file); else { read_error(); return -1; } if (byte_in != 0xFF) { error_bell(); form_alert(1,"[3][File Format Error|Scanline Flag Expected][STOP]"); return -1; } if (! feof(img_file)) scan_run = fgetc(img_file); else { read_error(); return -1; } scan_length = 0; while (scan_length < bytes_per_line) { if (! feof(img_file)) byte_in = fgetc(img_file); else { read_error(); return -1; } if (byte_in == 0) { if (! feof(img_file)) byte_in = fgetc(img_file); else { read_error(); return -1; } if (byte_in == 0) { error_bell(); form_alert(1,"[3][Scan Run|Logic Error][STOP]"); return -1; } else { for (cntr = 0; cntr < img_header.pattern_length; cntr++) { if (! feof(img_file)) *pattern_pointer = fgetc(img_file); else { read_error(); return -1; } pattern_pointer++; } pattern_pointer = pattern_buffer; for (cntr = 0; cntr < byte_in; cntr++) { for (cntr1 = 0; cntr1 < img_header.pattern_length; cntr1++) { *scan_pointer = *pattern_pointer; scan_pointer++; pattern_pointer++; scan_length++; } pattern_pointer = pattern_buffer; } byte_in = 0; } } if (byte_in == 128) { if (! feof(img_file)) byte_in = fgetc(img_file); else { read_error(); return -1; } for (cntr = 0; cntr < byte_in; cntr++) { if (! feof(img_file)) *scan_pointer = fgetc(img_file); else { read_error(); return -1; } scan_pointer++; scan_length++; } byte_in = 128; } if (byte_in != 0 && byte_in != 128) { temp_byte_in = byte_in & 128; if (temp_byte_in == 128) { byte_in = byte_in & 127; for (cntr = 0; cntr < byte_in; cntr++) { *scan_pointer = 0xFF; scan_pointer++; scan_length++; } } else { byte_in = byte_in & 127; for (cntr = 0; cntr < byte_in; cntr++) { *scan_pointer = 0; scan_pointer++; scan_length++; } } } } scan_pointer = scan_buffer; for (cntr = 0; cntr < scan_run; cntr++) { for (cntr1 = 0; cntr1 < bytes_per_line; cntr1++) { *img_pointer = *scan_pointer; byte_count++; img_pointer++; scan_pointer++; } scan_pointer = scan_buffer; } return 0; } int pattern_run(void) { /* Read In Data - Pattern Run Compression */ pattern_pointer = pattern_buffer; for (cntr = 0; cntr < img_header.pattern_length; cntr++) { if (! feof(img_file)) *pattern_pointer = fgetc(img_file); else { read_error(); return -1; } } pattern_pointer = pattern_buffer; for (cntr = 0; cntr < patt_repeat; cntr ++) { for (cntr1 = 0; cntr1 < img_header.pattern_length; cntr1++) { *img_pointer = *pattern_pointer; byte_count++; img_pointer++; pattern_pointer++; } pattern_pointer = pattern_buffer; } byte_in = 0; return 0; } int literal_read(void) { /* Read In Data - Literal Data Bytes */ if (! feof(img_file)) byte_in = fgetc(img_file); else { read_error(); return -1; } for (cntr = 0; cntr < byte_in; cntr++) { if (! feof(img_file)) *img_pointer = fgetc(img_file); else { read_error(); return -1; } byte_count++; img_pointer++; } byte_in = 128; return 0; } int solid_run_set(void) { /* Solid Run - 0xFF */ byte_in = byte_in & 127; for (cntr = 0; cntr < byte_in; cntr++) { *img_pointer = 0xFF; byte_count++; img_pointer++; } return 0; } int solid_run_clear(void) { /* Solid Run - 0x00 */ byte_in = byte_in & 127; for (cntr = 0; cntr < byte_in; cntr++) { *img_pointer = 0; byte_count++; img_pointer++; } return 0; } void read_error(void) { /* Report An EOF Condition When Reading img_file */ error_bell(); form_alert(1,"[3][Read Error|Reached EOF][STOP]"); fclose(img_file); free(pattern_buffer); free(scan_buffer); wind_close(wi_handle[GENL]); wind_delete(wi_handle[GENL]); wi_handle[GENL] = -1; img_data = NULL; return; } . fn.. Kn}PICSHOW C !PICSHOW PRG 0ICTARI NEO m}PICSHOW TXT amM/***************************************************/ /* Program : PICSHOW.c */ /* Author : Lee Russell */ /* Written : 28/03/93 */ /* Revsion : 08/07/93 */ /* Purpose : To display NEOCHROME format pictures */ /***************************************************/ #include #include #include #include #include #include /******************************************************************/ /* Define The Global Variables Required To Make The GEM Interface */ /******************************************************************/ int ap_id; /* AES Application Handle */ short handle; /* VDI Virtual Workstation Handle */ short phys_handle; /* VDI Physical Workstation Handle */ short work_in[11]={1,1,1,1,1,1,1,1,1,1,2}; short work_out[57]; short contrl[12]; short ptsin[128]; short ptsout[128]; /*************************************************/ /* Now Define The Global Variables For PICSHOW.c */ /*************************************************/ FILE *neo_file; short old_pal[16]; /* The Old Colour Palette */ short new_pal[16]; /* The New Colour Palette */ short *picaddr; /* Pointer To Base Of Temporary Screen Buffer */ short *tpicaddr; /* Temporary Pointer To Screen Buffer */ short dummy; /* Dummy GEM Function Parameter */ /**************************************/ /* Now Define The Function Prototypes */ /**************************************/ void main(void); void initialise(void); void open_file(void); void disp_pic(void); void close_station(void); void read_error(void); /* End Of Function Prototype Definitions */ /*********************************/ /* The Main Function Begins Here */ /*********************************/ void main(void) { initialise(); open_file(); disp_pic(); close_station(); } /******************************/ /* The Main Funtion Ends Here */ /******************************/ /********************************************/ /* Now Define The PICSHOW Control Functions */ /********************************************/ void initialise(void) { long amount; int cntr; short old, mode; ap_id = appl_init(); /* Set Up AES Global Arrays, Get Application ID */ if (ap_id == -1) { exit(EXIT_FAILURE); } phys_handle=graf_handle(&dummy,&dummy,&dummy,&dummy); handle = phys_handle; /* Get VDI Physical Workstation Handle */ v_opnvwk(work_in, &handle, work_out); /* Open Virtual Workstation */ if (handle == 0) { appl_exit(); exit(EXIT_FAILURE); } amount = 32000; /* A Low Resolution Screen Requires 32000 Bytes */ picaddr = malloc(amount); if (picaddr == NULL) { form_alert(1,"[3][Can't Alloc Block][Quit]"); v_clsvwk(handle); appl_exit(); exit(EXIT_FAILURE); } tpicaddr=picaddr; mode = -1; old = Blitmode(mode); /* Determine Blitter Status */ if (old & 2) { Blitmode(old | 1); /* A Blitter Is Available, So Enable It */ } for (cntr = 0; cntr <16; cntr++) { old_pal[cntr] = Setcolor(cntr, -1); /* Save The Current Palette */ } mode = Getrez(); if (mode != 0) { form_alert(1,"[3][Low Resolution Only !][QUIT]"); close_station(); } } void open_file(void) { char select[FNSIZE]; char dirname[FMSIZE]; char fname[FNSIZE+FMSIZE]; /* More Than Enough Room For Path & Name */ short button; short pathlen; short cntr; short dummy; getcd(0, dirname); /* Get The Current Drive And Path */ strcat(dirname, "\\*.*"); *select = 0; /* Now Display The GEM Extended File Selector */ fsel_exinput(dirname, select, &button, "Load a File"); if (button) /* If The User Has Double Clicked A File OR Clicked OK */ { /* Now Construct The Path And Filename To The Selected File */ pathlen = strlen(dirname); for (cntr = 0; cntr < pathlen; cntr++) { if (dirname[cntr] == '*') { break; } } strncpy(fname, dirname, cntr); strcat(fname, select); neo_file = fopen(fname, "rb"); if (neo_file == NULL) { free(picaddr); form_alert(1,"[3][Can't Open NEO][Quit]"); v_clsvwk(handle); appl_exit(); exit(EXIT_FAILURE); } for (cntr = 0; cntr < 2; cntr++) { if (feof(neo_file) == 0) { dummy = fgetw(neo_file); } else { read_error(); } } for (cntr = 0; cntr < 16; cntr++) { if (feof(neo_file) == 0) { new_pal[cntr]=fgetw(neo_file); } else { read_error(); } } for (cntr = 0; cntr < 46; cntr++) { if (feof(neo_file)== 0) { dummy=fgetw(neo_file); } else { read_error(); } } /* Now Read In 16000 Words Of Picture Data */ for (cntr = 0; cntr < 16000; cntr++) { if (feof(neo_file) == 0) { dummy=fgetw(neo_file); *tpicaddr= dummy; tpicaddr++; } else { read_error(); } } fclose(neo_file); } else { close_station(); /* The User Has Selected Cancel, So Quit */ } } void disp_pic(void) { int maxclicks, mask, state; /* Variables Used By evnt_button() */ short *x, *y, *button, *kstate; /* Variables Used By evnt_button() */ short xpos, ypos, butt, ks; /* Variables Used By evnt_button() */ /* Now Define Blitting Variables */ short pxyarray[8]={0,0,319,199,0,0,319,199}; /* Clipping Rectangle */ MFDB srce; /* Source Memory Form Definition Block */ MFDB dest; /* Destination Memory Form Definition Block */ MFDB *s, *d; /* Pointers To MFDB's */ /* Initialise Variables */ x = &xpos; /* Final Mouse X Co-Ordinate */ y = &ypos; /* Final Mouse Y Co-Ordinate */ button = &butt; /* Final State Of Mouse Buttons */ kstate = &ks; /* Final State Of Shift Kets */ maxclicks = 2; /* Wait For Single Or Double Mouse Click */ mask = 1; /* Check Left Mouse Button Status Only */ state = 1; /* Check That The Left Button Is Down */ s = &srce; d = &dest; srce.fd_addr=picaddr; /* Start Of Temporary Screen Buffer */ srce.fd_w=319; /* Height Of Source Form In Pixels */ srce.fd_h=199; /* Width Of Source Form In Pixels */ srce.fd_wdwidth=20; /* Word Width Of Source Form */ srce.fd_stand=0; /* Source Is In Device Specific Format */ srce.fd_nplanes=4; /* A Low Resolution Picture Has 4 Bit Planes */ dest.fd_addr=NULL; /* The Destination Is The Physical Screen */ graf_mouse(M_OFF,NULL); v_clrwk(handle); Setpalette(new_pal); vro_cpyfm(handle, S_ONLY, pxyarray, s, d); /* Perform The Blit */ graf_mouse(M_ON,NULL); evnt_button(maxclicks, mask, state, x, y, button, kstate); Setpalette(old_pal); } void close_station(void) { free(picaddr); /* Release The Temporary Screen Buffer */ v_clrwk(handle); v_clsvwk(handle); appl_exit(); exit(EXIT_SUCCESS); } void read_error(void) { form_alert(1,"[3][Error Reading File][QUIT]"); free(picaddr); /* Release The Temporary Screen Buffer */ v_clsvwk(handle); appl_exit(); exit(EXIT_FAILURE); } /* EOF : PICSHOW.c */ `-`x$oI-)JhHz?<&NN\O"j)I`A W*j,Jf J-gRMJg"R Af^&MRM Rf Gf Vf =fp#@JgNB&I P@Jf"Ͱf ,Ma*N#MJfB` _fj Pfd Bf^ PfX =fRYIJfLrpg: 0e< 9dҀ` Ae( zd" Fc ae Ҁ`²$W`"JfgBN&IAp }bB0,Ha0N(|"@$ p lep@)@)I"`BQ)I`BQ ,< o2V@ l$)IB"Y"XI#I2B"2D)IxX" VA"Ad?<`J@fF" leT)A@ ,H0BAHAR@&,HHCBCHCFC׬H.I/ S// / ?<?<JNAO N#hN >`\O ,dg/?<?<NMPO?<LNAJ =fJRNrtB0m B nҁ&ҁҁ҃҂`.g g+g fv RNJg)AX NNu ,",Le ЀЀNu)x| x h9h2(I9ABNuxe aa8aaNuNVxe |H/ pN )@Rf HxN6XOA~////N\9@9@HlHlHlN\O0,fp$N HxNXO.<}/NXO)@vf*HlHxN0,H.Np$N xHxNO )lvzx ?r@?NNXO*g @?r@?NNXO|pl( Ѐ"/@t??t?NN\OA6"/1R`p?NNTO(JDgHl4HxNbaPOLN^NuNVڿxePHHnsBN HlVHnsNB.Hl\HnHnHnsN:O JngXAs"HJfS. |GlAsp*gRF` H/HnsHnN~HnHnN^HlhHnN O)@2f2/,vN`HllHxN0,H.Np$NHxN^O|p@l l2(f /N|XO*`aRF`|p@l4 l2(f HЀ//@NJXOAV"/ 1`aRF`|p.@l l2(f /NXO*`avRF`| F>l* l2(f/NXO* lz0)Hz`aDRF`/,2NPXO`aLN^NuNVxeH'4AC""""KGAC~|zE-HA-lv=|?=|=|Bn=|BBHx-H-I-JN~0,H.NHlVp?NNO0,H/./.HnHx/NOBHxN:././ / ///NHl6p?NNO&L,N^Nuxe/,vNb0,H.N0,H.Np$N BNbXONuxeHlHxNn.vN0,H.Nnp$NHxN(O NuNVH..,. *.*n&n 9@ 9@ 9@p,N:"6$ n0& n 0(0, HL(N^NuNVH*n&n )MpXЀNV6" n0$ n0& n0(0, HL(N^NupaNVH..,. *.(. 9@ 9@ 9@ 9@ .9@ .9@ . 9@ .$9@ .(9@pRЀNLN^NuNVH*n&n )M)K <@N n0"0, HL(N^NuNVH*n&n < Nh:"6$ n0& n0(0, HL(N^NuH./ *o 9@)M <N"L NuNVH*n.. ,.*.(.)M 9@ 9@ 9@ 9@ .9@ . 9@ p@ЀNL N^NuH*o ./)M 9@ <NL Nu/./ H@B@rHANz.Nu/./ H@B@reHANb.NuNVH*n&n )MT n)H\CZ)I`0HH@B@@ rdHAN&6A)HTA)H\A)H`L(N^NupaNVH..,. *n&n)K)n 9@)MX H@B@@"<mNA)HXL(N^NupaNV/..)n  9@pTЀNx.N^NuNVH?*n&n 0,H 0dJ4fBHlNHxHlNtBHlNjBHlN`A.///HlNHlHnHlHlHlNO<0,2,AY@9@4,2HVHn 0,2X@9@p)@40,H2,H4,H6,H8,H:,H<,H/F8<,H///?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}AP"P"3@H@3@ "psNBNuNVH# ..Bl29|0p)@r)A/NXOJgp)@9|6/NXOJgpBЀ9|4` pB)@p9@4p)@9|:/NTXOJg)|9|8` pB)@9|8|pn/N$XOJf A0R`p?p?pF?NA\O9|>p9@<9@Bp9@@p)@)|p)@(pB)@$JgF0,0H@9@00,4H@9@40,8H@9@80,?<>NATON@)_lHp?o?<BBg?Hx?<BNAO rJWD?A NAO p0LpNu)_lHp?o?<=NATON)_lHp2/.?o ?A?<BNATONr)_lHpJfpN\> //o/@?<@NATON>H#$./*oJf"p?NATO,HJj N`. rAЁ` r@Ё|:A ?/rG?NAPONL$Nu// "QS@t `| SAgU@c дgSAW  _ $Nu/2H@t `WWf(CQQ`дWWf CQQ g$Nu/./p)@Jk"hl A0Jpg A0 `p )@p.NupaH!$.//NXO*@ fp`.-gBUp` 0-?r>?NAXONBUJgp`pL$NuNVH/4..*n ,./NDXO&@ fp`>+H+g JlgHlpHxp?p@?NAO f0+H/NBXOJg // /N`xz M ///NO (JoN Ѕr 5f. MSHHx//NbO =@f "҅ `R@g M N6"ځfJgp` L,N^NupaNVH/4..,./NDXO*@ f p)@p` >-H-gJlgHltHxp?p@?NAO -gHxB/NO *Jjp`g//. /NO *` n -H-Hp@Ѐe^(,Dl( O ",x(p@Ѐm&A`:HxpH?NA\O(p@Ѐm$ Ѐc( m(</pH?NA\O&@ g-K Hd"`"H nCN-@윀f ." Jg6// /NO `& ." // /NO Rg -K-n` lxg/ pI?NA\O`Jg/Hx /.N O -@f//./NO n"H-I`N//./NjO ".찁f.HxHlv/NLO UfR ."윁-@`x . *Jgp` L,N^NuNVH..*n *p1gp`HJff-f^p+@ rg,/ N0XOJg p` -+@ S m mR r` / /NPO" `-g.pfp` @x /Hn/-NO ,~`, -(Jg//-/-NO ,`|pf` g-gr+A `"-+A m+Hg$S m mR r` / /NPO"p0gp` pfp` L N^Nu/ *oJg -fp`4/,4NXO+@+@f p )@p`+l4pp+@ +@*_NupaH'4./ &l8 g kN&S`K g.pUf -,Jg//-/-NO *U`J0g l0XH/pA?NA\ON< l0)P0`zhl$ A0JpgA020H/NXOR`/N XOL,NuH*o~-g / NBXO.p fJg /-N XOB/-NXO,pgJfpL NuH*o g-f*+mp+@B- +@ -g`pJgj - Jjr+A - ,Jg,//-/-NnO .pf` g+m-gp+@ rí`+m p0gp`(p`$~K gJg/ a,XOJg~*U` L NuNVH*n~-gAfHlHxNPOSm mR~ `p3Sg$pLFr@ҁf` p+@p`-gPB~- JgB- `HxHn/-NNO .Jo ~.`Jf`~`hJf/ NXOJg p`N+m/-/-/-NO +@.oS mR~`Jf` B~ L N^NuNV/ *n/ HxHxHnNOSf0.H`p*_N^NuNVH..*n | @S m4 mR -gp f/ NhXO,Jg `& ` p3UgVpLFr@ҁf -+@ `6-gp`pnFrnFf+m ` p+@ `-g8B HxHn/-NO ,pf `pg|`tJf$/ NJg~@އ`~`p`,f+H+H+F+m+H +H+G L N^Nupa  # /"_` "llLpJj$AD)@ @ClApfp)@pNBN /"_` "llLpJkp)@NNVH'$*n-I-Hg?NAXOJgp> nf?NAXOL$N^NuNVH!4*H&IC&-H-Hr gr fRM`Jgvp@"g$[@g @gU@g `RM-M~`RM-M~` RM&`&~Jg.JgVDHH`r g r gp`pJgRM`JgB`vHxNXOJgp?p?pF?NA\OB n"naB L,N^NuNVHp)@,hl A0JpgR` ,hf p)@p`@ A0*Hp "<¬d =@.g". AA-A J@gS@gS@f . R.` p)@p`-l0.H//.NPO*JkbJg /NZp` < f/N/ NXO-@rf"҇S "N" ` .S*MRM` L(N^Nu[3][Can't Alloc Block][Quit][3][Low Resolution Only !][QUIT]\*.*Load a Filerb[3][Can't Open NEO][Quit]??[3][Error Reading File][QUIT]..( . (P5555553X3p5p4p5( /~/// ( 8@$"""fU UDK3!0a YA"f . @NEO!00 xx@  Wcd0 40@ 040@ --??  -> -? Z~??:0[~0 0@(}l00 @`h`| ah```` ~߽`a````@~aa````^G``````A@``````` 0``````>??``````wPx ``````D``````?@^````````````UU`````` `@``````P0```````?? P``````@"``````??E`````` ``````    000``````  Z,~>  0m0y 0?@`````` @ 0X0@00y0?E`````` ݙy`?h`???000y00?`````` -? `0h0y`0?`h````08```Rv`h`00y0=`0h`0```pl```0 0@00}0<0Z~0```\d```hh`@? ?0@00< Z-~? ```\```` -? @040< -? ```\```Z~ -? 020<Ø`@``N```Kπ0H0 [040<ǘ E```H``a{`ƀ6?`h`3000020Ϙ```L`q`y`L-ÿ ???00go``h`000Ϙ```Ga ` b~@`h`044ÚϘ`@``F`ab00 @426ǘKV```@`ab`h`?? @642ǘ  ```@`9ab -? P@ 6,6> 0ϙ-Z?~ ```` `acZ~_ -? 6\6~0ϙ0Z0```i`ac@Ѓ00A [<66<00`h0```` "` ac0ρ`h` 3000l6l6l0`j`````6pÈ,2` `c)*??00go``h`6600`h0````0| <@` `g0ϩ`*hU`@`h`6600ڴ~0````0  <A` `q{gLԃ0T0  6L6 0Θ?Z?~ ``` s| ry``y{g၀ꉂ[? #?@ 66 0Θ? ֭ ```x@`_`Pj``y{g² -? X -? 6 6 0̚V```Ϛ? `Z`~`@`O|@?c| ``9;g9[?+/@G660ϛf  ` `@g ~301 0go`66 0!0`j` 1;Pp,0x``gՁ?? :CEgo```j`66 0Û0`l0aq{Pp/< |?dbc񱪁Fama'66 0 0ڶ~0q{Pp??Pp xga{`1TFރ00O#?66 0-[? a{AT?p??1xg``@9c[kc?@6 0?x  >Ttg`g``| 6 6 ? ? 66 0 Vxx cd`AyagVc ~A?`g`?? k`9EGoבD7C(`gdɿ p>!g`9`%}oh~>@[KR!V`_g`ɏ ???op`g``_ke iޕJ֭-af`` 8g``_ `{7Qw{RVk<af0` >??g``[YJ_O"QkK(@cd` II$$mA $HIm`dciy3 IOgH3 KYJ@d` ci$mI$ې@Im$mI$I$I$bI`g~X,_3Pb`gII$I$Immmci`gc`gimmmg`gg`gg`gg`gchc+> UWcNhcIm$mI$Im0 o1 UUW01 @o?$mI$Im$mI$Im$mI$FZ~F$Im$mI$ImL --? ?U|(  -? 6UQ?UPcq4sUTWPU?061mmImm$۶I$mmKDI} $I$I$$I$mII $I$}Imm$۶I$mmImm$۶I$mmImm$۶I$lnPI$I$}II$$$I$mAI$I$~[[mmImm$۶I$mmI??I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$II$I$I$I$I$I$I$I$I$I$I$I$I$II$I$I$I$I$I$I$I$I$I$I$I$I$$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$mmmmmmmmmmmmmNp  < b'N/Noq"q "a p"qc^w^/o(H# " / * /FF(@""# *x^}^A <q<p6y BBNx NEOCHROME FORMAT PICTURE VIEWER By Lee Russell Index. Section A. A.1. - The Purpose Of This Program. A.2. - The Limitations Of This Program Section B. B.1. - Machine Type. B.2. - Display Type. B.3. - Available Memory. Section C. C.1. - A Discussion Of Coding Conventions Used In This Program. C.2. - A Discussion Of Memory Allocation. C.3. - Discussion Of GEM Usage. C.4. - Function Prototype Analysis. Section D. D.1. - User Guide. Section E. E.1. - Technical Guide. Appendix A. A.1. - Acknowledgements. ====================================================================== Section A.1. - The Purpose Of This Program. This program allows the user to display NEOCHROME format ('.neo') picture files without having to load the package itself. This will be of benefit to artists who need to locate pictures quickly, to DTP users who may not have access to the package itself and to anyone who simply wants to view artwork. The NEOCHROME format is well supported, with most packages offering a load / export '.neo' option. ====================================================================== Section A.2. - The Limitations Of This Program. This program has the following limitations, a) It will only allow the user to display one picture per session. It must be re-loaded every time a picture is to be displayed. I hope to improve on this in version 1.1. b) It does not give any on-screen help to indicate that the user should click the left mouse button in order to finish looking at a picture. Again, I hope version 1.1 will correct this. c) The NEOCHROME format only supports low resolution pictures. Therefore the user will have to find another viewer to display medium and high resolution pictures. ====================================================================== Section B.1. - Machine Type. This program should run on any Atari ST/STE/TT/Falcon. It makes no assumptions about the platform it is running on. If the host machine has a Blitter chip available this is enabled and utilised. Section B.2. - Display Type. This program is configured to run in low resolution only. Therefore, a colour display is required. Section B.3. - Available Memory. This program should run quite happily run on a 'standard' (512k) machine. It requires a workspace of approximately 32k. This program will operate much faster if the pictures to be displayed are kept in a RAM disk. ====================================================================== Section C.1. - Discussion Of Coding Conventions. In this program the following coding conventions apply, The base address for the temporary screen buffer is referenced by the variables picaddr and tpicaddr. All GEM function calls requiring the VDI workstation handle are passed the variable handle. The variable name cntr is used as the control variable in loops. In the function disp_pic the clipping rectangle is referenced using the 'standard' variable name 'pxyarray'. Memory for the temporary screen buffer is allocated using malloc rather than the GEMDOS function Malloc. This is because Malloc is limited to 20 blocks of allocated memory per process under TOS 1.0/1.2 and exceeding this limit may cause the machine to crash. There are no limitations using the internal memory manager malloc. All error conditions are flagged to the user via form_alert's and the program then terminates via the exit function with the status EXIT_FAILURE. ====================================================================== Section C.2. - Memory Allocation. This program uses only a handful of global variables and there is only one significant local memory allocation; Global Variables : FILE *neo_file This is the pointer to the picture file which is to be displayed. short old_pal[16] This contains a copy of the initial colour palette, before a picture is displayed. short new_pal[16] This contains a copy of the colour palette to use for the picture to be displayed; as read in from neo_file. short *picaddr This points to the base of the area allocated to hold the picture to be displayed. short *tpicaddr This is initialy set as a copy of picaddr. It is used in the function open_file() to store each word of picture data in the malloc'ed area. int ap_id The GEM AES application handle. short handle The GEM VDI virtual workstation handle. short phys_handle The GEM VDI physical workstation handle. short work_in[11] GEM VDI workstation defaults array. Used in v_opnvwk(). short work_out[57] Array returned by v_opnvwk(). short contrl[12] VDI global array. short ptsin[128] VDI global array. short ptsout[128] VDI global array. short dummy Used in functions requiring a dummy parameter. Local Variables : As detailed above, both picaddr and tpicaddr reference a malloc'ed memory area. This area is allocated in the function initialise() and is 32000 bytes long (the size of the ST's memory mapped screen display). ====================================================================== Section C.3. - Discussion Of GEM Usage. There is very little use made of GEM in this program. In particular, no use is made of windows. The system will only communicate errors to the user via form_alert()s, otherwise the program is quite 'un- friendly'. However, the concept is simple so very little on screen help should be required. The program incorporates calls to the following GEM / TOS functions; Function OS Purpose appl_init() AES Open AES Application graf_handle() AES Find The VDI Physical Handle v_opvwk() VDI Open VDI Virtual Workstation appl_exit() AES Inform AES That Program Is Terminating. form_alert() AES Display Error Messages. v_clsvwk() VDI Close VDI Virtual Workstation. Blitmode() XBIOS Get / Set Blitter Configuration. Getrez() XBIOS Determine Current Display Mode. getcd() GEMDOS Determine Current Drive & Path. fsel_exinput() AES Get A Filename Using Extended File Selector. graf_mouse() AES Change The Mouse Form. v_clrwk() VDI Clear The VDI Virtual Workstation. Setpalette XBIOS Set The Display Palette. vro_cpyfm() VDI Perform A BitBlit Operation. evnt_button() AES Wait For A Mouse Button Event. The picture is displayed by 'blitting' it from the buffer pointed to by picaddr using the VDI function vro_cpyfm(). This method was chosen for its speed of screen update; it will of course be faster on those machines fitted with a blitter chip. ====================================================================== Section C.4. - Function Prototype Analysis. void initialise(void); Initialises the AES via appl_init(). A GEM VDI virtual workstation is opened. A memory block 32000 bytes long is reserved for the NeoChrome format picture data. If a hardware blitter is available it is enabled. The current screen resolution is checked to ensure that the program is run in low resolution only. The current colour palette is stored to enable the initial colours to be restored before the program terminates. void open_file(void); The user selects a '.neo' file to display from the GEM extended file selector. This file is opened and read into memory. void disp_pic(void); The mouse pointer is hidden and the picture is displayed. Once the user has clicked the left mouse button the colour palette is reset and the mouse is re-enabled. void close_station(void); The memory reserved for the picture data is released. The workstation is cleared and closed and the AES application terminated via appl_exit(). void read_error(void); This function displays an error form_alert() if the NeoChrome disk file cannot be read. The memory reserved for the picture data is released. The workstation is closed and the AES informed that the program no longer requires its services via appl_exit(). The program then terminates via exit() returning the value EXIT_FAILURE to the OS. ====================================================================== Section D.1. - User Guide. PICSHOW is a low resolution program that uses the ST's 16 colour , 320 x 200 pixel display mode. Your ST computer must be in low resolution before you start this program. If the computer is not in low resolution an error message is diplayed and the program terminates. To use this program simply select a NeoChrome format picture file to display from the GEM File Selector. After a few moments the selected picture will be displayed. When you have finished viewing the picture click the left mouse button to return to the desktop. If you decide not to display a picture simply click on the CANCEL button on the File Selector, you will be returned immediately to the desktop. If your machine is equipped with a Blitter chip this will be enabled and used by this program. You may wish to deactivate this chip after using Picshow. Installation Notes. This program will run from any disk drive / RAM disk setup that you may have and need not be in the root directory. Disclaimer Notice. This program has been tested on the following machine configurations; ATARI 520STFM - with 4meg memory ATARI 520STE - with 1meg memory and has been found to work with no problems. In the event of this program malfunctioning the author accepts no responsibility for any losses that the user may suffer. The user is deemed to accept these terms as soon as he / she first uses the program. ====================================================================== NEOCHROME FORMAT PICTURE VIEWER Suggestions, Comments and Bug Reports. Any user who wishes to offer suggestions or comments on this program or who has discovered a serious bug can write to me at the following address, Mr. L.J. Russell 1 Drybeck Avenue Ramsgate Kent CT11 ONX This program is designated to be PUBLIC DOMAIN software by the author. The software may be freely copied and distributed subject to the following conditions, i) The author retains the 'intellectual' copyright to the program code, ii) These notes must be distributed along with the software, iii) The program code may not be altered by anyone other than the author. Enjoy ! L.J. Russell 15th August 1993 Revised 22nd October 1994 ====================================================================== Section E.1. - Technical Guide There are three technically interesting topics addressed by this program, i) The format of a NeoChrome picture file, ii) The layout of the ST's video RAM, iii) The usage of the Blitter functions. i) NeoChrome File Format. The format of a NeoChrome disk file is :- 1 Word - Flag (Always 0) 1 Word - Resolution (0 = Low Res, 1 = Med. Res, 2 = High Res) (Picshow assumes all .neo files are Low Res) 16 Words - Colour palette. Word 1 = Background colour. 12 Bytes - Filename (Usually left blank) 3 Words - Colour animation controls. 1 Word - Image X offset (Unused, always 0) 1 Word - Image Y offset (Unused, always 0) 1 Word - Image Width (Unused, always 320) 1 Word - Image Height (Unused, always 200) 33 Words - Reserved for future expansion 16000 Words - Picture data, entire screen saved as one continuous block. ii) The Layout Of The ST's Video RAM. The Atari ST range of computers use a 32k block of RAM to produce a memory mapped video display. This RAM can currently be configured into three modes, Low Resolution - 320 x 200 pixels, 16 colours Medium Resolution - 640 x 200 pixels, 4 colours High Resolution - 640 x 400 pixels, 2 colours This program in only concerned with generating a low resolution display. In low resolution each pixel requires 4 bits to hold its colour index. However, rather than storing all the bits for a pixel in one word, they are spread over 4 words. Thus four words hold all the data for sixteen pixels. All the lsb's of a pixel are in one word and the next bits in the next word and so on. iii) The Usage Of The Blitter Functions. This program uses the Blitter (Bit Block Transfer) routines to copy the picture data from a temporary buffer into the memory mapped video RAM. If there is a Blitter chip fitted it is enabled. The presence of a hardware blitter is checked for by calling the XBIOS function Blitmode. The format of this function is :- old = Blitmode(mode); short old; - old blitter configuration short mode; - new blitter configuration The presence of a blitter is checked for by passing -1 in mode to obtain the current blitter status. This status is returned in old and has two bits which are of use, Bit Meaning When Set 0 Perform Blits In Hardware 1 Hardware Blitter Is Available If a blitter is available the command Blitmode(old|1); will enable it. This programs performs a blit by calling the VDI function vro_cpyfm(). The format of this function is :- vro_cpyfm(handle, wr_mode, pxyarray, source, dest); int handle; - VDI workstation handle int wr_mode - logic operation to perform short *pxyarray - pointer to 8 shorts which describe the source rectangle in the source form and target rectangle in the destination form. MFDB source - source memory form definition block MFDB dest - destination memory form definition block An MFDB has the following structure, typedef struct fdbstr { void *fd_addr; short fd_w; short fd_h; short fd_wdwidth; short fd_stand; short fd_nplanes; short fd_r1; short fd_r2; short fd_r3; } MFDB; The various elements of the MFDB are :- fd_addr - gives the address of the memory area to use. This should be NULL if a physical device (like the screen) is to be used. the remaining parameters may be left blank if fd_addr = NULL. fd_w - Width of form in pixels. For a low resolution picture this should be 320. fd_h - Height of form in pixels. For a low resolution picture this should be 200. fd_wdwidth - Form width in words. This is equal to the width of the raster area in pixels divided by the word size. For a low resolution picture this should be 20 - 320 / 16 = 20. fd_stand - 0 = the form is in device specific format 1 = the form is in device independant format For a NeoChrome file this is set to 0. fd_nplanes - Number of bit planes. A low resolution screen has 4 bit planes. fd_r1 to fd_r3 - Reserved fields, do not set to any value. The wr_mode parameter can be one of 16 values. It describes the logic operation to be performed between the source and destination forms. As this program is merely concerned with displaying a full screen picture the value S_ONLY has been selected. Note, the HiSoft documentation is rather confusing on this parameter, the Atari documentation is easier to understand. The pxyarray parameter is a pointer to 8 shorts with the following meanings, pxyarray[0] - x co-ordinate of top left corner of source rectangle pxyarray[1] - y co-ordinate of top left corner of source rectangle pxyarray[2] - x co-ordinate of bottom right corner of source rectangle pxyarray[3] - y co-ordinate of bottom right corner of source destination pxyarray[4] to pxyarray[7] repeat these for the destination rectangle. In order to display a full screen picture in low resolution pxyarray is declared as follows; short pxyarray[8]={0,0,319,199,0,0,319,199}; ====================================================================== Appendix A.1. - Acknowledgements. The author would like to acknowledge the following reference sources, Atari ST - Tricks & Tips - Abacus Software ISBN 0-916439-47-X Atari ST/TT Developer's Kit - GEM VDI Programming Guide (Published by Atari Corp (UK) Ltd, Ref ST/TT Developer's Kit Document SSW 1201-0003) The author would also like to thank Mr A. Bodin of Atari Corp (UK) Ltd for the information regarding the ST screen display which he provided. The information regarding the format of a NeoChrome disk file was taken from an article which appeared in the December 1991 issue of ST Format - Probing Inside Pictures. Atari ST, 520STFM and 520STE are trademarks or registered trademarks of Atari Corporation. GEM is a registered trademark of Digital Research Inc. The following software was used during the development of this program, Lattice C v 5.5 - (c) HiSoft & Lattice Inc Protext v 4.37 - (c) Arnor ====================================================================== ?UU@PUWwwww7;> 9p   C`#$nЀ H f$:SI0w]ʻˢ9΃YXHw3X~~< F @DU5!n. 6{&Zlt @Kt8`Q 1l"18p#$nЀ v{ 8 gY«ޥ98?|o?Y)@`#N4 F @jٞ矿c{p u@Q 1 p<ۑ 0xLw.&A'&=T5_/~{zYi1 . tn.. TINY_PIC unCRACKART xnSPU_FORM  nG. vn.. tnTINY_GFABAS X GfABASIC*J   F IXDATALXYCPTRIIIPDATARESIXPTRTFIPACODALEPIDATAPDATARESRESLCODELDATACOPTRDAPTRIXIYXXICODECOVALIIXPICIXDATAPIWORDPICPTRLCPTRPTRPICPWORDICPICIXXIXPTRPTRCOPTRDAIXCODELABEL1JLBGETDATAPUTDATAFIXITE FfE:\gfabasic\*.tn*! !F 6 FF FE! F palette 4E! F code table and data table length $Ez ! F code table buffer $Ez! F data word buffer 7I!M!F EqM FM!z !FFM!z !FF$E3z F length of code table >E3z  Fg length of data area (sometimes 2 * length) $M!z !Fg read in code table e*vLF handle iffiness on data length m(M!z !Fe read in the data words hvF8MF 2Ez F remove pointer evaluation from loops 2 Ez F6 Fr res 3,4,5 = 0,1,2 + color rotation data 1!F FE F EF "FF Epi1F EGpF6 !3z   F  F F jF Epi2Fb EGF6 !3z   F F F F F! Epi3F 6!3z F  F&)!unknown resolution!! OK !FF! F F! F!2Ez! F decompressed picture goes here d Ez F EFEF EFEF( E9F want to know how long it takes g ,F E2 F&  F  0= 16 bit repeat count s" E#2  2 F0!F,F EG F,F  FG $F & V F 1= 16 bit string length " E#2  2 F0!FN EG F,F,F 8 FF  F*  F  2..127 = repeat count 4FF,F EG F,F  FG4 F  128..255 = two's complement string length 4FF E F EG F,F,F  FG F F FFF$ E9 F2 compute decompress time Time = " H"FEa F wait for keypress 2F  try to display (no resolution adjustment) z !F E6 F*E?@@  F7O!M!F M!z !FM!z !FM!z !zzF8MFz<6!`!`!`Fs restore my original palette 6!`!!F6!!@!F 6!!!FF!+F` E3 F0 !F.FE+F  !F0 !F  zF0 !F1 !zF  F0 !F1 !F FE FE.FE+FFF.FF   ;UUUUUU4-;-<8'/GG;H@OH;;;;;;88FssxDDDUEUU/UU]W"DDDDUEUU/UU]WwWUUUUGBUU]UTH@PPJ_WXWN"". yn.. tnCA_PACK GFA c CA_PACK INL eNCA_PACK LST fnCA_PACK S h"_'CA_TITLECA1 k,<CA_UNPACGFA n<CA_UNPACINL p> CA_UNPACLST q?KCA_UNPACS sBGFA-BASIC3&lllzzz B B ^ UNDOFPATHFNAMEABUFFERPALHEADER CA_DECOMPCURRENT CA_DECOMPRESS CA_UNPACKBUFFEROFFSETCA_PACKLENISCREENSRTING RESOLUTIONI  CRACK ART 'CA_PACK'  (Kompressionsroutine fr CA?-Bilder  ,̽ Detlef Rttger & Jan Borchers 1989-91   \!NFr`0HL<"o@$I0<BZQ$H0<|BAARqQ$IE2<~0<4"Al62gQVC>$I2<0<4Ao62QDCC< oLY! "Bild von 32000 Bytes auf "7" Bytes ("F.L7% " %) gepackt.F $O!M!F File ffnen  0 F!  F( Low Res "4߀߀! F !CAF 'CA'-Kennung ,߀!Fu Low Res gepackt dGF  16 Farben B߀݀!V!!  F FFG&M!!݀߀Fe  F( Med Res "4߀߀! F !CAF 'CA'-Kennung ,߀!Fb Med Res gepackt dGF 4 Farben B߀݀!V!!  F hFG&M!!݀߀Fe  F( High Res 4! F !CAF 'CA'-Kennung .߀!F  High Res gepackt M!!F 4F M!!F 0MF  $I2<0<4Ao62QDCC< o0 BLOAD fname$,buffer% ! Bild laden ' IF DPEEK(buffer%)<>&H4341 ! 'CA'-Kennung checken PRINT "Kein CA-Bild! (Abbruch)" ELSE IF PEEK(buffer%+3)<>resolution& ! Auflsung checken PRINT "' ' ' CRACK ART 'CA_PACK' ' ' Kompressionsroutine fr CA?-Bilder ' ' Detlef Rttger & Jan Borchers 1989-91 ' ' INLINE ca_pack%,590 ' resolution&=XBIOS(4) ! Auflsung holen IF resolution&>2 ! keine ST-Auflsung END ENDIF ' DO fpath$=CHR$(GEMDOS(25)+65)+":"+DIR$(0)+"\*.CA"+CHR$(resolution&+49) FILESELECT #"SAVE CA",fpath$,"",fname$ ! File auswhlen CLS IF fname$="" ! Abbruch END ENDIF ' FOR i&=1 TO 50 DEFFILL RANDOM(16),1,0 PCIRCLE RANDOM(WORK_OUT(0)),RANDOM(WORK_OUT(1)),RANDOM(50) NEXT i& ' buffer$=STRING$(32000,0) ! Buffer reservieren HIDEM len%=C:ca_pack%(L:XBIOS(2),L:V:buffer$) ! Image packen SHOWM ' PRINT AT(1,1);"Bild von 32000 Bytes auf ";len%;" Bytes ("; PRINT INT(100*len%/32000);"%) gepackt." ' OPEN "O",#1,fname$ ! File ffnen ' SELECT resolution& ' CASE 0 ! Low Res header$=STRING$(4+32,0) DPOKE V:header$,&H4341 ! 'CA'-Kennung DPOKE V:header$+2,&H100 ! Low Res gepackt FOR i&=0 TO 15 ! 16 Farben DPOKE V:header$+4+i&*2,XBIOS(7,W:i&,W:-1) AND &H777 NEXT i& BPUT #1,V:header$,4+32 ' CASE 1 ! Med Res header$=STRING$(4+8,0) DPOKE V:header$,&H4341 ! 'CA'-Kennung DPOKE V:header$+2,&H101 ! Med Res gepackt FOR i&=0 TO 3 ! 4 Farben DPOKE V:header$+4+i&*2,XBIOS(7,W:i&,W:-1) AND &H777 NEXT i& BPUT #1,V:header$,4+8 ' CASE 2 ! High Res header$=STRING$(4,0) DPOKE V:header$,&H4341 ! 'CA'-Kennung DPOKE V:header$+2,&H102 ! High Res gepackt BPUT #1,V:header$,4 ' ENDSELECT ' BPUT #1,V:buffer$,len% CLOSE #1 ' WHILE MOUSEK WEND REPEAT UNTIL MOUSEK LOOP A_DECOMPRESS%( l:Quelle, l:Ziel ) TEXT movem.l d1-a6,-(sp) movem.l 60(sp),a0-a1 ; Quelle/Ziel movea.l a1,a2 lea 32000(a1),a3 clr.w d7 clr.w d6 move.b (a0)+,d7 ; ESC move.b (a0)+,d6 ; DELTA move.w (a0)+,d0 ; Offset and.l #$7fff,d0 movea.l d0,a5 move.b d6,d1 ; DELTA in Langwoerter bringen lsl.w #8,d1 move.b d6,d1 move.w d1,d2 swap d1 move.w d2,d1 move.l d1,d2 move.l d1,d3 move.l d1,d4 move.l d1,d5 movea.l a3,a6 ; CRACK ART Kompressionsroutine fr Bilddaten (CA?) ; Copyright Detlef Rttger 04.03.1990 ; GFA-Aufruf: Lnge%=C:CA_COMPRESS%( l:Quelle, l:Ziel ) ; Kompressions-Codes: ; Byte = unkomprimiertes Byte ; ESC ESC = ein ESC Byte ; ESC Anzahl-1 Byte = Anzahl gleiche Bytes ; ESC 0 Anzahl-1 Byte = Anzahl gleiche Bytes (ntig, falls Anzahl-1=ESC) ; ESC 1 Mult Rest-1 Byte = 256 * Mult + Rest gleiche Bytes ; ESC 2 Mult Rest-1 = 256 * Mult + Rest DELTA Bytes ; ESC 2 0 = Bildende ; Komprimiertes Image: ; ESC.b DELTA.b OFFSET.w Komprimierte_Bilddaten... ESC 2 0 TEXT movem.l d1-a6,-(sp) movem.l 60(sp),a0-a1 ; Quelle/Ziel movea.l 64(sp),a1 ; Zieladresse movea.l a1,a2 ; Platz fr die Bytehufigkeit vorbereiten move.w #255,d0 init: clr.w (a2)+ dbra d0,init movea.l a0,a2 ; Bytehufigkeit zhlen move.w #31999,d0 ; 32000 Bytes pro Bildschirm zaehl: clr.w d1 move.b (a2)+,d1 ; Byte vom Quellbildschirm add.w d1,d1 addq.w #1,0(a1,d1.w) ; wortweise reicht dbra d0,zaehl ; Das seltenste Byte finden, von hinten suchen, damit die Wahrscheinlichkeit, ; da das ESC Byte mit dem Anzahl-Zhler bereinstimmt, geringer wird ; (ESC 0 Anzahl-1 Byte) soll so selten wie mglich auftreten movea.l a1,a2 ; Minimum finden lea 512(a2),a2 ; an das Ende der Zhler move.w #32500,d1 ; Minimum vorbelegen move.w #252,d0 ; Bytes 0,1,2 sind reservierte Codes minimum: move.w -(a2),d2 cmp.w d1,d2 ; mit bisherigem Minimum vergleichen bge.s nextmin ; das erste Minimum behalten move.w d0,d3 ; Zhler merken move.w d2,d1 ; neues Minimum merken beq.s minend ; d1=0 kein kleinerer Wert moeglich nextmin: dbra d0,minimum minend: addq.w #3,d3 ; das ist das Esc Byte move.w d3,d7 ; ESC Byte merken movea.l a1,a2 ; Maximum finden move.w #-1,d1 ; Maximum vorbelegen move.w #255,d0 maximum: move.w (a2)+,d2 cmp.w d1,d2 ; mit bisherigem Maximum vergleichen ble.s nextmax ; bei gleichhufigen Bytes das erste nehmen ; damit ESC und DELTA niemals gleich sein koennen move.w d0,d3 ; Zhler merken move.w d2,d1 ; neues Maximum merken nextmax: dbra d0,maximum neg.w d3 addi.w #255,d3 ; das ist das DELTA Byte move.w d3,d6 ; DELTA Byte merken ; =================================== Hier beginnt der Kompressionsalgorithmus movea.l 60(sp),a0 ; Quelladresse lea 32000(a0),a2 ; Endadresse move.w #32000,d4 ; Vergleichslnge lea offset(pc),a6 ; Offsetliste while: movea.l (a6)+,a5 ; Offset holen cmpa.l #0,a5 beq.s endwhile ; Offset=0 ist Abbruchkriterium cmpa.l #-1,a5 beq.s endprg ; -1 ist Programmende movem.l 60(sp),a0/a3 ; Quelle/Ziel movea.l a0,a1 ; Workadresse move.b d7,(a3)+ ; ESC auf Zielbildschirm merken move.b d6,(a3)+ ; DELTA uebertragen move.w a5,(a3)+ ; Offset move.w #4,d3 ; Lnge des komprimierten Bildes ; ESC.b + DELTA.b + Offset.w move.l a5,d0 ; Offset als subq.w #1,d0 ; Durchlaufzhler mainloop: tst.w d0 bmi.s endcode ; neuer Offset move.b (a1),d1 ; erstes Byte holen clr.w d2 ; gleiche Bytes zhlen testloop: ; Nchste Adresse errechnen adda.l a5,a1 ; Offset addieren cmpa.l a2,a1 ; Hinter dem Bildschirmende ? blt.s nextok ; wenn nicht, dann weiter addq.l #1,a0 ; sonst Quelladresse einen weiter movea.l a0,a1 ; und neue Workadresse subq.w #1,d0 ; ein berschlag bmi.s compress ; Ende der Kompression anzeigen nextok: cmp.b (a1),d1 bne.s compress ; Reihe abgebrochen addq.w #1,d2 bra.s testloop endcode: addq.w #1,d3 ; Code: ESC 2 0 (Endekennung) cmp.w d4,d3 bge.s while move.b d7,(a3)+ ; ESC addq.w #1,d3 cmp.w d4,d3 bge.s while move.b #2,(a3)+ ; 2 addq.w #1,d3 cmp.w d4,d3 bge.s while clr.b (a3)+ ; 0 move.w d3,d4 ; neue Lnge move.l a5,d5 ; Offset merken bra.s while ; und weiter endwhile: cmp.w #32000,d4 bge.s endprg move.w #32000,d4 lea shortest(pc),a6 move.l d5,(a6) move.l #-1,4(a6) bra.s while endprg: moveq #0,d0 move.w d4,d0 ; Lnge des komprimierten Bildes movem.l (sp)+,d1-a6 rts ; ========================================================= compress ; In d1.b ist das Byte, in d2.w die Anzahl compress: tst.w d0 bpl.s intern cmp.b d6,d1 ; DELTA beq.s endcode intern: cmp.b d7,d1 bne.s noesc compesc: addq.w #1,d3 ; Code: ESC ESC cmp.w d4,d3 bge while ; nchste Kompression move.b d7,(a3)+ addq.w #1,d3 cmp.w d4,d3 bge while move.b d7,(a3)+ dbra d2,compesc ; Lnge erhhen bra mainloop ; und weiter noesc: cmp.w #2,d2 bgt.s more ; mehr als 3 Bytes gleich uncomp: addq.w #1,d3 ; Code: Byte cmp.w d4,d3 bge while move.b d1,(a3)+ ; Byte dbra d2,uncomp bra mainloop more: cmp.w #255,d2 bgt.s evenmore addq.w #1,d3 ; Code: ESC Anzahl-1 Byte cmp.w d4,d3 ; oder: ESC 0 Anzahl-1 Byte bge while move.b d7,(a3)+ ; ESC cmp.b d7,d2 ; zufllig Anzahl-1 = ESC ? bne.s morenorm addq.w #1,d3 cmp.w d4,d3 bge while clr.b (a3)+ ; 00 morenorm: addq.w #1,d3 cmp.w d4,d3 bge while move.b d2,(a3)+ ; Anzahl-1 addq.w #1,d3 cmp.w d4,d3 bge while move.b d1,(a3)+ ; Byte bra mainloop evenmore: cmp.b d6,d1 ; DELTA ? beq.s moredelta addq.w #1,d3 ; Code: ESC 1 Mult Rest-1 Byte cmp.w d4,d3 bge while move.b d7,(a3)+ ; ESC addq.w #1,d3 cmp.w d4,d3 bge while move.b #1,(a3)+ ; 1 addq.w #1,d3 cmp.w d4,d3 bge while movea.w d2,a4 ; sichern lsr.w #8,d2 ; div 256 move.b d2,(a3)+ ; Mult addq.w #1,d3 cmp.w d4,d3 bge while move.w a4,d2 and.w #255,d2 move.b d2,(a3)+ ; Rest-1 addq.w #1,d3 cmp.w d4,d3 bge while move.b d1,(a3)+ ; Byte bra mainloop moredelta: addq.w #1,d3 ; Code: ESC 2 Mult Rest-1 cmp.w d4,d3 bge while move.b d7,(a3)+ addq.w #1,d3 cmp.w d4,d3 bge while move.b #2,(a3)+ addq.w #1,d3 cmp.w d4,d3 bge while movea.w d2,a4 ; sichern lsr.w #8,d2 ; div 256 move.b d2,(a3)+ addq.w #1,d3 cmp.w d4,d3 bge while move.w a4,d2 and.w #255,d2 move.b d2,(a3)+ bra mainloop DATA EVEN offset: DC.L 160,8,80,1,2,4,320,640,480,0 shortest: DC.L 0,-1 END ״PHp-$P:ßdvј& 뜰jh^< AwYckvB ȵ٭/|5|~tʴ-0&ڒ<k\LktQ3W ~ EiЯx3?CA"33DCUTfevw@""""*ꪮ """"*ꪮ1Ű!""""*ꪮJJ瓗Ԕ """"*ꪮYUF?9IMJJSYQ@9 NJJSYQ@9 SJJSYQ@9 """"*ꪮ))|3y5ww""""*ꪮ ))ݽ}}wwM))3y1M))""M))3y1S))S))3y1S))""""*ꪮ$)(&I&pJJUu݈""""*ꪮ(dPw*w*wRPUUM)(&I&JJ"""Md@"w"" RP""*M)(&I&JJSd@"w"RPS)(&I&JJSd@"w"RP"""*ꪮ?'www9K9uȁUUQU"*ꪮ䥃' `pwwUݕѕUQUUQT4("9K92ȡ *0p̴{~)"""Ғ"***3 9K90ȁ5<̴{~) АX 9K90ȁ[А ꪮ ?pUUUUQUUQTDTꪮ+ PTQUUQTDTDD9lh( @""*Ġ=@ ?lh( @ =߀ń@@\$@@TDDD@  B??@) iy w`q_g8    0<  0^`pp<( !iy w_C?&ph{%#    0?  &0n@@`$ `x>8 ?Nv>#/?7?  @>??U 6 p ??  ???D#3|p Cl>%n!pX~ ,fd8sO]{v[6zm-;f3<\z<<DP  @` n*D5v;}t\3| xK{n kA1 af398>%n!pKwaxsO\utY?>xߦUm;@##Z$0 DP  @#Oš<w{[kA1 a8>`t opT 0"9o>]Z]?x]I%/{]?jx<|FEϋ0 =aǏx@D3|???o@Xx|?;$9v#>S88lH=q| pj?fчYfǡXGys3:@m}?o|,2X4cv^OPc<$@@@OOؾ[WnL?ϟЃ>7<\:[HáX_ys3:@}x@?px?ˏc>@@@@ g鬼W [?@@s/_w<?[R>PyB __wy 1?p @$r__Q?|lhyf(B [Xp߳(,,8@0w =#;.-@@? =`  0 C@0`><7"[YRa~R.Gx}<'x_ &&s =#;.-@9?psQg 0(؀@D" px>O47"[YRa~Si1c`X\^?;ք` @@ _O`pp@<A0 Y4$( @߿1Uu]UDTDD` w7}uUU]ՑqTDTDED@ ,8 pj?c. 90p  `|14!?9wwwUuUUUUQUUQTDTDD@%@tvwUuUUUUQUUQTDTDDD@.@(@ @ .Tݯ Q@n_ /&x??` A @ g @ *Pś OW@#'x@ C @  /^:t??$Y@@@p~ܟ"uUUUUQUUQTDTDDD@ > " UUUQUUQTDTDDD@@8Twc'J " +n,s>~?~?x ~  +?S/` 篭 &̃̿<7#p(`h"8X 䠀'  )|,p`_6}w΀` +QW_^|8pDDD@$@ ww?"A @1` `/Pc ? ؘP0dx9̀&C? ' $/S %< '/?_( A"2P0d _( $/S % 0`2?x?wwwU?:wwuUUթ﮼и4"*": ǃ ,3C<p0?_?"""":?CA`Ȁ~|8||< 0?_? ? @@`ȀJ?8?UuUUW^|p??/ "*UUUUQU@% "*"*""*@9`?0@?"*L@ q @? /?~I_?H;?88 """"*SX($;&,4x80/; pHCÃOH;;;;888GD@ """"* """"* n_F|zt8IW/WV𘌇V?? """"*  """"*y  F@`pxVIР@pź8pp8ű@DDDEUUUUU-U]UUWwwvvwDDDEUUUUU-U]UUWwwww畄UDDDDEKDDDDEUUůDEUUUUU-U]UUWwwwww1J1 cUUUUU-U]UUWwwwwwPԛ 8L1J1EUcUUIDPЄD@@U9UL1J1cRPЀ8R1J1cRPЀ8UUU-U]UUWwwwww*IA-U]UUWwwwww)) ))(edEDDDETIAU\RR\UADDDD))UUUTUUeeULIAR))edRIAR))ed)U]UUWwwwww/& )3B2%U]UUWwwwwwSCG"""^L=DDDDEU/&U )UR3B2U9DDDDEUUUSCWU"""DUU^LUL/& )3B2RSCG"""^LR/& )3B2RSCG"""^L!U]UUWwwwww~Р JJU]UUWwwwww~K# X***3OGG_KQPTUUJJUU0 0g9B@PTUY U3 JJ5xg9 XXJJ[X UTU]UUWwwwww?* J UU]UUWwwwwwB 'DDDDEUU;  PTU#DDDDDPP@@@@PT U?;  =@Ł!]UTTUUUPT\TTWwwwww  UUT ?訩0???DDDD@ `@PTTU#p9|Up]wtot(   F GDDEWWTQP@` 38``p0<@@@P) 0` #p9|U`O9Rpl_s!    `> 28f@@` 4 qfD ??bI20?== ?  UU @??w H2  ?UU@PUWwwww7;> 9p   C`#$nЀ H f$:SI0w]ʻˢ9΃YXHw3X~~< F @DU5!n. 6{&Zlt @Kt8`Q 1l"18p#$nЀ v{ 8 gY«ޥ98?|o?Y)@`#N4 F @jٞ矿c{p u@Q 1 p<ۑ 0xLw.&A'&=T5_/~{zYi1 o=??{u?<x~UUwwwwwr.˻@yc;̤@ys>y 07Pm5xdžlSf{xOPu}:< DDCx?0xN?v_L΃6}@ !f8x  D' #%&9:?G; 76 0X0 P$8| ~E>#[WOה`?>1!@XX\^_ǟ 0 ?pp|> :]2$(0 `wꮺ{Ͼ|8** xo  ꪫj**zzj*j !M}uϾ|8rxG 'ǁV  DDDEUUUU]wupp@ 8@p08? 'V r̘ 8`@>=?8 p'0~ r̘ 8 ??<>7;髫몪 A" "! "" D@GUUWwuuUTQ -uˉH   >i`DEax@@w1@'M>$ ?x@@|UUUE "|?7 ~UUUUUP""" H``8DDUUǀ UP UUU]P`}}7-UUUUU   QUUQQEU]Up`cwS(?8˕_2Ap&?wO Ap)@"x 8P'? 2*|x<p`?~؀@TDP """ D@@oEO{UUU""" UUUUUU@'``@cARXP U]UuWwx@`ҘZ`UUUUUU|b%  lU]UuWwwww1Np; j 'L.. #? /_<)@` Z`OÀ @%p; j 'L.. #8p1@?Ã??UIUUIi""" @@@@UUUU]U""" DDD UUUU=3C2ǃUU]UuWwwww?Ȉa__O_@UUUUՁ8`ЌuWwwwwA`~|<~||8a__O_@  /???@`J???=5?==5" DDD@TDP@UUUpp DDDDUEUUUUWs` @@s88@wwww UUUUUU  N@` qww?? -W?~JH8`8?88H@?UU_wwDDDDUEUU#U EM `|\`DDDDUEUU*UU1bnfG`UUUUUU ` ,; *O OK??JH?UUu~DDDDUEUU+U;;:^U?:;w88DDUEUU2UUUU4S   ;UUUUUU4-;-<8'/GG;H@OH;;;;;;88FssxDDDUEUU/UU]W"DDDDUEUU/UU]WwWUUUUGBUU]UTH@PPJ_WXWN"""~<?DDDUEUU/UU]WwWww""" ~DUEUU/UU]WwWwwwUU]U uW ?xx<dp|JuWwwwwGEEJȐŻ 0``0 5/ᥭGef18uCESZ;Xaf롥rzJeMDKM?_*ՙl:!le5waໜuGRKr nxJd3JXQZ`|xTk\{X}u P);WO>,WL-{flQuXH~~˛?{zpg젝Ɗ~Ig,a.F,7mtzmU򊾢OγdSj ޚKbXۙ;uIgRPb;zuuLebuO]<M=9E_+ŤV$, ujz]٘|M 6ͬqy<>>ssj/BΛ+Un ߷aŏqFgϞ]Q53/g֩+?A[ }0KB(vS %=?{8_19,C34?/@gR[$6No6;S4~9_5U먧Sj|iԇoY?2 ! keine ST-Auflsung END ENDIF ' DO fpath$=CHR$(GEMDOS(25)+65)+":"+DIR$(0)+"\*.CA"+CHR$(resolution&+49) FILESELECT #"LOAD CA",fpath$,"",fname$ ! File auswhlen CLS IF EXIST(fname$)=FALSE ! File nicht existent oder Abbruch END ENDIF ' RESERVE 64000 ' OPEN "I",#1,fname$ ! File ffnen buffer%=MALLOC(LOF(#1)) ! Buffer reservieren CLOSE #1 ' IF buffer%<>0 BLOAD fname$,buffer% ! Bild laden ' IF DPEEK(buffer%)<>&H4341 ! 'CA'-Kennung checken PRINT "Kein CA-Bild! (Abbruch)" ELSE IF PEEK(buffer%+3)<>resolution& ! Auflsung checken PRINT "Falsche Auflsung! (Abbruch)" ELSE ' SELECT resolution& CASE 0 ! Low Res ~XBIOS(6,L:buffer%+4) ! Farben setzen offset%=36 ! Image-Beginn CASE 1 ! Med Res ~XBIOS(6,L:buffer%+4) offset%=12 CASE 2 ! High Res ' Monochrombilder besitzen keine Farbpalette offset%=4 ENDSELECT ' HIDEM IF PEEK(buffer%+2)=0 ! Bild ungepackt BMOVE buffer%+offset%,XBIOS(2),32000 ELSE ! Bild gepackt ~C:ca_unpack%(L:buffer%+offset%,L:XBIOS(2)) ! Image Auspacken ENDIF SHOWM ' ENDIF ENDIF ' ~MFREE(buffer%) ! Speicher wieder freigeben ENDIF RESERVE ' WHILE MOUSEK WEND REPEAT UNTIL MOUSEK LOOP +SYWtgȧF^ɟh5Qն?K\$V q}ALnp՟JDbD%@Rgڻ/X?C2l-೭ogiوڹU⟱b09{];TG '$K#*>OZ2 `</;87%s!^d_l l"E(n) ̴XߖZ5L 'ҐtP(*ӏ^gzrNj|]kXJEu+]M0 Ty6aM5ȯK"fY)3n`%XZYOdrK(Ko":^i# ƻJsƼ,q}1j> Y_`} (>pT؅J/QQJrTmϩlA= k)-Eu?__:~soNo9-'!j;|7w/'~Z's޿4 Ώ7ljSæ&6A1:?O'No_;?~ߟ_pO{GNGi߿M/6%VZӧSN߮Nuz 9]gսo;|p;>?0 45}ZY3us.GLs~i(:| Oq9ͽo;>=.|O:}i9}[9/L V/k~v7u,wofqjWy|-<M ; Dekomprimierung von CRACK ART Bildern (CA?) ; Copyright Detlef Rttger 04.03.1990 ; GFA-Aufruf: ~C:CA_DECOMPRESS%( l:Quelle, l:Ziel ) TEXT movem.l d1-a6,-(sp) movem.l 60(sp),a0-a1 ; Quelle/Ziel movea.l a1,a2 lea 32000(a1),a3 clr.w d7 clr.w d6 move.b (a0)+,d7 ; ESC move.b (a0)+,d6 ; DELTA move.w (a0)+,d0 ; Offset and.l #$7fff,d0 movea.l d0,a5 move.b d6,d1 ; DELTA in Langwoerter bringen lsl.w #8,d1 move.b d6,d1 move.w d1,d2 swap d1 move.w d2,d1 move.l d1,d2 move.l d1,d3 move.l d1,d4 move.l d1,d5 movea.l a3,a6 ; Ziel mit DELTA fuellen move.w #1599,d0 delta: movem.l d1-d5,-(a6) dbra d0,delta move.l a5,d0 ; Offset subq.w #1,d0 ; Durchlaeufe bmi.s endmain main: clr.w d1 move.b (a0)+,d1 ; Erstes Byte holen cmp.b d7,d1 beq.s esccode writeone: move.b d1,(a2) ; Wenn kein ESC, dann gleich schreiben adda.l a5,a2 cmpa.l a3,a2 blt.s main addq.l #1,a1 movea.l a1,a2 dbra d0,main endmain: movem.l (sp)+,d1-a6 rts ; Hier wurde ein ESC gefunden esccode: move.b (a0)+,d1 ; zweites Byte cmp.b d7,d1 beq.s writeone ; ESC schreiben tst.b d1 ; COMP0 bne.s code1 clr.w d2 ; ESC 00 ANZAHL-1 BYTE move.b (a0)+,d2 ; Anzahl 3-255 ist bedeutet 4-256 move.b (a0)+,d1 ; gleiche Bytes loop0: move.b d1,(a2) adda.l a5,a2 cmpa.l a3,a2 blt.s drin0 addq.l #1,a1 movea.l a1,a2 subq.w #1,d0 ; Ueberschlag gemacht bmi.s endmain drin0: dbra d2,loop0 bra.s main code1: cmpi.b #1,d1 ; COMP1 bne.s code2 clr.w d2 ; ESC 01 MULT REST-1 BYTE clr.w d3 move.b (a0)+,d3 ; Multiplikator lsl.w #8,d3 move.b (a0)+,d2 ; Anzahl 1-256 add.w d3,d2 move.b (a0)+,d1 ; komprimiertes Byte loop1: move.b d1,(a2) adda.l a5,a2 cmpa.l a3,a2 blt.s drin1 addq.l #1,a1 movea.l a1,a2 subq.w #1,d0 ; Ueberschlag gemacht bmi.s endmain drin1: dbra d2,loop1 bra.s main code2: cmpi.b #2,d1 ; SAME bne.s multiple ; Komprimiert 3pR(NZY>%t Aߪ\w_zOMX?_w2$Ӳ䬀 n,Ү虒{ENGK_0: B_ ?&$!Ƃ8rueԤFdž((L18Q`(A*6 q#LQq{`p J}|X uSKgI,S ޢ8T5B,[QP5~|UEaeŲێrᾌe8#4~w.  nG.. tnX_SPU TOS ;9HȋREAD_ME 1ST 3Nk^` &`Pack-IceI *L,La*oMGLA "grѓg f``GP$]t***Ԋ**EL ;H ;C&NN&&Sj&n".扑&&SjN%a*d"ra$dCv !a"H@@Vҩ%QnNuf%Nurf%AQNuCtaTxr1 Hka1 Ag"CvtaTr1 HaBq jD`rptadpt?aBC@!!Q`R   ICE! `xxD}^**3Dl@ZIP!}$NVl/H xGIt *I 1\v/t:E9 ,@$4BCo:wQ/ 7gffXgZJ_lB{"`RB[W~J1 fb|L|E.Gp~v`8k ;Ѻl$ҩ j.) \cd]ŅP(",08fY/IL~(_ fn0{yO+L6[^?RE5 3;̀>Q,͋0|nm Ćmnbm7mJ}|lf8i$\L#[pd[dl+>l(GV9ƻSrJ+A|2p"mC/B14/tІv" Tl(A|?-dWb/ C]<.+ PKp>L"2'Q%nmflۥ[N5_o t.^/CӖ+BJH;B33NWn 3/3f\0RCh ҚqpMpt! cLph&i&v`M Df>avJgHPHS?~8JDlݗܪvp0O0vsg\ |Gv&J*t:t4t ̂ҭ # Mr2Cĭz)/ۄx̶Z6r%ִ5t#8r3IiJ_i/%RH`"i޲g}) :ۥN zjEP\A Wącrҁ2 _Nי-rxWC ;_Cv/ iܐ HV6$?$j8g2=U?* AN_A.BAHAҌ(ARFTHHf:YN<*8< self+ v1.1 - (c) V Pf~e1992!(na?liNe9ughjkm%toHT uXSZSTZip(Invalihd, damag. []E :;Cannot3ba#+L[ncrypm~f;@ri,E while~xtractin+5CRC errw|3 Unsupported compQion m!hodZ*Press Return. llHj %/*l+.x ;" ˀx$HBJW )f&fmf f( S߀.]p%0b HXv$_"_S@dLj*X pe35% 5xSuL<=9Zb7<^2x+>Z iT-`\Z5,< ANH<- /X  @Nh=2ώ `` /.WLN^?ZP-vUNV 2C"?LB o|B@$I!Sf+H (Ш"h$)r"҈.A//?ۿ0JO %f+@ܝ#?jaBNAW?f~o#ˁ:*}x/J:i\Q=JBf.r&J@F.5h Q^gYB@F|~ _>NJ^fϲ\$Jv&KTFCCgpOSBrtFt\ \oszpM2Qɬ;3*Ck %d10s qk< R@:rޛ8VDp8MC^<23~0(ZÎZaf9EZce042vsa  ݒwl1,0<Rp}sfu43e\*Hm̜ L@ERG,"j#C MԬcXVKrjf(`" fxApT@ r-d@ B?e?\\G@uAeL&Lk>G5xapID;G@,KfUBD|BE4Pf"<2tQP`<<żGg pFvk>CRE EeRF FUOf$vt2&4&0&v@xlSDzmV?(EQf?eNq Wd6Ѐ01N>m xOl SFf`*gycb{B9`4@_4' 2 .I#3 R08b`p1-BCNC/0d c &Ts 23v@FeO<2^BSBSIILH@-g0dr cGRH&Ki, K0S@*nMLpA"6b#6n/ /?Ҿv?<@NAgo"pX`p~0< L@(y+|Q]9gFLO?Nu`*({F!bn %;$ǔTI#+3;CWcs䘑'"5X$ȣ!1AaO  0~$`C??I     {A7pBrB* ,`f J Bf R@ @fN?Y+ 9D?Z²!LSTY{sFUO ^x T⒥VYx$lsϞ=F,YxUI{~Lʐ??9<(Mp{h c_@l;oy`y%K,&w:ij:7 Qm7smpOA,~)$۠V.qq3Q:nH2L}@HxoEp*oL|=!~?#CD#Y0oĊ0\ύ7*'C=a)c荻_6)giz`bvd'^JdjLK1=VGUMQCYYޣ_<'o冢8bˏ&,rWrgy_ KD=JE>uhg["~LD$n|*l tfi>'MǯtS\dh<Õ3ru9k؎%E+ԋ\6~@JQ=kΰt,Ķfr`ȬՒ/of叇@-qnlH Gd۴R'4܈nrmb1la @TN-VA&C+P3p^Һ%@fᶍ1'갹zɿ1˾ȶ|ø }I]_a\&[oHV5:0:lJgZk~5M0!OXQS'T@;Pek: =+)%h"s ob3dY? - hFJx7-79ft3"Vo?EOU*UARWR(JZ*B3O%aalG#!&8EC\*Wdzl9+iVtUʯK%˹_k hZ*02Qzu,֩=タja=!%:ҋ#z58OOdpoV [Juj9YޛOu0\czTN瓄tvC6- RhZU|K_eiVJV|>,oKx#vv)X۳Q~[ojlS< ! +EH\I٘T]>~2fyס;z}D/DΠ {ʩgEt>Fil_/u&[ O4h'sʹ!}&/"ɊWl 3CzKd+ ځ4ҷ1#L4#' 9m5 $H@Ÿ,A{\n,O%@ hJԑ @K_cK^_F"54@*/ =Hj _?[t3]X6`ZoFM`Q7X8UևQ]V cF(@k*1˷^ _oդx3/r_"\5@FU)׏U}fQn[\7x誨#WՐ9}=kA.ijh/xi1ep#fͅ{L:>_0:q{C^!&aobLѦ."~WO 2#<ŗ[?c7Zlߏ^{rUASaqJ>Bk=5{^'Go= pz\/~ c[-66]c"5MY&ۺMm'10 1/P?3ؑjݮl&W$<эM ˏ8VʭERPN˜lUK?{ UUԜ:Xžk!;%+=w1YM~~&L_scrjŤ!)0ٸC&w %̮:N]~.nD#`Lw`UsQbݹӷdZI8F``H\&{|pZ^bMҮ c KazTc\#OcmϗTrlo8z8MWHՖ vt.yiFF3{2^NFJJ]CA?3]֩6gg5n657T_AqLN[XZDI{It Pxi(N^ܢ-~جyu&t`'j| 7_rD3pWف8Eƣ:yCzmvapѤuy3{!4@I:t QrUsD%'6[y$End@lIR5OI\C9B*y`:BgHGgNŕYNyz:1f=Tsow=Sw;2_Mģ`{"O@|W6wv\[jdaREfb3~yb;W3¬7|ܤ'A'9 Xl":8xyԩJCKm;^.J<g^/Z!jeE6'u,=bqkl8H}2&X9Zf wN_Ni|u$cD3m|#)ϰzZBZm0z%#٘d÷:/2\MMOEGk+m:wyK#2pF"=vHY</90(RL9+'8PFN VuL}bskvdvJ6gO|Sn8{qPymd؋nP}3`}}:Wk [4O+.E'[׌'Te􍷨]ݫ;bjLF gBK=e k:=wΟOAMONDc+ "I{lUkܫnո"('~LmH S*To8D-0N+Ƃż nxʜQe4w8s YO]G1 ?v*?Lr0km'2E3Sf{_aRQαͽZJا=-gR2Nmsͅ. kGH5d%/xg`So0vR /<|MNo?0@_.ja*,T}ZxG a  .!%_,;! y6Qw|1g9g2f䬓Z/5w X|*SE6CD  >&@]=o2H~2A}ҩJGSA^Jn1YBCeٰw/[tE$벼lD_ ӰѠXٹj w1n>%?024p΃4E<k;9K"Xy¬lIFSQKcԓ K `|3s i2Zb v^s_0I7a'8&4buuzM9"ncr_hׂr?qTpTU2kr_c/.[(O_1 3wkLLf,:]zʙ3G)>8'S?PцKǪrd/͊8^Jv˷~[cEh|J,|P6 >6W dNƆH4F_tȓI$ pao@@0 "$E$+.!QZ(Bi*DN% F$QgUsނ!_7}Ov! BhX%ى[ESVp3Ɋ a+&"XSH`};pWfhg5tw"RJN}iL9{4@[**UJAO7x\V||sd􌨑.>3=!~jp^ 8hxl=쎂M$ça] zKр_x PL%9x/7<(uc)d[i32*iD<{hBFݛG 1EgUx#FT\y0+y3殇\FNIT8}_Ams&KMU.E^)2_MEק>y4ϔamV?pwlj fFxWOh:(ߙSu?ALL4.IAH!y⿊ p5B-CiW#g }YQb̸iÂ?4̙>ҙ^rt0/ B2OG_SL ?nYiͶ)Coth nÆᬹ#*'׮`浕Q?GH_y磵e5c#:5kBGl:j( B8[?D7/F? ?Д'VhS=Ĥp/b[ T~q漢# 6_DDn[ Dz2x'x+yRWu?DoXۈ!01PyfHY;uvO>煿߷w5"epT*NZ'g:,8)ښ\R#aw spB 4G>LFK2.]9Zb+nzc۬,fTl9GdduaUWܞM"ke^c!J=RBd,@ħ9xs1j=!&BoJZtem[s}c#.!S_nvXB9P_:k0[N,:g5$Nek#N'"|BrDP(;1`9{LP-$JY6Nv&mo}1@*UVm~X61J0bh4Mq NqR{oTTbr~mAėĴxm6a|H@Z,"@\ȃBK':ޯwcc[ 9AҎcs_>aV{GߞyS.0l/ ǁ=d݂H9 j?1T1'nyYWF6`x SU_0)w܃3-o#F5L+siZChVm:~A+e49ڦ`SO1%X+_igRrr|)0K(\d.`i _AUhTPYJ[]#ߚ8\wJ;okhs1 ν9TB'uw#Brs]#9>Pt~QZ߃sDߎr~> -MsU-.Ȫa~j߳ d=&Kn u9y>YP+_H$QT?PctP0Ƒj`_}꿞bN\gCn>9rs,n輁т@Uk =Yƻ^vMzs ^׼ "\Tlg/DduaIJ8A}#'F#^e)z9Uk̊0Su!+- @#=zZ 7y`qQy?X0{u}ɵ]ۚ2\x02OĩH#Gr 5Pxi7-]ݛr'WҞW o0w1ߝVߏ8#D:1TW|z.Z|;K5MA}mԞg%OdY,|!G?)d K=ޗN2ϧP8-BGr:Uσ_8R4u q ),.C3@ anPIA:w:`+ذ7rN yzl#?=>I<ƆYSHB:LV:B=v5GeRK}QbRըMP4@WMXFCGzIPDBWWCl/I(GԼP7q8N'o}?ޑr`NK oyJ㣑{t Crߘ٪Ow0w 8X%+~`w &&DU"M߇Mi,<'@ Hr-&YhVxCzsdWYs]ŋ!(N!lLNQz0eM:wG-;=Nrt6f" BtּT_,ÜTRnd̽dRXci?{WgA/G6cNj v|&`N(Z1t@g4Um?4zKvZ$یn[.sw,A)tౚX HRᓲH`DϫU78 hĻ~Ǽi Q&YOM;`1W]N{hh;ǔ5 NR9gyƠ6m4xx69᱑ N_y}0 ^E79H|§ 6P;γw/tx܅kO7 &s 8[ {LوPfodR`fGKu-'f|!~tr^<ښn4 ,,ڱd׾SMZZeJ{1 +G%V?iJ7ƥ n$h?6xrڙM*vɏ08 4l7 qIԻ\nj;Vŭex8m)?:/+ib7PTc+$ŋtFJ(/ !QBK"E9 n$;}=]#g4iR[Xݑ&?*)~=x qvYӚK6EEOMo4[mBhG)/CՇ y N˷ YcB|~I?ZCMET/͉H\7zYd+<_}3X̗܌wxD"-x&3yAoz͜X;ezjy̸ :34B"|,.qOhvS;[;};M6i70piQdl7ٽ|L0l/n+Ztּu˿ 2`ʟ?*O{(va"E@4gva߯ڍ@؃_ +*=P{^#$Vxrha,Xw6-šZϳkzJHƎv[t˵j3$R_^zWaU!C2MqpQ.7"$c^(zGKKQclJC? 71- G)P [!xHЕy&Qې|-eoyZ,wNv'[8JqE]$:+3)fϏImg+(m&40H L6栙24"Reק֪r0n\$>6FZaJLH` ~J Q绾cN_[ R}1*]/ k&^}G~y #lnȐTx>p.%LTp _`*!gđ#l<5M˒Hs;w'4`%\:_Y0<~OqS5{~nN R}o( ܫ+[DyRLqE~ߪ͘_.S+M&5Z6׽U3 n}lɽh4 yEbN^ 'C!dRylw_35㱫@&[EGYFgc&4*DAT< C}1nӺLۨX2by7DŽShMxj+b}N>SPfOo@gWJ^PXRoeh[AϪIuZ.ե4ĻXș v-h)]<)/zFRg!?#1cM,RTNMAXC:+· 05-m5x,ޒ)'} KAvVS(Zj^/w"[S*e<̉~dzԉ?(j&ud0eAe6%4k,)]}e}t(s wSYL-[J}250ejީ. Qć>{]Sa\7q3薽FBO9[o.<0:o=A_'p)qԓmg@SOl8zXҙ5KX6z無c']'ɼ^Csa{gW;!38urO:]`!^=PB*lA`)& LП7e8RokBYd% QJ{RC UTpL { 8LՈMRc8Rl4׈-(p2p O$,n~"V[ UPJyD,F`,!.*K$GB*6"6DA={9N_jPn~HwwD}f5|;^nѸ!+R)kWCb ,x_sfIZa&k}`_NnoNw{_yQT(n.gk҄"gwd/-lK/^JeB)k+ oĄ66^.3U\sƂJxِ K؟AE)(|fhb0mY `3FGcVG^®/zU>}}[w)^}N#dsqmԂaL@#5tc~[caMFVkvE l̲yMSi}ŅAd*=#-̴ I]I\=5csIK7oۮ/ao8/ئ^-Dc&>N6K!] 7-,=V!Q q}Ty#tx .{9bSf{U=y2l8%^iVny8n$LݳS'ؓgunЬ7u-\CLS{`kل[WKble}c3#ˊ0aW O[&7N 퓖WO7)Ï_ [ < kO&٩ZWCNnB ( _$2+; 7~ԫZ'OYn'[S(:eoLT97l'>?sqw_~ ?3j[Զ<e|mR_fXzNd],b3lxWmR@Y'Z\w6߻w~ `HpU%ߊDN()[&rաa^&CbҴ h{5^W3[u՜֭x3-JUaڦՉIq>~@DGgLg[=]ܝY *ϽaG<|v/HZs_m#P Q$C_[xf<)Euئ; Zں'_X#\˦vyzr? i7 !3՚MԸ6+`69z[X u:++zЯ̇N~k#ÁIsa>?x81=;bg͜6с/9a抈 iSOO9)g 9kF5RV5Z{ىd~凨?զ$e[L\ߔݭ & i/WgN^5>jȮe|v.OU?g&$<*e ӓ`=< xp F.]KG9A/$)ZL +" < nd& Hr31͔t &gad%Ib!nS0Wŷ+̈́4~p#@!Ƨf$$csV"('fK@AFAAӼ&?0Owj^j72b~h+-c0 ~ `8$J,d0R>[?jڃ00:8* OR 1hcq XC+F !PK*Tܨ(GǰR_SHOW.BCC/ՏHERE_3.SPU]o%Cn`uPa>v %QЋҘC^7 J"PCݖB&R{ `B"R!h0[.Ekwvo߼RľžM*$}D,~QKjK[$Q&\BwKT~dA&{"ιsżQ?ӗJ ;ip5ԗ~p3oS$U?B7t}^(8yr߆9,Aμ]VArqҀ.#g-mװ@8o,z'ee,HMawP5"x=[%[es+w $lV/qWpy{/T]1ZD-z|HÏ/=OC GǻBl30C }(E<.Ƒ_{{1꟮H? g'Scb%]:tZ}Q%KB6O)I?'7(rF؉) ͂p4>#i:B9MOAw߂/U,9ܝ;`t¯wwq=|0#ƍO9iczZSx<:- zKm˖Q]V#)瀄tw'lqE.^R,VzY>=P!XLT>< _uدA.DO#{YϐI3!؈j|h?mDUD01k+[M[w3^g̣U bp8 9 ]YR#柁+o#턚W[QaTل!~Yy8wZ+ ہW8aÃ{ # q +Gc_Gryzz {K&v#I7PtiG<eiaj[gH?e;[3{ߥy+[- / =L{Jкlez+/[YD)/R^R_q`_o?/\Y ?[)bx8 +a#덬_@_mğ,i{)|ΓqAۏ'({gQOd3b,WL-{flQuXH~~˛?{zpg젝Ɗ~Ig,a.F,7mtzmU򊾢OγdSj ޚKbXۙ;uIgRPb;zuuLebuO]<M=9E_+ŤV$, ujz]٘|M 6ͬqy<>>ssj/BΛ+Un ߷aŏqFgϞ]Q53/g֩+?A[ }0KB(vS %=?{8_19,C34?/@gR[$6No6;S4~9_5U먧Sj|iԇoY?}&Dg-uګKoXy[]?)HFS8ca)y a.'>T^? w:/ Cg֋"utx*BԽ;dtO;=_bo@ ^}/ [-?.8߀t&` qW ΟOI?kgx7èg+u~?~uQ_}ݤށ%fj9E?dv8k a~Xk4ߋ_A}l?nM˽߿[0w?1}t3L2Sůh~U-~_2mZEl̒F^}Z?oVIVY6HI#WIx ~˂FEt,O@[%YOjI. BN/Y}%a>-0-$#>߭ [2)Y厯, |+$i1n|z2o0_`~oiYM?yvŏ3>6'_1o~B ]'a|<6lūg>vƯg}\ qy/$ly-W7;mlo3i%IQjvdcH\Cp?M O7Im#7F2 iC7o%M/ˏ 2睶7ot;-Nߞ'q_7 1ލ\Mӑ[j\(ߟ~k:q=:=>ZQ\;G;~Z7׏?N_Qo r|^ԟƝ #`O?u?|0|p~}~Ҏ!BQw*oۧ~FLJ\G)Ms_4/^?yӫtGppSxp̥I ?dGƟ^ I{[z5 ~/Cnj62Aۡ=x9g00Qןc~ /?ucDў?g!OoOeAo;^#Q_r~O Fw~>¹.Xo`1Q 74giEp7u׏# QG}~o_aO7yT쟏?kPKO'   _SHOW.BCC/UHERE_4.SPU]OlWzYM* D×,6|ZB`{X"@a+--Abgú2}h7!U% zC DoNFIZiZ}gHIȱ{F}t6\K%mo3D0~KR%g\BѰ,!-i}n=/Nsp3`LFe o8ZDf0 O/}YBA]D0%d?w9I0JEaǏoV\6@+*մ.-ָ= ήe;m3e9G*Vy u-)I͸|wRǯO#9 =vǵ7`Q+ډq%I!JV2%@KX nX9+})Q3mPOE%ٙ Y[VKq{J8I3ԇҹtXuq^C$A I9#Z{E!9PXL8&t\bcB~D)N[NKLq|̍OZ-.&e09Xf ġ2;Kz; ⠽-&ޓ? ="G|ؖLi\Mm/U^#g/CP$# Sfz]^_W4 Śҋ/*Abeng6I5䟅#rFOh4E"3#Q ?w/ 'ehc%e^l_o'UKBr7W} 77N}EAF~!2.ɋx/@^۵8FIEq2dn^;rՉ~?a,g~O@'MrLbIc,3ck碠"궷 Y,Sgm"51ht%gv?="l2[%2Fˑ&—h02ܼl;m 6 2HfxȾ~Ć^5cG%O4Y{i=1@[ߥW }rHbteqp$kw2$v`dJ$"I(XԎ?5]}!.ZPGAo d s>_ynldiJde?R"gf Voψ|\VH\xZqpDN2uO=_IO}zևN h qS$#)n(IS$&'ruߵgf.nE6}/d⟢sĶW*D$&&=< YW~/M1.TVJR [F ߡ0F;K'd/Bk!俲hq>jg̿63o`G/-Fm5i/1|9Þ/WK%ʃÁ!R-#}KC7(@tŏ7?.|#wv<Icf!")d,jGh;N[[sEKU.Ba>^?o'3!CѠ%Hxw_d[cw 7Q7PrRO/d{Mpʭ)!鿚0ؚ:J77DpS w߅# o`r=%G?!KB|DwRXA>rllDcsVb翣_ac8l^?m_eD,⬿ϛ)B eo'E$J$6)b]픀YɄTuo\3k(f:%l u'/Y9>dq0%|Tq~Cٿx,?mc^7d ?&Wߗ2>#,Ɗ:d *G _W!.@0kFX6ʈ-#3! ]=W]+^m,_*|@Y$ E8E1F*B2؆8:BVCru+lJFeP,d̻%15 Uxaa/g5hI on T|O+A2ꨤWlOP`E9S3Qf#Qq2Y]Sm0, ?%h:^,S֙_I9ÿ|*UȐ4]l˞C3"hN`N d\:a﹗W0htw,xZ=$iP|G~/ _xSk#_2n#|!x_ -EGįGxO2?^Vd{'~l"UW/N"*//b};3+cwbu%2$}kmG(ۈ;I9;b6YO30=⺁LſWnqQbž?FNWg)C|Q1ֺЊ%E[ s*tv'V 5،P02A%ZDz/y'/wNkB,2u\?$?`ߞ4q^nڂO(+SYWtgȧF^ɟh5Qն?K\$V q}ALnp՟JDbD%@Rgڻ/X?C2l-೭ogiوڹU⟱b09{];TG '$K#*>OZ2 `</;87%s!^d_l l"E(n) ̴XߖZ5L 'ҐtP(*ӏ^gzrNj|]kXJEu+]M0 Ty6aM5ȯK"fY)3n`%XZYOdrK(Ko":^i# ƻJsƼ,q}1j> Y_`} (>pT؅J/QQJrTmϩlA= k)-Eu?__:~soNo9-'!j;|7w/'~Z's޿4 Ώ7ljSæ&6A1:?O'No_;?~ߟ_pO{GNGi߿M/6%VZӧSN߮Nuz 9]gսo;|p;>?0 45}ZY3us.GLs~i(:| Oq9ͽo;>=.|O:}i9}[9/L V/k~v7u,wofqjWy|-<Mqrv4 !e??U5tӜi|ƩT˯+Ƨf7;Zlѯ3L;mCgt wM^g`r)x?ik{>Kk2OpkTs O;G짵iz:kzeifPn8s `Ccq/uMs_2_4pokF/rڞύo{߯kj4kBRCѻ4_*}3kC#k>wR}0JN$hYCYJjGJz|B[ζ̭_P}ߌ~-]_Poyl %ݮi}< ~t;7Cw l@>coFZ?>-h~o0ʰn|_GN?p^;$-}D>VyX~,_3g >q "v䘱ÇAWkQ@Wh!@?y=A?4ѣ7CsCnld+_`xzsƁxPwG&4~efϱ7[YǪCm#o^>C4}PTF؝Λ*:T&J;Xk!0/^/,Ӈ3}7/$:9%#өD ?g㇕gR08бj:'k??;gF_wmc|og7}gLӍ̓d>WM?>kR҇`Bh-?@,}w2 NU>~uw(5Ma"6ѯ85~xjù9ZrУkcwM/7O~Oz%~F@3W+8]YKȼcD?OXPgj߄>GNh^`^? ئBƏܪ}Ώp\xw<~w|*D;~[_٭>KG^=;/\?zk߽?ڱÒ'-yycz{^??UȯzzSB l#};gػzy oy?XTz}ݿ>h?^߿~~];|ǟw=[1 +s\`X@HEREwuS]o0}p(*ҴeL-ERi3!i;R~vM1HI=s9b,b2}iy7G;V]d04t\ǝfB~4- > dEnu|JFۻh]5Sn 3(vJ(䪋/&bY =Sd Ca![<ϩMHRZ::m0(^ޑwJ*BrƹB3&y,Ol $TfKXTn *ÔLȘ,Xj+s$X 8 Z|*X jEd''5lRh?~:G,:W8:ΥAnfN[ qVG BP9!"**C]d\Y0\PzVQ6,E ۹tA&M纉YBm{ɳ+LFu᩠dke뫝탲17"3DDI̓ZFH+딹BST=QDR6z?N ,TT,Fen,$7z$|GZ*j<-(iM|(-=>x#=M(\)y~뿐}.پO5Eֆ6!~,-DaGlWzPKT+kh;BCC/SPU_SHOW.TXT[rFyvK"#ZǖʒhlKN&J@I"Nn^=sJJ{R5aUb>}\t\V i깺^fU|^Fs]"tUޅ)&, ˏ=kMW>Vo_= 4ԋJ>=:.fEUGF5'?ZUg~kq`u."sJR|N"UE>Poz[:/_cUw!\ģn& M6a}V)BqQX9c"A~<)!J~#v:/NzN+_͢TQ$J5)פJ 9#+NgU:趕:xER$--|"I'+݆s,c݁*425D?IP+(٫Ǫ,jkBKg})"\'SNutAb`h$ʹjW/:-Թ:MK] KzE\7ιYؤzB 벴8j-ʎ97C/<ʲU"RƂ8z5P:}z(L d;}m=]c:wVKPuY^,quͺ0M8υ&ү@3g'i|mzT]KQ X' 8Kc+@KjCX _? -њEĆE5**][UUdAP )jP`&Y{AajiٵzJ$UjI: e16zjEHde:29?Pj9+͐vi OT^[H "* 1`dYOjE^8  `h”PWX)3X]^Q= `y`9l-c\EЪt 9.' D\$5P׺)-PQQeW!edbƸ;WK&H&r"8AX ֣Z-zY K*r ~mm8AzAHт]¢kV@A!58J!|j+6uπ2JktpB.&XK=QRLzQYsb՛R@S@a~k\9,3).سa@%l>o/GöxwVQæMt$Ɔw VꈅM"U&w-}`=l^FBDALJ\t |(Vć5EVS}f0L5,$ǥ!@Cy 3 ZjY+u*2A҃]ܽf@5B2*ͮ }uOߔ};^07PQ%I$)8-6<ܤ?9YxQQFPkm2b*a%T&y8(&N6i6 !s3o7*e;m $tAv؍ "h~$,I vj9 X1\ ,=~y2H=Jğ$h,W?z 6O6YFռ9DjQp:dk2'Qdyceq} ?W,qlvwʽ)r-)W*S)P27G&E!FcN~Aop5qIR(o苙8o-cǼNmwH-g:uB҂~PaK/(8(F+tkSv>pR(NZY>%t Aߪ\w_zOMX?_w2$Ӳ䬀 n,Ү虒{ENGK_0: B_ ?&$!Ƃ8rueԤFdž((L18Q`(A*6 q#LQq{`p J}|X uSKgI,S ޢ8T5B,[QP5~|UEaeŲێrᾌe8#4~wĹZ2et I 8zra]Z(q6y\xoU \8gڽۭX n$4U)BzY45(U?ENĉ6Ol%)H*ZV$bɉ'W1(ܻO+` RVQ?I m c3|Ȟ%|CYT.[×7<ՠ|tp9 ;o;p][ 3,9qi!=fzoGΏ#\~| ooD~1XNB$i~oO=zYX7m @sWayU1Ƽf#!:xxv֪ifd^ \rcI-AJֳ0Ơ݇KD$X3m|3.'D/*1 賂VI I勿K|N#TY_YTJF^ؕ(ۊ\4I|'M~'y!G=~|>I .A`''מ: fr6 fB|52MUp j!F ٜGlY9?gC"6[m_yjs'U@=o*ywSȃ$o=_նCiv7AُK"Gꥉ)i(Pl(|2Ρ },c 7P@i.\-7_j, 1icRʏV;ƥFcOv?=nu}V ,_iW;|=M:N.L)'!ѕ\1<ۏm帥[Z;ݴj6'ÎI`3sYQzӡ+M\̕K2W-Z;(8E ;w)"9(omRL\J?/ޥh]7) W(])wGҚs4Ѐ,hkcMmkC19a.FMTj+9̄2JI^~m㐬Aar >Z#P` S9ǍUNb)W-B(ІKgkQco{T]H_['2œ+VeXKX9?G4a>oFbLKf|`-H@ڬ/=.w^!f4 -@~4fҐN>z\񵝠/!18Q;#䯡wI%(;J*[OP`IMӬ5As WW4:xf"< +LjVt9A';RغgjFPTI䛵2n1?Jb'yη'qW?=wIK)H90DM.)mBAN':4 :4~gto5r:a^ikj}Im| ٴXLw9uWturFkM J^қNn; a*IoOxk$kd*_T;6FweBX0)>wM!w#y*J+"K VVT޾TĎm΄@m`BM)sX) f8umlc`woEeDOn.`[.|'k!{^"nzx=!/rCJzȟ@cjɚpעPI" roAQIgIfKV +nilV?ElnX{涯Qr)Yjۖt0i ^߽,{9))\DyL%dv ^?AW/0'v%hҳ>Mo fs,4Zo^ʻYa#Qd94r#`H]){}DM8ݢ0[@Ӱ=5w)۰fEk8ۛ/6ZWQ$7.\'sY@ 9D?Z JLSxQ)}X45qPRG|sܨ(GSP$ 3 ' ,.[M key and press . Be sure you have done the correct printer installation if you are using a non-Atari printer capable of doing raster dumps. Repeat the key sequence to halt the print at any time. The best part of this feature is that you can activate it from any program even if it does not use GEM. Q: How do you turn off the cursor in a TOS application? A: Send the VT-52 escape to turn off the cursor. Look in the Hitchhiker's Guide to the BIOS for a complete description of all the escape codes. ----------------------------------------------------------------- 3. DOS Q: How do I install partitions I have created with the hard disk format program? A: Select one of the drive icons on the desktop. Move the mouse to the Options menu and select Install drive. Change the drive identifier to your choice and select OK. Be sure to do a Save desktop so that the installed drive will appear each time you boot. Q: How can I recover some files I accidentally trashed? A: There is hope -- but not much. If you have not created a new file since your accident, you may be able to use a disk utility to view sectors and piece it back together. However, those sectors may be scattered all over your disk. The best medicine is prevention. Leave the confirm deletes option connected. ----------------------------------------------------------------- 4. VDI Q: Looking into the VDIBIND library, I saw that there were many functions which couldn't be found in VDIBIND.H. e.g. vro_cpyfm, vs_clip. What is the reason for not including these in VDIBIND. Can we include them or don't they work well? A: VDIBIND.H is not necessary because all functions return an integer value--if ever. All these functions are in the VDIBIND object library. Q: I was using vg_text to output a string. When I switched to line-a to output the characters of the string one character at a time it was slower -- why? A: When you pass a string to the VDI you are performing only one Trap or software interrupt. If you use line-a, you do a software interrupt--and all of the overhead--on each character you output. In the end, the same code outputs the pixels to the video RAM. In some cases, like this, VDI can exceed line-a. Q: What is the standard address of screen memory? A: Unlike older systems, the ST can have video display memory located anywhere as long as it is on 1k boundaries. Various device drivers may be loaded in at boot time so it is impossible to say that video memory has a fixed address. The only thing that can be said for sure about the location is that it is usually the high end of RAM. ----------------------------------------------------------------- 5. AES Q: Is it possible to track the mouse through the process of menu item selection? I need to change the appearance of items on the fly. A: No, this is not an option of the AES. The usual scenario is to stay in the evnt_multi until a menu_event message is returned. Q: How many parameters does the objc_edit command have? A: The correct number is five. The AES manual incorrectly states six. ----------------------------------------------------------------- 6. Desktop Q: I would like to know if information from the desktop.inf is available to normal applications. I have noticed that the control panel and VT 52 emulator accessories have access to this information. A: It is the responsibility of every application in the system to save and restore changes to system variables. We do not recommend an application going in to directly change the desktop.inf. There is a constant danger of user modifications causing damage to the system that cannot be supported. The best way to change colors is through the vdi. Modifying the hardware registers directly is also discouraged since you are locking your application to one version of ST hardware. There are also xbios entries for changing and inquiring printer and rs-232 data. ----------------------------------------------------------------- 7. BASIC Q: How can I access the RS-232 from BASIC? A: Where X is a character: For output use OUT 1,X and for input use X = INP(1). ----------------------------------------------------------------- 8. Development Tools Q: The function itoa is not in the C-runtime libraries. What can I use for this function? A: If you have a special case where you need a small number of digits in a hurry, you can write a quick C function to convert an integer to ASCII characters. Another way is if you have included stdio, you can use sprintf to output an integer to a string. Q: We are having serious troubles with C. The functions getchar and stream reading does not work properly. In fact a German version has modified "stdio.h" definitions, (sic) though it works better has still problems. A: There is another version of stdio.h floating around Europe which uses a #define of getchar to be a BIOS Bconin(CON). This does not help the scanf function however. There is also a danger in using this version because when you mix Gem DOS calls (which the C run-time uses), and direct BIOS calls to get characters from the keyboard. A symptom of this has been noted as "10 characters disappearing" as the DOS buffers them up whenever a printf is called. The safest replacements for getchar and scanf are using Cconin and Cconrs+sscanf respectively. ----------------------------------------------------------------- 9. New On Compuserve In data library 7 (for registered Atari Developers only) in the Atari Developers SIG on Compuserve, the following files are new this month: gemlib Works with both Alcyon versions. test.c Example using stdio to lst: and con:. qa4.doc May Q/A newsletter. ribed below: 1 = BYTE An 8-bit unsigned integer. 2 = ASCII 8-bit bytes that store ASCII codes; the last byte must be null. 3 = SHORT A 16-bit (2-byte) unsigned integer. 4 = LON. ny.. nlALGO_CNRTXT \}hz& Algorithm Corner ================ By Simon Rigby This month I will be looking (briefly) at two questions posed in previous issues of ICTARI. The first concerns displaying Star Wars type text. The second concerns file compression. Any members with general questions of the form 'How Do I ...' please let me know and I'll try my best. I cannot guarantee a flawless method, but I'm sure I can point you in the right direction or give you some idea of the possibilities. 3D Type Text ------------ The first point to note is that you will need to design your own letters using vector points; bitmaps would need to be scaled which is beyond the scope of this section. The points for all the letters would need to be arranged as if they were arranged normally down the screen. Basically, this section will look at changing points from flat screen to simulated perspective. What is perspective? Imagine being in a cinema and looking at the screen. This is our screen as we see it. Now imagine looking up; you will see a beam of light getting smaller as it heads towards the projector. If we move the screen nearer to the projector, the screen image will become smaller. /| / | / | / | /| | / | |<-big image projector \ |<--|--small image \| | \ | \ | \ | \| <--------Z axis It is fairly straightforward to see that as Z is increased, the X & Y points will head towards the middle of the screen, until (at the projector) any point will be a dot at the middle of the screen. If the centre of the screen is at X,Y and a point is at x,y then x'=((x-X)/z)+X y'=((y-Y)/z)+Y providing Z>0 So, if we feed our x,y points into a routine that performs these calculations on them, we will get a new set of x',y' points out that scale according to the z value given. But does this help us? We haven't got a z to work with and all this does is make our screen points gradually smaller as we increase z. Well, now look at the line from the bottom of the screen to the projector. If we put our points on this plane, the text will move away and up as z is increased. ----- | --- | --- | | | | ----- --- ------- Normal Scaled Along Z So, if we take our points as being x,z points instead of x,y points and say that the y value will be at the bottom of the screen for all points: x'=((x-X)/z)+X y'=((y-Y)/z)+Y but y=bottom-of-screen so for low rez, (320 by 200) x'=((x-160)/z)+160 y'=((199-100)/z)+100 but our y is z in this equation so... x'=((x-160)/y)+160 y'=(99/y)+100 If we feed all our points into this equation we will get a set of points to draw that looks as if it is disappearing into the distance. Finally... If we have an offset counter that starts well negative (say -200) and add one to it before adding it to our starting y values before each calculation, then each draw will show the objects going up the screen (remembering to clear the last draw before the next draw). The only thing to remember is that z must not be zero so any y+offset value at zero must be made into a 1. Setting a clip rectangle for the screen will mean that all points can be passed and only those in range will be drawn. I hope this makes sense to someone - try playing with simple line shapes and let us know how you get on. Compression (Huffman Coding) ---------------------------- Imagine we have a (very) small file containing the letters A,B,C & D. Imagine that the file is ABADACAA. Now, we can store this as eight bytes using the ASCII codes for the letters, but this is very inefficient. We can see that there are only four different letters and so could use 2 bits to store the four possibilities of letter. This would mean storing a table at the start of the file containing each code and the letter it represents. So, we can have the letters represented by a binary tree as follows: * 1 / \0 So, A=11, B=10, C=01, D=00 / \ * * 1/ \0 1/ \0 A B C D This would mean storing the tree instead of a table, which would make the compressed file bigger than necessary, but bear with me... Each tree entry point would have two values; a pointer to the left tree entry and a pointer to the right tree entry. If there was no left (or right) entry, the entry would contain the letter instead of a pointer (one bit of the entry being reserved for whether the entry was a letter or a pointer). Now look at this tree... * / \ A * / \ B * / \ C D This is the worst case scenario for a tree where there is one less node than there are letters. But is this the worst case for us? Let's look again ... * 1/ \0 A=1, B=01, C=001, D=000 A * 1/ \0 B * 1/ \0 C D Now, with 2 bits per letter and eight letters, there are 16 bits used. With the situation above we have... 5 x A = 5 bits 1 x B = 2 bits 1 x C = 3 bits 1 x D = 3 bits ------- 13 bits a saving of 3 bits - why? It's all down to probability; if a letter occurs more often than another it would be profitable to use less bits for that letter at the expense of less used letters. The procedure is as follows:- 1] Make a table of all letters (bytes) and how often they occur. 2] Take the two letters (or pointers or one of each) that occur least and add the number of times they occur together. 3] Put the two letters (or pointers) with the smallest occurrence into a tree. 4] Remove the two least occurring letters from the table. 5] Put the tree pointer into the table with an occurrence of the two letter's occurrences added together. 6] Repeat from step 2 until no entries left in table. This is the optimum tree for this distribution. * Look familiar? / \ * A / \ * B / \ C D Now go through the file again and walk the tree to find each letter, adding a 1 bit to the output stream for a left branch and a 0 bit for a right branch. At the end of the file, write the last few bits and pad out with zero's to the end of the byte. To unpack the file again, simply load in the tree structure and go down the tree taking a left for a 1 bit and a right for a 0 bit until each letter is found. Note that the length of the original file needs to be known to stop a spurious character possibly occurring at the end of the file, due to the padded bits. Improvements: Note that the compression factor of Huffman coding is improved whenever there are a lot more of one value than another (as above) but that it will never be worse than the original scheme (ignoring the overhead of storing the tree structure and file length. Therefore - Samples can be improved by delta coding first, then Huffman coding (as above). Delta coding means taking the difference between adjacent values and then storing this value instead of the true value. Because samples tend to increase and decrease slowly the differences tend to stay small. eg. 25 30 40 45 47 50 46 40 37 30 24 is 25 05 10 05 02 03 -4 -6 -3 -7 -6 You can see that over the length of the sample, the values will all become concentrated around small positive and negative values, perfect for Huffman coding. True colour pictures that are based on real (or ray-traced) images would have slowly changing RGB values. If all the values of red (green and blue) are separately delta coded and then Huffman coded, substantial compression can be achieved. Most formats can be delta (or run length) encoded, which will probably help the subsequent compression. ----------------------------- That's all for this month, next month is (hopefully) up to you... Simon Rigby. a.k.a. PoshPaws GrayResponseCurve Tag = 291 (123) Type = SHORT N = 2**BitsPerSample The purpose of the gray response curve and the gray units is to provide more exact photometric interpretation information for gray scale image data, in terms of optical density. The GraySca. !n.. nlFILEFORMTXT ˀQ Video Master file format: (VID,FLM & VSQ) ----------------------------------------- Header (32 bytes) size name Description ---------------------------------------------------------------------- long header Contains 'VMAS' word version Contains '1x' for version 1.x word no_frames No. of frames of video... word frame_rate Playback rate of video (see table) long sample_size Amount of audio in file... (0=no audio - VID) word sample_rate Sample playback rate (Hz) word seq_length Size of sequence data... (0=no seq - FLM) word no_keys How many keys there are... . . . Padded to 32 bytes Video frames ------------ 16 words of palette, 1 word for each colour 0 to 15 (the palette data is in the AMIGA format... xxxxrrrr/ggggbbbb). 8000 bytes of picture per frame, no. of frames in header. Audio data ---------- 8 bit signed samples, size of sample in header. Sequence data ------------- No. of bytes given in the header. Each byte represents 1/10s interval. 0 = no event, 128+key = event Key data -------- No. of keys as given in the header, each key definition contains the following: word start_frame First video frame for this key word last_frame Last video frame for this key word video_speed Speed of video for this key (see table) long audio_start Offset to start of audio for this key long audio_length Length of audio for this key word audio_rate Sample speed of this key (Hz) 0=audio off byte video_loop Flag - loop video byte audio_loop Flag - loop audio Speed of video playback ----------------------- Value PAL NTSC Units ------------------------------------- 0 25 30 frames/second 1 12 15 frames/second 2 8 10 frames/second 3 6 7 frames/second 4 4 5 frames/second 5 3 3 frames/second 6 2 2 frames/second 7 1 1 frame/second 8 2 2 seconds/frame 9 3 3 seconds/frame 10 4 4 seconds/frame 11 5 5 seconds/frame 12 6 6 seconds/frame 13 7 7 seconds/frame 14 8 8 seconds/frame 15 9 9 seconds/frame Curve may look therefore something like: 2000, 1177, 875, 699, 574, 477, 398, 331, 273, 222, 176, 135, 97, 62, 30, 0, assuming GrayResponseUnit=3. Such a curve would be consistent with PhotometricInterpretation=1. See also GrayResponseUnit, PhotometricInterpretation, ColorMa. $n.. nlFALCSCRNTXT jQ Falcon Screen Resolution Detecting ================================== by Richard Davey (fog@walusoft.centron.com) FALCON SCREEN RESOLUTIONS ========================= One major problem I had when making my programs was detecting which damn screen resolution the user was loading my programs in. Could they be in a weird True Colour resolution? If they have a VGA, do they have the Double Line On or Off ? All these questions and no obvious answer made me fiddle around and here is what I found... SYSTAB & GETREZ% ================ Both these calls are supposed to be used independently, but with the amount of resolutions on the Falcon it makes sense to combine their results to find out the correct resolution and warn the user accordingly. r%=peekw(systab) g%=getrez% The above are the methods used from within HB. You will need to have the Xbios library installed for the getrez command to work or else it will return a value of 0 each time regardless. VGA WARNING =========== I am yet to test my results on an RGB monitor, or indeed a TV should that be any different, so for the meantime here are the results as tested on a Samsung VGA. RESULTS! ======== Falcon Resolution SYSTAB GETREZ ------------------------------------------------- 4 colour, 40 column, DL Off 2 7 4 colour, 80 column, DL Off 2 2 4 colour, 40 column, DL On 2 0 4 colour, 80 column, DL On 2 1 16 colour, 40 column, DL Off 4 7 16 colour, 80 column, DL Off 4 2 16 colour, 40 column, DL On 4 0 16 colour, 80 column, DL On 4 1 256 colour, 40 column, DL Off 8 7 256 colour, 80 column, DL Off 8 2 256 colour, 40 column, DL On 8 0 256 colour, 80 column, DL On 8 1 True Colour, 40 column, DL Off 16 7 True Colour, 40 column, DL On 16 0 ST LOW 4 0 ST MED 2 1 ST HIGH 1 2 THAT'S IT! ========== I am sure you can see from the above table that when you check the systab and getrez results if they equal 16 and 7 then the user is in the True Colour, 40 column, Double Line Off mode... and so on... PROBLEM ======= There is only one problem though. The ST LOW results (4/0) are the same as the 16 colour, 40 column, Double Line On (4,0). This is because essentially they are the same screen dimensions but you still might like to know which one of the two it is. Well I can't help you here I am afraid but if anyone out there can then PLEASE get in touch! CONTACT ======= Falcon owning, HiSoft Basic programming people please get in touch with me to swap ideas and routines. Richard Davey, 10 Oak Drive, Portishead, Bristol, BS20 8QS, UK. Phone: +44 (0) 275 843241 Email: requiem@armory.com The email will become active again during October 1994. Please allow some time for a reply as all mail is forwarded to me when at University. Anyone who wants details about the FALCON OWNERS GROUP just send me a single first class stamp and your address. of the pixels themselves. 3="Palette color._ In this mode, a color is described with a single sample. The sample is used as an index into ColorMap. The sample is used to index into each of the red, green and blue curve tables to retrieve an RGB triplet defining an actual color. When this PhotometricI. 'n.. nlTIFF_LZWTXT zel TIFF and LZW COMPRESSION SYSTEMS ================================ An Aldus/Microsoft Technical Memorandum: 8/8/88 Preface This memorandum has been prepared jointly by Aldus and Microsoft in conjunction with leading scanner vendors and other interested parties. This document does not represent a commitment on the part of either Microsoft or Aldus to provide support for this file format in any application. When responding to specific issues raised in this memo, or when requesting additional tag or field assignments, please address your correspondence to either: Developers_ Desk Windows Marketing Group Aldus Corporation Microsoft Corporation 411 First Ave. South 16011 NE 36th Way Suite 200 Box 97017 Seattle, WA 98104 Redmond, WA 98073-9717 (206) 622-5500 (206) 882-8080 Revision Notes This revision replaces _TIFF Revision 4._ Sections in italics are new or substantially changed in this revision. Also new, but not in italics, are Appendices F, G, and H. The major enhancements in TIFF 5.0 are: 1. Compression of grayscale and color images, for better disk space utilization. See Appendix F. 2. TIFF Classes_restricted TIFF subsets that can simplify the job of the TIFF implementor. You may wish to scan Appendix G before reading the rest of this document. In fact, you may want to use Appendix G as your main guide, and refer back to the main body of the specification as needed for details concerning TIFF structures and field definitions. 3. Support for _palette color_ images. See the TIFF Class P description in Appendix G, and the new ColorMap field description. 4. Two new tags that can be used to more fully define the characteristics of full color RGB data, and thereby potentially improve the quality of color image reproduction. See Appendix H. The organization of the document has also changed slightly. In particular, the tags are listed in alphabetical order, within several categories, in the main body of the specification. As always, every attempt has been made to add functionality in such a way as to minimize incompatibility problems with older TIFF software. In particular, many TIFF 5.0 files will be readable even by older applications that assume TIFF 4.0 or an earlier version of the specification. One exception is with files that use the new TIFF 5.0 LZW compression scheme. Old applications will have to give up in this case, of course, and will do so _gracefully_ if they have been following the rules. We are grateful to all of the draft reviewers for their suggestions. Especially helpful were Herb Weiner of Kitchen Wisdom Publishing Company, Brad Pillow of TrueVision, and engineers from Hewlett Packard and Quark. Chris Sears of Magenta Graphics provided information which is included as Appendix H. Abstract This document describes TIFF, a tag based file format that is designed to promote the interchange of digital image data. The fields were defined primarily with desktop publishing and related applications in mind, although it is possible that other sorts of imaging applications may find TIFF to be useful. The general scenario for which TIFF was invented assumes that applications software for scanning or painting creates a TIFF file, which can then be read and incorporated into a _document_ or _publication_ by an application such as a desktop publishing package. TIFF is not a printer language or page description language, nor is it intended to be a general document interchange standard. The primary design goal was to provide a rich environment within which the exchange of image data between application programs can be accomplished. This richness is required in order to take advantage of the varying capabilities of scanners and similar devices. TIFF is therefore designed to be a superset of existing image file formats for _desktop_ scanners (and paint programs and anything else that produces images with pixels in them) and will be enhanced on a continuing basis as new capabilities arise. A high priority has been given to structuring the data in such a way as to minimize the pain of future additions. TIFF was designed to be an extensible interchange format. Although TIFF is claimed to be in some sense a rich format, it can easily be used for simple scanners and applications as well, since the application developer need only be concerned with the capabilities that he requires. TIFF is intended to be independent of specific operating systems, filing systems, compilers, and processors. The only significant assumption is that the storage medium supports something like a _file,_ defined as a sequence of 8-bit bytes, where the bytes are numbered from 0 to N. The largest possible TIFF file is 2**32 bytes in length. Since TIFF uses pointers (byte offsets) quite liberally, a TIFF file is most easily read from a random access device such as a hard disk or flexible diskette, although it should be possible to read and write TIFF files on magnetic tape. The recommended MS-DOS, UNIX, and OS/2 file extension for TIFF files is _.TIF._ The recommended Macintosh filetype is _TIFF_. Suggestions for conventions in other computing environments are welcome. 1) Structure In TIFF, individual fields are identified with a unique tag. This allows particular fields to be present or absent from the file as required by the application. For an explanation of the rationale behind using a tag structure format, see Appendix A. A TIFF file begins with an 8-byte _image file header_ that points to one or more _image file directories._ The image file directories contain information about the images, as well as pointers to the actual image data. See Figure 1. We will now describe these structures in more detail. Image file header A TIFF file begins with an 8-byte image file header, containing the following information: Bytes 0-1: The first word of the file specifies the byte order used within the file. Legal values are: _II_ (hex 4949) _MM_ (hex 4D4D) In the _II_ format, byte order is always from least significant to most significant, for both 16-bit and 32-bit integers. In the _MM_ format, byte order is always from most significant to least significant, for both 16-bit and 32-bit integers. In both formats, character strings are stored into sequential byte locations. All TIFF readers should support both byte orders_see Appendix G. Bytes 2-3 The second word of the file is the TIFF _version number._ This number, 42 (2A in hex), is not to be equated with the current Revision of the TIFF specification. In fact, the TIFF version number (42) has never changed, and probably never will. If it ever does, it means that TIFF has changed in some way so radical that a TIFF reader should give up immediately. The number 42 was chosen for its deep philosophical significance. It can and should be used as additional verification that this is indeed a TIFF file. A TIFF file does not have a real version/revision number. This was an explicit, conscious design decision. In many file formats, fields take on different meanings depending on a single version number. The problem is that as the file format _ages,_ it becomes increasingly difficult to document which fields mean what in a given version, and older software usually has to give up if it encounters a file with a newer version number. We wanted TIFF fields to have a permanent and well-defined meaning, so that _older_ software can usually read _newer_ TIFF files. The bottom line is lower software release costs and more reliable software. Bytes 4-7 This long word contains the offset (in bytes) of the first Image File Directory. The directory may be at any location in the file after the header but must begin on a word boundary. In particular, an Image File Directory may follow the image data it describes. Readers must simply follow the pointers, wherever they may lead. (The term _byte offset_ is always used in this document to refer to a location with respect to the beginning of the file. The first byte of the file has an offset of 0.) Image file directory An Image File Directory (IFD) consists of a 2-byte count of the number of entries (i.e., the number of fields), followed by a sequence of 12-byte field entries, followed by a 4-byte offset of the next Image File Directory (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD. Each 12-byte IFD entry has the following format: Bytes 0-1 contain the Tag for the field. Bytes 2-3 contain the field Type. Bytes 4-7 contain the Length (_Count_ might have been a better term) of the field. Bytes 8-11 contain the Value Offset, the file offset (in bytes) of the Value for the field. The Value is expected to begin on a word boundary; the corresponding Value Offset will thus be an even number. This file offset may point to anywhere in the file, including after the image data. The entries in an IFD must be sorted in ascending order by Tag. Note that this is not the order in which the fields are described in this document. For a numerically ordered list of tags, see Appendix E. The Values to which directory entries point need not be in any particular order in the file. In order to save time and space, the Value Offset is interpreted to contain the Value instead of pointing to the Value if the Value fits into 4 bytes. If the Value is less than 4 bytes, it is left-justified within the 4-byte Value Offset, i.e., stored in the lower-numbered bytes. Whether or not the Value fits within 4 bytes is determined by looking at the Type and Length of the field. The Length is specified in terms of the data type, not the total number of bytes. A single 16-bit word (SHORT) has a Length of 1, not 2, for example. The data types and their lengths are described below: 1 = BYTE An 8-bit unsigned integer. 2 = ASCII 8-bit bytes that store ASCII codes; the last byte must be null. 3 = SHORT A 16-bit (2-byte) unsigned integer. 4 = LONG A 32-bit (4-byte) unsigned integer. 5 = RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator. The value of the Length part of an ASCII field entry includes the null. If padding is necessary, the Length does not include the pad byte. Note that there is no _count byte,_ as there is in Pascal-type strings. The Length part of the field takes care of that. The null is not strictly necessary, but may make things slightly simpler for C programmers. The reader should check the type to ensure that it is what he expects. TIFF currently allows more than 1 valid type for some fields. For example, ImageWidth and ImageLength were specified as having type SHORT. Very large images with more than 64K rows or columns are possible with some devices even now. Rather than add parallel LONG tags for these fields, it is cleaner to allow both SHORT and LONG for ImageWidth and similar fields. See Appendix G for specific recommendations. Note that there may be more than one IFD. Each IFD is said to define a _subfile._ One potential use of subsequent subfiles is to describe a _sub-image_ that is somehow related to the main image, such as a reduced resolution version of the image. If you have not already done so, you may wish to turn to Appendix G to study the sample TIFF images. 2) Definitions Note that the TIFF structure as described in the previous section is not specific to imaging applications in any way. It is only the definitions of the fields themselves that jointly describe an image. Before we begin defining the fields, we will define some basic concepts. An image is defined to be a rectangular array of _pixels,_ each of which consists of one or more _samples._ With monochromatic data, we have one sample per pixel, and _sample_ and _pixel_ can be used interchangeably. RGB color data contains three samples per pixel. 3) The Fields This section describes the fields defined in this version of TIFF. More fields may be added in future versions_if possible they will be added in such a way so as not to break old software that encounters a newer TIFF file. The documentation for each field contains the name of the field (quite arbitrary, but convenient), the Tag value, the field Type, the Number of Values (N) expected, comments describing the field, and the default, if any. Readers must assume the default value if the field does not exist. _No default_ does not mean that a TIFF writer should not pay attention to the tag. It simply means that there is no default. If the writer has reason to believe that readers will care about the value of this field, the writer should write the field with the appropriate value. TIFF readers can do whatever they want if they encounter a missing _no default_ field that they care about, short of refusing to import the file. For example, if a writer does not write out a PhotometricInterpretation field, some applications will interpret the image _correctly,_ and others will display the image inverted. This is not a good situation, and writers should be careful not to let it happen. The fields are grouped into several categories: basic, informational, facsimile, document storage and retrieval, and no longer recommended. A future version of the specification may pull some of these categories into separate companion documents. Many fields are described in this document, but most are not _required._ See Appendix G for a list of required fields, as well as examples of how to combine fields into valid and useful TIFF files. Basic Fields Basic fields are fields that are fundamental to the pixel architecture or visual characteristics of an image. BitsPerSample Tag = 258 (102) Type = SHORT N = SamplesPerPixel Number of bits per sample. Note that this tag allows a different number of bits per sample for each sample corresponding to a pixel. For example, RGB color data could use a different number of bits per sample for each of the three color planes. Most RGB files will have the same number of BitsPerSample for each sample. Even in this case, be sure to include all three entries. Writing _8_ when you mean _8,8,8_ sets a bad precedent for other fields. Default = 1. See also SamplesPerPixel. ColorMap Tag = 320 (140) Type = SHORT N = 3 * (2**BitsPerSample) This tag defines a Red-Green-Blue color map for palette color images. The palette color pixel value is used to index into all 3 subcurves. For example, a Palette color pixel having a value of 0 would be displayed according to the 0th entry of the Red, Green, and Blue subcurves. The subcurves are stored sequentially. The Red entries come first, followed by the Green entries, followed by the Blue entries. The length of each subcurve is 2**BitsPerSample. A ColorMap entry for an 8-bit Palette color image would therefore have 3 * 256 entries. The width of each entry is 16 bits, as implied by the type of SHORT. 0 represents the minimum intensity, and 65535 represents the maximum intensity. Black is represented by 0,0,0, and white by 65535, 65535, 65535. The purpose of the color map is to act as a _lookup_ table mapping pixel values from 0 to 2**BitsPerSample-1 into RGB triplets. The ColorResponseCurves field may be used in conjunction with ColorMap to further refine the meaning of the RGB triplets in the ColorMap. However, the ColorResponseCurves default should be sufficient in most cases. See also PhotometricInterpretation_palette color. No default. ColorMap must be included in all palette color images. ColorResponseCurves Tag = 301 (12D) Type = SHORT N = 3 * (2**BitsPerSample) This tag defines three color response curves, one each for Red, Green and Blue color information. The Red entries come first, followed by the Green entries, followed by the Blue entries. The length of each subcurve is 2**BitsPerSample, using the BitsPerSample value corresponding to the respective primary. The width of each entry is 16 bits, as implied by the type of SHORT. 0 represents the minimum intensity, and 65535 represents the maximum intensity. Black is represented by 0,0,0, and white by 65535, 65535, 65535. Therefore, a ColorResponseCurve entry for RGB data where each of the samples is 8 bits deep would have 3 * 256 entries, each consisting of a SHORT. The purpose of the color response curves is to refine the content of RGB color images. See Appendix H, section VII, for further information. Default: curves based on the NTSC recommended gamma of 2.2. Compression Tag = 259 (103) Type = SHORT N = 1 1 = No compression, but pack data into bytes as tightly as possible, with no unused bits except at the end of a row. The bytes are stored as an array of type BYTE, for BitsPerSample <= 8, SHORT if BitsPerSample > 8 and <= 16, and LONG if BitsPerSample > 16 and <= 32. The byte ordering of data >8 bits must be consistent with that specified in the TIFF file header (bytes 0 and 1). _II_ format files will have the least significant bytes preceeding the most significant bytes while _MM_ format files will have the opposite. If the number of bits per sample is not a power of 2, and you are willing to give up some space for better performance, you may wish to use the next higher power of 2. For example, if your data can be represented in 6 bits, you may wish to specify that it is 8 bits deep. Rows are required to begin on byte boundaries. The number of bytes per row is therefore (ImageWidth * SamplesPerPixel * BitsPerSample + 7) / 8, assuming integer arithmetic, for PlanarConfiguration=1. Bytes per row is (ImageWidth * BitsPerSample + 7) / 8 for PlanarConfiguration=2. Some graphics systems want rows to be word- or double-word- aligned. Uncompressed TIFF rows will need to be copied into word- or double-word-padded row buffers before being passed to the graphics routines in these environments. 2 = CCITT Group 3 1-Dimensional Modified Huffman run length encoding. See Appendix B: _Data Compression -- Scheme 2._ BitsPerSample must be 1, since this type of compression is defined only for bilevel images. When you decompress data that has been compressed by Compression=2, you must translate white runs into 0_s and black runs into 1_s. Therefore, the normal PhotometricInterpretation for those compression types is 0 (WhiteIsZero). If a reader encounters a PhotometricInterpretation of 1 (BlackIsZero) for such an image, the image should be displayed and printed with black and white reversed. 5 = LZW Compression, for grayscale, mapped color, and full color images. See Appendix F. 32773 = PackBits compression, a simple byte oriented run length scheme for 1-bit images. See Appendix C. Data compression only applies to raster image data, as pointed to by StripOffsets. All other TIFF information is unaffected. Default = 1. GrayResponseCurve Tag = 291 (123) Type = SHORT N = 2**BitsPerSample The purpose of the gray response curve and the gray units is to provide more exact photometric interpretation information for gray scale image data, in terms of optical density. The GrayScaleResponseUnits specifies the accuracy of the information contained in the curve. Since optical density is specified in terms of fractional numbers, this tag is necessary to know how to interpret the stored integer information. For example, if GrayScaleResponseUnits is set to 4 (ten-thousandths of a unit), and a GrayScaleResponseCurve number for gray level 4 is 3455, then the resulting actual value is 0.3455. Optical densitometers typically measure densities within the range of 0.0 to 2.0. If the gray scale response curve is known for the data in the TIFF file, and if the gray scale response of the output device is known, then an intelligent conversion can be made between the input data and the output device. For example, the output can be made to look just like the input. In addition, if the input image lacks contrast (as can be seen from the response curve), then appropriate contrast enhancements can be made. The purpose of the gray scale response curve is to act as a _lookup_ table mapping values from 0 to 2**BitsPerSample-1 into specific density values. The 0th element of the GrayResponseCurve array is used to define the gray value for all pixels having a value of 0, the 1st element of the GrayResponseCurve array is used to define the gray value for all pixels having a value of 1, and so on, up to 2**BitsPerSample-1. If your data is _really,_ say, 7-bit data, but you are adding a 1-bit pad to each pixel to turn it into 8-bit data, everything still works: If the data is high-order justified, half of your GrayResponseCurve entries (the odd ones, probably) will never be used, but that doesn't hurt anything. If the data is low-order justified, your pixel values will be between 0 and 127, so make your GrayResponseCurve accordingly. What your curve does from 128 to 255 doesn't matter. Note that low-order justification is probably not a good idea, however, since not all applications look at GrayResponseCurve. Note also that LZW compression yields the same compression ratio regardless of whether the data is high-order or low-order justified. It is permissable to have a GrayResponseCurve even for bilevel (1-bit) images. The GrayResponseCurve will have 2 values. It should be noted, however, that TIFF B readers are not required to pay attention to GrayResponseCurves in TIFF B files. See Appendix G. If both GrayResponseCurve and PhotometricInterpretation fields exist in the IFD, GrayResponseCurve values override the PhotometricInterpretation value. But it is a good idea to write out both, since some applications do not yet pay attention to the GrayResponseCurve. Writers may wish to purchase a Kodak Reflection Density Guide, catalog number 146 5947, available for $10 or so at prepress supply houses, to help them figure out reasonable density values for their scanner or frame grabber. If that sounds like too much work, we recommend a curve that is linear in intensity/reflectance. To compute reflectance from density: R = 1 / pow(10,D). To compute density from reflectance: D = log10 (1/R). A typical 4-bit GrayResponseCurve may look therefore something like: 2000, 1177, 875, 699, 574, 477, 398, 331, 273, 222, 176, 135, 97, 62, 30, 0, assuming GrayResponseUnit=3. Such a curve would be consistent with PhotometricInterpretation=1. See also GrayResponseUnit, PhotometricInterpretation, ColorMap. GrayResponseUnit Tag = 290 (122) Type = SHORT N = 1 1 = Number represents tenths of a unit. 2 = Number represents hundredths of a unit. 3 = Number represents thousandths of a unit. 4 = Number represents ten-thousandths of a unit. 5 = Number represents hundred-thousandths of a unit. Modifies GrayResponseCurve. See also GrayResponseCurve. For historical reasons, the default is 2. However, for greater accuracy, we recommend using 3. ImageLength Tag = 257 (101) Type = SHORT or LONG N = 1 The image_s length (height) in pixels (Y: vertical). The number of rows (sometimes described as _scan lines") in the image. See also ImageWidth. No default. ImageWidth Tag = 256 (100) Type = SHORT or LONG N = 1 The image_s width, in pixels (X: horizontal). The number of columns in the image. See also ImageLength. No default. NewSubfileType Tag = 254 (FE) Type = LONG N = 1 Replaces the old SubfileType field, due to limitations in the definition of that field. A general indication of the kind of data that is contained in this subfile. This field is made up of a set of 32 flag bits. Unused bits are expected to be 0. Bit 0 is the low-order bit. Currently defined values are: Bit 0 is 1 if the image is a reduced resolution version of another image in this TIFF file; else the bit is 0. Bit 1 is 1 if the image is a single page of a multi-page image (see the PageNumber tag description); else the bit is 0. Bit 2 is 1 if the image defines a transparency mask for another image in this TIFF file. The PhotometricInterpretation value must be 4, designating a transparency mask. These values have been defined as bit flags because they are pretty much independent of each other. For example, it be useful to have four images in a single TIFF file: a full resolution image, a reduced resolution image, a transparency mask for the full resolution image, and a transparency mask for the reduced resolution image. Each of the four images would have a different value for the NewSubfileType field. Default is 0. PhotometricInterpretation Tag = 262 (106) Type = SHORT N = 1 0 = For bilevel and grayscale images: 0 is imaged as white. 2**BitsPerSample-1 is imaged as black. If GrayResponseCurve exists, it overrides the PhotometricInterpretation value, although it is safer to make them match, since some old applications may still be ignoring GrayResponseCurve. This is the normal value for Compression=2. 1 = For bilevel and grayscale images: 0 is imaged as black. 2**BitsPerSample-1 is imaged as white. If GrayResponseCurve exists, it overrides the PhotometricInterpretation value, although it is safer to make them match, since some old applications may still be ignoring GrayResponseCurve. If this value is specified for Compression=2, the image should display and print reversed. 2 = RGB. In the RGB model, a color is described as a combination of the three primary colors of light (red, green, and blue) in particular concentrations. For each of the three samples, 0 represents minimum intensity, and 2**BitsPerSample - 1 represents maximum intensity. Thus an RGB value of (0,0,0) represents black, and (255,255,255) represents white, assuming 8-bit samples. For PlanarConfiguration = 1, the samples are stored in the indicated order: first Red, then Green, then Blue. For PlanarConfiguration = 2, the StripOffsets for the sample planes are stored in the indicated order: first the Red sample plane StripOffsets, then the Green plane StripOffsets, then the Blue plane StripOffsets. The ColorResponseCurves field may be used to globally refine or alter the color balance of an RGB image without having to change the values of the pixels themselves. 3="Palette color._ In this mode, a color is described with a single sample. The sample is used as an index into ColorMap. The sample is used to index into each of the red, green and blue curve tables to retrieve an RGB triplet defining an actual color. When this PhotometricInterpretation value is used, the color response curves must also be supplied. SamplesPerPixel must be 1. 4 = Transparency Mask. This means that the image is used to define an irregularly shaped region of another image in the same TIFF file. SamplesPerPixel and BitsPerSample must be 1. PackBits compression is recommended. The 1-bits define the interior of the region; the 0-bits define the exterior of the region. The Transparency Mask must have the same ImageLength and ImageWidth as the main image. A reader application can use the mask to determine which parts of the image to display. Main image pixels that correspond to 1-bits in the transparency mask are imaged to the screen or printer, but main image pixels that correspond to 0-bits in the mask are not displayed or printed. It is possible to generalize the notion of a transparency mask to include partial transparency, but it is not clear that such information would be useful to a desktop publishing program. No default. That means that if you care if your image is displayed and printed as _normal_ vs _inverted,_ you must write out this field. Do not rely on applications defaulting to what you want! PhotometricInterpretation = 1 is recommended for bilevel (except for Compression=2) and grayscale images, due to popular user interfaces for changing the brightness and contrast of images. PlanarConfiguration Tag = 284 (11C) Type = SHORT N = 1 1 = The sample values for each pixel are stored contiguously, so that there is a single image plane. See PhotometricInterpretation to determine the order of the samples within the pixel data. So, for RGB data, the data is stored RGBRGBRGB...and so on. 2 = The samples are stored in separate _sample planes._ The values in StripOffsets and StripByteCounts are then arranged as a 2-dimensional array, with SamplesPerPixel rows and StripsPerImage columns. (All of the columns for row 0 are stored first, followed by the columns of row 1, and so on.) PhotometricInterpretation describes the type of data that is stored in each sample plane. For example, RGB data is stored with the Red samples in one sample plane, the Green in another, and the Blue in another. If SamplesPerPixel is 1, PlanarConfiguration is irrelevant, and should not be included. Default is 1. See also BitsPerSample, SamplesPerPixel. Predictor Tag = 317 (13D) Type = SHORT N = 1 To be used when Compression=5 (LZW). See Appendix F. 1 = No prediction scheme used before coding. Default is 1. ResolutionUnit Tag = 296 (128) Type = SHORT N = 1 To be used with XResolution and YResolution. 1 = No absolute unit of measurement. Used for images that may have a non-square aspect ratio, but no meaningful absolute dimensions. The drawback of ResolutionUnit=1 is that different applications will import the image at different sizes. Even if the decision is quite arbitrary, it might be better to use dots per inch or dots per centimeter, and pick XResolution and YResolution such that the aspect ratio is correct and the maximum dimension of the image is about four inches (the _four_ is quite arbitrary.) 2 = Inch. 3 = Centimeter. Default is 2. See also XResolution, YResolution. RowsPerStrip Tag = 278 (116) Type = SHORT or LONG N = 1 The number of rows per strip. The image data is organized into strips for fast access to individual rows when the data is compressed_though this field is valid even if the data is not compressed. RowsPerStrip and ImageLength together tell us the number of strips in the entire image. The equation is StripsPerImage = (ImageLength + RowsPerStrip - 1) / RowsPerStrip, assuming integer arithmetic. Note that either SHORT or LONG values can be used to specify RowsPerStrip. SHORT values may be used for small TIFF files. It should be noted, however, that earlier TIFF specification revisions required LONG values and that some software may not expect SHORT values. See Appendix G for further recommendations. Default is 2**32 - 1, which is effectively infinity. That is, the entire image is one strip. We do not recommend a single strip, however. Choose RowsPerStrip such that each strip is about 8K bytes, even if the data is not compressed, since it makes buffering simpler for readers. The _8K_ part is pretty arbitrary, but seems to work well. See also ImageLength, StripOffsets, StripByteCounts. SamplesPerPixel Tag = 277 (115) Type = SHORT N = 1 The number of samples per pixel. SamplesPerPixel is 1 for bilevel, grayscale, and palette color images. SamplesPerPixel is 3 for RGB images. Default = 1. See also BitsPerSample, PhotometricInterpretation. StripByteCounts Tag = 279 (117) Type = SHORT or LONG N = StripsPerImage for PlanarConfiguration equal to 1. = SamplesPerPixel * StripsPerImage for PlanarConfiguration equal to 2 For each strip, the number of bytes in that strip. The existence of this field greatly simplifies the chore of buffering compressed data, if the strip size is reasonable. No default. See also StripOffsets, RowsPerStrip. StripOffsets Tag = 273 (111) Type = SHORT or LONG N = StripsPerImage for PlanarConfiguration equal to 1. = SamplesPerPixel * StripsPerImage for PlanarConfiguration equal to 2 For each strip, the byte offset of that strip. The offset is specified with respect to the beginning of the TIFF file. Note that this implies that each strip has a location independent of the locations of other strips. This feature may be useful for editing applications. This field is the only way for a reader to find the image data, and hence must exist. Note that either SHORT or LONG values can be used to specify the strip offsets. SHORT values may be used for small TIFF files. It should be noted, however, that earlier TIFF specifications required LONG strip offsets and that some software may not expect SHORT values. See Appendix G for further recommendations. No default. See also StripByteCounts, RowsPerStrip. XResolution Tag = 282 (11A) Type = RATIONAL N = 1 The number of pixels per ResolutionUnit in the X direction, i.e., in the ImageWidth direction. It is, of course, not mandatory that the image be actually printed at the size implied by this parameter. It is up to the application to use this information as it wishes. No default. See also YResolution, ResolutionUnit. YResolution Tag = 283 (11B) Type = RATIONAL N = 1 The number of pixels per ResolutionUnit in the Y direction, i.e., in the ImageLength direction. No default. See also XResolution, ResolutionUnit. Informational Fields Informational fields are fields that can provide useful information to a user, such as where the image came from. Most are ASCII fields. An application could have some sort of _More Info..._ dialog box to display such information. Artist Tag = 315 (13B) Type = ASCII Person who created the image. If you need to attach a Copyright notice to an image, this is the place to do it. In fact, you may wish to write out the contents of the field immediately after the 8-byte TIFF header. Just make sure your IFD and field pointers are set accordingly, and you_re all set. DateTime Tag = 306 (132) Type = ASCII N = 20 Date and time of image creation. Use the format _YYYY:MM:DD HH:MM:SS_, with hours on a 24-hour clock, and one space character between the date and the time. The length of the string, including the null, is 20 bytes. HostComputer Tag = 316 (13C) Type = ASCII _ENIAC_, or whatever. See also Make, Model, Software. ImageDescription Tag = 270 (10E) Type = ASCII For example, a user may wish to attach a comment such as _1988 company picnic_ to an image. It has been suggested that this is what the newspaper and magazine industry calls a _slug._ Make Tag = 271 (10F) Type = ASCII Manufacturer of the scanner, video digitizer, or whatever. See also Model, Software. Model Tag = 272 (110) Type = ASCII The model name/number of the scanner, video digitizer, or whatever. This tag is intended for user information only. See also Make, Software. Software Tag = 305 (131) Type = ASCII Name and release number of the software package that created the image. This tag is intended for user information only. See also Make, Model. Facsimile Fields Facsimile fields may be useful if you are using TIFF to store facsimile messages in _raw_ form. They are not recommended for use in interchange with desktop publishing applications. Compression (a basic tag) Tag = 259 (103) Type = SHORT N = 1 3 = Facsimile-compatible CCITT Group 3, exactly as specified in _Standardization of Group 3 facsimile apparatus for document transmission,_ Recommendation T.4, Volume VII, Fascicle VII.3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Geneva, 1985, pages 16 through 31. Each strip must begin on a byte boundary. (But recall that an image can be a single strip.) Rows that are not the first row of a strip are not required to begin on a byte boundary. The data is stored as bytes, not words_byte-reversal is not allowed. See the Group3Options field for Group 3 options such as 1D vs 2D coding. 4 = Facsimile-compatible CCITT Group 4, exactly as specified in _Facsimile Coding Schemes and Coding Control Functions for Group 4 Facsimile Apparatus,_ Recommendation T.6, Volume VII, Fascicle VII.3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Geneva, 1985, pages 40 through 48. Each strip must begin on a byte boundary. Rows that are not the first row of a strip are not required to begin on a byte boundary. The data is stored as bytes, not words. See the Group4Options field for Group 4 options. Group3Options Tag = 292 (124) Type = LONG N = 1 See Compression=3. This field is made up of a set of 32 flag bits. Unused bits are expected to be 0. Bit 0 is the low-order bit. It is probably not safe to try to read the file if any bit of this field is set that you don_t know the meaning of. Bit 0 is 1 for 2-dimensional coding (else 1-dimensional is assumed). For 2-D coding, if more than one strip is specified, each strip must begin with a 1-dimensionally coded line. That is, RowsPerStrip should be a multiple of _Parameter K_ as documented in the CCITT specification. Bit 1 is 1 if uncompressed mode is used. Bit 2 is 1 if fill bits have been added as necessary before EOL codes such that EOL always ends on a byte boundary, thus ensuring an eol-sequence of a 1 byte preceded by a zero nibble: xxxx-0000 0000-0001. Default is 0, for basic 1-dimensional coding. See also Compression. Group4Options Tag = 293 (125) Type = LONG N = 1 See Compression=4. This field is made up of a set of 32 flag bits. Unused bits are expected to be 0. Bit 0 is the low-order bit. It is probably not safe to try to read the file if any bit of this field is set that you don_t know the meaning of. Gray scale and color coding schemes are under study, and will be added when finalized. For 2-D coding, each strip is encoded as if it were a separate image. In particular, each strip begins on a byte boundary; and the coding for the first row of a strip is encoded independently of the previous row, using horizontal codes, as if the previous row is entirely white. Each strip ends with the 24-bit end-of- facsimile block (EOFB). Bit 0 is unused. Bit 1 is 1 if uncompressed mode is used. Default is 0, for basic 2-dimensional binary compression. See also Compression. Document Storage and Retrieval Fields These fields may be useful for document storage and retrieval applications. They are not recommended for use in interchange with desktop publishing applications. DocumentName Tag = 269 (10D) Type = ASCII The name of the document from which this image was scanned. See also PageName. PageName Tag = 285 (11D) Type = ASCII The name of the page from which this image was scanned. See also DocumentName. No default. PageNumber Tag = 297 (129) Type = SHORT N = 2 This tag is used to specify page numbers of a multiple page (e.g. facsimile) document. Two SHORT values are specified. The first value is the page number; the second value is the total number of pages in the document. Note that pages need not appear in numerical order. The first page is 0 (zero). No default. XPosition Tag = 286 (11E) Type = RATIONAL The X offset of the left side of the image, with respect to the left side of the page, in ResolutionUnits. No default. See also YPosition. YPosition Tag = 287 (11F) Type = RATIONAL The Y offset of the top of the image, with respect to the top of the page, in ResolutionUnits. In the TIFF coordinate scheme, the positive Y direction is down, so that YPosition is always positive. No default. See also XPosition. No Longer Recommended These fields are not recommended except perhaps for local use. They should not be used for image interchange. They have either been superseded by other fields, have been found to have serious drawbacks, or are simply not as useful as once thought. They may be dropped entirely from a future revision of the specification. CellLength Tag = 265 (109) Type = SHORT N = 1 The length, in 1-bit samples, of the dithering/halftoning matrix. Assumes that Threshholding = 2. This field, plus CellWidth and Threshholding, are problematic because they cannot safely be used to reverse-engineer grayscale image data out of dithered/halftoned black-and-white data, which is their only plausible purpose. The only _right_ way to do it is to not bother with anything like these fields, and instead write some sophisticated pattern-matching software that can handle screen angles that are not multiples of 45 degrees, and other such challenging dithered/halftoned data. So we do not recommend trying to convert dithered or halftoned data into grayscale data. Dithered and halftoned data require careful treatment to avoid _stretch marks,_ but it can be done. If you want grayscale images, get them directly from the scanner or frame grabber or whatever. No default. See also Threshholding. CellWidth Tag = 264 (108) Type = SHORT N = 1 The width, in 1-bit samples, of the dithering/halftoning matrix. No default. See also Threshholding. See the comments for CellLength. FillOrder Tag = 266 (10A) Type = SHORT N = 1 The order of data values within a byte. 1 = most significant bits of the byte are filled first. That is, data values (or code words) are ordered from high order bit to low order bit within a byte. 2 = least significant bits are filled first. Since little interest has been expressed in least-significant fill order to date, and since it is easy and inexpensive for writers to reverse bit order (use a 256-byte lookup table), we recommend FillOrder=2 for private (non-interchange) use only. Default is FillOrder = 1. FreeByteCounts Tag = 289 (121) Type = LONG For each _free block_ in the file, the number of bytes in the block. TIFF readers can ignore FreeOffsets and FreeByteCounts if present. FreeOffsets and FreeByteCounts do not constitute a remapping of the logical address space of the file. Since this information can be generated by scanning the IFDs, StripOffsets, and StripByteCounts, FreeByteCounts and FreeOffsets are not needed. In addition, it is not clear what should happen if FreeByteCounts and FreeOffsets exist in more than one IFD. See also FreeOffsets. FreeOffsets Tag = 288 (120) Type = LONG For each _free block_ in the file, its byte offset. See also FreeByteCounts. MaxSampleValue Tag = 281 (119) Type = SHORT N = SamplesPerPixel The maximum used sample value. For example, if the image consists of 6-bit data low-order-justified into 8-bit bytes, MaxSampleValue will be no greater than 63. This is field is not to be used to affect the visual appearance of the image when displayed. Nor should the values of this field affect the interpretation of any other field. Use it for statistical purposes only. Default is 2**(BitsPerSample) - 1. MinSampleValue Tag = 280 (118) Type = SHORT N = SamplesPerPixel The minimum used sample value. This field is not to be used to affect the visual appearance of the image when displayed. See the comments for MaxSampleValue. Default is 0. SubfileType Tag = 255 (FF) Type = SHORT N = 1 A general indication of the kind of data that is contained in this subfile. Currently defined values are: 1 = full resolution image data_ImageWidth, ImageLength, and StripOffsets are required fields; and 2 = reduced resolution image data_ImageWidth, ImageLength, and StripOffsets are required fields. It is further assumed that a reduced resolution image is a reduced version of the entire extent of the corresponding full resolution data. 3 = single page of a multi-page image (see the PageNumber tag description). Note that several image types can be found in a single TIFF file, with each subfile described by its own IFD. No default. Continued use of this field is not recommended. Writers should instead use the new and more general NewSubfileType field. Orientation Tag = 274 (112) Type = SHORT N = 1 1 = The 0th row represents the visual top of the image, and the 0th column represents the visual left hand side. 2 = The 0th row represents the visual top of the image, and the 0th column represents the visual right hand side. 3 = The 0th row represents the visual bottom of the image, and the 0th column represents the visual right hand side. 4 = The 0th row represents the visual bottom of the image, and the 0th column represents the visual left hand side. 5 = The 0th row represents the visual left hand side of the image, and the 0th column represents the visual top. 6 = The 0th row represents the visual right hand side of the image, and the 0th column represents the visual top. 7 = The 0th row represents the visual right hand side of the image, and the 0th column represents the visual bottom. 8 = The 0th row represents the visual left hand side of the image, and the 0th column represents the visual bottom. Default is 1. This field is recommended for private (non-interchange) use only. It is extremely costly for most readers to perform image rotation _on the fly,_ i.e., when importing and printing; and users of most desktop publishing applications do not expect a file imported by the application to be altered permanently in any way. Threshholding Tag = 263 (107) Type = SHORT N = 1 1 = a bilevel _line art_ scan. BitsPerSample must be 1. 2 = a _dithered_ scan, usually of continuous tone data such as photographs. BitsPerSample must be 1. 3 = Error Diffused. Default is Threshholding = 1. See also CellWidth, CellLength. 4) Private Fields An organization may wish to store information that is meaningful to only that organization in a TIFF file. Tags numbered 32768 or higher are reserved for that purpose. Upon request, the administrator will allocate and register a block of private tags for an organization, to avoid possible conflicts with other organizations. Tags are normally allocated in blocks of five. If that is not enough, feel free to ask for more. You do not need to tell the TIFF administrator or anyone else what you are going to use them for. Private enumerated values can be accommodated in a similar fashion. For example, you may wish to experiment with a new compression scheme within TIFF. Enumeration constants numbered 32768 or higher are reserved for private usage. Upon request, the administrator will allocate and register a block of enumerated values for a particular field (Compression, in our example), to avoid possible conflicts. Tags and values which are allocated in the private number range are not prohibited from being included in a future revision of this specification. Several such instances can be found in the TIFF specification. Do not choose your own tag numbers. If you do, it could cause serious problems some day. 5) Image File Format Issues In the quest to give users no reason NOT to buy a product, some scanning and image editing applications overwhelm users with an incredible number of _Save As..._ options. Let_s get rid of as many of these as we possibly can. For example, a single TIFF choice should suffice once most major readers are supporting the three TIFF compression schemes; then writers can always compress. And given TIFF_s flexibility, including private tag and image editing support features, there does not seem to be any legitimate reason for continuing to write image files using proprietary formats. Along the same lines, there is no excuse for making a user have to know the file format of a file that is to be read by an application program. TIFF files, as well as most other file formats, contain sufficient information to enable software to automatically and reliably distinguish one type of file from another. 6) For Further Information Contact the Aldus Developers_ Desk for sample TIFF files, source code fragments, and a list of features that are currently supported in Aldus products. The Aldus Developers_ Desk is the current _TIFF administrator._ Various TIFF related aids are found in Microsoft_s Windows Developers Tookit for developers writing Windows applications. Finally, a number of scanner vendors are providing various TIFF services, such as helping to distribute the TIFF specification and answering TIFF questions. Contact the appropriate product manager or developer support service group. Appendix A: Tag Structure Rationale A file format is defined by both form (structure) and content. The content of TIFF consists of definitions of individual fields. It is therefore the content that we are ultimately interested in. The structure merely tells us how to find the fields. Yet the structure deserves serious consideration for a number of reasons that are not at all obvious at first glance. Since the structure described herein departs significantly from several other approaches, it may be useful to discuss the rationale behind it. The simplest, most straightforward structure for something like an image file is a positional format. In a positional scheme, the location of the data defines what the data means. For example, the field for _number of rows_ might begin at byte offset 30 in the image file. This approach is simple and easy to implement and is perfect for static environments. But if a significant amount of ongoing change must be accommodated, subtle problems begin to appear. For example, suppose that a field must be superseded by a new, more general field. You could bump a version number to flag the change. Then new software has no problem doing something sensible with old data, and all old software will reject the new data, even software that didn_t care about the old field. This may seem like no more than a minor annoyance at first glance, but causing old software to break more often than it would really need to can be very costly and, inevitably, causes much gnashing of teeth among customers. Furthermore, it can be avoided. One approach is to store a _valid_ flag bit for each field. Now you don_t have to bump the version number, as long as you can put the new field somewhere that doesn_t disturb any of the old fields. Old software that didn_t care about that old field anyway can continue to function. (Old software that did care will of course have to give up, but this is an unavoidable price to be paid for the sake of progress, barring total omniscience.) Another problem that crops up frequently is that certain fields are likely to make sense only if other fields have certain values. This is not such a serious problem in practice; it just makes things more confusing. Nevertheless, we note that the _valid_ flag bits described in the previous paragraph can help to clarify the situation. Field-dumping programs can be very helpful for diagnostic purposes. A desirable characteristic of such a program is that it doesn_t have to know much about what it is dumping. In particular, it would be nice if the program could dump ASCII data in ASCII format, integer data in integer format, and so on, without having to teach the program about new fields all the time. So maybe we should add a _data type_ component to our fields, plus information on how long the field is, so that our dump program can walk through the fields without knowing what the fields _mean." But note that if we add one more component to each field, namely a tag that tells what the field means, we can dispense with the _valid_ flag bits, and we can also avoid wasting space on the non-valid fields in the file. Simple image creation applications can write out several fields and be done. We have now derived the essentials of a tag-based image file format. Finally, a caveat. A tag based scheme cannot guarantee painless growth. But is does provide a useful tool to assist in the process. Appendix B: Data Compression_Scheme 2 Abstract This document describes a method for compressing bilevel data that is based on the CCITT Group 3 1D facsimile compression scheme. References 1. _Standardization of Group 3 facsimile apparatus for document transmission,_ Recommendation T.4, Volume VII, Fascicle VII.3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Geneva, 1985, pages 16 through 31. 2. _Facsimile Coding Schemes and Coding Control Functions for Group 4 Facsimile Apparatus,_ Recommendation T.6, Volume VII, Fascicle VII.3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Geneva, 1985, pages 40 through 48. We do not believe that these documents are necessary in order to implement Compression=2. We have included (verbatim in most places) all the pertinent information in this Appendix. However, if you wish to order the documents, you can write to ANSI, Attention: Sales, 1430 Broadway, New York, N.Y., 10018. Ask for the publication listed above_it contains both Recommendation T.4 and T.6. Relationship to the CCITT Specifications The CCITT Group 3 and Group 4 specifications describe communications protocols for a particular class of devices. They are not by themselves sufficient to describe a disk data format. Fortunately, however, the CCITT coding schemes can be readily adapted to this different environment. The following is one such adaptation. Most of the language is copied directly from the CCITT specifications. Coding Scheme A line (row) of data is composed of a series of variable length code words. Each code word represents a run length of either all white or all black. (Actually, more than one code word may be required to code a given run, in a manner described below.) White runs and black runs alternate. In order to ensure that the receiver (decompressor) maintains color synchronization, all data lines will begin with a white run length code word set. If the actual scan line begins with a black run, a white run length of zero will be sent (written). Black or white run lengths are defined by the code words in Tables 1 and 2. The code words are of two types: Terminating code words and Make-up code words. Each run length is represented by zero or more Make-up code words followed by exactly one Terminating code word. Run lengths in the range of 0 to 63 pels (pixels) are encoded with their appropriate Terminating code word. Note that there is a different list of code words for black and white run lengths. Run lengths in the range of 64 to 2623 (2560+63) pels are encoded first by the Make-up code word representing the run length that is nearest to, not longer than, that required. This is then followed by the Terminating code word representing the difference between the required run length and the run length represented by the Make-up code. Run lengths in the range of lengths longer than or equal to 2624 pels are coded first by the Make-up code of 2560. If the remaining part of the run (after the first Make-up code of 2560) is 2560 pels or greater, additional Make-up code(s) of 2560 are issued until the remaining part of the run becomes less than 2560 pels. Then the remaining part of the run is encoded by Terminating code or by Make-up code plus Terminating code, according to the range mentioned above. It is considered an unrecoverable error if the sum of the run lengths for a line does not equal the value of the ImageWidth field. New rows always begin on the next available byte boundary. No EOL code words are used. No fill bits are used, except for the ignored bits at the end of the last byte of a row. RTC is not used. Table 1/T.4 Terminating codes White Black run Code run Code length word length word ---- ---- ------ ---- 0 00110101 0 0000110111 1 000111 1 010 2 0111 2 11 3 1000 3 10 4 1011 4 011 5 1100 5 0011 6 1110 6 0010 7 1111 7 00011 8 10011 8 000101 9 10100 9 000100 10 00111 10 0000100 11 01000 11 0000101 12 001000 12 0000111 13 000011 13 00000100 14 110100 14 00000111 15 110101 15 000011000 16 101010 16 0000010111 17 101011 17 0000011000 18 0100111 18 0000001000 19 0001100 19 00001100111 20 0001000 20 00001101000 21 0010111 21 00001101100 22 0000011 22 00000110111 23 0000100 23 00000101000 24 0101000 24 00000010111 25 0101011 25 00000011000 26 0010011 26 000011001010 27 0100100 27 000011001011 28 0011000 28 000011001100 29 00000010 29 000011001101 30 00000011 30 000001101000 31 00011010 31 000001101001 32 00011011 32 000001101010 33 00010010 33 000001101011 34 00010011 34 000011010010 35 00010100 35 000011010011 36 00010101 36 000011010100 37 00010110 37 000011010101 38 00010111 38 000011010110 39 00101000 39 000011010111 40 00101001 40 000001101100 41 00101010 41 000001101101 42 00101011 42 000011011010 43 00101100 43 000011011011 44 00101101 44 000001010100 45 00000100 45 000001010101 46 00000101 46 000001010110 47 00001010 47 000001010111 48 00001011 48 000001100100 49 01010010 49 000001100101 50 01010011 50 000001010010 51 01010100 51 000001010011 52 01010101 52 000000100100 53 00100100 53 000000110111 54 00100101 54 000000111000 55 01011000 55 000000100111 56 01011001 56 000000101000 57 01011010 57 000001011000 58 01011011 58 000001011001 59 01001010 59 000000101011 60 01001011 60 000000101100 61 00110010 61 000001011010 62 00110011 62 000001100110 63 00110100 63 000001100111 Table 2/T.4 Make-up codes White Black run Code run Code length word length word ------ ---- ------ ---- 64 11011 64 0000001111 128 10010 128 000011001000 192 010111 192 000011001001 256 0110111 256 000001011011 320 00110110 320 000000110011 384 00110111 384 000000110100 448 01100100 448 000000110101 512 01100101 512 0000001101100 576 01101000 576 0000001101101 640 01100111 640 0000001001010 704 011001100 704 0000001001011 768 011001101 768 0000001001100 832 011010010 832 0000001001101 896 011010011 896 0000001110010 960 011010100 960 0000001110011 1024 011010101 1024 0000001110100 1088 011010110 1088 0000001110101 1152 011010111 1152 0000001110110 1216 011011000 1216 0000001110111 1280 011011001 1280 0000001010010 1344 011011010 1344 0000001010011 1408 011011011 1408 0000001010100 1472 010011000 1472 0000001010101 1536 010011001 1536 0000001011010 1600 010011010 1600 0000001011011 1664 011000 1664 0000001100100 1728 010011011 1728 0000001100101 EOL 000000000001 EOL 000000000001 Additional make-up codes White and Black Make-up run code length word ------ ---- 1792 00000001000 1856 00000001100 1920 00000001101 1984 000000010010 2048 000000010011 2112 000000010100 2176 000000010101 2240 000000010110 2304 000000010111 2368 000000011100 2432 000000011101 2496 000000011110 2560 000000011111 Appendix C: Data Compression_Scheme 32773_ _PackBits_ Abstract This document describes a simple compression scheme for bilevel scanned and paint type files. Motivation The TIFF specification defines a number of compression schemes. Compression type 1 is really no compression, other than basic pixel packing. Compression type 2, based on CCITT 1D compression, is powerful, but not trivial to implement. Compression type 5 is typically very effective for most bilevel images, as well as many deeper images such as palette color and grayscale images, but is also not trivial to implement. PackBits is a simple but often effective alternative. Description Several good schemes were already in use in various settings. We somewhat arbitrarily picked the Macintosh PackBits scheme. It is byte oriented, so there is no problem with word alignment. And it has a good worst case behavior (at most 1 extra byte for every 128 input bytes). For Macintosh users, there are toolbox utilities PackBits and UnPackBits that will do the work for you, but it is easy to implement your own routines. A pseudo code fragment to unpack might look like this: Loop until you get the number of unpacked bytes you are expecting: Read the next source byte into n. If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. Else if n is 128, noop. Endloop In the inverse routine, it's best to encode a 2-byte repeat run as a replicate run except when preceded and followed by a literal run, in which case it's best to merge the three into one literal run. Always encode 3-byte repeats as replicate runs. So that's the algorithm. Here are some other rules: Each row must be packed separately. Do not compress across row boundaries. The number of uncompressed bytes per row is defined to be (ImageWidth + 7) / 8. If the uncompressed bitmap is required to have an even number of bytes per row, decompress into word- aligned buffers. If a run is larger than 128 bytes, simply encode the remainder of the run as one or more additional replicate runs. When PackBits data is uncompressed, the result should be interpreted as per compression type 1 (no compression). Appendix D Appendix D has been deleted. It formerly contained guidelines for passing TIFF files on the Microsoft Windows Clipboard. This was judged to not be a good idea, in light of the ever-increasing size of scanned images. Applications are instead encouraged to employ file-based mechanisms to exchange TIFF data. Aldus_ PageMaker, for example, implements a _File Place_ command to allow TIFF files to be imported. Appendix E: Numerical List of TIFF Tags NewSubfileType Tag = 254 (FE) Type = LONG N = 1 SubfileType Tag = 255 (FF) Type = SHORT N = 1 ImageWidth Tag = 256 (100) Type = SHORT or LONG N = 1 ImageLength Tag = 257 (101) Type = SHORT or LONG N = 1 BitsPerSample Tag = 258 (102) Type = SHORT N = SamplesPerPixel Compression Tag = 259 (103) Type = SHORT N = 1 PhotometricInterpretation Tag = 262 (106) Type = SHORT N = 1 Threshholding Tag = 263 (107) Type = SHORT N = 1 CellWidth Tag = 264 (108) Type = SHORT N = 1 CellLength Tag = 265 (109) Type = SHORT N = 1 FillOrder Tag = 266 (10A) Type = SHORT N = 1 DocumentName Tag = 269 (10D) Type = ASCII ImageDescription Tag = 270 (10E) Type = ASCII Make Tag = 271 (10F) Type = ASCII Model Tag = 272 (110) Type = ASCII StripOffsets Tag = 273 (111) Type = SHORT or LONG N = StripsPerImage for PlanarConfiguration equal to 1. = SamplesPerPixel * StripsPerImage for PlanarConfiguration equal to 2 Orientation Tag = 274 (112) Type = SHORT N = 1 SamplesPerPixel Tag = 277 (115) Type = SHORT N = 1 RowsPerStrip Tag = 278 (116) Type = SHORT or LONG N = 1 StripByteCounts Tag = 279 (117) Type = LONG or SHORT N = StripsPerImage for PlanarConfiguration equal to 1. = SamplesPerPixel * StripsPerImage for PlanarConfiguration equal to 2. MinSampleValue Tag = 280 (118) Type = SHORT N = SamplesPerPixel MaxSampleValue Tag = 281 (119) Type = SHORT N = SamplesPerPixel XResolution Tag = 282 (11A) Type = RATIONAL N = 1 YResolution Tag = 283 (11B) Type = RATIONAL N = 1 PlanarConfiguration Tag = 284 (11C) Type = SHORT N = 1 PageName Tag = 285 (11D) Type = ASCII XPosition Tag = 286 (11E) Type = RATIONAL YPosition Tag = 287 (11F) Type = RATIONAL FreeOffsets Tag = 288 (120) Type = LONG FreeByteCounts Tag = 289 (121) Type = LONG GrayResponseUnit Tag = 290 (122) Type = SHORT N = 1 GrayResponseCurve Tag = 291 (123) Type = SHORT N = 2**BitsPerSample Group3Options Tag = 292 (124) Type = LONG N = 1 Group4Options Tag = 293 (125) Type = LONG N = 1 ResolutionUnit Tag = 296 (128) Type = SHORT N = 1 PageNumber Tag = 297 (129) Type = SHORT N = 2 ColorResponseCurves Tag = 301 (12D) Type = SHORT N = 3 * (2**BitsPerSample) Software Tag = 305 (131) Type = ASCII DateTime Tag = 306 (132) Type = ASCII N = 20 Artist Tag = 315 (13B) Type = ASCII HostComputer Tag = 316 (13C) Type = ASCII Predictor Tag = 317 (13D) Type = SHORT N = 1 WhitePoint Tag = 318 (13E) Type = RATIONAL N = 2 PrimaryChromaticities Tag = 319 (13F) Type = RATIONAL N = 6 ColorMap Tag = 320 (140) Type = SHORT N = 3 * (2**BitsPerSample) Appendix F: Data Compression_Scheme 5_LZW Compression Abstract This document describes an adaptive compression scheme for raster images. Reference Terry A. Welch, _A Technique for High Performance Data Compression_, IEEE Computer, vol. 17 no. 6 (June 1984). Describes the basic Lempel-Ziv & Welch (LZW) algorithm. The author_s goal in the article is to describe a hardware-based compressor that could be built into a disk controller or database engine, and used on all types of data. There is no specific discussion of raster images. We intend to give sufficient information in this Appendix so that the article is not required reading. Requirements A compression scheme with the following characteristics should work well in a desktop publishing environment: Must work well for images of any bit depth, including images deeper than 8 bits per sample. Must be effective: an average compression ratio of at least 2:1 or better. And it must have a reasonable worst-case behavior, in case something really strange is thrown at it. Should not depend on small variations between pixels. Palette color images tend to contain abrupt changes in index values, due to common patterning and dithering techniques. These abrupt changes do tend to be repetitive, however, and the scheme should make use of this fact. For images generated by paint programs, the scheme should not depend on a particular pattern width. 8x8 pixel patterns are common now, but we should not assume that this situation will not change. Must be fast. It should not take more than 5 seconds to decompress a 100K byte grayscale image on a 68020- or 386-based computer. Compression can be slower, but probably not by more than a factor of 2 or 3. The level of implementation complexity must be reasonable. We would like something that can be implemented in no more than a couple of weeks by a_competent software engineer with some experience in image processing. The compiled code for compression and decompression combined should be no more than about 10K. Does not require floating point software or hardware. The following sections describe an algorithm based on the _LZW_ (Lempel-Ziv & Welch) technique that meets the above requirements. In addition meeting our requirements, LZW has the following characteristics: LZW is fully reversible. All information is preserved. But if noise or information is removed from an image, perhaps by smoothing or zeroing some low-order bitplanes, LZW compresses images to a smaller size. Thus, 5-bit, 6-bit, or 7-bit data masquerading as 8-bit data compresses better than true 8-bit data. Smooth images also compress better than noisy images, and simple images compress better than complex images. On a 68082- or 386-based computer, LZW software can be written to compress at between 30K and 80K bytes per second, depending on image characteristics. LZW decompression speeds are typically about 50K bytes per second. LZW works well on bilevel images, too. It always beats PackBits, and generally ties CCITT 1D (Modified Huffman) compression, on our test images. Tying CCITT 1D is impressive in that LZW seems to be considerably faster than CCITT 1D, at least in our implementation. Our implementation is written in C, and compiles to about 2K bytes of object code each for the compressor and decompressor. One of the nice things about LZW is that it is used quite widely in other applications such as archival programs, and is therefore more of a known quantity. The Algorithm Each strip is compressed independently. We strongly recommend that RowsPerStrip be chosen such that each strip contains about 8K bytes before compression. We want to keep the strips small enough so that the compressed and uncompressed versions of the strip can be kept entirely in memory even on small machines, but large enough to maintain nearly optimal compression ratios. The LZW algorithm is based on a translation table, or string table, that maps strings of input characters into codes. The TIFF implementation uses variable-length codes, with a maximum code length of 12 bits. This string table is different for every strip, and, remarkably, does not need to be kept around for the decompressor. The trick is to make the decompressor automatically build the same table as is built when compressing the data. We use a C-like pseudocode to describe the coding scheme: InitializeStringTable(); WriteCode(ClearCode); _ = the empty string; for each character in the strip { K = GetNextCharacter(); if _+K is in the string table { _ = _+K; /* string concatenation */ } else { WriteCode (CodeFromString(_)); AddTableEntry(_+K); _ = K; } } /* end of for loop */ WriteCode (CodeFromString(_)); WriteCode (EndOfInformation); That's it. The scheme is simple, although it is fairly challenging to implement efficiently. But we need a few explanations before we go on to decompression. The _characters_ that make up the LZW strings are bytes containing TIFF uncompressed (Compression=1) image data, in our implementation. For example, if BitsPerSample is 4, each 8-bit LZW character will contain two 4-bit pixels. If BitsPerSample is 16, each 16-bit pixel will span two 8-bit LZW characters. (It is also possible to implement a version of LZW where the LZW character depth equals BitsPerSample, as was described by Draft 2 of Revision 5.0. But there is a major problem with this approach. If BitsPerSample is greater than 11, we can not use 12-bit-maximum codes, so that the resulting LZW table is unacceptably large. Fortunately, due to the adaptive nature of LZW, we do not pay a significant compression ratio penalty for combining several pixels into one byte before compressing. For example, our 4-bit sample images compressed about 3 percent worse, and our 1-bit images compressed about 5 percent better. And it is easier to write an LZW compressor that always uses the same character depth than it is to write one which can handle varying depths.) We can now describe some of the routine and variable references in our pseudocode: InitializeStringTable() initializes the string table to contain all possible single-character strings. There are 256 of them, numbered 0 through 255, since our characters are bytes. WriteCode() writes a code to the output stream. The first code written is a Clear code, which is defined to be code #256. _ is our _prefix string._ GetNextCharacter() retrieves the next character value from the input stream. This will be number between 0 and 255, since our characters are bytes. The _+_ signs indicate string concatenation. AddTableEntry() adds a table entry. (InitializeStringTable() has already put 256 entries in our table. Each entry consists of a single-character string, and its associated code value, which is, in our application, identical to the character itself. That is, the 0th entry in our table consists of the string <0>, with corresponding code value of <0>, the 1st entry in the table consists of the string <1>, with corresponding code value of <1>, ..., and the 255th entry in our table consists of the string <255>, with corresponding code value of <255>.) So the first entry that we add to our string table will be at position 256, right? Well, not quite, since we will reserve code #256 for a special _Clear_ code, and code #257 for a special _EndOfInformation_ code that we will write out at the end of the strip. So the first multiple-character entry added to the string table will be at position 258. Let_s try an example. Suppose we have input data that looks like: Pixel 0: <7> Pixel 1: <7> Pixel 2: <7> Pixel 3: <8> Pixel 4: <8> Pixel 5: <7> Pixel 6: <7> Pixel 7: <6> Pixel 8: <6> First, we read Pixel 0 into K. _K is then simply <7>, since _ is the empty string at this point. Is the string <7> already in the string table? Of course, since all single character strings were put in the table by InitializeStringTable(). So set _ equal to <7>, and go to the top of the loop. Read Pixel 1 into K. Does _K (<7><7>) exist in the string table? No, so we get to do some real work. We write the code associated with _ to output (write <7> to output), and add _K (<7><7>) to the table as entry 258. Store K (<7>) into _. Note that although we have added the string consisting of Pixel 0 and Pixel 1 to the table, we _re-use_ Pixel 1 as the beginning of the next string. Back at the top of the loop. We read Pixel 2 into K. Does _K (<7><7>) exist in the string table? Yes, the entry we just added, entry 258, contains exactly <7><7>. So we just add K onto the end of _, so that _ is now <7><7>. Back at the top of the loop. We read Pixel 3 into K. Does _K (<7><7><8>) exist in the string table? No, so write the code associated with _ (<258>) to output, and add _K to the table as entry 259. Store K (<8>) into _. Back at the top of the loop. We read Pixel 4 into K. Does _K (<8><8>) exist in the string table? No, so write the code associated with _ (<8>) to output, and add _K to the table as entry 260. Store K (<8>) into _. Continuing, we get the following results: After reading: We write to output: And add table entry: Pixel 0 Pixel 1 <7> 258: <7><7> Pixel 2 Pixel 3 <258> 259: <7><7><8> Pixel 4 <8> 260: <8><8> Pixel 5 <8> 261: <8><7> Pixel 6 Pixel 7 <258> 262: <7><7><6> Pixel 8 <6> 263: <6><6> WriteCode() also requires some explanation. The output code stream, <7><258><8><8><258><6>... in our example, should be written using as few bits as possible. When we are just starting out, we can use 9-bit codes, since our new string table entries are greater than 255 but less than 512. But when we add table entry 512, we must switch to 10-bit codes. Likewise, we switch to 11-bit codes at 1024, and 12-bit codes at 2048. We will somewhat arbitrarily limit ourselves to 12-bit codes, so that our table can have at most 4096 entries. If we push it any farther, tables tend to get too large. What happens if we run out of room in our string table? This is where the afore-mentioned Clear code comes in. As soon as we use entry 4094, we write out a (12-bit) Clear code. (If we wait any longer to write the Clear code, the decompressor might try to interpret the Clear code as a 13-bit code.) At this point, the compressor re-initializes the string table and starts writing out 9-bit codes again. Note that whenever you write a code and add a table entry, _ is not left empty. It contains exactly one character. Be careful not to lose it when you write an end-of-table Clear code. You can either write it out as a 12-bit code before writing the Clear code, in which case you will want to do it right after adding table entry 4093, or after the clear code as a 9-bit code. Decompression gives the same result in either case. To make things a little simpler for the decompressor, we will require that each strip begins with a Clear code, and ends with an EndOfInformation code. Every LZW-compressed strip must begin on a byte boundary. It need not begin on a word boundary. LZW compression codes are stored into bytes in high-to-low-order fashion, i.e., FillOrder is assumed to be 1. The compressed codes are written as bytes, not words, so that the compressed data will be identical regardless of whether it is an _II_ or _MM_ file. Note that the LZW string table is a continuously updated history of the strings that have been encountered in the data. It thus reflects the characteristics of the data, providing a high degree of adaptability. LZW Decoding The procedure for decompression is a little more complicated, but still not too bad: while ((Code = GetNextCode()) != EoiCode) { if (Code == ClearCode) { InitializeTable(); Code = GetNextCode(); if (Code == EoiCode) break; WriteString(StringFromCode(Code)); OldCode = Code; } /* end of ClearCode case */ else { if (IsInTable(Code)) { WriteString(StringFromCode(Code)); AddStringToTable(StringFromCode(OldCode)+Firs tChar(StringFromCode(Code))); OldCode = Code; } else { OutString = StringFromCode(OldCode) + FirstChar(StringFromCode(OldCode)); WriteString(OutString); AddStringToTable(OutString); OldCode = Code; } } /* end of not-ClearCode case */ } /* end of while loop */ The function GetNextCode() retrieves the next code from the LZW- coded data. It must keep track of bit boundaries. It knows that the first code that it gets will be a 9-bit code. We add a table entry each time we get a code, so GetNextCode() must switch over to 10-bit codes as soon as string #511 is stored into the table. The function StringFromCode() gets the string associated with a particular code from the string table. The function AddStringToTable() adds a string to the string table. The _+_ sign joining the two parts of the argument to AddStringToTable indicate string concatenation. StringFromCode() looks up the string associated with a given code. WriteString() adds a string to the output stream. When SamplesPerPixel Is Greater Than 1 We have so far described the compression scheme as if SamplesPerPixel were always 1, as will be be the case with palette color and grayscale images. But what do we do with RGB image data? Tests on our sample images indicate that the LZW compression ratio is nearly identical regardless of whether PlanarConfiguration=1 or PlanarConfiguration=2, for RGB images. So use whichever configuration you prefer, and simply compress the bytes in the strip. It is worth cautioning that compression ratios on our test RGB images were disappointing low: somewhere between 1.1 to 1 and 1.5 to 1, depending on the image. Vendors are urged to do what they can to remove as much noise from their images as possible. Preliminary tests indicate that significantly better compression ratios are possible with less noisy images. Even something as simple as zeroing out one or two least-significant bitplanes may be quite effective, with little or no perceptible image degradation. Implementation The exact structure of the string table and the method used to determine if a string is already in the table are probably the most significant design decisions in the implementation of a LZW compressor and decompressor. Hashing has been suggested as a useful technique for the compressor. We have chosen a tree based approach, with good results. The decompressor is actually more straightforward, as well as faster, since no search is involved_strings can be accessed directly by code value. Performance Many people do not realize that the performance of any compression scheme depends greatly on the type of data to which it is applied. A scheme that works well on one data set may do poorly on the next. But since we do not want to burden the world with too many compression schemes, an adaptive scheme such as LZW that performs quite well on a wide range of images is very desirable. LZW may not always give optimal compression ratios, but its adaptive nature and relative simplicity seem to make it a good choice. Experiments thus far indicate that we can expect compression ratios of between 1.5 and 3.0 to 1 from LZW, with no loss of data, on continuous tone grayscale scanned images. If we zero the least significant one or two bitplanes of 8-bit data, higher ratios can be achieved. These bitplanes often consist chiefly of noise, in which case little or no loss in image quality will be perceived. Palette color images created in a paint program generally compress much better than continuous tone scanned images, since paint images tend to be more repetitive. It is not unusual to achieve compression ratios of 10 to 1 or better when using LZW on palette color paint images. By way of comparison, PackBits, used in TIFF for black and white bilevel images, does not do well on color paint images, much less continuous tone grayscale and color images. 1.2 to 1 seemed to be about average for 4-bit images, and 8-bit images are worse. It has been suggested that the CCITT 1D scheme could be used for continuous tone images, by compressing each bitplane separately. No doubt some compression could be achieved, but it seems unlikely that a scheme based on a fixed table that is optimized for short black runs separated by longer white runs would be a very good choice on any of the bitplanes. It would do quite well on the high-order bitplanes (but so would a simpler scheme like PackBits), and would do quite poorly on the low-order bitplanes. We believe that the compression ratios would generally not be very impressive, and the process would in addition be quite slow. Splitting the pixels into bitplanes and putting them back together is somewhat expensive, and the coding is also fairly slow when implemented in software. Another approach that has been suggested uses uses a 2D differencing step following by coding the differences using a fixed table of variable-length codes. This type of scheme works quite well on many 8-bit grayscale images, and is probably simpler to implement than LZW. But it has a number of disadvantages when used on a wide variety of images. First, it is not adaptive. This makes a big difference when compressing data such as 8-bit images that have been _sharpened_ using one of the standard techniques. Such images tend to get larger instead of smaller when compressed. Another disadvantage of these schemes is that they do not do well with a wide range of bit depths. The built-in code table has to be optimized for a particular bit depth in order to be effective. Finally, we should mention _lossy_ compression schemes. Extensive research has been done in the area of lossy, or non- information-preserving image compression. These techniques generally yield much higher compression ratios than can be achieved by fully-reversible, information-preserving image compression techniques such as PackBits and LZW. Some disadvantages: many of the lossy techniques are so computationally expensive that hardware assists are required. Others are so complicated that most microcomputer software vendors could not afford either the expense of implementation or the increase in application object code size. Yet others sacrifice enough image quality to make them unsuitable for publishing use. In spite of these difficulties, we believe that there will one day be a standardized lossy compression scheme for full color images that will be usable for publishing applications on microcomputers. An International Standards Organization group, ISO/IEC/JTC1/SC2/WG8, in cooperation with CCITT Study Group VIII, is hard at work on a scheme that might be appropriate. We expect that a future revision of TIFF will incorporate this scheme once it is finalized, if it turns out to satisfy the needs of desktop publishers and others in the microcomputer community. This will augment, not replace, LZW as an approved TIFF compression scheme. LZW will very likely remain the scheme of choice for Palette color images, and perhaps 4-bit grayscale images, and may well overtake CCITT 1D and PackBits for bilevel images. Future LZW Extensions Some images compress better using LZW coding if they are first subjected to a process wherein each pixel value is replaced by the difference between the pixel and the preceding pixel. Performing this differencing in two dimensions helps some images even more. However, many images do not compress better with this extra preprocessing, and for a significant number of images, the compression ratio is actually worse. We are therefore not making differencing an integral part of the TIFF LZW compression scheme. However, it is possible that a _prediction_ stage like differencing may exist which is effective over a broad range of images. If such a scheme is found, it may be incorporated in the next major TIFF revision. If so, a new value will be defined for the new _Predictor_ TIFF tag. Therefore, all TIFF readers that read LZW files must pay attention to the Predictor tag. If it is 1, which is the default case, LZW decompression may proceed safely. If it is not 1, and the reader does not recognize the specified prediction scheme, the reader should give up. Acknowledgements The original LZW reference has already been given. The use of ClearCode as a technique to handle overflow was borrowed from the compression scheme used by the Graphics Interchange Format (GIF), a small-color-paint-image-file format used by CompuServe that also is an adaptation of the LZW technique. Joff Morgan and Eric Robinson of Aldus were each instrumental in their own way in getting LZW off the ground. Appendix G: TIFF Classes Rationale TIFF was designed to make life easier for scanner vendors, desktop publishing software developers, and users of these two classes of products, by reducing the proliferation of proprietary scanned image formats. It has succeeded far beyond our expectations in this respect. But we had expected that TIFF would be of interest to only a dozen or so scanner vendors (there weren_t any more than that in 1985), and another dozen or so desktop publishing software vendors. This turned out to be a gross underestimate. The only problem with this sort of success is that TIFF was designed to be powerful and flexible, at the expense of simplicity. It takes a fair amount of effort to handle all the options currently defined in this specification (probably no application does a complete job), and that is currently the only way you can be sure that you will be able to import any TIFF image, since there are so many image-generating applications out there now. So here is an attempt to channel some of the flexibility of TIFF into more restrictive paths, using what we have learned in the past two years about which options are the most useful. Such an undertaking is of course filled with fairly arbitrary decisions. But the result is that writers can more easily write files that will be successfully read by a wide variety of applications, and readers can know when they can stop adding TIFF features. The price we pay for TIFF Classes is some loss in the ability to adapt. Once we establish the requirements for a TIFF Class, we can never add new requirements, since old software would not know about these new requirements. (The best we can do at that point is establish new TIFF Classes. But the problem with that is that we could quickly have too many TIFF Classes.) So we must believe that we know what we are doing in establishing these Classes. If we do not, any mistakes will be expensive. Overview Four TIFF Classes have been defined: Class B for bilevel (1-bit) images Class G for grayscale images Class P for palette color images Class R for RGB full color images To save time and space, we will usually say _TIFF B_, _TIFF G_, _TIFF P,_ and _TIFF R._ If we are talking about all four types, we may write _TIFF X._ (Note to fax people: if you are interested in a fax TIFF F Class, please get together and decide what should be in TIFF Class F files. Let us know if we can help in any way. When you have decided, send us your results, so that we can include the information here.) Core Requirements This section describes requirements that are common to all TIFF Class X images. General Requirements The following are required characteristics of all TIFF Class X files. Where there are options, TIFF X writers can do whichever one they want, though we will often recommend a particular choice, but TIFF X readers must be able to handle all of them. Please pay close attention to the recommendations. It is possible that at some point in the future, new and even-simpler TIFF classes will be defined that include only recommended features. You will need to read at least the first three sections of the main specification in order to fully understand the following discussion. Defaults. TIFF X writers may, but are not required, to write out a field that has a default value, if the default value is the one desired. TIFF X readers must be prepared to handle either situation. Other fields. TIFF X readers must be prepared to encounter fields other than the required fields in TIFF X files. TIFF X writers are allowed to write fields such as Make, Model, DateTime, and so on, and TIFF X readers can certainly make use of such fields if they exist. TIFF X readers must not, however, refuse to read the file if such optional fields do not exist. _MM_ and _II_ byte order. TIFF X readers must be able to handle both byte orders. TIFF writers can do whichever is most convenient or efficient. Images are crossing the IBM PC/Macintosh boundary (and others as well) with a surprisingly high frequency. We could force writers to all use the same byte order, but preliminary evidence indicates that this will cause problems when we start seeing greater-than-8-bit images. Reversing bytes while scanning could well slow down the scanning process enough to cause the scanning mechanism to stop, which tends to create image quality problems. Multiple subfiles. TIFF X readers must be prepared for multiple images (i.e., subfiles) per TIFF file, although they are not required to do anything with any image after the first one. TIFF X writers must be sure to write a long word of 0 after the last IFD (this is the standard way of signalling that this IFD was the last one) as indicated in the TIFF structure discussion. If a TIFF X writer writes multiple subfiles, the first one must be the full resolution image. Subsequent subimages, such as reduced resolution images and transparency masks, may be in any order in the TIFF file. If a reader wants to make use of such subimages, it will have to scan the IFD's before deciding how to proceed. TIFF X Editors. Editors, applications that modify TIFF files, have a few additional requirements. TIFF editors must be especially careful about subfiles. If a TIFF editor edits a full-resolution subfile, but does not update an accompanying reduced-resolution subfile, a reader that uses the reduced-resolution subfile for screen display will display the wrong thing. So TIFF editors must either create a new reduced-resolution subfile when they alter a full-resolution subfile, or else they must simply delete any subfiles that they aren_t prepared to deal with. A similar situation arises with the fields themselves. A TIFF X editor need only worry about the TIFF X required fields. In particular, it is unnecessary, and probably dangerous, for an editor to copy fields that it does not understand. It may have altered the file in a way that is incompatible with the unknown fields. Required Fields NewSubfileType. LONG. Recommended but not required. ImageWidth. SHORT or LONG. (That is, both _SHORT_ and _LONG_ TIFF data types are allowed, and must be handled properly by readers. TIFF writers can use either.) TIFF X readers are not required to read arbitrarily large files however. Some readers will give up if the entire image cannot fit in available memory. (In such cases the reader should inform the user of the nature of the problem.) Others will probably not be able to handle ImageWidth greater than 65535. Recommendation: use LONG, since resolutions seem to keep going up. ImageLength. SHORT or LONG. Recommendation: use LONG. RowsPerStrip. SHORT or LONG. Readers must be able to handle any value between 1 and 2**32-1. However, some readers may try to read an entire strip into memory at one time, so that if the entire image is one strip, the application may run out of memory. Recommendation 1: Set RowsPerStrip such that the size of each strip is about 8K bytes. Do this even for uncompressed data, since it is easy for a writer and makes things simpler for readers. (Note: extremely wide, high-resolution images may have rows larger than 8K bytes; in this case, RowsPerStrip should be 1, and the strip will just have to be larger than 8K. Recommendation 2: use LONG. StripOffsets. SHORT or LONG. As explained in the main part of the specification, the number of StripOffsets depends on RowsPerStrip and ImageLength. Recommendation: always use LONG. (LONG must, of course, be used if the file is more than 64K bytes in length.) StripByteCounts. SHORT or LONG. Many existing TIFF images do not contain StripByteCounts, because, in a strict sense, they are unnecessary. It is possible to write an efficient TIFF reader that does not need to know in advance the exact size of a compressed strip. But it does make things considerably more complicated, so we will require StripByteCounts in TIFF X files. Recommendation: use SHORT, since strips are not supposed to be very large. XResolution, YResolution. RATIONAL. Note that the X and Y resolutions may be unequal. A TIFF X reader must be able to handle this case. TIFF X pixel-editors will typically not care about the resolution, but applications such as page layout programs will. ResolutionUnit. SHORT. TIFF X readers must be prepared to handle all three values for ResolutionUnit. TIFF Class B - Bilevel Required (in addition to the above core requirements) The following fields and values are required for TIFF B files, in addition to the fields required for all TIFF X images (see above). SamplesPerPixel = 1. SHORT. (Since this is the default, the field need not be present. The same thing holds for other required TIFF X fields that have defaults.) BitsPerSample = 1. SHORT. Compression = 1, 2 (CCITT 1D), or 32773 (PackBits). SHORT. TIFF B readers must handle all three. Recommendation: use PackBits. It is simple, effective, fast, and has a good worst-case behavior. CCITT 1D is definitely more effective in some situations, such as scanning a page of body text, but is tough to implement and test, fairly slow, and has a poor worst-case behavior. Besides, scanning a page of 12 point text is not very useful for publishing applications, unless the image data is turned into ASCII text via OCR software, which is outside the scope of TIFF. PhotometricInterpretation = 0 or 1. SHORT. A Sample TIFF B Image Offset Value (hex) Name (mostly hex) Header: 0000 Byte Order 4D4D 0002 Version 002A 0004 1st IFD pointer 00000014 IFD: 0014 Entry Count 000D 0016 NewSubfileType 00FE 0004 00000001 00000000 0022 ImageWidth 0100 0004 00000001 000007D0 002E ImageLength 0101 0004 00000001 00000BB8 003A Compression 0103 0003 00000001 8005 0000 0046 PhotometricInterpretation 0106 0003 00000001 0001 0000 0052 StripOffsets 0111 0004 000000BC 000000B6 005E RowsPerStrip 0116 0004 00000001 00000010 006A StripByteCounts 0117 0003 000000BC 000003A6 0076 XResolution 011A 0005 00000001 00000696 0082 YResolution 011B 0005 00000001 0000069E 008E Software 0131 0002 0000000E 000006A6 009A DateTime 0132 0002 00000014 000006B6 00A6 Next IFD pointer 00000000 Fields pointed to by the tags: 00B6 StripOffsets Offset0, Offset1, ... Offset187 03A6 StripByteCounts Count0, Count1, ... Count187 0696 XResolution 0000012C 00000001 069E YResolution 0000012C 00000001 06A6 Software "PageMaker 3.0" 06B6 DateTime "1988:02:18 13:59:59" Image Data: 00000700 Compressed data for strip 10 xxxxxxxx Compressed data for strip 179 xxxxxxxx Compressed data for strip 53 xxxxxxxx Compressed data for strip 160 . . . End of example Comments on the TIFF B example 1. The IFD in our example starts at position hex 14. It could have been anywhere in the file as long as the position is even and greater than or equal to 8, since the TIFF header is 8 bytes long and must be the first thing in a TIFF file. 2. With 16 rows per strip, we have 188 strips in all. 3. The example uses a number of optional fields, such as DateTime. TIFF X readers must safely skip over these fields if they do not want to use the information. And TIFF X readers must not require that such fields be present. 4. Just for fun, our example has highly fragmented image data; the strips of our image are not even in sequential order. The point is that strip offsets must not be ignored. Never assume that strip N+1 follows strip N. Incidentally, there is no requirement that the image data follows the IFD information. Just the follow the pointers, whether they be IFD pointers, field pointers, or Strip Offsets. TIFF Class G - Grayscale Required (in addition to the above core requirements) SamplesPerPixel = 1. SHORT. BitsPerSample = 4, 8. SHORT. There seems to be little justification for working with grayscale images shallower than 4 bits, and 5-bit , 6-bit, and 7-bit images can easily be stored as 8-bit images, as long as you can compress the _unused_ bit planes without penalty. And we can do just that with LZW (Compression = 5.) Compression = 1 or 5 (LZW). SHORT. Recommendation: use 5, since LZW decompression is turning out to be quite fast. PhotometricInterpretation = 0 or 1. SHORT. Recommendation: use 1, due to popular user interfaces for adjusting brightness and contrast. TIFF Class P - Palette Color Required (in addition to the above core requirements) SamplesPerPixel = 1. SHORT. We use each pixel value as an index into all three color tables in ColorMap. BitsPerSample = 1,2,3,4,5,6,7, or 8. SHORT. 1,2,3,4, and 8 are probably the most common, but as long as we are doing that, the rest come pretty much for free. Compression = 1 or 5. SHORT. PhotometricInterpretation = 3 (Palette Color). SHORT. ColorMap. SHORT. Note that bilevel and grayscale images can be represented as special cases of palette color images. As soon as enough major applications support palette color images, we may want to start getting rid of distinctions between bilevel, grayscale, and palette color images. TIFF Class R - RGB Full Color Required (in addition to the above Core Requirements) SamplesPerPixel = 3. SHORT. One sample each for Red, Green, and Blue. BitsPerSample = 8,8,8. SHORT. Shallower samples can easily be stored as 8-bit samples with no penalty if the data is compressed with LZW. And evidence to date indicates that images deeper than 8 bits per sample are not worth the extra work, even in the most demanding publishing applications. PlanarConfiguration = 1 or 2. SHORT. Recommendation: use 1. Compression = 1 or 5. SHORT. PhotometricInterpretation = 2 (RGB). SHORT. Recommended Recommended for TIFF Class R, but not required, are the new (as of Revision 5.0) colorimetric information tags. See Appendix H. Conformance and User Interface Applications that write valid TIFF X files should include _TIFF B_ and/or _TIFF G_ and/or _TIFF P_ and/or _TIFF R_ and/or in their product spec sheets, if they can write the respective TIFF Class X files. If your application writes all four of these types, you may wish to write it as _TIFF B,G,P,R._ Of course, a term like _TIFF B,_ while fine for communicating with other vendors, will not convey much information to a typical user. In this case, a phrase such as _Standard TIFF Black-and-White Scanned Images_ might be better. The same terminology guidelines apply to applications that read TIFF Class X files. If your application reads more kinds of files than it writes, or vice versa, it would be a good idea to make that clear to the buyer. For example, if your application reads TIFF B and TIFF G files, but writes only TIFF G files, you should write it that way in the spec sheet. Appendix H: Image Colorimetry Information Chris Sears 210 Lake Street San Francisco, CA 94118 June 4, 1988 Revised August 8, 1988 I. Introduction Our goal is to accurately reproduce a color image using different devices. Accuracy requires techniques of measurement and a standard of comparison. Different devices imply device independence. Colorimetry provides the framework to solve these problems. When an image has a complete colorimetric description, in principle it can be reproduced identically on different monitors and using different media, such as offset lithography. The colorimetry data is specified when the image is created or changed. A scanned image has colorimetry data derived from the filters and light sources of the scanner and a synthetic image has colorimetry data corresponding to the monitor used to create it or the monitor model of the rendering environment. This data is used to map an input image to the markings or colors of a particular output device. Section II describes various standards organizations and their work in color. Section III describes our motivation for seeking these tags. Section IV describes our goals of reproduction. Sections V, VI and VII introduce the colorimetry tags. Section VIII specifies the tags themselves. Section IX describes the defaults. Section X discusses the limitations and some of the other issues. Section XI provides a few references. II. Related Standards TIFF is a general standard for describing image data. It would be foolish for us to change TIFF in a way that did not match existing industry and international standards. Therefore, we have taken pains to note in the discussion below the efforts of various standards organizations and select defaults from the work of these organizations. CIE (Commission Internationale de l'Eclairage) The basis of the colorimetry information is the CIE 1931 Standard Observer [3]. While other color models could be supported [1] [4], CIE 1931 XYZ is the international standard accepted across industries for specifying color and CIE xyY is the chromaticity diagram associated with CIE 1931 XYZ tristimulus values. NTSC (National Television System Committee) NTSC is of interest primarily for historical reasons and its use in encoding television data. Manufacturing standards for monitors have for some time drifted significantly from the 1953 NTSC colorimetry specification. SMPTE (Society of Motion Picture and Television Engineers) Most of the work by NTSC has been largely subsumed by SMPTE. This organization has a set of standards called "Recommended Practices" that apply to various technical aspects of film and television production [5] [6]. ISO (International Standards Organization) ISO has become involved in color standards through work on a color addendum to "Office Document Architecture (ODA) and Interchange Format" [7]. ANSI (American National Standards Institute) ANSI is the American representative to ISO . III. Motivation Our motivation for defining these tags stems from our research and development in color separation technology. With the information described here and the RGB pixel data, we have all of the information necessary for generating high-quality color separations. We could supply the colorimetry information outside of the image file. But since TIFF provides a convenient mechanism for bundling all of the relevant information in a single place, tags are defined to describe this information in color TIFF files. A color image rendered with incorrect colorimetry information looks different from the original. One of our early test images has an artifact in it where the image was scanned with one set of primaries and color ramps were overlaid on top of it with different primaries. The blue ramp looked purple when we printed it. Using incorrect gamma tables or white points can also lead to distorted images. The best way to avoid these kinds of errors is to allow the creator of an image to supply the colorimetry information along with the RGB values [1] [2]. The purpose of the colorimetry data is to allow a projective transformation from the primaries and white point of the image to the primaries and white point of the rendering media. Gamma reflects the non-linear transfer gradient of real media. IV. Colorimetric Color Reproduction Earlier we said that given the proper colorimetric data an image could be rendered identically using two different calibrated devices. By identical, we mean colorimetric reproduction [9]. Specifically, the chromaticities match and the luminance is scaled to correspond to the luminance range of the output device. Because of this, we only need the chromaticity coordinates of the white point and primaries. The absolute luminance is arbitrary and unnecessary. V. White Point In TIFF 4.0, the white point was specified as D65. This appendix allocates a separate tag for describing the white point and D65 is the logical default since it is the SMPTE standard [6]. The white point is defined colorimetrically in the CIE xyY chromaticity diagram. While it is rare for monitors to differ from D65, scanned images often have different white points. Rendered images can have arbitrary white points. The graphic arts use D50 as the standard viewing light source [8]. VI. Primary Chromaticities In TIFF 4.0, the primary color chromaticities matched the NTSC specification. With the wide variety of color scanners, monitors and renderers, TIFF needs a mechanism for accurately describing the chromaticities of the primary colors. We use SMPTE as the default chromaticity since conventional monitors are closer to SMPTE and some monitors (Conrac 6545) are manufactured to the SMPTE specifications. We dont use the NTSC chromaticities and white point because present day monitors don't use them and must be _matrixed_ to approximate them. As an example, the primary color chromaticities used by the Sony Trinatron differ from those recommended by SMPTE. In general, since real monitors vary from the industry standards, the chromaticities of primaries are described in the CIE xyY system. This allows a reproduction system to compensate for the differences. VII. Color Response Curves This tag defines three color response curves, one each for red, green, and blue color information. The width of each entry is 16 bits, as implied by the type SHORT. The minimum intensity is represented by 0 and the maximum by 65535. For example, black is represented by 0,0,0 and white by 65535, 65535, 65535. The length of each curve is 2**BitsPerSample. A ColorResponseCurves field for RGB data where each of the samples is 8 bits deep would have 3*256 entries. The 256 red entries would come first, followed by 256 green entries, followed by 256 blue entries. The purpose of the ColorResponseCurves field is to act as a lookup table mapping sample values to specific intensity values, so that an image created on one system can be displayed on another with minimal loss of color fidelity. The ColorResponseCurves field thus describes the _gamma_ of an image, so that a TIFF reader on another system can compensate for both the image gamma and the gamma of the reading system. Gamma is a term that relates to the typically nonlinear response of most display devices, including monitors. In most display systems, the voltage applied to the CRT is directly proportional to the value of the red, green, or blue sample. However, the resulting luminance emitted by the phosphor is not directly proportional to the voltage. This relationship is approximated in most displays by luminance = voltage ** gamma The NTSC standard gamma of 2.2 adequately describes most common video systems. The standard gamma of 2.2 implies a dim viewing surround. (We know of no SMPTE recommended practice for gamma.) The following example uses an 8 bit sample with value of 127. voltage = 127 / 255 = 0.4980 luminance = 0.4980 ** 2.2 = 0.2157 In the examples below, we only consider a single primary and therefore only a single curve. The same analysis applies to each of the red, green, and blue primaries and curves. Also, and without loss of generality, we assume that there is no hardware color map, so that we must alter the pixel values themselves. If there is a color map, the manipulations can be done on the map instead of on the pixels. If no ColorResponseCurves field exists in a color image, the reader should assume a gamma of 2.2 for each of the primaries. This default curve can be generated with the following C code: ValuesPerSample = 1 << BitsPerSample; for (curve[0] = 0, i = 1; i < ValuesPerSample; i++) curve[i] = floor (pow (i / (ValuesPerSample - 1.0), 2.2) * 65535.0 + .5); The displaying or rendering application can know its own gamma, which we will call the _destination gamma._ (An uncalibrated system can usually assume that its gamma is 2.2 without going too far wrong.) Using this information the application can compensate for the gamma of the image, as we shall see below. If the source and destination systems are both adequately described by a gamma of 2.2, the writer would omit the ColorResponseCurves field, and the reader can simply read the image directly into the frame buffer. If a writer writes out the ColorResponseCurves field, then a reader must assume that the gammas differ. A reader must then perform the following computation on each sample in the image: NewSampleValue = floor (pow (curve[OldSampleValue] / 65535.0, 1.0 / DestinationGamma) * (ValuesPerSample - 1.0) + .5); Of course, if the _gamma_ of the destination system is not well- approximated with an exponential function, an arbitrary table lookup may be used in place of raising the value to 1.0 / DestinationGamma. Leave out ColorResponseCurves if using the default gamma. This saves about 1.5K in the most common case, and, after all, omission is the better part of compression. Do not use this field to store frame buffer color maps. Use instead the ColorMap field. Note, however, that ColorResponseCurves may be used to refine the information in a ColorMap if desired. The above examples assume that a single parameter gamma system adequately approximates the response characteristics of the image source and destination systems. This will usually be true, but our use of a table instead of a single gamma parameter gives the flexibility to describe more complex relationships, without requiring additional computation or complexity. VIII. New Tags and Changes The following tags should be placed in the "Basic Fields" section of the TIFF specification: White Point Tag = 318 (13E) Type = RATIONAL N = 2 The white point of the image. Note that this value is described using the 1931 CIE xyY chromaticity diagram and only the chromaticity is specified. The luminance component is arbitrary and not specified. This can correspond to the white point of a monitor that the image was painted on, the filter set/light source combination of a scanner, or to the white point of the illumination model of a rendering package. Default is the SMPTE white point, D65: x = 0.313, y = 0.329. The ordering is x, y. PrimaryChromaticities Tag = 319 (13F) Type = RATIONAL N = 6 The primary color chromaticities. Note that these values are described using the 1931 CIE xyY chromaticity diagram and only the chromaticities are specified. For paint images, these represent the chromaticities of the monitor and for scanned images they are derived from the filter set/light source combination of a scanner. Default is the SMPTE primary color chromaticities: Red: x = 0.635 y = 0.340 Green: x = 0.305 y = 0.595 Blue: x = 0.155 y = 0.070 The ordering is red x, red y, green x, green y, blue x, blue y. Color Response Curves Default for ColorResponseCurves represents curves corresponding to the NTSC standard gamma of 2.2. IX. Defaults The defaults used by TIFF reflect industry standards. Both the WhitePoint and PrimaryChromaticities tags have defaults that are promoted by SMPTE. In addition, the default for the ColorResponseCurves tag matches the NTSC specification of a gamma of 2.2. The purpose of these defaults is to allow reasonable results in the absence of accurate colorimetry data. An uncalibrated scanner or paint system produces an image that be displayed identically, though probably incorrectly on two different but calibrated systems. This is better then the uncertain situation where the image might be rendered differently on two different but calibrated systems. X. Limitations and Issues This section discusses several of the limitations and issues involved in colorimetric reproduction. Scope of Usefulness For many purposes the data recommended here is unnecessary and can be omitted. For presentation graphics where there are only a few colors, being able to tell red from green is probably good enough. In this case the tags can be ignored and there is no overhead. In more demanding color reproduction environments, this data allows images to be described device independently and at small cost. User Burdens The data we recommend isnt a user burden; it is really a systems issue. It allows a systems solution but doesn't require user intercession. Calibration however is a separate issue. It is likely to involve the user. Resolution Versus Fidelity Some manufacturers supply greater than 24 bits of resolution for color specification. The purpose of this is either to avoid artifacts such as contouring in the shadows or in some cases to be more specific or device independent about the color. Both reasons can be misguided. Other, less expensive techniques can be used to prevent artifacts, such as deeper color maps. As for accuracy, fidelity is more important than precision. Colorimetric Color Reproduction There are other choices for objectives of color reproduction [9]. Spectral color reproduction is a stronger condition and most are weaker, such as preferred color reproduction. While device independent spectral color reproduction is impossible, device independent colorimetric reproduction is possible, within a tolerance and within the limits of the gamuts of the devices. By choosing a strong criteria we allow the important objectives of weaker criteria, such as preferred color reproduction, to be part of design packages. Metamerism If two patches of color are identical under one light and different under another, they are said to be metameric pairs. Colorimetric color reproduction is a weaker condition than spectral color reproduction and hence allows metamerism problems. By standardizing the viewing conditions we can largely finesse the metamerism problem for print. Because television is self- luminous and doesn't use spectral absorption, metamerism isnt so much a problem. Color Models - xyY Versus Luv, etc. We choose xyY over Luv [1] because XYZ is the international standard for color specification and xyY is the chromaticity diagram associated with XYZ. Luv is meant for color difference measurement. Ambient Environment And Viewing Surrounds The viewing environment affects how the eye perceives color. The eye adapts to a dark room and it adapts to a colored surround. While these problems can be compensated for within the colorimetric framework [4], it is much better to finesse them by standardizing. The design environment should match the intended viewing environment. Specifically it should not be a pitch dark room and, on average, it should be of a neutral color. For print, ANSI recommends a Munsell N-8 surface [8]. XI. References In particular, we would like to mention the work of Stuart Ring of the Copy Products Division of the Eastman Kodak Company. He and his colleagues are promoting a color data interchange paradigm. They are working closely with the ISO 8613 Working Group [7]. [1] Color Data Interchange Paradigm, Eastman Kodak, Rochester, New York, 7 December 1987. [2] Color Reproduction and Illumination Models, Roy Hall, International Summer Institute: State of the Art in Computer Graphics, 1986. [3] CIE Colorimetry: Official Recommendations of the International Commission on Illumination, Publication 15-2, 1986. [4] Color Science: Concepts and Methods, Quantitative Data and Formulae, Gunter Wyszecki, W.S. Stiles, John Wiley and Sons, Inc., New York, New York, 1982. [5] Color Monitor Colorimetry, SMPTE Recommended Practice RP 145-1987. [6] Color Temperature for Color Television Studio Monitors, SMPTE Recommended Practice RP 37. [7] Office Document Architecture (ODA) and Interchange Format_Addendum on Colour, ISO 8613 Working Draft. [8] ANSI Standard PH 2.30-1985. [9] The Reproduction of Colour in Photography, Printing and Television, R. W. G. Hunt, Fountain Press, Tolworth, England, 1987. [10] Raster Graphics Handbook, The Conrac Corporation, Van Nostrand Reinhold Company, New York, New York, 1985. Good description of gamma. Appendix I: Horizontal Differencing Predictor This appendix, written after the release of Revision 5.0 of the TIFF specification, is still in draft form. Please send any comments to the Aldus Developers Desk. Revision 5.0 of the TIFF specification defined a new tag called _Predictor_ that describes techniques that may be used in conjuction with TIFF compression schemes. We now define a Predictor that greatly improves compression ratios for some images. The horizontal differencing predictor is assigned the tag value Predictor = 2: Predictor Tag = 317 (13D) Type = SHORT N = 1 A predictor a mathematical operator that is applied to the image data before the encoding scheme is applied. Currently (as of revision 5.0) this tag is used only with LZW (Compression=5) encoding, since LZW is probably the only TIFF encoding scheme that benefits significantly from a predictor step. See Appendix F. 1 = No prediction scheme used before coding. 2 = Horizontal differencing. See Appendix I. Default is 1. The algorithm The idea is to make use of the fact that many continuous tone images rarely vary much in pixel value from one pixel to the next. In such images, if we replace the pixel values by differences between consecutive pixels, many of the differences should be 0, plus or minus 1, and so on. This reduces the apparent information content, and thus allows LZW to encode the data more compactly. Assuming 8-bit grayscale pixels for the moment, a basic C implementation might look something like this: char image[ ][ ]; int row, col; /* take horizontal differences: */ for (row = 0; row < nrows; row++) for (col = ncols - 1; col >= 1; col--) image[row][col] -= image[row][col-1]; If we don_t have 8-bit samples, we need to work a little harder, so that we can make better use of the architecture of most CPUs. Suppose we have 4-bit samples, packed two to a byte, in normal TIFF uncompressed (i.e., Compression=1) fashion. In order to find differences, we want to first expand each 4-bit sample into an 8-bit byte, so that we have one sample per byte, low-order justified. We then perform the above horizontal differencing. Once the differencing has been completed, we then repack the 4- bit differences two to a byte, in normal TIFF uncompressed fashion. If the samples are greater than 8 bits deep, expanding the samples into 16-bit words instead of 8-bit bytes seems like the best way to perform the subtraction on most computers. Note that we have not lost any data up to this point, nor will we lose any data later on. It might at first seem that our differencing might turn 8-bit samples into 9-bit differences, 4- bit samples into 5-bit differences, and so on. But it turns out that we can completely ignore the _overflow_ bits caused by subtracting a larger number from a smaller number and still reverse the process without error. Normal twos complement arithmetic does just what we want. Try an example by hand if you need more convincing. Up to this point we have implicitly assumed that we are compressing bilevel or grayscale images. An additional consideration arises in the case of color images. If PlanarConfiguration is 2, there is no problem. Differencing proceeds the same way as it would for grayscale data. If PlanarConfiguration is 1, however, things get a little trickier. If we didn't do anything special, we would be subtracting red sample values from green sample values, green sample values from blue sample values, and blue sample values from red sample values, which would not give the LZW coding stage much redundancy to work with. So we will do our horizontal differences with an offset of SamplesPerPixel (3, in the RGB case). In other words, we will subtract red from red, green from green, and blue from blue. The LZW coding stage is identical to the SamplesPerPixel=1 case. We require that BitsPerSample be the same for all 3 samples. Results and guidelines LZW without differencing works well for 1-bit images, 4-bit grayscale images, and synthetic color images. But natural 24-bit color images and some 8-bit grayscale images do much better with differencing. For example, our 24-bit natural test images hardly compressed at all using _plain_ LZW: the average compression ratio was 1.04 to 1. The average compression ratio with horizontal differencing was 1.40 to 1. (A compression ratio of 1.40 to 1 means that if the uncompressed image is 1.40MB in size, the compressed version is 1MB in size.) Although the combination of LZW coding with horizontal differencing does not result in any loss of data, it may be worthwhile in some situations to give up some information by removing as much noise as possible from the image data before doing the differencing, especially with 8-bit samples. The simplest way to get rid of noise is to mask off one or two low- order bits of each 8-bit sample. On our 24-bit test images, LZW with horizontal differencing yielded an average compression ratio of 1.4 to 1. When the low-order bit was masked from each sample, the compression ratio climbed to 1.8 to 1; the compression ratio was 2.4 to 1 when masking two bits, and 3.4 to 1 when masking three bits. Of course, the more you mask, the more you risk losing useful information along with the noise. We encourage you to experiment to find the best compromise for your device. For some applications it may be useful to let the user make the final decision. Interestingly, most of our RGB images compressed slightly better using PlanarConfiguration=1. One might think that compressing the red, green, and blue difference planes separately (PlanarConfiguration=2) might give better compression results than mixing the differences together before compressing (PlanarConfiguration=1), but this does not appear to be the case. Incidentally, we tried taking both horizontal and vertical differences, but the extra complexity of two-dimensional differencing did not appear to pay off for most of our test images. About one third of the images compressed slightly better with two-dimensional differencing, about one third compressed slightly worse, and the rest were about the same. Appendix J: Palette Color This appendix, written after the release of Revision 5.0 of the TIFF specification, is still in draft form. Please send any comments to the Aldus Developers Desk. Revision 5.0 of the TIFF specification defined a new PhotometricInterpretation value called _palette color._ We have been wondering lately if this additional complexity is worth the implementation expense. If not, let's drop it before too many people start creating palette color images. The Proposal Instead of a separate palette color image type, there seems to be no compelling reason why palette (mapped) color images should not be stored as _full color_ (usually 24-bit) RGB images. Objections The most obvious objection is the amount of space required. But if you care about how much space the image takes up on disk, you should use LZW compression, which is ideally suited to most palette color images. (LZW compresses most paint-type palette color images 5:1 or more.) And if you use LZW compression, it turns out that palette color images stored as full color images compress to almost exactly the same size as palette color images stored as palette color images. That is, with LZW compression, there is no penalty for storing palette color images as full color RGB images. The resulting file may be a few percent larger, but it will not be three times as large. See Appendix F for more information on how LZW works. Another objection might be that an application might want to process the image differently if it is _really_ a palette color image. But we can easily add auxiliary information that can help a TIFF reader to quickly categorize color images if it wants to. See the _New tags_ section below. Benefits It may be obvious, but it is probably worth discussing why we want to abolish palette color images as a distinct classification. The main problem is that palette color as a separate type makes life more hazardous and confusing for users. The confusion factor is aggravated because users already have to be somewhat aware of distinctions between bilevel, grayscale, and color images. Having two main types of color images is hard for many users to grasp, and it is probably not possible to totally hide this complexity from the user in certain situations. The hazard level goes up because some applications may accept palette color but not full color images, or full color but not palette color images, or may accept 8-bit palette color images but not 4-bit or 3-bit palette color images. The second problem is that writing and maintaining code to deal with an additional image type is somewhat expensive for TIFF readers. The cost of supporting palette color images will depend on the application, but we believe that, in general, the cost will be substantial. It seems to make more sense to put the burden on TIFF writers to convert palette color images into full color image form than to make TIFF readers handle an additional color image type, since there are more TIFF readers than writers at this point. New tags Here are some proposed new tags that can help to classify color images, and make up for not having a separate palette color class. They are not required for TIFF Class R , but are strongly recommended for color TIFF images created by palette-type color paint programs. ColorImageType Tag = 318 (13E) Type = SHORT N = 1 Gives TIFF color image readers a better idea of what kind of color image it is. There will be borderline cases. 1 = Continuous tone, natural image. 2 = Synthetic image, using a greatly restricted range of colors. Such images are produced by most color paint programs. See ColorList for a list of colors used in this image. Default is 1. ColorList Tag = 319 (13F) Type = BYTE or SHORT N = the number of colors that are used in this image, times SamplesPerPixel A list of colors that are used in this image. Use of this field is only practical for images containing a greatly restricted (usually less than or equal to 256) range of colors. ColorImageType should be 2. See ColorImageType. The list is organized as an array of RGB triplets, with no pad. The RGB triplets are not guaranteed to be in any particular order. Note that the red, green, and blue components can either be a BYTE or a SHORT in length. BYTE should be sufficient for most applications. No default. " 1 long length of CMAP (=48 for a 16 colour palette) 48 bytes (for a 16 colour palette) palette data Of course the PROP could also have included a common BMHD chunk and possibly other chunks as well. And then the ILBM FORMs of the list follows. These can in their turn be grouped in sub-lists - e.g. one list with 16*16 sprites, one with 32*32 sprites, special lists with 3 plane sprites etc. . ;n*.. nlIMG_IFF TXT Q+]~ THE IMG AND IFF ILBM PICTURE FILE FORMATS (plus the EDT sprite file of Art Studio) BY: Mrten Lindstrm ----------------------------------------- As a small addition to Dave Baggett's file on picture formats, here are some bits that were missing, some of which I have found through investigations of my own, and some of which I owe to other sources (mainly articles in old ST World and German ST Magazine): 1) Fuller descriptions of the two general - not hardware dependent - picture formats presented in Baggett's file, i.e. IMG and IFF ILBM (and of the general IFF system). This includes colour IMG files (XIMG, True and High colour and HyperPaint), some more chunk types that could be found in an IFF (ILBM) file, and what I've found out about the IFF ILBM compression type 2 of Deluxe Paint. 2) The Art Studio EDT sprite file, for anyone interested. I have not got the Commodore IFF DOCUMENTS file referred to in Guido van Rossum's file (sound.txt in ICTARI 4). If anyone has, I would be very interested to see it. WHAT PICTURE FORMAT IS THE 'ATARI STANDARD'? -------------------------------------------- Anyone wading through the plethora of ST picture formats listed in Dave Baggett's file, must feel impressed or even dismayed by the useless multitude. And be depressed by the fact that of these the only truly 'device independent' format, usable with screen dimensions and colour ranges outside the standard ST ones, is the IFF ILBM, defined by Commodore and Electronic Arts 1985. Closely hardware dependent file formats can be quite useful as simple to program screen dumps (basic Degas), or for instant show, without any data conversion, of full colour pictures on the ST screen (the clever Spectrum 512). But you don't need a TT or Falcon to have sometimes wished there was an agreed system on how to store larger than screen (scrolling) pictures or smaller than screen blocks, or emulated extra colours (shown on screen as two-colour patterns for instance). The apparent reason for the existing mess seems to be that IMG, the otherwise natural picture standard for the Atari GEM environment, was for a long time poorly or even incorrectly documented by Digital Research (the makers of GEM) and in addition lacked adequate rules on how to deal with colour which left able programmers without organised support and guidance, to define their own formats. Tom Hudson, the very man who defined Degas, soon realised the problem and made an admirable effort to replace the growing jungle of inadequate ST picture formats with IFF (Note that Degas Elite could handle IFF images long before any other ST paint program could) but sadly he succumbed to the dark forces of picture Chaos. Later Dimitri Koveos, who wrote the Atari registered HyperPaint, tried to re-establish IMG as the Atari picture standard and designed a way to store a colour palette with his IMG files (which he published in ST World). But he made the serious mistake of - like Degas - using the hardware format for the colours of the palette (which means that the HyperPaint format is unusable with Falcon pictures for example). There now - a bit late - DOES seem to exist a standard for colour IMG files, as anyone with the Atari Compendium will have noticed, capable of dealing with ANY bitmapped image including all Falcon screens (just like the IFF system could already do almost a decade ago). The palette colours are now stored in GEM VDI format as was always the only logical solution considering that the IMG format was supposed to be part of GEM. So what picture file format should be chosen by anyone now writing his own image handling ST program, who needs a more flexible format than Degas/NEO? Below are descriptions of IMG and IFF ILBM. I don't however know the PC formats GIF or TIFF (yet, but am expecting to get some material here soon), and maybe these are the ones to adopt on the ST as every- thing is revolving more and more around the PC? Any views (and infor- mation) are welcome. And here are the file descriptions, beginning with the EDT sprite file:- EDT sprite files of Art Studio ------------------------------ 16 words Hardware palette 1 byte Colour cycling: 1st nybble = start, 2nd nybble = end colour. 1 byte Colour cycling: Period (in VBLs I think) between moves (left to right). Negative: reverse direction. 0: Off. 1 word Sprite size (0-7): 0: 8x8 1: 16x16 2: 16x32 3: 32x16 4: 32x32 5: 32x64 6: 64x32 7: 64x64 (WxH) 1 word Animation: # of start sprite (First sprite = 0) 1 word Animation: # of end sprite 1 word Animation: Period (in VBLs?) between frames 1 word Animation: ? (Appears to take values between 0 and Period. Perhaps just a counter?) 1 word ? (Has always been zero in my tests) 1 word Output: "Output byte" (31 default) 1 word Output: 0:File 1:RS232 1 word Output: Number of lines/sprite 1 word Output: 0:Lines 1:Words 40960 bytes Data (E.g. 1024 8x8 sprites, 256 16x16 sprites 64 32x32 sprites or 16 64x64 sprites each with mask + 4 colour planes) --------- 41014 bytes total Data are stored sprite by sprite, line by line and, within each line, plane by plane. First comes the mask for a line and sprite, thereafter each of the four planes. Then the mask for a new line of the sprite etc. Note that in the mask, empty positions are marked with ones and the sprite is marked with zeroes. In spite of this, Art Studio zeroes in its entirety any 'empty' sprite slot in the EDT file, where no sprite has been placed, thus actually letting it represent a sprite filled with colour number zero. IMG files --------- ~~~~~~~~~~~ First eight words present in EVERY IMG file: 1 word IMG file version (usually 1) 1 word Header length IN WORDS. (Multiply by 2 and add to address of file start to determine where picture data start.) 1 word Number of colour planes (1=mono,2-8=palette,16=hi,24=true) (This word should also be possible to use, it struck me, for anyone wanting his program to cope with PC-imported IMG files. In these the first byte of the word should be non-zero and the second byte zero; in ST files vice versa. A PC file needs to have the bytes of every word in its header swapped, except anything to be read as bytes - see XIMG below. The picture byte data are identical on ST and PC.) 1 word Pattern length in bytes (usually 2) 1 word Source device pixel width (in principle in the unit m) 1 word Source device pixel height The unit is formally m (microns or micrometres i.e. thousands of millimetres). In reality however, these two values (usually taken from the VDI WORKOUT array) seem to be implemented only as relative dimensions (to be compared to each other only), except for ST high res where the pixel size of 372x372 m given by the VDI can be accepted in absolute terms as well. (For ST medium and low the VDI values 169x372 and 338x372 are obviously way too small.) 1 word Line width in pixels 1 word Number of lines ~~~~~~~~~~~ Should be present for 2-8 planes (but don't count on it) 4 bytes 'XIMG' in ascii 1 word Should be 0 for RGB colour format. 3 words GEM RGB-components for 'colour 0' (0-1000 in each word) 3 words GEM RGB-components for 'colour 1' etc. ...... (Colours must be listed in 'hardware' order - see below.) ~~~~~~~~~~~ Header length determines where data start ? bytes picture data ---------------- XIMG is (or so I would like to believe) the standard that anyone should now use when creating colour IMG pictures of his own. But a program could also come across IMG pictures with 2-8 planes without the XIMG header extension. You might then, if you are a zealous person, want your program to check for a HyperPaint hardware palette: ~~~~~~~~~~~ After the first 8 header words 1 word $80 (hexadecimal = 128 in decimal) HyperPaint flag 16 words (for 4 planes) Hardware palette data: 1 word per colour ~~~~~~~~~~~ Also check header length (=25 for 4-plane HyperPaint IMG) The Atari Compendium mentions a colour IMG format called 'STTT'. I assume this is the HyperPaint format, but since the Compendium seems - understandably perhaps - to be very reluctant to go any deeper into the subject of alternatives to the one hardware independent IMG standard, XIMG, I don't know. (Perhaps anyone else does?) If you can't find any (recognizable) palette data, and there are no external means to determine colours you may possibly, as a last resort, use the standard palette (the one that goes 'hardware' colour 0=White, 1=Red - except if last colour when black, 2=Green, 3=Yellow - except if last colour when black, 4=Blue, 5=Magenta, 6=Cyan, 7= Light Gray, 8=Dark gray, 9=Light Red, 10=L.Green, 11=L.Yellow, 12=L.Blue, 13=L.Magenta, 14=L.Cyan 15=Black). This seems to be how Digital Research had intended multi colour pictures to work in those days when GEM and IMG were conceived, with old fixed palette PC machines in mind. ---------------- When the number of colour planes is 16 (High Colour, by Atari misleadingly called true colour) or 24 (True Colour) any extra header data can be ignored. There should be no palette. Data will be stored in planes as usual, with each plane containing one RGB bit (of each pixel) in the order: RRRRR GGGGGG BBBBB (5R,6G,5B) for High Colour and RRRRRRRR GGGGGGGG BBBBBBBB (8R,8G,8B) for True Colour. Neither are any palette data needed with a 1 plane - i.e. monochrome - picture. In this case all ones are automatically assumed to be black and zeros white. Note that plane numbers 9-15 aren't really defined (in the Atari Compendium anyway). But should an IMG with 9, 12 or 15 planes and no XIMG palette appear, I think it would be natural to consider it a direct colour RGB picture analogue to the High and True Colour ones. A Spectrum 512 picture could thus be translated into an IMG with 9 or 12 RGB planes, depending on whether the extended 4096 colour range of the STE is used or not. ---------------- IMG picture data are stored a line at a time and, within each line, a plane at a time. Each line (and plane) is padded to the next byte boundary, and is stored as a series of commands (none of which can span over a line/plane boundary): BYTE 1 BYTE 2 BYTE 3 BYTE 4 00 00 $FF # Precedes line to repeat # times (Thus occurs only at line starts). 00 # pattern pattern Pattern to repeat # times (Bytes per pattern given in file header). $80 # Precedes # bytes to use as they are. 00+# # zero-bytes to write (# = 1-127). $80+# # $FF-bytes to write (# = 1-127). (All numbers unsigned; $ stands for hexadecimal) The order of XIMG palette colours --------------------------------- As everybody knows, the ST built in VDI has a weird colour numbering system of its own. (I don't know why. Does anyone?) Which raises the question of the colour order. .--------------------------------------------------------------. | NOTE: Although the XIMG palette is made up from VDI colours, | | these have to appear in the 'hardware' order!!! | '--------------------------------------------------------------' This is NOT mentioned in the Atari Compendium, but if the XIMG format is to be at all 'device independent' the colour index numbers of the palette have got to correspond to the bit plane data of the image. And since VDI image transformation with VR_TRNFM, contrary to what is stated in the Compendium, DOES NOT translate colour numbers, it must be reasonable to expect a program to do this translation itself, if it is to use VS_COLOR to set colours. Note that you cannot use a fixed translation table if you want your program to work with graphic cards for instance. (I know there are cards with VDI colours = hardware colours.) The 100% compatible way of handling colours, is to let your program create an 'on the fly' translation table from hardware to VDI colours. For this the VDI function V_GET_PIXEL can be used, since - as given in the Compendium - it returns two values the first of which is the 'hardware' (or as I would like to call it 'bit plane related') colour number, and the second is the VDI colour number for the examined pixel. Assuming both marker type and write mode are = 1 (the normal value), the routine to do this would look like: 1) Set VDI colour number for markers with VSM_COLOR 2) Write pixel (0,0) with V_PMARKER 3) Read pixel (0,0) with V_GET_PIXEL and use the first return value (the bitplane related colour) as an index in the table to enter the VDI colour. 4) Go back to 1) and do next VDI colour. (Of course, you should restore the original colour of pixel (0,0) afterwards). Again, the conclusion that the 'bitplane' - rather than VDI - colour order must always be used in any palette, whether GEM or not, is my own. If anyone knows differently, I would be interested to hear it (in which case XIMG would be unusable as I see it). Using XIMG without GEM ---------------------- The (less compatible) alternative is to not use VS_COLOR at all, but to set the palette with some XBIOS function. This is what you have to do anyway if you don't use GEM. You can then use the colours in the order given, but need to translate between hardware and GEM colour intensities. For which I think the following formulas will serve well: VDI -> hardware: (Max hard intensity + 1) * VDI intensity Hard intensity = ---------------------------------------- truncated 1001 Hardware -> VDI: 1000 * hardware intensity VDI intensity = -------------------------- truncated max hardware intensity E.g. for STFM, STE/TT, Falcon and 24-bit ("TRUE") respectively: STFM hard = VDI*8/1001 VDI = STFM*1000/7 STE hard = VDI*16/1001 VDI = STE*1000/15 Falcon = VDI*64/1001 VDI = Falcon*1000/63 TRUE = VDI*256/1001 VDI = TRUE*1000/255 The second formula (hard->VDI) corresponds to how it's done by the VDI (of TOS 1.4 on an STF). The first one doesn't, but it's simpler and works just as well with the second one (translations hardware -> VDI and back will return to same values). In addition it even happens to be more logical, I think, since it maps approximately equal numbers of possible VDI levels onto each of the hardware ones. (The VDI uses a 'closest match' method that approaches the extreme values - 0 and max - only half as often as other values.) The general IFF (Interchange File Format) ----------------------------------------- Every IFF file consists of a single "FORM chunk" which contains a type ID and a number of subordinate chunks. Most often these will only be so called "local" chunks with the hard data, but a general FORM can also contain subordinate FORMs (or other structural chunks) with their own subordinates. (For instance a slideshow FORM could be made up of many picture FORMs and possibly sound FORMs.) Note that wherever a chunk ends on an odd address, a zero fillbyte is added, to make sure that every chunk starts on an even address. The IFF picture --------------- For pictures, the FORM type is "ILBM" and the subordinate chunks may be as few as three: "BMHD" with general information, "CMAP" with the palette and "BODY" with the picture data. 4 bytes "FORM" - identifies the parent FORM chunk. (A file that doesn't start with these four letter isn't an IFF file) 1 long length of chunk (= of file minus 8 byte chunk header) 4 bytes "ILBM" = the FORM type (InterLeaved BitMap i.e. a picture) (Not all IFF files are pictures; those of type "8SVX" are samples, type "FTXT" means formatted text etc.) 4 bytes "BMHD" - BitMap HeaDer chunk 1 long length of chunk [20] 20 bytes 1 word image width in pixels 1 word image height in pixel lines 1 word x-offset on screen (for a sprite or brush) 1 word y-offset on screen - " - 1 byte number of colour planes 1 byte mask (0=no, 1=yes, 2=transparent colour, 3=lasso) Interesting only for sprite/brush, except that if mask=1 you will have to take into account (skip) an extra "plane" of mask data when reading the image. 1 byte compression type (0, 1 or 2 - See below) 1 byte unused [0] 1 word transparent colour number (for mask=2) 1 byte x-aspect (usually 10 for ST low; 5 for med) 1 byte y-aspect (usually 11 for ST low or med) x- and y-aspects together form a ratio describing the "pixel shape" of original screen. (1/1 for perfect squares) 1 word "page" (=original screen) width [320 for ST low] 1 word page height [200 for ST low] (Note that page dimensions are totally unrelated to actual picture size! They just give the size of the screen where the picture was created, for anyone interested) 4 bytes "CMAP" - ColourMAP chunk 1 long length of chunk [3*n where n is the number of colours] 3n bytes the palette - 3 bytes per colour: red, green, blue 8 bit colour components (0-255). Note that when a computer uses less than 8 bit colour resolution (the STFM uses 3, STE/TT 4, Falcon 6) these will always be the "leftmost" (most significant) bits of each byte. (To use CMAP colours on an STFM shift right 5 bits, i.e. divide by 32.) 4 bytes "BODY" - BODY chunk 1 long length of chunk ? bytes image data (at last!) This 'completes the picture'. Unfortunately I don't know how to store a 16-bit High Colour or 24-bit True Colour image as an IFF ILBM picture. It may be as simple as to just state 16 or 24 for number of planes, leave out a palette and then store data as usual as RGB bit planes, analogically to how it is done in IMG files (see above). But I don't know this. Maybe someone else does? The compression of BODY data ---------------------------- COMPRESSION TYPE 0 AND 1: (uncompressed and Byterun) have the data stored in order: line 0 { in order plane 0, plane 1 ... (mask) } line 1 { ditto } ... NOTE: Not even when uncompressed are the colour planes stored word interleaved as in ST screen memory. Compression 1 is the same as in DEGAS.PC?, i.e. data are stored as a series of commands each lead by a signed control byte: x >= 0: use next x+1 bytes as they are (1-128). x < 0 repeat the one next byte |x|+1 times (2-128). x = -128 not used. No command may extend over more than one plane and line. COMPRESSION TYPE 2: - as I found it to work in (the demo version of) Deluxe Paint - is a vertical word compression algorithm, very similar to the one used in the TINY format, where each plane is done word column by word column, rather than line by line, and is completed for the full picture before starting with the next plane. Decompressed word sequences CAN extend over more than one column (but not more than one plane). The data of the BODY chunk are split into as many "VDAT" sub-chunks as there are planes (+ plus an ending one for any mask) - beginning right after the 8 byte BODY chunk header. Each VDAT sub-chunk has the following format: 4 bytes "VDAT" (Presumably stands for "Vertical DATa") 1 long length of chunk 1 word offset to word data (Add to the address of this word). ? bytes control bytes ? words data words And the control byte meanings are: x<0 Use next |x| data words as they are (1-128). x=0 Next data word is the number of following words to use as they are (1-65536, ie any 0 interpreted as $10000 =65536). x=1 Next data word is the number of times to repeat the following data word (1-65536). x>1 Repeat next data word x times (2-127). The (colour plane of the) picture is built up in word (i.e. 16 pixel wide) columns, top to bottom and left to right. Note that the de- compression routine has to keep track of when to change columns (using the image height got from the BMHD header), and that this can occur in the middle of a command. (In fact a complete blank screen can be compressed into one single command - per plane that is - consisting of only one control byte = 1, one count word and one data word to fill with.) Extensions to the ILBM format ----------------------------- In addition to the basic chunks already mentioned, you may in an ILBM picture come across a number of extra chunks, needed only for special purposes. When reading an IFF file, you should make it a habit always to look for the IDs of the chunks you need, and never to assume that a chunk will start at a certain position in the file. (The order in which the basic chunks of the ILBM FORM appear is in fact standard, but special extra chunks can be inserted in any order between the CMAP and BODY chunks.) When reading the chunk formats below, remember that EVERY chunk is preceded by an eight byte header - first the four-byte ID, and then always - as a longword - the length of the chunk (Use this, rounded up to next even address, to find the next chunk). Colour cycling (more than one chunk can be used in one picture): ---------------------------------------------------------------- "CRNG" (eight bytes) W: reserved [0]. W: rate. A rate of $1000 (= 4096 in decimal) means 15 colour moves per second (or 4 screen VBLs per move if the screen frequency is 60 Hz). $2000 means 30 moves/sec (2 VBLs/move) etc. The move is 'from left to right' (lower colour numbers to higher), except if the rate is negative which should reverse the direction, although (the demo version of) Deluxe Paint doesn't seem to recognize this. W: activity flag (0=OFF, 1=ON). B: lower colour limit. B: upper colour limit Information for sprites/brushes: -------------------------------- "DEST" (8 bytes) used with sprites of fewer colour planes than the screen for which they were designed. B: Number of planes of the screen for which sprite was designed. B: unused. W: Bitmap: screen planes to be used by this sprite. W: Bitmap: (unused) planes to be set (the others are cleared). W: Bitmap: planes to be at all touched (either used or set/clear). For each of the bitmap words, each bit corresponds to the plane of the same number (bit 0 to plane 0 etc.), and each set bit marks a plane to be used, set or touched respectively. "GRAB" (4 bytes) W: x position on screen for the upper left corner of sprite/brush. W: y position. "SPRT" (2 bytes) W: Precedence (0-65535) of this sprite compared to others. Amiga hardware information: --------------------------- "CAMG" (4 bytes) L: viewport mode (bit 11=HAM, bit 3=interlaced) Other chunks could also be found. For instance in the IFF images that come with the game CIVILISATION (file name extension = .LBM, and there are 32 colour Amiga piccies, although only the first 4 planes are used by ST Civilization) I found a DPPS chunk, the meaning of which is unknown to me. But again, this is not a big problem, since any chunk that isn't needed can be skipped. All the really crucial data should be contained in the three basic chunks anyway - BMHD, CMAP and BODY. More on the general IFF: Structuring chunks -------------------------------------------- Let's finally leave the pure picture file and look at the general IFF system. In a general IFF file there can occur chunks of the types: FORM, CAT, LIST, PROP and local chunks. The purpose of the first four types is only to give structure to the IFF file, similar to what folders do to a file structure. Every structuring chunk has a type ID (e.g. "ILBM" for pictures) which is found as the first longword after the 8 byte chunk header. Local chunks don't have type IDs. Data are always placed in local chunks, never directly in a structure chunk. FORM has already been explained I think. It is the basic structuring unit. Any IFF file always constitutes one single FORM, which can represent one single image, sound, text or other data block, but can also represent a collection of images, sounds etc. in which case the individual images etc. will be contained in subordinate FORMs. Every FORM has a type ID which should be identical only for FORMs with identical types of data. E.g. any FORM that represents a single bitmapped image should have type ID "ILBM", but for a FORM that represents a collection of images a separate type ID has to be invented. CAT ("CAT " - note the ending space character) is just a way to group together a number of FORMs. A CAT can contain FORMs and groups of FORMs only (i.e. FORMs, CATs and LISTs). For instance the pictures of a slide-show can be put together in a CAT chunk while a title picture is left outside of this, to mark that it doesn't belong to the others. The type ID of CATs (and LISTs) should be the same as of the FORMs they are containing if all FORMs are of the same type (e.g. "ILBM" for a CAT with only pictures). For a CAT/LIST with mixed FORMs (e.g. both picture and sound) a special type ID will have to be invented. LIST = CAT, with only one difference: it can contain PROP chunks. PROP in its turn contains local chunks that are common PROPerty of all the FORMs of the LIST. This means that if a certain local chunk isn't found in a FORM (e.g. a palette chunk in a picture), then it is to be looked for in the common PROP of the LIST. All PROP chunks of a LIST have to be placed before any other chunks (the FORMs and groups of FORMs), right after the type ID of the LIST. The type ID of a PROP chunk should be the same as of the FORMs, with which it is intended to be used. Local chunks are all the other chunks, - those which contain the hard data. These can occur only inside FORM or PROP chunks, and have no type ID. (The VDAT sub-chunks, within the local BODY chunk of DELUXE PAINT compression 2 pictures (see above), are not called for by the IFF standard. They must just have been considered to be a neat way to split the plane data.) So a sprite collection file could for instance look like this: 4 bytes "FORM" (Every IFF file starts like this, remember?) 1 long length of this parent chunk (file length minus 8) 4 bytes "xxxx" - form type. (How about "BLST" for Bitmap LiST?) 4 bytes "LIST" 1 long length of LIST chunk 4 bytes "ILBM" LIST type (This list contains only pictures) 4 bytes "PROP" 1 long length of PROP chunk (= 60 in this example) 4 bytes "ILBM" PROP type (PROP is to be used with picture forms) 4 bytes "CMAP" 1 long length of CMAP (=48 for a 16 colour palette) 48 bytes (for a 16 colour palette) palette data Of course the PROP could also have included a common BMHD chunk and possibly other chunks as well. And then the ILBM FORMs of the list follows. These can in their turn be grouped in sub-lists - e.g. one list with 16*16 sprites, one with 32*32 sprites, special lists with 3 plane sprites etc. (A$)z:::, :A$::. ::::OP4 MX:MXPd:FREDF$(MX),NAME$(FRED,MXP),$NAME(FRED),LINE$(FRED,MXP),$LINE(FRED,MXP),DSC$(FRED):IFRED:DSC$(I):IT ICTARI PROGRAMMERS USER GROUP ============================= INDEX FOR MAGAZINE ISSUES 1-15 ============================== Symbol keys used (S)=Source code (Assembler, C, Basic, etc) (P)=Program file only. (D)=Document text files. ------------------------- ICTARI ISSUE 1 --------------------------- ASSEMBLY Executable file cruncher/decruncher code (S) Mandelbrot & Julia set picture generator, low rez (S) Caps lock disable routine (S) Virus killer program and info on 19 viruses (S)(D) Bootsector Companion program and bootsector code (S) Code to erase boot sector programs (S) C Routine to set time on boot up (S) Code to fast erase floppy disk data (S) istBinary file to assembler source code converter (S) Program to show ST type (P)(S) Code to get/set system clock (S) STOS Games cheat program code (S) MISC Falcon review (D) Falcon specifications (D) Program to force memory size to 0.5Mb (P) Personal accounts program (P) ------------------------- ICTARI ISSUE 2 --------------------------- ASSEMBLY Document displayer code, medium rez (S) ICTARI display text code (S) Menu display program (S) Code to remove lower screen border (S) Code to play digitized sampled sound on STE only (S)(P) C ICTARI Database program (S)(P) Graphics information, VDI calls demo (S) Code to read and display keyboard data (S) GFA Font designer program (S)(P) STOS Ball-in-ball model demo code (S) Cyberworm game (S) Filter scroller (S) Pontoon game (S)(P) Raster routine code (S) Unlimited sprites demo code (S) MISC File move program (P) ST Zip program (P) Bug reports (D) Midi information and sample (D) ------------------------- ICTARI ISSUE 3 --------------------------- ASSEMBLY Code to display disk information (S) Tips for speeding up machine code programs (D) Code to remove top and bottom screen borders (S) Loading and saving files using GEMDOS (S) Code to load and display SPECTRUM pictures (S) Accessing disk sectors including boot sectors (S) Code to display TOS, sysbase, STE, memsize, etc (S) C Disk back up utility (S) Tim Oren GEM Tutorial Parts 1 & 2 Windows (D)(S) Screen re-draw code (S) GFA Multi-tasking clock accessory (S) Routines to load and display DEGAS pictures (S) Beginners GFA Tutorial (D)(S) Make menu utility program for GFA programmers (P) BASIC Mandelbrot & Julia display program (S) STOS Circle drawing demo (S) Code for calendar to name any day (S) Code to display droid sprite in dungeon (S) Program to rescue data from corrupted disk (S) MISC Chameleon program (P) Demos information (D) GEMBENCH program (P) GEMDOS manual for assembler and C (D) File unpacker program (P) Program to trace TOS system calls (P) Virus protection program (P) Guide to Railroad Tycoon game (D) ------------------------- ICTARI ISSUE 4 --------------------------- ASSEMBLY Mega funky screen flipper (S) Document display program (S) HBL colour change code (S) Assembler Tutorial Part 1 Scope and concepts (D)(S) Assembler Tutorial Part 2 Editors, Assembler, Debugger C A simple form handler routine (S)(P) Tim Oren GEM Tutorial Part 3 Dialog handler (S)(D) Tim Oren GEM Tutorial Part 4 GEM Resource structures (D) Sound samples replay code for STE (S) STOS 29 new STOS Basic extensions (S)(D) MISC Sampled sounds formats V2.10 by Guido van Rossum (D) VT52 Control codes list (D) Program to convert DEGAS pictures to DEGAS ELITE (P) Stereo hardware modifications for STFMs (D) Picture formats information by David Baggett (D) Sound sample play program before a warm reset (P) Program to change mouse cursor to hourglass shape (P) Program to play MOD files on machines with PCM chip (P) ------------------------- ICTARI ISSUE 5 --------------------------- ASSEMBLY Code to remove upper and lower screen borders (S) Document display program (S) Reset picture demo, displays picture after reset (S) Starburst routine (S) TOS version detection code (S) Assembler Tutorial Part 3 Addressing modes (D)(S) Assembler Tutorial Part 4 Initialization Algorithms (D) Simple raster routine (S) Executable file structure & relocation table (S) C Tim Oren GEM Tutorial Part 5 Resource Trees (D)(S) Tim Oren GEM Tutorial Part 6 Raster operations (D)(S) Linking machine code into C programs and vice versa (S) GFA Image display routines (S) Scroller routine (S) Screen meltdown routine (S) Sprite routines (S) STOS Screen border removal code (S) Sine wave programs (S) Code to use more than 15 sprites (S) MISC MIDI data file Disk formats information (D) Pexec TOS call information (D) Disk/memory display/edit program (P) Icon editor program (P) ------------------------- ICTARI ISSUE 6 --------------------------- ASSEMBLY Assembler Tutorial Part 5 Program timings (D)(S) Assembler Tutorial Part 6 Instruction timings (D)(S) Routine to locate box image on screen (S) GEM shell code (S) Code to use interrupt routines (S) Random number generator and scaling routines (S) Hi-resolution scroll routine (S) Square root calculation routine (S) Notes on sub-routine library files (D) C Tim Oren GEM Tutorial Part 7 Menu structures (D)(S) Tim Oren GEM Tutorial Part 8 User interfaces (D)(S) GFA 19 miscellaneous procedures (S) 22 misc files including sprites, rasters, starfields (S) STOS Screen demos (S) PASCAL Disk address book program (S) MISC Handwriting recognition program (P) Stereo sampled sound sequencer system STE (P) Playing sampled sounds in Basic using DMA, STE only (S) ------------------------- ICTARI ISSUE 7 --------------------------- ASSEMBLY Assembler Tutorial Part 7 Adaptive algorithms (D)(S) Assembler Tutorial Part 8 Seizing system control (D)(S) Routine to position cursor at set co-ordinates (S) Sprite routines using Neochrome Master (S)(P)(D) Tracker module player code (S) C Tim Oren GEM Tutorial Part 9 Lines & Solids (D)(S) Tim Oren GEM Tutorial Part 10 Text output (D)(S) GFA Overscan utility (S) Pixels program (P) STOS ICTARI accounts program (S)(P) MISC Text printing program for GDOS fonts (P) Astra DTMT program (Update from issue 4) (P) Handwriting recognition program (P) Bitplane information (D) ------------------------- ICTARI ISSUE 8 --------------------------- ASSEMBLY Programming techniques. Passing in-line data to S/R (D) Notes on program layout (D) 68K instruction set display accessory (S) Mouse problem request (S) C GEM tutorial Chapters 0-7 (D) 82 source code routines for use with GEM tutorial (S) GFA Graphics mapping bitmaps (D)(S) PASCAL Directory lister (S)(P) MISC Images (4D cube and DSP56001 circuit) 18 Atari books briefly reviewed (D) ------------------------- ICTARI ISSUE 9 --------------------------- ASSEMBLY Cookie Jar find program and documentation (D) Cookie Jar find source code (S) Assembler MACRO tutorial (D) Two MACRO files for system TOS calls (S) Twist scroll demo program and source (needs fixing) (S) Mouse problem answered (D) C C Tutorial Chapters 8-14, (see last month also) (D) Program to delete .BAK files and source code (S)(P) GFA Handy tips for GFA programmers (D) Horizontal scroll routine (S) PASCAL Address book program and source code (S)(P) MISC Atari Explorer Online Programmers Journal Issue 1 (D) 'The Atari Compendium' book review (D) Pexec TOS call information (D) Executable file structure information (D) Index for ICTARI disk magazines issues 1-8 (D) List of current members (D) ------------------------- ICTARI ISSUE 10 --------------------------- ASSEMBLY Complete set of floating point arithmetic routines (S) Routine to read command line text string (S) Event_multi 'right button' problem solved at last (D) Twist scroll program, updated version (S) C Boink, a Break-out type game with source code (S) Event_multi 'right button' problem solved at last (D) GFA Code to read command line on TTP programs (S) GEM Window handling routines (S) Colour scroller routines (S) Corrections to bit maps article (see issue 8) (D) PASCAL Pipe monitor. Displays AES messages (S) STOS Moving block puzzle game (S) MISC Atari Explorer Online Programmers Journal Issue 2 (D) Various GEM bugs discussed (D) Program to show active GEM/TOS/BIOS/AES/VDI calls (P) The LZW and GIF compression algorithms explained (D) List of current members (D) Index for issues 1-9 (D) ------------------------- ICTARI ISSUE 11 --------------------------- ASSEMBLY Binary to decimal conversion routine (S) Decimal to binary conversion routine (S) Code to play chip music in interrupts (S) Code to set up time and date when booting up (S) Routine to convert number to a binary string (S) Routine to convert number to hex string (S) Routine to enter hex number from keyboard (S) C GEM Tutorial by J White. Part 1 Introduction (D) Using floating dialogue boxes in Lattice C (S) Porting IBM PC RSC/Doodle to Atari GEM (D) GFA Code to generate circles, spheres, etc (S)(D) Picture image cutter and saver program for GFA (P) PASCAL Code to show boolean expressions as a Karnaugh map (S) STOS Code for number guessing game that talks to you (S) MISC Blitter chip manual (D) Click anywhere title box using Resource File editor (D) List of current members (D) Index for issues 1-10 (D) ------------------------- ICTARI ISSUE 12 --------------------------- ASSEMBLY Load file routines (S) Colour palette fading routines for STFM & STE (S) User defined mouse shapes for GEM programs (S) Sprite drawing routines (S) Sprite tutorial Part 1. Beating the 16 pixel boundary (D) Updates for Cookie find & Mouse routines (S) C Fractal drawing code (S) GEM Tutorial by J White. Part 2. Initialising GEM (D) GFA GFA Manual Part 1 of 3 (D) GFA Alert box designer program (P) STOS Bootview program code (S) Pipe perfect game (S) MISC Analyzer program to display info on system crashes (P) Atari Questions and Answers file No 1 (D) Warning for DevPac 3 and Lattice C users (D) Current membership list (D) Index for issues 1-11 (D) ------------------------- ICTARI ISSUE 13 --------------------------- ASSEMBLY Setting up system timers (S) Vertical Blanking List (VBL) notes (D) Colour cycling on raster lines (S) Nick Bates. Sprite tutorial Part 2. (S)(D) Keyboard equates file (S) Compare routine (S) Disk formatting code (S)(P) C C Graphics extensions (S) Various C functions for use with GEM dialogs (S) GEM Tutorial by J White. Part 3. (D) GFA Anagram generating code and recursion techniques (D)(S) Sound sample play routines (S) STOS STOS extra extensions (S) MISC Atari Questions and Answers file No 2 (D) Discussion on drawing tangents on curves (D) Current membership list (D) ------------------------- ICTARI ISSUE 14 --------------------------- ASSEMBLY STFM Overscan techniques (S) Nick Bates, Sprite tutorial Part 3 (S) Code to calculate position of bouncing sprite (S) Sign Integer Fraction maths routines (S) C GEM Tutorial by J White. Part 4 (D) Othello game source code (S) League table solution (D) GFA GFA Manual Part 2 (S) Dialog box designer program (P) STOS STOS accessory to display text files (S) Rebound football game (S) Plazma screen display code (S) MISC Atari Questions and Answers file No 3 (D) STE hardware information (D) Current membership list (D) Index to issues 1-13 (D) ------------------------- ICTARI ISSUE 15 --------------------------- ASSEMBLY DMA sampled sound play routines (S). PSG sampled sound play routine and editor program (S)(P). Sound sample conversion routines (S). C Falcon DSP sound playing routines (S). GEM Tutorial by J White. Part 5 (D). GFA GFA Manual Part 3 (D). DMA sound playing routines (S). STOS Disco display and sound play program with notes (S). Dot matrix count up timer routine (S). MISC Algorithm Corner. Date to day conversion algorithm (D). Atari Questions and Answers file No 4 (D). Information on sampled sounds and PSG chip (D). PSG Musical note/register code listing (D). Current membership list (D). Copies of any of the above issues are available for 1 each from :- ICTARI User Group, 63 Woolsbridge Road, Ringwood, Hampshire, BH24 2LX. -------------- END ------------- . Gn[.. nlPCX_INFOTXT Q]X\1 Technical Reference Manual for PCX picture format ================================================= ZSoft Corporation 450 Franklin Rd. Suite 100 Marietta, GA 30067 (404) 428-0008 Copyright 1988 ZSoft Corporation Table of Contents Introduction Image File (.PCX) Format Decoding the .PCX File Format Palette Information Description PC Paintbrush Bitmap Font Format Sample "C" Routines Introduction This booklet was designed to aid developers and users in understanding the technical aspects of the .PCX file format and the use of FRIEZE. Any comments, questions or suggestions should be sent to:- ZSoft Corporation Technical Support Department ATTN: Technical Reference Manual 450 Franklin Rd. Suite 100 Marietta, GA 30067 IMAGE FILE (.PCX) FORMAT The information in this section will be useful if you want to write a program to read or write PCX files (images). If you want to write a special case program for one particular image format you should be able to produce something that runs twice as fast as "Load from..." in PC Paintbrush. Image files used by PC Paintbrush product family and FRIEZE (those with a .PCX extension) begin with a 128 byte header. Usually you can ignore this header, since your images will all have the same resolution. If you want to process different resolutions or colors, you will need to interpret the header correctly. The remainder of the image file consists of encoded graphic data. The encoding method is a simple byte oriented run-length technique. We reserve the right to change this method to improve space efficiency. When more than one color plane is stored in the file, each line of the image is stored by color plane (generally ordered red, green, blue, intensity), As shown below. Scan line 0: RRR... GGG... BBB... III... Scan line 1: RRR... GGG... BBB... III... (etc.) The encoding method is: FOR each byte, X, read from the file IF the top two bits of X are 1's then count = 6 lowest bits of X data = next byte following X ELSE count = 1 data = X Since the overhead this technique requires is, on average, 25% of the non- repeating data and is at least offset whenever bytes are repeated, the file storage savings are usually considerable. The format of the file header is shown below. ZSoft .PCX FILE HEADER FORMAT Byte Item Size Description/Comments 0 Manufacturer 1 Constant Flag 10 = ZSoft .PCX 1 Version 1 Version information: 0 = Version 2.5 2 = Version 2.8 w/palette information 3 = Version 2.8 w/o palette information 5 = Version 3.0 2 Encoding 1 1 = .PCX run length encoding 3 Bits per pixel 1 Number of bits/pixel per plane 4 Window 8 Picture Dimensions (Xmin, Ymin) - (Xmax - Ymax) in pixels, inclusive 12 HRes 2 Horizontal Resolution of creating device 14 VRes 2 Vertical Resolution of creating device 16 Colormap 48 Color palette setting, see text 64 Reserved 1 65 NPlanes 1 Number of color planes 66 Bytes per Line 2 Number of bytes per scan line per color plane (always even for .PCX files) 68 Palette Info 2 How to interpret palette - 1 = color/BW, 2 = grayscale 70 Filler 58 blank to fill out 128 byte header NOTES: All sizes are measured in BYTES. All variables of size 2 are integers. Decoding .PCX Files First, find the pixel dimensions of the image by calculating [XSIZE = Xmax - Xmin + 1] and [YSIZE = Ymax - Ymin + 1]. Then calculate how many bytes are required to hold one complete uncompressed scan line: TotalBytes = NPlanes * BytesPerLine Note that since there are always an integral number of bytes, there will probably be unused data at the end of each scan line. TotalBytes shows how much storage must be available to decode each scan line, including any blank area on the right side of the image. You can now begin decoding the first scan line - read the first byte of data from the file. If the top two bits are set, the remaining six bits in the byte show how many times to duplicate the next byte in the file. If the top two bits are not set, the first byte is the data itself, with a count of one. Continue decoding the rest of the line. Keep a running subtotal of how many bytes are moved and duplicated into the output buffer. When the subtotal equals TotalBytes, the scan line is complete. There will always be a decoding break at the end of each scan line. But there will not be a decoding break at the end of each plane within each scan line. When the scan line is completed, there may be extra blank data at the end of each plane within the scan line. Use the XSIZE and YSIZE values to find where the valid image data is. If the data is multi-plane BytesPerLine shows where each plane ends within the scan line. Continue decoding the remainder of the scan lines. There may be extra scan lines at the bottom of the image, to round to 8 or 16 scan lines. Palette Information Description EGA/VGA 16 Color Palette Information The palette information is stored in one of two different formats. In standard RGB format (IBM EGA, IBM VGA) the data is stored as 16 triples. Each triple is a 3 byte quantity of Red, Green, Blue values. The values can range from 0-255 so some interpretation into the base card format is necessary. On an IBM EGA, for example, there are 4 possible levels of RGB for each color. Since 256/4 = 64, the following is a list of the settings and levels: Setting Level 0-63 0 64-127 1 128-192 2 193-254 3 VGA 256 Color Palette Information ZSoft has recently added the capability to store palettes containing more than 16 colors in the .PCX image file. The 256 color palette is formatted and treated the same as the 16 color palette, except that it is substantially longer. The palette (number of colors x 3 bytes in length) is appended to the end of the .PCX file, and is preceded by a 12 decimal. To determine the VGA BIOS palette you need only divide the values read in the palette by 4. To access a 256 color palette: First, check the version number in the header, if it contains a 5 there is a palette. Second, read to the end of the file and count back 769 bytes. The value you find should be a 12 decimal, showing the presence of a 256 color palette. CGA Color Palette Information For a standard IBM CGA board, the palette settings are a bit more complex. Only the first byte of the triple is used. The first triple has a valid first byte which represents the background color. To find the background, take the (unsigned) byte value and divide by 16. This will give a result between 0-15, hence the background color. The second triple has a valid first byte, which represents the foreground palette. PC Paintbrush supports 8 possible CGA palettes, so when the foreground setting is encoded between 0 and 255, there are 8 ranges of numbers and the divisor is 32. CGA Color Map Header Byte #16 Background color is determined in the upper four bits. Header Byte #19 Only upper 3 bits are used, lower 5 bits are ignored. The first three bits that are used are ordered C, P, I. These bits are interpreted as follows: c: color burst enable - 0 = color; 1 = monochrome p: palette - 0 = yellow; 1 = white i: intensity - 0 = dim; 1 = bright PC Paintbrush Bitmap Character Format The bitmap character fonts are stored in a particularly simple format. The format of these characters is as follows: Header (2 bytes) font width db 0a0h + character width (in dots) font height db character height (in dots) Character Widths (256 bytes) char widths db 256 dup(each char's width +1) Character Images (remainder of the file) The characters are stored in ASCII order and as many as 256 may be provided. Each character is left justified in the character block, all characters take up the same number of bytes. Bytes are organized as N strings, where each string is one scan line of the character. See figure 2. For example, each character in a 5x7 font requires 7 bytes. A 9x14 font uses 28 bytes per character (stored two bytes per scan line in 14 sets of 2 byte packets). Custom fonts may be any size up to the current maximum of 10K bytes allowed for a font file. Sample "C" Routines The following is a simple set of C subroutines to read data from a .PCX file. /* This procedure reads one encoded block from the image file and stores a count and data byte. Result: 0 = valid data stored EOF = out of data in file */ encget(pbyt, pcnt, fid) int *pbyt; /* where to place data */ int *pcnt; /* where to place count */ FILE *fid; /* image file handle */ { int i; *pcnt = 1; /* safety play */ if(EOF == (i = getc(fid))) return(EOF); if(0xc0 == (0xc0 & i)) { *pcnt = 0x3f&i; if(EOF == (i=getc(fid))) return(EOF); } *pbyt = i; return(0); } /* Here's a program fragment using encget. This reads an entire file and stores it in a (large) buffer, pointed to by the variable "bufr". "fp" is the file pointer for the image */ while (EOF != encget(&chr, &cnt, fp)) for (i = 0; i *bufr++ = chr; The following is a set of C subroutines to write data to a .PCX file. /* This subroutine encodes one scanline and writes it to a file */ encLine(inBuff, inLen, fp) unsigned char *inBuff; /* pointer to scanline data */ int inLen; /* length of raw scanline in bytes */ FILE *fp; /* file to be written to */ { /* returns number of bytes written into outBuff, 0 if failed */ unsigned char this, last; int srcIndex, i; register int total; register unsigned char runCount; /* max single runlength is 63 */ total = 0; last = *(inBuff); runCount = 1; for (srcIndex = 1; srcIndex inLen; srcIndex++) { this = *(++inBuff); if (this == last) { runCount++; /* it encodes */ if (runCount == 63) { if (!(i=encput(last, runCount, fp))) return(0); total += i; runCount = 0; } } else { /* this != last */ if (runCount) { if (!(i=encput(last, runCount, fp))) return(0); total += i; } last = this; runCount = 1; } } /* endloop */ if (runCount) { /* finish up */ if (!(i=encput(last, runCount, fp))) return(0); return(total + i); } return(total); } /* subroutine for writing an encoded byte pair (or single byte if it doesn't encode) to a file */ encput(byt, cnt, fid) /* returns count of bytes written, 0 if err */ unsigned char byt, cnt; FILE *fid; { if(cnt) { if( (cnt==1) && (0xc0 != (0xc0&byt)) ) { if(EOF == putc((int)byt, fid)) return(0); /* disk write error (probably full) */ return(1); } else { if(EOF == putc((int)0xC0 | cnt, fid)) return(0); /* disk write error */ if(EOF == putc((int)byt, fid)) return(0); /* disk write error */ return(2); } } return(0); } ------- End of file -------- x<________((((8((  ________ ICTARI PROGRAMMERS GROUP ======================== CURRENT LIST OF MEMBERS November 1994 Martyn Armitage 101 South Terrace, Wales Bar, Sheffield S31 8QL Tel. 0909 773564 Keith Baines 8 Lumley Court, Denmark Avenue, London SW19 4HQ Ian & Mark Baker 256 Lower Road, Great Bookham, Surrey KT23 4DL Tel. 0372 454875 Nick Bates 17 Elliott Close, Forches estate, Barnstaple N Devon, EX32 8EW Tel. 0271 24633 Mark Blackwood 64 Chantry Road, Moseley, Birmingham, B73 8DJ Te. 027 4490936 Paul Brookes 32 Dudsbury Road, West Parley, Ferndown Dorset, BH22 8RE Jamie-Lee Burrows 220 Broadlands Drive, Lawrence Weston Bristol, Avon, BS11 0PN Tel. 0272 384771 Albert Cartwright 258 Yardley Fields Rd, Stechford, Birmingham B33 8RL Tel. 021 6283710 James Collett Park View Farm, Carlton, Nr Nuneaton, Warks CV13 0DA Internet E-Mail- S6005/46@brookes.ac.uk Martin Cubitt 14 Deepdene Ave, Rayleigh, Essex SS6 9LG Robert Darling 11b Turketel Rd, Folkstone, Kent CT20 2PA Tel. 0303 245203, BBS No 0303 249306 Richard Davey 10 Oak Drive, Portishead, Bristol, Avon BS20 8QS Tel. 0275 843241 Rene Dencker Oesterbaeksvej 91, V10, 5230 Odense M Denmark EMail. edr.imada.ou.dk Leslie Dewhurst 8 Stirling Avenue, Leamington Spa Warwickshire, CV32 7HN Paul Ditchfield 499 Owen, Ashworth North, Maghull, Liverpool L31 1HW Tel. 051 4730303 Extn 4364 Richard Evans 3 Gervis Crescent, Parkstone, Poole, Dorset BH14 0LR Tel. 0202 744151 James Ford 4 Berceau Walk, Watford, Herts, WD1 3BL Tel. 0923 248762 Richard Gage 43 Chilton Lane, Ramsgate, Kent, CT11 0LQ Tel. 0843 851706 Vimal Goricha 29 Abbey Way, Bradville, Milton Keynes MK13 7AQ Ian Hancock 74 Frank Cowin Court, Sussex St, Salford M7 1QX Roland Henriksson Myggvagen 28, Skovde, Sweden, 54165 Tel. 46 500 438295 George Hodgson 4a Millicent Close, Hednesford, Staffs WS12 4BJ Tel. 0543 423804 Robert Holbrook 3 Windmill Close, Haylands, Ryde, IoW PO33 3JB David Holmes 22 South Road, Great Abington, Cambridge CB1 6AU Kenneth Hughes 220 Broadlands Drive, Lawrence Weston Bristol, Avon, BS11 0PN Tel. 0272 384771 Jefferson Humber 16 Kingfisher Close, Carisbrooke Park Newport, Isle of Wight, PO30 5XS Tel. 0983 526046 Steven Jordan 38 Wylam, Mill Park, Bracknell, Berks RG12 8XS Tel. 0344 427581 Peter King 5 Marlborough Rd, Dover, Kent CT17 9ND Paul Laddie 29 Elliott Street, Sacriston, Durham DH7 6JH Tel. 0913 710554 John Levon 12 St Johns Ave, Wakefield, W Yorks WF1 2RE Marten Lindstrom Savenasvagen 24, S-932 31 Skelleftehamn Sweden John Logan 9 Myrtlefield Park, Belfast, BT9 6NE Tel. 0232 660302 Adrian Lovatt Flat C, 5 Bower Street, Harrogate, N Yorks HG1 5BQ Ralph Lovesy 12 Bell Lane, Syresham, Northants, NN13 5HP Tel. 0280 850450 John McGarrity 1 Hillwood Gdns, Ratho Station, Newbridge Midlothian, EH28 8PX Tel. 031 333 3982 Iain McLeod 15 Regis Court, Barnton, Edinburgh, EH4 6RG Martin Milner 1 Portland Avenue, Burton on Trent, Staffs DE14 3GD Tel. 021 7046256 (Day) 0283 500944 (Evening) Jason Murdoch 13 Woodmoor, Finchampstead, Wokingham Berks, RG11 3TT Simon Oke 10 Symn Lane, Wotton-under-edge, Gloucs GL12 7BG Carl Pattinson 40 Silverdale Place, Newton Ayecliffe County Durham, DL5 7DZ John Phillips 20 Hawkesworth House, Cavendish Road, London SW4 8NA Kevin Preece 17 Chislet way, Grange Park, Tuffley Gloucester, GL4 0QQ Dave Price 42 Foxglove Green, Willesborough, Ashford Kent, TN24 0RJ Tel. 01233 636209 Gary Priest 167 Ludlow Rd, Itchen, Southampton SO19 2EL Christopher Reeder 101 Icknield Walk, Royston, Herts SG8 7LJ Tel. 0763 247913 Raymond Reid 10 Daviot Drive, Hilton, Inverness, IV2 4UL Alan Richardson 55 Alderbrook Road, Clapham South, London SW12 8AD Tel. 081 675 1866 Simon Rigby 116 Rosmead St, Newbridge Rd, Hull HU9 2TE Geoff Smith 6 Humber Road, Beeston, Nottingham, NG9 2EF John Stevenson 28 Gun Bank, Masham, Ripon, N Yorkshire HG4 4EW Peter Strath 75 Cavendish Road, Highams Park, London E4 9NQ James Taylor 12 West Drive, Cleadon, Sunderland, T & W SR6 7SJ Tel. 091 5362165 Christopher Teague 7 Union Street, Nantyffylon, Maesteg Mid Glamorgan, S Wales, CF34 0BG George Voulgaris Larisis 88, TK60100, Katerini, GREECE Martyn Wells 86 Cornwall Road, Kettering, Northants NN16 8PE Tel. 0536 520240 Jonathan White 20 Fryent Close, Black Rod, Bolton BL6 5BU (This is not a complete list as some members do not want to be listed) ssion definitions and algorithms. Neochrome picture show program. GEM Tutorial by J White. Part 6. IMG picture loader code. GFA CrackArt picture compression/decompression code. Spectrum (SPU) picture format information and code. Tiny picture decompression code. STOS Using 80 column text in low rez. VDU dump program. MISC Algorithm Corner. Star Wars scroll. Mnq.. VDUMP NnrFONT Rn|. Onr.. MnqVDUMP BAS 4jxs*!Lionpoulos  LLLLLLPPP (line,pos)rͦ::ަ:::,:,$:,#:::,:X$SPBQ:b:& *** Variable Dumper - M.Cubitt *** :,: A STOS utility : , :5Select Output - (S)creen, (P)rinter ,(B)oth or (Q)uit: , :Output (P,S,B or Q): ;::_A$:A$ A$(A$):OP(X$,A$):OP v:(A$)(A$)z:::, :A$::. ::::OP4 MX:MXPd:FREDF$(MX),NAME$(FRED,MXP),$NAME(FRED),LINE$(FRED,MXP),$LINE(FRED,MXP),DSC$(FRED):IFRED:DSC$(I):ITIFRED:$NAME(I):I:TVd(Q$("):C$ABCDEFGHIJKLMNOPQRSTUVWXYZ:C1$C$ 1234567890$#J- ===================================================================*2:LJ7 ===================================================================<Z::,,,,,,:::: Thinking...::,, ,,,,:::,: Done %JA -------------------------------------------------------------------2F:A$F$(Z):BJ}- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -d S=instr(A$," ") : if S=0 then 500 else N$=left$(A$,S-1) : if len(N$)<6 then N$=space$(7-S)+N$<S(A$, ):S`N$(Z) :(N$)N$((N$))N$ (N$)0A$N$ J- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BSz:B$(A$,B,):B$Q$B(A$,Q$,B):2B$r&H:R(C$,B$)Z$B$:FB,B:B(A$)B$(A$,B,):(C1$,B$)Z$Z$B$:B$(Z$Z$B$RX:(Z$,)(XFRED<E(Z$,#):E.X:X: :<E(Z$,$):E&ҵX: :ܙ J- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - B:B(A$)J- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -~BZ#Z:BL#L:CPC#BZ#BL#:CPC#CPC#d: ,:###;CPC#Z:ZLP::PR:::OP Printing...P$(*,) VARIABLE DUMP OF FL$ (*,):%:QThP$$Total number of different variables:(TV):%:QT6I:#(:QT(%:QT(#(:QT(%:QTP$O-------------------------------------------------------------------------------:%:QT(#(:QTP$()Type: DSC$(I), TOTAL:($NAME(I)):%:QT:L:$NAME(I)LA$NAME$(I,L):#(:QT(%:QTNP:P$A$::%:QT2&F:%:QT,+PS(P) V0L$(LINE$(I,L),PS, ):L$t:Y$(L$,):Q(Y$):W((L$,(L$))):Z$F$(Q)fXF񸣚]P$(Z$,W):%:QT0YPR:W(Z$)]FZP$(Z$,(A$)):%:QT[PR:v]FbP$(Z$,PP(A$),WPP(A$)):%:QT^PRBbP$(Z$,W,(A$)):%:PRPd(LINE$(I,L),Y$,PS)l4gPPW:P:F:+blP$(Z$,(Z$)W(A$)):%:QTqPR(v#(:QT<P:P$LINE(I,L)&4L:L$NAME(I),I:IFRED|::Dump another (Y/N)?;A$:A$(A$):A$Y:A$N͊Ί *** READ IN FILE ***ϊL:A$(*.asc, Load file):A$(A$,.)A$A$.ASC.բ(A$,)`֠:::,,,,,,:::: Loading...ڠ#,A$ #,LINE$(L:F$(L)LINE$() #,FL$A$.ASC:: " *** SORT OUT WHICH TYPE *** $NAME(X)TV:$NAME(X):S$NAME(X):NAME$(X,S)Z$: S::B$NAME$(X,S):S:B$Z$S$NAME(X):B$Z$TV:$NAME(X):S$NAME(X):NAME$(X,S)Z$Sb TT$(F) :(TT$)TT$((TT$))TT$ ۵$LINE(X,S):A$LINE(X,S):LINE$(X,S)LINE$(X,S)N$TT$:bINTEGER,STRING,REAL, INTEGER ARRAY, STRING ARRAY, REAL ARRAY#%#& *** PRINT ROUTINE ***#'#((,OP):#2(,OP)PPP$:&#<%PR:PP$():PR$()E:PPO$():PRO$()FPP$:PR$:PPO$:PRO$:%!(,OP)PP$;P$;PPO$;(%&(,OP)񸣚%+6%'PPP$PR$:':QT4%(PPP$P$:':QT6%)PPP$PRO$:':QT%+PR%0&%(,OP)P$<%(,OP)PPP$P$:&%%䢸<%,::Press a key for more:::w%::&&HB(A$)@&R(A$,B,)remB(A$)&\&QT&&&PPP$&ʏ&ԋj&ޠ, , ,2,,::PRINTER NOT READY::(C)ancel or (R)etry:b&SA$():SA$C:QT:SA$R&&:&'QT''B'$PPP$;'.'8j'B, , ,2,,::PRINTER NOT READY::(C)ancel or (R)etry:b'LSA$():SA$C:QT:SA$R'L'V:'complain about non-standard disk formats, primarily disks that were formatted to more than 80 tracks. Also we have had problems formatting some disks with more than 80 tracks which is why we do not use this option any more. Formatting tracks with 10 sectors is usually OK but we normally use 9 sectors unless we really need to squeeze more on a disk. As I am sure everyone knows, if you copy a full disk that is formatted with 10 sectors/track onto a disk which is formatted with 9 sectors/track using the normal desk top copying options, the TOS copies what it can to the target disk but leaves off anything it cannot copy WITHOUT informing the us. Rn|.. MnqFONT_01 BAS 4}gLionpoulosgTTa , >> The Small Font Routine Version 1.0, >> Copyright Diamond Software 1994F2 >> Show how to have more than 40 character per line on low rez2< >> The font is 4*5 and is stored as a 'bob'= Fͦ:::ަ::TP S8(): A( S8):,,@,,,ZZ A$:: POS: X: Y: DdJn ------------------------------------------------------------------Jx | Main Loop |J ------------------------------------------------------------------ n TXT$( A$, POS,): TXT( TXT$)A: TXT!: TXTTXT: > Full Stop2, S8, TXT, X, Y,R X X: POS: POS( A$)POS>Ȣ X@YY:X0ҝ DELAY D: DELAY܄ 9J ------------------------------------------------------------------J | The Text |J ------------------------------------------------------------------F A$ A$+ DIAMOND SOFTWARE PRESENTS THE SMALL FONT.." A$ A$ @, A$ A$% THIS WAS CODED BY STEVEN JORDAN OF 66 A$ A$THE GREAT DIAMOND SOFTWARE.@ A$ A$ J8H wU SB13DUw  <Xt $'+:.2V59r=@DGK8NRTUYp\x<______8(((8((  ______8(((8((  x< __ __ 8(8(8   __ __ 8(8(8  x<__ __ 8 ( (8   __ __ 8 ( (8   x< ______ 8(((8  ______ 8(((8 x<??8 0 8  ??8 0 8  x<??8 0  ??8 0  x<____8 ((8((  ____8 ((8((  x<________((8((  ________((8((  x<@@@88@@@88x<` __(8((  ` __(8((  x<____ ____((0((  ____ ____((0((  x<__ (8((  __ (8((  x<________(8(((  ________(8(((  x<________8(((((( ________8(((((( x<______8(((((8((  ______8(((((8((  x< __ 8(8   __ 8(8  x<______8(((((4(( ??@______8(((((4(( ??@x< __ ____8(8((   __ ____8(8((  x<`   80 `   80 x<@@@@8@@@@8x<________((((8 ________((((8 x<________((((8((  ________((((8((  x<________(((8(  ________(((8(  x<________((8((((  ________((8((((  x<____@@((8((  ____@@((8((  x< @@@??@@8(( 8  @@@??@@8(( 8 x<@@#a000000 #b000000 #c7770007000600070055200505552220770557075055507703111103 #d #E 18 12 #W 00 00 02 07 4B 10 08 A:\*.*@ #W 00 00 02 0B 4C 09 00 @ #W 00 00 0A 0F 34 09 00 @ #W 00 00 0E 01 34 09 00 @ #M 03 00 00 FF A ICTARI_MAG@ @ #M 00 01 00 FF B FLOPPY DISK@ @ #T 00 03 02 FF TRASH@ @ #F FF 04 @ *.*@ #D FF 01 @ *.*@ #G 03 FF *.APP@ @ #G 03 FF *.PRG@ @ #P 03 FF *.TTP@ @ #F 03 04 *.TOS@ @  ICTARI USER GROUP ISSUE 16 November 1994 ___ ______ ___ _________ _________ ___ \__\ \ __\ \ \__ \______ \ \ _____\ \__\ ___ \ \ \ __\ _____\ \ \ \ ___ \ \ \ \ \ \ \ ____ \ \ \ \ \ \ \ \ \_____ \ \____ \ \__\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \__\ \_______\ \______\ \________\ \__\ \__\ * m a g a z i n e * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= I C T A R I U S E R G R O U P 63 Woolsbridge Road, Ringwood, Hants, BH24 2LX Tel. 0425-474415 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= INDEX FOR ISSUE 16 ================== ASSEMBLY IMG mono picture decoding routine. Degas PI1-3/PC1-3 picture decoding routine. Tiny TNY/TN1-3 picture decoding routine. PC Paintbrush PCX picture decoding routine. STAD PAC picture decoding routine. Tiny low rez decoding routine. IMG/IFF/DEGAS/NEO packing/unpacking routines. C TNY picture decoding routine. LZW TIFF compression definitions and algorithms. Neochrome picture show program. GEM Tutorial by J White. Part 6. IMG picture loader code. GFA CrackArt picture compression/decompression code. Spectrum (SPU) picture format information and code. Tiny picture decompression code. STOS Using 80 column text in low rez. VDU dump program. MISC Algorithm Corner. Star Wars scroll & Huffman coding. Video Master picture file format information. Falcon screen resolution detection. The IMG, IFF ILBM formats plus EDT sprite file info. Atari Questions and Answers file No 5. PCX picture format information. TIFF/LZW technical memorandum. Current membership list. Index to Issues 1-15. In next months issue of ICTARI (this may change) :- ASSEMBLY GEM Window redraw routine. Sprite packer/unpacker routine. Rectangle intersection detection code. C Useful MagiC C functions. GEM Tutorial. GFA Mandelbrot generator. ShadeBob, 4 bit real time shading. GFA menu locating techniques. STOS Factorial generator program. Puzzle solving program. STOS on the Falcon. PASCAL Program to calculate shift bonuses, etc. MISC For future issues :- Polyline drawing routines in machine code. Bezier curve drawing routines. Picture compression routine for IMG pictures. HP DeskJet/LaserJet compression routines (Mode 2 TIFF). Picture switching techniques. Printer driver code for printing mono/colour images. Using Calamus fonts. Sorting algorithms. Using the BitBlit routines. Code for using the File Selector. Using multiple rasters in machine code. Overscan techniques. STOS sprites. ---------------------------------------------------------------------- EDITORIAL ========= MEMBERSHIP ---------- We have had four new members join this month including one from Denmark, welcome to them. IMAGE COMPRESSION/DECOMPRESSION ------------------------------- As you can see, this month most of the code and articles are concerned with picture formats, image decompression and compression. In the assembly folder I have provided a few of my own unpacking routines for various picture formats that I have been using for some time. We have also received a comprehensive file of similar routines from Marten Lindstrom which, I have to say, are considerably better, but I have left mine in anyway just in case anyone finds them useful. OMEGA BBS --------- We have had an offer to advertise ICTARI on the OMEGA BBS system in Ewell, Surrey which we have taken up. This may provide a few more members although I think the BBS is mainly geared towards the PC community. Perhaps anyone with a Modem could check it out on 081-393- 6226, 8.00pm to 2.00am (8-N-I to 14400bps). DISK COPYING ------------ Does anyone have a program that can make FAST multiple copies of a disk. When I have to duplicate the ICTARI disks it takes me about 3 hours using Acopy and it would be nice to be able to do it a bit quicker. I have tried FastCopy Pro but this takes four times longer when it has to format the disk as well. I have got a demo copy of Kobold (which won't format) but I don't think this is any better for multiple copies. If anyone can suggest a suitable program (or would like to write one) please let me know. ARTICLES -------- We are now very short of articles for next years issues of ICTARI. If you have any code or general programming articles please send them in as soon as possible, especially if you have not contributed before. If everybody just sits back and waits for someone else to send in material for disks we will shortly run out altogether. If you have any ideas on what subjects you would like to see covered in future issues please let us know and we might be able to help. ---------------------------------------------------------------------- CORRESPONDENCE ============== To: *.* From: Peter Hibbs Since this issue is all about picture compression techniques I would like to ask if anyone has any thoughts about text compression. I am writing a program in machine code which requires access to a large number of words in the form of a dictionary. Obviously the simplest way would be to store all the words as an ASCII file with a non ASCII separator between each one and there is a PD file available which does just this. The problem with this approach is the size of the file/s which could run to 1-2 Mb. I really need to compress the file in such a way that I can still access individual words as well as add or delete words from the file. The sort of options I would require are to copy a word into a small buffer and then call a routine which would tell me if the word exists in the dictionary file. Upper or lower case letters don't matter and the order of the words in the file is not important, the number of letters in each word could vary from 2 to whatever. I don't know how the dictionary files that come with word processors are made up but I would guess that they are compressed in some way using common letter groups found in English, i.e. 'ing', 'ation', 'ent', etc which could be encoded as single bytes and common endings like 's','ed','es', etc could be stored as set/reset bits in a single 'endings' byte. If anyone knows how dictionary files are stored or has any ideas on the subject please let me know. ---------------------------------------------------------------------- To: *.* From: Dick Teuber Does anyone have (or has anyone written) a program which can intercept the output from a program to a printer and write it to a disk file instead. The file on disk can then be moved to another computer (i.e. a PC) and sent direct to the printer and so be printed out like the original file. The program should be able to work with any program that outputs data to the parallel port. ---------------------------------------------------------------------- To: ICTARI From: Iain Mcleod I think I should have explained my request more clearly about bitmapping a sprite onto a polygon!!!! What I was wanting to know is, well I'll give an example.. You have a wall disappearing into the distance and how do you give it a *wall* texture, i.e map a drawing of a wall on to the (distorted) rectangle. But of a higher priority... I want to play tracker songs in my game - I can only get one channel using the XBIOS bufptr, etc.. calls. Does anyone know how or (even better) have a copy of the tracker code they can give me. Keep up the *great* work on ICTARI. I will try to add more answers and less questions but then without questions, you wouldn't have answers (?!). I'm also a member of the FALCON FACT FILE so I'll mention ICTARI to them. I also recommend any FALCON user to register : FFF, 11 POUND MEADOW, THE GREEN, WHITCHURCH, HANTS, RG28 7LG Membership is free also except for postage. ---------------------------------------------------------------------- To: Mark Baker From: Jim Ford Thanks for the info that Gnu C++ is up to 2.5.8. Does anyone know where it is available for the Atari ? ---------------------------------------------------------------------- To: Peter Hibbs From: John Logan "Computer Arithmetic - Logic and Design" by Otto Spaniol. John Wiley & Sons 1981. I had forgotten how expensive this book was and now that I am reminded of the price, I doubt whether it is of interest to anyone other than mathematicians or computer engineers. It offers "quick and economical algorithms for performing arithmetic operations in digital computers" and "a comparative discussion of differing concepts which can be used when designing computers". The book considers representation of numbers, the logic of adders, methods of multiplication and division and the calculation of special functions. The algorithms are described by microprograms. These are the hardwired programs used to control the CPUs of computers. The first 2 lines of a restoring division microprogram follow (DD, DE and DR are registers, n is the number of bits in a word, SHL is shift left, Z is the zero flag, V overflow flag): 0: (DD,DE) := dividend;DR := divisor;Z := n;V := DD n-1 XOR DR n-1; 1: if DR n-1 = DR n-2 then [SHL(DD,DE);DE 0 := 0;SHL(DR);DR 0 := 0;goto 1]; 2: etc etc Such emphasis as there is, is on the implementation of the algorithms in hardware although there is probably no reason why they should not be implemented in software. I was interested in the methods of speeding up division but I have to confess that I could not understand them. Not recommended for most programmers. "Microprocessors and Microcomputers" by Eric Huggins. The Macmillan Press Ltd 1979. This a much friendlier book. It describes the evolution of the microprocessor and then considers in some detail how a CPU works, the binary and other number systems, programming elements, hardware and finally software. The latter was the most useful to me as both fixed and floating point algorithms were covered and methods of division were fully explained. (It is not uncommon for an author to explain in detail the process of multiplication and then "leave it to the reader" to work out how to divide. I always take that to mean that he/she did not know how do it.) Special functions (square roots, logs, antilogs and trig) are all covered. Not only are the basic principles set out but flow charts are given. Do not be put off by the beginning of the book where there are descriptions and pictures of elffins and demure damsels! The former are "ELectronic Fetchers and Filers of INformation" and the latter "Direct the Elffin to the correct Memory location, Updating REgisters, Doing Arithmetic and Monitoring the Status record; all ELectronically." They are used to illustrate how the basic CPU works. I am sorry to hear that the book is no longer available. Perhaps a second hand bookshop might be able to supply a copy. */ Thanks for the info John, perhaps if anyone comes across the Huggins book in a bookshop anywhere they could let us know. ICTARI /* ---------------------------------------------------------------------- To: Ictari and all assembler programmers From: Mrten Lindstrm I send you (together with this message) an assembler sub-routine library, for un/packing IMG and IFF ILBM pictures. It has been fairly extensively tested, except that PUTFM and GETFM (non-GEM image copying) have only been briefly tested with a blitter (I don't have one of my own so I had to ask my brother to do the test for me) and that SETTRU (for non-GEM palette setting on a general Atari machine) may not work on a Falcon. I hope you have found room for it all on this disk. */ See ASSEMBLY/PIC_PACS folder. ICTARI /* Has anyone complained about the disk format (10 sectors/track)? I personally find nothing wrong with it (it is what I use with all my personal disks), but if anyone should have problems copying it, I think it would be possible to include a small (1-2 K) simple format/copy program on each disk. Maybe you should also consider packing more of your material into self-extracting files? */ We have had someone 'complain' about non-standard disk formats, primarily disks that were formatted to more than 80 tracks. Also we have had problems formatting some disks with more than 80 tracks which is why we do not use this option any more. Formatting tracks with 10 sectors is usually OK but we normally use 9 sectors unless we really need to squeeze more on a disk. As I am sure everyone knows, if you copy a full disk that is formatted with 10 sectors/track onto a disk which is formatted with 9 sectors/track using the normal desk top copying options, the TOS copies what it can to the target disk but leaves off anything it cannot copy WITHOUT informing the user. If you don't happen to notice the missing files you end up with some files missing on your copied disk. We could provide a small copying type program but we suspect that most members just take copies of the files that they are interested in and, in any case, there are plenty of copying programs in the Public Domain. We try NOT to pack files if we can avoid it because it is easier for members to copy/read files when they are not packed. If anyone has any views on this subject please let us know. ICTARI /* As soon as I joined Ictari I, of course, immediately ordered all back issues, so I have read all the main texts at least. Regarding the discussion on the format for assembler sub-routines I'd like to say: 1) I wholeheartedly support the view that routines intended to be used by others should normally save all registers that aren't used for return values. 2) I also would like to suggest that low ordered registers - and especially D0 - are used as preferred registers for return values. 3) Finally, my own practice is to always make sure that the processor condition flags on return from a sub-routine are set according to a return value in D0 (when applicable - e.g. when a negative value signals error). I know it may be a bit late, but how about it? The advantage is that it saves you a TST instruction in the main routine - which is where I think brevity and clarity should be maximised. To: PoshPaws - man with many names - or anyone else with a Falcon Could you please answer two Falcon questions (that came up when I was writing the non-GEM complemental sub-routines in the PICPAC.S library - for IMG/IFF pictures un/packing - hopefully on this disk): 1) With the exception of high colour resolutions, could you confirm that all other Falcon screens (especially the 256 colour ones) are organized word-interleaved? I know that the Atari compendium says that each and every Atari resolution (except high colour) is word interleaved, but the reason I doubt this is an article in German ST Magazine indicating that TT low rez is BYTE interleaved. 2) Does VSETRGB copy colours to hardware immediately or does it wait for a VBL (like SETPALETTE)? (My routine SETTRU, intended to set a palette on a general Atari computer, as written is counting on the former.) To: everybody I hope you gave my country a thought this month. When you read this the Swedish people will hopefully have decided to join the EU, in which case I intend to order some things from 'Europe' (=UK mostly) this new year, first of which will be Devpack 3. I have, for fear of harassments by the Swedish customs, for a long time now lived with Devpack 2 (cover disk release) + Easy Rider, a German assembler which is very good, with more and better optimization options and more powerful macro string handling than Devpack 2, but not as friendly. Long live the European Union!!! */ Just heard on the news that Sweden have voted to join the EU, just. Hope they know what they are doing. ICTARI /* ---------------------------------------------------------------------- To: ICTARI From: Tony Harris I have a couple of programming questions (in 68000). 1) Any examples of using the STE's BLITTER and H/W chips for sprite and screen manipulation. 2) I need to know how to redraw windows after a DA's been called or any disturbance of the windows ? */ We have published a comprehensive document on the Blitter chip in Issue 11 and various issues have covered sprite use (although not, I think, using the blitter). See next month for window redraw code and INDEX.TXT file in the MISC folder for back issue list. ICTARI /* ---------------------------------------------------------------------- To: *.* From: Richard Evans Some views on 2B's MagiC 2.0 multitasking OS, running on an STFM with NVDI 2.51 ----------------------------------------------------------------- I'm sure that any feedback on newish software such as MagiC must be useful, so I will give you my impressions so far. Generally it is excellent, the whole system is much faster, and with NVDI as well screen updates are no longer a problem- programs such as Kandinsky become useable. Whilst the mag reviews didn't think much of MagXdesk, I have to say that for the majority of users it should be fine, as it is small and there are many useful features hidden in the keyboard shortcuts for editing, calling cli's, passing parameters etc. The system copes well with minor compatibility problems and is able to tidy corrupted screens, pass control away from locked programs and delete them from memory. Extended system routines mean that some GEM programs have extra features similar (but less comprehensive) to Let'em'fly, windows have a backdrop button and do not have to be topped to scroll, size, close, etc (some older programs, and the LC 5.52 editor don't respond however). TOS programs run in windows, can use GDOS fonts and have clipboard support etc. Software written with multitasking in mind run perfectly (obvious I s'pose), though MagiC does not yet support some more advanced MTOS features such as drag- and-drop. Badly written software such as Protext and Prodata can be run in single mode, though the reliability of the system may be compromised. In use, the system is responsive even when multi-tasking and is absolutely brilliant for those endless hours of de-compacting files etc. If the system is slowing down too much, there are utilities to control the amount of processor given to background tasks, which should greatly reduce any potential problem (I've only needed to use them once). The manual supplied is better than average, covering MagXdesk and listing new system calls in some detail- it does however presume that readers have knowledge of GEM/2 and AES 4.x, and an appropriate reference manual would be required for inexperienced programmers (me!) trying to write programs for these enhanced environments; there also appears to be a few errors in the programmers section, just to keep you on your toes! Installation is refreshingly simple, and some utilities such as a CLI and routine library are supplied; unfortunately the utilities and accompanying text files are in German, and the library is for Pure C- I hope 2B might consider translating these and supplying a Lattice library (unlikely !). Cons: There had to be some! A lot of my older GEM software, especially PD is not reliable enough to use safely; though most of it is junk anyway, it could be worth checking your favourite software before you buy. KnifeST goes very strange if you try to swap to another program and Wercs (as supplied with LC 5.52) crashes all the time- I have never liked this program much, and it apparently crashes on the Falcon anyway: come on HiSoft, it's about time WERCS was rewritten. Protext V4.37 and Prodata will only run in single mode and the Protext config program locks if the mouse is used. My favourite CLI by far, Gulam (beta test version) has problems running because the key bindings remap the keyboard and some (not all) other programs then produce junk when you use the keyboard; pressing shift-help when in use can sometimes limit the problem. Cubase Lite, and a borrowed Cubase V3 crash on loading- this is a major problem for me, so for the meantime it has to be back to my old TOS for these; apparently Steinberg are checking this out. KCS Omega V1 is out as well, Tiger is better than KCS, but the screen still gets messed up, menus swap by themselves and so it's again back to the old TOS (mind you, Omega isn't too stable under that either!). Lastly, and most drastic of all in my case, X- Debug has become unusable; whilst it appears to run OK, it locks up when running my GEM programs, I don't know where the problem lies, but I have set the assumptions flag to 1, and it makes no difference; I admit this may be because I haven't fully got to grips with all the options available in X-Debug, but as my programs run OK normally on MagiC, within MonST and within X-Debug with my old ROM TOS, I don't think this is the case. I sincerely hope that Andy Pennel will revise this, as I can't live without it. Here is a list of all the software I have been able to try out, with the results as follows: Software that runs OK under Magic & NVDI (may need memory limiting): Address V1.8 (unregistered) AIM V3.00 Calligrapher (STR version):won't switch when printing though Devpac V2 Edith V1.0 (demo) Egale V1.52 Everest 3.3E (unregistered) Ghostscript V? ST-Guide V(27.5.94) Imagecopy Colour Kandinsky (unregistered) Kobold (STR demo) Lizard V1.0 Opus V2.2 ORCS V1.0(unregistered) Lattice C V5.52: won't switch when compiling from editor Selectric V1.1 Superbase Personal V1.028 TEX V2.9 & probably V3.1 Programs that run with some problems: (* means not recommended): Arabesque (STF version) ; single only Calamus V1.09n Easydraw V2 Gulam Beta test; problems with keyboard entry KCS Omega V1 * KnifeST V1.1 ; don't switch tasks Megapaint V2 * ; single only, screen gets very messy Music DTP V2.2c Prodata V1.18 ; single only Protext V4.37 ; single only, avoid config program if possible Tempus V2 * ; As usual, this one is a real pain ! Programs that fail miserably: Cubase Lite V1.0 Cubase V3 ; presumable all other V's as well DIPS V? Omen (demo version); What did you expect? TEXdraw V? Wercs V? X-Debug V1.01 ; unless I'm missing the point here? A lot of old PD doesn't work. Autoswitch Overscan software V3.00 is not compatible with MagiC, I was told it was when I bought mine, so ask for some proof before forking out good money! Summary: In general, the sooner MagiC becomes officially recognised as the new Atari operating system the better, as unlike MTOS, it offers a stable total replacement to TOS, and it works !! There are also hints in the MagiC manual that suggest Mint/MTOS features will be added in later releases. Despite problems, compatibility is generally excellent, and lets face it, none of the competition is going to be better in this department; many recent German shareware programs actively support MagiC, and the quality of this stuff is generally amazing. From what I read, Geneva seems less reliable than Magic, is not a complete replacement (it won't run on pre TOS 1.4 machines) and does not support pre-emptive multitasking, - I'll leave you to make your mind up on that one! These are obviously only my views and I will not be held responsible for any errors in this text or problems arising from any interpretations or assumptions made from it. I hope this has been of some use, and please bear in mind that some compatibility problems may be caused by NVDI and not MagiC (I couldn't be bothered to check every program!). If anyone has similar problems, or has answers to some of mine, please write to me. If anyone has software that needs testing under MagiC or/and NVDI (4 meg STFM+HD+MM), I will be happy to check it, but you must enclose a S.S.A.E. or I will not reply, you have been warned! By some freak happening (why do I get so many of these?!) NVDI did not get installed correctly on my system, and it took me 3 weeks to notice. The differences are not great, but the system is now even faster, and slightly more compatible:- If run singly, the screen problems with KCS and Tiger are less problematic, however the MPE crashes badly if you try to run Tiger from within KCS. I was having screen redraw problems with Megapaint, but these are fine now. Arabesque (STF version) also runs fine in single mode. X-Debug seems slightly happier, but is still unstable under many weird and wonderful situations (i.e. who knows when it will crash next!). I have found that the config program for Prodata behaves exactly as the one in Protext i.e. moving the mouse within this program will crash the system:- don't do it! Apparently the latest version of Autoswitch Overscan does work with MagiC, it is probably version 3.0 zi (I have 3.0 zg), and if anyone needs to get the upgrade, Compo are the new distributors, not System Solutions. As I haven't got it yet, I can't comment on this one. I have also included a print out from the GEM_TEST program supplied with NVDI to give an idea of the speed increases. Remember that this is compared to a TOS1.0 STFM, users of later TOS's will not get quite so good results, and that since GEM_TEST was written by 2B, its probably optimised to give particularly impressive results with MagiC & NVDI !!! Even so, not bad eh? I haven't got a program to test the speed of non-GEM system routines, but believe me, they are all considerably faster. You can also throw away Pinhead, Poolfix, Foldxxx and all those other utilities that were an essential part of TOS based setups. I am sure that there are problems with this system, but the only one I have noticed is that the menu bars occasionally don't switch when the window of another application is selected; this is easily corrected however by selecting the application required from the program manager. * * NVDI GEM-Test V2.00 (c) 1991-1993 by Sven & Wilfried Behne * * Resolution : 640 X, 400 Y * Planes : 1 * color pens : 2 * Palette : 2 grades * Operating system: Mag!X 2.01 * Reference : TOS 1.00/ST-High * NVDI version : V2.51 installed * GDOS version : NVDI * Blitter : doesn't exist * CPU : M68000 * FPU : doesn't exist * Machine : ATARI ST * Text output : 952 % Lines : 325 % Rectangles : 498 % Polygons : 220 % Circles/Ellipses : 371 % Raster operations : 315 % Attribute functions: 489 % Inquire functions : 365 % ESCAPES : 297 % BIOS output : 225 % GEMDOS output : 647 % AES object draw : 1121 % v_gtext (8 chars, height 4, effect 0): 309781 P/s v_gtext (40 chars, height 4, effect 0): 366441 P/s v_gtext (8 chars, height 6, effect 0): 642509 P/s v_gtext (40 chars, height 6, effect 0): 1103299 P/s v_gtext (8 chars, height 13, effect 0): 794375 P/s v_gtext (40 chars, height 13, effect 0): 1255478 P/s v_gtext (8 chars, height 13, effect 1): 789590 P/s v_gtext (40 chars, height 13, effect 1): 1248304 P/s v_gtext (20 chars, height 13, effect 29): 131072 P/s v_justified (8 chars, height 13, effect 8): 775573 P/s v_justified (40 chars, height 13, effect 8): 1243567 P/s v_gtext (20 chars, height 26, effect 0): 1217010 P/s v_pline (horizontal): 3192019 P/s v_pline (vertical): 321027 P/s v_pline (arbitrary): 131850 P/s vr_recfl (48 * 20 points, fillcolor 0, fillstyle 0, fillindex 0): 3130000 P/s vr_recfl (48 * 20 points, fillcolor 1, fillstyle 2, fillindex 3): 1674000 P/s vr_recfl (100 * 100 points, fillcolor 0, fillstyle 0, fillindex 0): 7792000 P/s vr_recfl (100 * 100 points, fillcolor 1, fillstyle 2, fillindex 3): 3708000 P/s vr_recfl (638 * 400 points, fillcolor 0, fillstyle 0, fillindex 0): 21096000 P/s vr_recfl (638 * 400 points, fillcolor 1, fillstyle 2, fillindex 3): 12594000 P/s vrt_cpyfm (100 * 100 points, horizontal moved): 1624000 P/s vrt_cpyfm (640 * 200 points, horizontal moved): 2510000 P/s vrt_cpyfm (640 * 200 points, horizontal moved): 2512000 P/s vrt_cpyfm (320 * 200 points, vertical moved): 7327000 P/s vrt_cpyfm (640 * 200 points, vertical moved): 8894000 P/s vrt_cpyfm (640 * 200 points, vertical moved): 8914000 P/s vro_cpyfm (100 * 100 points, horizontal moved): 1624000 P/s vro_cpyfm (640 * 200 points, horizontal moved): 2511000 P/s vro_cpyfm (640 * 200 points, horizontal moved): 2512000 P/s vro_cpyfm (320 * 200 points, vertical moved): 7340000 P/s vro_cpyfm (640 * 200 points, vertical moved): 8904000 P/s vro_cpyfm (640 * 200 points, vertical moved): 8923000 P/s 1000 calls of vst_height : 68 ms 1000 calls of vst_point : 69 ms 1000 calls of vst_color : 61 ms 1000 calls of vst_effects : 59 ms 1000 calls of vst_alignment : 67 ms 1000 calls of vsl_color : 62 ms 1000 calls of vsl_type : 60 ms 1000 calls of vsl_udsty : 55 ms 1000 calls of vsf_interior : 73 ms 1000 calls of vsf_style : 78 ms 1000 calls of vsf_color : 61 ms 1000 calls of vswr_mode : 61 ms 1000 calls of vql_attributes : 68 ms 1000 calls of vqf_attributes : 64 ms 1000 calls of vqt_attributes : 85 ms 1000 calls of vqt_extent : 226 ms 1000 calls of von vqt_width : 85 ms 1000 calls of vqt_name : 146 ms 1000 calls of vqt_fontinfo : 104 ms 1000 calls of vqin_mode : 63 ms 1000 calls of vq_color : 91 ms v_curtext (without scrolling) : 2308000 P/s v_curtext (scrolling) : 462000 P/s Bconout (without scrolling) : 1106000 P/s Bconout (scrolling) : 379000 P/s Cconws (without scrolling) : 2496000 P/s Cconws (scrolling) : 469000 P/s --------------------------- End of file ------------------------------