2016-11-26 20 views
2

Я пытаюсь скопировать UIImage на основе заданного UIBezierPath, но результирующее изображение сохраняет оригинальную форму и размер. Я хочу, чтобы форма и размер напоминали путь, т. Е. Видимое содержимое нового изображения.Clip UIImage to UIBezierPath (не маскировка)

Взгляните на этот следующий пример:

Input image, input path, actual result, and desired result

Ниже приведен код, я использую, чтобы скрыть изображение заданного пути:

func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage { 
    UIGraphicsBeginImageContextWithOptions(CGSize(
     width: size.width, 
     height: size.height 
    ), false, 0.0) 
    let context = UIGraphicsGetCurrentContext()! 
    context.saveGState() 

    path.addClip() 
    draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) 

    let newImage = UIGraphicsGetImageFromCurrentImageContext()! 

    context.restoreGState() 
    UIGraphicsEndImageContext() 

    return newImage 
} 

Одним из решений было бы для посева изображение после маскировки, но в идеале я хочу, чтобы это было сделано как можно проще и эффективнее за один раз.

Любое решение/советы будут оценены.

+1

Нет, просто перейдите и обрезайте его. Это довольно просто, хотя, один вызов 'CGImageCreateWithImageInRect'. – Rob

+0

Спасибо за ваш комментарий @Rob, не могли бы вы поделиться быстрым фрагментом кода о том, как это реализовать? – Aleksander

+0

Не нужно кодировать образец Роб, я понял это. Спасибо за указатель! – Aleksander

ответ

3

Благодаря Rob Мне удалось реализовать решение, используя метод cropping(to:)CGImage.

Это двухэтапный процесс, когда сначала я маскирую изображение с использованием заданного пути, а затем обрезаю результат с помощью границ пути.

Ниже мой последний рабочие исходный код для реализации вырезкиUIBezierPathUIImage расширения:

extension UIImage { 

    func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage { 
     // Mask image using path 
     let maskedImage = imageByApplyingMaskingBezierPath(path) 

     // Crop image to frame of path 
     let croppedImage = UIImage(cgImage: maskedImage.cgImage!.cropping(to: path.bounds)!) 
     return croppedImage 
    } 

    func imageByApplyingMaskingBezierPath(_ path: UIBezierPath) -> UIImage { 
     // Define graphic context (canvas) to paint on 
     UIGraphicsBeginImageContext(size) 
     let context = UIGraphicsGetCurrentContext()! 
     context.saveGState() 

     // Set the clipping mask 
     path.addClip() 
     draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height)) 

     let maskedImage = UIGraphicsGetImageFromCurrentImageContext()! 

     // Restore previous drawing context 
     context.restoreGState() 
     UIGraphicsEndImageContext() 

     return maskedImage 
    } 

} 
+0

Отличная работа! man ~ – Jerome

+0

В приведенной ниже версии c показана только кадрирующая часть вашего решения, и мы можем получить полную версию c всего вашего решения? @Александр, я бы по достоинству оценил это. –

0

Я использую следующий в объективном C, для обрезки любого изображения без transperant прямоугольника. проверьте, не полезно ли его

@implementation UIImage (cropTransperant) 
- (UIImage *) CropimageByTrimmingTransparentPixels { 
    int rows = self.size.height; 
    int cols = self.size.width; 
    int bytesPerRow = cols*sizeof(uint8_t); 

    if (rows < 2 || cols < 2) { 
     return self; 
    } 


    uint8_t *bitmapData = calloc(rows*cols, sizeof(uint8_t)); 


    CGContextRef contextRef = CGBitmapContextCreate(bitmapData, cols, rows, 8, bytesPerRow, NULL, kCGImageAlphaOnly); 


    CGImageRef cgImage = self.CGImage; 
    CGRect rect = CGRectMake(0, 0, cols, rows); 
    CGContextDrawImage(contextRef, rect, cgImage); 

    //get all non-transparent pixels in every row and every column 
    uint16_t *rowSum = calloc(rows, sizeof(uint16_t)); 
    uint16_t *colSum = calloc(cols, sizeof(uint16_t)); 

    //loop through all pixels 
    for (int row = 0; row < rows; row++) { 
     for (int col = 0; col < cols; col++) 
     { 
      if (bitmapData[row*bytesPerRow + col]) { //when you get non transperant pixel 
       rowSum[row]++; 
       colSum[col]++; 
      } 
     } 
    } 

     UIEdgeInsets crop = UIEdgeInsetsMake(0, 0, 0, 0); //croprect, initialize with 0,0,0, 

    for (int i = 0; i<rows; i++) {  //get top 
     if (rowSum[i] > 0) { 
      crop.top = i; break; 
     } 
    } 

    for (int i = rows; i >= 0; i--) {  //get bottom 
     if (rowSum[i] > 0) { 
      crop.bottom = MAX(0, rows-i-1); break; 
     } 
    } 

    for (int i = 0; i<cols; i++) {  //get left 
     if (colSum[i] > 0) { 
      crop.left = i; break; 
     } 
    } 

    for (int i = cols; i >= 0; i--) {  //get right 
     if (colSum[i] > 0) { 
      crop.right = MAX(0, cols-i-1); break; 
     } 
    } 

    free(bitmapData); 
    free(colSum); 
    free(rowSum); 

    if (crop.top == 0 && crop.bottom == 0 && crop.left == 0 && crop.right == 0) { 

     return self; 
    } 
    else { 

     rect.origin.x += crop.left; 
     rect.origin.y += crop.top; 
     rect.size.width -= crop.left + crop.right; 
     rect.size.height -= crop.top + crop.bottom; 

     //crop image to calcualted rect 
     CGImageRef newImage = CGImageCreateWithImageInRect(cgImage, rect); 


     return [UIImage imageWithCGImage:newImage]; 
    } 
} 

@end 
+0

У меня не было возможности протестировать ваш код, но логически это, кажется, имеет смысл. Однако в идеале я искал более простое и эффективное решение. Я не знаю, почему это было проголосовано, хотя, кто-нибудь хочет объяснить? – Aleksander