嚴格說起來這一篇其實跟 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
沒有留言:
張貼留言