On Apr 3, 2017, at 8:54 PM, Patrick J. Collins <patr...@collinatorstudios.com> 
wrote:
> 
> I have a NSView in which I am drawing a waveform from a buffer. This
> NSView has a child playhead element, that I want to move across the
> waveform as it plays. The problem is, drawRect: is expensive and anytime
> the playhead moves, the waveform has to draw itself all over again,
> causing terrible performance problems.
> 
> My thought was to draw the waveform once, save it as an NSImage, and
> then have drawRect: draw itself from the saved image, and when a new
> buffer is loaded, discard the image and let drawRect: draw the new
> waveform and save it as an NSImage, etc.
> 
> However, when drawRect: is called subsequent times (after new buffers
> are set), the image appears to be garbage and therefore this strategy is
> not quite working as planned... What am I doing wrong?
> 
>  @property (nonatomic, strong) NSImage *image;
> 
>  ...
> 
>  -(void)drawRect:(NSRect)dirtyRect {
>      if (self.image) {
>          [self.image drawInRect:dirtyRect];

You need to draw to self.bounds, here, not dirtyRect.  If the dirtyRect is some 
portion of the view bounds, because only that portion was marked as needing 
display, the above code would draw the whole image scaled down into that 
portion.

You could also draw using one of the other draw methods.  Some will allow you 
to specify both the source and destination rects, which will allow you to only 
draw the dirty rect without the scaling problem.  Also, specifying the 
compositing operation as NSCompositingOperationCopy will be more efficient.

>          return;
>      }
> 
>      [[NSColor blackColor] setFill];
>      NSRectFill(dirtyRect);
>      [super drawRect:dirtyRect];
> 
>      float zero = self.bounds.size.height / 2;
>      [[NSColor blueColor] set];
> 
>      NSPoint pointA = NSMakePoint(0, zero);
>      NSPoint pointB = NSMakePoint(self.bounds.size.width, zero);
>      [self drawLineFromPointA:pointA toPointB:pointB];
> 
>      if (self.buffer) {
>          float spacing = self.bounds.size.width / self.buffer.size;
>          for (int i = 0; i < self.buffer.size - 1; i++) {
>              NSPoint pointA = NSMakePoint(i * spacing, zero - 
> (self.buffer.samples[i] * zero));
>              NSPoint pointB = NSMakePoint((i + 1) * spacing, zero - 
> (self.buffer.samples[i + 1] * zero));
>              [self drawLineFromPointA:pointA toPointB:pointB];
>          }
>      }
> 
>      [self lockFocus];
>      NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] 
> initWithFocusedViewRect:[self bounds]];
>      self.image = [[NSImage alloc] initWithData:[rep TIFFRepresentation]];
>      [self unlockFocus];
>  }

Don't draw into the view and then attempt to capture the drawing as an image.  
Draw into an image directly and then draw that image in the view.

First, Cocoa locks focus on your view before calling -drawRect:.  That's how 
drawing you do within -drawRect: ends up in your view.  So, you don't need to 
call [self lockFocus] in -drawRect: and thus you shouldn't call [self 
unlockFocus].

That said, the drawing you do during -drawRect: is not necessarily flushed to 
the window backing store and/or the window server immediately.  Therefore, the 
image you capture with -initWithFocusedViewRect: doesn't necessarily contain 
what you just drew.  I expect that's why you're getting garbage on a draw after 
the first.

To draw directly into an image, do something like:

    NSImage* image = [NSImage imageWithSize:self.bounds.size 
flipped:self.flipped drawingHandler:^BOOL(NSRect dstRect){
        // drawing relative to dstRect (not dirtyRect nor the view's bounds)
        // Note that you can't make use of [super drawRect:…] here.  If the 
super class is NSView, it did nothing, anyway.
        return TRUE;
    }];

Note that you want to invalidate the image when the view changes size, in 
addition to when you change the buffer.

Cheers,
Ken

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to