官方文档
在实现之前需要了解关于 Bitmap 概念及存储方式
Bitmap 是使用像素列阵来表示图像
位图的像素都分配有特定的位置和颜色值,每一个像素的颜色信息由 RGB 或者 灰度值表示
根据位深度,可以将位图分为 1、4、8、18、24 及 32 位图像,每个像素使用的位信息越多,可用的颜色越多,颜色表现越逼真,相应的数据量越大
举例如果使用 RGB 色彩空间表示 32 位图像,即每一个像素点对应的存储方式为用 4 个字节表示一个像素,每 8 位(每个字节) 表示一个颜色值
在了解了 bitmap 之后,我们看下创建 bitmap 上下文的函数
CGBitmapContextCreate
/**
通过 rgba 数组转 Bitmap 上下文
@param data: rgba 数组
@param width: 图片的宽度
@param height: 图片的高度
@param bitsPerComponent: 每一个颜色值所包含的位数 (RGB 中的 R 用多少位表示)
@param bytesPerRow: 每一行的所有的字节数 (width * 每一个像素使用多少字节表示)
@param colorspace: 色彩空间 RGBA、灰度、CMYK
@param bitmapInfo: 指定渲染区域是否包含 alpha,以及每个像素点的位置
*/
CGBitmapContextCreate(void * __nullable data,
size_t width,
size_t height,
size_t bitsPerComponent,
size_t bytesPerRow,
CGColorSpaceRef cg_nullable space,
uint32_t bitmapInfo)
>> CGColorSpaceRef cg_nullable space
色彩空间有三种: 灰度、RGBA、CMYK
// 灰度 色彩
CGColorSpaceRef graySpaceRef = CGColorSpaceCreateDeviceGray();
// RGBA 色彩 (显示3色)
CGColorSpaceRef rgbSapceRef = CGColorSpaceCreateDeviceRGB();
// CMYK 色彩 (印刷4色)
CGColorSpaceRef cmykSpaceRef = CGColorSpaceCreateDeviceCMYK();
>> uint32_t bitmapInfo
CGImageAlphaInfo | CGBitmapInfo 的组合; 例如 kCGImageAlphaPremultipliedLast|kCGBitmapFloatComponents|kCGImageByteOrder16Little
CGImageAlphaInfo
typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {
kCGImageAlphaNone, /* 等价于 kCGImageAlphaNoneSkipLast; 例如: RGB. */
kCGImageAlphaPremultipliedLast, /* alpha 存储在低位, 且 alpha 已于每个颜色分量进行相乘; 例如: RGBA */
kCGImageAlphaPremultipliedFirst, /* alpha 存储在高位, 且 alpha 已于每个颜色分量进行相乘; 例如: ARGB */
kCGImageAlphaLast, /* alpha 存储在低位; 例如 RGBA */
kCGImageAlphaFirst, /* alpha 存储在高位; 例如 ARGB */
kCGImageAlphaNoneSkipLast, /* 如果色值位数大于所需空间,则低位忽略; 例如 RBGX. */
kCGImageAlphaNoneSkipFirst, /* 如果色值位数大于所需空间,则高位忽略; 例如, XRGB. */
kCGImageAlphaOnly /* No color data, alpha data only */
};
CGBitmapInfo
typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {
kCGBitmapAlphaInfoMask = 0x1F,
kCGBitmapFloatInfoMask = 0xF00,
kCGBitmapFloatComponents = (1 << 8), // 浮点型表示
kCGBitmapByteOrderMask = kCGImageByteOrderMask,
kCGBitmapByteOrderDefault = kCGImageByteOrderDefault, // 默认
kCGBitmapByteOrder16Little = kCGImageByteOrder16Little, // 16 位小端
kCGBitmapByteOrder32Little = kCGImageByteOrder32Little, // 32 位小端
kCGBitmapByteOrder16Big = kCGImageByteOrder16Big, // 16 位大端
kCGBitmapByteOrder32Big = kCGImageByteOrder32Big // 32 位大端
} CG_AVAILABLE_STARTING(10.0, 2.0);
// Big、Little 大端和小端分别
// 大端表示低字节放在高地址,高字节放在低地址
// 小端表示高字节放在高地址,低字节放在低地址
>> 如何查看 bitmapInfo 的组合是否正确
当 CGBitmapContextCreate 创建失败时,context 为 0x0,添加 CGBITMAP_CONTEXT_LOG_ERRORS 环境变量,可以查看正确 bitmapInfo 组合
目前我打印出来的是一下组合
CGBitmapContextCreate: unsupported parameter combination:
8 bits/component; integer;
24 bits/pixel;
RGB color space model; kCGImageAlphaNone;
default byte order;
2400 bytes/row.
Valid parameters for RGB color space model are:
// 每一个像素点多少位 每一个颜色值用多少位表示 BitmapInfo
16 bits per pixel, 5 bits per component, kCGImageAlphaNoneSkipFirst
32 bits per pixel, 8 bits per component, kCGImageAlphaNoneSkipFirst
32 bits per pixel, 8 bits per component, kCGImageAlphaNoneSkipLast
32 bits per pixel, 8 bits per component, kCGImageAlphaPremultipliedFirst
32 bits per pixel, 8 bits per component, kCGImageAlphaPremultipliedLast
32 bits per pixel, 10 bits per component, kCGImageAlphaNone|kCGImagePixelFormatRGBCIF10
64 bits per pixel, 16 bits per component, kCGImageAlphaPremultipliedLast
64 bits per pixel, 16 bits per component, kCGImageAlphaNoneSkipLast
64 bits per pixel, 16 bits per component, kCGImageAlphaPremultipliedLast|kCGBitmapFloatComponents|kCGImageByteOrder16Little
64 bits per pixel, 16 bits per component, kCGImageAlphaNoneSkipLast|kCGBitmapFloatComponents|kCGImageByteOrder16Little
128 bits per pixel, 32 bits per component, kCGImageAlphaPremultipliedLast|kCGBitmapFloatComponents
128 bits per pixel, 32 bits per component, kCGImageAlphaNoneSkipLast|kCGBitmapFloatComponents
See Quartz 2D Programming Guide (available online) for more information.
2021-06-09 07:06:10.288051+0800 Block[96262:4388274] [Unknown process name] CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
示例代码
>> RGB 转 UIImage
+ (UIImage *)imageForRGBA:(unsigned char *)rgba
width:(CGFloat)width
height:(CGFloat)height {
int bytes_per_pix = 4;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(rgba,
width, height, 8,
width * bytes_per_pix,
colorSpace, kCGImageAlphaNoneSkipLast);
CGImageRef frame = CGBitmapContextCreateImage(newContext);
UIImage *image = [UIImage imageWithCGImage:frame];
CGImageRelease(frame);
CGContextRelease(newContext);
CGColorSpaceRelease(colorSpace);
return image;
}
>> UIImage 转 RGB
+ (unsigned char *)rgbArray:(UIImage *)image {
CGImageRef refImage = [image CGImage];
CGSize size = image.size;
int bitsPerComponent = 8;
int bytePerPixel = 4;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
int pixelCount = size.width * size.height;
uint8_t *rgba = malloc(pixelCount * bytePerPixel);
CGContextRef context = CGBitmapContextCreate(rgba,
size.width,
size.height,
bitsPerComponent,
bytePerPixel * size.width,
colorSpace,
kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), refImage);
CGContextRelease(context);
uint8_t *rgb = malloc(pixelCount * bytePerPixel);
int m = 0;
int n = 0;
for(int i = 0; i < pixelCount; i++) {
rgb[m++] = rgba[n++]; // r
rgb[m++] = rgba[n++]; // g
rgb[m++] = rgba[n++]; // b
rgb[m++] = rgba[n++]; // a
}
free(rgba);
return rgb;
}