Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cfb84d31fb | |||
| 984bcb8e15 | |||
| d7164f8b76 | |||
| fb7ad31bf0 | |||
| ec61b1f7ec | |||
| ba1280af3a | |||
| 9e99357113 | |||
| 8057a3e013 | |||
| 981b0dd2cd | |||
| d93d4b9bf5 | |||
| df6164fed1 | |||
| ba19663059 | |||
| 0967800b23 | |||
| d0ef23d887 | |||
| 807d83c263 | |||
| 28d20814f6 | |||
| 4c385f6e89 | |||
| 8708e04c0d | |||
| e47f3154a8 | |||
| 7062cdcf7c | |||
| bbe11292de | |||
| 2b51c69007 | |||
| eb0edf85d2 | |||
| 5b097680f9 | |||
| fb9ecaabad | |||
| 6a971f872f | |||
| c86a88487d | |||
| beac14592c | |||
| 24f8b42880 | |||
| 11848599b5 | |||
| d45e3770d6 | |||
| d883f023ca | |||
| 0d269dcf25 | |||
| 3b7ad944af | |||
| 3e92282448 | |||
| f2f25ea19d | |||
| b910e120ff | |||
| 3d5c88416b | |||
| 79744c0096 | |||
| 191a603f98 | |||
| fff7d4513d | |||
| 4f58fd019e | |||
| a440270bb0 | |||
| d57d987e67 | |||
| 204aaa6d09 | |||
| 3c58ff7e49 | |||
| 320307bc2a | |||
| 0e7b2475d7 | |||
| 531c92df51 | |||
| 91e72e2211 | |||
| 5cc50b6d47 | |||
| 659dfb81d0 | |||
| 4865bdc508 | |||
| e63026415a | |||
| 21335ed3f0 | |||
| ba1f7a383c | |||
| b109289bc1 | |||
| 1e56fc2941 | |||
| 14a3b2eb4b | |||
| 510d796ab1 | |||
| 48dac15d54 | |||
| 13bc9ad1eb | |||
| 1e6e42f2c6 | |||
| 519b685855 |
+22
-1
@@ -1,4 +1,25 @@
|
||||
# infer
|
||||
infer-out/
|
||||
|
||||
#CocoaPods
|
||||
# Xcode
|
||||
.DS_Store
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
*.xcworkspace
|
||||
!default.xcworkspace
|
||||
xcuserdata
|
||||
profile
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
.idea/
|
||||
|
||||
# CocoaPods
|
||||
Pods/
|
||||
Podfile.lock
|
||||
+5
-10
@@ -2,25 +2,20 @@
|
||||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "LFLiveKit"
|
||||
s.version = "1.7.0"
|
||||
s.version = "1.9.6"
|
||||
s.summary = "LaiFeng ios Live. LFLiveKit."
|
||||
s.homepage = "https://github.com/chenliming777"
|
||||
s.license = { :type => "MIT", :file => "LICENSE" }
|
||||
s.author = { "chenliming" => "chenliming777@qq.com" }
|
||||
s.platform = :ios, "8.0"
|
||||
s.ios.deployment_target = "8.0"
|
||||
s.platform = :ios, "7.0"
|
||||
s.ios.deployment_target = "7.0"
|
||||
s.source = { :git => "https://github.com/LaiFengiOS/LFLiveKit.git", :tag => "#{s.version}" }
|
||||
s.source_files = "LFLiveKit/**/*.{*}"
|
||||
s.source_files = "LFLiveKit/**/*.{h,m,mm,cpp,c}"
|
||||
s.public_header_files = "LFLiveKit/**/*.h"
|
||||
|
||||
s.frameworks = "VideoToolbox", "AudioToolbox","AVFoundation","Foundation","UIKit"
|
||||
s.library = "z"
|
||||
s.libraries = "c++", "z"
|
||||
|
||||
s.requires_arc = true
|
||||
|
||||
s.dependency "CocoaAsyncSocket", "~> 7.4.1"
|
||||
s.dependency 'LMGPUImage', '~> 0.1.9'
|
||||
s.dependency "pili-librtmp", "~> 1.0.2"
|
||||
s.dependency "YYDispatchQueuePool"
|
||||
|
||||
end
|
||||
|
||||
+1616
-162
File diff suppressed because it is too large
Load Diff
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
-71
@@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890388.753673"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "18"
|
||||
endingLineNumber = "18"
|
||||
landmarkName = "-setUp"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890389.93478"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "24"
|
||||
endingLineNumber = "24"
|
||||
landmarkName = "-tearDown"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890391.195074"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "30"
|
||||
endingLineNumber = "30"
|
||||
landmarkName = "-testExample"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "LFLiveKitTests/LFLiveKitTests.m"
|
||||
timestampString = "486890392.195974"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "37"
|
||||
endingLineNumber = "37"
|
||||
landmarkName = "-testPerformanceExample"
|
||||
landmarkType = "5">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
BIN
Binary file not shown.
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.7.0</string>
|
||||
<string>1.9.6</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
+14
-23
@@ -15,15 +15,6 @@
|
||||
#import "LFLiveVideoConfiguration.h"
|
||||
#import "LFLiveDebug.h"
|
||||
|
||||
typedef void (^ LFRequestComplete)(_Nullable id info,NSError *_Nullable errorMsg);
|
||||
|
||||
/// 流类型
|
||||
typedef NS_ENUM(NSUInteger, LFLiveType){
|
||||
/// rtmp格式
|
||||
LFLiveRTMP = 0,
|
||||
/// tcp 传输flv格式
|
||||
LFLiveFLV = 1,
|
||||
};
|
||||
|
||||
@class LFLiveSession;
|
||||
@protocol LFLiveSessionDelegate <NSObject>
|
||||
@@ -32,9 +23,9 @@ typedef NS_ENUM(NSUInteger, LFLiveType){
|
||||
/** live status changed will callback */
|
||||
- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state;
|
||||
/** live debug info callback */
|
||||
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug*)debugInfo;
|
||||
- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug *)debugInfo;
|
||||
/** callback socket errorcode */
|
||||
- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode;
|
||||
- (void)liveSession:(nullable LFLiveSession *)session errorCode:(LFLiveSocketErrorCode)errorCode;
|
||||
@end
|
||||
|
||||
@class LFLiveStreamInfo;
|
||||
@@ -46,13 +37,13 @@ typedef NS_ENUM(NSUInteger, LFLiveType){
|
||||
/// @name Attribute
|
||||
///=============================================================================
|
||||
/** The delegate of the capture. captureData callback */
|
||||
@property (nullable,nonatomic, weak) id<LFLiveSessionDelegate> delegate;
|
||||
@property (nullable, nonatomic, weak) id<LFLiveSessionDelegate> delegate;
|
||||
|
||||
/** The running control start capture or stop capture*/
|
||||
@property (nonatomic, assign) BOOL running;
|
||||
|
||||
/** The preView will show OpenGL ES view*/
|
||||
@property (nonatomic, strong,null_resettable) UIView *preView;
|
||||
@property (nonatomic, strong, null_resettable) UIView *preView;
|
||||
|
||||
/** The captureDevicePosition control camraPosition ,default front*/
|
||||
@property (nonatomic, assign) AVCaptureDevicePosition captureDevicePosition;
|
||||
@@ -76,22 +67,22 @@ typedef NS_ENUM(NSUInteger, LFLiveType){
|
||||
@property (nonatomic, assign) BOOL mirror;
|
||||
|
||||
/** The muted control callbackAudioData,muted will memset 0.*/
|
||||
@property (nonatomic,assign) BOOL muted;
|
||||
@property (nonatomic, assign) BOOL muted;
|
||||
|
||||
/** The stream control upload and package*/
|
||||
@property (nullable,nonatomic, strong,readonly) LFLiveStreamInfo * streamInfo;
|
||||
@property (nullable, nonatomic, strong, readonly) LFLiveStreamInfo *streamInfo;
|
||||
|
||||
/** The status of the stream .*/
|
||||
@property (nonatomic,assign,readonly) LFLiveState state;
|
||||
@property (nonatomic, assign, readonly) LFLiveState state;
|
||||
|
||||
/** The showDebugInfo control streamInfo and uploadInfo(1s) *.*/
|
||||
@property (nonatomic,assign) BOOL showDebugInfo;
|
||||
@property (nonatomic, assign) BOOL showDebugInfo;
|
||||
|
||||
/** The reconnectInterval control reconnect timeInterval(重连间隔) *.*/
|
||||
@property (nonatomic,assign) NSUInteger reconnectInterval;
|
||||
@property (nonatomic, assign) NSUInteger reconnectInterval;
|
||||
|
||||
/** The reconnectCount control reconnect count (重连次数) *.*/
|
||||
@property (nonatomic,assign) NSUInteger reconnectCount;
|
||||
@property (nonatomic, assign) NSUInteger reconnectCount;
|
||||
|
||||
#pragma mark - Initializer
|
||||
///=============================================================================
|
||||
@@ -101,13 +92,13 @@ typedef NS_ENUM(NSUInteger, LFLiveType){
|
||||
+ (nullable instancetype)new UNAVAILABLE_ATTRIBUTE;
|
||||
|
||||
/**
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
The designated initializer. Multiple instances with the same configuration will make the
|
||||
capture unstable.
|
||||
*/
|
||||
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration*)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration*)videoConfiguration liveType:(LFLiveType)liveType NS_DESIGNATED_INITIALIZER;
|
||||
- (nullable instancetype)initWithAudioConfiguration:(nullable LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(nullable LFLiveVideoConfiguration *)videoConfiguration NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** The start stream .*/
|
||||
- (void)startLive:(nonnull LFLiveStreamInfo*)streamInfo;
|
||||
- (void)startLive:(nonnull LFLiveStreamInfo *)streamInfo;
|
||||
|
||||
/** The stop stream .*/
|
||||
- (void)stopLive;
|
||||
|
||||
+78
-65
@@ -11,19 +11,16 @@
|
||||
#import "LFAudioCapture.h"
|
||||
#import "LFHardwareVideoEncoder.h"
|
||||
#import "LFHardwareAudioEncoder.h"
|
||||
#import "LFStreamRtmpSocket.h"
|
||||
#import "LFStreamTcpSocket.h"
|
||||
#import "LFStreamRTMPSocket.h"
|
||||
#import "LFLiveStreamInfo.h"
|
||||
#import "LFGPUImageBeautyFilter.h"
|
||||
|
||||
#define LFLiveReportKey @"com.youku.liveSessionReport"
|
||||
|
||||
@interface LFLiveSession ()<LFAudioCaptureDelegate,LFVideoCaptureDelegate,LFAudioEncodingDelegate,LFVideoEncodingDelegate,LFStreamSocketDelegate>
|
||||
@interface LFLiveSession ()<LFAudioCaptureDelegate, LFVideoCaptureDelegate, LFAudioEncodingDelegate, LFVideoEncodingDelegate, LFStreamSocketDelegate>
|
||||
{
|
||||
dispatch_semaphore_t _lock;
|
||||
}
|
||||
///流媒体格式
|
||||
@property (nonatomic, assign) LFLiveType liveType;
|
||||
///音频配置
|
||||
@property (nonatomic, strong) LFLiveAudioConfiguration *audioConfiguration;
|
||||
///视频配置
|
||||
@@ -49,7 +46,7 @@
|
||||
/// uploading
|
||||
@property (nonatomic, assign) BOOL uploading;
|
||||
/// state
|
||||
@property (nonatomic,assign,readwrite) LFLiveState state;
|
||||
@property (nonatomic, assign, readwrite) LFLiveState state;
|
||||
|
||||
@end
|
||||
|
||||
@@ -66,99 +63,101 @@
|
||||
@implementation LFLiveSession
|
||||
|
||||
#pragma mark -- LifeCycle
|
||||
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(LFLiveVideoConfiguration *)videoConfiguration liveType:(LFLiveType)liveType{
|
||||
if(!audioConfiguration || !videoConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"audioConfiguration or videoConfiguration is nil " userInfo:nil];
|
||||
if(self = [super init]){
|
||||
- (instancetype)initWithAudioConfiguration:(LFLiveAudioConfiguration *)audioConfiguration videoConfiguration:(LFLiveVideoConfiguration *)videoConfiguration {
|
||||
if (!audioConfiguration || !videoConfiguration) @throw [NSException exceptionWithName:@"LFLiveSession init error" reason:@"audioConfiguration or videoConfiguration is nil " userInfo:nil];
|
||||
if (self = [super init]) {
|
||||
_audioConfiguration = audioConfiguration;
|
||||
_videoConfiguration = videoConfiguration;
|
||||
_liveType = liveType;
|
||||
_lock = dispatch_semaphore_create(1);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
- (void)dealloc {
|
||||
self.audioCaptureSource.running = NO;
|
||||
self.videoCaptureSource.running = NO;
|
||||
}
|
||||
|
||||
#pragma mark -- CustomMethod
|
||||
- (void)startLive:(LFLiveStreamInfo*)streamInfo{
|
||||
if(!streamInfo) return;
|
||||
- (void)startLive:(LFLiveStreamInfo *)streamInfo {
|
||||
if (!streamInfo) return;
|
||||
_streamInfo = streamInfo;
|
||||
_streamInfo.videoConfiguration = _videoConfiguration;
|
||||
_streamInfo.audioConfiguration = _audioConfiguration;
|
||||
[self.socket start];
|
||||
}
|
||||
|
||||
- (void)stopLive{
|
||||
- (void)stopLive {
|
||||
self.uploading = NO;
|
||||
[self.socket stop];
|
||||
self.socket = nil;
|
||||
}
|
||||
|
||||
#pragma mark -- CaptureDelegate
|
||||
- (void)captureOutput:(nullable LFAudioCapture*)capture audioBuffer:(AudioBufferList)inBufferList{
|
||||
[self.audioEncoder encodeAudioData:inBufferList timeStamp:self.currentTimestamp];
|
||||
- (void)captureOutput:(nullable LFAudioCapture *)capture audioBuffer:(AudioBufferList)inBufferList {
|
||||
if (self.uploading) [self.audioEncoder encodeAudioData:inBufferList timeStamp:self.currentTimestamp];
|
||||
}
|
||||
|
||||
- (void)captureOutput:(nullable LFVideoCapture*)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer{
|
||||
[self.videoEncoder encodeVideoData:pixelBuffer timeStamp:self.currentTimestamp];
|
||||
- (void)captureOutput:(nullable LFVideoCapture *)capture pixelBuffer:(nullable CVImageBufferRef)pixelBuffer {
|
||||
if (self.uploading) [self.videoEncoder encodeVideoData:pixelBuffer timeStamp:self.currentTimestamp];
|
||||
}
|
||||
|
||||
#pragma mark -- EncoderDelegate
|
||||
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame*)frame{
|
||||
if(self.uploading) [self.socket sendFrame:frame];//<上传
|
||||
- (void)audioEncoder:(nullable id<LFAudioEncoding>)encoder audioFrame:(nullable LFAudioFrame *)frame {
|
||||
if (self.uploading) [self.socket sendFrame:frame]; //<上传
|
||||
}
|
||||
|
||||
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame*)frame{
|
||||
if(self.uploading) [self.socket sendFrame:frame];//<上传
|
||||
- (void)videoEncoder:(nullable id<LFVideoEncoding>)encoder videoFrame:(nullable LFVideoFrame *)frame {
|
||||
if (self.uploading) [self.socket sendFrame:frame]; //<上传
|
||||
}
|
||||
|
||||
#pragma mark -- LFStreamTcpSocketDelegate
|
||||
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status{
|
||||
if(status == LFLiveStart){
|
||||
if(!self.uploading){
|
||||
- (void)socketStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveState)status {
|
||||
if (status == LFLiveStart) {
|
||||
if (!self.uploading) {
|
||||
self.timestamp = 0;
|
||||
self.isFirstFrame = YES;
|
||||
self.uploading = YES;
|
||||
}
|
||||
}else if(status == LFLiveStop || status == LFLiveError){
|
||||
self.uploading = NO;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.state = status;
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:liveStateDidChange:)]){
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:liveStateDidChange:)]) {
|
||||
[self.delegate liveSession:self liveStateDidChange:status];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode{
|
||||
- (void)socketDidError:(nullable id<LFStreamSocket>)socket errorCode:(LFLiveSocketErrorCode)errorCode {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:errorCode:)]){
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:errorCode:)]) {
|
||||
[self.delegate liveSession:self errorCode:errorCode];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug*)debugInfo{
|
||||
- (void)socketDebug:(nullable id<LFStreamSocket>)socket debugInfo:(nullable LFLiveDebug *)debugInfo {
|
||||
self.debugInfo = debugInfo;
|
||||
if(self.showDebugInfo){
|
||||
if (self.showDebugInfo) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]){
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(liveSession:debugInfo:)]) {
|
||||
[self.delegate liveSession:self debugInfo:debugInfo];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status{
|
||||
- (void)socketBufferStatus:(nullable id<LFStreamSocket>)socket status:(LFLiveBuffferState)status {
|
||||
NSUInteger videoBitRate = [_videoEncoder videoBitRate];
|
||||
if(status == LFLiveBuffferIncrease){
|
||||
if(videoBitRate < _videoConfiguration.videoMaxBitRate){
|
||||
if (status == LFLiveBuffferDecline) {
|
||||
if (videoBitRate < _videoConfiguration.videoMaxBitRate) {
|
||||
videoBitRate = videoBitRate + 50 * 1000;
|
||||
[_videoEncoder setVideoBitRate:videoBitRate];
|
||||
}
|
||||
}else{
|
||||
if(videoBitRate > _videoConfiguration.videoMinBitRate){
|
||||
} else {
|
||||
if (videoBitRate > _videoConfiguration.videoMinBitRate) {
|
||||
videoBitRate = videoBitRate - 100 * 1000;
|
||||
[_videoEncoder setVideoBitRate:videoBitRate];
|
||||
}
|
||||
@@ -166,8 +165,8 @@
|
||||
}
|
||||
|
||||
#pragma mark -- Getter Setter
|
||||
- (void)setRunning:(BOOL)running{
|
||||
if(_running == running) return;
|
||||
- (void)setRunning:(BOOL)running {
|
||||
if (_running == running) return;
|
||||
[self willChangeValueForKey:@"running"];
|
||||
_running = running;
|
||||
[self didChangeValueForKey:@"running"];
|
||||
@@ -175,32 +174,40 @@
|
||||
self.audioCaptureSource.running = _running;
|
||||
}
|
||||
|
||||
- (void)setPreView:(UIView *)preView{
|
||||
- (void)setPreView:(UIView *)preView {
|
||||
[self willChangeValueForKey:@"preView"];
|
||||
[self.videoCaptureSource setPreView:preView];
|
||||
[self didChangeValueForKey:@"preView"];
|
||||
}
|
||||
|
||||
- (UIView*)preView{
|
||||
- (UIView *)preView {
|
||||
return self.videoCaptureSource.preView;
|
||||
}
|
||||
|
||||
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition{
|
||||
- (void)setCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition {
|
||||
[self willChangeValueForKey:@"captureDevicePosition"];
|
||||
[self.videoCaptureSource setCaptureDevicePosition:captureDevicePosition];
|
||||
[self didChangeValueForKey:@"captureDevicePosition"];
|
||||
}
|
||||
|
||||
- (AVCaptureDevicePosition)captureDevicePosition{
|
||||
- (AVCaptureDevicePosition)captureDevicePosition {
|
||||
return self.videoCaptureSource.captureDevicePosition;
|
||||
}
|
||||
|
||||
- (void)setBeautyFace:(BOOL)beautyFace{
|
||||
- (void)setBeautyFace:(BOOL)beautyFace {
|
||||
[self willChangeValueForKey:@"beautyFace"];
|
||||
[self.videoCaptureSource setBeautyFace:beautyFace];
|
||||
[self didChangeValueForKey:@"beautyFace"];
|
||||
}
|
||||
|
||||
- (BOOL)beautyFace{
|
||||
- (BOOL)beautyFace {
|
||||
return self.videoCaptureSource.beautyFace;
|
||||
}
|
||||
|
||||
- (void)setBeautyLevel:(CGFloat)beautyLevel {
|
||||
[self willChangeValueForKey:@"beautyLevel"];
|
||||
[self.videoCaptureSource setBeautyLevel:beautyLevel];
|
||||
[self didChangeValueForKey:@"beautyLevel"];
|
||||
}
|
||||
|
||||
- (CGFloat)beautyLevel {
|
||||
@@ -208,7 +215,9 @@
|
||||
}
|
||||
|
||||
- (void)setBrightLevel:(CGFloat)brightLevel {
|
||||
[self willChangeValueForKey:@"brightLevel"];
|
||||
[self.videoCaptureSource setBrightLevel:brightLevel];
|
||||
[self didChangeValueForKey:@"brightLevel"];
|
||||
}
|
||||
|
||||
- (CGFloat)brightLevel {
|
||||
@@ -216,7 +225,9 @@
|
||||
}
|
||||
|
||||
- (void)setZoomScale:(CGFloat)zoomScale {
|
||||
[self willChangeValueForKey:@"zoomScale"];
|
||||
[self.videoCaptureSource setZoomScale:zoomScale];
|
||||
[self didChangeValueForKey:@"zoomScale"];
|
||||
}
|
||||
|
||||
- (CGFloat)zoomScale {
|
||||
@@ -224,7 +235,9 @@
|
||||
}
|
||||
|
||||
- (void)setTorch:(BOOL)torch {
|
||||
[self willChangeValueForKey:@"torch"];
|
||||
[self.videoCaptureSource setTorch:torch];
|
||||
[self didChangeValueForKey:@"torch"];
|
||||
}
|
||||
|
||||
- (BOOL)torch {
|
||||
@@ -232,76 +245,76 @@
|
||||
}
|
||||
|
||||
- (void)setMirror:(BOOL)mirror {
|
||||
[self willChangeValueForKey:@"mirror"];
|
||||
[self.videoCaptureSource setMirror:mirror];
|
||||
[self didChangeValueForKey:@"mirror"];
|
||||
}
|
||||
|
||||
- (BOOL)mirror {
|
||||
return self.videoCaptureSource.mirror;
|
||||
}
|
||||
|
||||
- (void)setMuted:(BOOL)muted{
|
||||
- (void)setMuted:(BOOL)muted {
|
||||
[self willChangeValueForKey:@"muted"];
|
||||
[self.audioCaptureSource setMuted:muted];
|
||||
[self didChangeValueForKey:@"muted"];
|
||||
}
|
||||
|
||||
- (BOOL)muted{
|
||||
- (BOOL)muted {
|
||||
return self.audioCaptureSource.muted;
|
||||
}
|
||||
|
||||
- (LFAudioCapture*)audioCaptureSource{
|
||||
if(!_audioCaptureSource){
|
||||
- (LFAudioCapture *)audioCaptureSource {
|
||||
if (!_audioCaptureSource) {
|
||||
_audioCaptureSource = [[LFAudioCapture alloc] initWithAudioConfiguration:_audioConfiguration];
|
||||
_audioCaptureSource.delegate = self;
|
||||
}
|
||||
return _audioCaptureSource;
|
||||
}
|
||||
|
||||
- (LFVideoCapture*)videoCaptureSource{
|
||||
if(!_videoCaptureSource){
|
||||
- (LFVideoCapture *)videoCaptureSource {
|
||||
if (!_videoCaptureSource) {
|
||||
_videoCaptureSource = [[LFVideoCapture alloc] initWithVideoConfiguration:_videoConfiguration];
|
||||
_videoCaptureSource.delegate = self;
|
||||
}
|
||||
return _videoCaptureSource;
|
||||
}
|
||||
|
||||
- (id<LFAudioEncoding>)audioEncoder{
|
||||
if(!_audioEncoder){
|
||||
- (id<LFAudioEncoding>)audioEncoder {
|
||||
if (!_audioEncoder) {
|
||||
_audioEncoder = [[LFHardwareAudioEncoder alloc] initWithAudioStreamConfiguration:_audioConfiguration];
|
||||
[_audioEncoder setDelegate:self];
|
||||
}
|
||||
return _audioEncoder;
|
||||
}
|
||||
|
||||
- (id<LFVideoEncoding>)videoEncoder{
|
||||
if(!_videoEncoder){
|
||||
- (id<LFVideoEncoding>)videoEncoder {
|
||||
if (!_videoEncoder) {
|
||||
_videoEncoder = [[LFHardwareVideoEncoder alloc] initWithVideoStreamConfiguration:_videoConfiguration];
|
||||
[_videoEncoder setDelegate:self];
|
||||
}
|
||||
return _videoEncoder;
|
||||
}
|
||||
|
||||
- (id<LFStreamSocket>)socket{
|
||||
if(!_socket){
|
||||
if(self.liveType == LFLiveRTMP){
|
||||
_socket = [[LFStreamRtmpSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
}else if(self.liveType == LFLiveFLV){
|
||||
_socket = [[LFStreamTcpSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
}
|
||||
- (id<LFStreamSocket>)socket {
|
||||
if (!_socket) {
|
||||
_socket = [[LFStreamRTMPSocket alloc] initWithStream:self.streamInfo videoSize:self.videoConfiguration.videoSize reconnectInterval:self.reconnectInterval reconnectCount:self.reconnectCount];
|
||||
[_socket setDelegate:self];
|
||||
}
|
||||
return _socket;
|
||||
}
|
||||
|
||||
- (LFLiveStreamInfo*)streamInfo{
|
||||
if(!_streamInfo){
|
||||
- (LFLiveStreamInfo *)streamInfo {
|
||||
if (!_streamInfo) {
|
||||
_streamInfo = [[LFLiveStreamInfo alloc] init];
|
||||
}
|
||||
return _streamInfo;
|
||||
}
|
||||
|
||||
- (uint64_t)currentTimestamp{
|
||||
- (uint64_t)currentTimestamp {
|
||||
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
|
||||
uint64_t currentts = 0;
|
||||
if(_isFirstFrame == true) {
|
||||
if (_isFirstFrame == true) {
|
||||
_timestamp = NOW;
|
||||
_isFirstFrame = false;
|
||||
currentts = 0;
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book.
|
||||
// A description of this can be found at his page on the topic:
|
||||
// http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html
|
||||
// I've extended this to be able to take programs as NSStrings in addition to files, for baked-in shaders
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
#import <OpenGLES/ES2/gl.h>
|
||||
#import <OpenGLES/ES2/glext.h>
|
||||
#else
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <OpenGL/gl.h>
|
||||
#endif
|
||||
|
||||
@interface GLProgram : NSObject
|
||||
{
|
||||
NSMutableArray *attributes;
|
||||
NSMutableArray *uniforms;
|
||||
GLuint program,
|
||||
vertShader,
|
||||
fragShader;
|
||||
}
|
||||
|
||||
@property(readwrite, nonatomic) BOOL initialized;
|
||||
@property(readwrite, copy, nonatomic) NSString *vertexShaderLog;
|
||||
@property(readwrite, copy, nonatomic) NSString *fragmentShaderLog;
|
||||
@property(readwrite, copy, nonatomic) NSString *programLog;
|
||||
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderString:(NSString *)fShaderString;
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
- (void)addAttribute:(NSString *)attributeName;
|
||||
- (GLuint)attributeIndex:(NSString *)attributeName;
|
||||
- (GLuint)uniformIndex:(NSString *)uniformName;
|
||||
- (BOOL)link;
|
||||
- (void)use;
|
||||
- (void)validate;
|
||||
@end
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
// This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book.
|
||||
// A description of this can be found at his page on the topic:
|
||||
// http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html
|
||||
|
||||
|
||||
#import "GLProgram.h"
|
||||
// START:typedefs
|
||||
#pragma mark Function Pointer Definitions
|
||||
typedef void (*GLInfoFunction)(GLuint program, GLenum pname, GLint* params);
|
||||
typedef void (*GLLogFunction) (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog);
|
||||
// END:typedefs
|
||||
#pragma mark -
|
||||
#pragma mark Private Extension Method Declaration
|
||||
// START:extension
|
||||
@interface GLProgram()
|
||||
|
||||
- (BOOL)compileShader:(GLuint *)shader
|
||||
type:(GLenum)type
|
||||
string:(NSString *)shaderString;
|
||||
@end
|
||||
// END:extension
|
||||
#pragma mark -
|
||||
|
||||
@implementation GLProgram
|
||||
// START:init
|
||||
|
||||
@synthesize initialized = _initialized;
|
||||
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderString:(NSString *)fShaderString;
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_initialized = NO;
|
||||
|
||||
attributes = [[NSMutableArray alloc] init];
|
||||
uniforms = [[NSMutableArray alloc] init];
|
||||
program = glCreateProgram();
|
||||
|
||||
if (![self compileShader:&vertShader
|
||||
type:GL_VERTEX_SHADER
|
||||
string:vShaderString])
|
||||
{
|
||||
NSLog(@"Failed to compile vertex shader");
|
||||
}
|
||||
|
||||
// Create and compile fragment shader
|
||||
if (![self compileShader:&fragShader
|
||||
type:GL_FRAGMENT_SHADER
|
||||
string:fShaderString])
|
||||
{
|
||||
NSLog(@"Failed to compile fragment shader");
|
||||
}
|
||||
|
||||
glAttachShader(program, vertShader);
|
||||
glAttachShader(program, fragShader);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithVertexShaderString:(NSString *)vShaderString
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
{
|
||||
NSString *fragShaderPathname = [[NSBundle mainBundle] pathForResource:fShaderFilename ofType:@"fsh"];
|
||||
NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragShaderPathname encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
if ((self = [self initWithVertexShaderString:vShaderString fragmentShaderString:fragmentShaderString]))
|
||||
{
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename
|
||||
fragmentShaderFilename:(NSString *)fShaderFilename;
|
||||
{
|
||||
NSString *vertShaderPathname = [[NSBundle mainBundle] pathForResource:vShaderFilename ofType:@"vsh"];
|
||||
NSString *vertexShaderString = [NSString stringWithContentsOfFile:vertShaderPathname encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
NSString *fragShaderPathname = [[NSBundle mainBundle] pathForResource:fShaderFilename ofType:@"fsh"];
|
||||
NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragShaderPathname encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
if ((self = [self initWithVertexShaderString:vertexShaderString fragmentShaderString:fragmentShaderString]))
|
||||
{
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
// END:init
|
||||
// START:compile
|
||||
- (BOOL)compileShader:(GLuint *)shader
|
||||
type:(GLenum)type
|
||||
string:(NSString *)shaderString
|
||||
{
|
||||
// CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
|
||||
|
||||
GLint status;
|
||||
const GLchar *source;
|
||||
|
||||
source =
|
||||
(GLchar *)[shaderString UTF8String];
|
||||
if (!source)
|
||||
{
|
||||
NSLog(@"Failed to load vertex shader");
|
||||
return NO;
|
||||
}
|
||||
|
||||
*shader = glCreateShader(type);
|
||||
glShaderSource(*shader, 1, &source, NULL);
|
||||
glCompileShader(*shader);
|
||||
|
||||
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
|
||||
|
||||
if (status != GL_TRUE)
|
||||
{
|
||||
GLint logLength;
|
||||
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength > 0)
|
||||
{
|
||||
GLchar *log = (GLchar *)malloc(logLength);
|
||||
glGetShaderInfoLog(*shader, logLength, &logLength, log);
|
||||
if (shader == &vertShader)
|
||||
{
|
||||
self.vertexShaderLog = [NSString stringWithFormat:@"%s", log];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.fragmentShaderLog = [NSString stringWithFormat:@"%s", log];
|
||||
}
|
||||
|
||||
free(log);
|
||||
}
|
||||
}
|
||||
|
||||
// CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
|
||||
// NSLog(@"Compiled in %f ms", linkTime * 1000.0);
|
||||
|
||||
return status == GL_TRUE;
|
||||
}
|
||||
// END:compile
|
||||
#pragma mark -
|
||||
// START:addattribute
|
||||
- (void)addAttribute:(NSString *)attributeName
|
||||
{
|
||||
if (![attributes containsObject:attributeName])
|
||||
{
|
||||
[attributes addObject:attributeName];
|
||||
glBindAttribLocation(program,
|
||||
(GLuint)[attributes indexOfObject:attributeName],
|
||||
[attributeName UTF8String]);
|
||||
}
|
||||
}
|
||||
// END:addattribute
|
||||
// START:indexmethods
|
||||
- (GLuint)attributeIndex:(NSString *)attributeName
|
||||
{
|
||||
return (GLuint)[attributes indexOfObject:attributeName];
|
||||
}
|
||||
- (GLuint)uniformIndex:(NSString *)uniformName
|
||||
{
|
||||
return glGetUniformLocation(program, [uniformName UTF8String]);
|
||||
}
|
||||
// END:indexmethods
|
||||
#pragma mark -
|
||||
// START:link
|
||||
- (BOOL)link
|
||||
{
|
||||
// CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
|
||||
|
||||
GLint status;
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||
if (status == GL_FALSE)
|
||||
return NO;
|
||||
|
||||
if (vertShader)
|
||||
{
|
||||
glDeleteShader(vertShader);
|
||||
vertShader = 0;
|
||||
}
|
||||
if (fragShader)
|
||||
{
|
||||
glDeleteShader(fragShader);
|
||||
fragShader = 0;
|
||||
}
|
||||
|
||||
self.initialized = YES;
|
||||
|
||||
// CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
|
||||
// NSLog(@"Linked in %f ms", linkTime * 1000.0);
|
||||
|
||||
return YES;
|
||||
}
|
||||
// END:link
|
||||
// START:use
|
||||
- (void)use
|
||||
{
|
||||
glUseProgram(program);
|
||||
}
|
||||
// END:use
|
||||
#pragma mark -
|
||||
|
||||
- (void)validate;
|
||||
{
|
||||
GLint logLength;
|
||||
|
||||
glValidateProgram(program);
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength > 0)
|
||||
{
|
||||
GLchar *log = (GLchar *)malloc(logLength);
|
||||
glGetProgramInfoLog(program, logLength, &logLength, log);
|
||||
self.programLog = [NSString stringWithFormat:@"%s", log];
|
||||
free(log);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
// START:dealloc
|
||||
- (void)dealloc
|
||||
{
|
||||
if (vertShader)
|
||||
glDeleteShader(vertShader);
|
||||
|
||||
if (fragShader)
|
||||
glDeleteShader(fragShader);
|
||||
|
||||
if (program)
|
||||
glDeleteProgram(program);
|
||||
|
||||
}
|
||||
// END:dealloc
|
||||
@end
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#import "GLProgram.h"
|
||||
|
||||
// Base classes
|
||||
#import "GPUImageContext.h"
|
||||
#import "GPUImageOutput.h"
|
||||
#import "GPUImageView.h"
|
||||
#import "GPUImageVideoCamera.h"
|
||||
#import "GPUImageStillCamera.h"
|
||||
#import "GPUImageMovie.h"
|
||||
#import "GPUImagePicture.h"
|
||||
#import "GPUImageRawDataInput.h"
|
||||
#import "GPUImageRawDataOutput.h"
|
||||
#import "GPUImageMovieWriter.h"
|
||||
#import "GPUImageFilterPipeline.h"
|
||||
#import "GPUImageTextureOutput.h"
|
||||
#import "GPUImageFilterGroup.h"
|
||||
#import "GPUImageTextureInput.h"
|
||||
#import "GPUImageUIElement.h"
|
||||
#import "GPUImageBuffer.h"
|
||||
#import "GPUImageFramebuffer.h"
|
||||
#import "GPUImageFramebufferCache.h"
|
||||
|
||||
// Filters
|
||||
#import "GPUImageFilter.h"
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
#import "GPUImagePixellateFilter.h"
|
||||
#import "GPUImagePixellatePositionFilter.h"
|
||||
#import "GPUImageSepiaFilter.h"
|
||||
#import "GPUImageColorInvertFilter.h"
|
||||
#import "GPUImageSaturationFilter.h"
|
||||
#import "GPUImageContrastFilter.h"
|
||||
#import "GPUImageExposureFilter.h"
|
||||
#import "GPUImageBrightnessFilter.h"
|
||||
#import "GPUImageLevelsFilter.h"
|
||||
#import "GPUImageSharpenFilter.h"
|
||||
#import "GPUImageGammaFilter.h"
|
||||
#import "GPUImageSobelEdgeDetectionFilter.h"
|
||||
#import "GPUImageSketchFilter.h"
|
||||
#import "GPUImageToonFilter.h"
|
||||
#import "GPUImageSmoothToonFilter.h"
|
||||
#import "GPUImageMultiplyBlendFilter.h"
|
||||
#import "GPUImageDissolveBlendFilter.h"
|
||||
#import "GPUImageKuwaharaFilter.h"
|
||||
#import "GPUImageKuwaharaRadius3Filter.h"
|
||||
#import "GPUImageVignetteFilter.h"
|
||||
#import "GPUImageGaussianBlurFilter.h"
|
||||
#import "GPUImageGaussianBlurPositionFilter.h"
|
||||
#import "GPUImageGaussianSelectiveBlurFilter.h"
|
||||
#import "GPUImageOverlayBlendFilter.h"
|
||||
#import "GPUImageDarkenBlendFilter.h"
|
||||
#import "GPUImageLightenBlendFilter.h"
|
||||
#import "GPUImageSwirlFilter.h"
|
||||
#import "GPUImageSourceOverBlendFilter.h"
|
||||
#import "GPUImageColorBurnBlendFilter.h"
|
||||
#import "GPUImageColorDodgeBlendFilter.h"
|
||||
#import "GPUImageScreenBlendFilter.h"
|
||||
#import "GPUImageExclusionBlendFilter.h"
|
||||
#import "GPUImageDifferenceBlendFilter.h"
|
||||
#import "GPUImageSubtractBlendFilter.h"
|
||||
#import "GPUImageHardLightBlendFilter.h"
|
||||
#import "GPUImageSoftLightBlendFilter.h"
|
||||
#import "GPUImageColorBlendFilter.h"
|
||||
#import "GPUImageHueBlendFilter.h"
|
||||
#import "GPUImageSaturationBlendFilter.h"
|
||||
#import "GPUImageLuminosityBlendFilter.h"
|
||||
#import "GPUImageCropFilter.h"
|
||||
#import "GPUImageGrayscaleFilter.h"
|
||||
#import "GPUImageTransformFilter.h"
|
||||
#import "GPUImageChromaKeyBlendFilter.h"
|
||||
#import "GPUImageHazeFilter.h"
|
||||
#import "GPUImageLuminanceThresholdFilter.h"
|
||||
#import "GPUImagePosterizeFilter.h"
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
#import "GPUImageAdaptiveThresholdFilter.h"
|
||||
#import "GPUImageSolarizeFilter.h"
|
||||
#import "GPUImageUnsharpMaskFilter.h"
|
||||
#import "GPUImageBulgeDistortionFilter.h"
|
||||
#import "GPUImagePinchDistortionFilter.h"
|
||||
#import "GPUImageCrosshatchFilter.h"
|
||||
#import "GPUImageCGAColorspaceFilter.h"
|
||||
#import "GPUImagePolarPixellateFilter.h"
|
||||
#import "GPUImageStretchDistortionFilter.h"
|
||||
#import "GPUImagePerlinNoiseFilter.h"
|
||||
#import "GPUImageJFAVoronoiFilter.h"
|
||||
#import "GPUImageVoronoiConsumerFilter.h"
|
||||
#import "GPUImageMosaicFilter.h"
|
||||
#import "GPUImageTiltShiftFilter.h"
|
||||
#import "GPUImage3x3ConvolutionFilter.h"
|
||||
#import "GPUImageEmbossFilter.h"
|
||||
#import "GPUImageCannyEdgeDetectionFilter.h"
|
||||
#import "GPUImageThresholdEdgeDetectionFilter.h"
|
||||
#import "GPUImageMaskFilter.h"
|
||||
#import "GPUImageHistogramFilter.h"
|
||||
#import "GPUImageHistogramGenerator.h"
|
||||
#import "GPUImageHistogramEqualizationFilter.h"
|
||||
#import "GPUImagePrewittEdgeDetectionFilter.h"
|
||||
#import "GPUImageXYDerivativeFilter.h"
|
||||
#import "GPUImageHarrisCornerDetectionFilter.h"
|
||||
#import "GPUImageAlphaBlendFilter.h"
|
||||
#import "GPUImageNormalBlendFilter.h"
|
||||
#import "GPUImageNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageRGBFilter.h"
|
||||
#import "GPUImageMedianFilter.h"
|
||||
#import "GPUImageBilateralFilter.h"
|
||||
#import "GPUImageCrosshairGenerator.h"
|
||||
#import "GPUImageToneCurveFilter.h"
|
||||
#import "GPUImageNobleCornerDetectionFilter.h"
|
||||
#import "GPUImageShiTomasiFeatureDetectionFilter.h"
|
||||
#import "GPUImageErosionFilter.h"
|
||||
#import "GPUImageRGBErosionFilter.h"
|
||||
#import "GPUImageDilationFilter.h"
|
||||
#import "GPUImageRGBDilationFilter.h"
|
||||
#import "GPUImageOpeningFilter.h"
|
||||
#import "GPUImageRGBOpeningFilter.h"
|
||||
#import "GPUImageClosingFilter.h"
|
||||
#import "GPUImageRGBClosingFilter.h"
|
||||
#import "GPUImageColorPackingFilter.h"
|
||||
#import "GPUImageSphereRefractionFilter.h"
|
||||
#import "GPUImageMonochromeFilter.h"
|
||||
#import "GPUImageOpacityFilter.h"
|
||||
#import "GPUImageHighlightShadowFilter.h"
|
||||
#import "GPUImageFalseColorFilter.h"
|
||||
#import "GPUImageHSBFilter.h"
|
||||
#import "GPUImageHueFilter.h"
|
||||
#import "GPUImageGlassSphereFilter.h"
|
||||
#import "GPUImageLookupFilter.h"
|
||||
#import "GPUImageAmatorkaFilter.h"
|
||||
#import "GPUImageMissEtikateFilter.h"
|
||||
#import "GPUImageSoftEleganceFilter.h"
|
||||
#import "GPUImageAddBlendFilter.h"
|
||||
#import "GPUImageDivideBlendFilter.h"
|
||||
#import "GPUImagePolkaDotFilter.h"
|
||||
#import "GPUImageLocalBinaryPatternFilter.h"
|
||||
#import "GPUImageColorLocalBinaryPatternFilter.h"
|
||||
#import "GPUImageLanczosResamplingFilter.h"
|
||||
#import "GPUImageAverageColor.h"
|
||||
#import "GPUImageSolidColorGenerator.h"
|
||||
#import "GPUImageLuminosity.h"
|
||||
#import "GPUImageAverageLuminanceThresholdFilter.h"
|
||||
#import "GPUImageWhiteBalanceFilter.h"
|
||||
#import "GPUImageChromaKeyFilter.h"
|
||||
#import "GPUImageLowPassFilter.h"
|
||||
#import "GPUImageHighPassFilter.h"
|
||||
#import "GPUImageMotionDetector.h"
|
||||
#import "GPUImageHalftoneFilter.h"
|
||||
#import "GPUImageThresholdedNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageHoughTransformLineDetector.h"
|
||||
#import "GPUImageParallelCoordinateLineTransformFilter.h"
|
||||
#import "GPUImageThresholdSketchFilter.h"
|
||||
#import "GPUImageLineGenerator.h"
|
||||
#import "GPUImageLinearBurnBlendFilter.h"
|
||||
#import "GPUImageGaussianBlurPositionFilter.h"
|
||||
#import "GPUImagePixellatePositionFilter.h"
|
||||
#import "GPUImageTwoInputCrossTextureSamplingFilter.h"
|
||||
#import "GPUImagePoissonBlendFilter.h"
|
||||
#import "GPUImageMotionBlurFilter.h"
|
||||
#import "GPUImageZoomBlurFilter.h"
|
||||
#import "GPUImageLaplacianFilter.h"
|
||||
#import "GPUImageiOSBlurFilter.h"
|
||||
#import "GPUImageLuminanceRangeFilter.h"
|
||||
#import "GPUImageDirectionalNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageDirectionalSobelEdgeDetectionFilter.h"
|
||||
#import "GPUImageSingleComponentGaussianBlurFilter.h"
|
||||
#import "GPUImageThreeInputFilter.h"
|
||||
#import "GPUImageFourInputFilter.h"
|
||||
#import "GPUImageWeakPixelInclusionFilter.h"
|
||||
#import "GPUImageColorConversion.h"
|
||||
#import "GPUImageColourFASTFeatureDetector.h"
|
||||
#import "GPUImageColourFASTSamplingOperation.h"
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
/** Runs a 3x3 convolution kernel against the image
|
||||
*/
|
||||
@interface GPUImage3x3ConvolutionFilter : GPUImage3x3TextureSamplingFilter
|
||||
{
|
||||
GLint convolutionMatrixUniform;
|
||||
}
|
||||
|
||||
/** Convolution kernel to run against the image
|
||||
|
||||
The convolution kernel is a 3x3 matrix of values to apply to the pixel and its 8 surrounding pixels.
|
||||
The matrix is specified in row-major order, with the top left pixel being one.one and the bottom right three.three
|
||||
If the values in the matrix don't add up to 1.0, the image could be brightened or darkened.
|
||||
*/
|
||||
@property(readwrite, nonatomic) GPUMatrix3x3 convolutionKernel;
|
||||
|
||||
@end
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
#import "GPUImage3x3ConvolutionFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImage3x3ConvolutionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mediump mat3 convolutionMatrix;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
mediump vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
mediump vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
mediump vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
mediump vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
mediump vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
mediump vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
mediump vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
|
||||
mediump vec3 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];
|
||||
resultColor += leftColor * convolutionMatrix[1][0] + centerColor.rgb * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];
|
||||
resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];
|
||||
|
||||
gl_FragColor = vec4(resultColor, centerColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImage3x3ConvolutionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mat3 convolutionMatrix;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
|
||||
vec3 resultColor = topLeftColor * convolutionMatrix[0][0] + topColor * convolutionMatrix[0][1] + topRightColor * convolutionMatrix[0][2];
|
||||
resultColor += leftColor * convolutionMatrix[1][0] + centerColor.rgb * convolutionMatrix[1][1] + rightColor * convolutionMatrix[1][2];
|
||||
resultColor += bottomLeftColor * convolutionMatrix[2][0] + bottomColor * convolutionMatrix[2][1] + bottomRightColor * convolutionMatrix[2][2];
|
||||
|
||||
gl_FragColor = vec4(resultColor, centerColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImage3x3ConvolutionFilter
|
||||
|
||||
@synthesize convolutionKernel = _convolutionKernel;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithFragmentShaderFromString:kGPUImage3x3ConvolutionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.convolutionKernel = (GPUMatrix3x3){
|
||||
{0.f, 0.f, 0.f},
|
||||
{0.f, 1.f, 0.f},
|
||||
{0.f, 0.f, 0.f}
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
convolutionMatrixUniform = [filterProgram uniformIndex:@"convolutionMatrix"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setConvolutionKernel:(GPUMatrix3x3)newValue;
|
||||
{
|
||||
_convolutionKernel = newValue;
|
||||
|
||||
[self setMatrix3f:_convolutionKernel forUniform:convolutionMatrixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
extern NSString *const kGPUImageNearbyTexelSamplingVertexShaderString;
|
||||
|
||||
@interface GPUImage3x3TextureSamplingFilter : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
CGFloat texelWidth, texelHeight;
|
||||
BOOL hasOverriddenImageSizeFactor;
|
||||
}
|
||||
|
||||
// The texel width and height determines how far out to sample from this texel. By default, this is the normalized width of a pixel, but this can be overridden for different effects.
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,121 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
// Override vertex shader to remove dependent texture reads
|
||||
NSString *const kGPUImageNearbyTexelSamplingVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 widthStep = vec2(texelWidth, 0.0);
|
||||
vec2 heightStep = vec2(0.0, texelHeight);
|
||||
vec2 widthHeightStep = vec2(texelWidth, texelHeight);
|
||||
vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);
|
||||
|
||||
textureCoordinate = inputTextureCoordinate.xy;
|
||||
leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
|
||||
rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;
|
||||
|
||||
topTextureCoordinate = inputTextureCoordinate.xy - heightStep;
|
||||
topLeftTextureCoordinate = inputTextureCoordinate.xy - widthHeightStep;
|
||||
topRightTextureCoordinate = inputTextureCoordinate.xy + widthNegativeHeightStep;
|
||||
|
||||
bottomTextureCoordinate = inputTextureCoordinate.xy + heightStep;
|
||||
bottomLeftTextureCoordinate = inputTextureCoordinate.xy - widthNegativeHeightStep;
|
||||
bottomRightTextureCoordinate = inputTextureCoordinate.xy + widthHeightStep;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@implementation GPUImage3x3TextureSamplingFilter
|
||||
|
||||
@synthesize texelWidth = _texelWidth;
|
||||
@synthesize texelHeight = _texelHeight;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithVertexShaderFromString:(NSString *)vertexShaderString fragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:vertexShaderString fragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [self initWithVertexShaderFromString:kGPUImageNearbyTexelSamplingVertexShaderString fragmentShaderFromString:fragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
if (!hasOverriddenImageSizeFactor)
|
||||
{
|
||||
_texelWidth = 1.0 / filterFrameSize.width;
|
||||
_texelHeight = 1.0 / filterFrameSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
if (GPUImageRotationSwapsWidthAndHeight(inputRotation))
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelHeight);
|
||||
glUniform1f(texelHeightUniform, _texelWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelWidth);
|
||||
glUniform1f(texelHeightUniform, _texelHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelWidth = newValue;
|
||||
|
||||
[self setFloat:_texelWidth forUniform:texelWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelHeight = newValue;
|
||||
|
||||
[self setFloat:_texelHeight forUniform:texelHeightUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@interface GPUImageAdaptiveThresholdFilter : GPUImageFilterGroup
|
||||
|
||||
/** A multiplier for the background averaging blur radius in pixels, with a default of 4
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat blurRadiusInPixels;
|
||||
|
||||
@end
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#import "GPUImageAdaptiveThresholdFilter.h"
|
||||
#import "GPUImageFilter.h"
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
#import "GPUImageGrayscaleFilter.h"
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageAdaptiveThresholdFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float blurredInput = texture2D(inputImageTexture, textureCoordinate).r;
|
||||
highp float localLuminance = texture2D(inputImageTexture2, textureCoordinate2).r;
|
||||
highp float thresholdResult = step(blurredInput - 0.05, localLuminance);
|
||||
|
||||
gl_FragColor = vec4(vec3(thresholdResult), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageAdaptiveThresholdFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
float blurredInput = texture2D(inputImageTexture, textureCoordinate).r;
|
||||
float localLuminance = texture2D(inputImageTexture2, textureCoordinate2).r;
|
||||
float thresholdResult = step(blurredInput - 0.05, localLuminance);
|
||||
|
||||
gl_FragColor = vec4(vec3(thresholdResult), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@interface GPUImageAdaptiveThresholdFilter()
|
||||
{
|
||||
GPUImageBoxBlurFilter *boxBlurFilter;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GPUImageAdaptiveThresholdFilter
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: reduce to luminance
|
||||
GPUImageGrayscaleFilter *luminanceFilter = [[GPUImageGrayscaleFilter alloc] init];
|
||||
[self addFilter:luminanceFilter];
|
||||
|
||||
// Second pass: perform a box blur
|
||||
boxBlurFilter = [[GPUImageBoxBlurFilter alloc] init];
|
||||
[self addFilter:boxBlurFilter];
|
||||
|
||||
// Third pass: compare the blurred background luminance to the local value
|
||||
GPUImageFilter *adaptiveThresholdFilter = [[GPUImageTwoInputFilter alloc] initWithFragmentShaderFromString:kGPUImageAdaptiveThresholdFragmentShaderString];
|
||||
[self addFilter:adaptiveThresholdFilter];
|
||||
|
||||
[luminanceFilter addTarget:boxBlurFilter];
|
||||
|
||||
[boxBlurFilter addTarget:adaptiveThresholdFilter];
|
||||
// To prevent double updating of this filter, disable updates from the sharp luminance image side
|
||||
[luminanceFilter addTarget:adaptiveThresholdFilter];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObject:luminanceFilter];
|
||||
self.terminalFilter = adaptiveThresholdFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
boxBlurFilter.blurRadiusInPixels = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurRadiusInPixels;
|
||||
{
|
||||
return boxBlurFilter.blurRadiusInPixels;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageAddBlendFilter : GPUImageTwoInputFilter
|
||||
|
||||
@end
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#import "GPUImageAddBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageAddBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
mediump float r;
|
||||
if (overlay.r * base.a + base.r * overlay.a >= overlay.a * base.a) {
|
||||
r = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
} else {
|
||||
r = overlay.r + base.r;
|
||||
}
|
||||
|
||||
mediump float g;
|
||||
if (overlay.g * base.a + base.g * overlay.a >= overlay.a * base.a) {
|
||||
g = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
} else {
|
||||
g = overlay.g + base.g;
|
||||
}
|
||||
|
||||
mediump float b;
|
||||
if (overlay.b * base.a + base.b * overlay.a >= overlay.a * base.a) {
|
||||
b = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
} else {
|
||||
b = overlay.b + base.b;
|
||||
}
|
||||
|
||||
mediump float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(r, g, b, a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageAddBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float r;
|
||||
if (overlay.r * base.a + base.r * overlay.a >= overlay.a * base.a) {
|
||||
r = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
} else {
|
||||
r = overlay.r + base.r;
|
||||
}
|
||||
|
||||
float g;
|
||||
if (overlay.g * base.a + base.g * overlay.a >= overlay.a * base.a) {
|
||||
g = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
} else {
|
||||
g = overlay.g + base.g;
|
||||
}
|
||||
|
||||
float b;
|
||||
if (overlay.b * base.a + base.b * overlay.a >= overlay.a * base.a) {
|
||||
b = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
} else {
|
||||
b = overlay.b + base.b;
|
||||
}
|
||||
|
||||
float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(r, g, b, a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@implementation GPUImageAddBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageAddBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageAlphaBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint mixUniform;
|
||||
}
|
||||
|
||||
// Mix ranges from 0.0 (only image 1) to 1.0 (only image 2), with 1.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat mix;
|
||||
|
||||
@end
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#import "GPUImageAlphaBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageAlphaBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
uniform lowp float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * mixturePercent), textureColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageAlphaBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
uniform float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * mixturePercent), textureColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageAlphaBlendFilter
|
||||
|
||||
@synthesize mix = _mix;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageAlphaBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
mixUniform = [filterProgram uniformIndex:@"mixturePercent"];
|
||||
self.mix = 0.5;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setMix:(CGFloat)newValue;
|
||||
{
|
||||
_mix = newValue;
|
||||
|
||||
[self setFloat:_mix forUniform:mixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@class GPUImagePicture;
|
||||
|
||||
/** A photo filter based on Photoshop action by Amatorka
|
||||
http://amatorka.deviantart.com/art/Amatorka-Action-2-121069631
|
||||
*/
|
||||
|
||||
// Note: If you want to use this effect you have to add lookup_amatorka.png
|
||||
// from Resources folder to your application bundle.
|
||||
|
||||
@interface GPUImageAmatorkaFilter : GPUImageFilterGroup
|
||||
{
|
||||
GPUImagePicture *lookupImageSource;
|
||||
}
|
||||
|
||||
@end
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#import "GPUImageAmatorkaFilter.h"
|
||||
#import "GPUImagePicture.h"
|
||||
#import "GPUImageLookupFilter.h"
|
||||
|
||||
@implementation GPUImageAmatorkaFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
UIImage *image = [UIImage imageNamed:@"lookup_amatorka.png"];
|
||||
#else
|
||||
NSImage *image = [NSImage imageNamed:@"lookup_amatorka.png"];
|
||||
#endif
|
||||
|
||||
NSAssert(image, @"To use GPUImageAmatorkaFilter you need to add lookup_amatorka.png from GPUImage/framework/Resources to your application bundle.");
|
||||
|
||||
lookupImageSource = [[GPUImagePicture alloc] initWithImage:image];
|
||||
GPUImageLookupFilter *lookupFilter = [[GPUImageLookupFilter alloc] init];
|
||||
[self addFilter:lookupFilter];
|
||||
|
||||
[lookupImageSource addTarget:lookupFilter atTextureLocation:1];
|
||||
[lookupImageSource processImage];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:lookupFilter, nil];
|
||||
self.terminalFilter = lookupFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
extern NSString *const kGPUImageColorAveragingVertexShaderString;
|
||||
|
||||
@interface GPUImageAverageColor : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
NSUInteger numberOfStages;
|
||||
|
||||
GLubyte *rawImagePixels;
|
||||
CGSize finalStageSize;
|
||||
}
|
||||
|
||||
// This block is called on the completion of color averaging for a frame
|
||||
@property(nonatomic, copy) void(^colorAverageProcessingFinishedBlock)(CGFloat redComponent, CGFloat greenComponent, CGFloat blueComponent, CGFloat alphaComponent, CMTime frameTime);
|
||||
|
||||
- (void)extractAverageColorAtFrameTime:(CMTime)frameTime;
|
||||
|
||||
@end
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
#import "GPUImageAverageColor.h"
|
||||
|
||||
NSString *const kGPUImageColorAveragingVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
|
||||
varying vec2 upperLeftInputTextureCoordinate;
|
||||
varying vec2 upperRightInputTextureCoordinate;
|
||||
varying vec2 lowerLeftInputTextureCoordinate;
|
||||
varying vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
upperLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, -texelHeight);
|
||||
upperRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, -texelHeight);
|
||||
lowerLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, texelHeight);
|
||||
lowerRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, texelHeight);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorAveragingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
varying highp vec2 outputTextureCoordinate;
|
||||
|
||||
varying highp vec2 upperLeftInputTextureCoordinate;
|
||||
varying highp vec2 upperRightInputTextureCoordinate;
|
||||
varying highp vec2 lowerLeftInputTextureCoordinate;
|
||||
varying highp vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 upperLeftColor = texture2D(inputImageTexture, upperLeftInputTextureCoordinate);
|
||||
highp vec4 upperRightColor = texture2D(inputImageTexture, upperRightInputTextureCoordinate);
|
||||
highp vec4 lowerLeftColor = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate);
|
||||
highp vec4 lowerRightColor = texture2D(inputImageTexture, lowerRightInputTextureCoordinate);
|
||||
|
||||
gl_FragColor = 0.25 * (upperLeftColor + upperRightColor + lowerLeftColor + lowerRightColor);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorAveragingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
varying vec2 outputTextureCoordinate;
|
||||
|
||||
varying vec2 upperLeftInputTextureCoordinate;
|
||||
varying vec2 upperRightInputTextureCoordinate;
|
||||
varying vec2 lowerLeftInputTextureCoordinate;
|
||||
varying vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 upperLeftColor = texture2D(inputImageTexture, upperLeftInputTextureCoordinate);
|
||||
vec4 upperRightColor = texture2D(inputImageTexture, upperRightInputTextureCoordinate);
|
||||
vec4 lowerLeftColor = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate);
|
||||
vec4 lowerRightColor = texture2D(inputImageTexture, lowerRightInputTextureCoordinate);
|
||||
|
||||
gl_FragColor = 0.25 * (upperLeftColor + upperRightColor + lowerLeftColor + lowerRightColor);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageAverageColor
|
||||
|
||||
@synthesize colorAverageProcessingFinishedBlock = _colorAverageProcessingFinishedBlock;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:kGPUImageColorAveragingVertexShaderString fragmentShaderFromString:kGPUImageColorAveragingFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
finalStageSize = CGSizeMake(1.0, 1.0);
|
||||
|
||||
__unsafe_unretained GPUImageAverageColor *weakSelf = self;
|
||||
[self setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime) {
|
||||
[weakSelf extractAverageColorAtFrameTime:frameTime];
|
||||
}];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc;
|
||||
{
|
||||
if (rawImagePixels != NULL)
|
||||
{
|
||||
free(rawImagePixels);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Managing the display FBOs
|
||||
|
||||
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
|
||||
{
|
||||
if (self.preventRendering)
|
||||
{
|
||||
[firstInputFramebuffer unlock];
|
||||
return;
|
||||
}
|
||||
|
||||
outputFramebuffer = nil;
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
|
||||
glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
|
||||
glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
|
||||
|
||||
GLuint currentTexture = [firstInputFramebuffer texture];
|
||||
|
||||
NSUInteger numberOfReductionsInX = floor(log(inputTextureSize.width) / log(4.0));
|
||||
NSUInteger numberOfReductionsInY = floor(log(inputTextureSize.height) / log(4.0));
|
||||
NSUInteger reductionsToHitSideLimit = MIN(numberOfReductionsInX, numberOfReductionsInY);
|
||||
for (NSUInteger currentReduction = 0; currentReduction < reductionsToHitSideLimit; currentReduction++)
|
||||
{
|
||||
CGSize currentStageSize = CGSizeMake(floor(inputTextureSize.width / pow(4.0, currentReduction + 1.0)), floor(inputTextureSize.height / pow(4.0, currentReduction + 1.0)));
|
||||
|
||||
[outputFramebuffer unlock];
|
||||
outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:currentStageSize textureOptions:self.outputTextureOptions onlyTexture:NO];
|
||||
[outputFramebuffer activateFramebuffer];
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, currentTexture);
|
||||
|
||||
glUniform1i(filterInputTextureUniform, 2);
|
||||
|
||||
glUniform1f(texelWidthUniform, 0.25 / currentStageSize.width);
|
||||
glUniform1f(texelHeightUniform, 0.25 / currentStageSize.height);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
currentTexture = [outputFramebuffer texture];
|
||||
finalStageSize = currentStageSize;
|
||||
}
|
||||
|
||||
[firstInputFramebuffer unlock];
|
||||
}
|
||||
|
||||
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
inputRotation = kGPUImageNoRotation;
|
||||
}
|
||||
|
||||
- (void)extractAverageColorAtFrameTime:(CMTime)frameTime;
|
||||
{
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
// we need a normal color texture for averaging the color values
|
||||
NSAssert(self.outputTextureOptions.internalFormat == GL_RGBA, @"The output texture internal format for this filter must be GL_RGBA.");
|
||||
NSAssert(self.outputTextureOptions.type == GL_UNSIGNED_BYTE, @"The type of the output texture of this filter must be GL_UNSIGNED_BYTE.");
|
||||
|
||||
NSUInteger totalNumberOfPixels = round(finalStageSize.width * finalStageSize.height);
|
||||
|
||||
if (rawImagePixels == NULL)
|
||||
{
|
||||
rawImagePixels = (GLubyte *)malloc(totalNumberOfPixels * 4);
|
||||
}
|
||||
|
||||
[GPUImageContext useImageProcessingContext];
|
||||
[outputFramebuffer activateFramebuffer];
|
||||
glReadPixels(0, 0, (int)finalStageSize.width, (int)finalStageSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
|
||||
|
||||
NSUInteger redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
|
||||
NSUInteger byteIndex = 0;
|
||||
for (NSUInteger currentPixel = 0; currentPixel < totalNumberOfPixels; currentPixel++)
|
||||
{
|
||||
redTotal += rawImagePixels[byteIndex++];
|
||||
greenTotal += rawImagePixels[byteIndex++];
|
||||
blueTotal += rawImagePixels[byteIndex++];
|
||||
alphaTotal += rawImagePixels[byteIndex++];
|
||||
}
|
||||
|
||||
CGFloat normalizedRedTotal = (CGFloat)redTotal / (CGFloat)totalNumberOfPixels / 255.0;
|
||||
CGFloat normalizedGreenTotal = (CGFloat)greenTotal / (CGFloat)totalNumberOfPixels / 255.0;
|
||||
CGFloat normalizedBlueTotal = (CGFloat)blueTotal / (CGFloat)totalNumberOfPixels / 255.0;
|
||||
CGFloat normalizedAlphaTotal = (CGFloat)alphaTotal / (CGFloat)totalNumberOfPixels / 255.0;
|
||||
|
||||
if (_colorAverageProcessingFinishedBlock != NULL)
|
||||
{
|
||||
_colorAverageProcessingFinishedBlock(normalizedRedTotal, normalizedGreenTotal, normalizedBlueTotal, normalizedAlphaTotal, frameTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,8 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@interface GPUImageAverageLuminanceThresholdFilter : GPUImageFilterGroup
|
||||
|
||||
// This is multiplied by the continually calculated average image luminosity to arrive at the final threshold. Default is 1.0.
|
||||
@property(readwrite, nonatomic) CGFloat thresholdMultiplier;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
#import "GPUImageAverageLuminanceThresholdFilter.h"
|
||||
#import "GPUImageLuminosity.h"
|
||||
#import "GPUImageLuminanceThresholdFilter.h"
|
||||
|
||||
@interface GPUImageAverageLuminanceThresholdFilter()
|
||||
{
|
||||
GPUImageLuminosity *luminosityFilter;
|
||||
GPUImageLuminanceThresholdFilter *luminanceThresholdFilter;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GPUImageAverageLuminanceThresholdFilter
|
||||
|
||||
@synthesize thresholdMultiplier = _thresholdMultiplier;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.thresholdMultiplier = 1.0;
|
||||
|
||||
luminosityFilter = [[GPUImageLuminosity alloc] init];
|
||||
[self addFilter:luminosityFilter];
|
||||
|
||||
luminanceThresholdFilter = [[GPUImageLuminanceThresholdFilter alloc] init];
|
||||
[self addFilter:luminanceThresholdFilter];
|
||||
|
||||
__unsafe_unretained GPUImageAverageLuminanceThresholdFilter *weakSelf = self;
|
||||
__unsafe_unretained GPUImageLuminanceThresholdFilter *weakThreshold = luminanceThresholdFilter;
|
||||
|
||||
[luminosityFilter setLuminosityProcessingFinishedBlock:^(CGFloat luminosity, CMTime frameTime) {
|
||||
weakThreshold.threshold = luminosity * weakSelf.thresholdMultiplier;
|
||||
}];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:luminosityFilter, luminanceThresholdFilter, nil];
|
||||
self.terminalFilter = luminanceThresholdFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "GPUImageGaussianBlurFilter.h"
|
||||
|
||||
@interface GPUImageBilateralFilter : GPUImageGaussianBlurFilter
|
||||
{
|
||||
CGFloat firstDistanceNormalizationFactorUniform;
|
||||
CGFloat secondDistanceNormalizationFactorUniform;
|
||||
}
|
||||
// A normalization factor for the distance between central color and sample color.
|
||||
@property(nonatomic, readwrite) CGFloat distanceNormalizationFactor;
|
||||
@end
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
#import "GPUImageBilateralFilter.h"
|
||||
|
||||
NSString *const kGPUImageBilateralBlurVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
|
||||
const int GAUSSIAN_SAMPLES = 9;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
textureCoordinate = inputTextureCoordinate.xy;
|
||||
|
||||
// Calculate the positions for the blur
|
||||
int multiplier = 0;
|
||||
vec2 blurStep;
|
||||
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
for (int i = 0; i < GAUSSIAN_SAMPLES; i++)
|
||||
{
|
||||
multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));
|
||||
// Blur in x (horizontal)
|
||||
blurStep = float(multiplier) * singleStepOffset;
|
||||
blurCoordinates[i] = inputTextureCoordinate.xy + blurStep;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageBilateralFilterFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
const lowp int GAUSSIAN_SAMPLES = 9;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 blurCoordinates[GAUSSIAN_SAMPLES];
|
||||
|
||||
uniform mediump float distanceNormalizationFactor;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 centralColor;
|
||||
lowp float gaussianWeightTotal;
|
||||
lowp vec4 sum;
|
||||
lowp vec4 sampleColor;
|
||||
lowp float distanceFromCentralColor;
|
||||
lowp float gaussianWeight;
|
||||
|
||||
centralColor = texture2D(inputImageTexture, blurCoordinates[4]);
|
||||
gaussianWeightTotal = 0.18;
|
||||
sum = centralColor * 0.18;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[0]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[1]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[2]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[3]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[5]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[6]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[7]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[8]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
gl_FragColor = sum / gaussianWeightTotal;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageBilateralFilterFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
const int GAUSSIAN_SAMPLES = 9;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 blurCoordinates[GAUSSIAN_SAMPLES];
|
||||
|
||||
uniform float distanceNormalizationFactor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 centralColor;
|
||||
float gaussianWeightTotal;
|
||||
vec4 sum;
|
||||
vec4 sampleColor;
|
||||
float distanceFromCentralColor;
|
||||
float gaussianWeight;
|
||||
|
||||
centralColor = texture2D(inputImageTexture, blurCoordinates[4]);
|
||||
gaussianWeightTotal = 0.18;
|
||||
sum = centralColor * 0.18;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[0]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[1]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[2]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[3]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[5]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[6]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[7]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
sampleColor = texture2D(inputImageTexture, blurCoordinates[8]);
|
||||
distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);
|
||||
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
|
||||
gaussianWeightTotal += gaussianWeight;
|
||||
sum += sampleColor * gaussianWeight;
|
||||
|
||||
gl_FragColor = sum / gaussianWeightTotal;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageBilateralFilter
|
||||
|
||||
@synthesize distanceNormalizationFactor = _distanceNormalizationFactor;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:kGPUImageBilateralBlurVertexShaderString
|
||||
firstStageFragmentShaderFromString:kGPUImageBilateralFilterFragmentShaderString
|
||||
secondStageVertexShaderFromString:kGPUImageBilateralBlurVertexShaderString
|
||||
secondStageFragmentShaderFromString:kGPUImageBilateralFilterFragmentShaderString])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
firstDistanceNormalizationFactorUniform = [filterProgram uniformIndex:@"distanceNormalizationFactor"];
|
||||
secondDistanceNormalizationFactorUniform = [filterProgram uniformIndex:@"distanceNormalizationFactor"];
|
||||
|
||||
self.texelSpacingMultiplier = 4.0;
|
||||
self.distanceNormalizationFactor = 8.0;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setDistanceNormalizationFactor:(CGFloat)newValue
|
||||
{
|
||||
_distanceNormalizationFactor = newValue;
|
||||
|
||||
[self setFloat:newValue
|
||||
forUniform:firstDistanceNormalizationFactorUniform
|
||||
program:filterProgram];
|
||||
|
||||
[self setFloat:newValue
|
||||
forUniform:secondDistanceNormalizationFactorUniform
|
||||
program:secondFilterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageGaussianBlurFilter.h"
|
||||
|
||||
/** A hardware-accelerated box blur of an image
|
||||
*/
|
||||
@interface GPUImageBoxBlurFilter : GPUImageGaussianBlurFilter
|
||||
|
||||
@end
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
|
||||
|
||||
@implementation GPUImageBoxBlurFilter
|
||||
|
||||
+ (NSString *)vertexShaderForOptimizedBlurOfRadius:(NSUInteger)blurRadius sigma:(CGFloat)sigma;
|
||||
{
|
||||
if (blurRadius < 1)
|
||||
{
|
||||
return kGPUImageVertexShaderString;
|
||||
}
|
||||
|
||||
// From these weights we calculate the offsets to read interpolated values from
|
||||
NSUInteger numberOfOptimizedOffsets = MIN(blurRadius / 2 + (blurRadius % 2), 7);
|
||||
|
||||
NSMutableString *shaderString = [[NSMutableString alloc] init];
|
||||
// Header
|
||||
[shaderString appendFormat:@"\
|
||||
attribute vec4 position;\n\
|
||||
attribute vec4 inputTextureCoordinate;\n\
|
||||
\n\
|
||||
uniform float texelWidthOffset;\n\
|
||||
uniform float texelHeightOffset;\n\
|
||||
\n\
|
||||
varying vec2 blurCoordinates[%lu];\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
gl_Position = position;\n\
|
||||
\n\
|
||||
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n", (unsigned long)(1 + (numberOfOptimizedOffsets * 2))];
|
||||
|
||||
// Inner offset loop
|
||||
[shaderString appendString:@"blurCoordinates[0] = inputTextureCoordinate.xy;\n"];
|
||||
for (NSUInteger currentOptimizedOffset = 0; currentOptimizedOffset < numberOfOptimizedOffsets; currentOptimizedOffset++)
|
||||
{
|
||||
GLfloat optimizedOffset = (GLfloat)(currentOptimizedOffset * 2) + 1.5;
|
||||
|
||||
[shaderString appendFormat:@"\
|
||||
blurCoordinates[%lu] = inputTextureCoordinate.xy + singleStepOffset * %f;\n\
|
||||
blurCoordinates[%lu] = inputTextureCoordinate.xy - singleStepOffset * %f;\n", (unsigned long)((currentOptimizedOffset * 2) + 1), optimizedOffset, (unsigned long)((currentOptimizedOffset * 2) + 2), optimizedOffset];
|
||||
}
|
||||
|
||||
// Footer
|
||||
[shaderString appendString:@"}\n"];
|
||||
|
||||
return shaderString;
|
||||
}
|
||||
|
||||
+ (NSString *)fragmentShaderForOptimizedBlurOfRadius:(NSUInteger)blurRadius sigma:(CGFloat)sigma;
|
||||
{
|
||||
if (blurRadius < 1)
|
||||
{
|
||||
return kGPUImagePassthroughFragmentShaderString;
|
||||
}
|
||||
|
||||
NSUInteger numberOfOptimizedOffsets = MIN(blurRadius / 2 + (blurRadius % 2), 7);
|
||||
NSUInteger trueNumberOfOptimizedOffsets = blurRadius / 2 + (blurRadius % 2);
|
||||
|
||||
NSMutableString *shaderString = [[NSMutableString alloc] init];
|
||||
|
||||
// Header
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
[shaderString appendFormat:@"\
|
||||
uniform sampler2D inputImageTexture;\n\
|
||||
uniform highp float texelWidthOffset;\n\
|
||||
uniform highp float texelHeightOffset;\n\
|
||||
\n\
|
||||
varying highp vec2 blurCoordinates[%lu];\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
lowp vec4 sum = vec4(0.0);\n", (unsigned long)(1 + (numberOfOptimizedOffsets * 2)) ];
|
||||
#else
|
||||
[shaderString appendFormat:@"\
|
||||
uniform sampler2D inputImageTexture;\n\
|
||||
uniform float texelWidthOffset;\n\
|
||||
uniform float texelHeightOffset;\n\
|
||||
\n\
|
||||
varying vec2 blurCoordinates[%lu];\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
vec4 sum = vec4(0.0);\n", 1 + (numberOfOptimizedOffsets * 2) ];
|
||||
#endif
|
||||
|
||||
GLfloat boxWeight = 1.0 / (GLfloat)((blurRadius * 2) + 1);
|
||||
|
||||
// Inner texture loop
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[0]) * %f;\n", boxWeight];
|
||||
|
||||
for (NSUInteger currentBlurCoordinateIndex = 0; currentBlurCoordinateIndex < numberOfOptimizedOffsets; currentBlurCoordinateIndex++)
|
||||
{
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[%lu]) * %f;\n", (unsigned long)((currentBlurCoordinateIndex * 2) + 1), boxWeight * 2.0];
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[%lu]) * %f;\n", (unsigned long)((currentBlurCoordinateIndex * 2) + 2), boxWeight * 2.0];
|
||||
}
|
||||
|
||||
// If the number of required samples exceeds the amount we can pass in via varyings, we have to do dependent texture reads in the fragment shader
|
||||
if (trueNumberOfOptimizedOffsets > numberOfOptimizedOffsets)
|
||||
{
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
[shaderString appendString:@"highp vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n"];
|
||||
#else
|
||||
[shaderString appendString:@"vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);\n"];
|
||||
#endif
|
||||
|
||||
for (NSUInteger currentOverlowTextureRead = numberOfOptimizedOffsets; currentOverlowTextureRead < trueNumberOfOptimizedOffsets; currentOverlowTextureRead++)
|
||||
{
|
||||
GLfloat optimizedOffset = (GLfloat)(currentOverlowTextureRead * 2) + 1.5;
|
||||
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[0] + singleStepOffset * %f) * %f;\n", optimizedOffset, boxWeight * 2.0];
|
||||
[shaderString appendFormat:@"sum += texture2D(inputImageTexture, blurCoordinates[0] - singleStepOffset * %f) * %f;\n", optimizedOffset, boxWeight * 2.0];
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
[shaderString appendString:@"\
|
||||
gl_FragColor = sum;\n\
|
||||
}\n"];
|
||||
|
||||
return shaderString;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
[super setupFilterForSize:filterFrameSize];
|
||||
|
||||
if (shouldResizeBlurRadiusWithImageSize == YES)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
// NSString *currentGaussianBlurVertexShader = [GPUImageGaussianBlurFilter vertexShaderForStandardGaussianOfRadius:4 sigma:2.0];
|
||||
// NSString *currentGaussianBlurFragmentShader = [GPUImageGaussianBlurFilter fragmentShaderForStandardGaussianOfRadius:4 sigma:2.0];
|
||||
|
||||
NSString *currentBoxBlurVertexShader = [[self class] vertexShaderForOptimizedBlurOfRadius:4 sigma:0.0];
|
||||
NSString *currentBoxBlurFragmentShader = [[self class] fragmentShaderForOptimizedBlurOfRadius:4 sigma:0.0];
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:currentBoxBlurVertexShader firstStageFragmentShaderFromString:currentBoxBlurFragmentShader secondStageVertexShaderFromString:currentBoxBlurVertexShader secondStageFragmentShaderFromString:currentBoxBlurFragmentShader]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
_blurRadiusInPixels = 4.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
CGFloat newBlurRadius = round(round(newValue / 2.0) * 2.0); // For now, only do even radii
|
||||
|
||||
if (newBlurRadius != _blurRadiusInPixels)
|
||||
{
|
||||
_blurRadiusInPixels = newBlurRadius;
|
||||
|
||||
NSString *newGaussianBlurVertexShader = [[self class] vertexShaderForOptimizedBlurOfRadius:_blurRadiusInPixels sigma:0.0];
|
||||
NSString *newGaussianBlurFragmentShader = [[self class] fragmentShaderForOptimizedBlurOfRadius:_blurRadiusInPixels sigma:0.0];
|
||||
|
||||
// NSLog(@"Optimized vertex shader: \n%@", newGaussianBlurVertexShader);
|
||||
// NSLog(@"Optimized fragment shader: \n%@", newGaussianBlurFragmentShader);
|
||||
//
|
||||
[self switchToVertexShader:newGaussianBlurVertexShader fragmentShader:newGaussianBlurFragmentShader];
|
||||
}
|
||||
shouldResizeBlurRadiusWithImageSize = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageBrightnessFilter : GPUImageFilter
|
||||
{
|
||||
GLint brightnessUniform;
|
||||
}
|
||||
|
||||
// Brightness ranges from -1.0 to 1.0, with 0.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat brightness;
|
||||
|
||||
@end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#import "GPUImageBrightnessFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageBrightnessFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform lowp float brightness;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageBrightnessFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float brightness;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageBrightnessFilter
|
||||
|
||||
@synthesize brightness = _brightness;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageBrightnessFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
brightnessUniform = [filterProgram uniformIndex:@"brightness"];
|
||||
self.brightness = 0.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBrightness:(CGFloat)newValue;
|
||||
{
|
||||
_brightness = newValue;
|
||||
|
||||
[self setFloat:_brightness forUniform:brightnessUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageBuffer : GPUImageFilter
|
||||
{
|
||||
NSMutableArray *bufferedFramebuffers;
|
||||
}
|
||||
|
||||
@property(readwrite, nonatomic) NSUInteger bufferSize;
|
||||
|
||||
@end
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
#import "GPUImageBuffer.h"
|
||||
|
||||
@interface GPUImageBuffer()
|
||||
|
||||
@end
|
||||
|
||||
@implementation GPUImageBuffer
|
||||
|
||||
@synthesize bufferSize = _bufferSize;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithFragmentShaderFromString:kGPUImagePassthroughFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
bufferedFramebuffers = [[NSMutableArray alloc] init];
|
||||
// [bufferedTextures addObject:[NSNumber numberWithInt:outputTexture]];
|
||||
_bufferSize = 1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
for (GPUImageFramebuffer *currentFramebuffer in bufferedFramebuffers)
|
||||
{
|
||||
[currentFramebuffer unlock];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark GPUImageInput
|
||||
|
||||
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
if ([bufferedFramebuffers count] >= _bufferSize)
|
||||
{
|
||||
outputFramebuffer = [bufferedFramebuffers objectAtIndex:0];
|
||||
[bufferedFramebuffers removeObjectAtIndex:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing yet in the buffer, so don't process further until the buffer is full
|
||||
outputFramebuffer = firstInputFramebuffer;
|
||||
[firstInputFramebuffer lock];
|
||||
}
|
||||
|
||||
[bufferedFramebuffers addObject:firstInputFramebuffer];
|
||||
|
||||
// Need to pass along rotation information, as we're just holding on to buffered framebuffers and not rotating them ourselves
|
||||
for (id<GPUImageInput> currentTarget in targets)
|
||||
{
|
||||
if (currentTarget != self.targetToIgnoreForUpdates)
|
||||
{
|
||||
NSInteger indexOfObject = [targets indexOfObject:currentTarget];
|
||||
NSInteger textureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
|
||||
|
||||
[currentTarget setInputRotation:inputRotation atIndex:textureIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// Let the downstream video elements see the previous frame from the buffer before rendering a new one into place
|
||||
[self informTargetsAboutNewFrameAtTime:frameTime];
|
||||
|
||||
// [self renderToTextureWithVertices:imageVertices textureCoordinates:[[self class] textureCoordinatesForRotation:inputRotation]];
|
||||
}
|
||||
|
||||
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
|
||||
{
|
||||
// No need to render to another texture anymore, since we'll be hanging on to the textures in our buffer
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBufferSize:(NSUInteger)newValue;
|
||||
{
|
||||
if ( (newValue == _bufferSize) || (newValue < 1) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue > _bufferSize)
|
||||
{
|
||||
NSUInteger texturesToAdd = newValue - _bufferSize;
|
||||
for (NSUInteger currentTextureIndex = 0; currentTextureIndex < texturesToAdd; currentTextureIndex++)
|
||||
{
|
||||
// TODO: Deal with the growth of the size of the buffer by rotating framebuffers, no textures
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSUInteger texturesToRemove = _bufferSize - newValue;
|
||||
for (NSUInteger currentTextureIndex = 0; currentTextureIndex < texturesToRemove; currentTextureIndex++)
|
||||
{
|
||||
GPUImageFramebuffer *lastFramebuffer = [bufferedFramebuffers lastObject];
|
||||
[bufferedFramebuffers removeObjectAtIndex:([bufferedFramebuffers count] - 1)];
|
||||
|
||||
[lastFramebuffer unlock];
|
||||
lastFramebuffer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
_bufferSize = newValue;
|
||||
}
|
||||
|
||||
@end
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
/// Creates a bulge distortion on the image
|
||||
@interface GPUImageBulgeDistortionFilter : GPUImageFilter
|
||||
{
|
||||
GLint aspectRatioUniform, radiusUniform, centerUniform, scaleUniform;
|
||||
}
|
||||
|
||||
/// The center about which to apply the distortion, with a default of (0.5, 0.5)
|
||||
@property(readwrite, nonatomic) CGPoint center;
|
||||
/// The radius of the distortion, ranging from 0.0 to 1.0, with a default of 0.25
|
||||
@property(readwrite, nonatomic) CGFloat radius;
|
||||
/// The amount of distortion to apply, from -1.0 to 1.0, with a default of 0.5
|
||||
@property(readwrite, nonatomic) CGFloat scale;
|
||||
|
||||
@end
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
#import "GPUImageBulgeDistortionFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageBulgeDistortionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform highp float aspectRatio;
|
||||
uniform highp vec2 center;
|
||||
uniform highp float radius;
|
||||
uniform highp float scale;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec2 textureCoordinateToUse = vec2(textureCoordinate.x, ((textureCoordinate.y - center.y) * aspectRatio) + center.y);
|
||||
highp float dist = distance(center, textureCoordinateToUse);
|
||||
textureCoordinateToUse = textureCoordinate;
|
||||
|
||||
if (dist < radius)
|
||||
{
|
||||
textureCoordinateToUse -= center;
|
||||
highp float percent = 1.0 - ((radius - dist) / radius) * scale;
|
||||
percent = percent * percent;
|
||||
|
||||
textureCoordinateToUse = textureCoordinateToUse * percent;
|
||||
textureCoordinateToUse += center;
|
||||
}
|
||||
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse );
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageBulgeDistortionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform float aspectRatio;
|
||||
uniform vec2 center;
|
||||
uniform float radius;
|
||||
uniform float scale;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 textureCoordinateToUse = vec2(textureCoordinate.x, ((textureCoordinate.y - center.y) * aspectRatio) + center.y);
|
||||
float dist = distance(center, textureCoordinateToUse);
|
||||
textureCoordinateToUse = textureCoordinate;
|
||||
|
||||
if (dist < radius)
|
||||
{
|
||||
textureCoordinateToUse -= center;
|
||||
float percent = 1.0 - ((radius - dist) / radius) * scale;
|
||||
percent = percent * percent;
|
||||
|
||||
textureCoordinateToUse = textureCoordinateToUse * percent;
|
||||
textureCoordinateToUse += center;
|
||||
}
|
||||
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse );
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@interface GPUImageBulgeDistortionFilter ()
|
||||
|
||||
- (void)adjustAspectRatio;
|
||||
|
||||
@property (readwrite, nonatomic) CGFloat aspectRatio;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GPUImageBulgeDistortionFilter
|
||||
|
||||
@synthesize aspectRatio = _aspectRatio;
|
||||
@synthesize center = _center;
|
||||
@synthesize radius = _radius;
|
||||
@synthesize scale = _scale;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageBulgeDistortionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
aspectRatioUniform = [filterProgram uniformIndex:@"aspectRatio"];
|
||||
radiusUniform = [filterProgram uniformIndex:@"radius"];
|
||||
scaleUniform = [filterProgram uniformIndex:@"scale"];
|
||||
centerUniform = [filterProgram uniformIndex:@"center"];
|
||||
|
||||
self.radius = 0.25;
|
||||
self.scale = 0.5;
|
||||
self.center = CGPointMake(0.5, 0.5);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)adjustAspectRatio;
|
||||
{
|
||||
if (GPUImageRotationSwapsWidthAndHeight(inputRotation))
|
||||
{
|
||||
[self setAspectRatio:(inputTextureSize.width / inputTextureSize.height)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setAspectRatio:(inputTextureSize.height / inputTextureSize.width)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forceProcessingAtSize:(CGSize)frameSize;
|
||||
{
|
||||
[super forceProcessingAtSize:frameSize];
|
||||
[self adjustAspectRatio];
|
||||
}
|
||||
|
||||
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
CGSize oldInputSize = inputTextureSize;
|
||||
[super setInputSize:newSize atIndex:textureIndex];
|
||||
|
||||
if ( (!CGSizeEqualToSize(oldInputSize, inputTextureSize)) && (!CGSizeEqualToSize(newSize, CGSizeZero)) )
|
||||
{
|
||||
[self adjustAspectRatio];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAspectRatio:(CGFloat)newValue;
|
||||
{
|
||||
_aspectRatio = newValue;
|
||||
|
||||
[self setFloat:_aspectRatio forUniform:aspectRatioUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
[super setInputRotation:newInputRotation atIndex:textureIndex];
|
||||
[self setCenter:self.center];
|
||||
[self adjustAspectRatio];
|
||||
}
|
||||
|
||||
- (void)setRadius:(CGFloat)newValue;
|
||||
{
|
||||
_radius = newValue;
|
||||
|
||||
[self setFloat:_radius forUniform:radiusUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setScale:(CGFloat)newValue;
|
||||
{
|
||||
_scale = newValue;
|
||||
|
||||
[self setFloat:_scale forUniform:scaleUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setCenter:(CGPoint)newValue;
|
||||
{
|
||||
_center = newValue;
|
||||
|
||||
CGPoint rotatedPoint = [self rotatedPoint:_center forRotation:inputRotation];
|
||||
|
||||
[self setPoint:rotatedPoint forUniform:centerUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCGAColorspaceFilter : GPUImageFilter
|
||||
|
||||
@end
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// GPUImageCGAColorspaceFilter.m
|
||||
//
|
||||
|
||||
#import "GPUImageCGAColorspaceFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCGAColorspaceFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec2 sampleDivisor = vec2(1.0 / 200.0, 1.0 / 320.0);
|
||||
//highp vec4 colorDivisor = vec4(colorDepth);
|
||||
|
||||
highp vec2 samplePos = textureCoordinate - mod(textureCoordinate, sampleDivisor);
|
||||
highp vec4 color = texture2D(inputImageTexture, samplePos );
|
||||
|
||||
//gl_FragColor = texture2D(inputImageTexture, samplePos );
|
||||
mediump vec4 colorCyan = vec4(85.0 / 255.0, 1.0, 1.0, 1.0);
|
||||
mediump vec4 colorMagenta = vec4(1.0, 85.0 / 255.0, 1.0, 1.0);
|
||||
mediump vec4 colorWhite = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
mediump vec4 colorBlack = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
mediump vec4 endColor;
|
||||
highp float blackDistance = distance(color, colorBlack);
|
||||
highp float whiteDistance = distance(color, colorWhite);
|
||||
highp float magentaDistance = distance(color, colorMagenta);
|
||||
highp float cyanDistance = distance(color, colorCyan);
|
||||
|
||||
mediump vec4 finalColor;
|
||||
|
||||
highp float colorDistance = min(magentaDistance, cyanDistance);
|
||||
colorDistance = min(colorDistance, whiteDistance);
|
||||
colorDistance = min(colorDistance, blackDistance);
|
||||
|
||||
if (colorDistance == blackDistance) {
|
||||
finalColor = colorBlack;
|
||||
} else if (colorDistance == whiteDistance) {
|
||||
finalColor = colorWhite;
|
||||
} else if (colorDistance == cyanDistance) {
|
||||
finalColor = colorCyan;
|
||||
} else {
|
||||
finalColor = colorMagenta;
|
||||
}
|
||||
|
||||
gl_FragColor = finalColor;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCGAColorspaceFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 sampleDivisor = vec2(1.0 / 200.0, 1.0 / 320.0);
|
||||
//highp vec4 colorDivisor = vec4(colorDepth);
|
||||
|
||||
vec2 samplePos = textureCoordinate - mod(textureCoordinate, sampleDivisor);
|
||||
vec4 color = texture2D(inputImageTexture, samplePos );
|
||||
|
||||
//gl_FragColor = texture2D(inputImageTexture, samplePos );
|
||||
vec4 colorCyan = vec4(85.0 / 255.0, 1.0, 1.0, 1.0);
|
||||
vec4 colorMagenta = vec4(1.0, 85.0 / 255.0, 1.0, 1.0);
|
||||
vec4 colorWhite = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
vec4 colorBlack = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
vec4 endColor;
|
||||
float blackDistance = distance(color, colorBlack);
|
||||
float whiteDistance = distance(color, colorWhite);
|
||||
float magentaDistance = distance(color, colorMagenta);
|
||||
float cyanDistance = distance(color, colorCyan);
|
||||
|
||||
vec4 finalColor;
|
||||
|
||||
float colorDistance = min(magentaDistance, cyanDistance);
|
||||
colorDistance = min(colorDistance, whiteDistance);
|
||||
colorDistance = min(colorDistance, blackDistance);
|
||||
|
||||
if (colorDistance == blackDistance) {
|
||||
finalColor = colorBlack;
|
||||
} else if (colorDistance == whiteDistance) {
|
||||
finalColor = colorWhite;
|
||||
} else if (colorDistance == cyanDistance) {
|
||||
finalColor = colorCyan;
|
||||
} else {
|
||||
finalColor = colorMagenta;
|
||||
}
|
||||
|
||||
gl_FragColor = finalColor;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageCGAColorspaceFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageCGAColorspaceFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@class GPUImageGrayscaleFilter;
|
||||
@class GPUImageSingleComponentGaussianBlurFilter;
|
||||
@class GPUImageDirectionalSobelEdgeDetectionFilter;
|
||||
@class GPUImageDirectionalNonMaximumSuppressionFilter;
|
||||
@class GPUImageWeakPixelInclusionFilter;
|
||||
|
||||
/** This applies the edge detection process described by John Canny in
|
||||
|
||||
Canny, J., A Computational Approach To Edge Detection, IEEE Trans. Pattern Analysis and Machine Intelligence, 8(6):679–698, 1986.
|
||||
|
||||
and implemented in OpenGL ES by
|
||||
|
||||
A. Ensor, S. Hall. GPU-based Image Analysis on Mobile Devices. Proceedings of Image and Vision Computing New Zealand 2011.
|
||||
|
||||
It starts with a conversion to luminance, followed by an accelerated 9-hit Gaussian blur. A Sobel operator is applied to obtain the overall
|
||||
gradient strength in the blurred image, as well as the direction (in texture sampling steps) of the gradient. A non-maximum suppression filter
|
||||
acts along the direction of the gradient, highlighting strong edges that pass the threshold and completely removing those that fail the lower
|
||||
threshold. Finally, pixels from in-between these thresholds are either included in edges or rejected based on neighboring pixels.
|
||||
*/
|
||||
@interface GPUImageCannyEdgeDetectionFilter : GPUImageFilterGroup
|
||||
{
|
||||
GPUImageGrayscaleFilter *luminanceFilter;
|
||||
GPUImageSingleComponentGaussianBlurFilter *blurFilter;
|
||||
GPUImageDirectionalSobelEdgeDetectionFilter *edgeDetectionFilter;
|
||||
GPUImageDirectionalNonMaximumSuppressionFilter *nonMaximumSuppressionFilter;
|
||||
GPUImageWeakPixelInclusionFilter *weakPixelInclusionFilter;
|
||||
}
|
||||
|
||||
/** The image width and height factors tweak the appearance of the edges.
|
||||
|
||||
These parameters affect the visibility of the detected edges
|
||||
|
||||
By default, they match the inverse of the filter size in pixels
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
/** The image width and height factors tweak the appearance of the edges.
|
||||
|
||||
These parameters affect the visibility of the detected edges
|
||||
|
||||
By default, they match the inverse of the filter size in pixels
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
/** The underlying blur radius for the Gaussian blur. Default is 2.0.
|
||||
*/
|
||||
@property (readwrite, nonatomic) CGFloat blurRadiusInPixels;
|
||||
|
||||
/** The underlying blur texel spacing multiplier. Default is 1.0.
|
||||
*/
|
||||
@property (readwrite, nonatomic) CGFloat blurTexelSpacingMultiplier;
|
||||
|
||||
/** Any edge with a gradient magnitude above this threshold will pass and show up in the final result.
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat upperThreshold;
|
||||
|
||||
/** Any edge with a gradient magnitude below this threshold will fail and be removed from the final result.
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat lowerThreshold;
|
||||
|
||||
@end
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
#import "GPUImageCannyEdgeDetectionFilter.h"
|
||||
|
||||
#import "GPUImageGrayscaleFilter.h"
|
||||
#import "GPUImageDirectionalSobelEdgeDetectionFilter.h"
|
||||
#import "GPUImageDirectionalNonMaximumSuppressionFilter.h"
|
||||
#import "GPUImageWeakPixelInclusionFilter.h"
|
||||
#import "GPUImageSingleComponentGaussianBlurFilter.h"
|
||||
|
||||
@implementation GPUImageCannyEdgeDetectionFilter
|
||||
|
||||
@synthesize upperThreshold;
|
||||
@synthesize lowerThreshold;
|
||||
@synthesize blurRadiusInPixels;
|
||||
@synthesize blurTexelSpacingMultiplier;
|
||||
@synthesize texelWidth;
|
||||
@synthesize texelHeight;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: convert image to luminance
|
||||
luminanceFilter = [[GPUImageGrayscaleFilter alloc] init];
|
||||
[self addFilter:luminanceFilter];
|
||||
|
||||
// Second pass: apply a variable Gaussian blur
|
||||
blurFilter = [[GPUImageSingleComponentGaussianBlurFilter alloc] init];
|
||||
[self addFilter:blurFilter];
|
||||
|
||||
// Third pass: run the Sobel edge detection, with calculated gradient directions, on this blurred image
|
||||
edgeDetectionFilter = [[GPUImageDirectionalSobelEdgeDetectionFilter alloc] init];
|
||||
[self addFilter:edgeDetectionFilter];
|
||||
|
||||
// Fourth pass: apply non-maximum suppression
|
||||
nonMaximumSuppressionFilter = [[GPUImageDirectionalNonMaximumSuppressionFilter alloc] init];
|
||||
[self addFilter:nonMaximumSuppressionFilter];
|
||||
|
||||
// Fifth pass: include weak pixels to complete edges
|
||||
weakPixelInclusionFilter = [[GPUImageWeakPixelInclusionFilter alloc] init];
|
||||
[self addFilter:weakPixelInclusionFilter];
|
||||
|
||||
[luminanceFilter addTarget:blurFilter];
|
||||
[blurFilter addTarget:edgeDetectionFilter];
|
||||
[edgeDetectionFilter addTarget:nonMaximumSuppressionFilter];
|
||||
[nonMaximumSuppressionFilter addTarget:weakPixelInclusionFilter];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObject:luminanceFilter];
|
||||
// self.terminalFilter = nonMaximumSuppressionFilter;
|
||||
self.terminalFilter = weakPixelInclusionFilter;
|
||||
|
||||
self.blurRadiusInPixels = 2.0;
|
||||
self.blurTexelSpacingMultiplier = 1.0;
|
||||
self.upperThreshold = 0.4;
|
||||
self.lowerThreshold = 0.1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
blurFilter.blurRadiusInPixels = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurRadiusInPixels;
|
||||
{
|
||||
return blurFilter.blurRadiusInPixels;
|
||||
}
|
||||
|
||||
- (void)setBlurTexelSpacingMultiplier:(CGFloat)newValue;
|
||||
{
|
||||
blurFilter.texelSpacingMultiplier = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurTexelSpacingMultiplier;
|
||||
{
|
||||
return blurFilter.texelSpacingMultiplier;
|
||||
}
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
edgeDetectionFilter.texelWidth = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)texelWidth;
|
||||
{
|
||||
return edgeDetectionFilter.texelWidth;
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
edgeDetectionFilter.texelHeight = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)texelHeight;
|
||||
{
|
||||
return edgeDetectionFilter.texelHeight;
|
||||
}
|
||||
|
||||
- (void)setUpperThreshold:(CGFloat)newValue;
|
||||
{
|
||||
nonMaximumSuppressionFilter.upperThreshold = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)upperThreshold;
|
||||
{
|
||||
return nonMaximumSuppressionFilter.upperThreshold;
|
||||
}
|
||||
|
||||
- (void)setLowerThreshold:(CGFloat)newValue;
|
||||
{
|
||||
nonMaximumSuppressionFilter.lowerThreshold = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)lowerThreshold;
|
||||
{
|
||||
return nonMaximumSuppressionFilter.lowerThreshold;
|
||||
}
|
||||
|
||||
@end
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
/** Selectively replaces a color in the first image with the second image
|
||||
*/
|
||||
@interface GPUImageChromaKeyBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint colorToReplaceUniform, thresholdSensitivityUniform, smoothingUniform;
|
||||
}
|
||||
|
||||
/** The threshold sensitivity controls how similar pixels need to be colored to be replaced
|
||||
|
||||
The default value is 0.3
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat thresholdSensitivity;
|
||||
|
||||
/** The degree of smoothing controls how gradually similar colors are replaced in the image
|
||||
|
||||
The default value is 0.1
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat smoothing;
|
||||
|
||||
/** The color to be replaced is specified using individual red, green, and blue components (normalized to 1.0).
|
||||
|
||||
The default is green: (0.0, 1.0, 0.0).
|
||||
|
||||
@param redComponent Red component of color to be replaced
|
||||
@param greenComponent Green component of color to be replaced
|
||||
@param blueComponent Blue component of color to be replaced
|
||||
*/
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
|
||||
@end
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
#import "GPUImageChromaKeyBlendFilter.h"
|
||||
|
||||
// Shader code based on Apple's CIChromaKeyFilter example: https://developer.apple.com/library/mac/#samplecode/CIChromaKeyFilter/Introduction/Intro.html
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageChromaKeyBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = 1.0 - smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = mix(textureColor, textureColor2, blendValue);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageChromaKeyBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = 1.0 - smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = mix(textureColor, textureColor2, blendValue);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageChromaKeyBlendFilter
|
||||
|
||||
@synthesize thresholdSensitivity = _thresholdSensitivity;
|
||||
@synthesize smoothing = _smoothing;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageChromaKeyBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
thresholdSensitivityUniform = [filterProgram uniformIndex:@"thresholdSensitivity"];
|
||||
smoothingUniform = [filterProgram uniformIndex:@"smoothing"];
|
||||
colorToReplaceUniform = [filterProgram uniformIndex:@"colorToReplace"];
|
||||
|
||||
self.thresholdSensitivity = 0.4;
|
||||
self.smoothing = 0.1;
|
||||
[self setColorToReplaceRed:0.0 green:1.0 blue:0.0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 colorToReplace = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:colorToReplace forUniform:colorToReplaceUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setThresholdSensitivity:(CGFloat)newValue;
|
||||
{
|
||||
_thresholdSensitivity = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_thresholdSensitivity forUniform:thresholdSensitivityUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setSmoothing:(CGFloat)newValue;
|
||||
{
|
||||
_smoothing = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_smoothing forUniform:smoothingUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageChromaKeyFilter : GPUImageFilter
|
||||
{
|
||||
GLint colorToReplaceUniform, thresholdSensitivityUniform, smoothingUniform;
|
||||
}
|
||||
|
||||
/** The threshold sensitivity controls how similar pixels need to be colored to be replaced
|
||||
|
||||
The default value is 0.3
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat thresholdSensitivity;
|
||||
|
||||
/** The degree of smoothing controls how gradually similar colors are replaced in the image
|
||||
|
||||
The default value is 0.1
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat smoothing;
|
||||
|
||||
/** The color to be replaced is specified using individual red, green, and blue components (normalized to 1.0).
|
||||
|
||||
The default is green: (0.0, 1.0, 0.0).
|
||||
|
||||
@param redComponent Red component of color to be replaced
|
||||
@param greenComponent Green component of color to be replaced
|
||||
@param blueComponent Blue component of color to be replaced
|
||||
*/
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
|
||||
@end
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
#import "GPUImageChromaKeyFilter.h"
|
||||
|
||||
// Shader code based on Apple's CIChromaKeyFilter example: https://developer.apple.com/library/mac/#samplecode/CIChromaKeyFilter/Introduction/Intro.html
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageChromaKeyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = vec4(textureColor.rgb, textureColor.a * blendValue);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageChromaKeyFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform float thresholdSensitivity;
|
||||
uniform float smoothing;
|
||||
uniform vec3 colorToReplace;
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
float maskY = 0.2989 * colorToReplace.r + 0.5866 * colorToReplace.g + 0.1145 * colorToReplace.b;
|
||||
float maskCr = 0.7132 * (colorToReplace.r - maskY);
|
||||
float maskCb = 0.5647 * (colorToReplace.b - maskY);
|
||||
|
||||
float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
|
||||
float Cr = 0.7132 * (textureColor.r - Y);
|
||||
float Cb = 0.5647 * (textureColor.b - Y);
|
||||
|
||||
// float blendValue = 1.0 - smoothstep(thresholdSensitivity - smoothing, thresholdSensitivity , abs(Cr - maskCr) + abs(Cb - maskCb));
|
||||
float blendValue = smoothstep(thresholdSensitivity, thresholdSensitivity + smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
|
||||
gl_FragColor = vec4(textureColor.rgb, textureColor.a * blendValue);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageChromaKeyFilter
|
||||
|
||||
@synthesize thresholdSensitivity = _thresholdSensitivity;
|
||||
@synthesize smoothing = _smoothing;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageChromaKeyFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
thresholdSensitivityUniform = [filterProgram uniformIndex:@"thresholdSensitivity"];
|
||||
smoothingUniform = [filterProgram uniformIndex:@"smoothing"];
|
||||
colorToReplaceUniform = [filterProgram uniformIndex:@"colorToReplace"];
|
||||
|
||||
self.thresholdSensitivity = 0.4;
|
||||
self.smoothing = 0.1;
|
||||
[self setColorToReplaceRed:0.0 green:1.0 blue:0.0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setColorToReplaceRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 colorToReplace = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:colorToReplace forUniform:colorToReplaceUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setThresholdSensitivity:(CGFloat)newValue;
|
||||
{
|
||||
_thresholdSensitivity = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_thresholdSensitivity forUniform:thresholdSensitivityUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setSmoothing:(CGFloat)newValue;
|
||||
{
|
||||
_smoothing = newValue;
|
||||
|
||||
[self setFloat:(GLfloat)_smoothing forUniform:smoothingUniform program:filterProgram];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
@class GPUImageErosionFilter;
|
||||
@class GPUImageDilationFilter;
|
||||
|
||||
// A filter that first performs a dilation on the red channel of an image, followed by an erosion of the same radius.
|
||||
// This helps to filter out smaller dark elements.
|
||||
|
||||
@interface GPUImageClosingFilter : GPUImageFilterGroup
|
||||
{
|
||||
GPUImageErosionFilter *erosionFilter;
|
||||
GPUImageDilationFilter *dilationFilter;
|
||||
}
|
||||
|
||||
@property(readwrite, nonatomic) CGFloat verticalTexelSpacing, horizontalTexelSpacing;
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)radius;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,57 @@
|
||||
#import "GPUImageClosingFilter.h"
|
||||
#import "GPUImageErosionFilter.h"
|
||||
#import "GPUImageDilationFilter.h"
|
||||
|
||||
@implementation GPUImageClosingFilter
|
||||
|
||||
@synthesize verticalTexelSpacing = _verticalTexelSpacing;
|
||||
@synthesize horizontalTexelSpacing = _horizontalTexelSpacing;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithRadius:1]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)radius;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: dilation
|
||||
dilationFilter = [[GPUImageDilationFilter alloc] initWithRadius:radius];
|
||||
[self addFilter:dilationFilter];
|
||||
|
||||
// Second pass: erosion
|
||||
erosionFilter = [[GPUImageErosionFilter alloc] initWithRadius:radius];
|
||||
[self addFilter:erosionFilter];
|
||||
|
||||
[dilationFilter addTarget:erosionFilter];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:dilationFilter, nil];
|
||||
self.terminalFilter = erosionFilter;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setVerticalTexelSpacing:(CGFloat)newValue;
|
||||
{
|
||||
_verticalTexelSpacing = newValue;
|
||||
erosionFilter.verticalTexelSpacing = newValue;
|
||||
dilationFilter.verticalTexelSpacing = newValue;
|
||||
}
|
||||
|
||||
- (void)setHorizontalTexelSpacing:(CGFloat)newValue;
|
||||
{
|
||||
_horizontalTexelSpacing = newValue;
|
||||
erosionFilter.horizontalTexelSpacing = newValue;
|
||||
dilationFilter.horizontalTexelSpacing = newValue;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageColorBlendFilter : GPUImageTwoInputFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,113 @@
|
||||
#import "GPUImageColorBlendFilter.h"
|
||||
|
||||
/**
|
||||
* Color blend mode based upon pseudo code from the PDF specification.
|
||||
*/
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
highp float lum(lowp vec3 c) {
|
||||
return dot(c, vec3(0.3, 0.59, 0.11));
|
||||
}
|
||||
|
||||
lowp vec3 clipcolor(lowp vec3 c) {
|
||||
highp float l = lum(c);
|
||||
lowp float n = min(min(c.r, c.g), c.b);
|
||||
lowp float x = max(max(c.r, c.g), c.b);
|
||||
|
||||
if (n < 0.0) {
|
||||
c.r = l + ((c.r - l) * l) / (l - n);
|
||||
c.g = l + ((c.g - l) * l) / (l - n);
|
||||
c.b = l + ((c.b - l) * l) / (l - n);
|
||||
}
|
||||
if (x > 1.0) {
|
||||
c.r = l + ((c.r - l) * (1.0 - l)) / (x - l);
|
||||
c.g = l + ((c.g - l) * (1.0 - l)) / (x - l);
|
||||
c.b = l + ((c.b - l) * (1.0 - l)) / (x - l);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
lowp vec3 setlum(lowp vec3 c, highp float l) {
|
||||
highp float d = l - lum(c);
|
||||
c = c + vec3(d);
|
||||
return clipcolor(c);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 baseColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
highp vec4 overlayColor = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(baseColor.rgb * (1.0 - overlayColor.a) + setlum(overlayColor.rgb, lum(baseColor.rgb)) * overlayColor.a, baseColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
float lum(vec3 c) {
|
||||
return dot(c, vec3(0.3, 0.59, 0.11));
|
||||
}
|
||||
|
||||
vec3 clipcolor(vec3 c) {
|
||||
float l = lum(c);
|
||||
float n = min(min(c.r, c.g), c.b);
|
||||
float x = max(max(c.r, c.g), c.b);
|
||||
|
||||
if (n < 0.0) {
|
||||
c.r = l + ((c.r - l) * l) / (l - n);
|
||||
c.g = l + ((c.g - l) * l) / (l - n);
|
||||
c.b = l + ((c.b - l) * l) / (l - n);
|
||||
}
|
||||
if (x > 1.0) {
|
||||
c.r = l + ((c.r - l) * (1.0 - l)) / (x - l);
|
||||
c.g = l + ((c.g - l) * (1.0 - l)) / (x - l);
|
||||
c.b = l + ((c.b - l) * (1.0 - l)) / (x - l);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
vec3 setlum(vec3 c, float l) {
|
||||
float d = l - lum(c);
|
||||
c = c + vec3(d);
|
||||
return clipcolor(c);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 baseColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlayColor = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(baseColor.rgb * (1.0 - overlayColor.a) + setlum(overlayColor.rgb, lum(baseColor.rgb)) * overlayColor.a, baseColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@implementation GPUImageColorBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
/** Applies a color burn blend of two images
|
||||
*/
|
||||
@interface GPUImageColorBurnBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#import "GPUImageColorBurnBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorBurnBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
mediump vec4 whiteColor = vec4(1.0);
|
||||
gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorBurnBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
vec4 whiteColor = vec4(1.0);
|
||||
gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorBurnBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorBurnBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef GPUImageColorConversion_h
|
||||
#define GPUImageColorConversion_h
|
||||
|
||||
extern GLfloat *kColorConversion601;
|
||||
extern GLfloat *kColorConversion601FullRange;
|
||||
extern GLfloat *kColorConversion709;
|
||||
extern NSString *const kGPUImageYUVVideoRangeConversionForRGFragmentShaderString;
|
||||
extern NSString *const kGPUImageYUVFullRangeConversionForLAFragmentShaderString;
|
||||
extern NSString *const kGPUImageYUVVideoRangeConversionForLAFragmentShaderString;
|
||||
|
||||
|
||||
#endif /* GPUImageColorConversion_h */
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
// Color Conversion Constants (YUV to RGB) including adjustment from 16-235/16-240 (video range)
|
||||
|
||||
// BT.601, which is the standard for SDTV.
|
||||
GLfloat kColorConversion601Default[] = {
|
||||
1.164, 1.164, 1.164,
|
||||
0.0, -0.392, 2.017,
|
||||
1.596, -0.813, 0.0,
|
||||
};
|
||||
|
||||
// BT.601 full range (ref: http://www.equasys.de/colorconversion.html)
|
||||
GLfloat kColorConversion601FullRangeDefault[] = {
|
||||
1.0, 1.0, 1.0,
|
||||
0.0, -0.343, 1.765,
|
||||
1.4, -0.711, 0.0,
|
||||
};
|
||||
|
||||
// BT.709, which is the standard for HDTV.
|
||||
GLfloat kColorConversion709Default[] = {
|
||||
1.164, 1.164, 1.164,
|
||||
0.0, -0.213, 2.112,
|
||||
1.793, -0.533, 0.0,
|
||||
};
|
||||
|
||||
|
||||
GLfloat *kColorConversion601 = kColorConversion601Default;
|
||||
GLfloat *kColorConversion601FullRange = kColorConversion601FullRangeDefault;
|
||||
GLfloat *kColorConversion709 = kColorConversion709Default;
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForRGFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
uniform mediump mat3 colorConversionMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 yuv;
|
||||
lowp vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).rg - vec2(0.5, 0.5);
|
||||
rgb = colorConversionMatrix * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForRGFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 yuv;
|
||||
vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).rg - vec2(0.5, 0.5);
|
||||
|
||||
// BT.601, which is the standard for SDTV is provided as a reference
|
||||
/*
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.39465, 2.03211,
|
||||
1.13983, -.58060, 0) * yuv;
|
||||
*/
|
||||
|
||||
// Using BT.709 which is the standard for HDTV
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.21482, 2.12798,
|
||||
1.28033, -.38059, 0) * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
NSString *const kGPUImageYUVFullRangeConversionForLAFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
uniform mediump mat3 colorConversionMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 yuv;
|
||||
lowp vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).ra - vec2(0.5, 0.5);
|
||||
rgb = colorConversionMatrix * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForLAFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
uniform mediump mat3 colorConversionMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec3 yuv;
|
||||
lowp vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r - (16.0/255.0);
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).ra - vec2(0.5, 0.5);
|
||||
rgb = colorConversionMatrix * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageYUVVideoRangeConversionForLAFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D luminanceTexture;
|
||||
uniform sampler2D chrominanceTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 yuv;
|
||||
vec3 rgb;
|
||||
|
||||
yuv.x = texture2D(luminanceTexture, textureCoordinate).r;
|
||||
yuv.yz = texture2D(chrominanceTexture, textureCoordinate).ra - vec2(0.5, 0.5);
|
||||
|
||||
// BT.601, which is the standard for SDTV is provided as a reference
|
||||
/*
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.39465, 2.03211,
|
||||
1.13983, -.58060, 0) * yuv;
|
||||
*/
|
||||
|
||||
// Using BT.709 which is the standard for HDTV
|
||||
rgb = mat3( 1, 1, 1,
|
||||
0, -.21482, 2.12798,
|
||||
1.28033, -.38059, 0) * yuv;
|
||||
|
||||
gl_FragColor = vec4(rgb, 1);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
/** Applies a color dodge blend of two images
|
||||
*/
|
||||
@interface GPUImageColorDodgeBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
#import "GPUImageColorDodgeBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorDodgeBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
|
||||
precision mediump float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
vec3 baseOverlayAlphaProduct = vec3(overlay.a * base.a);
|
||||
vec3 rightHandProduct = overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a);
|
||||
|
||||
vec3 firstBlendColor = baseOverlayAlphaProduct + rightHandProduct;
|
||||
vec3 overlayRGB = clamp((overlay.rgb / clamp(overlay.a, 0.01, 1.0)) * step(0.0, overlay.a), 0.0, 0.99);
|
||||
|
||||
vec3 secondBlendColor = (base.rgb * overlay.a) / (1.0 - overlayRGB) + rightHandProduct;
|
||||
|
||||
vec3 colorChoice = step((overlay.rgb * base.a + base.rgb * overlay.a), baseOverlayAlphaProduct);
|
||||
|
||||
gl_FragColor = vec4(mix(firstBlendColor, secondBlendColor, colorChoice), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorDodgeBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
vec3 baseOverlayAlphaProduct = vec3(overlay.a * base.a);
|
||||
vec3 rightHandProduct = overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a);
|
||||
|
||||
vec3 firstBlendColor = baseOverlayAlphaProduct + rightHandProduct;
|
||||
vec3 overlayRGB = clamp((overlay.rgb / clamp(overlay.a, 0.01, 1.0)) * step(0.0, overlay.a), 0.0, 0.99);
|
||||
|
||||
vec3 secondBlendColor = (base.rgb * overlay.a) / (1.0 - overlayRGB) + rightHandProduct;
|
||||
|
||||
vec3 colorChoice = step((overlay.rgb * base.a + base.rgb * overlay.a), baseOverlayAlphaProduct);
|
||||
|
||||
gl_FragColor = vec4(mix(firstBlendColor, secondBlendColor, colorChoice), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorDodgeBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorDodgeBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageColorInvertFilter : GPUImageFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#import "GPUImageColorInvertFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageInvertFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4((1.0 - textureColor.rgb), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorInvertFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageInvertFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
@interface GPUImageColorLocalBinaryPatternFilter : GPUImage3x3TextureSamplingFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,159 @@
|
||||
#import "GPUImageColorLocalBinaryPatternFilter.h"
|
||||
|
||||
// This is based on "Accelerating image recognition on mobile devices using GPGPU" by Miguel Bordallo Lopez, Henri Nykanen, Jari Hannuksela, Olli Silven and Markku Vehvilainen
|
||||
// http://www.ee.oulu.fi/~jhannuks/publications/SPIE2011a.pdf
|
||||
|
||||
// Right pixel is the most significant bit, traveling clockwise to get to the upper right, which is the least significant
|
||||
// If the external pixel is greater than or equal to the center, set to 1, otherwise 0
|
||||
//
|
||||
// 2 1 0
|
||||
// 3 7
|
||||
// 4 5 6
|
||||
|
||||
// 01101101
|
||||
// 76543210
|
||||
|
||||
@implementation GPUImageColorLocalBinaryPatternFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorLocalBinaryPatternFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
lowp vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
lowp vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
lowp vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
lowp vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
lowp vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
lowp vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
lowp vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
lowp vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
|
||||
lowp float redByteTally = 1.0 / 255.0 * step(centerColor.r, topRightColor.r);
|
||||
redByteTally += 2.0 / 255.0 * step(centerColor.r, topColor.r);
|
||||
redByteTally += 4.0 / 255.0 * step(centerColor.r, topLeftColor.r);
|
||||
redByteTally += 8.0 / 255.0 * step(centerColor.r, leftColor.r);
|
||||
redByteTally += 16.0 / 255.0 * step(centerColor.r, bottomLeftColor.r);
|
||||
redByteTally += 32.0 / 255.0 * step(centerColor.r, bottomColor.r);
|
||||
redByteTally += 64.0 / 255.0 * step(centerColor.r, bottomRightColor.r);
|
||||
redByteTally += 128.0 / 255.0 * step(centerColor.r, rightColor.r);
|
||||
|
||||
lowp float blueByteTally = 1.0 / 255.0 * step(centerColor.b, topRightColor.b);
|
||||
blueByteTally += 2.0 / 255.0 * step(centerColor.b, topColor.b);
|
||||
blueByteTally += 4.0 / 255.0 * step(centerColor.b, topLeftColor.b);
|
||||
blueByteTally += 8.0 / 255.0 * step(centerColor.b, leftColor.b);
|
||||
blueByteTally += 16.0 / 255.0 * step(centerColor.b, bottomLeftColor.b);
|
||||
blueByteTally += 32.0 / 255.0 * step(centerColor.b, bottomColor.b);
|
||||
blueByteTally += 64.0 / 255.0 * step(centerColor.b, bottomRightColor.b);
|
||||
blueByteTally += 128.0 / 255.0 * step(centerColor.b, rightColor.b);
|
||||
|
||||
lowp float greenByteTally = 1.0 / 255.0 * step(centerColor.g, topRightColor.g);
|
||||
greenByteTally += 2.0 / 255.0 * step(centerColor.g, topColor.g);
|
||||
greenByteTally += 4.0 / 255.0 * step(centerColor.g, topLeftColor.g);
|
||||
greenByteTally += 8.0 / 255.0 * step(centerColor.g, leftColor.g);
|
||||
greenByteTally += 16.0 / 255.0 * step(centerColor.g, bottomLeftColor.g);
|
||||
greenByteTally += 32.0 / 255.0 * step(centerColor.g, bottomColor.g);
|
||||
greenByteTally += 64.0 / 255.0 * step(centerColor.g, bottomRightColor.g);
|
||||
greenByteTally += 128.0 / 255.0 * step(centerColor.g, rightColor.g);
|
||||
|
||||
// TODO: Replace the above with a dot product and two vec4s
|
||||
// TODO: Apply step to a matrix, rather than individually
|
||||
|
||||
gl_FragColor = vec4(redByteTally, blueByteTally, greenByteTally, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorLocalBinaryPatternFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
vec3 bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
|
||||
vec3 topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
|
||||
vec3 topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
|
||||
vec3 bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
|
||||
vec3 leftColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
|
||||
vec3 rightColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
|
||||
vec3 bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
|
||||
vec3 topColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
|
||||
|
||||
float redByteTally = 1.0 / 255.0 * step(centerColor.r, topRightColor.r);
|
||||
redByteTally += 2.0 / 255.0 * step(centerColor.r, topColor.r);
|
||||
redByteTally += 4.0 / 255.0 * step(centerColor.r, topLeftColor.r);
|
||||
redByteTally += 8.0 / 255.0 * step(centerColor.r, leftColor.r);
|
||||
redByteTally += 16.0 / 255.0 * step(centerColor.r, bottomLeftColor.r);
|
||||
redByteTally += 32.0 / 255.0 * step(centerColor.r, bottomColor.r);
|
||||
redByteTally += 64.0 / 255.0 * step(centerColor.r, bottomRightColor.r);
|
||||
redByteTally += 128.0 / 255.0 * step(centerColor.r, rightColor.r);
|
||||
|
||||
float blueByteTally = 1.0 / 255.0 * step(centerColor.b, topRightColor.b);
|
||||
blueByteTally += 2.0 / 255.0 * step(centerColor.b, topColor.b);
|
||||
blueByteTally += 4.0 / 255.0 * step(centerColor.b, topLeftColor.b);
|
||||
blueByteTally += 8.0 / 255.0 * step(centerColor.b, leftColor.b);
|
||||
blueByteTally += 16.0 / 255.0 * step(centerColor.b, bottomLeftColor.b);
|
||||
blueByteTally += 32.0 / 255.0 * step(centerColor.b, bottomColor.b);
|
||||
blueByteTally += 64.0 / 255.0 * step(centerColor.b, bottomRightColor.b);
|
||||
blueByteTally += 128.0 / 255.0 * step(centerColor.b, rightColor.b);
|
||||
|
||||
float greenByteTally = 1.0 / 255.0 * step(centerColor.g, topRightColor.g);
|
||||
greenByteTally += 2.0 / 255.0 * step(centerColor.g, topColor.g);
|
||||
greenByteTally += 4.0 / 255.0 * step(centerColor.g, topLeftColor.g);
|
||||
greenByteTally += 8.0 / 255.0 * step(centerColor.g, leftColor.g);
|
||||
greenByteTally += 16.0 / 255.0 * step(centerColor.g, bottomLeftColor.g);
|
||||
greenByteTally += 32.0 / 255.0 * step(centerColor.g, bottomColor.g);
|
||||
greenByteTally += 64.0 / 255.0 * step(centerColor.g, bottomRightColor.g);
|
||||
greenByteTally += 128.0 / 255.0 * step(centerColor.g, rightColor.g);
|
||||
|
||||
// TODO: Replace the above with a dot product and two vec4s
|
||||
// TODO: Apply step to a matrix, rather than individually
|
||||
|
||||
gl_FragColor = vec4(redByteTally, blueByteTally, greenByteTally, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorLocalBinaryPatternFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
/** Transforms the colors of an image by applying a matrix to them
|
||||
*/
|
||||
@interface GPUImageColorMatrixFilter : GPUImageFilter
|
||||
{
|
||||
GLint colorMatrixUniform;
|
||||
GLint intensityUniform;
|
||||
}
|
||||
|
||||
/** A 4x4 matrix used to transform each color in an image
|
||||
*/
|
||||
@property(readwrite, nonatomic) GPUMatrix4x4 colorMatrix;
|
||||
|
||||
/** The degree to which the new transformed color replaces the original color for each pixel
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat intensity;
|
||||
|
||||
@end
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
#import "GPUImageColorMatrixFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorMatrixFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform lowp mat4 colorMatrix;
|
||||
uniform lowp float intensity;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 outputColor = textureColor * colorMatrix;
|
||||
|
||||
gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorMatrixFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mat4 colorMatrix;
|
||||
uniform float intensity;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 outputColor = textureColor * colorMatrix;
|
||||
|
||||
gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorMatrixFilter
|
||||
|
||||
@synthesize intensity = _intensity;
|
||||
@synthesize colorMatrix = _colorMatrix;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageColorMatrixFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
colorMatrixUniform = [filterProgram uniformIndex:@"colorMatrix"];
|
||||
intensityUniform = [filterProgram uniformIndex:@"intensity"];
|
||||
|
||||
self.intensity = 1.f;
|
||||
self.colorMatrix = (GPUMatrix4x4){
|
||||
{1.f, 0.f, 0.f, 0.f},
|
||||
{0.f, 1.f, 0.f, 0.f},
|
||||
{0.f, 0.f, 1.f, 0.f},
|
||||
{0.f, 0.f, 0.f, 1.f}
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setIntensity:(CGFloat)newIntensity;
|
||||
{
|
||||
_intensity = newIntensity;
|
||||
|
||||
[self setFloat:_intensity forUniform:intensityUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setColorMatrix:(GPUMatrix4x4)newColorMatrix;
|
||||
{
|
||||
_colorMatrix = newColorMatrix;
|
||||
|
||||
[self setMatrix4f:_colorMatrix forUniform:colorMatrixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,10 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageColorPackingFilter : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
CGFloat texelWidth, texelHeight;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,139 @@
|
||||
#import "GPUImageColorPackingFilter.h"
|
||||
|
||||
NSString *const kGPUImageColorPackingVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
|
||||
varying vec2 upperLeftInputTextureCoordinate;
|
||||
varying vec2 upperRightInputTextureCoordinate;
|
||||
varying vec2 lowerLeftInputTextureCoordinate;
|
||||
varying vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
upperLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, -texelHeight);
|
||||
upperRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, -texelHeight);
|
||||
lowerLeftInputTextureCoordinate = inputTextureCoordinate.xy + vec2(-texelWidth, texelHeight);
|
||||
lowerRightInputTextureCoordinate = inputTextureCoordinate.xy + vec2(texelWidth, texelHeight);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColorPackingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mediump mat3 convolutionMatrix;
|
||||
|
||||
varying highp vec2 outputTextureCoordinate;
|
||||
|
||||
varying highp vec2 upperLeftInputTextureCoordinate;
|
||||
varying highp vec2 upperRightInputTextureCoordinate;
|
||||
varying highp vec2 lowerLeftInputTextureCoordinate;
|
||||
varying highp vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
float upperLeftIntensity = texture2D(inputImageTexture, upperLeftInputTextureCoordinate).r;
|
||||
float upperRightIntensity = texture2D(inputImageTexture, upperRightInputTextureCoordinate).r;
|
||||
float lowerLeftIntensity = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).r;
|
||||
float lowerRightIntensity = texture2D(inputImageTexture, lowerRightInputTextureCoordinate).r;
|
||||
|
||||
gl_FragColor = vec4(upperLeftIntensity, upperRightIntensity, lowerLeftIntensity, lowerRightIntensity);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColorPackingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform mat3 convolutionMatrix;
|
||||
|
||||
varying vec2 outputTextureCoordinate;
|
||||
|
||||
varying vec2 upperLeftInputTextureCoordinate;
|
||||
varying vec2 upperRightInputTextureCoordinate;
|
||||
varying vec2 lowerLeftInputTextureCoordinate;
|
||||
varying vec2 lowerRightInputTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
float upperLeftIntensity = texture2D(inputImageTexture, upperLeftInputTextureCoordinate).r;
|
||||
float upperRightIntensity = texture2D(inputImageTexture, upperRightInputTextureCoordinate).r;
|
||||
float lowerLeftIntensity = texture2D(inputImageTexture, lowerLeftInputTextureCoordinate).r;
|
||||
float lowerRightIntensity = texture2D(inputImageTexture, lowerRightInputTextureCoordinate).r;
|
||||
|
||||
gl_FragColor = vec4(upperLeftIntensity, upperRightIntensity, lowerLeftIntensity, lowerRightIntensity);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageColorPackingFilter
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:kGPUImageColorPackingVertexShaderString fragmentShaderFromString:kGPUImageColorPackingFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
texelWidth = 0.5 / inputTextureSize.width;
|
||||
texelHeight = 0.5 / inputTextureSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
glUniform1f(texelWidthUniform, texelWidth);
|
||||
glUniform1f(texelHeightUniform, texelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Managing the display FBOs
|
||||
|
||||
- (CGSize)sizeOfFBO;
|
||||
{
|
||||
CGSize outputSize = [self maximumOutputSize];
|
||||
if ( (CGSizeEqualToSize(outputSize, CGSizeZero)) || (inputTextureSize.width < outputSize.width) )
|
||||
{
|
||||
CGSize quarterSize;
|
||||
quarterSize.width = inputTextureSize.width / 2.0;
|
||||
quarterSize.height = inputTextureSize.height / 2.0;
|
||||
return quarterSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return outputSize;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Rendering
|
||||
|
||||
- (CGSize)outputFrameSize;
|
||||
{
|
||||
CGSize quarterSize;
|
||||
quarterSize.width = inputTextureSize.width / 2.0;
|
||||
quarterSize.height = inputTextureSize.height / 2.0;
|
||||
return quarterSize;
|
||||
}
|
||||
|
||||
@end
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#import "GPUImageFilterGroup.h"
|
||||
|
||||
// This generates image-wide feature descriptors using the ColourFAST process, as developed and described in
|
||||
//
|
||||
// A. Ensor and S. Hall. ColourFAST: GPU-based feature point detection and tracking on mobile devices. 28th International Conference of Image and Vision Computing, New Zealand, 2013, p. 124-129.
|
||||
//
|
||||
// Seth Hall, "GPU accelerated feature algorithms for mobile devices", PhD thesis, School of Computing and Mathematical Sciences, Auckland University of Technology 2014.
|
||||
// http://aut.researchgateway.ac.nz/handle/10292/7991
|
||||
|
||||
@class GPUImageColourFASTSamplingOperation;
|
||||
@class GPUImageBoxBlurFilter;
|
||||
|
||||
@interface GPUImageColourFASTFeatureDetector : GPUImageFilterGroup
|
||||
{
|
||||
GPUImageBoxBlurFilter *blurFilter;
|
||||
GPUImageColourFASTSamplingOperation *colourFASTSamplingOperation;
|
||||
}
|
||||
// The blur radius of the underlying box blur. The default is 3.0.
|
||||
@property (readwrite, nonatomic) CGFloat blurRadiusInPixels;
|
||||
|
||||
@end
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#import "GPUImageColourFASTFeatureDetector.h"
|
||||
#import "GPUImageColourFASTSamplingOperation.h"
|
||||
#import "GPUImageBoxBlurFilter.h"
|
||||
|
||||
@implementation GPUImageColourFASTFeatureDetector
|
||||
|
||||
@synthesize blurRadiusInPixels;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// First pass: apply a variable Gaussian blur
|
||||
blurFilter = [[GPUImageBoxBlurFilter alloc] init];
|
||||
[self addFilter:blurFilter];
|
||||
|
||||
// Second pass: combine the blurred image with the original sharp one
|
||||
colourFASTSamplingOperation = [[GPUImageColourFASTSamplingOperation alloc] init];
|
||||
[self addFilter:colourFASTSamplingOperation];
|
||||
|
||||
// Texture location 0 needs to be the sharp image for both the blur and the second stage processing
|
||||
[blurFilter addTarget:colourFASTSamplingOperation atTextureLocation:1];
|
||||
|
||||
self.initialFilters = [NSArray arrayWithObjects:blurFilter, colourFASTSamplingOperation, nil];
|
||||
self.terminalFilter = colourFASTSamplingOperation;
|
||||
|
||||
self.blurRadiusInPixels = 3.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setBlurRadiusInPixels:(CGFloat)newValue;
|
||||
{
|
||||
blurFilter.blurRadiusInPixels = newValue;
|
||||
}
|
||||
|
||||
- (CGFloat)blurRadiusInPixels;
|
||||
{
|
||||
return blurFilter.blurRadiusInPixels;
|
||||
}
|
||||
|
||||
@end
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
// This is the feature extraction phase of the ColourFAST feature detector, as described in:
|
||||
//
|
||||
// A. Ensor and S. Hall. ColourFAST: GPU-based feature point detection and tracking on mobile devices. 28th International Conference of Image and Vision Computing, New Zealand, 2013, p. 124-129.
|
||||
//
|
||||
// Seth Hall, "GPU accelerated feature algorithms for mobile devices", PhD thesis, School of Computing and Mathematical Sciences, Auckland University of Technology 2014.
|
||||
// http://aut.researchgateway.ac.nz/handle/10292/7991
|
||||
|
||||
@interface GPUImageColourFASTSamplingOperation : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
|
||||
CGFloat texelWidth, texelHeight;
|
||||
BOOL hasOverriddenImageSizeFactor;
|
||||
}
|
||||
|
||||
// The texel width and height determines how far out to sample from this texel. By default, this is the normalized width of a pixel, but this can be overridden for different effects.
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
@end
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
#import "GPUImageColourFASTSamplingOperation.h"
|
||||
|
||||
NSString *const kGPUImageColourFASTSamplingVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec4 inputTextureCoordinate;
|
||||
attribute vec4 inputTextureCoordinate2;
|
||||
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 pointATextureCoordinate;
|
||||
varying vec2 pointBTextureCoordinate;
|
||||
varying vec2 pointCTextureCoordinate;
|
||||
varying vec2 pointDTextureCoordinate;
|
||||
varying vec2 pointETextureCoordinate;
|
||||
varying vec2 pointFTextureCoordinate;
|
||||
varying vec2 pointGTextureCoordinate;
|
||||
varying vec2 pointHTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
float tripleTexelWidth = 3.0 * texelWidth;
|
||||
float tripleTexelHeight = 3.0 * texelHeight;
|
||||
|
||||
textureCoordinate = inputTextureCoordinate.xy;
|
||||
|
||||
pointATextureCoordinate = vec2(textureCoordinate.x + tripleTexelWidth, textureCoordinate.y + texelHeight);
|
||||
pointBTextureCoordinate = vec2(textureCoordinate.x + texelWidth, textureCoordinate.y + tripleTexelHeight);
|
||||
pointCTextureCoordinate = vec2(textureCoordinate.x - texelWidth, textureCoordinate.y + tripleTexelHeight);
|
||||
pointDTextureCoordinate = vec2(textureCoordinate.x - tripleTexelWidth, textureCoordinate.y + texelHeight);
|
||||
pointETextureCoordinate = vec2(textureCoordinate.x - tripleTexelWidth, textureCoordinate.y - texelHeight);
|
||||
pointFTextureCoordinate = vec2(textureCoordinate.x - texelWidth, textureCoordinate.y - tripleTexelHeight);
|
||||
pointGTextureCoordinate = vec2(textureCoordinate.x + texelWidth, textureCoordinate.y - tripleTexelHeight);
|
||||
pointHTextureCoordinate = vec2(textureCoordinate.x + tripleTexelWidth, textureCoordinate.y - texelHeight);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageColourFASTSamplingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision highp float;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 pointATextureCoordinate;
|
||||
varying vec2 pointBTextureCoordinate;
|
||||
varying vec2 pointCTextureCoordinate;
|
||||
varying vec2 pointDTextureCoordinate;
|
||||
varying vec2 pointETextureCoordinate;
|
||||
varying vec2 pointFTextureCoordinate;
|
||||
varying vec2 pointGTextureCoordinate;
|
||||
varying vec2 pointHTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
const float PITwo = 6.2832;
|
||||
const float PI = 3.1416;
|
||||
void main()
|
||||
{
|
||||
vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
|
||||
vec3 pointAColor = texture2D(inputImageTexture, pointATextureCoordinate).rgb;
|
||||
vec3 pointBColor = texture2D(inputImageTexture, pointBTextureCoordinate).rgb;
|
||||
vec3 pointCColor = texture2D(inputImageTexture, pointCTextureCoordinate).rgb;
|
||||
vec3 pointDColor = texture2D(inputImageTexture, pointDTextureCoordinate).rgb;
|
||||
vec3 pointEColor = texture2D(inputImageTexture, pointETextureCoordinate).rgb;
|
||||
vec3 pointFColor = texture2D(inputImageTexture, pointFTextureCoordinate).rgb;
|
||||
vec3 pointGColor = texture2D(inputImageTexture, pointGTextureCoordinate).rgb;
|
||||
vec3 pointHColor = texture2D(inputImageTexture, pointHTextureCoordinate).rgb;
|
||||
|
||||
vec3 colorComparison = ((pointAColor + pointBColor + pointCColor + pointDColor + pointEColor + pointFColor + pointGColor + pointHColor) * 0.125) - centerColor;
|
||||
|
||||
// Direction calculation drawn from Appendix B of Seth Hall's Ph.D. thesis
|
||||
|
||||
vec3 dirX = (pointAColor*0.94868) + (pointBColor*0.316227) - (pointCColor*0.316227) - (pointDColor*0.94868) - (pointEColor*0.94868) - (pointFColor*0.316227) + (pointGColor*0.316227) + (pointHColor*0.94868);
|
||||
vec3 dirY = (pointAColor*0.316227) + (pointBColor*0.94868) + (pointCColor*0.94868) + (pointDColor*0.316227) - (pointEColor*0.316227) - (pointFColor*0.94868) - (pointGColor*0.94868) - (pointHColor*0.316227);
|
||||
vec3 absoluteDifference = abs(colorComparison);
|
||||
float componentLength = length(colorComparison);
|
||||
float avgX = dot(absoluteDifference, dirX) / componentLength;
|
||||
float avgY = dot(absoluteDifference, dirY) / componentLength;
|
||||
float angle = atan(avgY, avgX);
|
||||
|
||||
vec3 normalizedColorComparison = (colorComparison + 1.0) * 0.5;
|
||||
|
||||
gl_FragColor = vec4(normalizedColorComparison, (angle+PI)/PITwo);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageColourFASTSamplingFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 pointATextureCoordinate;
|
||||
varying vec2 pointBTextureCoordinate;
|
||||
varying vec2 pointCTextureCoordinate;
|
||||
varying vec2 pointDTextureCoordinate;
|
||||
varying vec2 pointETextureCoordinate;
|
||||
varying vec2 pointFTextureCoordinate;
|
||||
varying vec2 pointGTextureCoordinate;
|
||||
varying vec2 pointHTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
const float PITwo = 6.2832;
|
||||
const float PI = 3.1416;
|
||||
void main()
|
||||
{
|
||||
vec3 centerColor = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
|
||||
vec3 pointAColor = texture2D(inputImageTexture, pointATextureCoordinate).rgb;
|
||||
vec3 pointBColor = texture2D(inputImageTexture, pointBTextureCoordinate).rgb;
|
||||
vec3 pointCColor = texture2D(inputImageTexture, pointCTextureCoordinate).rgb;
|
||||
vec3 pointDColor = texture2D(inputImageTexture, pointDTextureCoordinate).rgb;
|
||||
vec3 pointEColor = texture2D(inputImageTexture, pointETextureCoordinate).rgb;
|
||||
vec3 pointFColor = texture2D(inputImageTexture, pointFTextureCoordinate).rgb;
|
||||
vec3 pointGColor = texture2D(inputImageTexture, pointGTextureCoordinate).rgb;
|
||||
vec3 pointHColor = texture2D(inputImageTexture, pointHTextureCoordinate).rgb;
|
||||
|
||||
vec3 colorComparison = ((pointAColor + pointBColor + pointCColor + pointDColor + pointEColor + pointFColor + pointGColor + pointHColor) * 0.125) - centerColor;
|
||||
|
||||
// Direction calculation drawn from Appendix B of Seth Hall's Ph.D. thesis
|
||||
|
||||
vec3 dirX = (pointAColor*0.94868) + (pointBColor*0.316227) - (pointCColor*0.316227) - (pointDColor*0.94868) - (pointEColor*0.94868) - (pointFColor*0.316227) + (pointGColor*0.316227) + (pointHColor*0.94868);
|
||||
vec3 dirY = (pointAColor*0.316227) + (pointBColor*0.94868) + (pointCColor*0.94868) + (pointDColor*0.316227) - (pointEColor*0.316227) - (pointFColor*0.94868) - (pointGColor*0.94868) - (pointHColor*0.316227);
|
||||
vec3 absoluteDifference = abs(colorComparison);
|
||||
float componentLength = length(colorComparison);
|
||||
float avgX = dot(absoluteDifference, dirX) / componentLength;
|
||||
float avgY = dot(absoluteDifference, dirY) / componentLength;
|
||||
float angle = atan(avgY, avgX);
|
||||
|
||||
vec3 normalizedColorComparison = (colorComparison + 1.0) * 0.5;
|
||||
|
||||
gl_FragColor = vec4(normalizedColorComparison, (angle+PI)/PITwo);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
@implementation GPUImageColourFASTSamplingOperation
|
||||
|
||||
@synthesize texelWidth = _texelWidth;
|
||||
@synthesize texelHeight = _texelHeight;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithFragmentShaderFromString:(NSString *)fragmentShaderString;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:kGPUImageColourFASTSamplingVertexShaderString fragmentShaderFromString:kGPUImageColourFASTSamplingFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
if (!hasOverriddenImageSizeFactor)
|
||||
{
|
||||
_texelWidth = 1.0 / filterFrameSize.width;
|
||||
_texelHeight = 1.0 / filterFrameSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
if (GPUImageRotationSwapsWidthAndHeight(inputRotation))
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelHeight);
|
||||
glUniform1f(texelHeightUniform, _texelWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform1f(texelWidthUniform, _texelWidth);
|
||||
glUniform1f(texelHeightUniform, _texelHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelWidth = newValue;
|
||||
|
||||
[self setFloat:_texelWidth forUniform:texelWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelHeight = newValue;
|
||||
|
||||
[self setFloat:_texelHeight forUniform:texelHeightUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
/** Adjusts the contrast of the image
|
||||
*/
|
||||
@interface GPUImageContrastFilter : GPUImageFilter
|
||||
{
|
||||
GLint contrastUniform;
|
||||
}
|
||||
|
||||
/** Contrast ranges from 0.0 to 4.0 (max contrast), with 1.0 as the normal level
|
||||
*/
|
||||
@property(readwrite, nonatomic) CGFloat contrast;
|
||||
|
||||
@end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#import "GPUImageContrastFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageContrastFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform lowp float contrast;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageContrastFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float contrast;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageContrastFilter
|
||||
|
||||
@synthesize contrast = _contrast;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageContrastFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
contrastUniform = [filterProgram uniformIndex:@"contrast"];
|
||||
self.contrast = 1.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setContrast:(CGFloat)newValue;
|
||||
{
|
||||
_contrast = newValue;
|
||||
|
||||
[self setFloat:_contrast forUniform:contrastUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCropFilter : GPUImageFilter
|
||||
{
|
||||
GLfloat cropTextureCoordinates[8];
|
||||
}
|
||||
|
||||
// The crop region is the rectangle within the image to crop. It is normalized to a coordinate space from 0.0 to 1.0, with 0.0, 0.0 being the upper left corner of the image
|
||||
@property(readwrite, nonatomic) CGRect cropRegion;
|
||||
|
||||
// Initialization and teardown
|
||||
- (id)initWithCropRegion:(CGRect)newCropRegion;
|
||||
|
||||
@end
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
#import "GPUImageCropFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCropFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCropFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@interface GPUImageCropFilter ()
|
||||
|
||||
- (void)calculateCropTextureCoordinates;
|
||||
|
||||
@end
|
||||
|
||||
@interface GPUImageCropFilter()
|
||||
{
|
||||
CGSize originallySuppliedInputSize;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GPUImageCropFilter
|
||||
|
||||
@synthesize cropRegion = _cropRegion;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithCropRegion:(CGRect)newCropRegion;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageCropFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.cropRegion = newCropRegion;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithCropRegion:CGRectMake(0.0, 0.0, 1.0, 1.0)]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Rendering
|
||||
|
||||
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
if (self.preventRendering)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if (overrideInputSize)
|
||||
// {
|
||||
// if (CGSizeEqualToSize(forcedMaximumSize, CGSizeZero))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// CGRect insetRect = AVMakeRectWithAspectRatioInsideRect(newSize, CGRectMake(0.0, 0.0, forcedMaximumSize.width, forcedMaximumSize.height));
|
||||
// inputTextureSize = insetRect.size;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
CGSize rotatedSize = [self rotatedSize:newSize forIndex:textureIndex];
|
||||
originallySuppliedInputSize = rotatedSize;
|
||||
|
||||
CGSize scaledSize;
|
||||
scaledSize.width = rotatedSize.width * _cropRegion.size.width;
|
||||
scaledSize.height = rotatedSize.height * _cropRegion.size.height;
|
||||
|
||||
|
||||
if (CGSizeEqualToSize(scaledSize, CGSizeZero))
|
||||
{
|
||||
inputTextureSize = scaledSize;
|
||||
}
|
||||
else if (!CGSizeEqualToSize(inputTextureSize, scaledSize))
|
||||
{
|
||||
inputTextureSize = scaledSize;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark GPUImageInput
|
||||
|
||||
- (void)calculateCropTextureCoordinates;
|
||||
{
|
||||
CGFloat minX = _cropRegion.origin.x;
|
||||
CGFloat minY = _cropRegion.origin.y;
|
||||
CGFloat maxX = CGRectGetMaxX(_cropRegion);
|
||||
CGFloat maxY = CGRectGetMaxY(_cropRegion);
|
||||
|
||||
switch(inputRotation)
|
||||
{
|
||||
case kGPUImageNoRotation: // Works
|
||||
{
|
||||
cropTextureCoordinates[0] = minX; // 0,0
|
||||
cropTextureCoordinates[1] = minY;
|
||||
|
||||
cropTextureCoordinates[2] = maxX; // 1,0
|
||||
cropTextureCoordinates[3] = minY;
|
||||
|
||||
cropTextureCoordinates[4] = minX; // 0,1
|
||||
cropTextureCoordinates[5] = maxY;
|
||||
|
||||
cropTextureCoordinates[6] = maxX; // 1,1
|
||||
cropTextureCoordinates[7] = maxY;
|
||||
}; break;
|
||||
case kGPUImageRotateLeft: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = maxY; // 1,0
|
||||
cropTextureCoordinates[1] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[2] = maxY; // 1,1
|
||||
cropTextureCoordinates[3] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[4] = minY; // 0,0
|
||||
cropTextureCoordinates[5] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[6] = minY; // 0,1
|
||||
cropTextureCoordinates[7] = 1.0 - minX;
|
||||
}; break;
|
||||
case kGPUImageRotateRight: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = minY; // 0,1
|
||||
cropTextureCoordinates[1] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[2] = minY; // 0,0
|
||||
cropTextureCoordinates[3] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[4] = maxY; // 1,1
|
||||
cropTextureCoordinates[5] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[6] = maxY; // 1,0
|
||||
cropTextureCoordinates[7] = 1.0 - maxX;
|
||||
}; break;
|
||||
case kGPUImageFlipVertical: // Works for me
|
||||
{
|
||||
cropTextureCoordinates[0] = minX; // 0,1
|
||||
cropTextureCoordinates[1] = maxY;
|
||||
|
||||
cropTextureCoordinates[2] = maxX; // 1,1
|
||||
cropTextureCoordinates[3] = maxY;
|
||||
|
||||
cropTextureCoordinates[4] = minX; // 0,0
|
||||
cropTextureCoordinates[5] = minY;
|
||||
|
||||
cropTextureCoordinates[6] = maxX; // 1,0
|
||||
cropTextureCoordinates[7] = minY;
|
||||
}; break;
|
||||
case kGPUImageFlipHorizonal: // Works for me
|
||||
{
|
||||
cropTextureCoordinates[0] = maxX; // 1,0
|
||||
cropTextureCoordinates[1] = minY;
|
||||
|
||||
cropTextureCoordinates[2] = minX; // 0,0
|
||||
cropTextureCoordinates[3] = minY;
|
||||
|
||||
cropTextureCoordinates[4] = maxX; // 1,1
|
||||
cropTextureCoordinates[5] = maxY;
|
||||
|
||||
cropTextureCoordinates[6] = minX; // 0,1
|
||||
cropTextureCoordinates[7] = maxY;
|
||||
}; break;
|
||||
case kGPUImageRotate180: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = maxX; // 1,1
|
||||
cropTextureCoordinates[1] = maxY;
|
||||
|
||||
cropTextureCoordinates[2] = minX; // 0,1
|
||||
cropTextureCoordinates[3] = maxY;
|
||||
|
||||
cropTextureCoordinates[4] = maxX; // 1,0
|
||||
cropTextureCoordinates[5] = minY;
|
||||
|
||||
cropTextureCoordinates[6] = minX; // 0,0
|
||||
cropTextureCoordinates[7] = minY;
|
||||
}; break;
|
||||
case kGPUImageRotateRightFlipVertical: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = minY; // 0,0
|
||||
cropTextureCoordinates[1] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[2] = minY; // 0,1
|
||||
cropTextureCoordinates[3] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[4] = maxY; // 1,0
|
||||
cropTextureCoordinates[5] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[6] = maxY; // 1,1
|
||||
cropTextureCoordinates[7] = 1.0 - minX;
|
||||
}; break;
|
||||
case kGPUImageRotateRightFlipHorizontal: // Fixed
|
||||
{
|
||||
cropTextureCoordinates[0] = maxY; // 1,1
|
||||
cropTextureCoordinates[1] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[2] = maxY; // 1,0
|
||||
cropTextureCoordinates[3] = 1.0 - maxX;
|
||||
|
||||
cropTextureCoordinates[4] = minY; // 0,1
|
||||
cropTextureCoordinates[5] = 1.0 - minX;
|
||||
|
||||
cropTextureCoordinates[6] = minY; // 0,0
|
||||
cropTextureCoordinates[7] = 1.0 - maxX;
|
||||
}; break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
static const GLfloat cropSquareVertices[] = {
|
||||
-1.0f, -1.0f,
|
||||
1.0f, -1.0f,
|
||||
-1.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
};
|
||||
|
||||
[self renderToTextureWithVertices:cropSquareVertices textureCoordinates:cropTextureCoordinates];
|
||||
|
||||
[self informTargetsAboutNewFrameAtTime:frameTime];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setCropRegion:(CGRect)newValue;
|
||||
{
|
||||
NSParameterAssert(newValue.origin.x >= 0 && newValue.origin.x <= 1 &&
|
||||
newValue.origin.y >= 0 && newValue.origin.y <= 1 &&
|
||||
newValue.size.width >= 0 && newValue.size.width <= 1 &&
|
||||
newValue.size.height >= 0 && newValue.size.height <= 1);
|
||||
|
||||
_cropRegion = newValue;
|
||||
[self calculateCropTextureCoordinates];
|
||||
}
|
||||
|
||||
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
|
||||
{
|
||||
[super setInputRotation:newInputRotation atIndex:textureIndex];
|
||||
[self calculateCropTextureCoordinates];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCrosshairGenerator : GPUImageFilter
|
||||
{
|
||||
GLint crosshairWidthUniform, crosshairColorUniform;
|
||||
}
|
||||
|
||||
// The width of the displayed crosshairs, in pixels. Currently this only works well for odd widths. The default is 5.
|
||||
@property(readwrite, nonatomic) CGFloat crosshairWidth;
|
||||
|
||||
// The color of the crosshairs is specified using individual red, green, and blue components (normalized to 1.0). The default is green: (0.0, 1.0, 0.0).
|
||||
- (void)setCrosshairColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
|
||||
// Rendering
|
||||
- (void)renderCrosshairsFromArray:(GLfloat *)crosshairCoordinates count:(NSUInteger)numberOfCrosshairs frameTime:(CMTime)frameTime;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,139 @@
|
||||
#import "GPUImageCrosshairGenerator.h"
|
||||
|
||||
NSString *const kGPUImageCrosshairVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
|
||||
uniform float crosshairWidth;
|
||||
|
||||
varying vec2 centerLocation;
|
||||
varying float pointSpacing;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(((position.xy * 2.0) - 1.0), 0.0, 1.0);
|
||||
gl_PointSize = crosshairWidth + 1.0;
|
||||
pointSpacing = 1.0 / crosshairWidth;
|
||||
centerLocation = vec2(pointSpacing * ceil(crosshairWidth / 2.0), pointSpacing * ceil(crosshairWidth / 2.0));
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCrosshairFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
uniform lowp vec3 crosshairColor;
|
||||
|
||||
varying highp vec2 centerLocation;
|
||||
varying highp float pointSpacing;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec2 distanceFromCenter = abs(centerLocation - gl_PointCoord.xy);
|
||||
lowp float axisTest = step(pointSpacing, gl_PointCoord.y) * step(distanceFromCenter.x, 0.09) + step(pointSpacing, gl_PointCoord.x) * step(distanceFromCenter.y, 0.09);
|
||||
|
||||
gl_FragColor = vec4(crosshairColor * axisTest, axisTest);
|
||||
// gl_FragColor = vec4(distanceFromCenterInX, distanceFromCenterInY, 0.0, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCrosshairFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
GPUImageEscapedHashIdentifier(version 120)\n
|
||||
|
||||
uniform vec3 crosshairColor;
|
||||
|
||||
varying vec2 centerLocation;
|
||||
varying float pointSpacing;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 distanceFromCenter = abs(centerLocation - gl_PointCoord.xy);
|
||||
float axisTest = step(pointSpacing, gl_PointCoord.y) * step(distanceFromCenter.x, 0.09) + step(pointSpacing, gl_PointCoord.x) * step(distanceFromCenter.y, 0.09);
|
||||
|
||||
gl_FragColor = vec4(crosshairColor * axisTest, axisTest);
|
||||
// gl_FragColor = vec4(distanceFromCenterInX, distanceFromCenterInY, 0.0, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageCrosshairGenerator
|
||||
|
||||
@synthesize crosshairWidth = _crosshairWidth;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithVertexShaderFromString:kGPUImageCrosshairVertexShaderString fragmentShaderFromString:kGPUImageCrosshairFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
crosshairWidthUniform = [filterProgram uniformIndex:@"crosshairWidth"];
|
||||
crosshairColorUniform = [filterProgram uniformIndex:@"crosshairColor"];
|
||||
|
||||
self.crosshairWidth = 5.0;
|
||||
[self setCrosshairColorRed:0.0 green:1.0 blue:0.0];
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Rendering
|
||||
|
||||
- (void)renderCrosshairsFromArray:(GLfloat *)crosshairCoordinates count:(NSUInteger)numberOfCrosshairs frameTime:(CMTime)frameTime;
|
||||
{
|
||||
if (self.preventRendering)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
#else
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
|
||||
outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:[self sizeOfFBO] textureOptions:self.outputTextureOptions onlyTexture:NO];
|
||||
[outputFramebuffer activateFramebuffer];
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, crosshairCoordinates);
|
||||
|
||||
glDrawArrays(GL_POINTS, 0, (GLsizei)numberOfCrosshairs);
|
||||
|
||||
[self informTargetsAboutNewFrameAtTime:frameTime];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates;
|
||||
{
|
||||
// Prevent rendering of the frame by normal means
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setCrosshairWidth:(CGFloat)newValue;
|
||||
{
|
||||
_crosshairWidth = newValue;
|
||||
|
||||
[self setFloat:_crosshairWidth forUniform:crosshairWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setCrosshairColorRed:(GLfloat)redComponent green:(GLfloat)greenComponent blue:(GLfloat)blueComponent;
|
||||
{
|
||||
GPUVector3 crosshairColor = {redComponent, greenComponent, blueComponent};
|
||||
|
||||
[self setVec3:crosshairColor forUniform:crosshairColorUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageCrosshatchFilter : GPUImageFilter
|
||||
{
|
||||
GLint crossHatchSpacingUniform, lineWidthUniform;
|
||||
}
|
||||
// The fractional width of the image to use as the spacing for the crosshatch. The default is 0.03.
|
||||
@property(readwrite, nonatomic) CGFloat crossHatchSpacing;
|
||||
|
||||
// A relative width for the crosshatch lines. The default is 0.003.
|
||||
@property(readwrite, nonatomic) CGFloat lineWidth;
|
||||
|
||||
@end
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#import "GPUImageCrosshatchFilter.h"
|
||||
|
||||
// Shader code based on http://machinesdontcare.wordpress.com/
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageCrosshatchFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform highp float crossHatchSpacing;
|
||||
uniform highp float lineWidth;
|
||||
|
||||
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float luminance = dot(texture2D(inputImageTexture, textureCoordinate).rgb, W);
|
||||
|
||||
lowp vec4 colorToDisplay = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
if (luminance < 1.00)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.75)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.50)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.3)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = colorToDisplay;
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageCrosshatchFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
uniform float crossHatchSpacing;
|
||||
uniform float lineWidth;
|
||||
|
||||
const vec3 W = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
void main()
|
||||
{
|
||||
float luminance = dot(texture2D(inputImageTexture, textureCoordinate).rgb, W);
|
||||
|
||||
vec4 colorToDisplay = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
if (luminance < 1.00)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.75)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y, crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.50)
|
||||
{
|
||||
if (mod(textureCoordinate.x + textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
if (luminance < 0.3)
|
||||
{
|
||||
if (mod(textureCoordinate.x - textureCoordinate.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)
|
||||
{
|
||||
colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = colorToDisplay;
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageCrosshatchFilter
|
||||
|
||||
@synthesize crossHatchSpacing = _crossHatchSpacing;
|
||||
@synthesize lineWidth = _lineWidth;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageCrosshatchFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
crossHatchSpacingUniform = [filterProgram uniformIndex:@"crossHatchSpacing"];
|
||||
lineWidthUniform = [filterProgram uniformIndex:@"lineWidth"];
|
||||
|
||||
self.crossHatchSpacing = 0.03;
|
||||
self.lineWidth = 0.003;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setCrossHatchSpacing:(CGFloat)newValue;
|
||||
{
|
||||
CGFloat singlePixelSpacing;
|
||||
if (inputTextureSize.width != 0.0)
|
||||
{
|
||||
singlePixelSpacing = 1.0 / inputTextureSize.width;
|
||||
}
|
||||
else
|
||||
{
|
||||
singlePixelSpacing = 1.0 / 2048.0;
|
||||
}
|
||||
|
||||
if (newValue < singlePixelSpacing)
|
||||
{
|
||||
_crossHatchSpacing = singlePixelSpacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
_crossHatchSpacing = newValue;
|
||||
}
|
||||
|
||||
[self setFloat:_crossHatchSpacing forUniform:crossHatchSpacingUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setLineWidth:(CGFloat)newValue;
|
||||
{
|
||||
_lineWidth = newValue;
|
||||
|
||||
[self setFloat:_lineWidth forUniform:lineWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDarkenBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,52 @@
|
||||
#import "GPUImageDarkenBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDarkenBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 overlayer = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(min(overlayer.rgb * base.a, base.rgb * overlayer.a) + overlayer.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlayer.a), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDarkenBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlayer = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = vec4(min(overlayer.rgb * base.a, base.rgb * overlayer.a) + overlayer.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlayer.a), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDarkenBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDarkenBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDifferenceBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#import "GPUImageDifferenceBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDifferenceBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
gl_FragColor = vec4(abs(textureColor2.rgb - textureColor.rgb), textureColor.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDifferenceBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
gl_FragColor = vec4(abs(textureColor2.rgb - textureColor.rgb), textureColor.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDifferenceBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDifferenceBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#import "GPUImageTwoPassTextureSamplingFilter.h"
|
||||
|
||||
// For each pixel, this sets it to the maximum value of the red channel in a rectangular neighborhood extending out dilationRadius pixels from the center.
|
||||
// This extends out bright features, and is most commonly used with black-and-white thresholded images.
|
||||
|
||||
extern NSString *const kGPUImageDilationRadiusOneVertexShaderString;
|
||||
extern NSString *const kGPUImageDilationRadiusTwoVertexShaderString;
|
||||
extern NSString *const kGPUImageDilationRadiusThreeVertexShaderString;
|
||||
extern NSString *const kGPUImageDilationRadiusFourVertexShaderString;
|
||||
|
||||
@interface GPUImageDilationFilter : GPUImageTwoPassTextureSamplingFilter
|
||||
|
||||
// Acceptable values for dilationRadius, which sets the distance in pixels to sample out from the center, are 1, 2, 3, and 4.
|
||||
- (id)initWithRadius:(NSUInteger)dilationRadius;
|
||||
|
||||
@end
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
#import "GPUImageDilationFilter.h"
|
||||
|
||||
@implementation GPUImageDilationFilter
|
||||
|
||||
NSString *const kGPUImageDilationRadiusOneVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusTwoVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
twoStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 2.0);
|
||||
twoStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 2.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusThreeVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
twoStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 2.0);
|
||||
twoStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 2.0);
|
||||
threeStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 3.0);
|
||||
threeStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 3.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusFourVertexShaderString = SHADER_STRING
|
||||
(
|
||||
attribute vec4 position;
|
||||
attribute vec2 inputTextureCoordinate;
|
||||
|
||||
uniform float texelWidthOffset;
|
||||
uniform float texelHeightOffset;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = position;
|
||||
|
||||
vec2 offset = vec2(texelWidthOffset, texelHeightOffset);
|
||||
|
||||
centerTextureCoordinate = inputTextureCoordinate;
|
||||
oneStepNegativeTextureCoordinate = inputTextureCoordinate - offset;
|
||||
oneStepPositiveTextureCoordinate = inputTextureCoordinate + offset;
|
||||
twoStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 2.0);
|
||||
twoStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 2.0);
|
||||
threeStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 3.0);
|
||||
threeStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 3.0);
|
||||
fourStepsNegativeTextureCoordinate = inputTextureCoordinate - (offset * 4.0);
|
||||
fourStepsPositiveTextureCoordinate = inputTextureCoordinate + (offset * 4.0);
|
||||
}
|
||||
);
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDilationRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, fourStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDilationRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageDilationRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float maxValue = max(centerIntensity, oneStepPositiveIntensity);
|
||||
maxValue = max(maxValue, oneStepNegativeIntensity);
|
||||
maxValue = max(maxValue, twoStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, twoStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, threeStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, threeStepsNegativeIntensity);
|
||||
maxValue = max(maxValue, fourStepsPositiveIntensity);
|
||||
maxValue = max(maxValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(maxValue), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)dilationRadius;
|
||||
{
|
||||
NSString *fragmentShaderForThisRadius = nil;
|
||||
NSString *vertexShaderForThisRadius = nil;
|
||||
|
||||
switch (dilationRadius)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusOneVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusOneFragmentShaderString;
|
||||
}; break;
|
||||
case 2:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusTwoVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusTwoFragmentShaderString;
|
||||
}; break;
|
||||
case 3:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusThreeVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusThreeFragmentShaderString;
|
||||
}; break;
|
||||
case 4:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
default:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageDilationRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
}
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:vertexShaderForThisRadius firstStageFragmentShaderFromString:fragmentShaderForThisRadius secondStageVertexShaderFromString:vertexShaderForThisRadius secondStageFragmentShaderFromString:fragmentShaderForThisRadius]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithRadius:1]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageDirectionalNonMaximumSuppressionFilter : GPUImageFilter
|
||||
{
|
||||
GLint texelWidthUniform, texelHeightUniform;
|
||||
GLint upperThresholdUniform, lowerThresholdUniform;
|
||||
|
||||
BOOL hasOverriddenImageSizeFactor;
|
||||
}
|
||||
|
||||
// The texel width and height determines how far out to sample from this texel. By default, this is the normalized width of a pixel, but this can be overridden for different effects.
|
||||
@property(readwrite, nonatomic) CGFloat texelWidth;
|
||||
@property(readwrite, nonatomic) CGFloat texelHeight;
|
||||
|
||||
// These thresholds set cutoffs for the intensities that definitely get registered (upper threshold) and those that definitely don't (lower threshold)
|
||||
@property(readwrite, nonatomic) CGFloat upperThreshold;
|
||||
@property(readwrite, nonatomic) CGFloat lowerThreshold;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,141 @@
|
||||
#import "GPUImageDirectionalNonMaximumSuppressionFilter.h"
|
||||
|
||||
@implementation GPUImageDirectionalNonMaximumSuppressionFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDirectionalNonmaximumSuppressionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision mediump float;
|
||||
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform highp float texelWidth;
|
||||
uniform highp float texelHeight;
|
||||
uniform mediump float upperThreshold;
|
||||
uniform mediump float lowerThreshold;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 currentGradientAndDirection = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
vec2 gradientDirection = ((currentGradientAndDirection.gb * 2.0) - 1.0) * vec2(texelWidth, texelHeight);
|
||||
|
||||
float firstSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate + gradientDirection).r;
|
||||
float secondSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate - gradientDirection).r;
|
||||
|
||||
float multiplier = step(firstSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * step(secondSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
|
||||
float thresholdCompliance = smoothstep(lowerThreshold, upperThreshold, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * thresholdCompliance;
|
||||
|
||||
gl_FragColor = vec4(multiplier, multiplier, multiplier, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDirectionalNonmaximumSuppressionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float texelWidth;
|
||||
uniform float texelHeight;
|
||||
uniform float upperThreshold;
|
||||
uniform float lowerThreshold;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 currentGradientAndDirection = texture2D(inputImageTexture, textureCoordinate).rgb;
|
||||
vec2 gradientDirection = ((currentGradientAndDirection.gb * 2.0) - 1.0) * vec2(texelWidth, texelHeight);
|
||||
|
||||
float firstSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate + gradientDirection).r;
|
||||
float secondSampledGradientMagnitude = texture2D(inputImageTexture, textureCoordinate - gradientDirection).r;
|
||||
|
||||
float multiplier = step(firstSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * step(secondSampledGradientMagnitude, currentGradientAndDirection.r);
|
||||
|
||||
float thresholdCompliance = smoothstep(lowerThreshold, upperThreshold, currentGradientAndDirection.r);
|
||||
multiplier = multiplier * thresholdCompliance;
|
||||
|
||||
gl_FragColor = vec4(multiplier, multiplier, multiplier, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@synthesize texelWidth = _texelWidth;
|
||||
@synthesize texelHeight = _texelHeight;
|
||||
@synthesize upperThreshold = _upperThreshold;
|
||||
@synthesize lowerThreshold = _lowerThreshold;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDirectionalNonmaximumSuppressionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
texelWidthUniform = [filterProgram uniformIndex:@"texelWidth"];
|
||||
texelHeightUniform = [filterProgram uniformIndex:@"texelHeight"];
|
||||
upperThresholdUniform = [filterProgram uniformIndex:@"upperThreshold"];
|
||||
lowerThresholdUniform = [filterProgram uniformIndex:@"lowerThreshold"];
|
||||
|
||||
self.upperThreshold = 0.5;
|
||||
self.lowerThreshold = 0.1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setupFilterForSize:(CGSize)filterFrameSize;
|
||||
{
|
||||
if (!hasOverriddenImageSizeFactor)
|
||||
{
|
||||
_texelWidth = 1.0 / filterFrameSize.width;
|
||||
_texelHeight = 1.0 / filterFrameSize.height;
|
||||
|
||||
runSynchronouslyOnVideoProcessingQueue(^{
|
||||
[GPUImageContext setActiveShaderProgram:filterProgram];
|
||||
glUniform1f(texelWidthUniform, _texelWidth);
|
||||
glUniform1f(texelHeightUniform, _texelHeight);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setTexelWidth:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelWidth = newValue;
|
||||
|
||||
[self setFloat:_texelWidth forUniform:texelWidthUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setTexelHeight:(CGFloat)newValue;
|
||||
{
|
||||
hasOverriddenImageSizeFactor = YES;
|
||||
_texelHeight = newValue;
|
||||
|
||||
[self setFloat:_texelHeight forUniform:texelHeightUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setLowerThreshold:(CGFloat)newValue;
|
||||
{
|
||||
_lowerThreshold = newValue;
|
||||
|
||||
[self setFloat:_lowerThreshold forUniform:lowerThresholdUniform program:filterProgram];
|
||||
}
|
||||
|
||||
- (void)setUpperThreshold:(CGFloat)newValue;
|
||||
{
|
||||
_upperThreshold = newValue;
|
||||
|
||||
[self setFloat:_upperThreshold forUniform:upperThresholdUniform program:filterProgram];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImage3x3TextureSamplingFilter.h"
|
||||
|
||||
@interface GPUImageDirectionalSobelEdgeDetectionFilter : GPUImage3x3TextureSamplingFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,103 @@
|
||||
#import "GPUImageDirectionalSobelEdgeDetectionFilter.h"
|
||||
|
||||
@implementation GPUImageDirectionalSobelEdgeDetectionFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDirectionalSobelEdgeDetectionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision mediump float;
|
||||
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
|
||||
float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;
|
||||
float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
|
||||
float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
|
||||
float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
|
||||
float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
|
||||
float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
|
||||
float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
|
||||
|
||||
vec2 gradientDirection;
|
||||
gradientDirection.x = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;
|
||||
gradientDirection.y = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;
|
||||
|
||||
float gradientMagnitude = length(gradientDirection);
|
||||
vec2 normalizedDirection = normalize(gradientDirection);
|
||||
normalizedDirection = sign(normalizedDirection) * floor(abs(normalizedDirection) + 0.617316); // Offset by 1-sin(pi/8) to set to 0 if near axis, 1 if away
|
||||
normalizedDirection = (normalizedDirection + 1.0) * 0.5; // Place -1.0 - 1.0 within 0 - 1.0
|
||||
|
||||
gl_FragColor = vec4(gradientMagnitude, normalizedDirection.x, normalizedDirection.y, 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDirectionalSobelEdgeDetectionFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 leftTextureCoordinate;
|
||||
varying vec2 rightTextureCoordinate;
|
||||
|
||||
varying vec2 topTextureCoordinate;
|
||||
varying vec2 topLeftTextureCoordinate;
|
||||
varying vec2 topRightTextureCoordinate;
|
||||
|
||||
varying vec2 bottomTextureCoordinate;
|
||||
varying vec2 bottomLeftTextureCoordinate;
|
||||
varying vec2 bottomRightTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;
|
||||
float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;
|
||||
float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;
|
||||
float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;
|
||||
float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;
|
||||
float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;
|
||||
float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;
|
||||
float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;
|
||||
|
||||
vec2 gradientDirection;
|
||||
gradientDirection.x = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;
|
||||
gradientDirection.y = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;
|
||||
|
||||
float gradientMagnitude = length(gradientDirection);
|
||||
vec2 normalizedDirection = normalize(gradientDirection);
|
||||
normalizedDirection = sign(normalizedDirection) * floor(abs(normalizedDirection) + 0.617316); // Offset by 1-sin(pi/8) to set to 0 if near axis, 1 if away
|
||||
normalizedDirection = (normalizedDirection + 1.0) * 0.5; // Place -1.0 - 1.0 within 0 - 1.0
|
||||
|
||||
gl_FragColor = vec4(gradientMagnitude, normalizedDirection.x, normalizedDirection.y, 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDirectionalSobelEdgeDetectionFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDissolveBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
GLint mixUniform;
|
||||
}
|
||||
|
||||
// Mix ranges from 0.0 (only image 1) to 1.0 (only image 2), with 0.5 (half of either) as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat mix;
|
||||
|
||||
@end
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#import "GPUImageDissolveBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDissolveBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
uniform lowp float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
lowp vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = mix(textureColor, textureColor2, mixturePercent);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDissolveBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
uniform float mixturePercent;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
gl_FragColor = mix(textureColor, textureColor2, mixturePercent);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDissolveBlendFilter
|
||||
|
||||
@synthesize mix = _mix;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDissolveBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
mixUniform = [filterProgram uniformIndex:@"mixturePercent"];
|
||||
self.mix = 0.5;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setMix:(CGFloat)newValue;
|
||||
{
|
||||
_mix = newValue;
|
||||
|
||||
[self setFloat:_mix forUniform:mixUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageDivideBlendFilter : GPUImageTwoInputFilter
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,96 @@
|
||||
#import "GPUImageDivideBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageDivideBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
mediump float ra;
|
||||
if (overlay.a == 0.0 || ((base.r / overlay.r) > (base.a / overlay.a)))
|
||||
ra = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
else
|
||||
ra = (base.r * overlay.a * overlay.a) / overlay.r + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
|
||||
|
||||
mediump float ga;
|
||||
if (overlay.a == 0.0 || ((base.g / overlay.g) > (base.a / overlay.a)))
|
||||
ga = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
else
|
||||
ga = (base.g * overlay.a * overlay.a) / overlay.g + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
|
||||
|
||||
mediump float ba;
|
||||
if (overlay.a == 0.0 || ((base.b / overlay.b) > (base.a / overlay.a)))
|
||||
ba = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
else
|
||||
ba = (base.b * overlay.a * overlay.a) / overlay.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
|
||||
mediump float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(ra, ga, ba, a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageDivideBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
float ra;
|
||||
if (overlay.a == 0.0 || ((base.r / overlay.r) > (base.a / overlay.a)))
|
||||
ra = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
else
|
||||
ra = (base.r * overlay.a * overlay.a) / overlay.r + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
|
||||
|
||||
|
||||
float ga;
|
||||
if (overlay.a == 0.0 || ((base.g / overlay.g) > (base.a / overlay.a)))
|
||||
ga = overlay.a * base.a + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
else
|
||||
ga = (base.g * overlay.a * overlay.a) / overlay.g + overlay.g * (1.0 - base.a) + base.g * (1.0 - overlay.a);
|
||||
|
||||
|
||||
float ba;
|
||||
if (overlay.a == 0.0 || ((base.b / overlay.b) > (base.a / overlay.a)))
|
||||
ba = overlay.a * base.a + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
else
|
||||
ba = (base.b * overlay.a * overlay.a) / overlay.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
|
||||
|
||||
float a = overlay.a + base.a - overlay.a * base.a;
|
||||
|
||||
gl_FragColor = vec4(ra, ga, ba, a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageDivideBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageDivideBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#import "GPUImage3x3ConvolutionFilter.h"
|
||||
|
||||
@interface GPUImageEmbossFilter : GPUImage3x3ConvolutionFilter
|
||||
|
||||
// The strength of the embossing, from 0.0 to 4.0, with 1.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat intensity;
|
||||
|
||||
@end
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
#import "GPUImageEmbossFilter.h"
|
||||
|
||||
@implementation GPUImageEmbossFilter
|
||||
|
||||
@synthesize intensity = _intensity;
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.intensity = 1.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setIntensity:(CGFloat)newValue;
|
||||
{
|
||||
// [(GPUImage3x3ConvolutionFilter *)filter setConvolutionMatrix:(GPUMatrix3x3){
|
||||
// {-2.0f, -1.0f, 0.0f},
|
||||
// {-1.0f, 1.0f, 1.0f},
|
||||
// { 0.0f, 1.0f, 2.0f}
|
||||
// }];
|
||||
|
||||
_intensity = newValue;
|
||||
|
||||
GPUMatrix3x3 newConvolutionMatrix;
|
||||
newConvolutionMatrix.one.one = _intensity * (-2.0);
|
||||
newConvolutionMatrix.one.two = -_intensity;
|
||||
newConvolutionMatrix.one.three = 0.0f;
|
||||
|
||||
newConvolutionMatrix.two.one = -_intensity;
|
||||
newConvolutionMatrix.two.two = 1.0;
|
||||
newConvolutionMatrix.two.three = _intensity;
|
||||
|
||||
newConvolutionMatrix.three.one = 0.0f;
|
||||
newConvolutionMatrix.three.two = _intensity;
|
||||
newConvolutionMatrix.three.three = _intensity * 2.0;
|
||||
|
||||
self.convolutionKernel = newConvolutionMatrix;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageTwoPassTextureSamplingFilter.h"
|
||||
|
||||
// For each pixel, this sets it to the minimum value of the red channel in a rectangular neighborhood extending out dilationRadius pixels from the center.
|
||||
// This extends out dark features, and is most commonly used with black-and-white thresholded images.
|
||||
|
||||
@interface GPUImageErosionFilter : GPUImageTwoPassTextureSamplingFilter
|
||||
|
||||
// Acceptable values for erosionRadius, which sets the distance in pixels to sample out from the center, are 1, 2, 3, and 4.
|
||||
- (id)initWithRadius:(NSUInteger)erosionRadius;
|
||||
|
||||
@end
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
#import "GPUImageErosionFilter.h"
|
||||
#import "GPUImageDilationFilter.h"
|
||||
|
||||
@implementation GPUImageErosionFilter
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageErosionRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
precision lowp float;
|
||||
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
lowp float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
minValue = min(minValue, fourStepsPositiveIntensity);
|
||||
minValue = min(minValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageErosionRadiusOneFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusTwoFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusThreeFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
|
||||
NSString *const kGPUImageErosionRadiusFourFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 centerTextureCoordinate;
|
||||
varying vec2 oneStepPositiveTextureCoordinate;
|
||||
varying vec2 oneStepNegativeTextureCoordinate;
|
||||
varying vec2 twoStepsPositiveTextureCoordinate;
|
||||
varying vec2 twoStepsNegativeTextureCoordinate;
|
||||
varying vec2 threeStepsPositiveTextureCoordinate;
|
||||
varying vec2 threeStepsNegativeTextureCoordinate;
|
||||
varying vec2 fourStepsPositiveTextureCoordinate;
|
||||
varying vec2 fourStepsNegativeTextureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
float centerIntensity = texture2D(inputImageTexture, centerTextureCoordinate).r;
|
||||
float oneStepPositiveIntensity = texture2D(inputImageTexture, oneStepPositiveTextureCoordinate).r;
|
||||
float oneStepNegativeIntensity = texture2D(inputImageTexture, oneStepNegativeTextureCoordinate).r;
|
||||
float twoStepsPositiveIntensity = texture2D(inputImageTexture, twoStepsPositiveTextureCoordinate).r;
|
||||
float twoStepsNegativeIntensity = texture2D(inputImageTexture, twoStepsNegativeTextureCoordinate).r;
|
||||
float threeStepsPositiveIntensity = texture2D(inputImageTexture, threeStepsPositiveTextureCoordinate).r;
|
||||
float threeStepsNegativeIntensity = texture2D(inputImageTexture, threeStepsNegativeTextureCoordinate).r;
|
||||
float fourStepsPositiveIntensity = texture2D(inputImageTexture, fourStepsPositiveTextureCoordinate).r;
|
||||
float fourStepsNegativeIntensity = texture2D(inputImageTexture, fourStepsNegativeTextureCoordinate).r;
|
||||
|
||||
float minValue = min(centerIntensity, oneStepPositiveIntensity);
|
||||
minValue = min(minValue, oneStepNegativeIntensity);
|
||||
minValue = min(minValue, twoStepsPositiveIntensity);
|
||||
minValue = min(minValue, twoStepsNegativeIntensity);
|
||||
minValue = min(minValue, threeStepsPositiveIntensity);
|
||||
minValue = min(minValue, threeStepsNegativeIntensity);
|
||||
minValue = min(minValue, fourStepsPositiveIntensity);
|
||||
minValue = min(minValue, fourStepsNegativeIntensity);
|
||||
|
||||
gl_FragColor = vec4(vec3(minValue), 1.0);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)initWithRadius:(NSUInteger)dilationRadius;
|
||||
{
|
||||
NSString *fragmentShaderForThisRadius = nil;
|
||||
NSString *vertexShaderForThisRadius = nil;
|
||||
|
||||
switch (dilationRadius)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusOneVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusOneFragmentShaderString;
|
||||
}; break;
|
||||
case 2:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusTwoVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusTwoFragmentShaderString;
|
||||
}; break;
|
||||
case 3:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusThreeVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusThreeFragmentShaderString;
|
||||
}; break;
|
||||
case 4:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
default:
|
||||
{
|
||||
vertexShaderForThisRadius = kGPUImageDilationRadiusFourVertexShaderString;
|
||||
fragmentShaderForThisRadius = kGPUImageErosionRadiusFourFragmentShaderString;
|
||||
}; break;
|
||||
}
|
||||
|
||||
if (!(self = [super initWithFirstStageVertexShaderFromString:vertexShaderForThisRadius firstStageFragmentShaderFromString:fragmentShaderForThisRadius secondStageVertexShaderFromString:vertexShaderForThisRadius secondStageFragmentShaderFromString:fragmentShaderForThisRadius]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [self initWithRadius:1]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#import "GPUImageTwoInputFilter.h"
|
||||
|
||||
@interface GPUImageExclusionBlendFilter : GPUImageTwoInputFilter
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
#import "GPUImageExclusionBlendFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageExclusionBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
varying highp vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
mediump vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
mediump vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
// Dca = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
|
||||
|
||||
gl_FragColor = vec4((overlay.rgb * base.a + base.rgb * overlay.a - 2.0 * overlay.rgb * base.rgb) + overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a), base.a);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageExclusionBlendFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
varying vec2 textureCoordinate2;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform sampler2D inputImageTexture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 base = texture2D(inputImageTexture, textureCoordinate);
|
||||
vec4 overlay = texture2D(inputImageTexture2, textureCoordinate2);
|
||||
|
||||
// Dca = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
|
||||
|
||||
gl_FragColor = vec4((overlay.rgb * base.a + base.rgb * overlay.a - 2.0 * overlay.rgb * base.rgb) + overlay.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlay.a), base.a);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageExclusionBlendFilter
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageExclusionBlendFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#import "GPUImageFilter.h"
|
||||
|
||||
@interface GPUImageExposureFilter : GPUImageFilter
|
||||
{
|
||||
GLint exposureUniform;
|
||||
}
|
||||
|
||||
// Exposure ranges from -10.0 to 10.0, with 0.0 as the normal level
|
||||
@property(readwrite, nonatomic) CGFloat exposure;
|
||||
|
||||
@end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#import "GPUImageExposureFilter.h"
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
NSString *const kGPUImageExposureFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying highp vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform highp float exposure;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);
|
||||
}
|
||||
);
|
||||
#else
|
||||
NSString *const kGPUImageExposureFragmentShaderString = SHADER_STRING
|
||||
(
|
||||
varying vec2 textureCoordinate;
|
||||
|
||||
uniform sampler2D inputImageTexture;
|
||||
uniform float exposure;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
|
||||
|
||||
gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);
|
||||
}
|
||||
);
|
||||
#endif
|
||||
|
||||
@implementation GPUImageExposureFilter
|
||||
|
||||
@synthesize exposure = _exposure;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and teardown
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
if (!(self = [super initWithFragmentShaderFromString:kGPUImageExposureFragmentShaderString]))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
exposureUniform = [filterProgram uniformIndex:@"exposure"];
|
||||
self.exposure = 0.0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
- (void)setExposure:(CGFloat)newValue;
|
||||
{
|
||||
_exposure = newValue;
|
||||
|
||||
[self setFloat:_exposure forUniform:exposureUniform program:filterProgram];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user