ba0k+@andrew.cmu.edu (Brian Patrick Arnold) (05/17/91)
Howdy, due to popular demand, here be solutions given to me for drawing rotated text. The most general solution was in: "Mactutor V4-N2-p62 and V4-N11-p86 were the article and its followup." The Pascal code in that article by Kelly King includes nicely optimized and therefore from my perspective unmaintainable assembler code as well. It also has some big phrase using the capitalized word EXPRESS followed by "permission of the author" which made me queasy about using it. It does however know how to rotate a bitmap up, down, backwards and flip vertical and horizontal. I also received some C code that can similarly rotate, flip etc. for text strings only, with a bonus that it also knows how to generate the appropriate PostScript PicComments for the LaserWriter. If Michael doesn't mind, I'll drop his name for anyone interested in his C code for doing this: that's Michael Hecht, hecht@sas.com. Since I am a lazy Pascal programmer, I chose to use "a hack" sent by Jim Amundson, which was suprisingly the most useable "out of the box". All it does is draw text rotated 90 degrees up from the current pen position. This is entirely in Pascal and I spent some time making it readable, although it isn't optimized. As Jim says, "it is simple at the price of efficiency" which is exactly what I was looking for. For the population of lazy programmers "just like me", here is "tha" code for drawing rotated text. ---- (* RotateBits and DrawStringUp: Copyright 1991 Jim Amundson and Brian Arnold All Rights Reserved, except: Feel free to distribute and use it WITHOUT permission of the authors, as long as you don't pocket any money in the process (duhh, no we don't want royalties from commercial apps that use this algo, but don't go charging people to use this either). Retain the copyright notice in your source code and contact us if you have questions! amun@midway.uchicago.edu ba0k@andrew.cmu.edu *) TYPE MaskArray = ARRAY[1..16] OF LongInt; BitArray = ARRAY[0..16000] OF INTEGER; BitPtr = ^BitArray; PROCEDURE InitMask( VAR masks: MaskArray ); (* Set "on" bits for mask LongInts *) VAR i: INTEGER; BEGIN masks[1] := 1; FOR i := 2 TO 16 DO masks[i] := masks[i - 1] * 2; END; PROCEDURE RotateBits( srcMap: BitMap; VAR dstMap: BitMap ); (* Turn srcMap bitmap counter-clockwise by 90 degrees *) VAR srcBits, (* Typecast baseAddrs to peek *) dstBits: BitPtr; (* and poke indexed Integer values *) dstIndex, srcIndex, dstWidth, srcWidth, dstHeight, srcHeight, whichMask, i, j, k: INTEGER; curMask, rawBits: LongInt; masks: MaskArray; BEGIN InitMask( masks ); srcBits := BitPtr( srcMap.baseAddr ); srcHeight := srcMap.bounds.bottom - srcMap.bounds.top; (* Number of integers in a row *) srcWidth := srcMap.rowBytes DIV 2; dstBits := BitPtr( dstMap.baseAddr ); dstHeight := srcWidth * 16; dstMap.rowBytes := ( srcHeight - 1 ) DIV 8 + 1; dstWidth := dstMap.rowBytes DIV 2; (* Set dstMapination rect *) WITH srcMap.bounds DO SetRect( dstMap.bounds, left, top, left + srcHeight, top + dstHeight ); dstIndex := 0; (* Walk across rows of dst *) FOR i := 0 TO dstHeight - 1 DO BEGIN srcIndex := srcWidth - 1 - i DIV 16; whichMask := i MOD 16 + 1; curMask := masks[whichMask]; (* walk across cols of dst *) FOR j := 1 TO dstWidth DO BEGIN rawBits := 0; (* set bits for this row/col *) FOR k := 16 DOWNTO 1 DO BEGIN rawBits := rawBits + BitShift( BitAnd( curMask, srcBits^[srcIndex] ), k - whichMask ); srcIndex := srcIndex + srcWidth; END; { FOR k } (* Poke in the bits *) dstBits^[dstIndex] := LoWord( rawBits ); dstIndex := dstIndex + 1; END; { FOR j } END; { FOR i } END; PROCEDURE DrawStringUp( theStr: Str255 ); (* Draw a string from current pen loc rotated counterclockwise 90 degrees *) LABEL 999; (* Exit label in case of error *) VAR offScreenAcross: GrafPort; rotatedBits: BitMap; actualPort: GrafPtr; theInfo: FontInfo; srcRect, dstRect: Rect; pnLoc: Point; PROCEDURE HdlFailure; (* In case of memory failure, set port back and jump to end *) BEGIN SetPort( actualPort ); GOTO 999; END; BEGIN GetPort( actualPort ); (* Open port and set font characteristics to actualPort *) OpenPort( @offScreenAcross ); TextFont( actualPort^.txFont ); TextSize( actualPort^.txSize ); TextFace( actualPort^.txFace ); SetPort( actualPort ); WITH offScreenAcross, portBits, bounds DO BEGIN (* make bitmap exactly the size of the port *) portRect.left := 0; portRect.right := StringWidth( theStr ); portRect.top := 0; GetFontInfo( theInfo ); portRect.bottom := theInfo.ascent + theInfo.descent + theInfo.leading; bounds := portRect; (* rowBytes is size of row rounded up to an even number of bytes *) rowBytes := ( ( ( right - left ) + 15 ) DIV 16 ) * 2; (* number of bytes in BitMap is rowBytes * number of rows this calculation must be done with longs *) baseAddr := NewPtr( rowBytes * LongInt( bottom - top ) ); (* check MemError to see if we had enough room for the bitmap *) IF MemError <> noErr THEN HdlFailure; END; SetPort( @offScreenAcross ); (* since it's just an area of memory, clear it before we start *) EraseRect( thePort^.portRect ); Moveto( 0, theInfo.ascent ); DrawString( theStr ); WITH offscreenAcross, portBits, bounds DO rotatedBits.baseAddr := NewPtr( rowBytes * LongInt( bottom - top ) * 2 ); IF MemError <> noErr THEN HdlFailure; RotateBits( offScreenAcross.portBits, rotatedBits ); SetPort( actualPort ); getpen( pnLoc ); WITH rotatedBits.bounds DO BEGIN SetRect( srcRect, top, bottom - StringWidth( theStr ), right, bottom ); SetRect( dstRect, pnLoc.h - ( right - left ) + theInfo.descent, pnLoc.v - StringWidth( theStr ), pnLoc.h + theInfo.descent, pnLoc.v ); END; CopyBits( rotatedBits, thePort^.portBits, srcRect, dstRect, srcCopy, NIL ); DisposPtr( rotatedBits.baseAddr ); 999: ClosePort( @offScreenAcross ); END; ----- - Brian