en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

4.23.2012

使用 Objective-C 製作 BMP 24bit 的圖片格式

嚴格說起來這一篇其實跟 iOS 比較沒關係,大部分的在 iOS 上都是採用 PNG 32 位元格式的影像來做應用,除非是專門在做影像處理相關的應用程式,不然很少會使用 BMP 24 位元格式的影像。BMP, bitmap 點陣圖,是 Windows 採用的點陣圖檔案儲存格式,也是做影像處理相關研究時很常被使用的一種影像格式,下面就來看看如何將圖片轉換成 BMP 24bit(不包含 alpha 透明度)的格式吧。

注意,以下是我使用在 MAC OS X Application 的方法函式,如果想要將此函式置入 iOS 中,可以參考影像與資料陣列的轉換(上)影像與資料陣列的轉換(下)等文章,做物件類別上的修正,原則上它們的差異只有在轉換至 CGImageRef 上比較不一樣,在 iOS 中的 UIImage 轉換至 CGImageRef 非常容易,你可以直接使用 UIImage.CGImage 就能輕鬆達成,但是在 MAC OS X Application 中,NSImage 要轉換至 CGImageRef 就沒這麼容易了。

首先是結構宣告的部分。
typedef struct RGBAData {
    Byte r;
    Byte g;
    Byte b;
    Byte a;
}RGBAData;

typedef struct RGBData {
    Byte r;
    Byte g;
    Byte b;
}RGBData;

#define BITS_PER_COMPONENT 8
#define _32_BYTE_PER_PIXEL 4
#define _24_BYTE_PER_PIXEL 3

接下來就是轉換成 BMP 24bit 的過程,從 NSImage 取得影像之後,將它放入宣告的 RGBA 結構中,這個結構是預設影像為 32 bit 位元深度的影像(包含 alpha 透明度),接著在宣告一個 RGB 的結構存放原影像去除透明度之後的資訊,最後才將此結構轉換成 BMP 的格式。
- (void)saveBMP24ImageFile:(NSImage *)image {

    //將圖片轉換成 CGImageRef 格式並取得大小
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)[image TIFFRepresentation], NULL);
    CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, NULL);
    int width = CGImageGetWidth(imageRef);
    int height = CGImageGetHeight(imageRef);

    //資料結構的參數和色域
    NSUInteger bytesPerRow = _32_BYTE_PER_PIXEL * width;
    NSUInteger bitsPerComponent = BITS_PER_COMPONENT;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    //宣告一個與圖片大小相同的資料結構一維陣列
    RGBAData *sourceData = malloc(height * width * _32_BYTE_PER_PIXEL);

    //將圖片資訊寫入資料結構陣列中
    CGContextRef context = CGBitmapContextCreate(sourceData,
                                                 width,
                                                 height,
                                                 bitsPerComponent,
                                                 bytesPerRow,
                                                 colorSpace,
                                                 kCGImageAlphaNoneSkipLast);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

    //宣告一個與圖片大小相同但是去掉alpha的資料結構一維陣列
    RGBData *bmpData = malloc(height * width * _24_BYTE_PER_PIXEL);

    //將alpha channel以外的資訊存入結構中
    for (int x=0; x!=width; x++) {
        for (int y=0; y!=height; y++) {
            bmpData[y*width + x].r = sourceData[y*width + x].r;
            bmpData[y*width + x].g = sourceData[y*width + x].g;
            bmpData[y*width + x].b = sourceData[y*width + x].b;
        }
    }

    //將結構轉換成bitmap格式
    unsigned char *bmpChar = (unsigned char *)bmpData;
    NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc]initWithBitmapDataPlanes:&bmpChar
                                                     pixelsWide:width
                                                     pixelsHigh:height
                                                  bitsPerSample:8
                                                samplesPerPixel:3
                                                       hasAlpha:NO
                                                       isPlanar:NO
                                                 colorSpaceName:NSDeviceRGBColorSpace
                                                    bytesPerRow:width*_24_BYTE_PER_PIXEL
                                                    bitsPerPixel:BITS_PER_COMPONENT*_24_BYTE_PER_PIXEL];


    //使用NSSavePanel來存檔
    NSSavePanel *sPanel = [NSSavePanel savePanel];
    [self savePanelSetupWithFileType:[NSArray arrayWithObject:@"bmp"]];

    if ([sPanel runModal] == NSOKButton) {

        //將bitmap轉換成NSData並儲存
        NSData *data = [bitmap representationUsingType:NSBMPFileType properties:nil];
        [data writeToURL:[sPanel URL] atomically:NO];
    }
}



ps:補充一下大致的轉換過程,以便釐清每個步驟的程式碼。
NSImage -> CGImageSourceRef -> CGImageRef -> CGContextRef -> RGBA Pixel Data -> RGB Pixel Data -> NSBitmapImageRep -> NSData -> BMP 24bit File







沒有留言:

張貼留言