【问题标题】:Rotating a CGImage旋转 CGImage
【发布时间】:2012-05-19 16:02:12
【问题描述】:

我有一个从 UIImagePickerController 获取的 UIImage。当我从- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 方法收到图像时,我将其直接放入 UIImageView 中进行预览,并为用户提供保存或丢弃图像的选项。

当用户选择保存选项时,应用程序会获取图像的 png 数据并将其保存到它的文档目录中。但是在两种可能的设备方向之一(仅限横向)下发生的情况是图像被颠倒保存(更具体地说,从它应该的位置旋转 180 度)。因此,当我在图库中加载图像时,它会出现颠倒。

(见图库左下角的图片)

我已经解决了这个问题,来自 UIImagePickerController 的 UIImage 中的原始图像数据不会旋转,而是将方向作为属性存储在对象上,并且仅在显示时应用。所以我正在尝试使用我在网上找到的一些代码来旋转与 UIImage 关联的 CGImage。但它似乎对图像完全没有影响。我使用的代码如下:

- (void)rotateImage:(UIImage*)image byRadians:(CGFloat)rads
{   
    // calculate the size of the rotated view's containing box for our drawing space 
    UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,image.size.width, image.size.height)];
    CGAffineTransform t = CGAffineTransformMakeRotation(rads);
    rotatedViewBox.transform = t;
    CGSize rotatedSize = rotatedViewBox.frame.size;

    // Create the bitmap context
    UIGraphicsBeginImageContext(rotatedSize);
    CGContextRef bitmap = UIGraphicsGetCurrentContext();

    // Move the origin to the middle of the image you want to rotate and scale around the center. 
    CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2); 

    // Rotate the image context
    CGContextRotateCTM(bitmap, rads);

    // Now, draw the rotated/scaled image into the context
    CGContextScaleCTM(bitmap, 1.0, -1.0);
    CGContextDrawImage(bitmap, CGRectMake(image.size.width / 2, image.size.height / 2, image.size.width, image.size.height), [image CGImage]);

    image = UIGraphicsGetImageFromCurrentImageContext();
    image = [UIImage imageWithCGImage:image.CGImage scale:1.0 orientation:UIImageOrientationDown];
    UIGraphicsEndImageContext();
}

我想知道为什么这段代码不起作用,因为我在网上找到的每段代码都可以进行图像旋转。

【问题讨论】:

    标签: objective-c ios uiimage cgimage


    【解决方案1】:
    -(UIImage*) rotate:(UIImage*) src andOrientation:(UIImageOrientation)orientation
    {
        UIGraphicsBeginImageContext(src.size);
    
        CGContextRef context=(UIGraphicsGetCurrentContext());
    
        if (orientation == UIImageOrientationRight) {
            CGContextRotateCTM (context, 90/180*M_PI) ;
        } else if (orientation == UIImageOrientationLeft) {
            CGContextRotateCTM (context, -90/180*M_PI);
        } else if (orientation == UIImageOrientationDown) {
            // NOTHING
        } else if (orientation == UIImageOrientationUp) {
            CGContextRotateCTM (context, 90/180*M_PI);
        }
    
        [src drawAtPoint:CGPointMake(0, 0)];
        UIImage *img=UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return img;
    
    }
    

    使用这个 .. 效果很好!当你从 UIImagePicker 中挑选图片时,请确保你使用了这个函数:

    示例:

    UIImage *image;
    image = [self rotate:image andOrientation:image.imageOrientation];
    

    确保在您从文档目录中选择照片的地方使用此功能。

    【讨论】:

    • 不错的答案...我唯一要改变的是“return img;”在“[img release];”之前被调用(这意味着发布调用从未真正命中)。此外,这假设此人未使用 ARC。
    • 有人知道为什么需要轮换吗?
    【解决方案2】:

    我遇到了你的问题。你必须使用CGContextTranslateCTM(context, -rotatedSize.width/2, -rotatedSize.height/2); 将上下文翻译回来,并将绘图中的矩形原点设置为rotatedViewBox.frame.origin.x,rotatedViewBox.frame.origin.y。 使用此代码。

    UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,image.size.width, image.size.height)];
    CGAffineTransform t = CGAffineTransformMakeRotation(0.3);
    rotatedViewBox.transform = t;
    CGSize rotatedSize = rotatedViewBox.frame.size;
    
    
    
    UIGraphicsBeginImageContext(rotatedSize);
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, rotatedSize.width/2, rotatedSize.height/2);
    CGContextRotateCTM (context, 0.3);
    CGContextScaleCTM(context, 1, -1);
    CGContextTranslateCTM(context, -rotatedSize.width/2, -rotatedSize.height/2);
    
    
    
    
    CGContextDrawImage(context, CGRectMake(-rotatedViewBox.frame.origin.x, -rotatedViewBox.frame.origin.y, image.size.width, image.size.height), [image CGImage]);
    
    
    UIImage *nn = UIGraphicsGetImageFromCurrentImageContext();
    
    NSLog(@"size is %f, %f",nn.size.width,nn.size.height);
    UIGraphicsEndImageContext();
    
    UIImageWriteToSavedPhotosAlbum(nn, nil,nil,nil);
    

    【讨论】:

      【解决方案3】:

      旋转和镜像 UIImage CGImage 支持数据 - Swift

      我最近想更正由 CGImage 支持的 UIImage 以匹配所需的方向,而不是依赖 API 来尊重 UIImageOrientation 参数。

      func createMatchingBackingDataWithImage(imageRef: CGImage?, orienation: UIImageOrientation) -> CGImage?
      {
          var orientedImage: CGImage?
      
          if let imageRef = imageRef {
              let originalWidth = imageRef.width
              let originalHeight = imageRef.height
              let bitsPerComponent = imageRef.bitsPerComponent
              let bytesPerRow = imageRef.bytesPerRow
      
              let bitmapInfo = imageRef.bitmapInfo
      
              guard let colorSpace = imageRef.colorSpace else {
                  return nil
              }
      
              var degreesToRotate: Double
              var swapWidthHeight: Bool
              var mirrored: Bool
              switch orienation {
              case .up:
                  degreesToRotate = 0.0
                  swapWidthHeight = false
                  mirrored = false
                  break
              case .upMirrored:
                  degreesToRotate = 0.0
                  swapWidthHeight = false
                  mirrored = true
                  break
              case .right:
                  degreesToRotate = 90.0
                  swapWidthHeight = true
                  mirrored = false
                  break
              case .rightMirrored:
                  degreesToRotate = 90.0
                  swapWidthHeight = true
                  mirrored = true
                  break
              case .down:
                  degreesToRotate = 180.0
                  swapWidthHeight = false
                  mirrored = false
                  break
              case .downMirrored:
                  degreesToRotate = 180.0
                  swapWidthHeight = false
                  mirrored = true
                  break
              case .left:
                  degreesToRotate = -90.0
                  swapWidthHeight = true
                  mirrored = false
                  break
              case .leftMirrored:
                  degreesToRotate = -90.0
                  swapWidthHeight = true
                  mirrored = true
                  break
              }
              let radians = degreesToRotate * Double.pi / 180.0
      
              var width: Int
              var height: Int
              if swapWidthHeight {
                  width = originalHeight
                  height = originalWidth
              } else {
                  width = originalWidth
                  height = originalHeight
              }
      
              let contextRef = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
              contextRef?.translateBy(x: CGFloat(width) / 2.0, y: CGFloat(height) / 2.0)
              if mirrored {
                  contextRef?.scaleBy(x: -1.0, y: 1.0)
              }
              contextRef?.rotate(by: CGFloat(radians))
              if swapWidthHeight {
                  contextRef?.translateBy(x: -CGFloat(height) / 2.0, y: -CGFloat(width) / 2.0)
              } else {
                  contextRef?.translateBy(x: -CGFloat(width) / 2.0, y: -CGFloat(height) / 2.0)
              }
              contextRef?.draw(imageRef, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(originalWidth), height: CGFloat(originalHeight)))
              orientedImage = contextRef?.makeImage()
          }
      
          return orientedImage
      }
      

      【讨论】:

      • "您正在旋转上下文(及其坐标系),然后将图像绘制到其中。"出于这个原因,他们可以称上下文为画架,对吧?想知道为什么他们没有。理解什么是“画架”(毕竟大多数人都使用过)比理解什么是“上下文”更容易。
      • 你的解决方案和Dream.In.Code的解决方案有什么区别?您正在考虑更多案例(例如镜像)。但还有什么?有些角度似乎不同,使用的方法也不同(CGContextRotateCTM vs.rotate)。你也在做一个translateBy,其他解决方案没有考虑到......
      • 另外这个Gist有什么区别?
      【解决方案4】:

      Cameron Lowell Palmer answerSwift 3 版本:

      func createMatchingBackingDataWithImage(imageRef: CGImage?, orienation: UIImageOrientation) -> CGImage? {
          var orientedImage: CGImage?
      
          if let imageRef = imageRef {
              let originalWidth = imageRef.width
              let originalHeight = imageRef.height
              let bitsPerComponent = imageRef.bitsPerComponent
              let bytesPerRow = imageRef.bytesPerRow
      
              let colorSpace = imageRef.colorSpace
              let bitmapInfo = imageRef.bitmapInfo
      
              var degreesToRotate: Double
              var swapWidthHeight: Bool
              var mirrored: Bool
              switch orienation {
              case .up:
                  degreesToRotate = 0.0
                  swapWidthHeight = false
                  mirrored = false
                  break
              case .upMirrored:
                  degreesToRotate = 0.0
                  swapWidthHeight = false
                  mirrored = true
                  break
              case .right:
                  degreesToRotate = 90.0
                  swapWidthHeight = true
                  mirrored = false
                  break
              case .rightMirrored:
                  degreesToRotate = 90.0
                  swapWidthHeight = true
                  mirrored = true
                  break
              case .down:
                  degreesToRotate = 180.0
                  swapWidthHeight = false
                  mirrored = false
                  break
              case .downMirrored:
                  degreesToRotate = 180.0
                  swapWidthHeight = false
                  mirrored = true
                  break
              case .left:
                  degreesToRotate = -90.0
                  swapWidthHeight = true
                  mirrored = false
                  break
              case .leftMirrored:
                  degreesToRotate = -90.0
                  swapWidthHeight = true
                  mirrored = true
                  break
              }
              let radians = degreesToRotate * Double.pi / 180
      
              var width: Int
              var height: Int
              if swapWidthHeight {
                  width = originalHeight
                  height = originalWidth
              } else {
                  width = originalWidth
                  height = originalHeight
              }
      
              if let contextRef = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace!, bitmapInfo: bitmapInfo.rawValue) {
      
                  contextRef.translateBy(x: CGFloat(width) / 2.0, y: CGFloat(height) / 2.0)
                  if mirrored {
                      contextRef.scaleBy(x: -1.0, y: 1.0)
                  }
                  contextRef.rotate(by: CGFloat(radians))
                  if swapWidthHeight {
                      contextRef.translateBy(x: -CGFloat(height) / 2.0, y: -CGFloat(width) / 2.0)
                  } else {
                      contextRef.translateBy(x: -CGFloat(width) / 2.0, y: -CGFloat(height) / 2.0)
                  }
                  contextRef.draw(imageRef, in: CGRect(x: 0, y: 0, width: originalWidth, height: originalHeight))
      
                  orientedImage = contextRef.makeImage()
              }
          }
      
          return orientedImage
      }
      

      用法:

      let newCgImage = createMatchingBackingDataWithImage(imageRef: cgImageRef, orienation: .left)
      

      【讨论】:

        【解决方案5】:

        这只是对先前答案的重构。

        func correctImageOrientation(cgImage: CGImage?, orienation: UIImage.Orientation) -> CGImage? {
            guard let cgImage = cgImage else { return nil }
            var orientedImage: CGImage?
        
            let originalWidth = cgImage.width
            let originalHeight = cgImage.height
            let bitsPerComponent = cgImage.bitsPerComponent
            let bytesPerRow = cgImage.bytesPerRow
            let bitmapInfo = cgImage.bitmapInfo
        
            guard let colorSpace = cgImage.colorSpace else { return nil }
        
            let degreesToRotate = orienation.getDegree()
            let mirrored = orienation.isMirror()
        
            var width = originalWidth
            var height = originalHeight
        
            let radians = degreesToRotate * Double.pi / 180.0
            let swapWidthHeight = Int(degreesToRotate / 90) % 2 != 0
        
            if swapWidthHeight {
                swap(&width, &height)
            }
        
            let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
        
            context?.translateBy(x: CGFloat(width) / 2.0, y: CGFloat(height) / 2.0)
            if mirrored {
                context?.scaleBy(x: -1.0, y: 1.0)
            }
            context?.rotate(by: CGFloat(radians))
            if swapWidthHeight {
                swap(&width, &height)
            }
            context?.translateBy(x: -CGFloat(width) / 2.0, y: -CGFloat(height) / 2.0)
        
            context?.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(originalWidth), height: CGFloat(originalHeight)))
            orientedImage = context?.makeImage()
        
            return orientedImage
        }
        
        extension UIImage.Orientation {
            func getDegree() -> Double {
                switch self {
                case .up, .upMirrored:
                    return 0.0
                case .right, .rightMirrored:
                    return 90.0
                case .down, .downMirrored:
                    return 180.0
                case .left, .leftMirrored:
                    return -90.0
                default:
                    return 0
                }
            }
        
            func isMirror() -> Bool {
                switch self {
                case .up, .right, .down, .left:
                    return false
                case .leftMirrored, .upMirrored, .downMirrored, .rightMirrored:
                    return true
                default:
                    return false
                }
            }
        }
        

        【讨论】:

          【解决方案6】:

          所有这些答案的问题在于它们具有 UIKit 依赖项。我正在开发面向 iOS 和 macOS 的 SwiftUI 通用应用程序。 Cameron Lowell Palmer answer 进行了一些修改:

          1. bytesPerRow 计算需要在 高度和宽度交换之后进行,否则 CGImageContext 抱怨不正确的 bytesPerRow

          2. 将 UIImageOrientation 更改为 CGImagePropertyOrientation,并交换左右旋转的符号

          这是我的版本,适用于 iOS 和 macOS:

          extension CGImage {
          func rotating(to orientation: CGImagePropertyOrientation) -> CGImage? {
              var orientedImage: CGImage?
          
              let originalWidth = self.width
              let originalHeight = self.height
              let bitsPerComponent = self.bitsPerComponent
              let bitmapInfo = self.bitmapInfo
          
              guard let colorSpace = self.colorSpace else {
                  return nil
              }
          
              var degreesToRotate: Double
              var swapWidthHeight: Bool
              var mirrored: Bool
          
              switch orientation {
              case .up:
                  degreesToRotate = 0.0
                  swapWidthHeight = false
                  mirrored = false
                  break
              case .upMirrored:
                  degreesToRotate = 0.0
                  swapWidthHeight = false
                  mirrored = true
                  break
              case .right:
                  degreesToRotate = -90.0
                  swapWidthHeight = true
                  mirrored = false
                  break
              case .rightMirrored:
                  degreesToRotate = -90.0
                  swapWidthHeight = true
                  mirrored = true
                  break
              case .down:
                  degreesToRotate = 180.0
                  swapWidthHeight = false
                  mirrored = false
                  break
              case .downMirrored:
                  degreesToRotate = 180.0
                  swapWidthHeight = false
                  mirrored = true
                  break
              case .left:
                  degreesToRotate = 90.0
                  swapWidthHeight = true
                  mirrored = false
                  break
              case .leftMirrored:
                  degreesToRotate = 90.0
                  swapWidthHeight = true
                  mirrored = true
                  break
              }
          
              let radians = degreesToRotate * Double.pi / 180.0
          
              var width: Int
              var height: Int
          
              if swapWidthHeight {
                  width = originalHeight
                  height = originalWidth
              } else {
                  width = originalWidth
                  height = originalHeight
              }
          
              let bytesPerRow = (width * bitsPerPixel) / 8
          
              let contextRef = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
          
              contextRef?.translateBy(x: CGFloat(width) / 2.0, y: CGFloat(height) / 2.0)
          
              if mirrored {
                  contextRef?.scaleBy(x: -1.0, y: 1.0)
              }
          
              contextRef?.rotate(by: CGFloat(radians))
          
              if swapWidthHeight {
                  contextRef?.translateBy(x: -CGFloat(height) / 2.0, y: -CGFloat(width) / 2.0)
              } else {
                  contextRef?.translateBy(x: -CGFloat(width) / 2.0, y: -CGFloat(height) / 2.0)
              }
          
              contextRef?.draw(self, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(originalWidth), height: CGFloat(originalHeight)))
          
              orientedImage = contextRef?.makeImage()
          
              return orientedImage
          }
          

          }

          【讨论】:

            猜你喜欢
            • 2011-05-20
            • 1970-01-01
            • 2012-09-20
            • 1970-01-01
            • 1970-01-01
            • 2012-03-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多