Redrawing CALayer subclass when super layer is scaled

  • I'm trying to use scaling of the superlayer to implement a zoom
    feature in my CAD app. The various elements in the canvas are drawn by
    a CALayer subclass I have, which overrides drawInContext:. After the
    zoom finishes animating, I want CA to call all the sublayer's
    drawInContext: methods as necessary to re-render the layer's contents.
    Is there any way to do this, short of keeping track of all the layers
    myself and calling setNeedsDisplay on them?

    TIA,

    --
    Rick
  • On Jul 8, 2008, at 10:53 PM, Rick Mann wrote:

    > I'm trying to use scaling of the superlayer to implement a zoom
    > feature in my CAD app. The various elements in the canvas are drawn
    > by a CALayer subclass I have, which overrides drawInContext:. After
    > the zoom finishes animating, I want CA to call all the sublayer's
    > drawInContext: methods as necessary to re-render the layer's
    > contents. Is there any way to do this, short of keeping track of all
    > the layers myself and calling setNeedsDisplay on them?

    Your best solution for rendering multi-representational content in
    Core Animation is to use a CATiledLayer. It will automatically be
    called to update content as you zoom in and out (assuming you specify
    that the level of content exists). The drawing model is exactly the
    same as with a CALayer, although you can optimize the drawing a bit
    with a bit more knowledge.
    --
    David Duncan
    Apple DTS Animation and Printing
    <david.duncan...>
  • On Jul 9, 2008, at 09:56:36, David Duncan wrote:

    > Your best solution for rendering multi-representational content in
    > Core Animation is to use a CATiledLayer. It will automatically be
    > called to update content as you zoom in and out (assuming you
    > specify that the level of content exists). The drawing model is
    > exactly the same as with a CALayer, although you can optimize the
    > drawing a bit with a bit more knowledge.

    My CAD program has a number of "parts" laid out on the canvas at the
    whim of the user. These parts are interconnected by the user with
    lines comprised of a series of orthogonal line segments (it's
    schematic capture CAD). There is one instance of my CALayer subclass
    for each of these parts, and I'm thinking I'll use one for each
    connected set of line segments (not sure yet).

    Are you suggesting that each of these should subclass CATiledLayer
    instead (or provide a delegate)? I was setting the transform on the
    window's (root?) layer. Will that result in tile sublayers redrawing
    their content?

    I'll read the docs on CATiledLayer, and give this a shot. Thanks!

    --
    Rick
  • On Jul 9, 2008, at 11:09 AM, Rick Mann wrote:

    > My CAD program has a number of "parts" laid out on the canvas at the
    > whim of the user. These parts are interconnected by the user with
    > lines comprised of a series of orthogonal line segments (it's
    > schematic capture CAD). There is one instance of my CALayer subclass
    > for each of these parts, and I'm thinking I'll use one for each
    > connected set of line segments (not sure yet).

    Given what it sounds like your content is, I might consider putting
    the whole canvas on a single or small set of tiled layers (they can be
    unbounded in size).

    > Are you suggesting that each of these should subclass CATiledLayer
    > instead (or provide a delegate)? I was setting the transform on the
    > window's (root?) layer. Will that result in tile sublayers redrawing
    > their content?

    A tiled layer will trigger redraws when it detects that higher (or
    lower) resolution content is available. It caches this drawing as
    well, so you won't get called to redraw just because of a resize of
    content at that level is already available.

    --
    David Duncan
    Apple DTS Animation and Printing
    <david.duncan...>
  • On Jul 9, 2008, at 12:59:33, David Duncan wrote:

    > Given what it sounds like your content is, I might consider putting
    > the whole canvas on a single or small set of tiled layers (they can
    > be unbounded in size).

    Oh. I had thought making each individual part its own CALayer was most
    appropriate. I intend for each part to change its appearance to
    reflect selection, hover, etc, as well as be draggable, rotatable,
    etc. Animating these these changes seems to dictate making each its
    own layer.

    Now, I'm not really sure how to animate something I'm drawing directly
    (for example, when you're hovering over a part, I would like it's
    "significant contour" to pulsate, or something like that).

    --
    Rick
  • On Jul 9, 2008, at 2:08 PM, Rick Mann wrote:

    > On Jul 9, 2008, at 12:59:33, David Duncan wrote:
    >
    >> Given what it sounds like your content is, I might consider putting
    >> the whole canvas on a single or small set of tiled layers (they can
    >> be unbounded in size).
    >
    >
    > Oh. I had thought making each individual part its own CALayer was
    > most appropriate. I intend for each part to change its appearance to
    > reflect selection, hover, etc, as well as be draggable, rotatable,
    > etc. Animating these these changes seems to dictate making each its
    > own layer.
    >
    > Now, I'm not really sure how to animate something I'm drawing
    > directly (for example, when you're hovering over a part, I would
    > like it's "significant contour" to pulsate, or something like that).

    Either method should work, but you may want to consider resource usage
    in both as well. Its certainly plausible to logically pull something
    out of its parent layer (i.e. redraw it into another layer) when
    selected, then when deselected burn it into the model so its drawn on
    its parent layer again.

    But its mostly an implementation detail. I would probably go with what
    works first, then if you find a performance issue to consider a change
    then. As always, premature optimization is to be avoided.
    --
    David Duncan
    Apple DTS Animation and Printing
    <david.duncan...>
  • On Jul 9, 2008, at 12:59:33, David Duncan wrote:

    > A tiled layer will trigger redraws when it detects that higher (or
    > lower) resolution content is available. It caches this drawing as
    > well, so you won't get called to redraw just because of a resize of
    > content at that level is already available.

    So, this isn't really working they way I want it to.

    I have a canvas, which is my window's root layer. In that I have an
    arbitrary number of sublayers. If I set the scaling (via
    "transform.scale") of the root layer to x2, everything is redrawn to
    double the size (and it smoothly animates up to that scale).

    However, at the time that my drawing code is called, the CGContextRef
    that's handed to me is NOT scaled. So my drawing is done small, and
    then only scaled afterward by some blit operation. The result is very
    pixilated lines.

    I've tried making my root layer a CATiledLayer, and I've tried making
    my individual sublayers CATiledLayers (and setting levels of detail
    for both). Nothing works.

    I don't really want to have to set the scale on each sublayer; this
    seems to defeat the purpose of the drawing transform.

    The documentation says that any transform applied to a layer also
    applies to its sublayers, but this is not what I'm seeing; at least,
    not at draw time.

    Any suggestions?

    --
    Rick
  • On Jul 15, 2008, at 10:42 PM, Rick Mann wrote:

    > However, at the time that my drawing code is called, the
    > CGContextRef that's handed to me is NOT scaled. So my drawing is
    > done small, and then only scaled afterward by some blit operation.
    > The result is very pixilated lines.

    If you are dealing with CALayers, then the context covers the number
    of pixels defined by layer.bounds.size. If you are dealing with a
    CATiledLayer, then the context passed covers the number of pixels
    defined by layer.tileSize. The primary difference between a CALayer
    and a CATiledLayer is that the tiled layer has multiple
    representations for the same content, whereas a CALayer has only one
    representation (which is also why a CATiledLayer requires more
    resources than a CALayer).

    This is also why when you scale up a CALayer its content its content
    looks interpolated - its the same number of pixels as before, just
    interpolated larger. If you exceed the maximum LOD of a tiled layer,
    you will see the same thing (which if you don't set a LOD bias means
    if you scale a tiled layer above 1.0 it will also have its content
    interpolated).

    > I've tried making my root layer a CATiledLayer, and I've tried
    > making my individual sublayers CATiledLayers (and setting levels of
    > detail for both). Nothing works.

    I'm not certain why it would not, at least not with all your layers as
    tiled layers. If just your root layer is a tiled layer, then it would
    need to be transformed for its drawing code to be re-invoked, and it
    would not effect any of the sublayers directly.

    > I don't really want to have to set the scale on each sublayer; this
    > seems to defeat the purpose of the drawing transform.

    This wouldn't have the effect you would want anyway (both transforms
    would be concatenated, which would end up with an explosion of size
    unless you flattened your layer hierarchy)

    > The documentation says that any transform applied to a layer also
    > applies to its sublayers, but this is not what I'm seeing; at least,
    > not at draw time.

    Can't really say what you are or are not seeing here, I'd probably
    have to see code. If this is critical, I'd recommend filing a DTS
    incident.
    --
    David Duncan
    Apple DTS Animation and Printing
    <david.duncan...>
  • On Jul 16, 2008, at 16:23:23, David Duncan wrote:

    > Can't really say what you are or are not seeing here, I'd probably
    > have to see code. If this is critical, I'd recommend filing a DTS
    > incident.

    I'm happy to send you my code. It's not critical, in that I can find
    other ways to do what I want (other than using Core Animation). This
    is a side project, and I can't afford DTS incidents.

    I'm just disappointed that it doesn't seem possible to make Core
    Animation do what I want: redraw me at the destination resolution.

    --
    Rick
  • On Jul 16, 2008, at 16:23:23, David Duncan wrote:

    > Can't really say what you are or are not seeing here, I'd probably
    > have to see code. If this is critical, I'd recommend filing a DTS
    > incident.

    David, I checked my code again, and realized I was setting the bias to
    1 (I thought I saw that in a sample). I don't really understand how
    the numbers are interpreted, but when I set it higher (4, and then
    10), my layers started getting redrawn.

    However, I'm seeing some incorrect drawing behavior as I zoom in. I
    made a movie demonstrating this:

    http://roderickmann.org/stuff/XcodeScreenSnapz001.mov

    Any idea what I'm seeing?

    TIA,

    --
    Rick
  • On Jul 16, 2008, at 16:23:23, David Duncan wrote:

    > This is also why when you scale up a CALayer its content its content
    > looks interpolated - its the same number of pixels as before, just
    > interpolated larger. If you exceed the maximum LOD of a tiled layer,
    > you will see the same thing (which if you don't set a LOD bias means
    > if you scale a tiled layer above 1.0 it will also have its content
    > interpolated).

    So why is it that when setting a CALayer's (not CATiledLayer)
    background color, corner and border properties, those render
    beautifully, no matter what the scale of the parent layer? Clearly, at
    some point, Quartz is getting called to fill & stroke the layer's
    frame with the appropriate CTM in place for it to appear as it does
    (scaled & rotated). I wish I could get it to do the same for my
    drawing code.

    --
    Rick
  • On 21-Jul-08, at 1:20 AM, Rick Mann wrote:

    >
    > On Jul 16, 2008, at 16:23:23, David Duncan wrote:
    >
    >> This is also why when you scale up a CALayer its content its
    >> content looks interpolated - its the same number of pixels as
    >> before, just interpolated larger. If you exceed the maximum LOD of
    >> a tiled layer, you will see the same thing (which if you don't set
    >> a LOD bias means if you scale a tiled layer above 1.0 it will also
    >> have its content interpolated).
    >
    >
    > So why is it that when setting a CALayer's (not CATiledLayer)
    > background color, corner and border properties, those render
    > beautifully, no matter what the scale of the parent layer? Clearly,
    > at some point, Quartz is getting called to fill & stroke the layer's
    > frame with the appropriate CTM in place for it to appear as it does
    > (scaled & rotated). I wish I could get it to do the same for my
    > drawing code.

    all the visual properties that are 'animatable' are applied at render
    time.
  • On Jul 20, 2008, at 22:44:37, Scott Anguish wrote:

    > all the visual properties that are 'animatable' are applied at
    > render time.

    Thanks for that answer, Scott.

    So, can I not make my own drawing code an animatable property? I
    thought I could.

    I was really hoping I would be able to do this:

    1. Define arbitrary properties
    2. Have CA interpolate to new values of those properties
    3. Have CA call me to draw the contents, which I would do based on the
    "current" (mid-animation) value of those properties.
    3a. Have my drawing environment set up to draw at the destination
    resolution.

    I'm not even sure I see a way to do that by writing my own animators &
    renderers (I can't figure out how to subclass CARenderer, anyway).

    --
    Rick
  • On 21-Jul-08, at 1:52 AM, Rick Mann wrote:

    >
    > On Jul 20, 2008, at 22:44:37, Scott Anguish wrote:
    >
    >> all the visual properties that are 'animatable' are applied at
    >> render time.
    >
    >
    > Thanks for that answer, Scott.
    >
    > So, can I not make my own drawing code an animatable property? I
    > thought I could.

    Nope. That is entirely private.

    The reason I had mentioned the animatable properties is because those
    visuals (including background color and the border) are not scaled the
    same as your content, since they aren't cached..
  • On Jul 20, 2008, at 22:57:12, Scott Anguish wrote:

    > Nope. That is entirely private.
    >
    > The reason I had mentioned the animatable properties is because
    > those visuals (including background color and the border) are not
    > scaled the same as your content, since they aren't cached..

    Okay, thanks for the info. I think it's clear that I cannot really use
    CA for my drawing, which is a pity. It seems like it's "really close"
    to giving me what I want, but in the end, doesn't.

    I might still get what I need from CATiledLayer, if the drawing
    glitches I posted about earlier can be resolved.

    Thanks again.

    --
    Rick
  • On Jul 20, 2008, at 4:04 PM, Rick Mann wrote:

    > David, I checked my code again, and realized I was setting the bias
    > to 1 (I thought I saw that in a sample). I don't really understand
    > how the numbers are interpreted, but when I set it higher (4, and
    > then 10), my layers started getting redrawn.

    Its all powers of 2. Levels of detail is how many of them you have,
    starting at 1. So if you set levelsOfDetail to 3, then you have 1,
    1/2, 1/4. If you set it to 7 you add 1/8, 1/16. This effectively means
    you have smaller representations of your data.

    levelsOfDetailBias biases levelsOfDetail so that you can scale up as
    well as down. So if we go back to levelsOfDetail set to 3, but set
    levelsOfDetailBias to 1, then you get instead 2, 1, 1/2. If you set it
    to 2, then you get 4, 2, 1. Setting this value greater than
    levelsOfDetail-1 is may work (haven't tried it) but doesn't really
    give you anything.

      Effectively together you get the powers of 2 from
    2^(levelsOfDetailBias) to 2^(levelsOfDetailBias-levelsOfDetail+1) for
    your representations.

    > I'm not even sure I see a way to do that by writing my own animators
    > & renderers (I can't figure out how to subclass CARenderer, anyway).

    You wouldn't want to subclass CARenderer anyway, I can't imagine
    anything useful that you could get out of doing so.
    --
    David Duncan
    Apple DTS Animation and Printing
    <david.duncan...>