Drawing into a window's GWorld

Post: #1
Well, I was writing a blitterÖ and it all seemed to be going ok, copying from one GWorld to another without a hitch, when I tried to convince it to draw from a GWorld to a window. This is what I did:

void DrawGWorldToWindow (GWorldPtr theGWorld, WindowRef theWindow)
    PixMapHandle        srcPixMap, dstPixMap;
        CGrafPtr        winPort, storePort;
        GDHandle        storeDevice;
    Rect            srcBounds, dstBounds;
    srcPixMap = GetGWorldPixMap(theGWorld);
    GetPixBounds(srcPixMap, &srcBounds);
        dstBounds = srcBounds;
        if (g->pref.border && theWindow == g->theWindow)
            OffsetRect(&dstBounds, BORDER_WIDTH, BORDER_HEIGHT);
        winPort = GetWindowPort(theWindow);
        dstPixMap = GetPortPixMap(winPort);
        GetGWorld(&storePort, &storeDevice);
        SetGWorld(winPort, NULL);
    [blitting function]
    /*    QuickCopy(srcPixMap, dstPixMap, &srcBounds, &dstBounds); */
        SetGWorld(storePort, storeDevice);

When I use CopyBits(), it draws to the window fine.

When I use my own function, it draws on top of everything on the main screen, even when the app is in the background, into the dstRect as interpreted in global co-ordinates rather than local (so the window gets drawn at the top left of the screen, eclipsing the menubar).

If I offset the dstRect to where it should be on screen, the picture is clipped to the window's port bounds where the port bounds are interpreted wrong in the same way as before; so I get the pixels drawn in the right place on screen, but the clipping's wrong, and you can't see anything except where the window overlaps.

This is probably because my blitter gets the dstPixMap's bounds and clips against themÖ but why does it draw directly onto screen in the first place? I asked for the window's port, after all, and I thought that the window's port was, well, the window's port, not the whole-screen-the-window-lives-on's portÖ

It's very vexing - All I do in my blitter is take two PixMapHandles and two Rect *s, clip the Rects a bit, get the PixMaps' Depths, RowBytes's and BaseAddrs, and BlockMoveData() with the information gleaned.

So, presumably there's something that CopyBits does that I should do tooÖ does anyone know what it might be? Does CopyBits somehow know when I'm drawing to a window and compensate? Surely not.

ButÖ all suggestions welcome, and thanks in advance.
Quote this message in a reply
Post: #2
First of all, do not underestimate CopyBits, it is a very complex function handling a lot of stuff for you if it needs to.

Internally, CopyBits handles the origins of both ports and compensates for them. Every window has a port, and since you have to set the current GWorld or Port, CB uses that port as the 0,0 position where to start drawing.

Additionaly, CB handles colorspace translations, dithering, scaling, clipping, memory alignment issues, images spanning multiple monitors, various modes of combing source and dest bits and most important of all: hardware acceleration. From 10.2 on (and 9.2.2 and less), CopyBits will use the graphics hardware to copy the graphics for you. When this happens, blits will be very fast. You will not match CB's speed with any assembler loop or whatever you may think of.

Just look for "optimizing copybits" on the 'net to find tips how to get optimal results from CopyBits. Really, there is no excuse not to use CopyBits for regular 2D blits. Your app will break or cause visual anomalies on people's systems if you write your own blitter. Use CopyBits. Did I mention to use CopyBits?

Drawing directly to the screen, other than leaving nasty ink marks on your monitor, has been discouraged by Apple for a long time now and in OSX, it is even more important to let go of the "Iron Man" graphics approach and just letting the OS do the blitting for you.

Of course, if you use OpenGL for your 2D stuff, you get rotation, scaling, color tinting, skewing and everything for free! And it's fast too!

Yay OpenGL and CopyBits! Sing their praises, and don't draw directly to the screen.
Quote this message in a reply
Post: #3
Ösound advice, indeed. Thank you; I appreciate your concern. But I want to write a blitterÖ it's not meant to be bigger or cleverer than CopyBits; I'm well aware that CopyBits is an excellent general-purpose blitter.

However, writing my own specialised one is an interesting challenge to wind down after uDG with, and I don't think it'll do me any harm to get a bit more of an understanding of what's involvedÖ regardless of all our lovely modern abstractions, those bits and bytes are still lurking underneath, and I like to maintain some sort of awareness of them.

I'll take your advice about not writing to the screen, but I think I'll keep playing with thisÖ it's fun.
Quote this message in a reply
Post: #4
Well, now that the formalities are out of the way, we can indeed investigate custom blitters.

In essence, writing them is not that hard, provided that you set sound limits within which your blitter will work. Multi-monitor support, bitdepth conversions, etc. are things to leave to CopyBits.

You can write a custom blitter in 2 environments.
In the first you completely integrate your code within QuickDraw and the whole pixmap, GWorld, GrafPort and windowing system which it uses. If you do it this way, you just access the currently set port for coord translations and other info and (optionally) clip your images to the boundaries as set by the ports.

Alternatively, you can setup your own environment and handle the creation and maintenance of images and coordinate systems yourself. This is a bit more work, but building such a system, which will basically be a simplified QuickDraw is interesting to make as you realize what is needed in a 2D graphics subsystem. (Not a lot for a bare bones functional system, luckily).

The only thing you really need to remember is that you have offscreen and onscreen image buffers. Typically, only the onscreen image buffers have location info linked to them as they need to have their physical place on the screen.

If you look at a GrafPort structure in QuickDraw.h, you'll see that it is just a bitmap (or pixmap, which is a deep bitmap) along with colorspace definitions, positioning info, and some other QD stuff.

Every window has a GrafPort, which is the canvas of the window. So if you choose to create a blitter that will blend in with QD, you can use any GrafPort, GWorld (a souped up offscreen grafport) or window with your blitter if you just use the info right.

Finally, you'll have to understand that offscreen ports are stored in normal RAM and onscreen ports are in VRAM, basically. Accessing VRAM just with normal loads and stores is slow as the memory accesses have to go through the PCI/AGP port. Therefore, it is usually good to use custom drawing routines for your image transfers between your (own) offscreen ports and then using CopyBits or a similar technique to let the DMA transfer blast your big rect of pixels to VRAM.

Of course, many combinations are possible. QD, for example, lets you create a GWorld in VRAM or AGP memory and for different means it can be wise to have an offscreen in VRAM, for example, to blindlingly fast copy a fixed bg image to your onscreen port (VRAM to VRAM copies are fast) and then doing small copies from normal offscreen ports to your onscreen port.

A fun way to start is I think to just to set the screen to a fixed depth and size (either manually or with DrawSprocket/CoreGraphics) and creating an app that just assumes this mode and starts blasting pixels. Write some demo effects and be a hero! I still have some (old) demo effect code lying around, I'll put it up a bit later.

Let me know if you build any fun stuff.
Quote this message in a reply
Post: #5
Thanks even moreÖ I'll tell you if I do anything interesting.

Quote this message in a reply
Post Reply