FB II Compiler
Handle refreshing of pictures
I have a question about finding/changing the clipping rectangle for Pictures created with the PICTURE ON and PICTURE OFF statements. When these pictures are recorded, they are automatically clipped to the size of the output window into which they are being drawn/recorded. All the information is there inside the PICT, but it always gets clipped off to the size of the original window in which it was recorded.
My question is this... How can I get parts of the picture to draw (at a later time in a bigger window), which were off-window when the PICT was recorded. Obviously a Clipping RECT was recorded as part of the PICT, but how can I find this rectangle and change it.
After you execute PICTURE ON (but before you start drawing), change the clipping region to a larger rectangle. For example:
CALL SETRECT(rect, -10000,-10000, 10000,10000)
the new, larger clip region will be recorded along with the subsequent drawing commands, and when you re-play the picture later, it will be clipped to the larger region.
It looks as though, when you draw a previously-recorded picture, the drawing gets clipped to the _intersection_ of these two regions:
* the clipping region recorded in the picture; and
* the window's current clipping region.
Therefore, do this: when you record the picture, use the larger clip region; but when you play the picture back, set the window's clip region (temporarily) to the small 16 by 16 area. For example:
CALL GETCLIP(saveRgn&) 'Save window's current clip region
CALL CLIPRECT(myLittleRect) 'Set clip region to 16 x 16
PICTURE ,mapPict& 'Draw the picture (it will be clipped)
CALL SETCLIP(saveRgn&) 'Restore window's old clip region
(Note: saveRgn& must be a handle to region that you previously created, using saveRgn& = FN NEWRGN.)
Very interesting. I could use this technique. BTW, I would also like to retrieve a small part of a picture recorded with PICTURE ON, PICTURE OFF.
Say, I have a 640 by 480 Pict recorded and I want to clip a 16 by 16 pixel portion of it at position 200, 320. How do I do that?
The reason is: I draw a map as a background, redord it, draw objects and lines then use the background as an "eraser". Refreshing the whole background requires to refresh all the objects and it takes time and looks "amateurish."
As I understand it, the picFrame is used as a reference for scaling and/or horizontal & vertical shifting of the picture when it's "played back." When you draw a previously-recorded picture, you have to specify a rectangle in which to draw it (FB does this internally if you don't specify it explicitly). QuickDraw compares the rectangle you specify in the DRAWPICTURE command against the picFrame specified inside the picture, and knows thereby how to shift and/or scale the picture. I don't think the picFrame has any effect on clipping--it's certainly true that the picture's content can go outside of the picFrame boundaries.
Won't this just display the top corner of the picture ((0,0)-(16,16))? I think the question was how do you get a certain section from a larger picture and display just that smaller portion. Bill says to use gWorlds and Copybits but I still can't figure those out.
It will display whatever portion of mapPict& intersects with myLittleRect. If myLittleRect is set to (0,0)-(16,16) then yes, the top corner of mapPict& will be displayed (assuming that mapPict&'s top corner is at (0,0)). If myLittleRect is set to something else, like (50,50)-(66,66), then a different part of mapPict& will be displayed.
I am still confused. Where will the picture be drawn (I have done very little with pictures)? Say the picture was originally drawn at coordinates (200,200)-(1000,1000). Then you do CALL CLIPRECT(myLittleRect) to get a small part of the picture, say (400,400)-(416,416). How do you draw that part at say (10,10)-(26,26)? I thought that myLittleRect is where the picture would be drawn and that the top left corner of the picture would be at the top left corner of myLittleRect.
CLIPRECT doesn't get a part of the picture. What it does is set the window's "clipping region" to be the rectangle specified by myLittleRect. That means that when you try to draw or print _anything_ new into the window, only the pieces that lie within that rectangle will actually get drawn--everything you try to draw outside of the clipping region gets "masked out." So if you set the window's clipping region to the rectangle (400,400)-(416,416), then draw a big picture into the window, the only part of the picture that will show up is the part that falls within window coordinates (400,400) and (416,416). If you specify that the picture should be drawn with a frame of, say, (200,200)-(1000,1000), then the only part of picture that shows up will be a little chunk out of its middle.
Now, if you're talking about "clipping out" and saving a little piece of the picture, then displaying the clipped-out part at some arbitrary location in the window, that's a different story. That can be done too (with or without GWorlds); but I don't think that's what Michel had in mind, exactly.
Easiest way to do this is to draw the entire PICT offscreen in a GWorld, then just copybits the little section you care about to the window.
That will work too, and will very likely be faster. On the other hand, GWorld graphics tend to require more memory, especially when compared to "vector"-type graphics (which is what Michel seems to be using). It's another case of the old "speed vs. memory" tradeoff.
Let's keep it easy; define two Rects:
LOCAL FN copyBits(srcWrld&,dstWrld&,@srcRect&,@dstRect&,cMode)
' Copy bits from one world to another.
hasQD32 = FN QD32Support
LONG IF hasQD32
IF cMode = _transparent THEN cMode = _srcOR
CALL COPYBITS(#srcWrld& + 2,#dstWrld& + 2,#srcRect&,#dstRect&,cMode,0)
LONG IF hasQD32
local fn fred
window #1, "Example",(0,0)-(640,480)
until fn button 'not Mouse(_down) :-)
As you can probably tell, untested, but I'm sure there's little errors if any in it. You end up copying from one point to another onscreen; pretending that your window is the gworld. (And it can be, too.)
Let me jump in here.
PICTURE ON/OFF works by using the area of the current output window as the picture rectangle. If you make a large window, you can have a large picture. But you can not make the window larger than the monitor.
The only sane way to build a picture is to do it in an offscreen bit world. We usually build one with PG's FN buildBWorld. Then dispose of the bworld as soon as the picture is complete. While the functions are created by PG, it is not necessary that you use PG to get at the functions. Just copy them from the GRFX.FLTR.
CALL SETRECT(T,0,0,100,200) : REM or whatever you want
bWorld& = FN buildBWorld(t)
pHndl& = FN OPENPICTURE(t)
REM draw picture here
Sorry, but this is just plain wrong. You can set the window size to anything you like within the limits set by FB. You can set the Clip region to the same size, on or off screen, and capture anything you like within this region. I routinely create and use Vector Maps of up to 8191 x 8191 pixels. That's about 9 ft x 9ft at 75 dpi.
If you want to draw Vector Maps, I've found that the best way is to draw them into a Resource where they're stored. Then create a gWorld the same size as your screen, and draw the Vector Map into the gWorld, the copy the gWorld to the screen. If you make the gWorld bigger than the screen, you can copy the gWorld across with different offsets. This allows you to a "smooth scrolling" map on most PowerMacs.
If anyone want specific help on this, EMail me directly.
That's what I thought once too. But on some system configs , it is impossible to use PICTURE ON/OFF to create a picture larger than a monitor. (Nn LCIII, Sys 7.something, is the one that brought this problem to light.) We had to deal with this when we created Classroom Publisher (circa 1990?). Our scrolling menu for fonts was often taller than the monitor on which the menus were displayed. (A day or so of hair pulling as I recall...) That was when I discovered the better method of using offscreen bworlds. So it's not _really_ wrong, but my note may cover a system that is not important to you.
Here's another thing that burned our grits: When you use COPYBITS, you _must_ set the port to the destination parameter. Failure to do so can cause a major crasholla. Now a lot of you will tell me that it doesn't happen on your computer. May not. But on an LCIII we went to hell in a handcart big time.
So... I'll stick by my original statement.
I'm not sure I follow this.
I have several applications that will create pictures that are many times larger than my monitor. I do this by creating a window (with a negative id) with a rectangle that will accomodate the picture, often many times larger than my monitor.
The negative id means the window is invisible. I draw my chart in this window and record it to a picture. I close the invisible window and redraw the picture in a window with scroll bars. It's all there. It's many times bigger than my monitor. Some of the largest pictures created this way have been over 100 inches in one direction.
I have never used a BWorld or a GWorld for this purpose.
True, you can't make the window larger than the monitor (not if you know what's good for you), but you can certainly record a _picture_ that's larger than the window--and larger than the monitor. For example:
PICTURE ON (-1000,-1000)-(1000,1000) '(frame's bigger than windowno problem!)
'Increase the clipping region accordingly:
CALL SETRECT(rect, -1000,-1000, 1000,1000)
'Draw a 1500-pixel diameter circle:
PEN 20,20 'thick lines!
CIRCLE 0, 0, 750
PICTURE OFF, h&
'To prove we captured it, we now scale it down
'to a visible size and display it:
INPUT "Press return to see circle (shrunken version):"; x$
PICTURE (0,0)-(200,200), h&
KILL PICTURE h&
<< The only sane way to build a picture is to do it in an offscreen bit world. >>
I agree, in cases where speed is an issue, or if you need to do some of the special effects that GWorld transfers can accomplish. But also consider this:
* GWorlds tend to use up a lot more memory than picture recordings (my 1500-pixel diameter circle only took up only 72 _bytes_--creating a GWorld to hold it would take more than 2 MEGAbytes.)
* GWorlds COPYBITS kills vectors. Create a picture by COPYBITS'ing a 72 dpi GWorld, and then send the picture to your printer--you'll get 72 dpi jaggies. But if you save the picture as a recording of QuickDraw commands (other than CopyBits) and send _that_ picture to your printer, then text, lines, curves, etc. are nice and smooth.
Also, GWorlds requires you to do the (admittedly small) work of building the GWorld, switching ports, doing the copybits, disposing the Gworld, etc. If you just wanted to record some drawing commands, you can do that all in your current window (as long as you temporarily set the clipping region appropriately). The picture will record invisibly, and won't disturb, nor be disturbed by, anything else that may be in your window.
With all the "yes you can do it this way" and "but you should do it this way" discussion here (which is very interesting) it seems a couple of basic things may be forgotten.
PICT recordings are designed for "resolution independent vector graphics". Bitmaps and GWorlds are designed for, well, bitmaps. You can certainly use a PICT just about anywhere a bitmap would work, at the cost of speed. And as long as you remain at the "creating" resolution, you can use a bitmap almost anywhere a PICT would work, at the cost of memory. In the specific original question (if I remember it right...) the need was for "refreshing" a portion of the onscreen window - exactly what GWorlds were created to achieve. To do this with PICTs, we've gotten some really complex answers!
Here's the important point: RUMOR TO THE CONTRARY (and some _really_ difficult-to-understand-documentation in IM aside) GWorlds are _not_ any harder to use than PICTs. They are just an offscreen area where you draw. Just like a window. Using PICTs and getting all the Clip Regions right seems _far_ harder to me.
"COPYBITS" is just a way of copying from one place to another. Yes, it has a lot of parameters, but look at them! Source GWorld (usually what you created "offscreen"). Destination GWorld (usually your window). Source Rectangle. Destination Rectangle. (Which are the same if all you're doing is keeping a copy of your "window background" offscreen.) "How to copy". (Usually _srcCopy.) That's it!
If you _aren't_ dealing with high-speed animation, you don't need to know anything else about GWorlds. Build one, using one of the dozens of example FNs. Draw into it just exactly as if it were a window. Whenever you need part or all of that drawing on screen, call CopyBits. When you're finished with it, close it!
One of the primary "nice things" about the Mac is the way windows in applications get "refreshed" after they've been covered up by windows from some other application. With GWorlds, I can do _all_ my drawing "offscreen". Then I have one "refresh" event function per window that does nothing but a full-window CopyBits. Presto! Instant fully-automatic window refresh!
I believe the confusion has come in, and GWorlds gotten a "bad name", because they _are_ used for high-speed animation, and in that role, you get into multiple-GWorld-"layering", double- or triple-buffering, etc. CopyBitsing from one GWorld to another all over the place before finally going to the screen. That _is_ a difficult concept to grasp.
And yes, I do have apps that use PICTs and not GWorlds; specifically the ones where the PICT is destined for a printer, and the "on screen" view is incidental, or must be scaled. Both techniques make sense, just for different purposes! (But GWorlds are easier...)