[comp.sys.mac.programmer] Help: Using CopyBits for Update

sam@neoucom.UUCP (Scott A. Mason) (05/08/89)

I need some help from some of you more experienced Mac programmers.
Although I am well versed in C, I am just starting to program in the
Macintosh environment.

I have a window which I use QuickDraw routines to draw characters.  (Not
a text edit window, that is)  I am have difficulties responding to an
update event.  I have IM volumes and several different books, but none of
them sufficiently covers the use of an offscreen buffer to store window
info for updating.  

How does one set up an offscreen buffer so that CopyBits may be used to 
refresh the window when responding to an update event? 
-- 
--------------------------------------------------------------------------------
"If it ain't broke, don't fix it," and certainly don't blame me.
UUCP:  {pitt, scooter, hal, cwjcc}!neoucom!sam    INTERNET:  sam@neoucom.UUCP
Scott A. Mason, Coordinator of Systems Operations, NEOUCOM

ksitze@nmsu.edu (Kevin Sitze) (05/10/89)

In article <sam@neoucom.UUCP's message of 8 May 89 04:17:52 GMT>
>I have a window which I use QuickDraw routines to draw characters.  (Not
>a text edit window, that is)  I am have difficulties responding to an
>update event.  I have IM volumes and several different books, but none of
>them sufficiently covers the use of an offscreen buffer to store window
>info for updating.  
>
>How does one set up an offscreen buffer so that CopyBits may be used to 
>refresh the window when responding to an update event?

This is really a rather simple procedure.  The idea is to draw into an
offscreen port and then CopyBits the wanted information to the desired
window.  To setup an offscreen port for drawing, you can do something
like this...

In Pascal (LSP):

Function SetupPort : GrafPtr;
var
  tmpPort, myPort : GrafPtr;
begin
  GetPort(tmpPort);
    { Need to allocate memory for the port }
  myPort := GrafPtr(NewPtr(sizeof(GrafPort));
    { -- Error checking for nil pointer goes here -- }
    { Create the internal structures for the port }
  OpenPort(myPort);
    { Now we need to set aside enough memory to draw into }
  with myPort^.portBits do
    begin
      baseAddr := NewPtr(rowBytes * (bounds.bottom - bounds.top));
        { -- Error checking for nil pointer goes here -- }
        { Don't need to initialize any of the rest of the fields in }
        { the port because Quickdraw has done all we need already. }
    end;
  { And return the resulting port, voila, an offscreen graph port! }
  SetPort(tmpPort);
  SetupPort := myPort;
end;

or, for them (LS)C buffs...

#define myBM myPort->portBits
GrafPtr SetupPort()
{
    GrafPtr tmpPort;
    register GrafPtr myPort;

    GetPort(&tmpPort);
    myPort = (GrafPtr) NewPtr(sizeof(GrafPort));
        /* -- Error checking for nil pointer goes here -- */
        /* Create the internal structures for the port */
    OpenPort(myPort);
        /* Now we need to set aside enough memory to draw into */
    myBM.baseAddr = NewPtr(myBM.rowBytes * (myBM.bounds.bottom -
        myBM.bounds.top)); 
        /* -- Error checking for nil pointer goes here -- */
        /* And return the resulting port, voila, an offscreen graph port! */
    SetPort(tmpPort);
    return myPort;
}
#undef myBM

There are several problems with the program as it stands above.  1) it
does not have any error conditions.  2) It eats up about 6K of the
heap everytime it's called (or more if your doing color, but if that's
the case, you'll _need_ to look at Macintosh Technote #120)

Notice that I saved the current port on entry, this is because I
didn't really want SetupPort to change my current graph port.  If you
don't care, you can remove these lines (but you may never know when
you may need them, I spent several hours tracking down a single
missing SetPort once...not a pleasant experience.)

You can save memory by passing a window to the procedure and then
resizing the port to the window size.  If you do this though, you have
to resize the port everytime you resize the window, something of a
bother.  Enough of this, let's describe how to draw, etc.

The procedure to draw into the newly created port may look something
like this (in Pascal):

Procedure DrawInMyPort( port : GrafPtr );
var
  tmpPort : GrafPtr;
begin
  GetPort(tmpPort);
  SetPort(port);
    { various Quickdraw calls here }
  SetPort(tmpPort);
end;

And in C...

DrawInMyPort(port)
GrafPtr port;
{
    GrafPtr tmpPort;

    GetPort(&tmpPort);
    SetPort(port);
        /* various Quickdraw calls here */
    SetPort(tmpPort);
}

Pretty easy huh?  The way the port is setup, you have the entire
'screen' to draw into, we'd have to do a few more calls if the window
are 'larger' than the screen (e.g. paint programs).  Finally, we want
to do an update event, drawing from the offscreen port to a window:

Procedure UpdateWind( wind : WindowPtr; port : GrafPtr );
var
  tmpPort;
begin
  GetPort(tmpPort);
  SetPort(wind);
  BeginUpdate(wind);
  CopyBits(port^.portBits, wind^.portBits, port^.portRect,
    wind^.portRect, srcCopy, nil);
  EndUpdate(wind);
  SetPort(tmpPort);
end;

or

#define NIL (0L)
UpdateWind(wind, port);
WindowPtr wind;
GrafPtr port;
{
    GrafPtr tmpPort;

    GetPort(&tmpPort);
    SetPort(wind);
    BeginUpdate(wind);
    CopyBits(&port->portBits, &wind->portBits, &port->portRect,
        &wind->portRect, srcCopy, NIL);
    EndUpdate(wind);
    SetPort(tmpPort);
}

These are all pretty much basic routines here but they should work.
If you have any more questions, you might refer to the Macintosh
technical notes.  (You might be able to find the most recently
distributed tn's on comp.mac.sources (comp.sources.mac?))
Specifically, TN#120 (which I know was uploaded recently)  Hope this
helps!

				-Kelesi
--
------------------------------------+-------------------------------
From the Macintosh of: Kevin Sitze  | Disclamer: Who the heck needs
                                    |   a disclamer?  After all, Dan
EMail: ksitze%NMSU.edu              |   Quayle doesn't.
SMail: 601 S. Melendres             +-------------------------------
       Las Cruces, NM  88005        | "We have the answers, the
------------------------------------+  trouble lies in finding the
"The difference between intelligence|  questions..."
and stupidity is that intelligence  | "The information is there,
has a limit."           - anonymous |  finding it is another story."
The dolt confuses you -- more --    |	   	    - Any consultant
------------------------------------+-------------------------------

tim@hoptoad.uucp (Tim Maroney) (05/12/89)

Maybe I'm being dense, but what is the advantage to keeping an
offscreen bitmap in a terminal emulator and using it for updates?
Individual updates will go faster.  However, you will spend more time
overall in QuickDraw, because what would ordinarily be a one-step
process has a new step added.

The alternative to using an offscreen bitmap is to draw the characters
directly into the window.  This takes a time we'll call Td.  The time
Td is going to be about the same whether you're drawing to the screen
or to an offscreen bitmap.  However, when drawing offscreen, you need
another step, a CopyBits to the screen, which takes time Tc.  So the
overall drawing time using the offscreen bitmap approach is Td+Tc,
while drawing directly to the window is Td only.

If a dialog box or something comes up in front of your window, then
goes away, then the offscreen bitmap approach will lead to a faster
update of the covered region, because Tc < Td.  However, this seems to
be the only case where you'd get a speedup.  (And related cases like
going to the back in MultiFinder then coming back to the front.)  Most
of the time in a terminal emulator in real life, you're likely to be
writing to the front window.

And I haven't even mentioned that the 20K or so for the offscreen
bitmap is likely to impose a speed hit on your software because there's
less free space in the heap....
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"Prisons are built with stones of Law, Brothels with bricks of Religion."
    - Blake, "The Marriage of Heaven and Hell"

jmunkki@kampi.hut.fi (Juri Munkki) (05/12/89)

In article <7254@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>Maybe I'm being dense, but what is the advantage to keeping an
>offscreen bitmap in a terminal emulator and using it for updates?
>Individual updates will go faster.  However, you will spend more time
>overall in QuickDraw, because what would ordinarily be a one-step
>process has a new step added.

I used to think this way too. Then I started working on a new terminal
emulator and I wanted this one to have really easy updates. The surprising
thing is that most users think that the program is better than others
because it is faster than any other program they have used...and my
program uses an offscreen bitmap.

Using the bitmap doesn't seem to matter too much. I keep the bitmap always
long-word aligned so that copybits can just blast the bits from the buffer
to the screen without shifting them. DrawText always has to do this anyway,
so calling DrawText takes much more time than the CopyBits for the same area.

I had a very good reason why using the offscreen bitmap might be even faster
than drawing directly (in some special cases), but I can't remember it right
now. I'll post the reasoning, if I remember it again.

Summary: In theory it might be slow, but in practise it doesn't seem to matter.

_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
|     Juri Munkki jmunkki@hut.fi  jmunkki@fingate.bitnet        I Want   Ne   |
|     Helsinki University of Technology Computing Centre        My Own   XT   |
^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^

sam@neoucom.UUCP (Scott A. Mason) (05/12/89)

In article <7254@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>
>The alternative to using an offscreen bitmap is to draw the characters
>directly into the window...
>

I am already using the DrawChar to draw the character directly into the
window, and that works fine.

>If a dialog box or something comes up in front of your window, then
>goes away, then the offscreen bitmap approach will lead to a faster
>update of the covered region...

This is exactly my problem.  How does one update the screen if it has not
been stored in an offscreen bitmap??
-- 
--------------------------------------------------------------------------------
"If it ain't broke, don't fix it," and certainly don't blame me.
UUCP:  {pitt, scooter, hal, cwjcc}!neoucom!sam    INTERNET:  sam@neoucom.UUCP
Scott A. Mason, Coordinator of Systems Operations, NEOUCOM

kk@mcnc.org (Krzysztof Kozminski) (05/12/89)

In article <21931@santra.UUCP> jmunkki@kampi.hut.fi (Juri Munkki) writes:
>In article <7254@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>>Maybe I'm being dense, but what is the advantage to keeping an
>>offscreen bitmap in a terminal emulator and using it for updates?
>
>I had a very good reason why using the offscreen bitmap might be even faster
>than drawing directly (in some special cases), but I can't remember it right
>now. I'll post the reasoning, if I remember it again.

How about this: sometimes the visible and/or clipping regions on the
screen are not rectangular => drawing to the screen is slower than
drawing to an offscreen *rectangular* PixMap due to the extra effort
necessary for the proper clipping of each object drawn.

KK

lippin@ronzoni.berkeley.edu (The Apathist) (05/13/89)

Recently tim@hoptoad.UUCP (Tim Maroney) wrote:
>Maybe I'm being dense, but what is the advantage to keeping an
>offscreen bitmap in a terminal emulator and using it for updates?
>Individual updates will go faster.  However, you will spend more time
>overall in QuickDraw, because what would ordinarily be a one-step
>process has a new step added.

It's a matter of trading time (and memory) now for time later.  Since
the amount of time spent in CopyBits is generally small, this is often
a good trade, particularly if the drawing is complex.

But, in a terminal program, the time to draw is also small, so I
imagine there would be little advantage.

A further advantage of CopyBits is that it avoids the flicker of doing
erase and redraw on the screen.  But for text, this is more simply
avoided by using srcCopy mode.

					--Tom Lippincott
					  lippin@math.berkeley.edu

		"Ask a fish head anything you want to --
		 It won't answer you; they can't talk."

tim@hoptoad.uucp (Tim Maroney) (05/14/89)

In article <1203@speedy.mcnc.org> kk@mcnc.org.UUCP (Krzysztof Kozminski) writes:
>How about this: sometimes the visible and/or clipping regions on the
>screen are not rectangular => drawing to the screen is slower than
>drawing to an offscreen *rectangular* PixMap due to the extra effort
>necessary for the proper clipping of each object drawn.

I'm pretty sure the opposite is true.  If part of your window is
covered, then the reduced Font Manager overhead from not drawing the
clipped-out characters makes drawing much faster.  Clipping resolution
seems to be more than fast enough to make this a win.  I have observed
this empirically with TOPS Terminal.  However, with an offscreen
bitmap, every character must be drawn.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"The government of the United States is not, in any sense, founded
 on the Christian religion." -- George Washington

jmunkki@kampi.hut.fi (Juri Munkki) (05/14/89)

In article <7287@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>In article <1203@speedy.mcnc.org> kk@mcnc.org.UUCP (Krzysztof Kozminski) writes:
>>How about this: sometimes the visible and/or clipping regions on the
>>screen are not rectangular => drawing to the screen is slower than
>>drawing to an offscreen *rectangular* PixMap due to the extra effort
>>necessary for the proper clipping of each object drawn.
>
>I'm pretty sure the opposite is true.  If part of your window is
>covered, then the reduced Font Manager overhead from not drawing the
>clipped-out characters makes drawing much faster.  Clipping resolution
>seems to be more than fast enough to make this a win.  I have observed
>this empirically with TOPS Terminal.  However, with an offscreen
>bitmap, every character must be drawn.

According to IM-I the whole string is drawn into an offscreen buffer
and only clipped after that. A complex clipping region will slow
down the copybits from this buffer to the screen. I still think you
don't loose any significant time. (Especially if you keep the screen
and your own offscreen bitmap aligned.)


From IM-I 172:
[	Warning: QuickDraw temporarily stores on the stack all of the text you
	ask it to draw, even if the text will be clipped. When drawing large
	font sizes or complex style variations, it's best to draw only what
	will be visible on the screen. You can determine how many characters
	will actually fit on the screen by calling the StringWidth function
	before calling DrawString.
]

None of this will be relevant if you use DrawChar for every character you
draw. (Why this is not a good idea was discussed a few months ago.)

It's somewhat easier to implement scrolling (or insert/delete) functions
if you keep an offscreen bitmap. Imagine that you have the calculator DA
in front of your terminal window. You update region after the operation
will most probably not be a rectangle. To update, you have to call DrawString
several times. QuickDraw will have to regenerate the mask from the region
every time you call DrawText. It only has to generate the mask once, when
you call copybits.

For operation as the front window, you probably have a small speed advantage
if you don't use the offscreen bitmap. My reason for using the offscreen
bitmap was that updating the screen was a lot faster & easier. Now that the
VT100 is ready, I find that it wouldn't take too much work to remove the
offscreen bitmap. I might have to do this, if I start writing YATI (Yet
Another Telnet Implementation). Right now (with only one offscreen buffer)
I don't think it's worth changing the program. Even with the bitmap, I
can run the program in a 160KB multifinder partition.

_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
|     Juri Munkki jmunkki@hut.fi  jmunkki@fingate.bitnet        I Want   Ne   |
|     Helsinki University of Technology Computing Centre        My Own   XT   |
^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^~^

alain@atr-la.atr.junet (Alain de Cheveigne) (05/14/89)

Recently tim@hoptoad.UUCP (Tim Maroney) wrote:
>Maybe I'm being dense, but what is the advantage to keeping an
>offscreen bitmap in a terminal emulator and using it for updates?
>Individual updates will go faster.  However, you will spend more time
>overall in QuickDraw, because what would ordinarily be a one-step
>process has a new step added.

There is one advantage in using an offscreen bitmap that has nothing to
do with speed.

Suppose you want your terminal emulator to print and scroll when it 
isn't frontmost.  For example it's in the background, or you brought
up a DA. 

Suppose the front window leaves an unmasked space at the top of the
emulator window.  If you just print to the window and scroll, then
this portion will remain blank: text will appear to "slide under"
the masking window, but won't come out at the top.

However, if you print both to the screen and to an offscreen
bitmap, and copy the bitmap back to the window at each newline, 
then the emulator will appear to scroll ok.

Of course it's a bit slow on a Mac+.  For speed you want to clip
the CopyBits to the part that "slid out" from under the masker(s).

You might also make special cases for when the drawing point is
completely masked (draw only to the offscreen bitmap) or completely
visible (draw only to the screen, and transfer each line to the
offscreen buffer at newlines and each time something is about
to mask it).

I tried to send a mail to tim@hoptoad.UUCP, but the mailer doesn't
know of domain UUCP.



Alain de Cheveigne,
alain@atr-la.atr.junet

tim@hoptoad.uucp (Tim Maroney) (05/17/89)

In article <7254@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>If a dialog box or something comes up in front of your window, then
>goes away, then the offscreen bitmap approach will lead to a faster
>update of the covered region...

In article <1625@neoucom.UUCP> sam@neoucom.UUCP (Scott A. Mason) writes:
>This is exactly my problem.  How does one update the screen if it has not
>been stored in an offscreen bitmap??

Store the characters in an array or a linked list or some cognate
structure.  When you get an update, determine which lines need to be
redrawn, then call DrawText on each line after moving to the
appropriate place on the screen.  Sorry that I didn't understand
your problem earlier or I'd have said this right away.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"Conversion, fastidious Goddess, loves blood better than brick, and feasts
 most subtly on the human will." - Virginia Woolf, "Mrs. Dalloway"

tim@hoptoad.uucp (Tim Maroney) (05/17/89)

In article <21965@santra.UUCP> jmunkki@kampi.hut.fi (Juri Munkki) writes:
>According to IM-I the whole string is drawn into an offscreen buffer
>and only clipped after that. A complex clipping region will slow
>down the copybits from this buffer to the screen. I still think you
>don't loose any significant time. (Especially if you keep the screen
>and your own offscreen bitmap aligned.)

Is this still true in the Mac Plus and later ROMs?  I doubt it.  Like I
said, I've seen that the contrary is true.  If I have a terminal window
open in TOPS Terminal and another window overlaps it, covering from
some point right of the left margin all the way to the right margin,
the lines go by significantly faster.  Remember that one of the things
they did in the Mac Plus ROMs was to speed up most QuickDraw
operations.

>It's somewhat easier to implement scrolling (or insert/delete) functions
>if you keep an offscreen bitmap. Imagine that you have the calculator DA
>in front of your terminal window. You update region after the operation
>will most probably not be a rectangle. To update, you have to call DrawString
>several times. QuickDraw will have to regenerate the mask from the region
>every time you call DrawText. It only has to generate the mask once, when
>you call copybits.

Yes, I already said that this kind of update would be faster using an
offscreen bitmap, and this may create an illusion of overall better
speed to a naive user.  That may be good enough, but for me, I prefer
to work with the actual speed.  And I still believe that overall, using
an offscreen bitmap will be slower for every other operation.
-- 
Tim Maroney, Consultant, Eclectic Software, sun!hoptoad!tim
"Americans will buy anything, as long as it doesn't cross the thin line
 between cute and demonic." -- Ian Shoales