home *** CD-ROM | disk | FTP | other *** search
/ Carousel Volume 2 #1 / carousel.iso / mactosh / code / p_dissbi.sit / DissBits / DissBits3.a next >
Encoding:
Text File  |  1984-12-28  |  23.5 KB  |  604 lines  |  [TEXT/MACA]

  1. ;
  2. ;  procedure dissBits (srcBits, dstBits: bitMap; srcRect, dstRect: rect); external;
  3. ;
  4. ; mike morton
  5. ; release: 11 november 1984
  6. ;
  7. ; this is the third version.  future versions will probably have fewer bugs.
  8. ; it's been tested fairly intensively, but by only a few programs.  to see
  9. ; if a bug is yours or mine, switch to copyBits and see if it works.
  10. ;
  11. ; differences from version 2 are:
  12. ;       documentation improved and neatened
  13. ;       log2 routine rewritten
  14. ;
  15. ; comments and suggestions are, of course, welcome.
  16. ;
  17. ; ******************************************************************************
  18. ; *                                           *
  19. ; *               copyright 1984 by michael s. morton               *
  20. ; *    please see details below on using, copying and changing this source.    *
  21. ; *                                           *
  22. ; ******************************************************************************
  23. ;
  24. ; what this routine does:
  25. ; ----------------------
  26. ;
  27. ; dissBits is like copyBits: it moves one rectangle to another, in their respective
  28. ; bitMaps.  it doesn't implement the modes of copyBits, nor clipping to a region.
  29. ; what it DOES do is copy the bits in a pseudo-random order, giving the appearance
  30. ; of "dissolving" from one image to another.  the dissolve is rapid: the entire
  31. ; screen will dissolve in about eight seconds.
  32. ;
  33. ; it may well be that copyBits has knowledge of QuickDraw databases, and clips to
  34. ; avoid overwriting other windows.  be aware that this routine does no such thing,
  35. ; although clipping could be added.
  36. ;
  37. ; other likely differences from copyBits:
  38. ;     o     the rectangles must have the same extents (not necessarily the same lrbt).
  39. ;     if they are not, the routine will return -- doing nothing!  no stretching
  40. ;     move is done as copyBits would.
  41. ;     o     the cursor is hidden during the dissolve, since drawing is done without
  42. ;     quickdraw calls.  the cursor reappears when the drawing is finished.  for
  43. ;     an odd effect, try changing it not to hide the cursor; is this how bill
  44. ;     atkinson thought of the spray can in MacPaint?
  45. ;     o     copyBits may be smart enough to deal with overlapping areas of memory.
  46. ;     this routine certainly isn't.
  47. ;     o     i may have misunderstood how to interpret rectangle bounds in quickdraw.
  48. ;     if your rectangles are off by a pixel, let me know.
  49. ;
  50. ; you should know a few implementation details which may help:
  51. ;     o     copying from a dark area (lots of 1 bits) is faster than from a light area.
  52. ;     but not a whole lot.  (about three per cent, i think.)
  53. ;     o     the closer your rectangle width is to a power of two (without exceeding
  54. ;     it), the faster the drawing will occur per pixel.  for an easy way to do
  55. ;     this, use bitmaps whose bitmap.bounds' match those of the screen.
  56. ;     o     there is no way to use this to randomly invert a rectangle.  instead,
  57. ;     copyBits it elsewhere, invert it, and dissBits it back into place.
  58. ;     o     there is also no way to slow the dissolve of a small area.  to do this,
  59. ;     copy a large area in which the only difference is the area to change.
  60. ;     o     if you fade in a solid area, you're likely to see patterns, since the
  61. ;     random numbers are so cheesy.  don't do this; fade in nifty patterns
  62. ;     which will distract your viewers.
  63. ;
  64. ; sample calling code:
  65. ; -------------------
  66. ;
  67. ; this is an excerpt from how a prerelease of DarTerminal called this routine.
  68. ; i make no claim to understand it.  note the clever use of "paintbehind".  this
  69. ; took about 3 seconds to dissolve onto the screen.
  70. ;
  71. ;var   rg:      rgnhandle;                (* window to copy into *)
  72. ;      aport:   grafptr;                (* port to draw into *)
  73. ;      bits:    bitmap;                    (* new bitmap for that port *)
  74. ;      r:       rect;                    (* rectangle to draw into *)
  75. ;      pat:     pattern;
  76. ;      text:    packed array[1..37] of char;
  77. ; ...
  78. ;   aport := grafptr(newptr(sizeof(grafport))); (* get a port *)
  79. ;   openport(aport);                    (* make it current *)
  80. ;
  81. ;   r := theport^.portbits.bounds;            (* start with the whole screen *)
  82. ;   insetrect(r,100,100);                (* get rect the size of the window *)
  83. ;   (* note that the number of bytes per row must be even! *)
  84. ;   bits.rowbytes := (((r.right-r.left)+15) div 16) * 2; (* bytes per row *)
  85. ;   bits.baseaddr := qdptr(newptr(bits.rowbytes*(r.bottom-r.top))); (* get bitmap *)
  86. ;   bits.bounds := r;                    (* set boundary *)
  87. ;
  88. ;   setportbits(bits);                    (* make that new bitmap current *)
  89. ;
  90. ;   eraserect(r);
  91. ;   textfont(london); textsize(18); textface([bold]);
  92. ;   text := 'DarTerminal version -1.9  August 1984';
  93. ;   textbox(@text,37,r,tejustcenter);
  94. ;
  95. ;   dissbits(bits,screenport^.portbits,r,r);    (* dissolve it in *)
  96. ;
  97. ;   repeat until getnextevent(mdownmask+keydownmask,anevent); (* let user marvel *)
  98. ;
  99. ;   rg := newrgn;                    (* get a region to clip with *)
  100. ;   rectrgn(rg,r);                    (* as a rectangle *)
  101. ;   paintbehind(windowpeek(frontwindow),rg);
  102. ;
  103. ;   disposergn(rg);
  104. ;   disposptr(ptr(bits.baseaddr));
  105. ;   disposptr(ptr(aport));
  106. ;
  107. ;
  108. ; calling from languages other than pascal:
  109. ; ----------------------------------------
  110. ;
  111. ; i suspect any reasonable language will stack arguments the way Lisa Pascal does.
  112. ; but it would be smart to check first.     if you try to call this routine, with or
  113. ; without changing it, from any language, let me know how it works.
  114. ;
  115. ; duplication and use of this routine:
  116. ; -----------------------------------
  117. ;
  118. ; this is freeware.  you're welcome to copy it and use it in programs.  you're
  119. ; welcome to modify it, as long as you leave everything up until this section
  120. ; unchanged.  i'd be very interested in seeing your changes, especially if you find
  121. ; a way to make the central loop faster.
  122. ;
  123. ; if you use it for profit, i ask that you pay me for my work.  why?
  124. ;
  125. ;       o if you have problems using it, i'll try to help you debug it.
  126. ;       o i'll send you improved, debugged, faster versions, if they happen.
  127. ;       o i'll tell you about future products.  this is the first thing i
  128. ;      wrote for the Mac; wouldn't you like to see what i produce when I
  129. ;      REALLY get going?  send me some positive feedback!
  130. ;
  131. ; how much should you pay?  my suggestion is:
  132. ;       (cost of one copy of the program) * (log10 of number of copies sold)
  133. ; if the subroutine is an integral part of your program, double the amount.
  134. ; if it's a frill (e.g., you dissolve in your "About MacWhatever"), halve it.
  135. ;
  136. ; i find it hard to believe that any damages to you or anyone else could come from
  137. ; bugs in this routine.     but, alas, whether or not you pay me, alas, i can't be
  138. ; liable in any way for any problems in it.
  139. ;
  140. ; send comments, contributions, criticisms, or whatever to:
  141. ;       mike morton
  142. ;       c/o teradyne, inc.
  143. ;       321 harrison ave., mail stop 71
  144. ;       boston, mass.  02118
  145. ;
  146. ; if, for some reason, you only have a hard copy of this and would like a source on
  147. ; a diskette, please contact:
  148. ;       robert hafer
  149. ;       the boston computer society
  150. ;       one center plaza
  151. ;       boston, mass.  02108
  152. ;
  153.  
  154. ;
  155. ;       -- end of introduction; real stuff starts here --
  156. ;
  157.  
  158. ;
  159. ; things left to do:
  160. ; -----------------
  161. ;
  162. ; clean up register usage (sigh)
  163. ; think about optimizing cases where some dimensions are powers of two, or where
  164. ;       the two bitmaps have the same stride, or...
  165. ; do real error handling (how do toolbox routines do it?)
  166. ;
  167.  
  168. ;
  169. ; include files:
  170. ;       tlasm/graftypes -- definitions of "bitMap" and "rect"
  171. ;       tlasm/quickmacs -- macros for quickdraw calls (e.g., _hidecursor)
  172. ;
  173.  
  174. .nolist
  175. .include tlasm/graftypes
  176. .include tlasm/quickmacs
  177. .list
  178.  
  179. ;
  180. ; definitions of the "ours" record: this structure, of which there are two copies in
  181. ; our stack frame, is a sort of bitmap:
  182. ;
  183.  
  184. oRows   .equ 0                    ; (word) number of last row (first is 0)
  185. oCols   .equ oRows+2                ; (word) number of last column (first is 0)
  186. oLbits  .equ oCols+2                ; (word) size of left margin within 1st byte
  187. oStride .equ oLbits+2                ; (word) stride in memory from row to row
  188. oBase   .equ oStride+2                ; (long) base address of bitmap
  189.  
  190. osize   .equ oBase+4                ; size, in bytes, of "ours" record
  191.  
  192. ;
  193. ; stack frame elements:
  194. ;
  195.  
  196. srcOurs .equ -osize                ; (osize) our view of source bits
  197. dstOurs .equ srcOurs-osize            ; (osize) our view of target bits
  198.  
  199. sflast  .equ dstOurs                ; relative address of last s.f. member
  200. sfsize  .equ -sflast                ; size of s.f. for LINK (must be EVEN!)
  201. ;
  202. ;       parameter offsets from the stack frame pointer, A6:
  203. ;       last parameter is above return address and old s.f.
  204. ;
  205.  
  206. dRptr   .equ 4+4                ; ^destination rectangle
  207. sRptr   .equ dRptr+4                ; ^source rectangle
  208. dBptr   .equ sRptr+4                ; ^destination bitMap
  209. sBptr   .equ dBptr+4                ; ^source bitMap
  210.  
  211. plast   .equ sBptr+4                ; address just past last parameter
  212.  
  213. psize   .equ plast-dRptr            ; size of parameters, in bytes
  214.  
  215. ;
  216. ; entrance: set up a stack frame, save some registers, hide the cursor.
  217. ;
  218.  
  219. .proc   dissBits
  220.  
  221.         link A6,#-sfsize            ; set up a stack frame
  222.         movem.l D3-D7/A2-A5,-(A7)       ; save registers compiler may need
  223.         _hidecurs                ; don't let the cursor show for now
  224.  
  225. ;
  226. ; convert the source and destination bitmaps and rectangles to a format we prefer.
  227. ; we won't look at these parameters after this.
  228. ;
  229.  
  230.         move.l sBptr(A6),A0            ; point to source bitMap
  231.         move.l sRptr(A6),A1            ; and source rectangle
  232.         lea srcOurs(A6),A2            ; and our source structure
  233.         bsr CONVERT                ; convert to our format
  234.  
  235.         move.l dBptr(A6),A0            ; point to destination bitMap
  236.         move.l dRptr(A6),A1            ; and rectangle
  237.         lea dstOurs(A6),A2            ; and our structure
  238.         bsr CONVERT                ; convert to our format
  239.  
  240. ;
  241. ; check that the rectangles match in size.
  242. ;
  243.         move.w srcOurs+oRows(A6),D0     ; pick up the number of rows
  244.         cmp.w dstOurs+oRows(A6),D0      ; same number of rows?
  245.         bne ERROR                ; nope -- bag it
  246.  
  247.         move.w srcOurs+oCols(A6),D0     ; check the number of columns
  248.         cmp.w dstOurs+oCols(A6),D0      ; same number of columns, too?
  249.         bne ERROR                ; that's a bozo no-no
  250.  
  251. ;
  252. ; figure the bit-width needed to span the columns, and the rows.
  253. ;
  254.  
  255.         move.w srcOurs+oCols(A6),D0     ; get count of columns
  256.         ext.l D0                ; make it a longword
  257.         bsr LOG2                ; figure bit-width
  258.         move.w D0,D1                ; save that result
  259.  
  260.         move.w srcOurs+oRows(A6),D0     ; get count of rows
  261.         ext.l D0                ; make it a longword
  262.         bsr LOG2                ; again, find the bit-width
  263.  
  264. ;
  265. ; set up various constants we'll need in the in the innermost loop
  266. ;
  267.  
  268.         move.l #1,D5                ; set up...
  269.         lsl.l D1,D5                ; ...the bit mask which is...
  270.         sub.l #1,D5                ; ...bit-width (cols) 1's
  271.  
  272.         add.w D1,D0                ; find total bit-width (rows plus columns)
  273.         lsl.w #2,D0                ; make the stride right [sic?] (longwords)
  274.         lea TABLE,A0                ; point to the table of XOR masks
  275.         move.l 0(A0,D0),D3            ; grab the correct XOR mask in D3
  276.  
  277.         move.l D3,D0                ; 1st sequence element is the mask itself
  278.  
  279.         move.l srcOurs+oBase(A6),D2     ; set up base pointer for our source bits
  280.         lsl.l #3,D2                ; make it into a bit address
  281.         move.l D2,A0                ; put it where the fast loop will use it
  282.         move.w srcOurs+oLbits(A6),D2    ; now pick up source left margin
  283.         ext.l D2                ; make it a longword
  284.         add.l D2,A0                ; and make A0 useful for odd routine below
  285.  
  286.         move.l dstOurs+oBase(A6),D2     ; set up base pointer for target
  287.         lsl.l #3,D2                ; again, bit addressing works out faster
  288.         move.l D2,A1                ; stuff it where we want it for the loop
  289.         move.w dstOurs+oLbits(A6),D2    ; now pick up destination left margin
  290.         ext.l D2                ; make it a longword
  291.         add.l D2,A1                ; and make A1 useful, too
  292.  
  293.         move.w srcOurs+oCols(A6),A2     ; pick up the often-used count of columns
  294.         move.w srcOurs+oRows(A6),D2     ; and of rows
  295.         add.w #1,D2                ; make row count one-too-high for compares
  296.         ext.l D2                ; and make it a longword
  297.         lsl.l D1,D2                ; slide it to line up w/rows part of D0
  298.         move.l D2,A4                ; and save that somewhere useful
  299.  
  300.         move.w D1,D2                ; put log2(columns) in a safe place (sigh)
  301.  
  302. ;
  303. ; try to reduce the amount we shift down D2.  this involves:
  304. ;    halving the strides as long as each is even, decrementing D2 as we go
  305. ;    masking the bottom bits off D4 when we extract the row count in the loop
  306. ;
  307. ; alas, we can't always shift as little as we want.  for instance, if we don't
  308. ; shift down far enough, the row count will be so high as to exceed a halfword,
  309. ; and the dread mulu instruction won't work (it eats only word operands).  so,
  310. ; we have to have an extra check to take us out of the loop early.
  311. ;
  312.  
  313.         move.w srcOurs+oStride(A6),D4   ; pick up source stride
  314.         move.w dstOurs+oStride(A6),D7   ; and target stride
  315.         move.w srcOurs+oRows(A6),D1     ; pick up row count for kludgey check
  316.  
  317.         tst.w D2                ; how's the bitcount?
  318.         beq HALFDONE                ; skip out if already down to zero
  319.  
  320. HALFLOOP
  321.         btst #0,D4                ; is this stride even?
  322.         bne HALFDONE                ; nope -- our work here is done
  323.         btst #0,D7                ; how about this one?
  324.         bne HALFDONE                ; have to have both even
  325.  
  326.         lsl.w #1,D1                ; can we keep max row number in a halfword?
  327.         bcs HALFDONE                ; nope -- D2 mustn't get any smaller!
  328.  
  329.         lsr.w #1,D4                ; halve each stride...
  330.         lsr.w #1,D7                ; ...like this
  331.         sub.w #1,D2                ; and remember not to shift down as far
  332.         bne.s HALFLOOP                ; loop unless we're down to no shift at all
  333.  
  334. HALFDONE                    ; no tacky platitudes, please
  335.         move.w D4,srcOurs+oStride(A6)   ; put back source stride
  336.         move.w D7,dstOurs+oStride(A6)   ; and target stride
  337.  
  338. ;
  339. ; make some stuff faster to access -- use the fact that (An) is faster to access
  340. ; than d(An).  this means we'll misuse our frame pointer, but don't worry -- we'll
  341. ; restore it before we use it again.
  342. ;
  343.  
  344.         move.l A6,-(A7)                ; save framitz
  345.         move.w srcOurs+oStride(A6),-(A7) ; make this faster to access, too
  346.         lea dstOurs+oStride(A6),A6      ; point to target stride with no offset
  347.  
  348. ;
  349. ; main loop: map the sequence element into rows and columns, check if it's in bounds
  350. ; and skip on if it's not, flip the appropriate bit, generate the next element in the
  351. ; sequence, and loop if the sequence isn't done.
  352. ;
  353.  
  354. ;
  355. ; check the row bounds.     note that we can check the row before extracting it from
  356. ; D0, ignoring the bits at the bottom of D0 for the columns.  to get these bits
  357. ; to be ignored, we had to make A4 one-too-high before shifting it up this far.
  358. ;
  359.  
  360. LOOP                        ; here for another time around
  361.         cmp.l A4,D0                ; is row in bounds?
  362.         bge.s NEXT                ; no: clip this
  363.  
  364. ;
  365. ; map it into the column; check bounds.     note that we save this check for second;
  366. ; it's a little slower because of the move and mask.
  367. ;
  368. ; chuck sagely points out that when the "bhi" at the end of the loop takes, we
  369. ; know we can ignore the above comparison.  thanks, chuck.  you're a great guy.
  370. ;
  371.  
  372. LOOPROW                        ; here when we know the row number is OK
  373.         move.w D0,D6                ; copy the sequence element
  374.         and.l D5,D6                ; find just the column number
  375.  
  376.         cmp.w A2,D6                ; too far to the right? (past oCols?)
  377.         bgt.s NEXT                ; yes: skip out
  378.  
  379.         move.l D0,D4                ; we know element will be used; copy it
  380.         sub.w D6,D4                ; remove column's bits
  381.         lsr.l D2,D4                ; shift down to row, not right-justified
  382.  
  383. ;
  384. ; get the source byte, and bit offset.  D4 has the bit offset in rows, and
  385. ; D6 is columns.
  386. ;
  387.  
  388.         move.w (A7),D1                ; get the stride per row (in bits)
  389.         mulu D4,D1                ; * stride; find source row's offset in bits
  390.         add.l D6,D1                ; add in column offset (bits)
  391.         add.l A0,D1                ; plus base of bitmap (bits [sic])
  392.         move.b D1,D7                ; save the bottom three bits for the BTST
  393.         lsr.l #3,D1                ; while we shift down to a word address
  394.         move.l D1,A3                ; and save that for the test, too
  395.  
  396.         not.b D7                ; get right bit number (compute #7-D7)
  397.  
  398. ;
  399. ; find the destination bit address and bit offset
  400. ;
  401.  
  402.         mulu (A6),D4                ; * stride; find dest row's offset in bits
  403.         add.l D6,D4                ; add in column bit offset
  404.         add.l A1,D4                ; and base address, also in bits
  405.         move.b D4,D6                ; set aside the bit displacement
  406.         lsr.l #3,D4                ; make a byte displacement
  407.         move.l D4,A5                ; stick it somewhere useful
  408.  
  409.         not.b D6                ; get right bit number (compute #7-D6)
  410.  
  411.         bset D6,(A5)                ; assume we should set destination on
  412.         btst D7,(A3)                ; test the D7th bit of source byte
  413.         bne.s NEXT                ; skip if we assumed right
  414.         bclr D6,(A5)                ; oops -- blew it; set destination off
  415.  
  416. ;
  417. ; find the next sequence element.  see knuth, vol ii., page 29 for sketchy details.
  418. ;
  419.  
  420. NEXT
  421.         lsr.l #1,D0                ; slide one bit to the right
  422.         bhi.s LOOPROW                ; if no carry out, but not zero, loop
  423.         eor.l D3,D0                ; flip magic bits...
  424.         cmp.l D3,D0                ; ...but has this brought us to square 1?
  425.         bne.s LOOP                ; if not, loop back; else fall through
  426.  
  427. ;
  428. ; here when we're done; the (0,0) point may not have been done yet.  this is
  429. ; really the (0,left margin) point.
  430. ;
  431.  
  432. DONE
  433.         tst.w (A7)+                ; pop kludgey stack temp which sped us up
  434.         move.l (A7)+,A6                ; and restore stack frame pointer
  435.  
  436.         move.l srcOurs+oBase(A6),A0     ; set up base pointer for our source bits
  437.         move.l dstOurs+oBase(A6),A1     ; and pointer for target
  438.  
  439.         move.w srcOurs+oLbits(A6),D0    ; pick up bit offset of left margin
  440.         move.w dstOurs+oLbits(A6),D1    ; and ditto for target
  441.         not.b D0                ; flip to number the bits for 68000
  442.         not.b D1                ; ditto
  443.         bset D1,(A1)                ; assume source bit was on; set target
  444.         btst D0,(A0)                ; was first bit of source on?
  445.         bne DONE2                ; yes: skip out
  446.         bclr D1,(A1)                ; no: oops!  set it right, and fall through
  447.  
  448. ;
  449. ; return
  450. ;
  451.  
  452. DONE2                        ; here when we're really done
  453. ERROR                        ; we return silently on errors
  454.         _showcurs                ; let's see this again
  455.         movem.l (A7)+,D3-D7/A2-A5       ; restore lots of registers
  456.         unlk A6                    ; restore caller's stack frame pointer
  457.         move.l (A7)+,A0                ; pop return address
  458.         add.l #psize,A7                ; unstack parameters
  459.         jmp (A0)                ; home to mother
  460.  
  461. ;
  462. ; -----------------------------------------------------------------------------------
  463. ;
  464. ; table of (longword) masks to XOR in strange Knuthian algorithm.  the first table
  465. ; entry is for a bit-width of two, so the table actually starts two longwords before
  466. ; that.     hardware jocks among you may recognize this scheme as the software analog
  467. ; of a "maximum-length sequence generator".
  468. ;
  469.  
  470. table   .equ *-8                ; first element is #2; stride is four bytes
  471. .long   3o                    ; 2
  472. .long   6o                    ; 3
  473. .long   14o                    ; 4
  474. .long   24o                    ; 5
  475. .long   60o                    ; 6
  476. .long   140o                    ; 7
  477. .long   270o                    ; 8
  478. .long   420o                    ; 9
  479. .long   1100o                    ; 10
  480. .long   2400o                    ; 11
  481. .long   6240o                    ; 12
  482. .long   15400o                    ; 13
  483. .long   32400o                    ; 14
  484. .long   60000o                    ; 15
  485. .long   132000o                    ; 16
  486. .long   220000o                    ; 17
  487. .long   402000o                    ; 18
  488. .long   1620000o                ; 19
  489. .long   2200000o                ; 20
  490. .long   5000000o                ; 21
  491. .long   14000000o                ; 22
  492. .long   20400000o                ; 23
  493. .long   66000000o                ; 24
  494. .long   110000000o                ; 25
  495. .long   342000000o                ; 26
  496. .long   710000000o                ; 27
  497. .long   1100000000o                ; 28
  498. .long   2400000000o                ; 29
  499. .long   6240000000o                ; 30
  500. .long   11000000000o                ; 31
  501. .long   24300000000o                ; 32
  502.  
  503. ;
  504. ; -----------------------------------------------------------------------------------
  505. ;
  506. ; convert -- convert a parameter bitMap and rectangle to our internal form.
  507. ;
  508. ; calling sequence:
  509. ;       lea bitMap,A0                ; point to the bitmap
  510. ;       lea rect,A1                ; and the rectangle inside it
  511. ;       lea ours,A2                ; and our data structure
  512. ;       bsr CONVERT                ; call us
  513. ;
  514. ; when done, all fields of the "ours" structure are filled in:
  515. ;       oBase is the address of the first byte in which any bits are to be changed
  516. ;       oLbits is the number of bits into that first byte which are ignored
  517. ;       oStride is the stride from one row to the next, in bits
  518. ;       oCols is the number of columns in the rectangle
  519. ;       oRows is the number of rows
  520. ;
  521. ; registers used: D0, D1, D2
  522. ;
  523.  
  524. CONVERT
  525.  
  526. ;
  527. ; save the starting word and bit address of the stuff:
  528. ;
  529.        move.w top(A1),D0            ; pick up top of inner rectangle
  530.        sub.w bounds+top(A0),D0            ; figure rows to skip within bitmap
  531.        mulu rowbytes(A0),D0            ; compute bytes to skip (relative offset)
  532.  
  533.        add.l baseaddr(A0),D0            ; find absolute address of first row to use
  534.  
  535.        move.w left(A1),D1            ; pick up left coordinate of inner rect
  536.        sub.w bounds+left(A0),D1            ; find columns to skip
  537.        move.w D1,D2                ; copy that
  538.        and.w #7,D2                ; compute bits to skip in first byte
  539.        move.w D2,oLbits(A2)            ; save that in the structure
  540.  
  541.        lsr.w #3,D1                ; convert column count from bits to bytes
  542.        ext.l D1                    ; convert to a long value, so we can...
  543.        add.l D1,D0                ; add to row start in bitmap to find 1st byte
  544.        move.l D0,oBase(A2)            ; save that in the structure
  545.  
  546. ;
  547. ; save the stride of the bitmap; this is the same as for the original, but in bits.
  548. ;
  549.        move.w rowbytes(A0),D0            ; pick up the stride
  550.        lsl.w #3,D0                ; multiply by eight to get a bit stride
  551.        move.w D0,oStride(A2)            ; stick it in the target structure
  552.  
  553. ;
  554. ; save the number of rows and columns.
  555. ;
  556.         move.w bottom(A1),D0            ; get the bottom of the rectangle
  557.         sub.w top(A1),D0            ; less the top coordinate
  558.         sub.w #1,D0                ; get number of highest row (1st is zero)
  559.         bmi CERROR                ; nothing to do?  (note: 0 IS ok)
  560.         move.w D0,oRows(A2);            ; save that in the structure
  561.  
  562.         move.w right(A1),D0            ; get the right edge of the rectangle
  563.         sub.w left(A1),D0            ; less the left coordinate
  564.         sub.w #1,D0                ; make it zero-based
  565.         bmi CERROR                ; nothing to do here?
  566.         move.w D0,oCols(A2)            ; save that in the structure
  567.  
  568. ;
  569. ; all done.  return.
  570. ;
  571.         rts
  572.  
  573. ;
  574. ; error found in CONVERT.  pop return and jump to the error routine, such as it is.
  575. ;
  576. CERROR
  577.         tst.l (A7)+                ; pop four bytes or return address.
  578.         bra ERROR                ; return silently
  579.  
  580. ;
  581. ; -----------------------------------------------------------------------------------
  582. ;
  583. ; log2 -- find the ceiling of the log, base 2, of a number.
  584. ;
  585. ; calling sequence:
  586. ;       move.l N,D0                ; store the number in D0
  587. ;       bsr LOG2                ; call us
  588. ;       move.w D0,...                ; D0 contains the word result
  589. ;
  590. ; registers used: D2, (D0)
  591. ;
  592.  
  593. LOG2
  594.         or.l #1,D0                ; make zero into one; avoid infinite loop
  595.         move.w #32,D2                ; initialize count
  596. LOG2LP
  597.         lsl.l #1,D0                ; slide bits to the left by one
  598.         dbcs D2,LOG2LP                ; decrement and loop until a bit falls off
  599.  
  600.         move.w D2,D0                ; else save our value where we promised it
  601.         rts                    ; and return
  602.  
  603. .end    ; procedure dissBits
  604.