FROM : Troy Stephens
DATE : Thu Feb 07 20:25:24 2008
On Feb 6, 2008, at 7:26 PM, alex wrote
> This is the perfect answer!! Thank you.
>
> One more question (sorry):
>
> How would one approach doing this exact thing with Layers? Or is
> that not recommended and it should be done in the view like below?
This could certainly be done using layers -- but if you aren't already
using Core Animation-based rendering in your UI for other reasons, I'd
recommend sticking with the plain NSView approach, as a CA renderer is
an awfully big hammer to bring in just to draw a simple set of moving
level bars.
If you did go the layer-based route, an efficient approach would be to
create a layer for each level bar, whose content was provided as a
CGImage or as a backgroundColor (which can be a solid or pattern
color), and change the layer's bounds height (or width) when the level
value changes. To suppress the default implicit layer bounds
animation so as to get an immediate response to level changes, you'd
make the change inside a CATransaction within which you disable actions:
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithBool:YES]
forKey:kCATransactionDisableActions];
levelLayer.bounds = CGRectMake(0, 0, LEVEL_BAR_WIDTH,
heightForNewLevelValue);
[CATransaction commit];
Providing the layer content as described effectively allows the redraw
work to be offloaded to the GPU, which should result in good
performance.
Troy
> On Feb 6, 2008, at 4:46 PM, Troy Stephens wrote:
>
>> On Feb 6, 2008, at 9:27 AM, alex wrote:
>>> Hi there,
>>>
>>> Thanks for this answer- From what I see below this looks like a
>>> technique to animate the change from one value to another (and the
>>> result being the width of the line animating).
>>>
>>> I'm not sure if this will work for me- I was really looking for a
>>> way to simply draw a rectangle that represents the meter level. I
>>> don't want to animate this per se. Just the act of drawing the
>>> rectangle repeatedly is the animation. This gets called
>>> periodically and ideally should not interfere with user
>>> interaction. What I have now works but the CPU usage is too high
>>> for me and I worry that this will become more of an issue later.
>>> My testing has led me to blame calling setNeedsDisplay, then
>>> waiting for the event loop, then the drawRect call (and all the
>>> setup work around those calls).. All that seems too slow and
>>> complex compared to what I "used to" do...
>>>
>>> What I want to do is have a timer or thread that looks at a value
>>> and draws a rectangle to represent it. Effectively I want to grab
>>> a CGContextRef and draw to it periodically without all the
>>> repeated setup work- for instance, create a transparent overlay
>>> view, grab its CGContextRef and then draw when I need to (outside
>>> the event loop). Is this still possible? Or recommended? I
>>> promise I won't draw more often than the refresh rate. :)
>>
>> Yes, it's quite possible. If you just want a bar display that
>> immediately updates each time the value changes, you can lock focus
>> on the view and redraw immediately each time the value changes.
>> e.g.:
>>
>> if ([view lockFocusIfCanDraw]) {
>> NSRect bounds = [view bounds];
>> CGFloat y = bounds.origin.y + (level value from 0.0 to 1.0) *
>> bounds.size.height;
>>
>> [[NSColor blackColor] set];
>> NSRectFill(bounds));
>> [[NSColor greenColor] set];
>> NSRectFill(NSMakeRect(bounds.origin.x, bounds.origin.y,
>> bounds.size.width, y - bounds.origin.y));
>>
>> [view unlockFocus];
>> }
>>
>>> How does Core Animation do its thing without hogging the CPU I
>>> wonder?
>>
>> By virtue of a background render thread, that offloads much work to
>> the GPU.
>>
>> Troy
>>
DATE : Thu Feb 07 20:25:24 2008
On Feb 6, 2008, at 7:26 PM, alex wrote
> This is the perfect answer!! Thank you.
>
> One more question (sorry):
>
> How would one approach doing this exact thing with Layers? Or is
> that not recommended and it should be done in the view like below?
This could certainly be done using layers -- but if you aren't already
using Core Animation-based rendering in your UI for other reasons, I'd
recommend sticking with the plain NSView approach, as a CA renderer is
an awfully big hammer to bring in just to draw a simple set of moving
level bars.
If you did go the layer-based route, an efficient approach would be to
create a layer for each level bar, whose content was provided as a
CGImage or as a backgroundColor (which can be a solid or pattern
color), and change the layer's bounds height (or width) when the level
value changes. To suppress the default implicit layer bounds
animation so as to get an immediate response to level changes, you'd
make the change inside a CATransaction within which you disable actions:
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithBool:YES]
forKey:kCATransactionDisableActions];
levelLayer.bounds = CGRectMake(0, 0, LEVEL_BAR_WIDTH,
heightForNewLevelValue);
[CATransaction commit];
Providing the layer content as described effectively allows the redraw
work to be offloaded to the GPU, which should result in good
performance.
Troy
> On Feb 6, 2008, at 4:46 PM, Troy Stephens wrote:
>
>> On Feb 6, 2008, at 9:27 AM, alex wrote:
>>> Hi there,
>>>
>>> Thanks for this answer- From what I see below this looks like a
>>> technique to animate the change from one value to another (and the
>>> result being the width of the line animating).
>>>
>>> I'm not sure if this will work for me- I was really looking for a
>>> way to simply draw a rectangle that represents the meter level. I
>>> don't want to animate this per se. Just the act of drawing the
>>> rectangle repeatedly is the animation. This gets called
>>> periodically and ideally should not interfere with user
>>> interaction. What I have now works but the CPU usage is too high
>>> for me and I worry that this will become more of an issue later.
>>> My testing has led me to blame calling setNeedsDisplay, then
>>> waiting for the event loop, then the drawRect call (and all the
>>> setup work around those calls).. All that seems too slow and
>>> complex compared to what I "used to" do...
>>>
>>> What I want to do is have a timer or thread that looks at a value
>>> and draws a rectangle to represent it. Effectively I want to grab
>>> a CGContextRef and draw to it periodically without all the
>>> repeated setup work- for instance, create a transparent overlay
>>> view, grab its CGContextRef and then draw when I need to (outside
>>> the event loop). Is this still possible? Or recommended? I
>>> promise I won't draw more often than the refresh rate. :)
>>
>> Yes, it's quite possible. If you just want a bar display that
>> immediately updates each time the value changes, you can lock focus
>> on the view and redraw immediately each time the value changes.
>> e.g.:
>>
>> if ([view lockFocusIfCanDraw]) {
>> NSRect bounds = [view bounds];
>> CGFloat y = bounds.origin.y + (level value from 0.0 to 1.0) *
>> bounds.size.height;
>>
>> [[NSColor blackColor] set];
>> NSRectFill(bounds));
>> [[NSColor greenColor] set];
>> NSRectFill(NSMakeRect(bounds.origin.x, bounds.origin.y,
>> bounds.size.width, y - bounds.origin.y));
>>
>> [view unlockFocus];
>> }
>>
>>> How does Core Animation do its thing without hogging the CPU I
>>> wonder?
>>
>> By virtue of a background render thread, that offloads much work to
>> the GPU.
>>
>> Troy
>>
| Related mails | Author | Date |
|---|---|---|
| alex | Feb 5, 21:30 | |
| Bill Dudney | Feb 5, 21:53 | |
| alex | Feb 6, 18:27 | |
| Troy Stephens | Feb 7, 01:46 | |
| alex | Feb 7, 04:26 | |
| Troy Stephens | Feb 7, 20:25 | |
| alex | Feb 8, 04:18 |






Cocoa mail archive

