Trouble creating drag image from a partial view

  • Hi

    I wrote a draggable view class and can successfully create a drag
    image of the entire view but am having trouble creating one from just
    part of the view. I can't seem to get the right combination of frame,
    image size, lock focus etc and am just getting these 1 to 2 pixel
    vertical slices of the requested area instead. Examining the
    resultant image with NSLog shows that it's dimensions are as expected
    but it only contains these tiny vertical slices.

    Here's the working code for the drag image of the entire view:

    - (NSImage *) dragImageWithAlpha:(float) inAlpha
    {
    NSRect    imgFrame  = NSMakeRect(0, 0, [self frame].size.width,
    [self frame].size.height);

    // create view bitmap
    [self lockFocus];
    NSBitmapImageRep * dragBitmap  = [[NSBitmapImageRep alloc]
    initWithFocusedViewRect: imgFrame];
    [self unlockFocus];

    NSImage  *resultImage = [[[NSImage alloc] initWithSize:
    imgFrame.size] autorelease];

    [resultImage addRepresentation: dragBitmap];

    [resultImage lockFocus];
    [resultImage drawAtPoint: NSZeroPoint
      fromRect: imgFrame
      operation: NSCompositeSourceOver
      fraction: inAlpha];
    [resultImage unlockFocus];

    [dragBitmap release];

    return resultImage;
    }

    And here's what I've tried for creating a drag image using a subview
    of the draggable view:
    (Note: "thumb" is an NSImageView containing a thumbnail of an image
    to drag)

    - (NSImage *) dragImageWithAlpha:(float) inAlpha
    {
    // tried both of the following
    NSRect    thumbFrame  = NSMakeRect(0, 0, [thumb frame].size.width,
    [thumb frame].size.height);
    NSRect    thumbFrame  = [thumb frame];

    // create view bitmap
    // tried both of the following with above thumb frames
    [self lockFocus];
    NSBitmapImageRep *dragBitmap  = [[NSBitmapImageRep alloc]
    initWithFocusedViewRect: thumbFrame];
    [self unlockFocus];

    [thumb lockFocus];
    NSBitmapImageRep * dragBitmap  = [[NSBitmapImageRep alloc]
    initWithFocusedViewRect: thumbFrame];
    [thumb unlockFocus];

    NSImage  *resultImage = [[[NSImage alloc] initWithSize:
    thumbFrame.size] autorelease];

    [resultImage addRepresentation: dragBitmap];

    [resultImage lockFocus];
    // tried both of the following
    [resultImage drawAtPoint: thumbFrame.origin
      fromRect: thumbFrame
      operation: NSCompositeSourceOver
      fraction: inAlpha];

    [resultImage drawAtPoint: NSZeroPoint
      fromRect: thumbFrame
      operation: NSCompositeSourceOver
      fraction: inAlpha];
    [resultImage unlockFocus];

    [dragBitmap release];

    return resultImage;
    }

    Anyone see where I'm going wrong?

    Thanks or any help

    Ken
  • On 7 Dec 2007, at 05:03, Ken Tozier wrote:

    > Here's the working code for the drag image of the entire view:
    >
    > - (NSImage *) dragImageWithAlpha:(float) inAlpha
    > {
    > NSRect                imgFrame        = NSMakeRect(0, 0, [self frame].size.width,
    > [self frame].size.height);
    >
    > // create view bitmap
    > [self lockFocus];
    > NSBitmapImageRep    * dragBitmap        = [[NSBitmapImageRep alloc]
    > initWithFocusedViewRect: imgFrame];
    > [self unlockFocus];
    >
    > NSImage            *resultImage    = [[[NSImage alloc] initWithSize:
    > imgFrame.size] autorelease];
    >
    > [resultImage addRepresentation: dragBitmap];

    Up to here you were fine, but then:

    > [resultImage lockFocus];
    > [resultImage drawAtPoint: NSZeroPoint
    > fromRect: imgFrame
    > operation: NSCompositeSourceOver
    > fraction: inAlpha];
    > [resultImage unlockFocus];

    What is the above code supposed to do?!  You don't need it, and I'm
    not in the slightest bit surprised that things are getting confused
    because you're trying to draw an image *into itself*!  I'd be very
    surprised if that's something that NSImage was designed to support.

    All you had to do was the -addRepresentation: call.

    > Anyone see where I'm going wrong?

    Well the only reason the first bit of code works (i.e. for the full
    view), I think, is that imgFrame.origin happens to be NSZeroPoint, so
    it's copying the image over itself.

    I think you'll find that if you delete the code between -lockFocus and
    -unlockFocus, it will probably work just fine.

    Kind regards,

    Alastair.

    --
    http://alastairs-place.net
  • On Dec 7, 2007, at 5:50 AM, Alastair Houghton wrote:

    > Up to here you were fine, but then:
    >
    >> [resultImage lockFocus];
    >> [resultImage drawAtPoint: NSZeroPoint
    >> fromRect: imgFrame
    >> operation: NSCompositeSourceOver
    >> fraction: inAlpha];
    >> [resultImage unlockFocus];
    >
    > What is the above code supposed to do?!

    That chunk allows me to set the transparency of the drag image to
    "inAlpha." There may be another way to do it but this is "first pass"
    code and it seemed to work so I used it.

    > You don't need it, and I'm not in the slightest bit surprised that
    > things are getting confused because you're trying to draw an image
    > *into itself*!  I'd be very surprised if that's something that
    > NSImage was designed to support.
    >
    > All you had to do was the -addRepresentation: call.
    >
    >> Anyone see where I'm going wrong?
    >
    > I think you'll find that if you delete the code between -lockFocus
    > and -unlockFocus, it will probably work just fine.

    I commented it out the "[resultImage lock focus]" block as you
    suggested but got the exact same result. (Basically empty image with
    a 1 pixel vertical strip)

    - (NSImage *) imageWithAlpha:(float) inAlpha
    {
    NSRect    imgFrame  = NSMakeRect(0, 0, [fileIcon frame].size.width,
    [fileIcon frame].size.height);

    // create view bitmap
    [fileIcon lockFocus];
    NSBitmapImageRep *selfBitmap  = [[NSBitmapImageRep alloc]
    initWithFocusedViewRect: [fileIcon frame]];
    [fileIcon unlockFocus];

    NSImage    *resultImage = [[[NSImage alloc] initWithSize: [fileIcon
    frame].size] autorelease];

    [resultImage addRepresentation: selfBitmap];

    /*
    [resultImage lockFocus];
    [resultImage drawAtPoint: NSZeroPoint //imgFrame.origin
      fromRect: [fileIcon frame] //imgFrame
      operation: NSCompositeSourceOver
      fraction: inAlpha];
    [resultImage unlockFocus];

    NSLog(@"resultImage: %@", resultImage);
    */

    //[selfBitmap release];

    return resultImage;
    }
  • On 7 Dec 2007, at 11:20, Ken Tozier wrote:

    > On Dec 7, 2007, at 5:50 AM, Alastair Houghton wrote:
    >
    >> Up to here you were fine, but then:
    >>
    >>> [resultImage lockFocus];
    >>> [resultImage drawAtPoint: NSZeroPoint
    >>> fromRect: imgFrame
    >>> operation: NSCompositeSourceOver
    >>> fraction: inAlpha];
    >>> [resultImage unlockFocus];
    >>
    >> What is the above code supposed to do?!
    >
    > That chunk allows me to set the transparency of the drag image to
    > "inAlpha." There may be another way to do it but this is "first
    > pass" code and it seemed to work so I used it.

    Even if you intended that the above should change the alpha value, and
    even if it is permissible to lock focus on an image and then draw that
    same image (which I seriously doubt), it's still wrong, because you'd
    need to use NSCompositeCopy.

    You don't need to make the drag image transparent yourself; the system
    will draw it transparently for you.  If you were doing that to try to
    make the background less visible, it's not really the right solution
    (see below).

    >> I think you'll find that if you delete the code between -lockFocus
    >> and -unlockFocus, it will probably work just fine.
    >
    > I commented it out the "[resultImage lock focus]" block as you
    > suggested but got the exact same result. (Basically empty image with
    > a 1 pixel vertical strip)

    > - (NSImage *) imageWithAlpha:(float) inAlpha
    > {
    > NSRect                imgFrame        = NSMakeRect(0, 0, [fileIcon frame].size.width,
    > [fileIcon frame].size.height);
    >
    > // create view bitmap
    > [fileIcon lockFocus];
    > NSBitmapImageRep    *selfBitmap        = [[NSBitmapImageRep alloc]
    > initWithFocusedViewRect: [fileIcon frame]];
    > [fileIcon unlockFocus];

    OK, the next problem is that in the code above, you're asking for the
    wrong rectangle.  You want [fileIcon bounds], assuming "fileIcon" is a
    view.  Otherwise you'll be grabbing the wrong bit of the display.

    Note also that -initWithFocusedViewRect: has one or two problems; if
    this view can be scrolled such that part of it isn't visible, you
    won't get any data for the non-visible part.  This happens because -
    initWithFocusedViewRect: takes its data directly from the backing
    store.  Also, you have very little control over any background that
    gets drawn by the view; usually backgrounds look wrong if you're
    dragging e.g. icons.

    Personally when implementing drag images, I prefer to separate out the
    drawing code that is shared between the drag image and the view's
    display, then I can just do something like this:

      - (NSImage *)dragImageFromRect:(NSRect)dragImageRect
      {
        NSImage *image = [[[NSImage alloc]
    initWithSize:dragImageRect.size] autorelease];

        [image lockFocus];
        [NSGraphicsContext saveGraphicsState];

        NSAffineTransform *transform = [NSAffineTransform transform];
        [transform translateXBy:-NSMinX (dragImageRect) yBy:-NSMinY
    (dragImageRect)];
        [transform concat];

        [self drawStuffInRect:dragImageRect];

        [NSGraphicsContext restoreGraphicsState];
        [image unlockFocus];

        return image;
      }

    to get my drag image, and my -drawRect: will look like this:

      - (void)drawRect:(NSRect)rect
      {
        // Draw the background
        [[NSColor whiteColor] set];
        NSRectFill (rect);

        // Draw some stuff
        [self drawStuffInRect:rect];
      }

    Kind regards,

    Alastair.

    p.s. I think I may accidentally have hit send on a window that had an
    empty reply in it.  My apologies to anyone who receives that.

    --
    http://alastairs-place.net
previous month december 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