Quickest Drawing Method

  • I'm writing an app which will constantly write an image to the
    screen. I would like to know the quickest way to draw an image to the
    screen (the image needs to be composed first).

    Currently I have a borderless window the size of the screen with
    clear background, and I set the window background to an image.

    I would like it to be an animation, like the Dock resizing; how is
    the Dock drawn without a window, and how can I accomplish this?

    Cheers
    Matt
  • This is not a real answer, as the topic doesn't have an easy one (out
    of the usual 'it depends'), just a couple of details that can help you
    frame the issue.

    > I'm writing an app which will constantly write an image to the screen.
    > I would like to know the quickest way to draw an image to the screen
    > (the image needs to be composed first).
    If that's an option for you (i.e. you feel comfortable with it), I'd
    definitely use OpenGL calls: it's quite well integrated with Cocoa
    (that I assume it's a requirement, as you're posting to this list),
    you have a lot of options for compositing, and a lot of control on
    getting things on the screen as fast as possible.

    > I would like it to be an animation, like the Dock resizing; how is the Dock drawn without
    > a window, and how can I accomplish this?
    There are far more windows on the screen than a user can perceive: try
    the Quartz Debug's 'Show Window List' command to see what I mean. And,
    yes, the Dock has its windows (and quite a lot of them) too.

    HTH
    Paolo
  • > If that's an option for you (i.e. you feel comfortable with it), I'd
    > definitely use OpenGL calls: it's quite well integrated with Cocoa
    > (that I assume it's a requirement, as you're posting to this list),
    > you have a lot of options for compositing, and a lot of control on
    > getting things on the screen as fast as possible.

    My current situation is that I have a custom NSWindow with a single
    custom view in it. I do my drawing in the view's drawRect function.
    The drawing consists of:

    - stretching a couple of images to size and drawing them
    - applying a mask image (I think using a NSBezierPath to make the
    mask image would be quicker than using a black and transparent PNG
    file).

    This all happens when I call setFrame for the custom window. Is
    OpenGL still viable for an odd-shaped image (not rectangular; with
    transparencies)? Or by using Quartz and CG* functions is that the
    same as OpenGL?

    > There are far more windows on the screen than a user can perceive: try
    > the Quartz Debug's 'Show Window List' command to see what I mean. And,
    > yes, the Dock has its windows (and quite a lot of them) too.

    Yeah I checked it out. There's a window for each icon plus more! Crazy.

    Right now I have a "scheduledTimerWithTimeInterval:0.1" that calls a
    function that just updates the custom window's size; this is quite
    draining on the resources as well as jumpy. What can I do to get this
    resize function called a lot, but without killing the system? Maybe a
    run loop callback?

    Cheers
  • On 22.07.2007, at 14:18, spiderlama wrote:
    > - stretching a couple of images to size and drawing them

      Can you do this beforehand, perhaps? Or composite the images into
    each other, and thus only scale the final image once? There are
    numerous ways to optimize stuff like that in general. Are you
    cacheing your images, or recreating each time? And what are you
    actually using this for? Animations? A status display? Graphing? What?

    > - applying a mask image (I think using a NSBezierPath to make the
    > mask image would be quicker than using a black and transparent PNG
    > file).

      You'd have to profile that. Are you using CoreGraphics for the mask
    image? CG has built-in masking, but last I checked this wasn't
    exposed through NSImage or NSGraphicsContext. Also, it helps if you
    only copy and redraw those rects that actually need to be drawn. E.g.
    not drawing something is always faster than clipping it out.

      And again, depending on what gets masked and what's doesn't you
    could probably improve performance a lot by only applying the mask on
    those parts that actually need it. It also depends on what commands
    you actually use to apply the mask. Can you maybe just draw using an
    alpha channel from the start instead of masking?

    > This all happens when I call setFrame for the custom window. Is
    > OpenGL still viable for an odd-shaped image (not rectangular; with
    > transparencies)? Or by using Quartz and CG* functions is that the
    > same as OpenGL?

      I think you'd have to do a lot of the initial drawing and masking
    in Quartz, (CG or NSImage), but yes, OpenGL supports transparency and
    compositing, so it'd be easy to do the scaling and drawing on top of
    each other in OpenGL.

    > Yeah I checked it out. There's a window for each icon plus more!
    > Crazy.

      AFAIK each window is essentially backed by an OpenGL quad, at least
    when running Quartz Etreme. That's why you can drag them so quickly,
    and why drawing into one window behind another (and even into
    transparent windows) is fairly fast.

    > Right now I have a "scheduledTimerWithTimeInterval:0.1" that calls
    > a function that just updates the custom window's size;

      What?! Why? What for? Unless you're doing animation, you shouldn't
    need to use a timer to react to resizing. There are messages and
    notifications sent whenever a window moves or resizes, or you can
    make one window a subwindow of another to have them move together.
    Those will generally cause your view's drawRect: method to be called
    the next time it's needed (and whether it's needed is determined by
    calls to setNeedsDisplay:, or even better setNeedsDisplayInRect:.

    > this is quite draining on the resources as well as jumpy. What can
    > I do to get this resize function called a lot, but without killing
    > the system? Maybe a run loop callback?

      What are you actually trying to do? So far, this sounds like you're
    going about things all wrong, and that's why you're getting bad
    performance.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • What I am attempting is to make the Leopard Dock (in Tiger) by first
    hiding the Dock's background and drawing the new image behind it. I
    am using the Accessibility framework to get the Dock's position, size
    and orientation so this is why I need it to constantly update (as I
    don't know when the Dock will change).

    On 22/07/2007, at 11:50 PM, Uli Kusterer wrote:

    > On 22.07.2007, at 14:18, spiderlama wrote:
    >> - stretching a couple of images to size and drawing them
    >
    > Can you do this beforehand, perhaps? Or composite the images into
    > each other, and thus only scale the final image once? There are
    > numerous ways to optimize stuff like that in general. Are you
    > cacheing your images, or recreating each time? And what are you
    > actually using this for? Animations? A status display? Graphing? What?

    Two images are dynamically taken from the system, another is an image
    with alpha channel, and another made using a multiple of a smaller
    image. All of these depend on the size of the Dock, so no pre-composing.

    >> - applying a mask image (I think using a NSBezierPath to make the
    >> mask image would be quicker than using a black and transparent PNG
    >> file).
    >
    > You'd have to profile that. Are you using CoreGraphics for the
    > mask image? CG has built-in masking, but last I checked this wasn't
    > exposed through NSImage or NSGraphicsContext. Also, it helps if you
    > only copy and redraw those rects that actually need to be drawn.
    > E.g. not drawing something is always faster than clipping it out.

    Not much masking is happening, the bare minimum is used. I use
    [NSImage drawInRect...] with operation NSCompositeDestinationIn to
    draw the mask (alpha image). I don't know if this is CG or what (as I
    am new to this).

    > And again, depending on what gets masked and what's doesn't you
    > could probably improve performance a lot by only applying the mask
    > on those parts that actually need it. It also depends on what
    > commands you actually use to apply the mask. Can you maybe just
    > draw using an alpha channel from the start instead of masking?
    >
    >> This all happens when I call setFrame for the custom window. Is
    >> OpenGL still viable for an odd-shaped image (not rectangular; with
    >> transparencies)? Or by using Quartz and CG* functions is that the
    >> same as OpenGL?
    >
    > I think you'd have to do a lot of the initial drawing and masking
    > in Quartz, (CG or NSImage), but yes, OpenGL supports transparency
    > and compositing, so it'd be easy to do the scaling and drawing on
    > top of each other in OpenGL.
    >
    >> Yeah I checked it out. There's a window for each icon plus more!
    >> Crazy.
    >
    > AFAIK each window is essentially backed by an OpenGL quad, at
    > least when running Quartz Etreme. That's why you can drag them so
    > quickly, and why drawing into one window behind another (and even
    > into transparent windows) is fairly fast.

    So is it feasible to copy this methodology? I want (need) lightning
    fast resizing of the window (and therefore drawing of the image).

    >> Right now I have a "scheduledTimerWithTimeInterval:0.1" that calls
    >> a function that just updates the custom window's size;
    >
    > What?! Why? What for? Unless you're doing animation, you shouldn't
    > need to use a timer to react to resizing. There are messages and
    > notifications sent whenever a window moves or resizes, or you can
    > make one window a subwindow of another to have them move together.
    > Those will generally cause your view's drawRect: method to be
    > called the next time it's needed (and whether it's needed is
    > determined by calls to setNeedsDisplay:, or even better
    > setNeedsDisplayInRect:.

    I know this sucks, but as the nature of the program is to follow Dock
    size and I don't know of any callbacks that I can use.

    >> this is quite draining on the resources as well as jumpy. What can
    >> I do to get this resize function called a lot, but without killing
    >> the system? Maybe a run loop callback?
    >
    > What are you actually trying to do? So far, this sounds like
    > you're going about things all wrong, and that's why you're getting
    > bad performance.

    Sorry, but I am trying to keep my project pretty need-to-know, to
    deter the real programmers from stealing my idea!

    Cheers
  • Spider,

    Instead of using images which you composite together and draw every
    time I would suggest using a NSBezierPath as a clipping path and
    filling it with a semi-transparent color for the dock background or,
    whatever you want. That should be faster than compositing images and
    drawing them, it will certainly be more memory efficient. Not to
    mention being much more simple.

    Good luck!
    Peace, Alan

    --
    // Quotes from Alan Smith -------------------------
    "You don't forget, you just don't remember."
    "Maturity resides in the mind."
    "Silence is the Universe's greatest gift."
    "When the World realizes that personal beliefs are not something to
    argue or fight over, it shall evolve."
  • On 22.07.2007, at 16:08, spiderlama wrote:
    > What I am attempting is to make the Leopard Dock (in Tiger) by
    > first hiding the Dock's background and drawing the new image behind
    > it. I am using the Accessibility framework to get the Dock's
    > position, size and orientation so this is why I need it to
    > constantly update (as I don't know when the Dock will change).

      Well, you don't need to constantly redraw, however. Just keep
    around the old rect and size, and only when it changes, redraw. I
    think that's your main issue. Only draw when something actually
    changes. The OS caches each window in a buffer, so you don't
    generally have to redraw when the dock changes, unless it actually
    changes in a way that makes you need to change your drawings.

    >> Can you do this beforehand, perhaps? Or composite the images into
    >> each other, and thus only scale the final image once? There are
    >> numerous ways to optimize stuff like that in general. Are you
    >> cacheing your images, or recreating each time? And what are you
    >> actually using this for? Animations? A status display? Graphing?
    >> What?
    >
    > Two images are dynamically taken from the system, another is an
    > image with alpha channel, and another made using a multiple of a
    > smaller image. All of these depend on the size of the Dock, so no
    > pre-composing.

      None of these should take very long to draw, if you do it
    correctly. You *can* pre-scale all of them, though, and only recreate
    the scaled versions from the larger versions when the size actually
    changes. The less pixels in need of being scaled, the faster your
    drawing will be. But as I said above, I doubt that's your problem.

    > Not much masking is happening, the bare minimum is used. I use
    > [NSImage drawInRect...] with operation NSCompositeDestinationIn to
    > draw the mask (alpha image). I don't know if this is CG or what (as
    > I am new to this).

      I think another compositing operation might be a little faster. I
    think using a mask on the actual images while drawing should be
    faster that using a compositing mode here. But even without that I
    can't see why you would need it to draw faster.

    > So is it feasible to copy this methodology? I want (need) lightning
    > fast resizing of the window (and therefore drawing of the image).

      You must be doing something else wrong. Have you timed your code in
    Shark? I would be very surprised if the window resized too slowly.
    Keep in mind that each scrollbar is made up of several images, some
    of them drawn repeating, and they resize fast enough while dragging
    and resizing movies.

    > I know this sucks, but as the nature of the program is to follow
    > Dock size and I don't know of any callbacks that I can use.

      As I said, resize only when needed.

    > Sorry, but I am trying to keep my project pretty need-to-know, to
    > deter the real programmers from stealing my idea!

      Thanks for believing in our honesty this much, I guess ...? You
    think nobody before you had the idea of copying the Leopard Dock?
    Gee, we must not only be dishonest, but stupid, too.

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • Ok here's my updated drawing code. I'm not able to test it in shark
    right now because I'm having trouble loading the CGImageRef's. After
    pouring over Apple's Quartz drawing guide, I think it can't get any
    faster...

    - (void)drawRect:(NSRect)rect {
    float takeIn = rect.size.height * 3/4; // amount to go in at edges
    CGContextRef myContext = [[NSGraphicsContext currentContext]
    graphicsPort];    // obtain graphics context for this view

    CGContextClearRect(myContext, *(CGRect*)&rect); // clear

    // create clipping path
    CGContextBeginPath(myContext); // begin path
    CGContextMoveToPoint(myContext, 0, 0); // move to start
    CGContextAddLineToPoint(myContext, takeIn, rect.size.height); //
    left edge
    CGContextAddLineToPoint(myContext, rect.size.width - takeIn,
          rect.size.height); // right edge
    CGContextAddLineToPoint(myContext, rect.size.width, 0); // bottom
    right corner
    CGContextClosePath(myContext); // close shape
    CGContextClip(myContext); // clip to path

    CGContextDrawImage(myContext, *(CGRect*)&rect, scurve); // scurve

    NSRect frontlineRect;
    frontlineRect = NSMakeRect(0, 0, rect.size.width, 2); // we know the
    height is 2
    CGContextDrawImage(myContext, *(CGRect*)&frontlineRect,
    frontline);    // frontline
    }

    And it gets called like this:

    - (void)applicationDidFinishLaunching:(NSNotification*)theNotification {
    ...
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
    selector:@selector(resize) userInfo:nil repeats:YES];
    }

    - (void)resize {
    [window setFrame:rect display:YES animate:NO]; // window contains
    our view subclass
    }

    I wish there was a better way than using NSTimer.

    Cheers
    Matt
  • On 25.07.2007, at 02:33, spiderlama wrote:
    > - (void)applicationDidFinishLaunching:(NSNotification*)
    > theNotification {
    > ...
    > [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
    > selector:@selector(resize) userInfo:nil repeats:YES];
    > }
    >
    > - (void)resize {
    > [window setFrame:rect display:YES animate:NO];    // window contains
    > our view subclass
    > }

      I hope you're doing this conditionally? There's no reason to change
    the window's frame rect repeatedly, not to mention to a value that is
    probably the same rect each time.

      What you should do at the least is

    -(void)    resize: (NSTimer*)myTimer
    {
    NSRect currWinRect = [window frame];
    if( currWinRect.origin.x != rect.origin.x || currWinRect.origin.y !=
    rect.origin.y
        || currWinRect.size.width != rect.size.width ||
    currWinRect.size.height != rect.size.height )
    {
      [window setFrame:rect display:YES animate:NO];
    }
    }

      Also, I don't see why you would want to do this in a timer. You
    must have some place where you fill out the "rect" instance variable
    with the dock's current rect. So, what you should do is resize the
    window from there, and *only* when its size has changed.

      What your code is doing right now is needlessly burning cycles by
    telling the window to constantly recreate and re-render its whole
    back buffer, even when nothing is happening. Why?

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
  • On 25/07/2007, at 6:53 PM, Uli Kusterer wrote:

    > I hope you're doing this conditionally? There's no reason to change
    > the window's frame rect repeatedly, not to mention to a value that
    > is probably the same rect each time.

    I've checked this out. It looks like if I call setFrame on a window,
    and the frame is the same as the current frame, nothing happens.
    Therefore I am only redrawing when the Dock moves. The activity
    monitor shows the process at 0% when the Dock doesn't move. I know
    this is similar to a spin lock, but I can't think of any other method.

    > Also, I don't see why you would want to do this in a timer. You
    > must have some place where you fill out the "rect" instance
    > variable with the dock's current rect. So, what you should do is
    > resize the window from there, and *only* when its size has changed.

    For sake of modularity, I have a function that returns the size of
    the dock: (NSRect)getDockRect. And remembering resize:

    - (void)resize {
    static NSRect rect;
    rect = [self getDockRect];
    [window setFrame:rect
                display:YES
                animate:NO];
    if (rect.size.width == 0) // NSZeroRect
      dock = connectToDock();
    }

    > What your code is doing right now is needlessly burning cycles by
    > telling the window to constantly recreate and re-render its whole
    > back buffer, even when nothing is happening. Why?

    I'm pretty sure it doesn't re-render. I checked this with Quartz Debug.

    Is there a callback I can place into the run loop?

    AND... how can I find out when the application closed, so I can
    release some CGImageRef's?

    Cheers,
    Matt
  • On Jul 25, 2007, at 2:33 AM, spiderlama wrote:
    > ...I think it can't get any faster...

    it can!

    i was struggling with a similair performance issue,
    but after some experiments i've come to very promising results;

    my approach at this point is;

    - create a tiny bitmap (CGBitmapContext) that containst all elements
    needed to draw the 'big image'

    - from that image, create tiles (corners, borders body etc..) and
    cache those as CGLayers
    the docs teach us that those are actually cached on the graphics-card
    where approreate/needed/possible..

    - in the drawrect:-routine, check what needs to be drawn, and draw
    only that.

    right now i'm just using NSViews' needsToDrawRect: to check for only
    9 areas;
    the corners (4), borders (4) and the body/fill.

    and then i simply draw (if/as needed)
    the corner-tiles on a 1:1 scale,
    the border-tiles streched to fill whatever area needs to be filled,
    and same for the body.

    i guess this could be further improved using NSViews'
    getRectsBeingDrawn:count:
    but this is already blazing fast. i'm happy :)
    i suspect that these CGLayers actually did the trick..

    i didn't profile with shark, but my home-made fps-counter showed an
    improvement of around 300%,
    and memory consumption is not even worth mentioning anymore...

    On Jul 22, 2007, at 4:52 PM, Alan Smith wrote:
    > Spider,
    >
    > Instead of using images which you composite together and draw every
    > time I would suggest using a NSBezierPath as a clipping path and
    > filling it with a semi-transparent color for the dock background or,
    > whatever you want. That should be faster than compositing images and
    > drawing them, it will certainly be more memory efficient. Not to
    > mention being much more simple.

    i might be wrong, please tell me if i am (i'm not exactly an expert..),
    but i find it a very strange idea that 'rendering' vectors (into
    bitmaps),
    and then using those for compositing,
    would be faster than just copying exisiting bitmap-data around.
    especially when that bitmap data can be cached in hardware,
    and even more when this compositing, and btw. also scaling can all be
    done on the GPU.

    i don't believe NSBezierpaths can benefit from hardware acceleration..

    €.02
    .a
  • sorry, forgot the secret (no, it's not a leapard Dock..)

    below;

    .a

    On Jul 27, 2007, at 3:38 AM, arri wrote:

    > On Jul 25, 2007, at 2:33 AM, spiderlama wrote:
    >> ...I think it can't get any faster...
    >
    > it can!
    >
    > i was struggling with a similair performance issue,
    > but after some experiments i've come to very promising results;
    >
    > my approach at this point is;
    >
    > - create a tiny bitmap (CGBitmapContext) that containst all
    > elements needed to draw the 'big image'
    >
    > - from that image, create tiles (corners, borders body etc..) and
    > cache those as CGLayers
    > the docs teach us that those are actually cached on the graphics-
    > card where approreate/needed/possible..
    >
    > - in the drawrect:-routine, check what needs to be drawn, and draw
    > only that.
    >
    > right now i'm just using NSViews' needsToDrawRect: to check for
    > only 9 areas;
    > the corners (4), borders (4) and the body/fill.
    >
    > and then i simply draw (if/as needed)
    > the corner-tiles on a 1:1 scale,
    > the border-tiles streched to fill whatever area needs to be filled,
    > and same for the body.
    >
    > i guess this could be further improved using NSViews'
    > getRectsBeingDrawn:count:
    > but this is already blazing fast. i'm happy :)
    > i suspect that these CGLayers actually did the trick..
    >
    > i didn't profile with shark, but my home-made fps-counter showed an
    > improvement of around 300%,
    > and memory consumption is not even worth mentioning anymore...
    >
    >
    >
    > On Jul 22, 2007, at 4:52 PM, Alan Smith wrote:
    >> Spider,
    >>
    >> Instead of using images which you composite together and draw every
    >> time I would suggest using a NSBezierPath as a clipping path and
    >> filling it with a semi-transparent color for the dock background or,
    >> whatever you want. That should be faster than compositing images and
    >> drawing them, it will certainly be more memory efficient. Not to
    >> mention being much more simple.
    >
    >
    > i might be wrong, please tell me if i am (i'm not exactly an
    > expert..),
    > but i find it a very strange idea that 'rendering' vectors (into
    > bitmaps),
    > and then using those for compositing,
    > would be faster than just copying exisiting bitmap-data around.
    > especially when that bitmap data can be cached in hardware,
    > and even more when this compositing, and btw. also scaling can all
    > be done on the GPU.
    >
    > i don't believe NSBezierpaths can benefit from hardware acceleration..
    >
    >
    > €.02
    > .a_______________________________________________

    - (void)drawRect:(NSRect)rect {
    if(!didInit) [self createBackgroundImage];
    NSRect  srect = [self bounds];
    CGRect  trect;    // target rectangle
    float  vwidth = srect.size.width; // view width
    float  vheight = srect.size.height; // view hight

    CGContextRef context = (CGContextRef)[[NSGraphicsContext
    currentContext] graphicsPort];
    CGContextSetInterpolationQuality(context,kCGInterpolationNone);

    trect = CGRectMake(kTileSize,kTileSize,vwidth-(2*kTileSize),vheight-
    (2*kTileSize));
    if([self needsToDrawRect: *(NSRect *)&trect ])
    CGContextDrawLayerInRect(context,trect,players[4]);
    else NSLog(@"%d", 4);

    // right edge
    trect = CGRectMake(vwidth-kTileSize,kTileSize,kTileSize,vheight-
    (2*kTileSize));
    if([self needsToDrawRect: *(NSRect *)&trect ])
    CGContextDrawLayerInRect(context,trect,players[5]);
    else NSLog(@"%d", 5);

    // bottom edge
    trect = CGRectMake(kTileSize,0,vwidth-(2*kTileSize),kTileSize);
    if([self needsToDrawRect: *(NSRect *)&trect ])
    CGContextDrawLayerInRect(context,trect,players[7]);
    else NSLog(@"%d", 7);

    // TOP edge
    trect = CGRectMake(kTileSize,vheight-kTileSize,vwidth-
    (kTileSize*2),kTileSize);
    if([self needsToDrawRect: *(NSRect *)&trect ])
    CGContextDrawLayerInRect(context,trect,players[1]);
    else NSLog(@"%d", 1);

    // left edge
    trect = CGRectMake(0,kTileSize,kTileSize,vheight-(2*kTileSize));
    if([self needsToDrawRect: *(NSRect *)&trect ])
    CGContextDrawLayerInRect(context,trect,players[3]);
    else NSLog(@"%d", 3);

    // top-right corner
    trect = CGRectMake(vwidth-kTileSize,vheight-
    kTileSize,kTileSize,kTileSize);
    if([self needsToDrawRect: *(NSRect *)&trect ])
    CGContextDrawLayerInRect(context,trect,players[2]);
      else NSLog(@"%d", 2);

    // bottom-right coner
    trect = CGRectMake(vwidth-kTileSize,0,kTileSize,kTileSize);
    if([self needsToDrawRect: *(NSRect *)&trect ])
    CGContextDrawLayerInRect(context,trect,players[8]);
    else NSLog(@"%d", 8);

    // bottom left
    trect = CGRectMake(0,0,kTileSize,kTileSize);
    if([self needsToDrawRect: *(NSRect *)&trect ] && first)
    CGContextDrawLayerInRect(context,trect,players[6]);
    else NSLog(@"%d", 6);

    // top right
    trect = CGRectMake(0,vheight-kTileSize,kTileSize,kTileSize);
    if([self needsToDrawRect: *(NSRect *)&trect ]&&first)
    CGContextDrawLayerInRect(context,trect,players[0]);
    else NSLog(@"%d", 0);

    frc++;
    }

    - (void)createBackgroundImage {
    didInit    = YES;
    int  i  = 0;
    float  radius  = 7;
    NSRect  rect  = NSMakeRect( 0, 0, (3*kTileSize),  (3*kTileSize) );
    NSRect  irect  = NSMakeRect( 5, 6, (3*kTileSize)-10,
    (3*kTileSize)-7 );
    float  rx  = irect.origin.x;
    float  ry  = irect.origin.y;
    float  rw  = irect.size.width;
    float  rh  = irect.size.height;
    float  cwidth  = rect.size.width;
    float  cheight  = rect.size.height;

    const int bytesPerPixel = 4;
    const int rowBytes  = cwidth * bytesPerPixel;
    void*  imageData  = malloc( (rowBytes * cheight) );

    CGColorSpaceRef cspace  = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bmInfo  = kCGBitmapByteOrder32Host |
    kCGImageAlphaPremultipliedFirst;

    CGContextRef context  = CGBitmapContextCreate(imageData,
            cwidth,
            cheight,
            8,  // bits per component
            rowBytes,
            cspace,
            bmInfo  );

    CGContextClearRect( context, CGRectMake
    (0,0,rect.size.width,rect.size.height) );
    assert( CGContextIsPathEmpty(context) );
    CGContextSetShouldAntialias( context,1);

    // path
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddArc(path, NULL, rx+radius, ry+radius, radius, radians(180),
    radians(270),0);
    CGPathAddArc(path, NULL, rw+rx-radius, ry+radius, radius, radians
    (270),    radians(360),0);
    CGPathAddLineToPoint(path,NULL,rw+rx,rh+ry);
    CGPathAddLineToPoint(path,NULL,rx,rh+ry);
    CGPathCloseSubpath(path);

    // fill with dropshadow
    float clr[4] = {0.0,0.0,0.0,0.5};
    CGColorRef shadowColor = CGColorCreate(cspace,clr);
    CGContextSetShadowWithColor(context,CGSizeMake(0.0,-2.0),
    5.0,shadowColor);
    CGContextAddPath(context,path);
    CGContextSetRGBFillColor(context,kJAWhite,kJAWhite,kJAWhite,kJAAlpha);
    CGContextFillPath(context);
    // clear fill area
    CGContextAddPath(context,path);
    CGContextClip(context);
    CGContextClearRect(context,CGRectMake
    (0,0,rect.size.width,rect.size.height));
    // fill again without shadow
    CGContextSetShadowWithColor(context,CGSizeZero,0,NULL);
    CGContextAddPath(context,path);
    CGContextSetRGBFillColor
    (context,kJAWhite,kJAWhite,kJAWhite,kJAAlpha-.1);
    CGContextFillPath(context);

    // thin outline
    CGContextSetLineWidth(context, 6.0);
    CGContextSetShadowWithColor(context,CGSizeMake(0,0),1,shadowColor);
    CGContextAddPath(context,path);
    CGContextSetRGBStrokeColor(context,1,1,1,1);
    CGContextStrokePath(context);

    // resize corner
    float stx, enx, len;
    len = (rw/5);
    if (len>30.) len=30.;
    stx = rx + (rw/2.) - (len/2.);
    enx = rx + (rw/2.) + (len/2.);
    CGContextSetShadowWithColor(context,CGSizeMake(0,0),2,shadowColor);
    CGContextSetRGBStrokeColor(context,1.,1.,1.,.99);
    CGContextSetLineWidth(context, 6.);
    CGContextMoveToPoint(context,rx+0,ry+radius+5);
    CGContextAddArc(context, rx+radius+1,ry+radius+1,radius+1,radians
    (180),radians(270),0);
    CGContextAddLineToPoint(context,rx+radius+5,ry+0);
    CGContextStrokePath(context);

    CGColorRelease(shadowColor);

    CGContextFlush(context);

    CGImageRef smallImage = CGBitmapContextCreateImage(context);

    CGContextRelease(context);

    free(imageData);

    // create CGLayers
    CGRect  srcR;
    CGImageRef  tile;
    CGRect  tileR = CGRectMake(0,0,kTileSize,kTileSize);
    CGContextRef lcontext;

    for (i=0; i<9; i++) {
      int ox = (i%3) * kTileSize;
      int oy = (i/3) * kTileSize;
      srcR = CGRectMake(ox,oy,kTileSize,kTileSize);
      tile = CGImageCreateWithImageInRect(smallImage,srcR);
      players[i] = CGLayerCreateWithContext(context,CGSizeMake
    (kTileSize,kTileSize),NULL);
      lcontext = CGLayerGetContext(players[i]);
      CGContextDrawImage(lcontext,tileR,tile);
      CGContextFlush(lcontext);
    }
    CGImageRelease(smallImage);
    }
  • On 27/07/2007, at 11:38 AM, arri wrote:
    > my approach at this point is;
    >
    > - create a tiny bitmap (CGBitmapContext) that containst all
    > elements needed to draw the 'big image'
    >
    > - from that image, create tiles (corners, borders body etc..) and
    > cache those as CGLayers
    > the docs teach us that those are actually cached on the graphics-
    > card where approreate/needed/possible..
    >
    > - in the drawrect:-routine, check what needs to be drawn, and draw
    > only that.

    So if I was to apply this methodology to my app, I should:

    - create a CGBitmapContext (how do I make it small/contain all the
    images I want to draw)
    - create a CGLayer for each of the images I need to draw
    - in drawRect make the path and then draw the images as needed.

    I don't know if this method would increase the drawing speed. If a
    small part of my window needs drawing, then every image has to be
    applied again (as every image is applied to the full size of the
    window). Meaning that no steps can be skipped for different drawing
    rectangles. Right now I:

    - create CGImages of the images
    - in drawRect make the path and then draw the images.

    I think the "lagginess" comes from the fact that I am continually
    changing the window's size.

    Cheers
    Matt
  • hoi matt,

    maybe i don't understand what exactly you are drawing..
    in my case, the 'tiny bitmap that containst all elements' looks
    something like this;(with dividing lines to illustrate the 'tiles')

    > If a small part of my window needs drawing, then every image has to
    > be applied again

    maybe you are compositing several layers with large images.
    in that case using CGLayers can still speed-up drawing, as long as
    you re-use them (see docs).
    so as long as the images (and paths and masks..) you use to composit
    are the same, use CGLayers.

    .a

    On Jul 28, 2007, at 9:52 AM, spiderlama wrote:
    >
    > On 27/07/2007, at 11:38 AM, arri wrote:
    >> my approach at this point is;
    >>
    >> - create a tiny bitmap (CGBitmapContext) that containst all
    >> elements needed to draw the 'big image'
    >>
    >> - from that image, create tiles (corners, borders body etc..) and
    >> cache those as CGLayers
    >> the docs teach us that those are actually cached on the graphics-
    >> card where approreate/needed/possible..
    >>
    >> - in the drawrect:-routine, check what needs to be drawn, and draw
    >> only that.
    >
    > So if I was to apply this methodology to my app, I should:
    >
    > - create a CGBitmapContext (how do I make it small/contain all the
    > images I want to draw)
    > - create a CGLayer for each of the images I need to draw
    > - in drawRect make the path and then draw the images as needed.
    >
    > I don't know if this method would increase the drawing speed. If a
    > small part of my window needs drawing, then every image has to be
    > applied again (as every image is applied to the full size of the
    > window). Meaning that no steps can be skipped for different drawing
    > rectangles. Right now I:
    >
    > - create CGImages of the images
    > - in drawRect make the path and then draw the images.
    >
    > I think the "lagginess" comes from the fact that I am continually
    > changing the window's size.
    >
    > Cheers
    > Matt