iOS 實現圖片自定義變換

先看效果看
加載了一張image,根據四個頂點任意變換。
 
知識點:1.BitmapContext  2.矩陣變換
 
 
一.什麼是BitmapContext
官方解釋:
The number of components for each pixel in a bitmap graphics context is specified by a color space, defined by a CGColorSpaceRef. The bitmap graphics context specifies whether the bitmap should contain an alpha channel, and how the bitmap is generated.
通俗來說:
首先,咱們根據圖片建立一個BitmapContext,把一張圖片繪製到這個BitmapContext上。這時你能夠把圖片當作是由不少個彩色的點組成的。
圖片局部放大後就造成了一個表格。每一個單元表明一個像素點其中包含了4個元素:alpha(透明度)紅 綠 藍
                    每一個像素點包含的信息
 
1.建立
CGContextRef contexRef = CGBitmapContextCreate(void *data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef space, uint32_t bitmapInfo);
 data: 建立BitmapContext所需的內存空間,由malloc建立
 width: 圖片的寬度
 height: 圖片的高度
 bitsPerComponent: data中的每一個數據所佔的字節數
 bytesPerRow: 圖片每行的位數 = 圖片列數*4(由於每一個點有4個通道)
 space: 顏色區間
 bitmapInfo: bitmap類型,通常選擇PremultipliedFirst(ARGB)
 
2.讀取圖片中的全部像素點數據
unsigned char* needData = malloc(須要多大);
needData = CGBitmapContextGetData(contexRef);
 
3.讀取某個點的內容  
 
  int alpha = needData[4*n];
  int red   = needData[4*n+1];
  int green = needData[4*n+2];
  int blue  = needData[4*n+3];
一個點由4個信息表示
 
4.修改某個點的內容
  newData[4*n    ] = alpha;
  newData[4*n + 1] = red;
  newData[4*n + 2] = green;
  newData[4*n + 3] = blue;
 
5.把data數據重新編程UIImage
CGImageRef cgImage = CGBitmapContextCreateImage(newContext);
_imageView.image = [UIImage imageWithCGImage:cgImage ];
 
有個了幾個函數,接下來就須要你來指定一套規則,獲取到的全部像素點,而後從組元素生成一張新的圖片。 而這套規則就是下面對應的矩陣變換
 
 
二.矩陣變換 注:這個部分原理居多,如有不是能夠直接跳過copy代碼,不影響你功能的實現
1.什麼是矩陣
若用一個行向量[X1 X2 ….Xn]表示n維空間中的一個點的座標,那麼n維空間中m個點座標就能夠表示爲一個向量集合,這個集合就是一個矩陣。

 

也就是說咱們二維圖片中全部的(x,y)座標的全部點組成的一個集合就是一個矩陣.
 
           圖片和其中的某些點
 
2.圖形變換
咱們對圖像的常見操做其實就是對圖形進行評議、旋轉、縮放等矩陣變換,實質上就是改變了各個點的座標。

全部的圖形點由老的x y座標變成了新的x` y`座標編程

 
3.常見的圖形變換
基本的二維變換可包括旋轉、縮放、扭曲,和平移四種,
                       
而這些幾何運算則能夠轉換爲一些基本的矩陣運算:
    
這幾個變換都是線性的,但平移運算不是線性的,不能經過2*2矩陣運算完成。若要將點 (2, 1)在 x 方向將其平移 3 個單位,在 y 方向將其平移 4 個單位。 可經過先使用矩陣乘法再使用矩陣加法來完成此操做。
    
綜合這幾種基本運算,數學家們將其統一爲一個3*3矩陣,存儲形式以下:
    
因爲表示仿射變換的矩陣的第三列老是(0,0,1),在存儲矩陣的時候,大多隻存成一個2*3的數組。
 
 
三.從由4個新頂點組成的圖片中找到對應圖形變換的規律
 
方法1.經過矩陣乘法計算
原始座標和變換好後的四個頂點座標咱們都是已知的,能夠經過已知數據創建一個4元一次方程式,得出a b c d四個位置數而後計算出新的圖形座標。此方式適用於作數學應用題。
 
方法2.經過x y的線性變換找出規律
咱們以前拿到的圖片都是很規規矩矩的,是一個矩形。加入獲得x相對於本身圖形空間的座標的話,這個x的座標就是 (7/11 * 圖片寬度,4/8 *圖片高度)
若是一個圖形變化了,那麼這個j 和i向量就不在是之前相互垂直90度了,而後裏面全部的點座標可能就位移了。
好比我要把x點移動到b點怎麼實現呢?
 
 
咱們能夠把上面的直角座標系想象成一輛公交車,全部的點都坐在公交車裏面,咱們在車裏沒有動,而後公交車到達目的地後,咱們相對於城市而言已經走了很遠很遠了。
就像這樣,x點是否是就移動到b點了。
 
重點:因此計算的思路就是,經過老的直角座標系的規律獲取到每個點的位置,再找出新的座標系的規律,把點移動上去,就完成了第一張圖片所達到的效果了
 
下面開始找規律
假如新的圖片變成了這樣

 

因爲4個頂點已知,那麼這4條邊有多長,每一個邊上面的點的具體座標咱們就能夠確認下來。

 

由上下兩條邊線確認兩個頂點,咱們就能夠確認某一個X座標上面y的線性變化規律。 好比之前圖片位於x方面1/4位置的座標點y方向線性變換在新圖中的線性變換就變成了左圖y’.    X座標固定,這時候咱們只須要傳入之前座標中的y值,就能獲得相對於本身寬度1/4出的新圖中全部的座標點。
同理可得,之前2/4 3/4等等全部頂點方向的y線性變換規律咱們均可以獲得。
而後咱們要作的就是經過循環遍歷出全部向下頂點而後獲得第n個位置處的全部y方向的點,從而得出全部的座標。而後這一組座標的就組成了新的圖片。
 
最後附上關鍵代碼
//數組包含了一次是 左上 右上 右下 左下  4個點位置不一樣,圖片顯示的就不一樣
 1 -(void)changeImageByPoints:(NSArray *)pointArray{
 2     UIImage * image = _image; //全局須要變換的圖片
 3     float width = CGImageGetWidth(image.CGImage);
 4     float height = CGImageGetHeight(image.CGImage);
 5     
 6     CGPoint p0 = [pointArray[0]CGPointValue];
 7     CGPoint p1 = [pointArray[1]CGPointValue];
 8     CGPoint p2 = [pointArray[2]CGPointValue];
 9     CGPoint p3 = [pointArray[3]CGPointValue];
10     
11     //痛覺相對於父視圖的絕對4個頂點計算出新的寬度和高度
12     float minLeft = MIN(MIN(p0.x, p1.x), MIN(p2.x, p3.x));
13     float minTop = MIN(MIN(p0.y, p1.y), MIN(p2.y, p3.y));
14     float shapW = KINT((MAX(MAX(p0.x, p1.x), MAX(p2.x, p3.x)) - minLeft));
15     float shapH = KINT((MAX(MAX(p0.y, p1.y), MAX(p2.y, p3.y)) - minTop));
16     
17     //change point relative to image not superview
18     p0.x = p0.x - minLeft;
19     p1.x = p1.x - minLeft;
20     p2.x = p2.x - minLeft;
21     p3.x = p3.x - minLeft;
22     p0.y = p0.y - minTop;
23     p1.y = p1.y - minTop;
24     p2.y = p2.y - minTop;
25     p3.y = p3.y - minTop;   
26     
27     //建立一個bitmapcontext
28     if (!_first) {
29         needData = malloc(KINT(width)* KINT(height) * 4);
30         CGContextRef imageContext = CGBitmapContextCreate(needData, width, height, 8, width * 4, CGImageGetColorSpace(image.CGImage), CGImageGetAlphaInfo(image.CGImage));
31         CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image.CGImage);
32         data = malloc(KINT(width) * KINT(height) * 4);
33         data = CGBitmapContextGetData(imageContext);
34         _first = YES;
35     }  
36     
37     //初始化新的圖片須要的data
38     unsigned char* shapeData = malloc(shapW * shapH * 4);
39     for (int i = 0; i < shapH -1; i ++) {
40         for (int j = 0; j < shapW -1; j++) {
41             int offset = (i * shapW + j) * 4;
42             shapeData[offset] = 255;
43             shapeData[offset + 1] = 255;
44             shapeData[offset + 2] = 255;
45             shapeData[offset + 3] = 255;
46         }
47     }
48  
49     //給data添加對應的像素值
50     for (int i = 0; i < height -1; i++) {
51         for (int j = 0; j < width -1; j++) {
52             CGPoint originPoint = CGPointMake(j, i);
53             int originOffset = (i * width + j) * 4;
54             // 計算原圖每一個點在新圖中的位置
55             float xFunc = (float)originPoint.x / (float)width;
56             float yFunc = (float)originPoint.y / (float)height;
57                    
58             float delx = (p1.x - p0.x) * xFunc;
59             float dely = (p1.y - p0.y) * xFunc;
60             CGPoint topPoint = CGPointMake(p0.x + delx, p0.y + dely);
61             
62             delx = (p2.x - p3.x) * xFunc;
63             dely = (p2.y - p3.y) * xFunc;
64             CGPoint bottomPoint = CGPointMake(p3.x + delx, p3.y + dely);
65             
66             delx = (bottomPoint.x - topPoint.x) * yFunc;
67             dely = (bottomPoint.y - topPoint.y) * yFunc;
68             
69             CGPoint newPoint = CGPointMake(topPoint.x + delx, topPoint.y + dely);
70             
71             int newOffset = ((KINT(newPoint.y) * shapW + KINT(newPoint.x))) * 4;
72             
73             //give shapeView new value
74             shapeData[newOffset] = data[originOffset];
75             shapeData[newOffset + 1] = data[originOffset + 1];
76             shapeData[newOffset + 2] = data[originOffset + 2];
77             shapeData[newOffset + 3] = data[originOffset + 3];
78             
79         }
80     }
81     //建立新圖片
82     CGContextRef newContext = CGBitmapContextCreate(shapeData, shapW, shapH, 8, shapW * 4, CGImageGetColorSpace(image.CGImage), CGImageGetAlphaInfo(image.CGImage));
83     
84     CGImageRef cgImage = CGBitmapContextCreateImage(newContext);
85     _imageView.image = [UIImage imageWithCGImage:cgImage ];  //這個_imageView就是貼上viewcontroller上面的UIImageview
86     _imageView.frame = CGRectMake(minLeft, minTop, shapW, shapH);
87     CGContextRelease(newContext);
88     CGImageRelease(cgImage);
89     free(shapeData);
90 }
相關文章
相關標籤/搜索