2012年8月3日 星期五

Filter4Cam 實作: 26. 選擇濾鏡之一

since: 2012/08/03
update: 2012/08/08


點選/取消 濾鏡

A. 新增: 記錄使用何種濾鏡的變數
      1. 開啟 GlobalDataClass.h 檔案, 修改如下:
#import <Foundation/Foundation.h>

@interface GlobalDataClass : NSObject
{   
    //@add: array for storage filterCells
    NSMutableArray *filterCellArray;
    BOOL isUsingFilter;        // 有使用濾鏡?
    NSMutableString *filterID; // 濾鏡 ID
}

//@add
@property (nonatomic, strong) NSMutableArray *filterCellArray;
@property (assign) BOOL isUsingFilter;
@property (nonatomic, strong) NSMutableString *filterID;

//@add
+ (GlobalDataClass *)getInstance;

@end

*************************************************

      2. 開啟 GlobalDataClass.m 檔案, 修改如下:
#import "GlobalDataClass.h"

@implementation GlobalDataClass

//@add
@synthesize filterCellArray = _filterCellArray;
@synthesize isUsingFilter = _isUsingFilter;
@synthesize filterID = _filterID;

#pragma mark Getter

//@add
- (NSMutableArray *)filterCellArray
{
    if (_filterCellArray == nil) {
        _filterCellArray = [[NSMutableArray alloc] init];
    }
   
    return _filterCellArray;
}

//@add
- (NSMutableString *)filterID
{
    if (_filterID == nil) {
        _filterID = [NSMutableString stringWithString:@"NoID"];
    }
    return _filterID;
}
....

-----------------------------------------------------------------------------

B. 在濾鏡表單的 Cell 新增 filterID 變數
      1. 開啟 FilterCell.h 檔案, 修改如下:
#import <UIKit/UIKit.h>
//@add
#import "ConstantDefined.h"
#import "FilterTitleLabel.h"

//@add
@class FilterTitleLabel;

@interface FilterCell : UITableViewCell
{
    //@add
    // hold the sample picture for our filter
    UIImageView *sampleImage; // 濾鏡效果的圖例
   
    // the title of our filter
    //UILabel *titleLabel;
    //@update
    FilterTitleLabel *titleLabel;
   
    //@add
    NSString *filterID;
}

//@add
@property (nonatomic, strong) UIImageView *sampleImage;
//@property (nonatomic, strong) UILabel *titleLabel;
//@update
@property (nonatomic, strong) FilterTitleLabel *titleLabel;
@property (nonatomic, strong) NSString *filterID;

@end

*************************************************

      2. 開啟 FilterCell.m 檔案, 修改如下:
....
//@add
@synthesize sampleImage = _sampleImage;
@synthesize titleLabel = _titleLabel;
@synthesize filterID = _filterID;

....
//@add
- (void)dealloc
{
    self.sampleImage = nil;
    self.titleLabel = nil;
    self.filterID = nil;
}
....

-----------------------------------------------------------------------------

C. 決定是否要在螢幕上畫出套用濾鏡的影像
     (並調整 GlobalDataClass 類別變數)
     1. 開啟 ViewController.h 檔案, 修改如下:
....
@interface ViewController : GLKViewController <AVCaptureVideoDataOutputSampleBufferDelegate, UITableViewDelegate, UITableViewDataSource>
{
....
    BOOL isUsingFrontCamera; // 目前使用前置鏡頭?
    BOOL isLastFrontCamera; // 上次是前置鏡頭?
    //@update: comment it
    //BOOL applyFilter; // 是否套用濾鏡?  // (update: 2012/08/08)

....
    // 設備上次的擺放方向(目前不記錄:FaceUp, FaceDown, 與 Unknown)
    //
    // Portrait,           // 直擺
    // PortraitUpsideDown, // 直擺, 上下顛倒
    // LandscapeRight,     // 橫擺朝右
    // LandscapeLeft,      // 橫擺朝左
    // FaceUp,             // 面朝上
    // FaceDown,           // 面朝下
    // Unknown             // 未知
    enum kLastOrientation lastOrientation;
   
    GlobalDataClass *dataObj;
}
....
@property (assign) BOOL isUsingFrontCamera;
@property (assign) BOOL isLastFrontCamera;
//@update: comment it
//@property (assign) BOOL applyFilter;
// (update: 2012/08/08)
....
//@add for reusable Cells
@property (nonatomic, strong) NSMutableArray *reusableCells;

@property (nonatomic, strong) GlobalDataClass *dataObj;
....

*************************************************

     2. 開啟 ViewController.m 檔案, 修改如下:
....
// step 1:
....
@synthesize isUsingFrontCamera = _isUsingFrontCamera;
@synthesize isLastFrontCamera = _isLastFrontCamera;
//@update: comment it
//@synthesize applyFilter = _applyFilter;
// (update: 2012/08/08)
....
//@add for reusable Cells

@synthesize reusableCells = _reusableCells;

@synthesize dataObj = _dataObj;

....
// step 2:
#pragma mark Getter
....
//@add
- (GlobalDataClass *)dataObj
{
    return [GlobalDataClass getInstance];
}

// step 3:
....
//@add:filter Test
- (CIImage *)filterTest:(CIImage *)sourceImage
{
    //@update: comment it
    //self.applyFilter = YES; // (update: 2012/08/08)
   
    self.coreImageFilter = [CIFilter filterWithName:@"CISepiaTone" keysAndValues:
                        kCIInputImageKey, sourceImage,
                        @"inputIntensity", [NSNumber numberWithFloat:0.8], nil];
   
    return [self.coreImageFilter outputImage];
}

....
// step 4:
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    //@update: comment it
    //GlobalDataClass *dataObj = [GlobalDataClass getInstance];
    self.dataObj.isUsingFilter = NO;
    //NSLog(@"filterID = %@", dataObj.filterID); // NoID
....
    //@update: comment it
    //self.applyFilter = NO;
// (update: 2012/08/08)
    self.isUsingFrontCamera = NO; // 預設為後置鏡頭
    self.isLastFrontCamera = NO;  // 預設上次為後置鏡頭
    lastOrientation = Unknown; // 設備上次的擺放方向: 未知
....

}

....
// step 5:
- (void)viewDidUnload
{   
    [super viewDidUnload];
   
    //@add
    if ([EAGLContext currentContext] == self.glContext) {
        [EAGLContext setCurrentContext:nil];
    }
    self.glContext = nil;
    self.filterDictionary = nil;
    self.reusableCells = nil;
    self.coreImageContext = nil;
    self.coreImageFilter = nil;
    self.ciImage = nil;
    self.glView = nil;
    self.session = nil;
    self.filterLensImageView = nil;
    self.filterListTableView = nil;
    self.buttonView = nil;
    self.saveButton = nil;
    self.switchButton = nil;
    self.torchButton = nil;
    self.observerButton = nil;
    self.dataObj = nil;
}

....
// step 6:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    //@add
    //@update: comment it
    //GlobalDataClass *dataObj = [GlobalDataClass getInstance];
    CGFloat filterCellRotationAngle; // filterCell Rotation Angle
....
    /*
    if (dataObj.filterCellArray.count > 0) {
        for (FilterCell* filterCell in dataObj.filterCellArray) {
            filterCell.transform = CGAffineTransformMakeRotation(filterCellRotationAngle);
        }
    }
    */
    //@update
    if (self.dataObj.filterCellArray.count > 0)
   {

        for (FilterCell* filterCell in self.dataObj.filterCellArray)
       {

            filterCell.transform = CGAffineTransformMakeRotation(filterCellRotationAngle);
        }
    }

    return YES;
}

....
// step 7:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
   
    // 將取得的 sampleBuffer 轉成 CVPixelBuffer
    CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
       
    //@update: use instance var (ciImage)
    // 接著, 將 CVPixelBuffer 利用 Core Image 的初始化方法再轉成 CIImage.
    self.ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
   
    //@add:轉換原始影像的方向
    self.ciImage = [self orientationTransform:self.ciImage];
   
    // 然後使用 CIContext 物件將其內容畫到 render buffer
    [self.coreImageContext drawImage:self.ciImage atPoint:CGPointZero fromRect:[self.ciImage extent]];
   
    //@update
    if(self.dataObj.isUsingFilter == YES)
    {
        // 濾鏡功能測試
        self.ciImage = [self filterTest:self.ciImage];
   
        // 畫出 "濾鏡範圍" 內的影像(含方向轉變的調整)
        [self drawFilteredImage:self.ciImage];
    }
   
    // 最後, 在螢幕上呈現出來.
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER];
}
....

-----------------------------------------------------------------------------

D. 選取 / 取消選取 filter cell
     (並調整 GlobalDataClass 類別變數, 幫 cell 記錄 filterID)
      1. 開啟 HorizontalTableCell.h 檔案, 修改如下:
....
@interface HorizontalTableCell : UITableViewCell <UITableViewDelegate, UITableViewDataSource>
{
    //@add
    // we will rotate and add as a subview of our cell
    UITableView *filterTableView;
   
    // hold the filters for the category we are in
    NSArray *filters;
   
    //@add: lastIndexPath in tableView:didSelectRowAtIndexPath:
    NSIndexPath *lastIndexPath;
   
    GlobalDataClass *dataObj;
}

//@add
@property (nonatomic, strong) UITableView *filterTableView;
@property (nonatomic, strong) NSArray *filters;
@property (nonatomic, strong) NSIndexPath *lastIndexPath;
@property (nonatomic, strong) GlobalDataClass *dataObj;

@end

*************************************************

      2. 開啟 HorizontalTableCell.m 檔案, 修改如下:
....
// step 1:
@synthesize filterTableView = _filterTableView;
@synthesize filters = _filters;
@synthesize lastIndexPath = _lastIndexPath;
@synthesize dataObj = _dataObj;

....
// step 2:
#pragma mark Getter
//@add:
// 記錄上次選取的資料列:
//
// 問題:
// 一開始尚未選取任何資料列時, lastIndexPath 預設為 nil,
// 第一次點選資料列時, 在 tableView:didSelectRowAtIndexPath: 中呼叫:
// int newRow = [indexPath row];
// int oldRow = [self.lastIndexPath row];
//
// 得到 oldRow 的值為 0, 如果點選的資料是第 1 列, newRow 的值也為 0,
// 會造成: 程式判斷成要 "取消選取濾鏡"
//
// 解決:
// 在初始化 lastIndexPath 時, 將其 indexPathForRow: 的值設為 -1
- (NSIndexPath *)lastIndexPath
{
    if (_lastIndexPath == nil) {
        _lastIndexPath = [NSIndexPath indexPathForRow:-1 inSection:0];
    }
   
    return _lastIndexPath;
}

//@add
- (GlobalDataClass *)dataObj
{
    return [GlobalDataClass getInstance];
}

....
// step 3:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
    //@title text: we don't need to load that, just read it from our array of filters. 
    cell.titleLabel.text = [currentFilter objectForKey:@"Title"];
    cell.filterID = [currentFilter objectForKey:@"FilterID"];
    //NSLog(@"filterID = %@", cell.filterID);
   
    //@add for storage filterCells
    //
    // 當沒有找到相同的 filterID 時, 才將新的 filterCell 存入 Array 裡.
    //
    /*
     備註: 1. 當找到相同的 filterID 時, 不刪除其所屬的 cell.
              因為, 如果刪除舊的 filterCell, 程式在執行時,
              於拖拉"濾鏡表單" 時會 crash 掉.
    
           2. 如果一律將 filterCell 儲存起來, 程式在執行時,
              於每次拖拉"濾鏡表單" 時, 都會將新產生的 filterCell
              (即使 filterID 名稱相同)存入 Array 裡.
    */
   
    //@update: comment it
    //GlobalDataClass *dataObj = [GlobalDataClass getInstance];

    BOOL saveCell = YES;
   
    //for (FilterCell* filterCell in dataObj.filterCellArray) {
    //@update
    for (FilterCell* filterCell in self.dataObj.filterCellArray) {
        //if ([filterCell.titleLabel.text isEqualToString:cell.titleLabel.text])
        //@update
        if ([filterCell.filterID isEqualToString:cell.filterID])
        {
            saveCell = NO;
            break;
        }
    }
    if (saveCell) {
        //[dataObj.filterCellArray addObject:cell];
        //@update
        [self.dataObj.filterCellArray addObject:cell];
    }
    //NSLog(@"self.dataObj.filterCellArray.count = %d", self.dataObj.filterCellArray.count);
   
    return cell;
}

....
// step 4:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    int newRow = [indexPath row];
    int oldRow = [self.lastIndexPath row];
    //NSLog(@"oldRow = %d", oldRow);
    //NSLog(@"newRow = %d", newRow);
   
    //@update comment it
    //GlobalDataClass *dataObj = [GlobalDataClass getInstance];
   
    // 選取濾鏡
    if (newRow != oldRow)
    {
        self.lastIndexPath = indexPath;
       
        //@TODO: 套用濾鏡功能
        /* (此方式亦可)
        FilterCell *cell = (FilterCell *)[tableView cellForRowAtIndexPath:indexPath];
        NSLog(@"FilterID = %@, Title = %@", cell.filterID, cell.titleLabel.text);
        */
        NSDictionary *currentFilter = [self.filters objectAtIndex:indexPath.row];
        /*
        NSString *FilterID = [currentFilter objectForKey:@"FilterID"];
        NSString *Title = [currentFilter objectForKey:@"Title"];
        NSString *ImageName = [currentFilter objectForKey:@"ImageName"];
        NSLog(@"FilterID = %@, Title = %@, ImageName = %@", FilterID, Title, ImageName);
        */
        self.dataObj.isUsingFilter = YES;

        self.dataObj.filterID = [NSMutableString stringWithString:[currentFilter objectForKey:@"FilterID"]];
    }
    // 取消選取濾鏡
    else
    {
        //self.lastIndexPath = nil; // -> not work at section:0 row:0
        self.lastIndexPath = [NSIndexPath indexPathForRow:-1 inSection:0];
        [tableView deselectRowAtIndexPath:indexPath animated:NO];
       
        //@TODO: 恢復原始影像
        self.dataObj.isUsingFilter = NO;
        self.dataObj.filterID = [NSMutableString stringWithString:@"NoID"];
    }
   
    NSLog(@"filterID = %@", self.dataObj.filterID);
}

....
// step 5:
- (void)dealloc
{
    self.filterTableView = nil;
    self.filters = nil;
    self.lastIndexPath = nil;
    self.dataObj = nil;
}
....

-----------------------------------------------------------------------------

E. 編譯並執行
     1. 未套用濾鏡: 相機畫面

     2. 未套用濾鏡: 拍照結果 (只會截取濾鏡方框內的影像)

     3. 套用濾鏡: 相機畫面 (目前每個濾鏡效果都一樣)

     4. 套用濾鏡: 拍照結果

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。