@interface YYImageEncoder : NSObject
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
@end
// - 实现 不可以直接调用 init 方法 可以调用 initWithType: 并且在initWithType中调用的是 [super init];
- (instancetype)init {
@throw [NSException exceptionWithName:@"YYImageEncoder init error" reason:@"YYImageEncoder must be initialized with a type. Use 'initWithType:' instead." userInfo:nil];
return nil;
}
- (instancetype)initWithType:(YYImageType)type {
self = [super init];
if (!self) return nil;
return self;
}
请先阅读 谈谈 iOS 中图片的解压缩
CGBitmapInfo : alpha 的信息 | 颜色分量是否为浮点数 | 像素格式的字节顺序 (用这三个数据来表示CGBitmapInfo)
alpha 的信息 :
是否有 alpha 信息
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaFirst ||
alphaInfo == kCGImageAlphaLast) {
hasAlpha = YES;
}
如果包含 alpha ,那么 alpha 信息所处的位置,在像素的最低有效位,比如 RGBA ,还是最高有效位,比如 ARGB ;
如果包含 alpha ,那么每个颜色分量是否已经乘以 alpha 的值,这种做法可以加速图片的渲染时间,因为它避免了渲染时的额外乘法运算。比如,对于 RGB 颜色空间,用已经乘以 alpha 的数据来渲染图片,每个像素都可以避免 3 次乘法运算,红色乘以 alpha ,绿色乘以 alpha 和蓝色乘以 alpha
CGImageAlphaInfo info = (hasAlpah) ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
至于颜色分量是否为浮点数,这个就比较简单了,直接逻辑或 kCGBitmapFloatComponents 就可以了; 我们一般用不上这个值
们知道字节顺序的值应该使用的是 32 位的主机字节顺序 kCGBitmapByteOrder32Host ,这样的话不管当前设备采用的是小端模式还是大端模式,字节顺序始终与其保持一致。
[代码]
CGImageRef imageRef = img.CGImage;
size_t wid = CGImageGetWidth(imageRef);
size_t hei = CGImageGetHeight(imageRef);
// - 位图是否包含透明度
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaFirst ||
alphaInfo == kCGImageAlphaLast) {
hasAlpha = YES;
}
// - 位图的字节存出顺序
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
// - 位图的透明度的信息的位置
bitmapInfo |= ((hasAlpha) ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst);
// - 色彩空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// - 创建图像上下文
CGContextRef context = CGBitmapContextCreate(NULL, wid, hei, 8, 0, colorSpace, bitmapInfo);
// - 将原始位图, 绘制到图形上下文;
CGContextDrawImage(context, CGRectMake(0, 0, wid, hei), imageRef);
// - 创建一个新的解压后的位图
CGImageRef newImage = CGBitmapContextCreateImage(context);
// - 声明 YYWeakProxy
@interface YYWeakProxy : NSProxy
@property (nullable, nonatomic, weak, readonly) id target;
@end
// - 定义 YYWeakProxy
@implementation YYWeakProxy
+ (instancetype)proxyWithTarget:(id)target {
return [[YYWeakProxy alloc] initWithTarget:target];
}
- (id)forwardingTargetForSelector:(SEL)selector {
return _target;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_target respondsToSelector:aSelector];
}
- (BOOL)isEqual:(id)object {
return [_target isEqual:object];
}
- (NSUInteger)hash {
return [_target hash];
}
- (Class)superclass {
return [_target superclass];
}
- (Class)class {
return [_target class];
}
- (BOOL)isKindOfClass:(Class)aClass {
return [_target isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass {
return [_target isMemberOfClass:aClass];
}
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
return [_target conformsToProtocol:aProtocol];
}
- (BOOL)isProxy {
return YES;
}
- (NSString *)description {
return [_target description];
}
- (NSString *)debugDescription {
return [_target debugDescription];
}
@end
// - 使用 YYWeakProxy
_link = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(step:)];
// - 强转的宏定义
#define typeChange(type, instance) *((type *)&instance)
// - 32位转换
static inline uint32_t yy_swap_endian_uint32(uint32_t value) {
return
(uint32_t)((value & 0x000000FFU) << 24) |
(uint32_t)((value & 0x0000FF00U) << 8) |
(uint32_t)((value & 0x00FF0000U) >> 8) |
(uint32_t)((value & 0xFF000000U) >> 24) ;
}
// - 在色值前边是加入透明度
static inline uint32_t yy_addApha_uint32(uint32_t value) {
return value | 0xFF000000;
}
// - 像素结构体
typedef struct QGPixStruct {
UInt8 pixA;
UInt8 pixR;
UInt8 pixG;
UInt8 pixB;
}QGPix;
@implementation QGImageTool
/** data 转 image */
+ (UIImage * )imageFromeData:(void *)imageData imageSize:(CGSize)imageSize{
// - 调用函数
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(imageData,
imageSize.width,
imageSize.height,
8,
imageSize.width * 4,
colorSpace,
kCGBitmapByteOrder32Host |
kCGImageAlphaPremultipliedFirst);
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage *image = [UIImage imageWithCGImage:imageRef];
// - 释放内存
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return image;
}
/** image 转 data */
+(void *)dataFromeImage:(UIImage *)image{
// - 方案一 : 先绘制图形上下文, 然后初始化一个内存空间, 之后把像素信息复制到申请的内存空间中
return ({
// - 函数调用条件
CGImageRef imageRef = [image CGImage];
size_t wid = CGImageGetWidth(imageRef);
size_t hei = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// - 使用 coreGraphics 方式解码
// - 调用函数
CGContextRef context = CGBitmapContextCreate(NULL,
wid,
hei,
8,
0,
colorSpace,
kCGBitmapByteOrder32Host |
kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, wid, hei), imageRef);
void *data = malloc(wid * hei * 4);
memcpy(data, CGBitmapContextGetData(context), wid * hei * 4);
// - 释放内存
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
// - 使用 imageIO 方式解码图片
CGImageRef newImageSource;
CFDataRef dataFromImageDataProvider = CGDataProviderCopyData(CGImageGetDataProvider(newImageSource));
imageData = (GLubyte *)CFDataGetBytePtr(dataFromImageDataProvider);
data;
});
// - 方案二 : 在创建图形上下文时候直接将像素信息赋值到内存中
return ({
// - 函数调用条件
CGImageRef imageRef = [image CGImage];
size_t wid = CGImageGetWidth(imageRef);
size_t hei = CGImageGetHeight(imageRef);
size_t size = wid * hei * 4;
void* data = malloc(size);
memset(data, 0, size);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// - 调用函数
CGContextRef context = CGBitmapContextCreate(data, wid, hei, 8, wid * 4, colorSpace, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, wid, hei), imageRef);
// - 释放内存
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
data;
});
}
/** 把图片中的颜色替换为另一个颜色 */
+(UIImage *)repalceImage:(UIImage *)image color:(int)sourceColor toColor:(int)targetColor{
CGFloat width = image.size.width;
CGFloat height = image.size.height;
uint32_t *data = (uint32_t *)[self dataFromeImage:image];
for (int i = 0 ; i < width * height; i++) {
uint32_t *souPix = (data + i);
if (*souPix == yy_addApha_uint32(sourceColor)) {
*souPix = yy_addApha_uint32(targetColor);
}
({ // - 类型转换(自己写代码) 使用 yy_swap_endian_uint32 让结构体是正常的存储方式
uint32_t result = yy_swap_endian_uint32(*souPix);
QGPix pix = *((QGPix *)&result);
NSLog(@"%x - %x - %x - %x", pix.pixA, pix.pixR, pix.pixG, pix.pixB);
});
({// - 强制类型转换(宏定义) 使用 yy_swap_endian_uint32 让结构体是正常的存储方式
uint32_t result = yy_swap_endian_uint32(*souPix);
QGPix pix = typeChange(QGPix, result);
NSLog(@"%x - %x - %x - %x", pix.pixA, pix.pixR, pix.pixG, pix.pixB);
});
}
UIImage *img = [self imageFromeData:data imageSize:CGSizeMake(width, height)];
free(data);
return img;
}
@end
1、CGContextDrawImage方式(CoreGraphics)以上就是这种方法
使用 CGBitmapContextCreate 函数创建一个位图上下文;
使用 CGContextDrawImage 函数将原始位图绘制到上下文中;
使用 CGBitmapContextCreateImage 函数创建一张新的解压缩后的位图。
2、CGImageGetDataProvider方式(ImageIO)
使用 CGImageSourceCreateWithData(data) 创建ImageSource。
使用 CGImageSourceCreateImageAtIndex(source) 创建一个未解码的 CGImage。
使用 CGImageGetDataProvider(image) 获取这个图片的数据源。
使用 CGDataProviderCopyData(provider) 从数据源获取直接解码的数据。
// - 函数有很多, 这里只是记录下, 需要时候查阅 在 CFByteOrder.h 文件中
CFSwapInt32HostToLittle()
CFSwapInt32HostToBig()
// - 逆序四个字节或两个字节
#define YY_FOUR_CC(c1,c2,c3,c4) ((uint32_t)(((c4) << 24) | ((c3) << 16) | ((c2) << 8) | (c1)))
#define YY_TWO_CC(c1,c2) ((uint16_t)(((c2) << 8) | (c1)))
// - 这里用的是 | 逻辑运算符, 我之前用的是+ 就显得有些 low 了 需要学习下.
// - 逻辑运算的 | 很多时候, 就相当于 +;
// - 16位转换
static inline uint16_t yy_swap_endian_uint16(uint16_t value) {
return
(uint16_t) ((value & 0x00FF) << 8) |
(uint16_t) ((value & 0xFF00) >> 8) ;
}
// - 32位转换
static inline uint32_t yy_swap_endian_uint32(uint32_t value) {
return
(uint32_t)((value & 0x000000FFU) << 24) |
(uint32_t)((value & 0x0000FF00U) << 8) |
(uint32_t)((value & 0x00FF0000U) >> 8) |
(uint32_t)((value & 0xFF000000U) >> 24) ;
}
//
// RHSocketUtils.h
// RHSocketKitDemo
//
// Created by zhuruhong on 15/12/23.
// Copyright © 2015年 zhuruhong. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface RHSocketUtils : NSObject
/**
* 反转字节序列
*
* @param srcData 原始字节NSData
*
* @return 反转序列后字节NSData
*/
+ (NSData *)dataWithReverse:(NSData *)srcData;
/** 将数值转成字节。编码方式:低位在前,高位在后 */
+ (NSData *)byteFromInt8:(int8_t)val;
+ (NSData *)bytesFromInt16:(int16_t)val;
+ (NSData *)bytesFromInt32:(int32_t)val;
+ (NSData *)bytesFromInt64:(int64_t)val;
+ (NSData *)bytesFromValue:(int64_t)value byteCount:(int)byteCount;
+ (NSData *)bytesFromValue:(int64_t)value byteCount:(int)byteCount reverse:(BOOL)reverse;
/** 将字节转成数值。解码方式:前序字节为低位,后续字节为高位 */
+ (int8_t)int8FromBytes:(NSData *)data;
+ (int16_t)int16FromBytes:(NSData *)data;
+ (int32_t)int32FromBytes:(NSData *)data;
+ (int64_t)int64FromBytes:(NSData *)data;
+ (int64_t)valueFromBytes:(NSData *)data;
+ (int64_t)valueFromBytes:(NSData *)data reverse:(BOOL)reverse;
/** 将字符串转换为data。例如:返回8个字节的data, upano --> <7570616e 6f000000> */
+ (NSData *)dataFromNormalString:(NSString *)normalString byteCount:(int)byteCount;
/** 16进制字符串转换为data。24211D3498FF62AF --> <24211D34 98FF62AF> */
+ (NSData *)dataFromHexString:(NSString *)hexString;
/** data转换为16进制。<24211D34 98FF62AF> --> 24211D3498FF62AF */
+ (NSString *)hexStringFromData:(NSData *)data;
/** hex字符串转换为ascii码。00de0f1a8b24211D3498FF62AF --> 3030646530663161386232343231314433343938464636324146 */
+ (NSString *)asciiStringFromHexString:(NSString *)hexString;
/** ascii码转换为hex字符串。343938464636324146 --> 498FF62AF */
+ (NSString *)hexStringFromASCIIString:(NSString *)asciiString;
// Return line separators.
+ (NSData *)CRLFData;
+ (NSData *)CRData;
+ (NSData *)LFData;
+ (NSData *)ZeroData;
@end
//
// RHSocketUtils.m
// RHSocketKitDemo
//
// Created by zhuruhong on 15/12/23.
// Copyright © 2015年 zhuruhong. All rights reserved.
//
#import "RHSocketUtils.h"
@implementation RHSocketUtils
/**
* 反转字节序列
*
* @param srcData 原始字节NSData
*
* @return 反转序列后字节NSData
*/
+ (NSData *)dataWithReverse:(NSData *)srcData
{
// NSMutableData *dstData = [[NSMutableData alloc] init];
// for (NSUInteger i=0; i<srcData.length; i++) {
// [dstData appendData:[srcData subdataWithRange:NSMakeRange(srcData.length-1-i, 1)]];
// }//for
NSUInteger byteCount = srcData.length;
NSMutableData *dstData = [[NSMutableData alloc] initWithData:srcData];
NSUInteger halfLength = byteCount / 2;
for (NSUInteger i=0; i<halfLength; i++) {
NSRange begin = NSMakeRange(i, 1);
NSRange end = NSMakeRange(byteCount - i - 1, 1);
NSData *beginData = [srcData subdataWithRange:begin];
NSData *endData = [srcData subdataWithRange:end];
[dstData replaceBytesInRange:begin withBytes:endData.bytes];
[dstData replaceBytesInRange:end withBytes:beginData.bytes];
}//for
return dstData;
}
+ (NSData *)byteFromInt8:(int8_t)val
{
NSMutableData *valData = [[NSMutableData alloc] init];
[valData appendBytes:&val length:1];
return valData;
}
+ (NSData *)bytesFromInt16:(int16_t)val
{
NSMutableData *valData = [[NSMutableData alloc] init];
[valData appendBytes:&val length:2];
return valData;
}
+ (NSData *)bytesFromInt32:(int32_t)val
{
NSMutableData *valData = [[NSMutableData alloc] init];
[valData appendBytes:&val length:4];
return valData;
}
+ (NSData *)bytesFromInt64:(int64_t)val
{
NSMutableData *tempData = [[NSMutableData alloc] init];
[tempData appendBytes:&val length:8];
return tempData;
}
+ (NSData *)bytesFromValue:(int64_t)value byteCount:(int)byteCount
{
NSMutableData *valData = [[NSMutableData alloc] init];
int64_t tempVal = value;
int offset = 0;
while (offset < byteCount) {
unsigned char valChar = 0xff & tempVal;
[valData appendBytes:&valChar length:1];
tempVal = tempVal >> 8;
offset++;
}//while
return valData;
}
+ (NSData *)bytesFromValue:(int64_t)value byteCount:(int)byteCount reverse:(BOOL)reverse
{
NSData *tempData = [self bytesFromValue:value byteCount:byteCount];
if (reverse) {
return tempData;
}
return [self dataWithReverse:tempData];
}
+ (int8_t)int8FromBytes:(NSData *)data
{
NSAssert(data.length >= 1, @"uint8FromBytes: (data length < 1)");
int8_t val = 0;
[data getBytes:&val length:1];
return val;
}
+ (int16_t)int16FromBytes:(NSData *)data
{
NSAssert(data.length >= 2, @"uint16FromBytes: (data length < 2)");
int16_t val = 0;
[data getBytes:&val length:2];
return val;
}
+ (int32_t)int32FromBytes:(NSData *)data
{
NSAssert(data.length >= 4, @"uint16FromBytes: (data length < 4)");
int32_t val = 0;
[data getBytes:&val length:4];
return val;
}
+ (int64_t)int64FromBytes:(NSData *)data
{
NSAssert(data.length >= 8, @"uint16FromBytes: (data length < 8)");
int64_t val = 0;
[data getBytes:&val length:8];
return val;
}
+ (int64_t)valueFromBytes:(NSData *)data
{
NSUInteger dataLen = data.length;
int64_t value = 0;
int offset = 0;
while (offset < dataLen) {
int32_t tempVal = 0;
[data getBytes:&tempVal range:NSMakeRange(offset, 1)];
value += (tempVal << (8 * offset));
offset++;
}//while
return value;
}
+ (int64_t)valueFromBytes:(NSData *)data reverse:(BOOL)reverse
{
NSData *tempData = data;
if (reverse) {
tempData = [self dataWithReverse:tempData];
}
return [self valueFromBytes:tempData];
}
/** 将字符串转换为data。例如:返回8个字节的data, upano --> <7570616e 6f000000> */
+ (NSData *)dataFromNormalString:(NSString *)normalString byteCount:(int)byteCount
{
NSAssert(byteCount > 0, @"byteCount <= 0");
char normalChar[byteCount];
memset(normalChar, 0, byteCount);
memcpy(normalChar, [normalString UTF8String], MIN(normalString.length, byteCount));
return [[NSData alloc] initWithBytes:normalChar length:byteCount];
}
+ (NSData *)dataFromHexString:(NSString *)hexString
{
NSAssert((hexString.length > 0) && (hexString.length % 2 == 0), @"hexString.length mod 2 != 0");
NSMutableData *data = [[NSMutableData alloc] init];
for (NSUInteger i=0; i<hexString.length; i+=2) {
NSRange tempRange = NSMakeRange(i, 2);
NSString *tempStr = [hexString substringWithRange:tempRange];
NSScanner *scanner = [NSScanner scannerWithString:tempStr];
unsigned int tempIntValue;
[scanner scanHexInt:&tempIntValue];
[data appendBytes:&tempIntValue length:1];
}
return data;
}
+ (NSString *)hexStringFromData:(NSData *)data
{
NSAssert(data.length > 0, @"data.length <= 0");
NSMutableString *hexString = [[NSMutableString alloc] init];
const Byte *bytes = data.bytes;
for (NSUInteger i=0; i<data.length; i++) {
Byte value = bytes[i];
Byte high = (value & 0xf0) >> 4;
Byte low = value & 0xf;
[hexString appendFormat:@"%x%x", high, low];
}//for
return hexString;
}
+ (NSString *)asciiStringFromHexString:(NSString *)hexString
{
NSMutableString *asciiString = [[NSMutableString alloc] init];
const char *bytes = [hexString UTF8String];
for (NSUInteger i=0; i<hexString.length; i++) {
[asciiString appendFormat:@"%0.2X", bytes[i]];
}
return asciiString;
}
+ (NSString *)hexStringFromASCIIString:(NSString *)asciiString
{
NSMutableString *hexString = [[NSMutableString alloc] init];
const char *asciiChars = [asciiString UTF8String];
for (NSUInteger i=0; i<asciiString.length; i+=2) {
char hexChar = '\0';
//high
if (asciiChars[i] >= '0' && asciiChars[i] <= '9') {
hexChar = (asciiChars[i] - '0') << 4;
} else if (asciiChars[i] >= 'a' && asciiChars[i] <= 'z') {
hexChar = (asciiChars[i] - 'a' + 10) << 4;
} else if (asciiChars[i] >= 'A' && asciiChars[i] <= 'Z') {
hexChar = (asciiChars[i] - 'A' + 10) << 4;
}//if
//low
if (asciiChars[i+1] >= '0' && asciiChars[i+1] <= '9') {
hexChar += asciiChars[i+1] - '0';
} else if (asciiChars[i+1] >= 'a' && asciiChars[i+1] <= 'z') {
hexChar += asciiChars[i+1] - 'a' + 10;
} else if (asciiChars[i+1] >= 'A' && asciiChars[i+1] <= 'Z') {
hexChar += asciiChars[i+1] - 'A' + 10;
}//if
[hexString appendFormat:@"%c", hexChar];
}
return hexString;
}
// Return line separators.
+ (NSData *)CRLFData
{
return [NSData dataWithBytes:"\x0D\x0A" length:2];
}
+ (NSData *)CRData
{
return [NSData dataWithBytes:"\x0D" length:1];
}
+ (NSData *)LFData
{
return [NSData dataWithBytes:"\x0A" length:1];
}
+ (NSData *)ZeroData
{
return [NSData dataWithBytes:"" length:1];
}
@end