Mailing Lists: Apple Mailing Lists

Image of Mac OS face in stamp
 
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: cache or draw direct?



In case this helps:

1.  With Quartz, you "draw into" 1 of 2 things: either:
     a)  CGContextRef
     this is like a window, or a PDF document, or similar.
     I think of it as a window.
     b)  CGBitmapContextRef
     this is like a gworld.  a bitmap buffer.

In Quartz, you draw into a CGContext. (end of sentence).

CGContexts can be attached to a number of different devices. For example, you can get a CGContext that allows you to draw to a window, or a context for drawing on a printer. You can create contexts for saving graphics to PDF files. Some contexts draw to devices that have pixel buffers associated with them (like a window), and some draw to devices that do not have pixel buffers (like a PDF file).

One particularly important drawing destination that is frequently used in applications is the offscreen pixel buffer. To draw into an offscreen pixel buffer using Quartz 2D, your application can create a CGBitmapContext that is connected to that pixel buffer. This is a CGContext, like any other CGContext, but it's drawing destination just happens to be a pixel-based device, and you own the pixels.

2.  In QD, you could "jump" between a window and a gworld
using CopyBits.  Draw to the screen directly, and the CopyBits
the bitmap over to a Gworld, or vice versa (usually vice versa, but
not always).

In QuickDraw, the CopyBits routine allows you to copy data between two pixel maps. Most of the time, those pixel maps were the drawing destinations of a CGrafPort. CopyBits, therefore, was a handy tool for transferring pixels from one CGrafPort to another. This included copying pixels from an offscreen GWorld to a window.


Unfortunately, not all CGContexts have pixel buffers associated with them. There is, therefore, no equivalent to CopyBits in Quartz 2D. It doesn't make sense, for example, to try and "CopyBits" from a PDF context to a window because the PDF context doesn't have any pixels to copy.

In Quartz, no more CopyBits, so you have to think ahead.  If you are
every going to want those bits for anything, you had better write
them into a CGBitmapContextRef first.

The abstraction you use in Quartz 2D to draw pixel-based structures is a CGImage. While your CGBitmapContext will allow you to use Quartz 2D to draw into a pixel buffer, in order to draw those pixels on another graphics context, you are going to have to create a CGImage from the pixels and draw that CGImage on the destination context


Once you draw to a CGContextRef (i.e. a window), it's gone.

If you draw your graphics into an offscreen pixel buffer using a CGBitmapContext, you will be able to retrieve the pixels you have drawn because you own the pixel buffer. In general, when working with CGContexts, this is not the case.


(note: you can draw into a CGContextRef until you run out of electrons, but
you won't see anything with your eyes until you do a CGContextFlush (unless you're
playing with compositing windows and such)).

You should rarely find the need to call CGContextFlush.

Instead, your application should draw in response to update events from the appropriate application framework. The framework will decide when to flush drawing forward. If you need to force one of your views to redraw, you should use HIViewSetNeedsDisplay in Carbon, or setNeedsDisplayInRect: (et al.) in Cocoa to ask the framework to send you an update event and perform your drawing in response.

In the rare instance that you have found yourself in a loop that is not passing control back to the main event loop as it should, or if you have a specific need for an intermediate drawing to be flushed to the display, then you may have to call CGContextFlush.

If you are in that situation, however, you should be careful to ensure that you are not calling CGContextFlush too often. Anything more than approximately 60 times a second could be a bad performance sink. You may end up blocking your application excessively and starving it of CPU time.

3. Some Quartz drawing commands are deadly on speed. Unless you're printing
(or making a PDF), never stroke a path when you can just fill it. StrokePath does all
kinds of intersection tests on the path so the PDF stuff comes out perfect; but for
screen drawing and refreshing on anything complicated, it will bring your app
to its knees. Figure out the "bounding path" for a given path, and fill it. Silly,
I know it sounds, but ...

If you are stroking a very complex path, one that has lots of self- intersections, and you are drawing it to a bitmap context with antialiasing enabled, then the self-intersection tests needed to correctly antialias the path during rasterization can be rather expensive. However, this will also be true if you have a very complex polygon, with lots of self intersections, and you ask the computer to fill it.


This situation should come up rather infrequently. Certainly not frequently enough to pass some sort of injunctive statements against stroking paths. In these cases, you may get better performance by splitting the complex, single path, into smaller individual paths that can be stroked, or filled independently.

The performance issue is not evident in PDF drawing (or printing, which is largely the same operation) because the PDF context has no pixels, is not antialiased, and there is no need to perform the self intersection calculations at the generating application's end. Depending on the output device, however, drawing that PDF may incur the same kind of performance issues and you would be better off splitting up the complex path if possible in that instance as well.

Similarly, cacheing to a CGBitmapContextRef (and either saving the CGBitmapContextRef
or the CGImageRef you make from it) is essential for speed - if you needed to do this in QD for your app, you probably need to do the same in Quartz.

I would advise trying to draw the graphics with Quartz first, and only jump to Quartz 2D if performance proves to be unacceptable.


For example, offscreen pixel buffers, particularly large, uncompressed offscreen pixel buffers, take up quite a lot of memory. The more memory your application uses, the more time the system will spend paging that memory in and out. Paging memory is MUCH slower than drawing simple graphics with Quartz 2D.

This is particularly true if you are drawing to the display screen (i.e. to a window). The system can optimize the Quartz 2D graphics path much more efficiently for drawing commands (drawing paths) than it can for drawing large pixel buffers. There's a good chance those large pixel buffers will have to be moved to VRAM at some point and shipping large pixel buffers over the graphics bus can be taxing.


_______________________________________________ Do not post admin requests to the list. They will be ignored. Quartz-dev mailing list (email@hidden) Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/quartz-dev/email@hidden

This email sent to email@hidden
References: 
 >cache or draw direct? (From: "Steve Mills" <email@hidden>)
 >Re: cache or draw direct? (From: Robert Curtis <email@hidden>)



Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Contact Apple | Terms of Use | Privacy Policy

Copyright © 2007 Apple Inc. All rights reserved.