(* PAINT.PAS -- translate geoPaint graphics ** Copyright (c) 1995,1996 Jochen Metzinger ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2, or (at your option) ** any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *) UNIT Paint; INTERFACE USES Crt, Graph, graphic, global, errors, cvt, pcx; CONST DEFAULT_COLOR = $BF; (* dark gray on lt grey *) ERROR_COLOR = $A6; (* lt red on blue *) FUNCTION IsPaint: BOOLEAN; (* is it a geoPaint document *) PROCEDURE DoPaint(pcx_name: STRING; view, wait_key: BOOLEAN; b_level: BYTE); (* do translation *) IMPLEMENTATION CONST PatSize = 2*80*8; NulSize = 8; ColSize = 2*80; FrmSize = PatSize + NulSize + ColSize; ErrSize = 2*Patsize; MaxSize = ErrSize + 1024; TYPE PaintPartType = RECORD size: WORD; CASE INTEGER OF 0: (raw: ARRAY [0..MaxSize] OF BYTE); 1: (pattern: ARRAY [0..1, 0..79, 0..7] OF BYTE; gap: ARRAY [0..NulSize-1] OF BYTE; color: ARRAY [0..1, 0..79] OF BYTE); 2: (error: ARRAY [0..3, 0..79, 0..7] OF BYTE); END; (* PaintPartType *) BitMapType = RECORD width, height: WORD; data: ARRAY [0..31, 0..3, 0..79] OF BYTE; __res: WORD; END; (* BitMapType *) VAR chain: BYTE; (* used chain *) pp: ^PaintPartType; (* paint part *) err_cp, err_pp: LongInt; err_why: STRING; (* errors from GetPaintPart() *) bm: ^BitMapType; (* bit map *) y0: LongInt; (* y coordiante of bit image *) FUNCTION IsPaint: BOOLEAN; VAR class: STRING; version: BYTE; BEGIN IsPaint := FALSE; cvt_class_version(class, version); IF class <> 'Paint Image' THEN EXIT; IF (version DIV 10) <> 1 THEN EXIT; IsPaint := TRUE; END; (* IsPaint *) PROCEDURE GetPaintPart; (* get paint part from selected chain *) VAR counter, i: BYTE; result: WORD; pattern: ARRAY [0..7] OF BYTE; BEGIN err_cp := -1; err_pp := -1; err_why := ''; WITH pp^ DO BEGIN size := 0; FillChar(pattern, SizeOf(pattern), 0); FillChar(gap, SizeOf(gap), 0); FillChar(color, SizeOf(color), DEFAULT_COLOR); END; (* with *) cvt_seek(0); IF cvt_eof THEN BEGIN (* empty chain *) pp^.size := FrmSize; EXIT; END; (* if *) WHILE cvt_getch(CHAR(counter)) DO BEGIN CASE counter OF $00..$40: BEGIN (* individual bytes *) IF (counter = 0) AND NOT cvt_eof THEN IF err_why = '' THEN BEGIN err_cp := cvt_pos - 1; err_pp := pp^.size; err_why := 'ind[0] at ' + long2str(err_cp,0); err_why := err_why + ' in #' + long2str(chain,0); END; (* if *) cvt_read(pp^.raw[pp^.size], counter, result); INC(pp^.size, result); IF result <> counter THEN BEGIN IF err_why = '' THEN err_why := '#'+long2str(chain,0)+' too short'; EXIT; END; (* if *) END; $41..$7F: BEGIN (* fill patterns *) counter := counter AND $3F; cvt_read(pattern, SizeOf(pattern), result); IF result <> SizeOf(pattern) THEN BEGIN IF err_why = '' THEN err_why := '#'+long2str(chain,0)+' too short'; EXIT; END; (* if *) WHILE counter > 0 DO BEGIN Move(pattern, pp^.raw[pp^.size], SizeOf(pattern)); Inc(pp^.size, SizeOf(pattern)); Dec(counter); END; (* for *) END; $80..$FF: BEGIN (* repeat *) counter := counter AND $7F; IF (counter = 0) AND (err_why <> '') THEN BEGIN err_cp := cvt_pos - 1; err_pp := pp^.size; err_why := 'rep[0] at ' + long2str(err_cp,0); err_why := err_why + ' in #' +long2str(chain,0); END; (* if *) IF NOT cvt_getch(CHAR(pattern[0])) THEN BEGIN IF err_why = '' THEN err_why := '#'+long2str(chain,0)+' too short'; EXIT; END; (* if *) FillChar(pp^.raw[pp^.size], counter, pattern[0]); Inc(pp^.size, counter); END; END; (* case *) IF pp^.size > ErrSize THEN BEGIN IF err_why = '' THEN err_why := '#'+long2str(chain,0)+' too long'; EXIT; END; (* if *) END; (* while *) IF err_why = '' THEN IF pp^.size = PatSize THEN pp^.size := FrmSize (* black/white *) ELSE IF pp^.size <> FrmSize THEN err_why := '#'+long2str(chain,0)+' has wrong size' ELSE FOR i := 0 TO 7 DO IF pp^.gap[i] <> 0 THEN BEGIN err_why := 'gap not null in #'+long2str(chain,0); EXIT; END (* if *) END; (* GetPaintPart *) PROCEDURE paint2bitmap; (* convert geoPaint part pp to bit map BM *) VAR line, column, row, color, a, b, plain: BYTE; pp_pos: LongInt; BEGIN IF pp^.size = FrmSize THEN BEGIN bm^.width := 639; bm^.height := 15; FOR line := 0 TO 1 DO FOR column := 0 TO 79 DO BEGIN color := pp^.color[line,column]; FOR plain := 0 TO 3 DO BEGIN CASE color AND $88 OF $00: BEGIN a := $00; b := $00; END; $08: BEGIN a := $FF; b := $FF; END; $80: BEGIN a := $FF; b := $00; END; $88: BEGIN a := $00; b := $FF; END; END; (* case *) FOR row := 0 TO 7 DO bm^.data[8*line+row,plain,column] := (pp^.pattern[line,column,row] AND a) XOR b; color := (color AND $77) SHL 1; END; (* for *) END; (* for *) END ELSE (* pp^.size <> FrmSize *) BEGIN bm^.width := 639; bm^.height := 31; IF err_pp < 0 THEN BEGIN err_pp := pp^.size; IF err_pp > 1280 THEN err_pp := 1280; END; (* if *) pp_pos := 0; FOR line := 0 TO 3 DO FOR column := 0 TO 79 DO FOR row := 0 TO 7 DO BEGIN IF pp_pos < err_pp THEN color := DEFAULT_COLOR ELSE IF pp_pos < pp^.size THEN color := ERROR_COLOR ELSE color := $00; INC(pp_pos); FOR plain := 0 TO 3 DO BEGIN CASE color AND $88 OF $00: bm^.data[8*line+row,plain,column] := $00; $08: bm^.data[8*line+row,plain,column] := NOT pp^.error[line,column,row]; $80: bm^.data[8*line+row,plain,column] := pp^.error[line,column,row]; $88: bm^.data[8*line+row,plain,column] := $FF; END; (* case *) color := (color AND $77) SHL 1; END; (* for *) END; (* for *) END; (* else *) END; (* paint2bitmap *) FUNCTION ReadChar: CHAR; (* ReadKey w/o special keys *) VAR ch, dummy: CHAR; BEGIN REPEAT ch := ReadKey; IF ch = #0 THEN dummy := ReadKey; UNTIL ch <> #0; ReadChar := ch; END; (* ReadChar *) PROCEDURE GetLevel(VAR level: BYTE); (* input forced level *) VAR ch: CHAR; BEGIN TextOut('Level: <0> back <1> stop <2> continue <3> continue b/w'); REPEAT ch := ReadChar; UNTIL ch in ['0'..'3']; level := ORD(ch) - ORD('0'); END; (* GetLevel *) PROCEDURE CorrectByte(chain_pos: LongInt; b: BYTE); (* change byte in chain and recycle images *) BEGIN cvt_buffer^[chain_pos] := b; GetPaintPart; paint2bitmap; PutImage(0, y0, bm^, NormalPut); IF bm^.height = 15 THEN BEGIN SetFillStyle(SolidFill, 0); Bar(0, y0+16, VGA_MAXX, y0+31); END; (* if *) END; (* CorrectByte *) PROCEDURE CorrectNext(chain_pos: LongInt; VAR value: BYTE); (* try to find right byte for correction *) VAR count: LongInt; step: BYTE; BEGIN step := 0; REPEAT (* increment value *) INC(value); (* set up paint *) cvt_buffer^[chain_pos] := value; GetPaintPart; TextOut('CORRECT: $'+HexStr(value,2)); (* `fast calculation' *) CASE value OF $00..$40: (* individual bytes *) IF pp^.size = FrmSize THEN BEGIN CorrectByte(chain_pos, value); EXIT; END; (* else *) $41..$7F: BEGIN (* fill patterns *) count := PatSize - LongInt(pp^.size); IF count < 0 THEN count := FrmSize - LongInt(pp^.size); IF count < 0 THEN value := $7F (* overflow *) ELSE IF count MOD 8 <> 0 THEN value := $7f (* odd number *) ELSE BEGIN count := value + count DIV 8; IF count > $7F THEN value := $7F ELSE BEGIN value := count; CorrectByte(chain_pos, value); EXIT; END; (* else *) END; (* else *) END; $80..$FF: BEGIN (* repeat *) count := PatSize - LongInt(pp^.size); IF count < 0 THEN count := FrmSize - LongInt(pp^.size); IF count < 0 THEN value := $FF (* overflow *) ELSE BEGIN count := value + count; IF count > $FF THEN value := $FF ELSE BEGIN value := count; CorrectByte(chain_pos, value); EXIT; END; (* else *) END; (* else *) END; END; (* case *) IF (value = $40) OR (value = $7F) OR (value = $FF) THEN INC(step); UNTIL step > 3; END; (* CorrectNext *) PROCEDURE CorrectPos(chain_pos: LongInt); (* correct menue *) VAR b, b0: BYTE; ch: CHAR; BEGIN b0 := cvt_buffer^[chain_pos]; IF b0 <> 0 THEN b := b0 ELSE b := $41; CorrectNext(chain_pos, b); WHILE TRUE DO BEGIN TextOut('CORRECT: orrect <+>1 <->1 <*>+16 -16 '+ 'one ndo ['+HexStr(b,2)+'] size='+long2str(pp^.size,4)); REPEAT ch := UpCase(ReadChar); UNTIL ch IN ['C', '+', '-', '*', '/', 'D', 'U']; CASE ch OF 'C': (* next *) CorrectNext(chain_pos, b); 'U': BEGIN (* undo *) CorrectByte(chain_pos, b0); EXIT; END; 'D': EXIT; (* done *) '+': BEGIN (* +1 *) INC(b); CorrectByte(chain_pos, b); END; '*': BEGIN (* +16 *) INC(b, 16); CorrectByte(chain_pos, b); END; '-': BEGIN (* -1 *) DEC(b); CorrectByte(chain_pos, b); END; '/': BEGIN (* -16 *) DEC(b, 16); CorrectByte(chain_pos, b); END; END; (* case *) END; (* while *) END; (* CorrectPos *) PROCEDURE DoCorrection(VAR level: BYTE); VAR ch: CHAR; BEGIN ch := 'E'; REPEAT CASE ch OF '?': TextOut('orrect ap evel rror one'); 'E': IF err_why <> '' THEN TextOut('CORRECT: '+err_why+' [Press ''?'' for menue]') ELSE TextOut('CORRECT: no reason [Press ''?'' for menue]'); 'L': BEGIN GetLevel(level); IF level <> 0 THEN EXIT; level := 4; TextOut('orrect ap evel rror one'); END; 'G': IF pp^.size = FrmSize THEN BEGIN FillChar(pp^.gap, SizeOf(pp^.gap), 0); level := 0; EXIT; END; (* if *) 'C': IF err_cp < 0 THEN TextOut('ERROR: no error file position found [Press ''?'' for menu]') ELSE BEGIN CorrectPos(err_cp); TextOut('orrect ap evel rror k'); END; (* else *) END; (* case *) ch := UpCase(ReadChar); UNTIL ch = 'D'; END; (* DoCorrection *) FUNCTION get_max_used_chain: BYTE; (* get max. used chain *) VAR chain: BYTE; BEGIN FOR chain := 126 DOWNTO 0 DO IF cvt_size(chain) >= 0 THEN BEGIN get_max_used_chain := chain; EXIT; END; (* if *) FATAL('empty painting'); END; (* get_max_used_chain *) PROCEDURE open_paint(pcx_name: STRING; view: BOOLEAN); (* open for output *) BEGIN IF view THEN BEGIN OpenGraphic(CBMcp); TextOut(cvt_name); END ELSE Write(cvt_name,' '); IF pcx_name <> '' THEN pcx_open(pcx_name, CBMcp, VGA_MAXX+1); END; (* open_paint *) PROCEDURE close_paint(pcx_name: STRING; do_view, wait_key: BOOLEAN); (* close output *) BEGIN IF pcx_name <> '' THEN pcx_close; IF do_view THEN CloseGraphic(wait_key) ELSE WriteLn; END; (* close_paint *) PROCEDURE prepare_line; VAR dy: LongInt; BEGIN INC(y0, 16); IF y0 > VGA_MAXY-50 THEN BEGIN dy := y0 DIV 2; ScrollUp(0, 0, VGA_MAXX, y0, dy); DEC(y0, dy); END; (* if *) END; (* prepare_line *) PROCEDURE DoPaint(pcx_name: STRING; view, wait_key: BOOLEAN; b_level: BYTE); VAR level: BYTE; BEGIN IF b_level = 0 THEN b_level := 1; IF b_level >= 4 THEN b_level := 3 + ORD(view); open_paint(pcx_name, view); y0 := -16; New(pp); New(bm); FOR chain := 0 TO get_max_used_chain DO BEGIN cvt_chain(chain); IF view THEN prepare_line ELSE Write('.'); REPEAT GetPaintPart; level := b_level * ORD(err_why <> ''); IF view AND (level > 0) THEN BEGIN paint2bitmap; PutImage(0, y0, bm^, NormalPut); IF level = 4 THEN BEGIN DoCorrection(level); TextOut(cvt_name); END; (* if *) END; (* if *) UNTIL level <> 4; IF err_why = '' THEN err_why := ''; paint2bitmap; IF view THEN PutImage(0, y0, bm^, NormalPut); IF level = 2 THEN BEGIN IF view THEN TextOut('*** '+err_why); bm^.height := 15; END; (* if *) IF level = 3 THEN BEGIN IF view THEN TextOut('*** '+err_why); pp^.size := FrmSize; paint2bitmap; END; (* if *) IF pcx_name <> '' THEN pcx_image(bm^); IF level = 1 THEN BEGIN IF pcx_name <> '' THEN pcx_close; G_FATAL(err_why); END; (* if *) END; (* for *) close_paint(pcx_name, view, wait_key); END; (* DoPaint *) END. (* Paint *)