developer tip

NSImage를 새 파일로 저장하는 방법

optionbox 2020. 10. 25. 12:08
반응형

NSImage를 새 파일로 저장하는 방법


NSImage를 특정 디렉토리에 새 파일 (png, jpg, ...)로 저장하려면 어떻게해야합니까?


다음과 같이하십시오.

NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];

NSImage에 다음과 같이 카테고리를 추가 할 수 있습니다.

@interface NSImage(saveAsJpegWithName)
- (void) saveAsJpegWithName:(NSString*) fileName;
@end

@implementation NSImage(saveAsJpegWithName)

- (void) saveAsJpegWithName:(NSString*) fileName
{
    // Cache the reduced image
    NSData *imageData = [self TIFFRepresentation];
    NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
    NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor];
    imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
    [imageData writeToFile:fileName atomically:NO];        
}

@end

"TIFFRepresentation"에 대한 호출은 필수입니다. 그렇지 않으면 유효한 이미지를 얻지 못할 수 있습니다.


나머지 분들에 대해서는 잘 모르겠지만 저는 엔칠 라다를 다 먹는 것을 선호합니다. 위에서 설명한 내용이 작동하고 아무런 문제가 없지만 몇 가지 누락 된 사항을 발견했습니다. 여기에서 내 관찰을 강조하겠습니다.

  • 먼저 이미지 해상도가 그보다 크더라도 72 DPI로 보이는 최상의 표현이 제공됩니다. 그래서 당신은 해상도를 잃고 있습니다
  • 둘째, 애니메이션 GIF 또는 PDF와 같은 다중 페이지 이미지는 어떻습니까? 계속해서 애니메이션 GIF를 시도해보세요. 애니메이션이 사라졌습니다.
  • 마지막으로 EXIF, GPS 등과 같은 모든 메타 데이터는 손실됩니다.

그 이미지를 변환하고 싶다면 정말로이 모든 것을 잃고 싶습니까? 전체 식사를하고 싶다면 계속 읽어 보자.

때때로 그리고 나는 때때로 좋은 올드 스쿨 개발보다 더 좋은 것이 없다는 것을 의미합니다. 예, 그것은 우리가 약간의 일을해야한다는 것을 의미합니다!

시작하자:

NSData에 카테고리를 생성합니다. 이것은 스레드로부터 안전하기를 원하고 스택에 물건을 넣는 것보다 더 안전한 것은 없기 때문에 클래스 메소드입니다. 두 가지 유형의 방법이 있습니다. 하나는 다중 페이지가 아닌 이미지 출력용이고 다른 하나는 다중 페이지 이미지 출력용입니다.

단일 이미지 목록 : JPG, PNG, BMP, JPEG-2000

여러 이미지 목록 : PDF, GIF, TIFF

먼저 메모리에 변경 가능한 데이터 공간을 만듭니다.

NSMutableData * imageData    = [NSMutableData data];

두 번째로 CGImageSourceRef를 가져옵니다. 네, 벌써 못 생겼 네요. 그다지 나쁘지 않습니다. 계속 진행하겠습니다. 소스 이미지가 데이터의 표현이나 NSImage 청크가 아니라 정말로 원합니다. 그러나 우리에게는 작은 문제가 있습니다. 소스가 호환되지 않을 수 있으므로 CGImageSourceCopyTypeIdentifiers ()에 나열된 것과 비교하여 UTI를 확인하십시오.

일부 코드 :

CGImageSourceRef imageSource = nil;
if ( /* CHECK YOUR UTI HERE */ )
    return CGImageSourceCreateWithURL( (CFURLRef)aURL, nil );

NSImage * anImage = [[NSImage alloc] initWithContentsOfURL:aURL];

if ( anImage )
    return CGImageSourceCreateWithData( (CFDataRef)[anImage TIFFRepresentation], nil );

잠깐만 요, NSImage가 왜 거기에 있습니까? 메타 데이터가없고 CGImageSource가 지원하지 않는 일부 형식이 있지만 이는 유효한 이미지입니다. 예를 들어 이전 스타일의 PICT 이미지가 있습니다.

이제 CGImageSourceRef가 있고, nil이 아닌지 확인한 다음 CGImageDestinationRef를 가져옵니다. 이 모든 심판이 추적해야합니다. 지금까지 우리는 2입니다!

이 함수를 사용합니다 : CGImageDestinationCreateWithData ()

  • 첫 번째 매개 변수는 imageData입니다 (Cast CFMutableDataRef).
  • 2nd Param은 출력 UTI입니다. 위의 목록을 기억하십시오. (예 : kUTTypePNG)
  • 3rd Param은 저장할 이미지의 수입니다. 단일 이미지 파일의 경우 1이고 그렇지 않으면 다음을 사용할 수 있습니다.

    CGImageSourceGetCount (이미지 소스);

  • 4 번째 Param은 nil입니다.

이 CGImageDestinationRef가 있는지 확인하고 이제 소스의 이미지를 여기에 추가해 보겠습니다. 이것은 또한 모든 / 모든 메타 데이터를 포함하고 해상도를 유지합니다.

여러 이미지의 경우 루프 :

for ( NSUInteger i = 0; i < count; ++i )
                CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );

단일 이미지의 경우 인덱스 0에있는 코드 한 줄입니다.

CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);

좋아, 디스크 또는 데이터 컨테이너에 쓰는 것을 마무리하십시오.

CGImageDestinationFinalize( imageDest );

따라서 처음부터 Mutable Data에는 이제 모든 이미지 데이터와 메타 데이터가 포함됩니다.

아직 끝났어? 거의 가비지 컬렉션을 사용하더라도 정리해야합니다! 소스와 대상에 대한 두 개의 Ref를 기억하고 있으므로 CFRelease ()를 수행하십시오.

이제 우리는 끝났고 당신이 끝내는 것은 모든 메타 데이터, 해상도 등을 유지하는 변환 된 이미지입니다.

내 NSData 카테고리 메소드는 다음과 같습니다.

+ (NSData *) JPGDataFromURL:(NSURL *)aURL;
+ (NSData *) PNGDataFromURL:(NSURL *)aURL;
+ (NSData *) BMPDataFromURL:(NSURL *)aURL;
+ (NSData *) JPG2DataFromURL:(NSURL *)aURL;

+ (NSData *) PDFDataFromURL:(NSURL *)aURL;
+ (NSData *) GIFDataFromURL:(NSURL *)aURL;
+ (NSData *) TIFFDataFromURL:(NSURL *)aURL;

크기 조정 또는 ICO / ICNS는 어떻습니까? 이것은 다른 날에 대한 것이지만 요약하면 먼저 크기 조정을 시작합니다.

  1. 새 크기로 컨텍스트 만들기 : CGBitmapContextCreate ()
  2. 인덱스에서 이미지 참조 가져 오기 : CGImageSourceCreateImageAtIndex ()
  3. 메타 데이터 사본 가져 오기 : CGImageSourceCopyPropertiesAtIndex ()
  4. 컨텍스트에 이미지를 그립니다. CGContextDrawImage ()
  5. 컨텍스트에서 크기 조정 된 이미지 가져 오기 : CGBitmapContextCreateImage ()
  6. 이제 이미지와 메타 데이터를 Dest Ref : CGImageDestinationAddImage ()에 추가합니다.

소스에 포함 된 여러 이미지에 대해 헹구고 반복합니다.

ICO와 ICNS의 유일한 차이점은 하나는 단일 이미지이고 다른 하나는 하나의 파일에 여러 이미지라는 것입니다. 어느 쪽인지 짐작할 수 있겠습니까?! ;-) 이러한 형식의 경우 특정 크기로 크기를 조정해야합니다. 그렇지 않으면 ERROR가 발생합니다. 하지만 프로세스는 적절한 UTI를 사용하는 곳과 정확히 동일하지만 크기 조정은 조금 더 엄격합니다.

좋아, 이것이 다른 사람들에게 도움이 되었기를 바랍니다. 그리고 당신은 지금처럼 가득 차 있습니다!

언급하는 것을 잊었습니다. NSData 객체를 얻으면 writeToFile, writeToURL과 같이 원하는대로 수행하거나 원하는 경우 다른 NSImage를 만듭니다.

즐거운 코딩 되세요!


swift3를 사용하여 PNG로 저장

import AppKit

extension NSImage {
    @discardableResult
    func saveAsPNG(url: URL) -> Bool {
        guard let tiffData = self.tiffRepresentation else {
            print("failed to get tiffRepresentation. url: \(url)")
            return false
        }
        let imageRep = NSBitmapImageRep(data: tiffData)
        guard let imageData = imageRep?.representation(using: .PNG, properties: [:]) else {
            print("failed to get PNG representation. url: \(url)")
            return false
        }
        do {
            try imageData.write(to: url)
            return true
        } catch {
            print("failed to write to disk. url: \(url)")
            return false
        }
    }
}

Swift 4.2 솔루션

public extension NSImage {
    public func writePNG(toURL url: URL) {

        guard let data = tiffRepresentation,
              let rep = NSBitmapImageRep(data: data),
              let imgData = rep.representation(using: .png, properties: [.compressionFactor : NSNumber(floatLiteral: 1.0)]) else {

            Swift.print("\(self) Error Function '\(#function)' Line: \(#line) No tiff rep found for image writing to \(url)")
            return
        }

        do {
            try imgData.write(to: url)
        }catch let error {
            Swift.print("\(self) Error Function '\(#function)' Line: \(#line) \(error.localizedDescription)")
        }
    }
}

스위프트 스타일 :

if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
      if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
      {
           data.writeToFile("/path/to/file.png", atomically: false)
      }
}

SWIFT를 사용하여 작업을 보장하는 한 가지 더 :

I have an "Image Well" where to user can drop any image. And this "Image Well" has an image property (of type NSImage) accessed via outlet:

@IBOutlet weak var imageWell: NSImageView!

And the code that saves this image (you can put it inside the button action) is:

if imageWell.image != nil {
   let bMImg = NSBitmapImageRep(data: (imageWell?.image?.TIFFRepresentation)!)
   let dataToSave = bMImg?.representationUsingType(NSBitmapImageFileType.NSJPEGFileType, properties: [NSImageCompressionFactor : 1])
   dataToSave?.writeToFile("/users/user/desktop/image.jpg", atomically: true)
}

In the 1st line of the given code we check if our Image Well has an image.

In the 2nd line we make a bitmap representation of that image.

In the 3rd line we convert our BitmapRepresentation to a JPG type with a compression factor set to "1" (no compression).

In the 4th line we save the JPG data with a given path. "atomically: true" means that the file is saved as one piece and that assures us that the operation will be successfull.

N.B.: You can use another NSBitmapImageFileType in the 3rd line, to save your image to another format. It has plenty:

NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType

etc.


To help with cross-platform code, I implemented a version ofUIImagePNGRepresentation() that runs on Mac (and uses NSImage).

#if os(macOS)

public func UIImagePNGRepresentation(_ image: NSImage) -> Data? {
    guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)
        else { return nil }
    let imageRep = NSBitmapImageRep(cgImage: cgImage)
    imageRep.size = image.size // display size in points
    return imageRep.representation(using: .png, properties: [:])
}

#endif

Usage:

if let data = UIImagePNGRepresentation(myImage) {
    do { try data.write(to: url, options: [.atomic]) }
    catch let error { print("Error writing image (\(error))") }
}

참고URL : https://stackoverflow.com/questions/3038820/how-to-save-a-nsimage-as-a-new-file

반응형