iOS - UIImage 与 RGB 互转

官方文档

在实现之前需要了解关于 Bitmap 概念及存储方式

Bitmap 是使用像素列阵来表示图像

image

位图的像素都分配有特定的位置和颜色值,每一个像素的颜色信息由 RGB 或者 灰度值表示

根据位深度,可以将位图分为 1、4、8、18、24 及 32 位图像,每个像素使用的位信息越多,可用的颜色越多,颜色表现越逼真,相应的数据量越大

举例如果使用 RGB 色彩空间表示 32 位图像,即每一个像素点对应的存储方式为用 4 个字节表示一个像素,每 8 位(每个字节) 表示一个颜色值

RGBA

在了解了 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 */
};
image.png

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 组合

Environment Variables

目前我打印出来的是一下组合

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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容