2
votes

I have a custom subclass of NSView that implements drag/drop for copying the image in the view to another application. The relevant code in my class looks like this:

#pragma mark -
#pragma mark Dragging Support
- (NSImage *)imageWithSubviews
{
    NSSize imgSize = self.bounds.size;
    NSBitmapImageRep *bir = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[self frame]];
    [self cacheDisplayInRect:[self frame] toBitmapImageRep:bir];

    NSImage* image = [[NSImage alloc]initWithSize:imgSize];
    [image addRepresentation:bir];

    return image;
}

- (void)mouseDown:(NSEvent *)theEvent
{
    NSSize dragOffset = NSMakeSize(0.0, 0.0); // not used in the method below, but required.
    NSPasteboard *pboard;
    NSImage *image = [self imageWithSubviews];
    pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
    [pboard declareTypes:[NSArray arrayWithObject:NSTIFFPboardType]
                   owner:self];

    [pboard setData:[image TIFFRepresentation]
            forType:NSTIFFPboardType];

    [self dragImage:image
                 at:self.bounds.origin
             offset:dragOffset
              event:theEvent
         pasteboard:pboard
             source:self
          slideBack:YES];

    return;
}

#pragma mark -
#pragma mark NSDraggingSource Protocol

- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
    return NSDragOperationCopy;
}

- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession *)session
{
    return YES;
}

This works as expected until I resize the main window. The main window only increases size/width in the same increments to maintain the proper ratio in this view. The view properly displays its content on the screen when the window is resized.

The problem comes when I resize the window more than about + 25%. While it still displays as expected, the image that is dragged off of it (into Pages, for example) is corrupt. It appears to have a portion of this image repeated on top of itself.

Here is what it looks like normally:

normal appearance

And here is what it looks like when dragged to Pages after resizing the main window to make it large (downsized to show here -- imagine it at 2-3x the size of the first image): corrupt appearance

Note that I highlighted the corrupt area with a dotted rectangle.

A few more notes: I have my bounds set like NSMakeRect(-200,-200,400,400) because it makes the symmetrical drawing a bit easier. When the window resizes, I recalculate the bounds to keep 0,0 in the center of the NSView. The NSView always is square.

Finally, the Apple docs state the following for the bitmapImageRep parameter in cacheDisplayInRect:toBitmapImageRep: should

An NSBitmapImageRep object. For pixel-format compatibility, bitmapImageRep should have been obtained from bitmapImageRepForCachingDisplayInRect:.

I've tried using bitmapImageRepForCachingDisplayInRect:, but then all I see is the lower-left quadrant of the pyramid in the upper-right quadrant of the image. That makes me think that I need to add an offset for the capture of the bitmapImageRep, but I've been unable to determine how to do that.

Here's what the code for imageWithSubviews looks like when I try that:

- (NSImage *)imageWithSubviews
{
    NSSize imgSize = self.bounds.size;
    NSBitmapImageRep *bir = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
    [self cacheDisplayInRect:[self bounds] toBitmapImageRep:bir];

    NSImage* image = [[NSImage alloc]initWithSize:imgSize];
    [image addRepresentation:bir];

    return image;
}

And this is how the resulting image appears:

only lower left quadrant shows in upper right

That is a view of the lower left quadrant being drawn in the upper-right corner.

What is causing the corruption when I drag from the NSView after enlarging the window? How to I fix that and/or change my implementation of the methods that I listed above to avoid the problem?


More info:

When I change the imageWithSubviews method to:

- (NSImage *)imageWithSubviews
{

    NSSize imgSize = self.bounds.size;
    NSBitmapImageRep *bir = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[self frame]];
    [self cacheDisplayInRect:[self bounds] toBitmapImageRep:bir];

    NSImage* image = [[NSImage alloc]initWithSize:imgSize];
    [image addRepresentation:bir];

    return image;
}

I get a corrupted image without scaling, where the bottom-left quadrant of the image is drawn again on top of the top-right quadrant, like this:

quadrant overwritten

What in the world am I doing wrong?


Solution:

While it does not address the core problem of drawing with NSBitmapImageRep, the following -imageWithSubviews prevents the corruption and outputs the correct image:

- (NSImage *)imageWithSubviews
{
    NSData *pdfData = [self dataWithPDFInsideRect:[self bounds]];
    NSImage* image = [[NSImage alloc] initWithData:pdfData];

    return image;
}
1
Any reason you're not using NSImageView?paulmelnikow
Have you checked whether -imageWithSubviews is being called more than once?paulmelnikow
I have checked, and -imageWithSubviews is only being called once. The pyramids are drawn dynamically, based on user input (not imported photos). I never considered using NSImageView and I'm not sure why that would be a better choice. It's very odd to me that this only happens when I scale up the window size beyond a certain threshold and then only when I drag on the pyramid. It appears fine in the window/view itself.Clay
I brought up NSImageView because you could render your data to an image and let NImageView take care of things like drag-and-drop for you. But of course what you're doing should work, too. Would you try writing bir to a file in -imageWithSubviews and seeing what it looks like?paulmelnikow
That's helpful – if I understand correctly that means the problem is there somewhere. My guess is that -cacheDisplayInRect:toBitmapImageRep isn't the right way to render the view to an image rep. Maybe you could try -dataWithPDFInsideRect instead?paulmelnikow

1 Answers

1
votes

Based on some debugging above, we determined the problem was in -imageWithSubviews.

Instead of generating image data for the view using -cacheDisplayInRect:toBitmapImageRep:, changing it to -dataWithPDFInRect: fixed the issue.