PDA

View Full Version : Quickdraw question.


MidshipPowerTRD
2003.11.29, 12:17 AM
Hello all!

Is it possible to use quickdraw to draw to a window created in Interface Builder?

I copied over some old code that drew to a window created with a WIND resource and that code worked fine.

Here's the function I came up with:



//------------------drawToScreen()---------------------
// draws contents of 'ofScreenBuffer' to 'mainWindow'

void drawToScreen(WindowRef &mainWindow, GWorldPtr &offScreenBuffer)
{
CGrafPtr source = offScreenBuffer;
CGrafPtr dest = GetWindowPort(mainWindow);
CGrafPtr oldPort;
GDHandle oldGDevice;
Rect sourceRect;
Rect destRect;
bool canLockPixels;

GetGWorld(&oldPort,&oldGDevice);
// saves the current drawing area before we move to a new one

if ( (source == NULL) || (dest == NULL) )
return;

GetPortBounds(source,&sourceRect);
GetPortBounds(dest,&destRect);

SetGWorld(dest, GetMainDevice() );
// sets the drawing area to be the screen

PixMapHandle myPixMap = GetGWorldPixMap(source);

canLockPixels = LockPixels(myPixMap);
// locks the pixels so they dont move during transition

if (canLockPixels)
CopyBits(GetPortBitMapForCopyBits(source),
GetPortBitMapForCopyBits(dest),
&sourceRect,&destRect,srcCopy,NULL);

UnlockPixels(myPixMap);

ShowWindow(mainWindow);

SetGWorld(oldPort,oldGDevice);
// restore graphics port and the GDevice

} //drawToScreen()


When I comment out

SetGWorld(dest, GetMainDevice() );


It seems to draw directly to the screen.
I can get rid of what it drew by dragging windows over it.

Can anyone tell me what's wrong?
I've Been at it for awhile and now I'm stumped. :p

Thanks in advance!

Fenris
2003.11.29, 12:50 AM
Under Mac OS X, all windows are double buffered. I believe that you would benefit hugely from a call to... lemme check...


QDFlushPortBuffer(dest, NULL);


after you're done drawing. This also means that you can kill all of your double-buffering code, which should provide a notable speed-up. (Notice that this is only true under X!) The NULL means that you flush the entire window, if you know that you only draw to a portion of the window, you can pass in a pointer to a region there. Check out QuickDraw.h.

Commenting out the call to SetGWorld will make you draw to somewhere unspecified (whereever the port was set before you draw), where it is very possible that it will be the screen. The screen isn't very adept at redrawing itself outside of the windows, so that's why you're getting the artifacts. (That clear out when you drag a clever window over it.)

HTH, Ivan

MidshipPowerTRD
2003.11.29, 02:03 AM
Originally posted by Fenris
Under Mac OS X, all windows are double buffered. I believe that you would benefit hugely from a call to... lemme check...


QDFlushPortBuffer(dest, NULL);


after you're done drawing. This also means that you can kill all of your double-buffering code, which should provide a notable speed-up. (Notice that this is only true under X!) The NULL means that you flush the entire window, if you know that you only draw to a portion of the window, you can pass in a pointer to a region there. Check out QuickDraw.h.

Commenting out the call to SetGWorld will make you draw to somewhere unspecified (whereever the port was set before you draw), where it is very possible that it will be the screen. The screen isn't very adept at redrawing itself outside of the windows, so that's why you're getting the artifacts. (That clear out when you drag a clever window over it.)

HTH, Ivan

Such a quick reply...thanks!
So I read up on it a bit and inserted the function call.

if (canLockPixels)
CopyBits(GetPortBitMapForCopyBits(source),
GetPortBitMapForCopyBits(dest),
&sourceRect,&destRect,srcCopy,NULL);

QDFlushPortBuffer(dest, NULL); // updated part

UnlockPixels(myPixMap);

Still a no go....sorry...I'm still new at this :blush:
Any more help is appreciated! :)

Fenris
2003.11.29, 02:07 AM
It might be that you need to unlock your pixels before you flush. If that's not the culprit, then you can either check out Apple's source code, or just mail me your project at ivan[at]rusted[dot]se, and I'll have a look at it... I usually need a debugger to understand code. :D

Edit:
Are you sure that you are actually getting to the CopyBits call? Have you made sure that canLockPixels evaluate to true? It can be that LockPixels fails for some reason, namely if the base address has been purged. Not likely to happen, though. Just checking. :)

MidshipPowerTRD
2003.11.29, 02:53 AM
Sent the thing over....Thanks again!

Fenris
2003.11.29, 06:12 AM
Hi again!

I looked at the source and cross-referenced it with my Mac Game Programming book. Turns out you used the code for OS 9 drawing. Turn to page 100 in that book (which I believe you were reading from?) for a complete instruction.

If you don't have the book, then your steps would be to replace your calls to LockPixels with calls to LockPortBits, and the same for the Unlock calls:


CGrafPtr windowPort = GetWindowPort (theWindow);
LockPortBits (windowPort);
DrawCoolStuffWithAttribs (DRAW_PHOTON_MAP | BETTER_RADIOSITY | REALLY_FAST);
UnlockPortBits (windowPort);
QDFlushPortBuffer (windowPort);


...putting in the appropriate error checking, of course. :)

Then, I would suggest that you move away from resources, but that's for another day, right? :) Good luck!

Fenris
2003.11.29, 06:28 AM
Oh, and there was another problem with your code when I ran it. You didn't get the resource in, so there was no image to draw. I don't know if I misinterpreted your project, but try testing it with a couple of MoveTo/DrawTo commands.

Also, the resource file you sent had the extension .rsr - it should be .rsrc to be completely fine-and-dandyô.

MidshipPowerTRD
2003.11.30, 03:13 PM
I looked at the source and cross-referenced it with my Mac Game Programming book. Turns out you used the code for OS 9 drawing. Turn to page 100 in that book (which I believe you were reading from?) for a complete instruction.

Yep...thats the one!
I let my friend borrow it on the notion that the code I written before would work.
So I got it back and turned to page 100.
I understand a bit better now ;)
So here's a rough sketch of what I have so far:

1.) Instead of creating an offscreen buffer, an OS X window will do it automatically. So I got rid of the one I created.

2.) Draw into the back buffer of the OS X window then call QDFlushPortBuffer().

3.) Call CopyBits() then call QDFlushPortBuffer().

Thats what I got from pages 100-107.

What I'm not sure of is when I call CopyBits()...whats the source bitmap and the dest bitmap since were talking about the same window?

This thing is putting up a good fight :p

Fenris
2003.11.30, 05:12 PM
Well, you don't absolutely have to call CopyBits - but here goes:

ï You can issue straight drawing commands into the back buffer (No CopyBits)
ï You can use things like DrawPicture (DrawPicture calls CopyBits for you)
ï You can composite images into another offscreen and then move that to the back buffer
ï You can load sprite sets and stuff into an offscreen, and then copy that to the back buffer.

You see, offscreens are not only for double buffering. In any case, your dest bitmap is the window port, and the source is whatever you want to copy into it.

Yes, we're talking about the same window, but when you use CopyBits, you often have multiple offscreens. For instance, assume you hvae a player sprite, two enemy sprites, and a background. Then, each of them would be separate offscreens. In this case, you would do the following four CopyBits calls:


+--[SRC]---------+---------[DST]
1: Background | Window
2: Player | Window
3: Enemy1 | Window
4: Enemy2 | Window
+------------------------------


...and then flush the port. Then, to draw text onto the screen, you would call DrawString with the port set to the window, but that doesn't require a CopyBits call. See how it works? :)

I don't know if I'm making sense here, post back if not.

MidshipPowerTRD
2003.12.02, 02:03 PM
I see how it works now thanks!

I still think something is missing since its still a blank window...maybe a setting in Interface Builder perhaps?

Here's yet another version of it, are there any bugs left?


//--------------------drawToBuffer()------------------------------------------
// draws to the back buffer of a window and then displays it on the screen

void drawToBuffer(WindowRef &mainWindow)
{
short picID = kBLKBackgroundID;
PicHandle picToOpen = GetPicture(picID);
RgnHandle theVisibleRegion = NewRgn();
CGrafPtr windowPort = GetWindowPort(mainWindow);
Rect portRect;

SetGWorld(windowPort, GetMainDevice() );

if (LockPortBits(windowPort) == noErr ) // draws to back buffer
{
GetPortBounds(windowPort, &portRect);
DrawPicture(picToOpen, &portRect);
UnlockPortBits(windowPort);
}

if (QDIsPortBuffered(windowPort) ) // flushes contents to screen
{
GetPortVisibleRegion(windowPort, theVisibleRegion);
QDFlushPortBuffer(windowPort, theVisibleRegion);
DisposeRgn(theVisibleRegion);
}

} //drawToBuffer()

I left out things that are unrelated to drawing to shorten it.

Fenris
2003.12.02, 02:55 PM
There isn't many ways to mess stuff up in Carbon IB, so that's almost completely ruled out. Are you sure that you are loading the resource correctly? When I tried your code, it didn't get loaded. To test this, swap your DrawPicture call for MoveTo (50, 50); LineTo (100, 100); and see if a line pops up.

Otherwise, mail me the thing and I'll have a look again. :)

/Ivan

Bachus
2003.12.02, 03:48 PM
Here's the code I use with Carbon:


// Init stuff
WindowRef gWindow;
IBNibRef nibRef;
OSStatus err;

err = CreateNibReference(CFSTR("main"), &nibRef);
require_noerr( err, CantGetNibRef );

err = CreateWindowFromNib(nibRef, CFSTR("MainWindow"), &gWindow);
require_noerr( err, CantCreateWindow );

SetPortWindowPort(gWindow);
ShowWindow(gWindow);

// Drawing stuff
CGrafPtr curPort;
GDHandle curDevice;

GetGWorld (&curPort, &curDevice);
SetPortWindowPort(gWindow);

// Do locking and drawing here

SetGWorld (curPort, curDevice);


Obviously the drawing code you'd use would depend on if you're using CopyBits with GWorlds, or QD drawing commands, or what have you.

MidshipPowerTRD
2003.12.07, 03:31 PM
I solved the problem...in a way...by accident.

I left a ShowWindow() call and RunApplicationEventLoop() call after I was done drawing to the window.

After seeing in Bachus' code that ShowWindow() was called before drawing, I did the same, without removing the old ShowWindow() call.

The following code only draws to the window if BOTH ShowWindow() and RunApplicationEventLoop() are called before AND after the drawing is done.

void drawToBuffer(WindowRef &mainWindow)
{
short picID = kBLKBackgroundID;
PicHandle picToOpen = GetPicture(picID);
RgnHandle theVisibleRegion = NewRgn();
GDHandle oldGDevice;
CGrafPtr oldPort;
CGrafPtr windowPort = GetWindowPort(mainWindow);
Rect portRect;

GetGWorld(&oldPort, &oldGDevice);
SetGWorld(windowPort, GetMainDevice() );

ShowWindow(mainWindow);
RunApplicationEventLoop(); // first calls

if (LockPortBits(windowPort) == noErr ) // draws to back buffer
{
GetPortBounds(windowPort, &portRect);
DrawPicture(picToOpen, &portRect);
UnlockPortBits(windowPort);
}

if (QDIsPortBuffered(windowPort) ) // flushes contents to screen
{
GetPortVisibleRegion(windowPort, theVisibleRegion);
QDFlushPortBuffer(windowPort, theVisibleRegion);
DisposeRgn(theVisibleRegion);
}

ShowWindow(mainWindow);
RunApplicationEventLoop(); // second calls

SetGWorld(oldPort, oldGDevice);
ReleaseResource(Handle(picToOpen) );

} //drawToBuffer()


When I run it, a blank window is shown.
I hit quit, it draws to the window. :???:

Then I after I hit quit again, it exits.

Wierd huh? :p

Anyone have any idea what calling ShowWindow() and RunApplicationEventLoop() twice have anything to do with drawing to the window?