开篇

计算机图形学(Computer Graphics)是一种运用数学算法将二维或三维图形转换为计算机显示器的栅格形式的科学

本文为学习图形学过程中,运用iOS平台开发语言模拟图画烘托和变化笔记

像素烘托

将像素展现到屏幕上需求借助CoreGraphics的接口,假定需求烘托width * height的图画,需求先分配色彩空间ARGB位图

#define BYTES_PER_RGB 4
typedef struct SDLBitmapContext {
    CGSize size;
    uint32_t *buffer;
    CGContextRef cgContext;
    CGColorSpaceRef colorSpace;
} SDLBitmapContext;
SDLBitmapContext sdlCreateArgbBitmapContext(size_t width, size_t height) {
    SDLBitmapContext context = identityBitmapContext();
    size_t bytesPerRow = width * BYTES_PER_RGB;
    size_t bytesCount = bytesPerRow * height;
    context.size = CGSizeMake(width, height);
    context.colorSpace = CGColorSpaceCreateDeviceRGB();
    if (context.colorSpace == NULL) {
        return identityBitmapContext();
    }
    context.buffer = (uint32_t *)malloc((unsigned long)bytesCount);
    if (context.buffer == NULL) {
        sdlFreeBitmapContext(&context);
        return identityBitmapContext();
    }
    memset(context.buffer, 0x0, bytesCount);
    context.cgContext = CGBitmapContextCreate(context.buffer, width, height, 8, bytesPerRow, context.colorSpace, kCGImageAlphaPremultipliedFirst);
    if (context.cgContext == NULL) {
        sdlFreeBitmapContext(&context);
        return identityBitmapContext();
    }
    return context;
}

在分配完位图后,遍历位图的内存将像素数据写入buffer

enum {
    ALPHA = 0,
    RED = 1,
    GREEN = 2,
    BLUE = 3,
};
void sdlForeachBitmapContext(SDLBitmapContext context, SDLBitmapPixelBody body) {
    uint32_t *imageBuffer = CGBitmapContextGetData(context.cgContext);
    if (imageBuffer == NULL) {
        return;
    }
    for (size_t row = 0; row < context.size.width; row++) {
        for (size_t column = 0; column < context.size.height; column++) {
            uint8_t *pixelPtr = (uint8_t *)imageBuffer;
            body(pixelPtr, row, column);
        }
    }
}
sdlForeachBitmapContext(context, ^(uint8_t *pixelPtr, size_t row, size_t column) {
    RGBAColor color = randomColor();
    pixelPtr[ALPHA] = color.alpha;
    pixelPtr[RED] = color.red;
    pixelPtr[GREEN] = color.green;
    pixelPtr[BLUE] = color.blue;
});

最后从位图上下文取出图片即可展现

UIImage *sdlGenerateImage(SDLBitmapContext context) {
    if (!isBitmapContextValid(context)) {
        return nil;
    }
    CGImageRef imageRef = CGBitmapContextCreateImage(context.cgContext);
    UIImage *image = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    return image;
}
- (void)renderImage:(SDLBitmapContext context) {
    UIImage *image = sdlGenerateImage(context);
    self.renderImageView.image = image;
}

随机像素烘托作用

图形学笔记-渲染

图片烘托

图片的烘托和随机像素烘托的过程相似,相同需求生成width * height巨细尺寸的位图,然后将图片写入到位图中,这次运用一个PixelsStorage模型来存储一切的像素信息

@interface SDLPixelsStorage : NSObject
/// 图片尺寸
@property (nonatomic, readonly) CGSize imageSize;
/// 像素存储buffer
@property (nonatomic, readonly) RGBAColor *pixelColors;
/// 生成指定巨细的像素缓存区
- (void)createImageWithSize:(CGSize)size;
/// 遍历一切像素点
- (void)enumeratePixelsWithVisitor:(SDLVisitor)visitor;
/// 修改坐标点的像素值
- (void)modifyColor:(RGBAColor)color atPoint:(CGPoint)point;
@end
- (SDLPixelsStorage *)readPixelsFromImage:(UIImage *)image {
    SDLBitmapContext context = sdlCreateArgbBitmapContext(image.size.width, image.size.height);
    SDLPixelsStorage *storage = [[SDLPixelsStorage alloc] init];
    [storage createImageWithSize:size];
    sdlDrawImageToBitmap(context, image.CGImage);
    sdlForeachBitmapPixel(context, ^(uint8_t *pixelPtr, size_t row, size_t column) {
        RGBAColor color = (RGBAColor){
            pixelPtr[RED],
            pixelPtr[GREEN],
            pixelPtr[BLUE],
            pixelPtr[ALPHA]
        };
        [storage modifyColor:color atPoint:CGPointMake(row, column)];
    });
    return storage;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    SDLPixelsStorage *storage = [self readPixelsFromImage:[UIImage imageNamed:@"emoji"]];
    [[[SDLRenderCanvas alloc] initWithDisplayView:self.view pixelsStorage:storage] render];
}

烘托作用

图形学笔记-渲染

锯齿问题

因为retina屏幕的特性,在3x机型上单个坐标点共有3 * 3 = 9个像素,要达到最佳的图画烘托需求运用3 * width * 3 * height尺寸巨细的位图,在此不做演示

为什么需求PixelsStorage

在后续文章中会模拟视图的旋转改换,需求对像素的每一个坐标点做矩阵改换得到新的坐标点,抽象出像素存储器的类能够很好的支撑运算

混合图画

像素烘托时遵从一个规则:当像素AB在同一坐标点且满足A.z < B.z时,假定B的透明度为alpha,坐标点的色彩计算公式为:

A * (1 – B.alpha) + B * B.alpha

B.alpha等于1时,A像素永久不会被表达,反之B.alpha不为1时,显示的色彩为像素的叠加。为了实现图画混合的作用,新增加一个PixelsComposite类实现作用

@implementation SDLPixelsComposite
- (SDLPixelsStorage *)compose {
    SDLPixelsStorage *composeStorage = [self normalizedStorage];
    SDLPixelsStorage *currentPixelsStorage = [[SDLPixelsStorage alloc] init];
    [currentPixelsStorage createImageWithSize:composeStorage.imageSize];
    // 遍历一切图画
    for (SDLPixelsComponent *component in self.pixelsComponents) {
        CGFloat widthScale = composeStorage.imageSize.width / component.size.width;
        CGFloat heightScale = composeStorage.imageSize.height / component.size.height;
        [component.pixels enumeratePixelsWithVisitor:^(RGBAColor color, CGPoint point) {
             // 读取当时图画的色彩信息存储到currentPixelsStorage中
        }];
        // 混合图画像素
        CGFloat alpha = component.alpha;
        [composeStorage enumratePixelsWithVisitor:^(RGBAColor color, CGPoint point) {
            // A * (1 - B.alpha)
            RGBAColor ultimateColor = multipleColor(color, 1 - alpha);
            // B * B.alpha
            RGBAColor currentColor = multipleColor([currentPixelsStorage colorAtPoint:point]);
            ultimateColor = addColor(ultimateColor, currentColor);
            [composeStorage modifyColor:ultimateColor atPoint:point];
        }];
    }
    return composeStorage;
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    SDLPixelsComposite *composite = [[SDLPixelsComposite alloc] init];
    [composite appendPixels:[self readPixelsFromImage:@"container"] alpha:1];
    [composite appendPixels:[self readPixelsFromImage:@"emoji"] alpha:0.2];
    [[[SDLRenderCanvas alloc] initWithDisplayView:self.view pixelsStorage:[composite compose] render];
}

烘托作用

图形学笔记-渲染

相关

LearnOpenGL

GAMES 101