iOS UImage 與 RGB 裸數據的相互轉換 Touch the data of image in iOS Get data from a image 較簡單,根據已有的 image 的屬性,創建 CGBitmapContext, 這個 context 是帶有直接訪問的指針的。然後將 Imag ...
iOS UImage 與 RGB 裸數據的相互轉換
Touch the data of image in iOS
Get data from a image
較簡單,根據已有的 image 的屬性,創建 CGBitmapContext, 這個 context 是帶有直接訪問的指針的。然後將 Image 繪製到這個 context, 得到裸數據。
Code:
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(srcImg.CGImage);
CGColorSpaceRef colorRef = CGColorSpaceCreateDeviceRGB();
float width = srcImg.size.width;
float height = srcImg.size.height;
// Get source image data
uint8_t *imageData = (uint8_t *) malloc(width * height * 4);
CGContextRef imageContext = CGBitmapContextCreate(imageData,
width, height,
8, static_cast<size_t>(width * 4),
colorRef, alphaInfo);
CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), srcImg.CGImage);
CGContextRelease(imageContext);
CGColorSpaceRelease(colorRef);
拿到指針就可以操作數據了。
需要註意的地方:alphaInfo, 通常我處理的圖像都是帶有透明度的,但是 RGBA 和 ARGB 都有遇到過,所以需要看清楚這個信息是哪一種,列舉如下:
typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {
kCGImageAlphaNone, /* For example, RGB. */
kCGImageAlphaPremultipliedLast, /* For example, premultiplied RGBA */
kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
kCGImageAlphaLast, /* For example, non-premultiplied RGBA */
kCGImageAlphaFirst, /* For example, non-premultiplied ARGB */
kCGImageAlphaNoneSkipLast, /* For example, RBGX. */
kCGImageAlphaNoneSkipFirst, /* For example, XRGB. */
kCGImageAlphaOnly /* No color data, alpha data only */
};
一般來說我們的工程都會打開一個png圖片壓縮的選項,所以我們拿到的 image 一般來說是 premultiplied 的,也就是說,RGB的值已經是和alpha值相乘的結果了,在 OpenGL 紋理操作的時候要註意。
Create a image from raw data
1) Create BitmapContext and get image from it
size_t bitsPerComponent = 8;
size_t bitsPerPixel = 32;
size_t bytesPerRow = static_cast<size_t>(4 * outWidth);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
// set the alpha mode RGBA
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
////
// This method is much simple and without coordinate flip
// You can use this method either
// but the UIGraphicsBeginImageContext() is much more modern.
////
CGContextRef cgBitmapCtx = CGBitmapContextCreate(outData,
static_cast<size_t>(outWidth),
static_cast<size_t>(outHeight),
bitsPerComponent,
bytesPerRow,
colorSpaceRef,
bitmapInfo);
CGImageRef cgImg = CGBitmapContextCreateImage(cgBitmapCtx);
UIImage *retImg = [UIImage imageWithCGImage:cgImg];
CGContextRelease(cgBitmapCtx);
CGColorSpaceRelease(colorSpaceRef);
free(outData);
主要方法就是 CGBitmapContextCreate
直接將數據地址 outData
作為初始化參數提供,這樣這個 context 就是帶有正確數據的了,然後就直接獲得 CGImage 了。
2) CGDataProvider --> CGImage --> UIImage
這個方法的思路就是直接使用 CGImageCreate()
函數直接創建 CGImage.
size_t bitsPerComponent = 8;
size_t bitsPerPixel = 32;
size_t bytesPerRow = static_cast<size_t>(4 * outWidth);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
// set the alpha mode RGBA
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, outData, outDataLength, NULL);
CGImageRef imageRef = CGImageCreate(outWidth, outHeight,
bitsPerComponent, bitsPerPixel, bytesPerRow,
colorSpaceRef, bitmapInfo, provider,
NULL, NO, renderingIntent);
UIImage *retImage1 = [UIImage imageWithCGImage:imageRef];
創建 provider 的時候無需回調函數,故直接提供 NULL.
創建 CGImage 時需要提供詳細的配置參數,其中部分參數和創建 CGBitmapContext 相同,額外需要提供的就是預設參數以及不需要使用的特性比如 decode array 等等。得到 CGImageRef 後可以直接得到 UIImage 對象,但是我發現我的同事寫瞭如下一段代碼:
UIGraphicsBeginImageContext(outSize);
// the same: UIGraphicsBeginImageContextWithOptions(outSize, NO, 1.0f);
CGContextRef cgCtx = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(cgCtx, kCGBlendModeCopy);
CGContextDrawImage(cgCtx, CGRectMake(0.0, 0.0, outWidth, outHeight), imageRef);
UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
這段代碼的功能是:創建CGContext 然後將 CGimage 繪製到當前的 Context 上面,再得到 UIImage.
基本可以認為是 [UIImage imageWithCGImage:imageRef]
我的不知道為什麼同事這樣寫,可能是這個方法是有什麼坑的,我暫時沒有遇到。所以我將這段代碼先記下來,留著以後用。
關於 UIGraphicsBeginImageContext()
方法,apple 的介紹如下:
iOS Note: iOS applications should use the function UIGraphicsBeginImageContextWithOptions instead of using the low-level Quartz functions described here. If your application creates an offscreen bitmap using Quartz, the coordinate system used by bitmap graphics context is the default Quartz coordinate system. In contrast, if your application creates an image context by calling the function UIGraphicsBeginImageContextWithOptions, UIKit applies the same transformation to the context’s coordinate system as it does to a UIView object’s graphics context. This allows your application to use the same drawing code for either without having to worry about different coordinate systems. Although your application can manually adjust the coordinate transformation matrix to achieve the correct results, in practice, there is no performance benefit to doing so.
上面所述的 low-level Quartz function 就是我們上面用的 CGBitmapContextCreate
這一類方法。所以用這個新的方法直接就將 創建好的 bitmapContext 綁定到當前狀態。仍然調用 CGContextDrawImage()
函數,將 CGImage 繪製到指定的 context 上面。然後再獲取 UIImage. 總的來說,這個和我們之前的那一套原理是一樣的,應該說這樣是更新式的做法,推薦的做法。
參考
How do I create a CGImage with RGB data?
Converting RGB data into a bitmap in Objective-C++ Cocoa
CGImage to UIImage doesn't work