2015-01-03 109 views
1

我想绘制一个按比例缩放和旋转90度的NSImage,但不断得到一个剪裁的结果图像。按比例缩放和旋转NSImage drawInRect

在一个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

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

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

回答

4

短版

地址:

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

drawRect

龙调用proportionallyScale版本

我拿着这两个步骤后:边界和缩放图像,并然后在视口中重新定位它。如果你想得到所有的幻想,你可以将它们结合起来。我认为这应该适用于各种视图尺寸。

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

******************** 
*     * 
*  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*****$$$$$$$$****** - 

至少要等到下一次黑暗邪恶力量到来时......