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;
}

你可能感兴趣的:(iOS - UIImage 与 RGB 互转)