iOS圖像處理之Core Graphics和OpenGL ES小析

iOS支持兩套圖形API族:Core Graphics/QuartZ 2D 和OpenGL ES。OpenGL ES是跨平臺的圖形API,屬于OpenGL的一個簡化版本。QuartZ 2D是蘋果公司開發的一套API,它是Core Graphics Framework的一部分,是一套基于C的API框架,使用了Quartz作為繪圖引擎。它提供了低級別、輕量級、高保真度的2D渲染。該框架可以用于基于路徑的繪圖、變換、顏色管理、脫屏渲染,模板、漸變、遮蔽、圖像數據管理、圖像的創建、遮罩以及PDF文檔的創建、顯示和分析。

OpenGL ES是應用程序編程接口,該接口描述了方法、結構、函數應具有的行為以及應該如何被使用的語義。也就是說它只定義了一套規范,具體的實現由設備制造商根據規范去做。因為制造商可以自由的實現Open GL ES,所以不同系統實現的OpenGL ES也存在著巨大的性能差異。

Core Graphics API所有的操作都在一個上下文中進行。所以在繪圖之前需要獲取該上下文并傳入執行渲染的函數中。如果你正在渲染一副在內存中的圖片,此時就需要傳入圖片所屬的上下文。獲得一個圖形上下文是我們完成繪圖任務的第一步,你可以將圖形上下文理解為一塊畫布。如果你沒有得到這塊畫布,那么你就無法完成任何繪圖操作。

UIKit和Core Graphics

UIImage、NSString(繪制文本)、UIBezierPath(繪制形狀)、UIColor知道如何繪制自己。這些類提供了功能有限但使用方便的方法來讓我們完成繪圖任務。使用UiKit,你只能在當前上下文中繪圖,所以如果你當前處于UIGraphicsBeginImageContextWithOptions函數或drawRect:方法中,你就可以直接使用UIKit提供的方法進行繪圖。如果你持有一個context:參數,那么使用UIKit提供的方法之前,必須將該上下文參數轉化為當前上下文。調用UIGraphicsPushContext 函數可以方便的將context:參數轉化為當前上下文,最后調用UIGraphicsPopContext函數恢復上下文環境。Core Graphics是一個繪圖專用的API族,它經常被稱為QuartZ或QuartZ 2D。Core Graphics是iOS上所有繪圖功能的基石,包括UIKit。

Core Graphics的圖形上下文

使用Core Graphics之前需要指定一個用于繪圖的圖形上下文(CGContextRef),這個圖形上下文會在每個繪圖函數中都會被用到。如果你持有一個圖形上下文context:參數,那么你等同于有了一個圖形上下文,這個上下文也許就是你需要用來繪圖的那個。三種獲得圖形上下文的方法(drawRect:、drawRect: inContext:、UIGraphicsBeginImageContextWithOptions)。

第一種方法就是創建一個圖片類型的上下文。調用UIGraphicsBeginImageContextWithOptions函數就可獲得用來處理圖片的圖形上下文。利用該上下文,你就可以在其上進行繪圖,并生成圖片。調用UIGraphicsGetImageFromCurrentImageContext函數可從當前上下文中獲取一個UIImage對象。記住在你所有的繪圖操作后別忘了調用UIGraphicsEndImageContext函數關閉圖形上下文。

第二種方法是利用cocoa為你生成的圖形上下文。當你子類化了一個UIView并實現了自己的drawRect:方法后,一旦drawRect:方法被調用,Cocoa就會為你創建一個圖形上下文,此時你對圖形上下文的所有繪圖操作都會顯示在UIView上。如果當前處于UIGraphicsBeginImageContextWithOptions函數或drawRect:方法中,并沒有引用一個上下文。為了使用Core Graphics,你可以調用UIGraphicsGetCurrentContext函數獲得當前的圖形上下文。

判斷一個上下文是否為當前圖形上下文需要注意的幾點:
1.UIGraphicsBeginImageContextWithOptions函數不僅僅是創建了一個適用于圖形操作的上下文,并且該上下文也屬于當前上下文。
2.當drawRect方法被調用時,UIView的繪圖上下文屬于當前圖形上下文。
3.回調方法所持有的context:參數并不會讓任何上下文成為當前圖形上下文。此參數僅僅是對一個圖形上下文的引用罷了。

UIKit繪圖

第一種繪圖形式:
在UIView的子類方法drawRect:中繪制一個藍色圓,使用UIKit在Cocoa為我們提供的當前上下文中完成繪圖任務。

- (void) drawRect: (CGRect) rect { 

UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; 

[[UIColor blueColor] setFill]; 

[p fill]; 

} 

第二種繪圖形式:
我將在UIView子類的drawLayer:inContext:方法中實現繪圖任務。drawLayer:inContext:方法是一個繪制圖層內容的代理方法。為了能夠調用drawLayer:inContext:方法,我們需要設定圖層的代理對象。但要注意,不應該將UIView對象設置為顯示層的委托對象,這是因為UIView對象已經是隱式層的代理對象,再將它設置為另一個層的委托對象就會出問題。輕量級的做法是:編寫負責繪圖形的代理類。在MyView.h文件中聲明如下代碼:

@interface MyLayerDelegate : NSObject 

@end 

然后MyView.m文件中實現接口代碼:

@implementation MyLayerDelegate 

- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx { 

UIGraphicsPushContext(ctx); 

UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; 

  [[UIColor blueColor] setFill]; 

[p fill]; 

UIGraphicsPopContext(); 

} 

@end 

直接將代理類的實現代碼放在MyView.m文件的#import代碼的下面,這樣感覺好像在使用私有類完成繪圖任務(雖然這不是私有類)。需要注意的是,我們所引用的上下文并不是當前上下文,所以為了能夠使用UIKit,我們需要將引用的上下文轉變成當前上下文。

因為圖層的代理是assign內存管理策略,那么這里就不能以局部變量的形式創建MyLayerDelegate實例對象賦值給圖層代理。這里選擇在MyView.m中增加一個實例變量,因為實例變量默認是strong:

@interface MyView () { 

MyLayerDelegate* _layerDeleagete; 

} 

@end 

使用該圖層代理:

MyView *myView = [[MyView alloc] initWithFrame: CGRectMake(0, 0, 320, 480)]; 

CALayer *myLayer = [CALayer layer]; 

_layerDelegate = [[MyLayerDelegate alloc] init]; 

myLayer.delegate = _layerDelegate; 

[myView.layer addSublayer:myLayer]; 

[myView setNeedsDisplay]; // 調用此方法,drawLayer: inContext:方法才會被調用。 

第三種繪圖形式:
使用UIKit實現:

UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0);     

UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)]; 

[[UIColor blueColor] setFill]; 

[p fill]; 

UIImage* im = UIGraphicsGetImageFromCurrentImageContext(); 

UIGraphicsEndImageContext(); 

UIGraphicsBeginImageContextWithOptions從上下文中生成一個UIImage對象。生成UIImage對象的代碼并不需要等待某些方法被調用后或在UIView的子類中才能去做。

UIGraphicsBeginImageContextWithOptions函數參數的含義:第一個參數表示所要創建的圖片的尺寸;第二個參數用來指定所生成圖片的背景是否為不透明,如上我們使用YES而不是NO,則我們得到的圖片背景將會是黑色,顯然這不是我想要的;第三個參數指定生成圖片的縮放因子,這個縮放因子與UIImage的scale屬性所指的含義是一致的。傳入0則表示讓圖片的縮放因子根據屏幕的分辨率而變化,所以我們得到的圖片不管是在單分辨率還是視網膜屏上看起來都會很好。

Core Graphics繪圖

第一種繪圖形式:
使用Core Graphics實現繪制藍色圓。

- (void) drawRect: (CGRect) rect { 

CGContextRef con = UIGraphicsGetCurrentContext(); 

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); 

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor); 

CGContextFillPath(con); 

} 

第二種繪圖形式:
使用Core Graphics在drawLayer:inContext:方法中實現同樣操作,代碼如下:

- (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con { 

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); 

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor); 

CGContextFillPath(con); 

} 

第三種繪圖形式:
使用Core Graphics實現:

UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0); 

CGContextRef con = UIGraphicsGetCurrentContext(); 

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100)); 

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor); 

CGContextFillPath(con); 

UIImage* im = UIGraphicsGetImageFromCurrentImageContext(); 

UIGraphicsEndImageContext(); 

UIKit和Core Graphics可以在相同的圖形上下文中混合使用。在iOS 4.0之前,使用UIKit和UIGraphicsGetCurrentContext被認為是線程不安全的。而在iOS4.0以后蘋果讓繪圖操作在第二個線程中執行解決了此問題。

OpenGL ES繪圖

圖元是構成復雜物體的基本繪圖要素。在OpenGL ES中,你可以使用的圖元有點,線,三角形。當我們繪制一個三角形的時候,我們需要告訴OpenGL在3d空間中的三角形的3系坐標,OpenGL將非常順利的渲染這個三角形。

.h文件
#import <GLKit/GLKit.h>

@interface OpenGLES_Ch2_1ViewController : GLKViewController
{
GLuint vertexBufferID;
}

@property (strong, nonatomic) GLKBaseEffect *baseEffect;

@end

.m文件

#import "OpenGLES_Ch2_1ViewController.h"

@implementation OpenGLES_Ch2_1ViewController

@synthesize baseEffect;

/////////////////////////////////////////////////////////////////
// This data type is used to store information for each vertex
typedef struct {
GLKVector3  positionCoords;
}
SceneVertex;

/////////////////////////////////////////////////////////////////
// Define vertex data for a triangle to use in example
static const SceneVertex vertices[] = 
{
{{-0.5f, -0.5f, 0.0}}, // lower left corner
{{ 0.5f, -0.5f, 0.0}}, // lower right corner
{{-0.5f,  0.5f, 0.0}}  // upper left corner
};

/////////////////////////////////////////////////////////////////
// Called when the view controller's view is loaded
// Perform initialization before the view is asked to draw
- (void)viewDidLoad
{
[super viewDidLoad];

// Verify the type of view created automatically by the
// Interface Builder storyboard
GLKView *view = (GLKView *)self.view;
NSAssert([view isKindOfClass:[GLKView class]],
  @"View controller's view is not a GLKView");

// Create an OpenGL ES 2.0 context and provide it to the
// view
view.context = [[EAGLContext alloc] 
  initWithAPI:kEAGLRenderingAPIOpenGLES2];

// Make the new context current
[EAGLContext setCurrentContext:view.context];

// Create a base effect that provides standard OpenGL ES 2.0
// Shading Language programs and set constants to be used for 
// all subsequent rendering
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
self.baseEffect.constantColor = GLKVector4Make(
  1.0f, // Red
  1.0f, // Green
  1.0f, // Blue
  1.0f);// Alpha

// Set the background color stored in the current context 
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // background color

// Generate, bind, and initialize contents of a buffer to be 
// stored in GPU memory
glGenBuffers(1,                // STEP 1
  &vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER,  // STEP 2
  vertexBufferID); 
glBufferData(                  // STEP 3
  GL_ARRAY_BUFFER,  // Initialize buffer contents
  sizeof(vertices), // Number of bytes to copy
  vertices,         // Address of bytes to copy
  GL_STATIC_DRAW);  // Hint: cache in GPU memory
}


/////////////////////////////////////////////////////////////////
// GLKView delegate method: Called by the view controller's view
// whenever Cocoa Touch asks the view controller's view to
// draw itself. (In this case, render into a frame buffer that
// shares memory with a Core Animation Layer)
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
[self.baseEffect prepareToDraw];

// Clear Frame Buffer (erase previous drawing)
glClear(GL_COLOR_BUFFER_BIT);

// Enable use of positions from bound vertex buffer
glEnableVertexAttribArray(      // STEP 4
  GLKVertexAttribPosition);
  
glVertexAttribPointer(          // STEP 5
  GLKVertexAttribPosition, 
  3,                   // three components per vertex
  GL_FLOAT,            // data is floating point
  GL_FALSE,            // no fixed point scaling
  sizeof(SceneVertex), // no gaps in data
  NULL);               // NULL tells GPU to start at 
                       // beginning of bound buffer
                               
// Draw triangles using the first three vertices in the 
// currently bound vertex buffer
glDrawArrays(GL_TRIANGLES,      // STEP 6
  0,  // Start with first vertex in currently bound buffer
  3); // Use three vertices from currently bound buffer
}


/////////////////////////////////////////////////////////////////
// Called when the view controller's view has been unloaded
// Perform clean-up that is possible when you know the view 
// controller's view won't be asked to draw again soon.
- (void)viewDidUnload
{
[super viewDidUnload];

// Make the view's context current
GLKView *view = (GLKView *)self.view;
[EAGLContext setCurrentContext:view.context];

// Delete buffers that aren't needed when view is unloaded
if (0 != vertexBufferID)
{
  glDeleteBuffers (1,          // STEP 7 
                   &vertexBufferID);  
  vertexBufferID = 0;
}

// Stop using the context created in -viewDidLoad
((GLKView *)self.view).context = nil;
[EAGLContext setCurrentContext:nil];
}

@end
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作為繪圖引擎。它提供了低...
    ShanJiJi閱讀 1,563評論 0 20
  • 原文地址:http://www.cocoachina.com/industry/20140115/7703.htm...
    默默_David閱讀 6,134評論 0 1
  • 如果說徐藤是一個善解人意,溫柔,永遠都帶著微笑讓人感到親和好相處的話且成績好外貌好家世好,是所有女生心中完美...
    涼盡則以閱讀 404評論 0 0
  • 字體樣式 屬性名 含義 舉例font-family 設置字體類型 font-f...
    __豆約翰__閱讀 4,940評論 0 5
  • 幸存偏誤:系統性的高估成功的概率 解決方法:多去看看曾經極有可能成功,但是最后卻失敗的項目。 自身案例:當我有一個...
    孤獨的時候寫點字吧閱讀 216評論 0 1