Skip to content

Pictures

dfgordon edited this page Feb 1, 2025 · 5 revisions

Pictures

A picture in DHRLIB is a sequence of drawing commands encoded as a bitstream. Conditional assembly controls availability of some picture related commands.

Command _pics<2 _pics=2 _pics=3
&DRAW
&REC
&SCAN
&STOP
&END
&SEEKG
&SEEKP
&TELLG
&TELLP

Basic Recording

You can record a sequence of drawing commands for later rendering as follows.

  1. Issue &SEEKP(<addr>,0)
  2. Issue &REC to start recording
  3. Issue any of the basic drawing commands
  4. Issue &END to flush and write the end of drawing marker
  5. Issue &STOP to stop recording
  6. Issue &TELLP(<end>,<bit>,<count>) to get the statistics

Here,

  • <addr> is the address where the drawing is stored
  • <end> is a real variable that receives the ending address
  • <bit> is a real variable that receives the unused bit count
  • <count> receives the number of commands in the drawing, not counting the end-of-drawing marker

Saving the Picture

The picture can be saved for playback later as follows:

10 REM A0=picture address
20 REM suppose we have just finished recording
30 &TELLP(A1,BIT,CNT)
40 L = A1 - A0 + 2
50 PRINT CHR$(4);"BSAVE PIC1,A";A0;",L";L

If you are interested in saving a byte, you can use the more exact length calculation:

40 L = INT((8*(A1-A0) + 8 - BIT + 3 + 7)/8)

Here, the bit offset and 3-bit end of picture marker are explicitly accounted for. The "7" causes the expression to round up.

Basic Playback

Basic playback is as follows:

  1. Issue &SEEKG(<addr>,0)
  2. Issue &DRAW AT <x>,<y>

Here, <addr> is an expression with the address of the drawing, while <x> and <y> are expressions with horizontal and vertical offsets. You can also playback a subset of the drawing commands:

  1. Issue &SEEKG(<addr>,<skip>)
  2. Issue &DRAW <count> AT <x>,<y>

Here, <skip> is an expression with the number of commands to skip from the beginning, and <count> is the number of commands to draw.

Overwrite Tail

The following replaces commands 100 and beyond of the drawing at $6000, with commands 50-150 of the drawing at $7000.

BLOAD PIC1,A$6000
BLOAD PIC2,A$7000

&SEEKG(7*4096,50)
&SEEKP(6*4096,100)
&REC: &DRAW 100 AT 0,0: &END: &STOP

Inserting

The following inserts the drawing at $7000 at position 50 in the drawing at $6000. It uses scratch space at $8000.

BLOAD PIC1,A$6000
BLOAD PIC2,A$7000

REM first copy tail to scratch

SEEKG(6*4096,50)
SEEKP(8*4096,0)
&REC: &DRAW AT 0,0: &END: &STOP

REM now copy both segments in turn

&SEEKP(6*4096,50): &REC

&SEEKG(7*4096,0)
&DRAW AT 0,0

&SEEKG(8*4096,0)
&DRAW AT 0,0

&END: &STOP

Scan Mode

In the above examples, drawing commands are displayed and recorded at the same time. Scan mode suppresses drawing while still allowing recording. Using scan mode is simple, just issue &SCAN. The &STOP command cancels both recording and scanning. Here is the overwrite tail example using scan mode:

BLOAD PIC1,A$6000
BLOAD PIC2,A$7000

&SEEKG(7*4096,50)
&SEEKP(6*4096,100)
&REC: &SCAN: &DRAW 100 AT 0,0: &END: &STOP

This can be much faster since nothing has to be drawn.

Modify in Place

You can modify a picture in place provided the old operations are replaced with new operations of the same length, e.g.

&SEEKG(A0,5)
&SEEKP(A0,5)
&REC: &SCAN: &DRAW 5 AT -8,0
GOSUB 1000: REM flush subroutine
&STOP

This performs an 8 pixel shift of everything associated with operations 5 through 9.

N.b. it is important to flush the stream. Normally we use &END, but here we do not actually want to end the drawing. A subroutine that can be used in this situation is

1000 REM flush without ending
1001 &DRAW 1 AT 0,0: REM flushes as long as there are more operations
1001 L = PEEK(249): L = L - 8*INT(L/8): REM get last opcode
1002 IF L = 0 THEN &END: REM there were no more operations
1003 RETURN

Tips

  • The &DRAW command is stateful, specifically cursor, color and mode comprise a state.
    • generally the state should be set first in any drawing
    • splicing in a way that interrupts cursor state will lead to unexpected results
  • Get the end of picture pointer using &SEEKG(<addr>,-1) followed by &TELLG.
  • Get the last drawing op-code by taking the remainder of peek(249)/8.
  • Get a screen address using &MOVE followed by peek(38) + peek(39)*256 + peek(229). If peek(49180)>127 the address is in auxiliary memory.
  • You can issue &MODE during recording, only the XOR bit is recorded. Playback will not affect any other status bits.
    • However, &MODE=128 will clear the recording bit, you probably want &MODE=192
    • This will also erase the information about the last drawing op-code (but not the command itself)
  • When translating a color drawing horizontally, best results are obtained by using specific increments
    • increment of 4 preserves edges of solid colors
    • increment of 8 preserves edges of dither patterns
    • increment of 7 preserves byte alignment within the screen buffer
    • use common multiples of two or more to satisfy simultaneous conditions
  • &DRAW is not recursive. Issuing &DRAW while recording merely copies all the commands from one drawing into another.
  • &HPLOT followed by &HPLOT TO renders the first pixel twice, which in XOR mode leaves it unchanged. Using &MOVE is an alternative.

Picture Operation Codes

Operation Code Arguments Total Bits
End 000 none 3
Color 001 dither pattern (16) 19
XOR 010 state (1) 4
Cursor 011 x (10), y (8) 21
Plot 100 x (10), y (8) 21
LineTo 101 x (10), y (8) 21
Trapezoid 110 x0 (10), x1 (10), x2 (10), x3 (10), y0 (8), y1 (8) 59
Stroke 111 x (10), y(8), brush (3) 24

Clone this wiki locally