Re: NSView drawRect: QuickDraw, CoreGraphics, OpenGL ? DP.2
Re: NSView drawRect: QuickDraw, CoreGraphics, OpenGL ? DP.2
- Subject: Re: NSView drawRect: QuickDraw, CoreGraphics, OpenGL ? DP.2
- From: Nat! <email@hidden>
- Date: Thu, 31 Oct 2002 01:04:39 +0100
<continuing from part 1>
Well, back to your question...
You should keep the following things in mind whenever you want to draw a
bitmap as fast as possible with Quartz:
1) Be absolutely sure that your bitmap data is in one of the formats
which
Quartz understands natively. I.e. use 32bit true color with the alpha
channel in the _highest_ 8bits (ARGB / XRGB). Don't use RGBA or RGBX as
this
will easily cut your drawing performance by half because Quartz has to
spend
extra time in converting from your "wrong" format into one of its native
formats. You specify the relative location of the alpha channel and
whether
your data contains an alpha channel at all with the CGImageAlphaInfo
enumeration. However, never use kCGImageAlphaNone for a bitmap without
an
alpha channel because it maps to kCGImageAlphaNoneSkipLast which is one
of
the inefficient bitmap formats...
2) Use a direct access data provider for your bitmap data on MacOS X
10.2. A
direct access data provider can simply return a pointer to the bitmap
data,
thus it avoids the otherwise necessary copy step.
3) In case your CTM contains scaling, rotation or skew (I'm not sure
about
sub-pixel translations) either create your CGImage with interpolation
turned
off, or select a low interpolation quality in your CGContext (i.e.
kCGInterpolationLow). Image interpolation can be a very expensive
process
depending on the type of pixel filter which the context implementation
uses.
4) Create your CGImage with the same color space than the output device
has.
You may need to use ColorSync directly in order to convert your pixels
from
the source color space to the device color space.
5) In case your bitmap is fully opaque, switch the alpha composition
function to NSCompositeCopy by adding the following call to your code
before
you draw the CGImage:
CGContextSetCompositeOperation(ctx, NSCompositeCopy);
The prototype is:
void CGContextSetCompositeOperation(CGContextRef, int);
WARNING: The above call is currently (still) a privat Quartz call. It
may
stop to work in the future, although is has been available since at
least
MacOS X 10.0. And yes, I have _absolutely_ no understanding for the fact
that _basic_ and _fundamental_ functions like this one are still
private.
A possible Quartz bitmap drawing example code might look like the
following
(works only on MacOS X 10.2 'cause of the direct access provider stuff):
static const void *get_byte_pointer(void *bitmap)
{
return bitmap;
}
CGDataProviderDirectAccessCallbacks gProviderCallbacks = {
get_byte_pointer,
NULL,
NULL,
NULL
};
- (void)drawRect: (NSRect)rect
{
CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort];
if(_bitmap == NULL) {
size_t numBytes = kBitmapWidth * kBitmapHeight * 4;
CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
_bitmap = malloc(numBytes);
_provider = CGDataProviderCreateDirectAccess(_bitmap,
numBytes, &gProviderCallbacks);
_cgImage = CGImageCreate(kBitmapWidth, kBitmapHeight,
8, 32, kBitmapWidth * 4, cs,
kCGImageAlphaNoneSkipFirst, _provider, NULL,
FALSE, kCGRenderingIntentAbsoluteColormetric);
CGColorSpaceRelease(cs);
// fill bitmap by either loading an image into it or by
// wrapping a CGBitmapContext around it and using that
// for drawing into it with Quartz calls
}
CGContextDrawImage(ctx,
CGRectMake(0.0, 0.0, kBitmapWidth, kBitmapHeight},
_cgImage);
}
Caution: Don't work with bitmaps which are too small because Quartz has
a
relatively high base overhead. Thats due to things like
locking/unlocking
the window backbuffer or checking whether the pixels must be format or
color
converted. The overhead is neglectable for bitmap sizes > 32x32.
If your code already takes all the above points into account and in fact
Sampler.app tells you that the vast majority of blitting time is spent
in
one of the XXX_image_mark Quartz routines, but you're still not
satisfied
with drawing performance, then its time to think about moving to OpenGL.
The first step in this case should be the clarification into which of
the
following categories your blitting needs fall:
A) You need to draw a _static_ bitmap very often per second.
The important aspect here is that the bitmap stays as it is for a very
long
time and just needs to be drawn in various different places many times
per
second.
The ideal solution to this problem is that we move the bitmap data
after its
creation as close as possible to the GPU. This can be accomplished in
OGL by
uploading the bitmap as a texture into VRAM. The bitmap is then
displayed by
drawing an OGL quad with texture mapping turned on.
B) You need to draw a _dynamic_ bitmap often per second.
Contrary to above, the bitmap changes very often, in the extreme case
once
per frame. Here, the goal should be to get the bitmap as fast as
possible to
the GPU. This can be done in OGL by taking advantage of Apple's special
texture range extension which allows you to texture from RAM via AGP.
Documentation about the APPLE_texture_range extension can be found on
Apple's OGL home page. The "OpenGL Image" example from Apple uses this
technique in order to display large bitmaps via OGL and to allow the
rotation, zooming and translation of the bitmap in real-time.
Regards,
Dietmar Planitzer
------------------------------------------------------
Certainly there is no hunting like the hunting of man,
and those who have hunted armed men long enough and
like it, never really care for anything else
thereafter. -- Hemingway
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.