【问题标题】:Getting bounds of an NSImage within an NSImageView在 NSImageView 中获取 NSImage 的边界
【发布时间】:2012-07-27 13:19:24
【问题描述】:

我有一个占据整个窗口的 NSImageView。图像视图没有边框,它设置为显示在左下方。所以这意味着无论窗口如何调整大小,视图的原点都与实际图像的原点匹配。

此外,图像比我在屏幕上完全可以合理地显示的要大得多。所以我也将 imageview 设置为按比例缩小图像的大小。但是,我似乎在任何地方都找不到这个比例因子。

我的最终目标是将鼠标按下事件映射到实际的图像坐标。为此,我想我还需要一条信息……显示的 NSImage 实际上有多大。

如果我查看[imageView bounds],我会得到图像视图的边界矩形,它通常会比图像大。

【问题讨论】:

  • 我试过了。框架和边界等都是相对于 NSView/NSWindow 类型的对象。所以我可以得到视图或窗口本身的边界框架......问题是 NSImageView 没有在其所有边界上绘制。部分视图是空白的。我需要的是 NSImageView 缩小其图像的量,或者 ImageView 实际绘制的边界矩形(而不是它所拥有的)
  • 现在,我正在使用一种解决方法,我不允许调整窗口大小,并从窗口中删除所有其他 UI 元素(如标题栏),这样我知道超级视图边界 = NSImageView 边界 = 实际图像的大小。
  • NSImages 没有框架。它们确实有尺寸,但尺寸是全尺寸图像,而不是它们实际绘制的尺寸。

标签: cocoa nsimage nsimageview


【解决方案1】:

我认为这可以满足您的需求:

NSRect imageRect = [imageView.cell drawingRectForBounds: imageView.bounds];

它返回视图中图像原点的偏移量,以及它的大小。

对于您重新映射鼠标坐标的最终目标,您的自定义视图类上的类似操作应该可以工作...

- (void)mouseUp:(NSEvent *)event
{
    NSPoint eventLocation = [event locationInWindow];    
    NSPoint location = [self convertPoint: eventLocation fromView: nil];

    NSRect drawingRect = [self.cell drawingRectForBounds:self.bounds];

    location.x -= drawingRect.origin.x;
    location.y -= drawingRect.origin.y;

    NSSize frameSize = drawingRect.size;
    float frameAspect = frameSize.width/frameSize.height;

    NSSize imageSize = self.image.size;
    float imageAspect = imageSize.width/imageSize.height;

    float scaleFactor = 1.0f;

    if(imageAspect > frameAspect) {

        ///in this case image.width == frame.width
        scaleFactor = imageSize.width / frameSize.width;

        float imageHeightinFrame = imageSize.height / scaleFactor;

        float imageOffsetInFrame = (frameSize.height - imageHeightinFrame)/2;

        location.y -= imageOffsetInFrame;

    } else {
        ///in this case image.height == frame.height
        scaleFactor = imageSize.height / frameSize.height;

        float imageWidthinFrame = imageSize.width / scaleFactor;

        float imageOffsetInFrame = (frameSize.width - imageWidthinFrame)/2;

        location.x -= imageOffsetInFrame;
    }

    location.x *= scaleFactor;
    location.y *= scaleFactor;

    //do something with you newly calculated mouse location    
}

【讨论】:

  • 对不起...第一行最终无法正常工作。我 NSLog'ed 计算的矩形,并且当我调整窗口大小以使纵横比发生变化(但图像单元设置为按比例绘制)时,图像单元的边界与窗口的边界匹配,而不是显示的图像。我开始认为对此没有 API 调用。我能做的是计算原始未缩放图像的纵横比......并使用它来计算调整大小窗口中的图像范围
  • 鉴于您对如何设置可能正确的事物的描述。然后上面的其余代码计算图像本身的偏移量。您是否尝试了其余的代码?我正在做与您非常相似的事情,除了我的视图和窗口边界不匹配,因为我有边框。您可能不需要最终的 *= scaleFactor ,因为我想要图像空间中的坐标(即相对于图像的实际大小)。
  • 我确实尝试了其余的代码。但是在drawingRect关闭的情况下,最终计算的坐标也关闭了。我已经决定了我想要的两管齐下的方法:从 NSWindowDelegate 实现一些方法,并强制任何手动调整大小以保持纵横比,然后在我的 mouseUp: 方法中,我可以假设图像大小 = 窗口大小跨度>
  • 这不起作用,因为您假设没有边距或其他装饰,并且 NSImageView 正在做完全相同的事情。您不知道图像的确切绘制位置,也不知道使用的比例因子。这几乎可行,但如果你仔细观察,(我修改了函数,使其返回绘制图像的假定位置),实际绘制的图像帧与使用此算法计算的帧之间存在细微差别。跨度>
  • 仍然是计算屏幕上矩形和图像内矩形之间差异以应用缩放和平移转换的好方法。使用 self.bounds 而不是单元格的方法也可以正常工作(也就是说,不是 100% 准确:))。
【解决方案2】:

由于我还没有找到任何解决方案来获取 NSImageView 中的真实图像帧,因此我手动进行了图像计算,并考虑了它的所有属性(缩放、对齐和边框)。这可能不是最有效的代码,并且可能与真实图像有 0.5-1 像素的微小偏差,但它非常接近原始图像(我知道这个问题很老,但解决方案可能对其他人有所帮助):

@implementation NSImageView (ImageFrame)

// -------------------------------------------------------------------------
// -imageFrame
// -------------------------------------------------------------------------
- (NSRect)imageFrame
{
    // Find the content frame of the image without any borders first
    NSRect contentFrame = self.bounds;
    NSSize imageSize = self.image.size;
    NSImageFrameStyle imageFrameStyle = self.imageFrameStyle;

    if (imageFrameStyle == NSImageFrameButton ||
        imageFrameStyle == NSImageFrameGroove)
    {
        contentFrame = NSInsetRect(self.bounds, 2, 2);
    }

    else if (imageFrameStyle == NSImageFramePhoto)
    {
        contentFrame = NSMakeRect(contentFrame.origin.x + 1,
                                  contentFrame.origin.y + 2,
                                  contentFrame.size.width - 3,
                                  contentFrame.size.height - 3);
    }

    else if (imageFrameStyle == NSImageFrameGrayBezel)
    {
        contentFrame = NSInsetRect(self.bounds, 8, 8);
    }


    // Now find the right image size for the current imageScaling
    NSImageScaling imageScaling = self.imageScaling;
    NSSize drawingSize = imageSize;

    // Proportionally scaling
    if (imageScaling == NSImageScaleProportionallyDown ||
        imageScaling == NSImageScaleProportionallyUpOrDown)
    {
        NSSize targetScaleSize = contentFrame.size;
        if (imageScaling == NSImageScaleProportionallyDown)
        {
            if (targetScaleSize.width > imageSize.width) targetScaleSize.width = imageSize.width;
            if (targetScaleSize.height > imageSize.height) targetScaleSize.height = imageSize.height;
        }

        NSSize scaledSize = [self sizeByScalingProportionallyToSize:targetScaleSize fromSize:imageSize];
        drawingSize = NSMakeSize(scaledSize.width, scaledSize.height);
    }

    // Axes independent scaling
    else if (imageScaling == NSImageScaleAxesIndependently)
        drawingSize = contentFrame.size;


    // Now get the image position inside the content frame (center is default) from the current imageAlignment
    NSImageAlignment imageAlignment = self.imageAlignment;
    NSPoint drawingPosition = NSMakePoint(contentFrame.origin.x + contentFrame.size.width / 2.0 - drawingSize.width / 2.0,
                                          contentFrame.origin.y + contentFrame.size.height / 2.0 - drawingSize.height / 2.0);

    // NSImageAlignTop / NSImageAlignTopLeft / NSImageAlignTopRight
    if (imageAlignment == NSImageAlignTop ||
        imageAlignment == NSImageAlignTopLeft ||
        imageAlignment == NSImageAlignTopRight)
    {
        drawingPosition.y = contentFrame.origin.y+contentFrame.size.height - drawingSize.height;

        if (imageAlignment == NSImageAlignTopLeft)
            drawingPosition.x = contentFrame.origin.x;
        else if (imageAlignment == NSImageAlignTopRight)
            drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width;
    }

    // NSImageAlignBottom / NSImageAlignBottomLeft / NSImageAlignBottomRight
    else if (imageAlignment == NSImageAlignBottom ||
             imageAlignment == NSImageAlignBottomLeft ||
             imageAlignment == NSImageAlignBottomRight)
    {
        drawingPosition.y = contentFrame.origin.y;

        if (imageAlignment == NSImageAlignBottomLeft)
            drawingPosition.x = contentFrame.origin.x;
        else if (imageAlignment == NSImageAlignBottomRight)
            drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width;
    }

    // NSImageAlignLeft / NSImageAlignRight
    else if (imageAlignment == NSImageAlignLeft)
        drawingPosition.x = contentFrame.origin.x;

    // NSImageAlignRight
    else if (imageAlignment == NSImageAlignRight)
        drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width;


    return NSMakeRect(round(drawingPosition.x),
                      round(drawingPosition.y),
                      ceil(drawingSize.width),
                      ceil(drawingSize.height));
}

// -------------------------------------------------------------------------
// -sizeByScalingProportionallyToSize:fromSize:
// -------------------------------------------------------------------------
- (NSSize)sizeByScalingProportionallyToSize:(NSSize)newSize fromSize:(NSSize)oldSize
{
    CGFloat widthHeightDivision = oldSize.width / oldSize.height;
    CGFloat heightWidthDivision = oldSize.height / oldSize.width;

    NSSize scaledSize = NSZeroSize;
    if (oldSize.width > oldSize.height)
    {
        if ((widthHeightDivision * newSize.height) >= newSize.width)
        {
            scaledSize = NSMakeSize(newSize.width, heightWidthDivision * newSize.width);
        }  else {
            scaledSize = NSMakeSize(widthHeightDivision * newSize.height, newSize.height);
        }

    } else {

        if ((heightWidthDivision * newSize.width) >= newSize.height)
        {
            scaledSize = NSMakeSize(widthHeightDivision * newSize.height, newSize.height);
        } else {
            scaledSize = NSMakeSize(newSize.width, heightWidthDivision * newSize.width);
        }
    }

    return scaledSize;
}

@end

【讨论】:

    【解决方案3】:

    Swift 5 实现@Toby 的解决方案

    extension NSImageView {
    
        /** Returns an `NSRect` of the drawn image in the view. */
        func imageRect() -> NSRect {
            // Find the content frame of the image without any borders first
            var contentFrame = self.bounds
            guard let imageSize = image?.size else { return .zero }
            let imageFrameStyle = self.imageFrameStyle
    
            if imageFrameStyle == .button || imageFrameStyle == .groove {
                contentFrame = NSInsetRect(self.bounds, 2, 2)
            } else if imageFrameStyle == .photo {
                contentFrame = NSRect(x: contentFrame.origin.x + 1, y: contentFrame.origin.x + 2, width: contentFrame.size.width - 3, height: contentFrame.size.height - 3)
            } else if imageFrameStyle == .grayBezel {
                contentFrame = NSInsetRect(self.bounds, 8, 8)
            }
    
    
            // Now find the right image size for the current imageScaling
            let imageScaling = self.imageScaling
            var drawingSize = imageSize
    
            // Proportionally scaling
            if imageScaling == .scaleProportionallyDown || imageScaling == .scaleProportionallyUpOrDown {
                var targetScaleSize = contentFrame.size
                if imageScaling == .scaleProportionallyDown {
                    if targetScaleSize.width > imageSize.width { targetScaleSize.width = imageSize.width }
                    if targetScaleSize.height > imageSize.height { targetScaleSize.height = imageSize.height }
                }
    
                let scaledSize = self.sizeByScalingProportianlly(toSize: targetScaleSize, fromSize: imageSize)
                drawingSize = NSSize(width: scaledSize.width, height: scaledSize.height)
            }
    
            // Axes independent scaling
            else if imageScaling == .scaleAxesIndependently {
                drawingSize = contentFrame.size
            }
    
    
            // Now get the image position inside the content frame (center is default) from the current imageAlignment
            let imageAlignment = self.imageAlignment
            var drawingPosition = NSPoint(x: contentFrame.origin.x + contentFrame.size.width / 2 - drawingSize.width / 2,
                                          y: contentFrame.origin.y + contentFrame.size.height / 2 - drawingSize.height / 2)
    
            // Top Alignments
            if imageAlignment == .alignTop || imageAlignment == .alignTopLeft || imageAlignment == .alignTopRight {
                drawingPosition.y = contentFrame.origin.y + contentFrame.size.height - drawingSize.height
    
                if imageAlignment == .alignTopLeft {
                    drawingPosition.x = contentFrame.origin.x
                } else if imageAlignment == .alignTopRight {
                    drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width
                }
            }
    
            // Bottom Alignments
            else if imageAlignment == .alignBottom || imageAlignment == .alignBottomLeft || imageAlignment == .alignBottomRight {
                drawingPosition.y = contentFrame.origin.y
    
                if imageAlignment == .alignBottomLeft {
                    drawingPosition.x = contentFrame.origin.x
                } else if imageAlignment == .alignBottomRight {
                    drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width
                }
            }
    
            // Left Alignment
            else if imageAlignment == .alignLeft {
                drawingPosition.x = contentFrame.origin.x
            }
    
            // Right Alginment
            else if imageAlignment == .alignRight {
                drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width
            }
    
            return NSRect(x: round(drawingPosition.x), y: round(drawingPosition.y), width: ceil(drawingSize.width), height: ceil(drawingSize.height))
        }
    
    
        func sizeByScalingProportianlly(toSize newSize: NSSize, fromSize oldSize: NSSize) -> NSSize {
            let widthHeightDivision = oldSize.width / oldSize.height
            let heightWidthDivision = oldSize.height / oldSize.width
    
            var scaledSize = NSSize.zero
    
            if oldSize.width > oldSize.height {
                if (widthHeightDivision * newSize.height) >= newSize.width {
                    scaledSize = NSSize(width: newSize.width, height: heightWidthDivision * newSize.width)
                } else {
                    scaledSize = NSSize(width: widthHeightDivision * newSize.height, height: newSize.height)
                }
            } else {
                if (heightWidthDivision * newSize.width) >= newSize.height {
                    scaledSize = NSSize(width: widthHeightDivision * newSize.height, height: newSize.height)
                } else {
                    scaledSize = NSSize(width: newSize.width, height: heightWidthDivision * newSize.width)
                }
            }
    
            return scaledSize
        }
    }
    

    【讨论】:

      【解决方案4】:

      正如我在上面的评论中指出的,这是我采取的方法:

      // the view that mouseUp: is part of doesnt draw anything. I'm layering it
      // in the window hierarchy to intercept mouse events. I suppose I could have
      // subclassed NSImageView instead, but I went this route.  isDragging is
      // an ivar...its cleared in mouseDown: and set in mouseDragged:
      // this view has no idea what the original unscaled image size is, so
      // rescaling is done by caller
      - (void)mouseUp:(NSEvent *)theEvent
      {
      
          if (!isDragging)
          {
              NSPoint rawPoint = [theEvent locationInWindow];
              NSImageView *view = self.subviews.lastObject;
      
              point = [self convertPoint:rawPoint fromView:view];
              point.x /= view.bounds.size.width;
              point.y /= view.bounds.size.height;
      
              [owner mouseClick:point];
      
          }
      }
      

      在我的 NSWindowController(我的鼠标视图的窗口委托)中,我有:

      static int resizeMode=-1;
      
      - (void)windowDidEndLiveResize:(NSNotification *)notification
      {
          if ([notification object]==frameWindow)
              self.resizeFrameSelection=0;
          resizeMode = -1;
      }
      
      - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
      {
      
          if (sender==frameWindow)
          {
              float imageAspectRatio = (float)movie.movieSize.width / (float)movie.movieSize.height;
              float newH = frameSize.height;
              float newW = frameSize.width;
              float currH = sender.frame.size.height;
              float currW = sender.frame.size.width;
              float deltaH = abs(newH-currH);
              float deltaW = abs(newW-currW);
      
              // lock onto one dimension to key off of, per drag.
              if  ( resizeMode==1 || (resizeMode==-1 && deltaW<deltaH ))
              {
                  // adjust width to match aspect ratio
                  frameSize.width = frameSize.height * imageAspectRatio;
                  resizeMode=1;
              }
              else
              {
                  // adjust height to match aspect ratio
                  frameSize.height = frameSize.width / imageAspectRatio;
                  resizeMode=2;
              }
          }
      
          return frameSize;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-05
        • 1970-01-01
        • 2022-01-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多