您的位置:首页 > 移动开发 > IOS开发

iOS_24_画画板(含取色板)

2016-01-07 14:57 405 查看
终于效果例如以下:



一、简单说明
1、使用一个数组 strokesArr(笔画数组)记录全部笔画。数组中保存的是一个个的笔画字典,一个字典就是一个笔画。笔画字典中有三项:笔画的大小、颜色、pointsArrInOneStroke数组,(保存的是touch  begin时的落笔点和touch move过程中经过的点)

2、绘制的时候,从strokesArr(笔画数组)里取出每个字典(一个字典就是一个笔画)。依据字典中笔画的大小、颜色、笔画所经过的点坐标(pointsArrInOneStroke数组)。使用UIBezierPath类完毕笔画绘制

二、撤销和回撤
一个笔画就是一个字典。
撤销:
使用abandonedStrokesArr (被丢弃的笔画数组)保存要撤销的笔画,即全部笔画数组中的最后一划,
同一时候将 strokesArr 笔画数组中的最后一个元素删除。
反之,重做:
即将abandonedStrokesArr (被丢弃的笔画数组)中最后一个元素加入到全部笔画数组中,同一时候将(被丢弃的笔画数组)中的最后一个元素删除。


Main.storyboard



主控制器



Canvas类封装了画画的全部核心代码



方法列表



//
//  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;
// 三个button,各自是撤销、重做、清除
@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.设置重做、撤销、清空三个button的状态
[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上面的button时候,才显示出来
_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.自己定义方法,设置 撤销、重做、清空三个button的可点击状态
- (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];
}
// 撤销button点击事件
-(IBAction) undo {
// 假设笔画数组中有笔画字典
if ([_strokesArr count]>0) {
// 最后一个笔画字典,即,被丢弃的笔画字典
NSMutableDictionary* abandonedStrokeDict = [_strokesArr lastObject];
// 将最后一个笔画字典,加入到被丢弃的笔画字典数组里面保存,以供drawRect
[_abandonedStrokesArr addObject:abandonedStrokeDict];
// 从全部笔画数组中移除掉最后一笔
[_strokesArr removeLastObject];
// 又一次调用drawRect进行绘制
[self setNeedsDisplay];
}

// 2.设置重做、撤销、清空三个button的状态
[self updateToolBarBtnStatus];
}
// 重做
-(IBAction) redo {
// 假设 被丢弃的笔画数组,里面有值
if ([_abandonedStrokesArr count]>0) {
// 取出最后一个被仍进来的 笔画字典,(即最先书写的,并且是在撤销的操作里面,最后被加入到【被丢弃的笔画数组】)
NSMutableDictionary* redoStrokeDict = [_abandonedStrokesArr lastObject];
// 将须要重画的笔画字典,加入到【全部笔画数组】中
[_strokesArr addObject:redoStrokeDict];
// 并且,从【被丢弃的笔画数组】中移除,该笔画字典
[_abandonedStrokesArr removeLastObject];
// 又一次调用drawRect进行绘制
[self setNeedsDisplay];
}

// 2.设置重做、撤销、清空三个button的状态
[self updateToolBarBtnStatus];
}
// 清空画布,仅仅需清空【全部笔画数组】和【被丢弃的笔画数组】
-(IBAction) clearCanvas {
// 建议不要将选择出来的背景图片清空,仅仅清空没写好的笔画算了
// _pickedImg = nil;
[_strokesArr removeAllObjects];
[_abandonedStrokesArr removeAllObjects];
// 又一次调用drawRect进行绘制
[self setNeedsDisplay];

// 2.设置重做、撤销、清空三个button的状态
[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];

}

// 点击选择颜色button
- (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;
}

// 点击,选择相片button
-(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.设置重做、撤销、清空三个button的状态
[self updateToolBarBtnStatus];

}

@end


颜色选择控制器

ColorPickerController



//
//  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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: