iOS中使用OpenGL 實現增高功能

茉莉兒發表於2017-08-22

功能效果

demo示例
demo示例

功能分析

  • 功能:渲染一張傳入的圖片 -> 手動選擇編輯區域 -> 通過滑塊來編輯區域的增高或者縮短

  • OpenGL原理:

    • 因為OpenGL 只能繪製三角形,所以在處理影象或者圖形的時候我們需要將被處理的物件用三角行來分割轉換為三角形和頂點的組成的物件。

    • OpenGL裡面座標是以左下角為原點X軸向上為正,Y軸向右為正

功能實現

  • 渲染圖片拆分圖片:

    • 拆分方法1:通過圖形看出是一個矩形,而矩形是可以分成兩個三角形和四個頂點,通過此可以用GL渲染出圖片。
      拆分三角形
      拆分三角形

      這樣拆分之後雖然可以正常渲染,但是帶來的問題是我的四個頂點都是死的,也就是四個頂點必須是畫布的四個頂點,改變頂點的座標後只能導致整張畫布的變動,而不是某一個區域的變動,拉伸的話也是整張圖片的拉伸,所以想要實現區域性處理的話這種分割方式不可行。
    • 拆分方法2:將整張圖片先拆分為三個矩形,然後再把每個矩形拆分成兩個三角形,得到6個三角形,8個頂點,如下圖:
      拆分2
      拆分2

      這樣一來就可以保證中間的矩形的高度可以任意變化而上下兩部分的高度不變只改變位置,也就是說我們這個DEMO中所做的任何拉伸操作都是對中間矩形的操作,換而言之就是改變最上面的矩形和最下面的矩形之間的距離來達到對中間區域的拉伸和壓縮的目的。根據拆分的方式我們用頂點的座標建立一個陣列
//頂點陣列
GLfloat vertices[] = {   
    -1.2, -1.2,     //左下
    1.2, -1.2,      //右下
    -1.2, -0.4,     //小矩形左下
    1.2, -0.4,      //小矩形右下
    -1.2,  0.4,     //小矩形左上
    1.2,  0.4,      //小矩形右上
    -1.2,  1.2,     //左上
    1.2,  1.2,      //右上
};

 //填充紋理的陣列
GLfloat texCoords[] = {     
    0, 0,        //左下                  //下標為 0 1
    1, 0,        //右下                  //下標為2 3
    0, 1.0/3.0,  //小矩形左下             //下標為4 5
    1, 1.0/3.0,  //小矩形右下             //下標為6 7
    0, 2.0/3.0,  //小矩形左上角           //下標為8 9
    1, 2.0/3.0,  //小矩形右上角           //下標為10 11
    0, 1,        //左上                  //下標為12 13
    1, 1,        //右上                  //下標為14 15
};複製程式碼
  • 手動選擇區域:通過新增帶有自定義手勢的UIView 來實現拖動修改選擇區域。
*****************CustomPanView程式碼******************
#import <UIKit/UIKit.h>

@protocol CustomPanViewDelegate <NSObject>

/**
 *  開始拖拽
 *
 *  @param customPanView 自身
 *  @param centerY       自身所在的y座標
 */
- (void)beginDragWithCoustomPanView:(UIView *)customPanView centerY:(CGFloat)centerY;

@end

@interface CustomPanView : UIView

@property (nonatomic, assign) id<CustomPanViewDelegate> delegate;

@end複製程式碼
*****************CustomPanViewDelegate程式碼******************

#pragma mark -
#pragma mark 拖拽View的代理方法
-(void)beginDragWithCoustomPanView:(UIView *)customPanView centerY:(CGFloat)centerY {

    // 限制範圍:裁剪區不能大於圖片區域
    if (customPanView.center.y >= imageBottom) {
        customPanView.center = CGPointMake(customPanView.center.x, imageBottom);
    }
    if (customPanView.center.y <= imageTop) {
        customPanView.center = CGPointMake(customPanView.center.x, imageTop);
    }

//    獲取兩條線的座標
    CGFloat topY = _topView.center.y;

    CGFloat bottomY = _bottomView.center.y;

//    根據兩條線的座標重新整理裁剪區域UI
    [_cutLabel setFrame:CGRectMake(0, topY < bottomY ? topY : bottomY, SCREEN_WIDTH, fabs(bottomY - topY))];

//    算出裁剪起始座標和結束座標
    CGFloat fromPoint = topY < bottomY ? (imageBottom - bottomY) / imageHeight : (imageBottom - topY) / imageHeight;

    CGFloat toPoint = topY < bottomY ? (imageBottom - topY) / imageHeight : (imageBottom - bottomY) / imageHeight;

   //將中間的矩形的頂點座標和座標聯絡裁剪區域聯絡起來。
    [self sendFromePoint:fromPoint endPoint:toPoint];



    if (_cutLabel.frame.size.height < 30) {  //隱藏文字
        _cutLabel.text = @"";
    } else {
        _cutLabel.text = @"編輯區域";
    }

    [self.slider setValue:0.0 animated:YES];

    tmpHeight = 0.0f;

}複製程式碼

使用一個Delegate將拖移後的Y座標返回,因為是豎直運動的所以我們只關心Y軸座標。

  • 改變大小:通過將UISliderBar的ValueChange和頂點座標關聯來實現改變頂點座標,之後呼叫GLKView 的display的方法來重新整理UI,將變化的過程展現出來。
- (void)action:(UISlider *)sender {
    //判斷是否是向右滑動
    isRightDirection = sender.value >= judgeDirection ? YES : NO;
    //所改變的高度
    changeHeight = sender.value - tmpHeight;

    //遍歷陣列
    for (int i = 0; i < 16; i ++) {
        //將Y座標篩選出來
        if (i % 2 != 0) {
            //下半部分矩形
            if (i <= 7) {
                //下半部分矩形Y軸做減法減去變化的高度
                vertices[i] = verticesCopy[i] - changeHeight;
                //上半部分矩形
            } else if (i >= 9) {
                //上半部分矩形Y軸做加法加上變化的高度
                vertices[i] = verticesCopy[i] + changeHeight;

            }

        }

    }
    //縮小時候如果編輯區域已經成為一條線了就不能在縮小了
    if (vertices[11] > vertices[7]) {

        [self.glView display];

    }

}複製程式碼
  • 通過glReadPixels來從記憶體中讀取畫素資料,GLubyte -> CGImageRef -> UIimage 然後最相關的儲存或者其他操作。
#pragma mark -
#pragma mark 獲取處理後的圖片
- (UIImage *) createImage {

    int imageY = 0;

    int imgHeight = 0;

    if (isRightDirection) { // 判斷slider滑動方向

        imageY = fabs(imageTop - fabs(changeHeight * perOpengleseCoordinate)) * screenScale;

        imgHeight = fabs(imageHeight + 2 * fabs(changeHeight * perOpengleseCoordinate)) * screenScale;

    } else {

        imageY = fabs(imageTop + fabs(changeHeight * perOpengleseCoordinate)) * screenScale;

        imgHeight = fabs(imageHeight - 2 * fabs(changeHeight * perOpengleseCoordinate)) * screenScale;

    }


    int imageWidth = SCREEN_WIDTH * screenScale;

    int dataLength = imageWidth * imgHeight * 4;

    GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte));

    glPixelStorei(GL_PACK_ALIGNMENT, 4);

    glReadPixels(0, imageY, imageWidth, imgHeight, GL_RGBA, GL_UNSIGNED_BYTE, data);  //從記憶體中讀取畫素
    CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGImageRef iref = CGImageCreate(imageWidth, imgHeight, 8, 32, imageWidth * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,ref, NULL, true, kCGRenderingIntentDefault);

    UIGraphicsBeginImageContext(CGSizeMake(imageWidth, imgHeight));
    CGContextRef cgcontext = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(cgcontext, kCGBlendModeCopy);
    CGContextDrawImage(cgcontext, CGRectMake(0, 0, imageWidth, imgHeight), iref);

    CGImageRef imageMasked = CGBitmapContextCreateImage(cgcontext);
    UIImage * image = [UIImage imageWithCGImage:imageMasked scale:screenScale orientation:UIImageOrientationUp];
    UIGraphicsEndImageContext();

    free(data);
    CFRelease(ref);
    CFRelease(colorspace);
    CGImageRelease(iref);

    return image;

}複製程式碼

我的簡書同步更新哦 www.jianshu.com/u/fd21f41fb…

相關文章