[comp.sys.mac] Macintosh programming. Help!

cutler@reed.UUCP (Steven J. Russell) (11/11/86)

I have a few questions pertaining to programming on the Macintosh in Pascal
(specifically LightSpeed Pascal).

          1. How do you get graphics to scroll smoothly?  I have a procedure
                  that draws graphics, and I want to get it to scroll.  I have
                  my scroll bars, and windows, etc.  Should I keep an offscreen
                  bitmap and scroll by CopyBits, or should I draw the image
                  each time.  Also, is there some formula that figures out what
                  a control's MaxValue should be when the image is bigger than
                  the window can show?  Source might be helpful.

          2. How do I update quickly?  This might be part of question 1.  Is
                  there any particular method that would update just the update
                  region instead of the whole image?  Source would be helpful.

          3. I seem to be having trouble with handling grows.  The trouble
                  seems to be with not erasing the scroll bars when enlarging
                  and not drawing them right when reducing by small amount.
                  How do you do this?

Thanks for any help (particularly source code) that you can lend me.  If you
have source, it would be most helpful in Pascal, but I will get by if it is in
C.  Even just the concepts behind scrolling, etc. would be of a great help.

One last question...

Has anyone written a Printer Driver in LightSpeed Pascal or LightSpeed C?  If
you have, I would be very interested in hearing about it, especially if it is
like the ImageWriter driver (dialog boxes, etc.).

Thanks for your help.

Steven J. Russell

DMB@PSUVMA.BITNET (11/12/86)

  About scrolling and updating fast:

     If you want to scroll in primarily fixed, rectangular reqions then
nothing beats ScrollRect for speed and ease of use. However for kind of
arbitrary scrolling, and the fact that you want to update quickly, the
the following scenario is probably better. Notice this illustration uses
fixed size windows, but it's possible with sizeable windows, but perhaps
a bit more sticky.

(* Set up an offscreen bitmap *)

Var offscreenbits:Bitmap;  (* the offscreen bitmap *)
    savebits:Bitmap;  (* a temporary bitmap for the windows portbits *)

(* initialize it to the size of the window: *)
  offscreenbits.rowbytes := thewindow@.portbits.rowbytes;
  (* @ is really up arrow I can't seem to type it here *)
  offscreenbits.bounds := thewindow@.portbits.bounds;
  bitsize := (thewindow@.portrect.bottom-thewindow@.portrect.top) *
             tempbits.rowbytes * 8;
  offscreenbits.baseaddr := Newptr(sizeof(bitsize);

  (* Save the window's portbits in a temporary bitmap and set the windows
     portbits to our offscreen bitmap *)

  savebits := thewindow@.portbits;
  setportbits(offscreenbitmap); (* sets the frontwindows bitmap notice *)
  eraserect(thewindow@.portrect); (* clear out the offscreen bitmap *)

(* Whenever you draw you can just draw into the window like normal as in *)
invertrect(thewindow@.portrect);
invertrect(thewindow@.portrect);
(* etc.... *)
(* When ever you draw now, it won't appear on the screen *)
(* when you want to put up on the screen what you've drawn call a procedure
   to update the screen, Let's call it updatescreen *)

Procedure updateScreen;
begin
   SetPortBits(tempbits);
   CopyBits(offscreenbits,thewindow@.portbits,offscreenbits.bounds,
            thewindow@.portrect,mode,maskrgn);
   (* note: mode is the overlay transfer mode, you probably want SrcCopy *)
   (*       maskrgn is the rgn you want to copy, if nil the whole rect
            is copied *)
   SetPortBits(offscreenbitmap);
end;

(* As for quick updates, this procedure makes life simple, The mac keeps a rgn
   which is the rgn that has to be redrawn, when a window deactivates in front
   of it, or you move the window off screen or what ever. Therefore to update
   do the following *)

   case
   .......
     updateevt:begin
                  BeginUpdate(theupdatewindow);
                     UpdateScreen;
                  EndUpdate(theupdatewindow);
               end;
    .......
    (* notice theupdatewindow is the window returned by findwindow *)


   That should do it, notice again this assumes fixed size windows, if you
   allow variable sized windows you'll probably have to smudge this alittle
   for when you change the size of the window, but anyway....
   Hopefully i haven't made any silly mistakes, but then again i guess i hear
   about it if i did, there's nothing worse then getting misinfo.
   **** Good Luck ****

                                  dave brosius
                                  PSUVMA.bitnet
Insert comment here:

lsr@apple.UUCP (Larry Rosenstein) (11/21/86)

In article <8419DMB@PSUVMA> DMB@PSUVMA.BITNET writes:
>
>  About scrolling and updating fast:
>
[ommitted article; the basic idea was to draw into an offscreen bitmap and
use Copybits to put the bitmap on the screen]


I have 1 optimization to add to this approach.  That is the fact that
CopyBits is fastest when the source and destination bitmaps are aligned on
the same bit boundary.  If this is not true, then Quickdraw will have to
shift each row of the bitmap as it copies it.

The trick to use in this case is to do the shifting off screen so that the
user doesn't see it.  This means doing an extra Copybits offscreen just to
perform the alignment.  Even though this takes some time, the visual effect
is much better.

Now for the theory.

The purpose of the ScrollRect calls is to conserve bits on the screen.  In
most applications, drawing the window is time-consuming because it involves
converting various shapes, text, etc. into a bitmap.  Because of this, you
want to reuse as many of those bits as possible.  That is exactly what
ScrollRect does.

If your application already has converted the image into a bitmap, then
there is no need to use ScrollRect.  You can just use Copybits to transfer
the bits to the proper place.  Copybits (if you do the alignment mentioned
above) is fast enough to do the copy.  MacPaint is a perfect example, since
it has the entire bitmap stored internally.

For an arbitrary application, drawing to an offscreen buffer will be useful
only if the bits can be reused, since drawing the offscreen bitmap takes as
long as drawing to the screen.  You can however, use the offscreen bitmap as
a cache and refresh the screen from it when possible.  (For example, when a
dialog comes up.)

-- 
Larry Rosenstein

Object Specialist
Apple Computer

AppleLink: Rosenstein1
UUCP:  {sun, voder, nsc, mtxinu, dual}!apple!lsr
CSNET: lsr@Apple.CSNET

DMB@PSUVMA.BITNET (11/25/86)

     In 324@apple.UUCP Larry Rosenstein writes ............

     [Stuff about bit alignment and using a second copybits to handle
      bit shifting.....]

     What exactly does this mean? I thought that if the offscreen bitmap was
 the same size as the original window's bitmap then you wouldn't have any bit
shifting problems, but maybe i'm not understanding the concept.

                                             dave

sdh@joevax.UUCP (Retief of the CDT) (12/05/86)

> 
>      In 324@apple.UUCP Larry Rosenstein writes ............
> 
>      [Stuff about bit alignment and using a second copybits to handle
>       bit shifting.....]
> 
>      What exactly does this mean? I thought that if the offscreen bitmap was
>  the same size as the original window's bitmap then you wouldn't have any bit
> shifting problems, but maybe i'm not understanding the concept.
> 
>                                              dave
Here's what it means, dave.   In the below examples, I'll use # for bitmap
borders and * for the bitmap bounds rectangle.
Suppose you have a bitmap that looks like this:
		****************################
		* X  X  X      *               #
		* XXXX  X      *	       #
		* X  X  X      *	       #
		****************################
and you want to copy it onto the screen's bitmap using the same size bounds
rectangle, but in a different place.
		###############################...
		#
		#	****************
		#	*	       *
		#	*	       *
		#	*	       *
		#	****************
		#
		.
		.
		.
Well, that's just fine.  CopyBits will do it for you, but it is not an
optimal transfer.  Notice that the destination rectangle is 9 bits further
to the right than the source.  This means that in order to get the bits
to coincide, the source bits will have to be either shifted 9 bits to the
right or 1 bit to the left (equivalent operations for bytes). 
The problem is that CopyBits with have to do a number of shifts for each
byte transferred.  Worst case (4 shifts), it will take about 6-10 times as
long to move each byte [the will be cases requiring shifting out one byte and
into another more often than not], if there were no shifts.  Better yet,
if things line up on byte boundries, the whole block can be moved by word,
instead of by byte (ever wonder why your RowBytes always has to be even?).
So, what you can do is:
1) draw 8 images of your object and have them in their own bitmaps.
2) draw 1 image of your object and let quickdraw generate the other 7
   for you off screen at startup in separate bitmaps.
3) use one bitmap with all the images and change the bounds Rect
   (this will save memory)
How do you know which bitmap to use?  Simple.
you will have 8 bitmaps and you want to refer to them so that the 0th
one will always align on multiples of 8, the 1st on multiples of 8 plus one,
the 2nd on multiples of 8 plus two etc.
The most basic way to do this is to use the MOD function (% in C).

	{ Pascal version: }
	which_map_to_use := destination_horizontal MOD 8;
	/* C version */
	which_map_to_use = destination_horizontal % 8;

This method works fine, but it is faster to do a logical and with 7,
as it is equivalent to MOD 8.
I don't recall how this is done in Pascal (I think the Macintosh Pascals
have a BitAnd function).  In C the process is:

	which_map_to_use = destination_horizontal & 0x7;
	/* I always put constants for logical operations in hex */

Hope this clarifies things.

Steve Hawley
bellcore!sdh