initializing images with view contents, then displaying those images instead of the view

  • Hi,

    In an effort to improve efficiency in my current project, I have decided to create NSImages to represent the contents of some of the subviews in the application.  This is a somewhat well-documented process that I've read about in several different places.

    These views contain dynamically positioned text, and when many of them are on screen and thousands of individual characters are being drawn using layoutmanagers etc, the application can gradually slow down to unacceptable levels (~4 frames per second or lower).  The solution I've come up with so far works, except that the image being drawn in the view does not exactly match the one which was copied using the view's contents...  Its width and height seem to vary by 1-2 pixels.

    This may have something to do with the fact that the view frame sometimes has non-integer width and height.  I'm not sure if NSImages are required to have integer NSSize values...

    the code for creating the view's image (sorry about the bad code formatting):

    [self lockFocus];
        NSBitmapImageRep *tempBitmap = [[[NSBitmapImageRep alloc] initWithFocusedViewRect: imgFrame] autorelease];
        [self unlockFocus];
        NSImage *tempImage = [[[NSImage alloc] initWithSize:imgFrame.size] autorelease],
            *resultImage = [[[NSImage alloc] initWithSize:imgFrame.size] autorelease];

        [tempImage addRepresentation: tempBitmap];
        if (inAlpha == 1.0)
            return tempImage;

        [resultImage lockFocus];
        [tempImage  drawInRect: (NSRect){0, 0, imgFrame.size.width, imgFrame.size.height}
                      fromRect: (NSRect){0, 0, imgFrame.size.width, imgFrame.size.height}
                    operation: NSCompositeSourceOver
                      fraction: 1.0];
        [resultImage unlockFocus];

        return resultImage;

    ------------

    for some reason, the image created with the code above is vertically flipped!  So I manually unflip it with an affine transformation using the drawing code below:

    [[NSGraphicsContext currentContext] saveGraphicsState];

        NSAffineTransform *t = [NSAffineTransform transform];
        [t scaleXBy:1.0 yBy:-1.0];
        [t translateXBy:0.0 yBy:-[myImage size].height];
        [t concat];
        [myImage  drawInRect: (NSRect){0,0,[myImage size].width, [myImage size].height}
                    fromRect: (NSRect){0,0,[myImage size].width, [myImage size].height}
                  operation: NSCompositeSourceOver
                    fraction: 1.0];

        [[NSGraphicsContext currentContext] restoreGraphicsState];

    If anyone has any suggestions I would greatly appreciate your input.

    Thanks,
    -Dave H.
  • On 12 Oct 2007, at 3:53 PM, David Harper wrote:

    > In an effort to improve efficiency in my current project, I have
    > decided to create NSImages to represent the contents of some of the
    > subviews in the application.  This is a somewhat well-documented
    > process that I've read about in several different places.
    >
    > These views contain dynamically positioned text, and when many of
    > them are on screen and thousands of individual characters are being
    > drawn using layoutmanagers etc, the application can gradually slow
    > down to unacceptable levels (~4 frames per second or lower).  The
    > solution I've come up with so far works, except that the image being
    > drawn in the view does not exactly match the one which was copied
    > using the view's contents...  Its width and height seem to vary by
    > 1-2 pixels.
    >
    > This may have something to do with the fact that the view frame
    > sometimes has non-integer width and height.  I'm not sure if
    > NSImages are required to have integer NSSize values...

    I am skeptical of fronting a live view of dynamic contents with an
    image. I last did it in 1987. Computers have gotten faster since then;
    a live representation should practically always be possible. Have you
    used Shark to determine where your performance problem actually is?
    I'd almost guarantee it would surprise you.

    The Cocoa text stack is much faster when you divide the text among
    separate storage and container objects, then draw the containers in a
    stack.

    I once did a terminal emulator that kept an indefinite scrollback
    buffer. I had the advantage that only the bottom of the buffer (the
    part comprising the terminal screen) ever changed. I put every couple
    of thousand lines into a SectionRecord, which had its own
    NSTextStorage and shared a custom NSLayoutManager. It knew how to draw
    itself. It was owned by a TallTextView, which knew how to stack the
    sections and handle mouse events.

    See the Crescat project at
    <http://www.manoverboard.org/~fritza/uoc/>
    The Download link includes the GPL source. May help, may not.

    — F
  • Am 12.10.2007 um 22:53 schrieb David Harper:
    > In an effort to improve efficiency in my current project, I have
    > decided to create NSImages to represent the contents of some of the
    > subviews in the application.  This is a somewhat well-documented
    > process that I've read about in several different places.

      Generally, I've found this approach to be not very useful. In most
    cases you'll be much better off finding out which parts of your views
    need redrawing, and only redrawing those parts by using
    setNeedsDisplayInRect: and querying your view for those rects in
    drawRect: (or even just using the argument passed to drawRect: will
    often speed up things considerably). Cache the rects of the various
    strings so you don't have to measure the text repeatedly, and share
    and cache the text system objects instead of having them created and
    torn down every time you draw if that still isn't enough.

      If you don't redraw everything or rely solely on clipping to cause
    things not to draw (which doesn't help, since the text system will
    still have to relayout all strings if even one changes, and only the
    pixel blitting part won't happen), you will usually get much better
    performance. Not drawing is always better than drawing fast.

    > These views contain dynamically positioned text, and when many of
    > them are on screen and thousands of individual characters are being
    > drawn using layoutmanagers etc, the application can gradually slow
    > down to unacceptable levels (~4 frames per second or lower).

      What exactly are you doing? Is this one view that drawAtPoint:s
    several strings, or are you creating several NSTextFields (or worse,
    NSTextViews???) and why do they have to be updated so much? A user
    can't really process that much information, so usually you can get
    away with just not showing the information unless the user requests
    it (distribute it across several tabs, and only update the fields in
    the visible tab, or use floating palettes or other panels or
    collapseable panes).

      If you're doing animation, you shouldn't be using views anyway,
    particularly if you're drawing overlapping views, because that only
    almost-works, and will break depending on system setup or phase of
    the moon in various ways, and views are too heavyweight to do more
    than simple, select GUI-feedback animation.

    > The solution I've come up with so far works, except that the image
    > being drawn in the view does not exactly match the one which was
    > copied using the view's contents...  Its width and height seem to
    > vary by 1-2 pixels.

      You're probably drawing between pixels. Are your images also kinda
    blurry? Check the archives and NSView docs, it's been explained a
    couple of times there, better than I can do right now.

    > This may have something to do with the fact that the view frame
    > sometimes has non-integer width and height.  I'm not sure if
    > NSImages are required to have integer NSSize values...

      Both views and NSImages are required. There's a piece of
    documentation that explicitly warns from creating views of non-
    integer sizes. Don't do it. Things will go screwy, you'll get drawing
    artefacts from half-erasded lines at the view edges etc.

    > for some reason, the image created with the code above is
    > vertically flipped!  So I manually unflip it with an affine
    > transformation using the drawing code below:

      Views have an isFlipped method. Have you tried using that?

    Cheers,
    -- M. Uli Kusterer
    http://www.zathras.de
previous month october 2007 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Go to today