iOS_24_画画板

最终效果如下:

iOS_24_画画板


一、简单说明
 1、使用一个数组 strokesArr(笔画数组)记录所有笔画,数组中保存的是一个个的笔画字典,一个字典就是一个笔画,笔画字典中有三项:笔画的大小、颜色、pointsArrInOneStroke数组,(保存的是touch  begin时的落笔点和touch move过程中经过的点)
 
 2、绘制的时候,从strokesArr(笔画数组)里取出每一个字典(一个字典就是一个笔画),根据字典中笔画的大小、颜色、笔画所经过的点坐标(pointsArrInOneStroke数组),使用UIBezierPath类完成笔画绘制
 
 二、撤销和回撤
 一个笔画就是一个字典。 
 撤销:
 使用abandonedStrokesArr (被丢弃的笔画数组)保存要撤销的笔画,即所有笔画数组中的最后一划,
 同时将 strokesArr 笔画数组中的最后一个元素删除。
 反之,重做:
 即将abandonedStrokesArr (被丢弃的笔画数组)中最后一个元素添加到所有笔画数组中,同时将(被丢弃的笔画数组)中的最后一个元素删除。


Main.storyboard

iOS_24_画画板




主控制器

iOS_24_画画板


Canvas类封装了画画的所有核心代码

iOS_24_画画板

方法列表

iOS_24_画画板

//
//  Canvas.h
//  24_Canvas画画板
//
//  Created by beyond on 14-8-26.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
/*
 一、简单说明
 1、使用一个数组 strokesArr(笔画数组)记录所有笔画,数组中保存的是一个个的笔画字典,一个字典就是一个笔画,笔画字典中有三项:笔画的大小、颜色、pointsArrInOneStroke数组,(保存的是touch  begin时的落笔点和touch move过程中经过的点)
 
 2、绘制的时候,从strokesArr(笔画数组)里取出每一个字典(一个字典就是一个笔画),根据字典中笔画的大小、颜色、笔画所经过的点坐标(pointsArrInOneStroke数组),使用UIBezierPath类完成笔画绘制
 
 二、撤销和回撤
 一个笔画就是一个字典。 
 撤销:
 使用abandonedStrokesArr (被丢弃的笔画数组)保存要撤销的笔画,即所有笔画数组中的最后一划,
 同时将 strokesArr 笔画数组中的最后一个元素删除。
 反之,重做:
 即将abandonedStrokesArr (被丢弃的笔画数组)中最后一个元素添加到所有笔画数组中,同时将(被丢弃的笔画数组)中的最后一个元素删除。
 */

#import <UIKit/UIKit.h>
// 自定义的颜色选择控制器,点击之后,它会告诉代理,选中了什么颜色
@class ColorPickerController;

@interface Canvas : UIView
#pragma mark - 属性列表 
// 标签,显示笔刷大小
@property (nonatomic,retain) IBOutlet UILabel *labelSize;
// 滑块 笔刷大小
@property (nonatomic,retain) IBOutlet UISlider *sliderSize;
// 三个按钮,分别是撤销、重做、清除
@property (nonatomic,retain) IBOutlet UIBarButtonItem *undoBtn;
@property (nonatomic,retain) IBOutlet UIBarButtonItem *redoBtn;
@property (nonatomic,retain) IBOutlet UIBarButtonItem *clearBtn;
// toolBar,目的是截图的时候,隐藏掉toolBar
@property (nonatomic,retain) IBOutlet UIToolbar *toolBar;

#pragma mark - 方法列表
// 初始化所有的准备工作
-(void) viewJustLoaded;
// 选择相册 被点击
-(IBAction) didClickChoosePhoto;
// 滑块滑动,设置笔刷大小
-(IBAction) setBrushSize:(UISlider*)sender;
// 撤销 被点击
-(IBAction) undo;
// 重做 被点击
-(IBAction) redo;
// 清除画布 被点击
-(IBAction) clearCanvas;
// 保存图片 被点击
-(IBAction) savePic;
// 颜色选择 被点击
- (IBAction) didClickColorButton;
// 重要~~开放给另一个控制器调用,它在调用代理时,会传入参数:即选择好的颜色
- (void) pickedColor:(UIColor*)color;
@end


核心代码

//
//  Canvas.h
//  24_Canvas画画板
//
//  Created by beyond on 14-8-26.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
/*
 
  这儿仅仅是做演示demo,直接让Canvas与控制器绑定,开始画画,监听事件
 
  如果,要更好的抽取出来,则需要创建一个模型类(model)来提供数据源(比如_strokesArr,_abandonedStrokesArr),供CanvasView显示
 
  UIView的setNeedsDisplay和setNeedsLayout方法
  首先两个方法都是异步执行的。而setNeedsDisplay会调用自动调用drawRect方法,这样可以拿到  UIGraphicsGetCurrentContext,就可以画画了。
 
  UIUserInterfaceIdiomPad   iPad上专用
 */



#import "Canvas.h"
#import "ColorPickerController.h"
#import "BeyondViewController.h"

@interface Canvas ()<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
{
    // 所有笔画
	NSMutableArray *_strokesArr;
    // 丢弃(撤销)的笔画
	NSMutableArray *_abandonedStrokesArr;
    // 当前笔刷颜色
	UIColor *_currentColor;
    // 当前的笔刷大小
	float currentSize;
    // 选中的图片
	UIImage *_pickedImg;
    // 截屏图片
	UIImage *_screenImg;
    // 自定义的 颜色选择控制器
	ColorPickerController *_colorPickerCtrl;
    // 相片选择器
	UIImagePickerController *_imagePickerCtrl;
}

@end

@implementation Canvas
#pragma mark - 生命周期方法
// 禁止多点触摸
-(BOOL)isMultipleTouchEnabled {
	return NO;
}
// 最重要的画图方法
- (void) drawRect: (CGRect) rect
{
    // 1.先把获取的图片,画到画布上
    [self drawPickedImgToCanvas];
	
    // 2.如果【笔画数组】有笔画字典,则按顺序将笔画取出,画到画布上
    [self drawStrokesArrToCanvas];
    
}

// 1.先把获取的图片,画到画布上
- (void)drawPickedImgToCanvas
{
	int width = _pickedImg.size.width;
	int height = _pickedImg.size.height;
	CGRect rectForImage = CGRectMake(0, 0, width, height);
	[_pickedImg drawInRect:rectForImage];
}
// 2.如果【笔画数组】有笔画字典,则按顺序将笔画取出,画到画布上
- (void)drawStrokesArrToCanvas
{
    // 如果【笔画数组】为空,则直接返回
	if (_strokesArr.count == 0) return;
	
    // 遍历【笔画数组】,取出每一个笔画字典,每一次迭代,画一个stroke
    for (NSDictionary *oneStrokeDict in _strokesArr)
    {
        // 取出点数组
        NSArray *pointsArr = [oneStrokeDict objectForKey:@"points"];
        // 取出颜色
        UIColor *color = [oneStrokeDict objectForKey:@"color"];
        // 取出笔刷尺寸
        float size = [[oneStrokeDict objectForKey:@"size"] floatValue];
        // 设置颜色
        [color set];
        // line segments within a single stroke (path) has the same color and line width
        // 画一个stroke, 一条接着一条,使用圆接头 round joint
        // 创建一个贝塞尔路径
        UIBezierPath* bezierPath = [UIBezierPath bezierPath];
        // 点数组 中的第一个,就是 起点
        CGPoint startPoint = CGPointFromString([pointsArr objectAtIndex:0]);
        // 将路径移动到 起点
        [bezierPath moveToPoint:startPoint];
        // 遍历点数组,将每一个点,依次添加到 bezierPath
        for (int i = 0; i < (pointsArr.count - 1); i++)
        {
            // 依次取出下一个点
            CGPoint pointNext = CGPointFromString([pointsArr objectAtIndex:i+1]);
            // 添加到路径
            [bezierPath addLineToPoint:pointNext];
        }
        // 设置线宽
        bezierPath.lineWidth = size;
        // 线连接处为 圆结头
        bezierPath.lineJoinStyle = kCGLineJoinRound;
        // 线两端为 圆角
        bezierPath.lineCapStyle = kCGLineCapRound;
        // 调用路径的方法 画出一条线
        [bezierPath stroke];
    }
}
// 重要~~~初始化所有东东
-(void) viewJustLoaded {
    // 1.初始化颜色选择控制器
    [self addColorPickerCtrl];
	
	// 2.初始化【相片选择器】
    [self addUIImagePickerCtrl];

    // 3.其他成员初始化
    // 【笔画数组】
	_strokesArr = [NSMutableArray array];
    // 【被丢弃的笔画数组】
	_abandonedStrokesArr = [NSMutableArray array];
    // 笔画大小
	currentSize = 5.0;
    // toolBar上笔画标签显示文字
	self.labelSize.text = @"Size: 5";
    // 设置笔刷 黑色
	[self setStrokeColor:[UIColor blackColor]];
    
    
    // 4.设置重做、撤销、清空三个按钮的状态
    [self updateToolBarBtnStatus];
}

// 1.初始化颜色选择控制器
- (void)addColorPickerCtrl
{
    // 1.添加【颜色选择控制器】ColorPickerController,因为要添加到主控制器中
    BeyondViewController *mainVC = [BeyondViewController sharedBeyondViewController];
	// 初始化自己封装的颜色选择控制器,并设置好代理,目的是颜色设置好了之后,回调告诉当前的canvas画布
	_colorPickerCtrl = [[ColorPickerController alloc] init];
	_colorPickerCtrl.pickedColorDelegate = self;
    // 控制器成为父子关系,视图也成为父子关系
    [mainVC addChildViewController:_colorPickerCtrl];
    [mainVC.view addSubview:_colorPickerCtrl.view];
    // 暂时隐藏【颜色选择控制器】,只有在点击了ToolBar上面的按钮时候,才显示出来
    _colorPickerCtrl.view.hidden = YES;
}
// 2.初始化【相片选择器】
- (void)addUIImagePickerCtrl
{
	if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
		_imagePickerCtrl = [[UIImagePickerController alloc] init];
		_imagePickerCtrl.delegate = self;
		_imagePickerCtrl.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        // 2) 设置允许修改
        // [_imagePickerCtrl setAllowsEditing:YES];
	}
}
// 3.自定义方法,设置 撤销、重做、清空三个按钮的可点击状态
- (void)updateToolBarBtnStatus
{

    _redoBtn.enabled = _abandonedStrokesArr.count > 0;
    _undoBtn.enabled = _strokesArr.count > 0;
    _clearBtn.enabled = _strokesArr.count > 0;
    
}
#pragma mark - 控件连线方法
// 滑块滑动
- (IBAction)setBrushSize:(UISlider*)sender
{
	currentSize = sender.value;
	self.labelSize.text = [NSString stringWithFormat:@"Size: %.0f",sender.value];
}
// 撤销按钮点击事件
-(IBAction) undo {
    // 如果笔画数组中有笔画字典
	if ([_strokesArr count]>0) {
        // 最后一个笔画字典,即,被丢弃的笔画字典
		NSMutableDictionary* abandonedStrokeDict = [_strokesArr lastObject];
        // 将最后一个笔画字典,添加到被丢弃的笔画字典数组里面保存,以供drawRect
		[_abandonedStrokesArr addObject:abandonedStrokeDict];
        // 从所有笔画数组中移除掉最后一笔
		[_strokesArr removeLastObject];
        // 重新调用drawRect进行绘制
		[self setNeedsDisplay];
	}
    
    
    
    // 2.设置重做、撤销、清空三个按钮的状态
    [self updateToolBarBtnStatus];
}
// 重做
-(IBAction) redo {
    // 如果 被丢弃的笔画数组,里面有值
	if ([_abandonedStrokesArr count]>0) {
        // 取出最后一个被仍进来的 笔画字典,(即最先书写的,而且是在撤销的操作里面,最后被添加到【被丢弃的笔画数组】)
		NSMutableDictionary* redoStrokeDict = [_abandonedStrokesArr lastObject];
        // 将需要重画的笔画字典,添加到【所有笔画数组】中
		[_strokesArr addObject:redoStrokeDict];
        // 并且,从【被丢弃的笔画数组】中移除,该笔画字典
		[_abandonedStrokesArr removeLastObject];
        // 重新调用drawRect进行绘制
		[self setNeedsDisplay];
	}
    
    
    
    // 2.设置重做、撤销、清空三个按钮的状态
    [self updateToolBarBtnStatus];
}
// 清空画布,只需清空【所有笔画数组】和【被丢弃的笔画数组】
-(IBAction) clearCanvas {
    // 建议不要将选择出来的背景图片清空,只清空没写好的笔画算了
	// _pickedImg = nil;
	[_strokesArr removeAllObjects];
	[_abandonedStrokesArr removeAllObjects];
    // 重新调用drawRect进行绘制
	[self setNeedsDisplay];
    
    
    
    // 2.设置重做、撤销、清空三个按钮的状态
    [self updateToolBarBtnStatus];
}
// 保存图片
-(IBAction) savePic {
	// 暂时移除 工具条
	//[_toolBar removeFromSuperview];
	
    // 截图代码
    // 1,开启上下文
	UIGraphicsBeginImageContext(self.bounds.size);
    // 2.将图层渲染到上下文
	[self.layer renderInContext:UIGraphicsGetCurrentContext()];
    // 开启上下文,使用参数之后,截出来的是原图(YES  0.0 质量高)
    //UIGraphicsBeginImageContextWithOptions(self.frame.size, YES, 0.0);
    // 3.从上下文中取出图片
	_screenImg = UIGraphicsGetImageFromCurrentImageContext();
    // 4.关闭上下文
	UIGraphicsEndImageContext();
	
	// 重新添加 工具条,并置最上方
	//[self addSubview:_toolBar];
	//[self bringSubviewToFront:self.labelSize];
	
	// 调用自定义方法,保存截屏到相册
	[self performSelector:@selector(saveToPhoto) withObject:nil afterDelay:0.0];
}
// 自定义方法,保存截屏到相册
-(void) saveToPhoto {
	// 一句话,写到相册
	UIImageWriteToSavedPhotosAlbum(_screenImg, nil, nil, nil);
	
    
	
	// UIAlertView 提示成功
	UIAlertView* alertView= [[UIAlertView alloc] initWithTitle:nil message:@"Image Saved" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
	[alertView show];
    
}


// 点击选择颜色按钮
- (IBAction) didClickColorButton {
    // 显示或隐藏 自己的【颜色选择控制器】
    _colorPickerCtrl.view.hidden = !_colorPickerCtrl.view.hidden;
    
}
// 当_colorPickerCtrl选择颜色完毕,会调用代理 的本方法
- (void) pickedColor:(UIColor*)color {
    // 将【颜色选择控制器】,回调的颜色,设置到控件上,并隐藏 【颜色选择控制器】
	[self setStrokeColor:color];
    _colorPickerCtrl.view.hidden = !_colorPickerCtrl.view.hidden;

}
// 重要,设置笔刷 新的颜色
-(void) setStrokeColor:(UIColor*)newColor
{
	_currentColor = newColor;
}

// 点击,选择相片按钮
-(IBAction) didClickChoosePhoto {
    // 展现,相片选择控制器
    [self addSubview:_imagePickerCtrl.view];
}
#pragma mark - imagePicker代理方法
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    // 必须手动,关闭照片选择器
    [picker.view removeFromSuperview];
    // 从info字典得到编辑后的照片【UIImagePickerControllerEditedImage】
    _pickedImg = [info valueForKey:@"UIImagePickerControllerOriginalImage"];
    // 将图片画到画板上去
    [self setNeedsDisplay];
}

// 【相片选择器】的代理方法,点击取消时,也要隐藏相片选择器
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [_imagePickerCtrl.view removeFromSuperview];
}



#pragma mark - 核心代码,重要~~~画布上手势处理
// 手势开始(画笔落下)
// 开始一个新的字典,为每一笔,包括点 和 颜色
// Start new dictionary for each touch, with points and color
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
{
	
    // 一个笔画中的所有点,触摸开始时的【起点】
	NSMutableArray *pointsArrInOneStroke = [NSMutableArray array];
	NSMutableDictionary *strokeDict = [NSMutableDictionary dictionary];
	[strokeDict setObject:pointsArrInOneStroke forKey:@"points"];
    // 笔的颜色
	[strokeDict setObject:_currentColor forKey:@"color"];
    // 笔的大小
	[strokeDict setObject:[NSNumber numberWithFloat:currentSize] forKey:@"size"];
	
    // 落笔点
	CGPoint point = [[touches anyObject] locationInView:self];
	[pointsArrInOneStroke addObject:NSStringFromCGPoint(point)];
	
	[_strokesArr addObject:strokeDict];
}

// 将每一个点添加到 点数组
// Add each point to points array
- (void) touchesMoved:(NSSet *) touches withEvent:(UIEvent *) event
{
    // 移动后的一个点
	CGPoint point = [[touches anyObject] locationInView:self];
    // 前一个点
	CGPoint prevPoint = [[touches anyObject] previousLocationInView:self];
    // 字典中先前的点数组
	NSMutableArray *pointsArrInOneStroke = [[_strokesArr lastObject] objectForKey:@"points"];
    // 在后面追加 新的点
	[pointsArrInOneStroke addObject:NSStringFromCGPoint(point)];
	
	CGRect rectToRedraw = CGRectMake(									 ((prevPoint.x>point.x)?point.x:prevPoint.x)-currentSize,									 ((prevPoint.y>point.y)?point.y:prevPoint.y)-currentSize,									 fabs(point.x-prevPoint.x)+2*currentSize,									 fabs(point.y-prevPoint.y)+2*currentSize									 );
	[self setNeedsDisplayInRect:rectToRedraw];
}

// 手势结束(画笔抬起)
// Send over new trace when the touch ends
- (void) touchesEnded:(NSSet *) touches withEvent:(UIEvent *) event
{
	[_abandonedStrokesArr removeAllObjects];
    
    
    // 2.设置重做、撤销、清空三个按钮的状态
    [self updateToolBarBtnStatus];
    
}

@end



颜色选择控制器

ColorPickerController


iOS_24_画画板

//
//  ColorPickerController.h
//  24_Canvas画画板
//
//  Created by beyond on 14-8-26.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//

#import <UIKit/UIKit.h>


@interface ColorPickerController : UIViewController 

#pragma mark - 属性列表
// xib上的imgView
@property (nonatomic,retain) IBOutlet UIImageView *imgView;
// 代理用weak
@property (weak) id pickedColorDelegate;

#pragma mark - 方法列表
// 核心,根据位图引用 创建基于该位图的上下文对象
- (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef)inImage;
// 核心,根据触摸点,从上下文中取出对应位置像素点的颜色值
- (UIColor*) getPixelColorAtLocation:(CGPoint)point;

@end



核心代码

//
//  ColorPickerController.m
//  24_Canvas画画板
//
//  Created by beyond on 14-8-26.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//

#import "ColorPickerController.h"
#import "Canvas.h"
@implementation ColorPickerController


#pragma mark - 点击结束
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
    
	UITouch* touch = [touches anyObject];
    // tap点击的位置
	CGPoint point = [touch locationInView:self.imgView];
    
    // 1.调用自定义方法,从【点】中取颜色
	UIColor *selectedColor = [self getPixelColorAtLocation:point];
    // 2.告诉代理,解析出来的颜色
	[_pickedColorDelegate pickedColor:selectedColor];
}


// 核心代码:关于下面两个方法更多的详细资料,敬请查阅【iOS Developer Library 】
#pragma mark - 核心代码,将图片写入内存,再依据【点】中取颜色
- (UIColor *) getPixelColorAtLocation:(CGPoint)point
{
	UIColor *color = nil;
    // 得到取色图片的引用
	CGImageRef colorImage = _imgView.image.CGImage;
    
	// Create off screen bitmap context to draw the image into. Format ARGB is 4 bytes for each pixel: Alpa, Red, Green, Blue
    
    // 调用自定义方法:从_imgView里面的image的引用,创建并返回对应的上下文
	CGContextRef contexRef = [self createARGBBitmapContextFromImage:colorImage];
    // 如果创建该图片对应的上下文失败
	if (contexRef == NULL){
        NSLog(@"取色图片--创建对应的上下文失败~");
        return nil;
    }
	// 准备将【取色图片】写入刚才创建出来的上下文
    size_t w = CGImageGetWidth(colorImage);		// problem!
	size_t h = CGImageGetHeight(colorImage);
	CGRect rect = {{0,0},{w,h}}; 
    log_rect(rect)
    // 调试输出rect:--{{0, 0}, {225, 250}}
    
    int bytesPerRow = CGBitmapContextGetBytesPerRow(contexRef);
    log_int(bytesPerRow) //调试输出int:--900
    
    
    
	// Draw the image to the bitmap context. Once we draw, the memory 
	// allocated for the context for rendering will then contain the 
	// raw image data in the specified color space.
    // 将位图写入(渲染)已经分配好的内存区域
	CGContextDrawImage(contexRef, rect, colorImage);
	
    // 得到位图上下文 内存数据块的首地址,用指针记住,作为基地址
	unsigned char* dataPoint = CGBitmapContextGetData (contexRef);
    NSLog(@"----首地址,指针%p",dataPoint);
    // ----首地址,指针0x8b3f000
    
    
    
	if (dataPoint != NULL) {
		//offset 即:根据触摸点的xy,定位到位图内存空间中的一个特定像素
		//4 的意思是每一个像素点,占4个字节
        // w是每一行所有点的总数
        // 根据所在行,所在列,算出在内存块中的偏移地址,然后乘以4,因为每一个点在内存中占四个字节
		int offset = 4*((w*round(point.y))+round(point.x));
        // alpha 为内存基地址+偏移地址
		int alpha =  dataPoint[offset];
        // red 为内存基地址+偏移地址+1   其他类似
		int red = dataPoint[offset+1];
		int green = dataPoint[offset+2];
		int blue = dataPoint[offset+3];
        
		NSLog(@"偏移地址: %i colors: RGBA %i %i %i  %i",offset,red,green,blue,alpha);
        // offset: 150908 colors: RGB A 255 0 254  255
        
        // 根据RGBA 生成颜色对象
		color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)];
	}
	
	// 操作完成后,释放上下文对象
	CGContextRelease(contexRef); 
	// 从内存中释放掉 加载到内存的图像数据
	if (dataPoint) {
        free(dataPoint);
    }
	
	return color;
}
// 自定义方法2:通过_imgView里面的image的引用,创建并返回对应的上下文
- (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef) inImage
{
	
    // 要创建的上下文
	CGContextRef    context = NULL;
    // 色彩空间
	CGColorSpaceRef colorSpace;
    // 位图数据在内存空间的首地址
	void *          bitmapData;
    // 每一行的字节数
	int             bitmapBytesPerRow;
    // 图片总的占的字节数
	int             bitmapByteCount;
    
	
	// 得到图片的宽度和高度,将要使用整个图片,创建上下文
	size_t pixelsWide = CGImageGetWidth(inImage);
	size_t pixelsHigh = CGImageGetHeight(inImage);
	
	// 每一行占多少字节. 本取色图片中的每一个像素点占4个字节;
    // 红 绿 蓝 透明度 各占一个字节(8位  取值范围0~255)
    // 每一行的字节数,因为每一个像素点占4个字节(包含RGBA)(其中一个R就是一个字节,占8位,取值是2的8次方 0~255)
	bitmapBytesPerRow   = (pixelsWide * 4);
    // 图片总的占的字节数
	bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);
	
	// 使用指定的 色彩空间(RGB)
	colorSpace = CGColorSpaceCreateDeviceRGB();
	if (colorSpace == NULL)
	{
		fprintf(stderr, "创建并分配色彩空间 出错\n");
		return NULL;
	}
	
	// This is the destination in memory
	// where any drawing to the bitmap context will be rendered.
    // 为取色图片数据  分配所有的内存空间
    // 所有画到取色图片上下文的操作,都将被渲染到此内存空间
	bitmapData = malloc( bitmapByteCount );
	if (bitmapData == NULL) 
	{
		fprintf (stderr, "内存空间分配失败~");
		CGColorSpaceRelease( colorSpace );
		return NULL;
	}
	
	// 创建位图上下文. 使用 pre-multiplied ARGB, ARGB中的每一个成员都占8个bit位,即一字节,一个像素共占4个字节
	// 无论原取色图片的格式是什么(CMYK或Grayscale),都将通过CGBitmapContextCreate方法,转成指定的ARGB格式
	context = CGBitmapContextCreate (bitmapData,
									 pixelsWide,
									 pixelsHigh,
									 8,      // bits per component
									 bitmapBytesPerRow,
									 colorSpace,
                                    kCGImageAlphaPremultipliedFirst);
	if (context == NULL)
	{
		free (bitmapData);
		fprintf (stderr, "位图上下文创建失败~");
	}
	
	// 在返回上下文之前 必须记得释放 色彩空间
	CGColorSpaceRelease( colorSpace );
	
	return context;
}






@end








iOS_24_画画板

上一篇:文字围绕鼠标旋转并移动(滚轮控制半径)


下一篇:记一次线程池引发的故障 排查下来是三歪的锅