All About SIXELs Chris_F_Chiesa@cup.portal.com 4:24 pm Sep 29, 1990 Well, all, it's been several months since I first agreed to write something about Sixel graphics. Sorry it's taken so long, but that's life when you're a busy computer jockey! :-) (I've actually written another couple of Sixel-generating programs since then, too. How time flies...!) I'm going to try to condense three years' worth of gleanings from manuals, experiments, programming, and other investigations, into one document, while trying to remain coherent. Please bear with me if I don't quite pull it off. Feel free to write me back and ask questions. Having said that, let the ramblings commence! ----- I. GETTING "INTO SIXEL MODE" Putting a device into Sixel interpretation mode is performed, not surprisingly, by means of an escape sequence. The particular escape sequence which tells a terminal (or printer, or Sixel interpretation software!) "sixel information follows," is DCS p1; p2; p3; q ... where DCS is EITHER the eight-bit "Device Control Sequence" character (decimal value 144), OR the two seven-bit characters "Escape, capital P" (Escape having the decimal value 27). "P1, p2," and "p3" are OPTIONAL parameters, separated by semicolons (;) IF they appear at all. The "q" is a literal, lower-case letter Q. There are, of course, no spaces in the sequence; I've used them here for clarity in your reading. The VT340 Programmer's Reference Guide (not the Manual -- I'm referring to the short summary thingie) describes these parameters this way: P1 is the macro parameter. This parameter indicates the pixel aspect ratio used by the application or terminal. The pixel aspect ratio defines the shape of the pixel dots the terminal uses to draw images. For example, a pixel that is twice as high as it is wide has an aspect ratio of 2:1. NOTE: The macro parameter is provided for compatibility with existing Digital software. New applications should set P1 to 0 and use the set raster attributes control, described later in this chapter. P1 Pixel Aspect Ratio (Vertical:Horizontal) Omitted 2:1 0 or 1 5:1 2 3:1 3 or 4 2:1 5 or 6 2:1 7,8, or 9 1:1 P2 selects how the terminal draws the background color. You can 3$ use one of three values. P2 Meaning 0 or 2 Pixel positions specified as 0 are set to the current background color. 1 Pixel positions specified as 0 remain at their current color. P3 is the horizontal grid size parameter. The horizontal grid size is the horizontal distance between two pixel dots. The VT300 ignores this parameter because the horizontal grid size is fixed at 0.0195 cm (0.0075 in). Keep in mind that the above description is somewhat specific to the VT340 graphics terminal; the general idea of "what the parameters represent" is more important than the specifics of how the VT340 happens to do things. (The "default" pixel aspect ratio of the VT340 and VT240, for instance, is 2:1, but for other devices or applications it might be different.) II. GETTING "OUT OF SIXEL MODE" Officially, one exits Sixel interpretation mode by following one's Sixel image data with the sequence ST ... where ST is either the eight-bit "String Terminator" character (ASCII 156 decimal), OR the sequence "Escape, backslash" (ESC \). In practice, for example when using the VT240 terminal, ANY escape sequence or eight-bit control character will cause Sixel interpretation mode to terminate. It's still a good idea to use the recommended sequence, though, in case someday a particular device, or program, depends on it. III. SIXEL DATA REPRESENTATION Now that you know how to put a device, or program, into Sixel interpretation mode, it's time to tell you what Sixel data IS! The word "sixel" actually means six pixels ("six" + "pixel" = "sixel") stacked atop each other, like this: * * * * * * Each pixel in the sixel can be either ON or OFF, in a single color (you'll see how multiple colors are handled, shortly). Each pixel position is assigned a numeric power-of-two value, from 1 at the top to 32 at the bottom; thus, for any given combination of on and off pixels, one can add up the values of the "on" pixels and obtain a unique number in the range 0 to 63 decimal. An offset of 63 decimal is added to this numeric value to obtain an ASCII character in the "printable" range, 63 to 126 decimal, and it is these characters which appear in a "Sixel image file." You can see that the MINIMUM numeric sixel value 0 gives ASCII code 63, the "?" character, and that numeric sixel value 63 gives ASCII code 126, the "~" character. Now you know why Sixel graphics files have so many "?"'s in them! Sixel data display begins at the "upper left corner" of whatever device or imaging area is being used; successive sixels display consecutively from left to right, building up a horizontal six-pixel-high "band" of imagery. You now know all you need to know in order to produce, in Sixel, a single "band" of monochrome graphics. The Sixel graphics format also includes several "control" characters -- in the printable ASCII range, but outside the range of 63-to 126 decimal used for image data -- which allow you to change the position where new Sixels will be displayed, and to set and use a particular "color map. I'll get to these shortly. Before that, though, there's one more thing to say about the ordinary "data" portion of the Sixel format. In the interest of efficiency, the Sixel format implements a run-length encoding scheme. The "!" character is used as a "repeat count introducer." A "!" followed by a string of digit characters "0" through "9," preceding any valid sixel-data character (63 to 126 decimal), causes that sixel to be repeated the number of times represented by the decimal string. For example, seven repetitions of the sixel represented by the letter "A" could be transmitted either as AAAAAAA or, using a repeat count, as !7A Obviously, the latter form is shorter; note that there is a tradeoff at 3 or 4 repetitions of a character: sending "!3A" doesn't gain any advantage over sending "AAA", and sending "!2A" would actually LOSE ground. I usually try to implement some sort of "optimization" of this factor, myself, although I've never run into any Sixel-eating device or program that cared WHICH way any of this was sent to it! (Another interesting side note: when the VT240 is commanded to do a "print screen," in ReGIS graphics mode, the resulting dump is in Sixel. Furthermore, the VT240 never specifies a repeat-count greater than 255: if it wants to send 600 repetitions of the "empty" sixel ("?" character), it will send !255?!255?!90 to "build up to" the full 600. I do not know whether this is a limitation of the Sixel format itself, or whether it's just an internal register-size limitation of the VT240...) IV. SIXEL "CONTROL" CHARACTERS The material presented so far covers the serial interpretation of a single horizontal band of sixels: each consecutive sixel is placed to the right of the last, indefinitely. If image data extends to the right (or down, when we get to that) beyond the physical range of the output device (e.g., a band of more than 640 sixels is sent to a VT240 terminal), data that is "out of range" is NOT displayed. Although I've never seen it described in such terms, one can think of a sixel "cursor" - the position where the NEXT sixel will be displayed, analogous to the usual text cursor. What I've described so far is equivalent to a text stream containing no Carriage Return or Linefeed characters: the "cursor" simply keeps moving "linearly" from left to right. But the Sixel format includes two "cursor repositioning" control characters, one which behaves analogous to a carriage return, and another which behaves like a "carriage return, linefeed" pair. The "-" (hyphen or minus sign) character moves the sixel "cursor" to the "beginning of the next line (or band)" of image data. In short, it acts like a "new line" (*NIX lingo) character or, more precisely, like a CR/LF pair would in text mode. The "$" (dollar sign) character moves the sixel "cursor" to the "beginning of the current (same) line (or band)," analogous to a plain CR in text mode. This allows new sixel data to "overstrike" what has already been displayed, a necessity for rendering multiple-color imagery. (Data of a single color is laid down, then a $ character returns the "cursor" to the left, then data for a new color is laid down "on top of" what's already there.) You now know everything you need in order to produce a monochrome, or single-bitplane, Sixel image. You can enter Sixel mode, format image pixels into sixel text characters, reposition the "Sixel cursor" to build up an image, and exit Sixel mode when you're finished. All there is left to cover is COLOR! Color Sixel graphics operate in "color map" fashion: color-selection sequences in the Sixel data stream tell the output device (or program) which of a set of "color map registers" is to be used to display subsequent data; the CONTENTS OF that color map register determine the color actually used for display of the data. The VT240, for instance, offers four color- map registers designated by the numbers 0 through 3; Sixel data can thus be displayed in four colors (including the background color). I read somewhere that the Sixel format imposes a limit of 256 colors (color map registers 0 through 255), but this seems unnecessary: the nature of the format is such that, with appropriate coding, ANY number of color map registers could be supported. On real devices, it appears (in my experience) that if Sixel data specifies a color register beyond the range available on a particular device, the color map number "wraps around," modulo N, where N is the number of available color map registers. (On the VT240, for instance, colors 0 through 3 are available. If Sixel data specifies color 4, color 0 is used; if color 5 is specified, color 1 is used, and so on.) In the Sixel format, the "#" character is the "color sequence introducer." It is followed by a decimal string (string of digit characters in the range "0" through "9") specifying a color-map register, and optionally by four semicolon-separated color-selection parameters. Without parameters, e.g. #3 the sequence means "USE color-map register" (in this example, "use color- map register 3") to display subsequent sixels. WITH parameters, e.g. #3;1;30;40;50 the sequence means "SET color-map register contents." The meaning of the parameters in the "SET" form of the sequence is as follows: Designating the syntax of the sequence as "#n;p1;p2;p3;p4", n = color-map register number to be set p1 = color space to be used: 1 = HLS (Hue, Lightness, Saturation) 2 = RGB (Red, Green, Blue) p2,p3,p4 = color to be used, in the notation specified by p1: HLS: p2 = Hue, as an angle in the range 0 to 360 degrees p3 = Lightness, as a percentage from 0 to 100 p4 = Saturation, as a percentage from 0 to 100 RGB: p2 = Red content, as a percentage from 0 to 100 p3 = Green content, as a percentage from 0 to 100 p4 = Blue content, as a percentage from 0 to 100 A few pointers from my practical experience: 1) On the VT240 at least, color-map numbers as used in Sixel mode appear NOT to bear a fixed relationship to the color-map numbers as used in ReGIS mode. I haven't been able to characterize this, but I CAN say that if I generate a color image in ReGIS graphics, then request a screen dump (which the VT240 performs in Sixel format), then type out that screen dump back to the terminal, the displayed color scheme is often DIFFERENT than the original image: the same COLORS are present, but they are assigned to color MAP REGISTERS in a different order. (If this color-rotated image is then dumped a second time, then this second dumpfile typed out, the colors rotate yet again.) There are also some discrepancies between WHICH color-map registers (as designated in ReGIS) actually receive the particular colors requested in a Sixel data stream. Play with this yourself and see if you can characterize it better than I've been able to; if anyone at DEC reads this who knows the innards of the VT240, I'd be very interested in hearing what the heck goes on. 2) Before you even ask, no, I DON'T know how to convert between "HLS" and "RGB" color representations. So far, I've only made use of the RGB mode in my own programs. 3) Characters outside the recognized range of Sixel data (e.g. characters other than "data" (ASCII 63-126 dec), "#", "!", "-" and "$", are IGNORED if they appear in the Sixel data stream. V. EXAMPLE SIXEL IMAGE Let's look at one very simple Sixel example. Suppose we have a very short Sixel image file that contains the following: <ESC>Pq #0;2;0;0;0#1;2;100;100;0#2;2;0;100;0 #1~~@@vv@@~~@@~~$ #2??}}GG}}??}}??- #1!14@ <ESC>\ First we see that the introductory and termination sequences (lines 1 and 6) are in seven-bit form, and that no parameters are explicitly specified in the introductory sequence. (The absence of parameters means that your device, or software, will use its "default" behavior, whatever that is.) Line 2 sets color-map registers 0, 1, and 2, all in RGB notation. Color 0 is set to the RGB triplet (0,0,0) -- BLACK. Color 1 is set to (100,100,0) -- YELLOW. Color 2 is set to (0,100,0) -- GREEN. Color 3 is unspecified, so will retain whatever setting it had previously. Line 3 supplies, first, the command to "use color (map register) 1," then 14 sixels' worth of image data, then the "$" character which causes a "carriage-return" to the beginning of this SAME line -- meaning that the next data will OVERSTRIKE this line's data. Line 4 performs much as did line 3, except that it specifies color 2, and that it ends with the "-" character, meaning that line 5's data will appear on a new line, rather than being overstruck again. Line 5 specifies 14 sixels of value 1 ("@" = ASCII 64; 64 - 63 = 1), by using a repeat-count rather than 14 individual instances of the same character. If you haven't figured it out yet, this image produces a 14-by-7-pixel yellow rectangle with the word "HI" picked out on it in green! On the VT240, with its pixel aspect ratio of 1:2, this rectangle should appear as a SQUARE, if I haven't screwed up somewhere. Like this: .............. ..**..**..**.. ..**..**..**.. . = color 1, yellow ..******..**.. * = color 2, green ..**..**..**.. ..**..**..**.. .............. This should appear on a black background. I'm afraid it'll be almost too small to see on a VT240 or 340, but you should be at least able to tell that it's there... That about does it for the theory end of it; I'll tack on some source code for your enlightenment and edification, and see if it makes sense to you now... ----- EXAMPLE 1 -- VAX FORTRAN. This example is VAX FORTRAN code to generate a single-bitplane (i.e. mono- chrome) Sixel image data file. An array of 800 by 51 bytes is used as a bitplane for drawing; each byte in this array is treated as a SIX-bit rather than EIGHT-bit quantity, so that no bit-shuffling is needed in order to convert the bitmap to Sixel: that is, each byte's two most-significant bits are unused, leaving each byte to contain only valid-Sixel values (0 to 63 decimal) which can be converted DIRECTLY to Sixel. (If all eight bits of each byte were used, the code to convert to Sixel would have to reorganize the bits into length-six units, taking time and effort I didn't feel like expending.) This fact makes the Sixel-output routine almost trivially simple. For the sake of brevity, I've omitted the code which actually generates the image -- sets bits in the IMAGE array -- but I can send it to anyone who would like to see it. I'm afraid the architecture of this example is pretty poor: the Sixel-output routine receives the "bitmap" array not as an explicit argument, but impli- citly through a COMMON block; the explicit argument to the Sixel-output routine is a buffer that isn't used ANYWHERE else in the main program, and could have been declared locally in the subroutine. Looking back now, so long after writing this, I can only assume that with my limited knowledge of FORTRAN I went for "anything that would WORK," rather than for elegance and efficiency. And now the code: C C IMAGE array is the bitmap (800 horizontal pixels by 51x6=306 vertical C pixels) in which the image is "drawn"; if this bitmap were "display C memory" on a microcomputer or VT240 terminal, the image would appear C immediately as the bits were set... C C BUFFER is a contiguous area of storage big enough to hold the longest C Sixel band that could ever be produced from the IMAGE array. C BYTE IMAGE(0:799,0:50) CHARACTER*800 BUFFER C C For obscure reasons, IMAGE is passed to the DUMPBUFFER Sixel-output C subroutine only by virtue of this COMMON block; BUFFER is the explicit C argument to DUMPBUFFER. Weird. C COMMON /SIXEL/ IMAGE C C ***** C * C * IMAGE-GENERATION AND BIT-SETTING CODE WOULD APPEAR HERE. C * OMITTED FOR BREVITY. C * C ***** C C The following call, although it's hardly obvious, causes the contents of C the bitplane array IMAGE to be output to a data file in Sixel format. C CALL DUMPFILE(BUFFER) C STOP END C C---------------------------------------------------------------------- C Finally, what you've all been waiting for: the Sixel output subroutine! C C Note again that A) the argument architecture here is kind of screwy: C BUFFER is passed explicitly, but IMAGE is passed in a COMMON block, C and B) only SIX bits are used of each byte in IMAGE. C C Output goes to a sequential output file whose name is hardcoded as C "SIXEL.DAT". The file is explicitly given a "record length" capable C of containing in ONE record the longest Sixel band the IMAGE array C could possibly give rise to: 800 characters. C SUBROUTINE DUMPFILE(BUFFER) C CHARACTER*1 BUFFER(0:799) C C TC is a "temporary character" I found necessary for moving data from C the IMAGES array to the BUFFER output buffer. C C X and Y are horizontal and vertical indices, respectively, for retrieval C of data from the IMAGE array. C INTEGER*2 TC INTEGER*2 X,Y C BYTE IMAGE(0:799,0:50) C COMMON /SIXEL/ IMAGE C C Create the output file... C OPEN(UNIT=10,FILE='SIXEL.DAT',RECL=800,ORGANIZATION='SEQUENTIAL', * TYPE='UNKNOWN') C C Sixel output starts with the introducer sequence, as documented 'way back C earlier in this posting: C write (10,*) char(27),'P9q' C C For each BAND (horizontal strip of sixels) in the image -- er, bitmap... C do y=0,50 C C ... and for each SIXEL within each band... C do x=0,799 C C ... do the following: C C Grab the sixel value stored at (X,Y) in the array IMAGE; this is raw C sixel data so we have to add 63 decimal to it to get an ASCII code for C the data file: C tc = image(x,y)+63 C C Stick the resulting ASCII code into the buffer C buffer(x) = char(tc) end do C C ... and when all Sixels of a band have been stuffed there, write out C the buffer! C C NOTE that I made NO attempt to optimize this output: no "color" infor- C mation is written, and NO "REPEAT COUNT" information! This leads to a C VERY INEFFICIENT Sixel data file! I didn't feel like implementing these C features at the time I wrote this code, but if YOU implement it I would C appreciate a copy! C write (10,*) (buffer(x), x=0,799),'-' end do C C Believe it or not, that's all there is to writing the file. A Sixel C data stream ends with the "string terminator" sequence or, in this C case, its seven-bit equivalent: C write (10,*) char(27),'\' close (unit=10) return end ----- EXAMPLE 2 -- AMIGA BASIC. ' (In Amiga BASIC, lines beginning with single quotes (') are COMMENTS.) ' ' The following is a Sixel image viewer written in AmigaBASIC, ' which is Microsoft BASIC with Amiga extensions. It should be ' trivial to port this to the PC, by replacing Amiga-specific ' statements (e.g. MOUSE, SCREEN, and WINDOW) with the equiva- ' lent PC-environment statements. I'll point out statements ' that do particular things, but won't go into detail about what ' they do in the Amiga environment. ' ' This program has been VERY heavily commented for this presentation, ' with the result that it may be almost impossible to read. I suggest ' you edit out all comment-only lines if you just want to read CODE... ' INPUT "File to display: ",fil$ MOUSE OFF ON MOUSE GOSUB quit MOUSE ON OPEN fil$ FOR INPUT AS #1 ' fil$ = fil$ + " -- Left-Click to exit!" ' ' Set up a four-color graphics screen/window ' SCREEN 1,640,240,2,2 WINDOW 2,fil$,,0,1 ' ' Set default colors, corresponding to the VT240 ' immediately after reset ' PALETTE 0,0,0,0 ' color 0 = black PALETTE 1,0,0,1 ' color 1 = blue PALETTE 2,1,0,0 ' color 2 = red PALETTE 3,0,1,0 ' color 3 = green ' ' This version of the program, as-is, will override ' the default colors above if it finds RGB color- ' specification data in the Sixel file. If you want ' an intermediate stage where you, the user, can ' "manually" specify colors, un-comment the following ' section of code. ' 'WINDOW 3,"color setup",(300,20)-(600,80),0,1 'WINDOW OUTPUT 3 'PRINT "Color range = 0 to 100" 'FOR k = 0 TO 3 ' PRINT "Color ";k; : INPUT " (R,G,B) ";r,g,b ' PALETTE k,r/100,g/100,b/100 'NEXT k 'WINDOW CLOSE 3 ' WINDOW OUTPUT 2 ' '==== ' The Sixel-interpretation process can be considered as ' a series of general operations repeated over and over: ' - read a data stream, ignoring record/line boundaries. ' While there is data, do the following. ' - Wait for the Sixel introducer sequence; after ' you have seen it, read the rest of the data ' stream, acting as follows while doing so: ' - if you encounter a Sixel control character -- ' "$" => carriage-return-like ' "-" => newline-like ' "!" => numeric "repeat-count" follows ' "#" => color USE or SET command follows ' ";" => one command-parameter is ended, and ' another follows ' -- perform the defined control operation ' - if you encounter a valid Sixel data character, ' convert it to pixels and plot them in the current ' color at the current position ' - if a character is invalid or out of range, ignore ' it. '==== ' ' Here's where Sixel interpretation starts! I'll note the ' use of variables where they first occur. ' ' X and Y represent the horizontal and vertical position where ' the uppermost pixel of the next sixel (stack of six pixels) will ' be plotted, measured in PIXELS. Since the VT240 begins displaying ' Sixel graphics in its upper left corner, so do we: initialize X and ' Y both to 0. ' x=0 y=0 ' sixelprocess: ' ' The string SIX$ and the index PT are used by the subroutine GETCHAR to ' transparently ignore record/line boundaries in the Sixel input file. ' More on that below... ' pt = 1 six$ = "" ' ' This first data-fetch primes the pump for the main loop. ' GOSUB GETCHAR ' ' C is the number of the color to be used for setting pixels. I don't ' recall just what the VT240 does, but I default to color 1 here. ' c=1 ' ' REPEAT is the "repeat count" used by Sixel to achieve a sort of run- ' length encoding for data-compression. ' repeat = 1 ' ' The following ten statements comprise the main program loop! ' xloop: ' ' I should actually recode this to ignore characters UNTIL an ' ESC P (or DCS) q is seen; for now, the file should START ' with the 7-bit form of the Sixel introducer sequence. When ' the initial ESC is seen, we ignore everything until the ' terminating "q". ' ' BYTE is the character most recently returned by the GETCHAR subroutine. ' IF byte = 27 THEN GOSUB header ' ' If the data is "out of range" for Sixel, ignore it and repeat the loop. ' IF byte < 33 OR byte > 126 THEN GOSUB GETCHAR: GOTO xloop ' ' Since this is the "outermost" level of interpretation, a semicolon is ' not valid here, so we ignore it. ' IF char$ = ";" THEN GOSUB GETCHAR : GOTO xloop ' ' The "#" character indicates color specification or selection to follow, ' so we go into a a subroutine to handle it. Every subroutine invoked ' like this must invoke GETCHAR at least once beyond the data it handles, ' so that the next character will kbe available at this level. ' IF char$ = "#" THEN GOSUB processcolor : GOTO xloop ' ' In exactly the same manner, the "!" indicates that we must go process ' a repeat-count specification. ' IF char$ = "!" THEN GOSUB getrepeat : GOTO xloop ' ' The "-" character says "reposition to the beginning of the NEXT line..." ' IF char$ = "-" THEN GOSUB linefeed : GOTO xloop ' ' The "$" character says "reposition to the beginning of the CURRENT line" ' IF char$ = "$" THEN GOSUB carret : GOTO xloop ' ' If the character is neither "out of range," "out of context," nor a ' "control" character, it must be data -- so we go plot it! ' GOSUB plotbyte GOTO xloop ' '==== ' The rest of the program consists of support routines. '==== ' plotbyte: ' ' This routine plots the current sixel data as a stack of six pixels ' repeated as many times as the immediately preceding repeat-count says, ' or just ONCE if NO repeat-count preceded the data. ' '--- ' First, remove the +63 "bias" that shifts Sixel data into the ' printable character range... this turns the byte into raw binary data. ' byte = byte - 63 ' ' In BASIC, we have to painstakingly loop-and-shift to pick bits out ' of the data byte; this would be easier in almost ANY other language. ' FOR offset = 5 TO 0 STEP -1 ' ' If the high bit was clear, we don't have to set anything and can ' skip some processing ' byte = byte * 2 IF byte < 64 THEN GOTO ploop ' ' The high bit was SET, so we chop it off to prepare for the next iter- ' ation of the loop ' byte = byte - 64 ' ' A pixel multiplied by a repeat-count equals a LINE; note that the Y ' coordinate of the LOWERMOST pixel is offset by the loop counter, so ' that the bits end up stacked. (You could exchange X and Y in this ' program, and display Sixel data SIDEWAYS! Other variations are just ' as easy...) ' LINE(x,y+offset)-(x+repeat-1,y+offset),c ploop: NEXT offset ' ' Horizontal position must advance by the number of pixels just drawn ' x=x+repeat ' ' The repeat-count applies only to the Sixel character immediately ' following it, so we reset it to 1 after using it. ' repeat = 1 ' ' As mentioned in the main loop comments, each subroutine called from ' there must read one character BEYOND what it uses. ' GOSUB GETCHAR RETURN ' '==== ' header: ' ' This routine waits for the Sixel introducer sequence. ' This is pretty naive, but it works. It relies on the ' assumption that we already KNOW we are dealing with a ' Sixel data file, and it just waits til the Sixel intro- ' ducer sequence termination character ("q") is seen. ' GOSUB GETCHAR IF char$ <> "q" THEN GOTO header GOSUB GETCHAR RETURN ' '==== ' processcolor: ' ' This procedure processes the two varieties of color control ' command sequence. ' cloop: ' ' The GETNUM procedure is a jacket routine that comes in ' handy: it calls GETCHAR repeatedly when a numeric string ' is expected, returning the numeric value in NN and the ' first non-numeric character in BYTE. ' ' Here, we're getting the first parameter after the "#" ' control character, which represents "color number:" ' GOSUB getnum c = nn ' ' Just like the VT240, we treat our colors "MODULO 4" ' WHILE c>3 c = c-4 WEND ' ' The "#" character can introduce a color SPECIFICATION or ' color SELECTION command. The former has multiple para- ' meters, the latter does not, so we can tell which is ' which by whether or not we see a parameter separator ' (semicolon (;) ). ' IF char$ = ";" THEN ' ' Arguments mean this is a command to SET the color. ' GOSUB getnum ' Param. 1 ==> color space (RGB or HLS) colorspace = nn ' GOSUB getnum ' Param. 2 ==> RED (channel 1) value chan1 = nn GOSUB getnum ' Param. 3 ==> GREEN (channel 2) value chan2 = nn GOSUB getnum ' Param. 4 ==> BLUE (channel 3) value chan3 = nn ' ' COLORSPACE will be either 1 for HLS, or 2 for RGB. The Amiga, ' fortunately, operates directly in RGB. Ideally, there would be ' code in the "ELSE" clause here to convert HLS to RGB, but I ' don't know how to do it! If anyone reading this knows how, write ' me at Chris_F_Chiesa@cup.portal.com and explain it to me. ' IF colorspace=2 THEN ' ' Comment out the PALETTE statement here to disable interpretation of ' color information fro the sixel file. Useful if file info is WRONG. ' PALETTE c,chan1/100,chan2/100,chan3/100 END IF END IF RETURN ' '==== ' getrepeat: ' ' This procedure reads the numeric string following a "repeat count ' introducer" character ("!"), and converts it to the numeric value of ' REPEAT for use in the PLOTBYTE routine. ' ' R$ is a starter string that we can fall back on if the repeat-count ' turns up damaged or missing ' r$ = "0" ' ' Tack characters onto the end of CHAR$, which is the ASCII string ' form of the character returned by GETCHAR, until we hit a non- ' numeric character which terminates the read. (I apparently wrote ' this routine before implementing the GETNUM routine, and never ' converted it to use GETNUM.) ' gloop: GOSUB GETCHAR n$ = char$ IF n$>="0" AND n$<="9" THEN r$=r$+n$ : GOTO gloop ' ' Once we have a numeric string, BASIC makes it easy to convert it to ' a truly numeric value... ' repeat = VAL(r$) ' ' If no numeric characters were seen, or if the repeat-count was ' specified as 0, we assume it should really be 1. ' IF repeat = 0 THEN repeat = 1 RETURN linefeed: ' ' This procedure performs a "graphic linefeed" -- repositions us to ' the "beginning of the next Sixel band." Simple - just hack X and Y. ' x=0 y=y+6 ' ' I apparently had in mind to disable plotting when I went off the ' bottom of the screen, but I don't think I use this anywhere... ' IF y>ymax THEN plotenable = 0 GOSUB GETCHAR RETURN ' '==== ' carret: ' ' This procedure performs a "graphic carriage-return" -- repositions us ' to the "beginning of the current Sixel band." Simple - just hack X. ' Note that the "plot enable" flag shows up here too! ' x=0 : IF y<= ymax THEN plotenable=1 GOSUB GETCHAR RETURN getnum: ' ' Read a number from the string data stream ' n$ = "" ' return numeric string value n$, nn = 0 ' numeric value nn, & terminator char$/byte GOSUB GETCHAR ' get new "char$" and "byte" WHILE char$ <= "9" AND char$ >= "0" n$ = n$ + char$ GOSUB GETCHAR WEND IF n$ <> "" THEN nn = VAL(n$) RETURN GETCHAR: ' ' Read a character from the string data stream ' ' Have we run out of data in the most-recently-read record/line? ' IF pt > LEN(six$) THEN ' ' Yes: reset the index and try to read another line -- ' pt = 1 IF EOF(1) THEN ' -- unless we're at end of file GOTO done ELSE six$="" ' ' If NOT at end-of-file, read until we get a non-empty record. ' WHILE LEN(six$)<1 INPUT #1,six$ WEND END IF END IF ' ' We get here with valid data at position PT in string SIX$. ' char$= MID$(six$,pt,1) ' Extract ASCII character, byte = ASC(char$) ' return it also as an integer pt = pt+1 ' and advance the index ' RETURN PRINT "This should never be printed!" ' ' The origins of the following tidbits are lost in the mists of ' time... ' done: CLOSE #1 ' waitpt: SLEEP GOTO waitpt ' ' We come here when the left mouse button is pressed, closing ' and shutting stuff down. ' quit: PRINT "Closing!" WINDOW CLOSE 2 SCREEN CLOSE 1 ' END /* ---------- */