按比例缩放并旋转drawInRect的NSImage

时间:2015-01-03 23:16:00

标签: objective-c cocoa nsview drawrect nsimage

我正在尝试绘制按比例缩放并旋转90度的NSImage,但不断获得剪裁的结果图像。

在NSView子类中,这是我目前所做的事情:

- (void) drawRect:(NSRect)dirtyRect
{
    [super drawRect:dirtyRect];

    NSImage* image = /* get image */;

    CGRect drawRect = dirtyRect;
    CGRect imageRect = [self proportionallyScale:image.size toSize:drawRect.size];

    NSAffineTransform* rotation = [[NSAffineTransform alloc] init];
    [rotation translateXBy:NSWidth(drawRect) / 2 yBy:NSHeight(drawRect) / 2];
    [rotation rotateByDegrees:90];
    [rotation translateXBy:-NSWidth(drawRect) / 2 yBy:-NSHeight(drawRect) / 2];

    NSGraphicsContext* context = [NSGraphicsContext currentContext];
    [context saveGraphicsState];
    [rotation concat];
    [image drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
    [context restoreGraphicsState];
}

缩放逻辑:

- (CGRect) proportionallyScale:(CGSize)fromSize toSize:(CGSize)toSize
{
    CGPoint origin = CGPointZero;
    CGFloat width = fromSize.width, height = fromSize.height;
    CGFloat targetWidth = toSize.width, targetHeight = toSize.height;

    float widthFactor = targetWidth / width;
    float heightFactor = targetHeight / height;

    CGFloat scaleFactor = std::min(widthFactor, heightFactor);

    CGFloat scaledWidth = width * scaleFactor;
    CGFloat scaledHeight = height * scaleFactor;

    if (widthFactor < heightFactor)
        origin.y = (targetHeight - scaledHeight) / 2.0;
    else if (widthFactor > heightFactor)
        origin.x = (targetWidth - scaledWidth) / 2.0;
    return {origin, {scaledWidth, scaledHeight}};
}

这是没有应用旋转的结果(图像不是方形,红色背景渲染以显示视图的位置):

No rotation applied

当我通过启用[rotation concat]来应用转换时:

Current code with 90 deg rotation applied

问题是图像的缩放及其偏移应该相对于旋转的视口大小而不是输入视图边界来计算。但是,我似乎无法想出正确计算这一点的逻辑。

任何人都可以帮助正确计算缩放和旋转图像的正确图像绘制矩形的逻辑吗?或者对整个问题有更好的解决方法吗?

1 个答案:

答案 0 :(得分:4)

短版

添加:

imageRect.origin.x += NSWidth(drawRect) / 2 - NSHeight(drawRect) / 2;
imageRect.origin.y += NSHeight(drawRect) / 2 - NSWidth(drawRect) / 2;
proportionallyScale

中致电drawRect

长版

我分两步执行此操作:对图像进行边界和缩放,然后在视口中重新定位。如果你想得到所有的想象,你可以将它们结合起来。我认为这应适用于各种视图大小。

让这成为我们原来的观点:

********************
*                  *
*       View       * 6
*                  *
*                  *
X*******************
        20

左下角的原点标记为X

假设图像为6 x 8(旋转之前)。

第一部分很简单,确定旋转后绘制图像的大小。只需将翻转的尺寸传递给proportionallyScale

CGRect imageRect = [self
    proportionallyScale:image.size
    toSize:CGSizeMake(
         drawRect.size.height, // 6
         drawRect.size.width)  // 20
];

这导致了一个矩形:

imageRect = { origin=(x=0, y=6) size=(width=6, height=8) };

但目前这没有多大意义:

  6
$$$$$$ 
$    $
$    $
$ IM $ 8
$    $
$    $
$    $
X$$$$$**************
*                  *
*       View       * 6
*                  *
*                  *
X*******************
         20

然后执行关于中心的视图的顺时针90度旋转:

                        8
               X++++X$$$$$$$$       -
               +    +       $       |
Rot'd View >-- +    $  IM   $       |
               +    +       $ 6     | 7
               +    $       $       |
               +    +$$$$$$$$       |
        *******+****+*******        -
        * VP   +    +      *        |
        *      +    +      *        | 6
        *      +    +      *        |
        *      +    +      *        |
        X******+****+*******        -
               +    +               |
               +    +               |
               +    +               |
               +    +               | 7
               +    +               |
               ++++++               -

        |------|----|------|
           7     6     7

VP是原始视图的位置。我们需要将原点从旋转空间转换为原始视口空间。

此变换可以计算为:

// note that drawRect hasn't been flipped here
CGFloat transX = NSWidth(drawRect) / 2 - NSHeight(drawRect) / 2 == 7;
CGFloat transY = NSHeight(drawRect) / 2 - NSWidth(drawRect) / 2 == -7

因此我们将变换应用于当前图像:

imageRect.origin.x += transX;
imageRect.origin.y += transY;

此转换产生:

               X++++X
               +    +
               +    +
               +    +
               +    +
               +    +
        ******$+$$$$+$******
        * VP  $+    +$     *
        *     $+    +$     *
        *     $+    +$     *
        *     $+    +$     * 
        X*****$+$$$$+$******
               +    +
               +    +
               +    + 
               +    +
               +    +
               ++++++

现在一切都很好地定位和缩放:

           6     8      6
        |-----|------|-----|

        ******$$$$$$$$******  -
        *     $      $     *  |
        *     $      $     *  | 6
        *     $      $     *  |
        *     $      $     *  |
        X*****$$$$$$$$******  -

至少在下一个黑暗的邪恶力量到来之前......