Skip to content

Instantly share code, notes, and snippets.

@Nyx0uf
Last active November 15, 2020 22:31
Show Gist options
  • Save Nyx0uf/217d97f81f4889f4445a to your computer and use it in GitHub Desktop.
Save Nyx0uf/217d97f81f4889f4445a to your computer and use it in GitHub Desktop.
UIImage scale using vImage
-(UIImage*)mmg_imageScaledToFitSize:(CGSize)fitSize
{
// Create a vImage_Buffer from the CGImage
CGImageRef sourceRef = self.CGImage;
vImage_Buffer srcBuffer;
vImage_CGImageFormat format = {
.bitsPerComponent = 8,
.bitsPerPixel = 32,
.colorSpace = NULL,
.bitmapInfo = (CGBitmapInfo)kCGImageAlphaFirst,
.version = 0,
.decode = NULL,
.renderingIntent = kCGRenderingIntentDefault,
};
vImage_Error ret = vImageBuffer_InitWithCGImage(&srcBuffer, &format, NULL, sourceRef, kvImageNoFlags);
if (ret != kvImageNoError)
{
free(srcBuffer.data);
return nil;
}
// Create dest buffer
const NSUInteger scale = (NSUInteger)[[UIScreen mainScreen] scale];
const NSUInteger dstWidth = (NSUInteger)fitSize.width * scale;
const NSUInteger dstHeight = (NSUInteger)fitSize.height * scale;
const NSUInteger bytesPerPixel = 4;
const NSUInteger dstBytesPerRow = bytesPerPixel * dstWidth;
uint8_t* dstData = (uint8_t*)calloc(dstHeight * dstWidth * bytesPerPixel, sizeof(uint8_t));
vImage_Buffer dstBuffer = {
.data = dstData,
.height = dstHeight,
.width = dstWidth,
.rowBytes = dstBytesPerRow
};
// Scale
ret = vImageScale_ARGB8888(&srcBuffer, &dstBuffer, NULL, kvImageHighQualityResampling);
free(srcBuffer.data);
if (ret != kvImageNoError)
{
free(dstData);
return nil;
}
// Create CGImage from vImage_Buffer
ret = kvImageNoError;
CGImageRef destRef = vImageCreateCGImageFromBuffer(&dstBuffer, &format, NULL, NULL, kvImageNoFlags, &ret);
free(dstData);
// Create UIImage
UIImage* destImage = [[UIImage alloc] initWithCGImage:destRef scale:0.0 orientation:self.imageOrientation];
// Free up the remaining memory
CGImageRelease(destRef);
return destImage;
}
@conroewuhao
Copy link

Learn a lot from this code,thanks~

@victorstewart
Copy link

victorstewart commented Oct 31, 2017

you can use the kvImageNoAllocate flag instead to do a no-copy CGImageRef creation in vImageCreateCGImageFromBuffer

@VincentSit
Copy link

I posted the Swift 4.2 version with kvImageNoAllocate here to make it easier for anyone to use it in the future.

func scaled(toFit fitSize: CGSize) -> UIImage? {
    // Create a vImage_Buffer from the CGImage
    guard let sourceRef = cgImage else { return nil }
    
    var srcBuffer = vImage_Buffer()
    var format = vImage_CGImageFormat(bitsPerComponent: 8,
                                      bitsPerPixel: 32,
                                      colorSpace: nil,
                                      bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue),
                                      version: 0,
                                      decode: nil,
                                      renderingIntent: .defaultIntent)
    var ret = vImageBuffer_InitWithCGImage(&srcBuffer, &format, nil, sourceRef, vImage_Flags(kvImageNoFlags))
    
    guard ret == kvImageNoError else {
      free(srcBuffer.data)
      return nil
    }
    
    // Create dest buffer
    let scale = UIScreen.main.scale
    let dstWidth = Int(fitSize.width * scale)
    let dstHeight = Int(fitSize.height * scale)
    let bytesPerPixel: Int = 4
    let dstBytesPerRow: Int = bytesPerPixel * dstWidth
    let dstData = UnsafeMutablePointer<UInt8>.allocate(capacity: dstHeight * dstWidth * bytesPerPixel)
    
    var dstBuffer = vImage_Buffer(data: dstData,
                                  height: vImagePixelCount(dstHeight),
                                  width: vImagePixelCount(dstWidth),
                                  rowBytes: dstBytesPerRow)

    // Scale
    ret = vImageScale_ARGB8888(&srcBuffer, &dstBuffer, nil, vImage_Flags(kvImageHighQualityResampling))
    free(srcBuffer.data)
    
    guard ret == kvImageNoError else { return nil }
    
    // Create CGImage from vImage_Buffer
    ret = kvImageNoError
    guard let destRef = vImageCreateCGImageFromBuffer(&dstBuffer, &format, nil, nil, vImage_Flags(kvImageNoAllocate), &ret)?.takeRetainedValue() else {
      return nil
    }
    
    guard ret == kvImageNoError else { return nil }
    
    // Create UIImage
    let destImage = UIImage(cgImage: destRef, scale: 0.0, orientation: imageOrientation)
    return destImage
  }

@PasqualePuzio
Copy link

Thank you! This code literally saved me from hours of coding and testing :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment