Skip to content

Add ToneCurve filter; #274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,16 @@ let filterOperations: Array<FilterOperationInterface> = [
},
filterOperationType:.singleInput
),
// TODO : Tone curve
FilterOperation(
filter:{ToneCurveFilter()},
listName:"Tone curve",
titleName:"Tone curve",
sliderConfiguration:.enabled(minimumValue:0.0, maximumValue:1.00, initialValue:0.5),
sliderUpdateCallback: {(filter, sliderValue) in
filter.blueControlPoints = [Position(0, 0), Position(0.5, sliderValue), Position(1, 1)]
},
filterOperationType:.singleInput
),
FilterOperation(
filter:{HighlightsAndShadows()},
listName:"Highlights and shadows",
Expand Down
14 changes: 14 additions & 0 deletions framework/GPUImage.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,10 @@
BCFF46FC1CBAF85000A0C521 /* TransformOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF46FB1CBAF85000A0C521 /* TransformOperation.swift */; };
BCFF46FE1CBB0C1F00A0C521 /* AverageColorExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF46FD1CBB0C1F00A0C521 /* AverageColorExtractor.swift */; };
BCFF47081CBB443B00A0C521 /* CameraConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF47071CBB443B00A0C521 /* CameraConversion.swift */; };
E9E37505215920AF00CA9F00 /* ToneCurveFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E37504215920AF00CA9F00 /* ToneCurveFilter.swift */; };
E9E37506215920AF00CA9F00 /* ToneCurveFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E37504215920AF00CA9F00 /* ToneCurveFilter.swift */; };
E9E37517215923D200CA9F00 /* ACVFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E37516215923D200CA9F00 /* ACVFile.swift */; };
E9E37518215923D200CA9F00 /* ACVFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E37516215923D200CA9F00 /* ACVFile.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -686,6 +690,9 @@
BCFF46FF1CBB0D8900A0C521 /* AverageColor_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = AverageColor_GL.fsh; path = Source/Operations/Shaders/AverageColor_GL.fsh; sourceTree = "<group>"; };
BCFF47001CBB0D8900A0C521 /* AverageColor.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = AverageColor.vsh; path = Source/Operations/Shaders/AverageColor.vsh; sourceTree = "<group>"; };
BCFF47071CBB443B00A0C521 /* CameraConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CameraConversion.swift; path = Source/CameraConversion.swift; sourceTree = "<group>"; };
E9E37504215920AF00CA9F00 /* ToneCurveFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ToneCurveFilter.swift; path = Source/Operations/ToneCurveFilter.swift; sourceTree = "<group>"; };
E9E37507215920E000CA9F00 /* ToneCurve_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = ToneCurve_GL.fsh; path = Source/Operations/Shaders/ToneCurve_GL.fsh; sourceTree = "<group>"; };
E9E37516215923D200CA9F00 /* ACVFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ACVFile.swift; path = Source/Operations/ACVFile.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -783,6 +790,9 @@
BC4C85F31C9F114600FD95D8 /* Color processing */ = {
isa = PBXGroup;
children = (
E9E37516215923D200CA9F00 /* ACVFile.swift */,
E9E37504215920AF00CA9F00 /* ToneCurveFilter.swift */,
E9E37507215920E000CA9F00 /* ToneCurve_GL.fsh */,
BC4C85F11C9F05EB00FD95D8 /* Luminance.swift */,
BC4C85F01C9F054100FD95D8 /* Luminance_GL.fsh */,
BC7FD0851CA62E1100037949 /* BrightnessAdjustment.swift */,
Expand Down Expand Up @@ -1404,6 +1414,7 @@
BCFF46E21CBADB3E00A0C521 /* SingleComponentGaussianBlur.swift in Sources */,
BC7FD0F71CB0620E00037949 /* ChromaKeyBlend.swift in Sources */,
BC7FD0861CA62E1100037949 /* BrightnessAdjustment.swift in Sources */,
E9E37517215923D200CA9F00 /* ACVFile.swift in Sources */,
BC7FD1631CB17C8D00037949 /* ImageOrientation.swift in Sources */,
BC9673411C8B897100FB64C2 /* FramebufferCache.swift in Sources */,
BC7FD0BB1CA7799B00037949 /* Halftone.swift in Sources */,
Expand Down Expand Up @@ -1488,6 +1499,7 @@
BC7FD0F31CB0612700037949 /* AddBlend.swift in Sources */,
BC7FD1931CB1D38500037949 /* Size.swift in Sources */,
BC4EE1741CB3711600AD8A65 /* GaussianBlur.swift in Sources */,
E9E37505215920AF00CA9F00 /* ToneCurveFilter.swift in Sources */,
BC7FD0A21CA6316600037949 /* Haze.swift in Sources */,
BCB279EA1C8D0E800013E213 /* Camera.swift in Sources */,
BC4C85FA1C9F169D00FD95D8 /* SaturationAdjustment.swift in Sources */,
Expand Down Expand Up @@ -1587,6 +1599,7 @@
BC9E35871E52571D00B8604F /* PrewittEdgeDetection.swift in Sources */,
BC9E35551E52522200B8604F /* ConvertedShaders_GLES.swift in Sources */,
BC9E35D21E52580700B8604F /* SoftLightBlend.swift in Sources */,
E9E37518215923D200CA9F00 /* ACVFile.swift in Sources */,
BC9E353C1E524D8400B8604F /* Color.swift in Sources */,
BC9E357B1E5256F500B8604F /* AverageColorExtractor.swift in Sources */,
BC9E35701E5256D200B8604F /* LuminanceRangeReduction.swift in Sources */,
Expand Down Expand Up @@ -1671,6 +1684,7 @@
BC9E35621E5256A500B8604F /* OperationGroup.swift in Sources */,
BC9E35381E524D7E00B8604F /* OpenGLRendering.swift in Sources */,
BC9E35B41E5257A900B8604F /* ToonFilter.swift in Sources */,
E9E37506215920AF00CA9F00 /* ToneCurveFilter.swift in Sources */,
BC9E354F1E52508A00B8604F /* RawDataInput.swift in Sources */,
BC9E35681E5256BD00B8604F /* GammaAdjustment.swift in Sources */,
BC9E35A81E52578400B8604F /* Vignette.swift in Sources */,
Expand Down
69 changes: 69 additions & 0 deletions framework/Source/Operations/ACVFile.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Foundation

public class ACVFile {
fileprivate var version: UInt16
fileprivate var curvesCount: UInt16

public var redControlPoints: [Position]
public var greenControlPoints: [Position]
public var blueControlPoints: [Position]
public var rgbCompositeControlPoints: [Position]

public convenience init?(fileName: String) throws {
guard let url = Bundle.main.url(forResource: fileName, withExtension: "acv") else {
return nil
}
let data = try Data(contentsOf: url)
self.init(data: data)
}

public init?(data: Data) {
guard data.count > 0 else { return nil }
let stepSize = 2

var offset = 0
self.version = data.scanValue(start: offset, length: stepSize)
offset += stepSize

self.curvesCount = data.scanValue(start: offset, length: stepSize)
offset += stepSize

let pointRate: Float = 1.0 / 255

var curves: [[Position]] = []

for _ in 0 ..< curvesCount {
let pointCount = data.scanValue(start: offset, length: stepSize)
offset += stepSize

var points = [Position]()

for _ in 0 ..< pointCount {
let y = data.scanValue(start: offset, length: stepSize)
offset += stepSize
let x = data.scanValue(start: offset, length: stepSize)
offset += stepSize

points.append(Position(Float(x) * pointRate, Float(y) * pointRate))
}
curves.append(points)
}
self.rgbCompositeControlPoints = curves[0]
self.redControlPoints = curves[1]
self.greenControlPoints = curves[2]
self.blueControlPoints = curves[3]
}
}

extension Data {
fileprivate func scanValue(start: Int, length: Int) -> UInt16 {
var bytes = [UInt8](repeating: 0, count: count)
copyBytes(to: &bytes, count: count)

let slice = Array(bytes[start ..< start + length])
let u16raw = UnsafePointer(slice).withMemoryRebound(to: UInt16.self, capacity: length) { $0.pointee }
let u16 = CFSwapInt16BigToHost(u16raw)

return u16
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public let ThresholdEdgeDetectionFragmentShader = "varying vec2 textureCoordinat
public let ThresholdSketchFragmentShader = "varying vec2 textureCoordinate;\n varying vec2 leftTextureCoordinate;\n varying vec2 rightTextureCoordinate;\n \n varying vec2 topTextureCoordinate;\n varying vec2 topLeftTextureCoordinate;\n varying vec2 topRightTextureCoordinate;\n \n varying vec2 bottomTextureCoordinate;\n varying vec2 bottomLeftTextureCoordinate;\n varying vec2 bottomRightTextureCoordinate;\n \n uniform sampler2D inputImageTexture;\n uniform float threshold;\n \n uniform float edgeStrength;\n \n void main()\n {\n float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;\n float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;\n float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;\n float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;\n float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;\n float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;\n float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;\n float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;\n float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;\n h = max(0.0, h);\n float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;\n v = max(0.0, v);\n \n float mag = length(vec2(h, v)) * edgeStrength;\n mag = 1.0 - step(threshold, mag);\n \n gl_FragColor = vec4(vec3(mag), 1.0);\n }\n "
public let ThresholdedNonMaximumSuppressionFragmentShader = "uniform sampler2D inputImageTexture;\n \n varying vec2 textureCoordinate;\n varying vec2 leftTextureCoordinate;\n varying vec2 rightTextureCoordinate;\n \n varying vec2 topTextureCoordinate;\n varying vec2 topLeftTextureCoordinate;\n varying vec2 topRightTextureCoordinate;\n \n varying vec2 bottomTextureCoordinate;\n varying vec2 bottomLeftTextureCoordinate;\n varying vec2 bottomRightTextureCoordinate;\n \n uniform float threshold;\n \n void main()\n {\n float bottomColor = texture2D(inputImageTexture, bottomTextureCoordinate).r;\n float bottomLeftColor = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;\n float bottomRightColor = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;\n vec4 centerColor = texture2D(inputImageTexture, textureCoordinate);\n float leftColor = texture2D(inputImageTexture, leftTextureCoordinate).r;\n float rightColor = texture2D(inputImageTexture, rightTextureCoordinate).r;\n float topColor = texture2D(inputImageTexture, topTextureCoordinate).r;\n float topRightColor = texture2D(inputImageTexture, topRightTextureCoordinate).r;\n float topLeftColor = texture2D(inputImageTexture, topLeftTextureCoordinate).r;\n \n // Use a tiebreaker for pixels to the left and immediately above this one\n float multiplier = 1.0 - step(centerColor.r, topColor);\n multiplier = multiplier * (1.0 - step(centerColor.r, topLeftColor));\n multiplier = multiplier * (1.0 - step(centerColor.r, leftColor));\n multiplier = multiplier * (1.0 - step(centerColor.r, bottomLeftColor));\n \n float maxValue = max(centerColor.r, bottomColor);\n maxValue = max(maxValue, bottomRightColor);\n maxValue = max(maxValue, rightColor);\n maxValue = max(maxValue, topRightColor);\n \n float finalValue = centerColor.r * step(maxValue, centerColor.r) * multiplier;\n finalValue = step(threshold, finalValue);\n \n gl_FragColor = vec4(finalValue, finalValue, finalValue, 1.0);\n //\n // gl_FragColor = vec4((centerColor.rgb * step(maxValue, step(threshold, centerColor.r)) * multiplier), 1.0);\n }\n "
public let TiltShiftFragmentShader = "varying vec2 textureCoordinate;\n varying vec2 textureCoordinate2;\n \n uniform sampler2D inputImageTexture;\n uniform sampler2D inputImageTexture2;\n \n uniform float topFocusLevel;\n uniform float bottomFocusLevel;\n uniform float focusFallOffRate;\n \n void main()\n {\n vec4 sharpImageColor = texture2D(inputImageTexture, textureCoordinate);\n vec4 blurredImageColor = texture2D(inputImageTexture2, textureCoordinate2);\n \n float blurIntensity = 1.0 - smoothstep(topFocusLevel - focusFallOffRate, topFocusLevel, textureCoordinate2.y);\n blurIntensity += smoothstep(bottomFocusLevel, bottomFocusLevel + focusFallOffRate, textureCoordinate2.y);\n \n gl_FragColor = mix(sharpImageColor, blurredImageColor, blurIntensity);\n }\n "
public let ToneCurveFragmentShader = "varying vec2 textureCoordinate;\n uniform sampler2D inputImageTexture;\n uniform sampler2D toneCurveTexture;\n \n void main()\n {\n vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n float redCurveValue = texture2D(toneCurveTexture, vec2(textureColor.r, 0.0)).r;\n float greenCurveValue = texture2D(toneCurveTexture, vec2(textureColor.g, 0.0)).g;\n float blueCurveValue = texture2D(toneCurveTexture, vec2(textureColor.b, 0.0)).b;\n \n gl_FragColor = vec4(redCurveValue, greenCurveValue, blueCurveValue, textureColor.a);\n }\n "
public let ToonFragmentShader = "varying vec2 textureCoordinate;\n varying vec2 leftTextureCoordinate;\n varying vec2 rightTextureCoordinate;\n \n varying vec2 topTextureCoordinate;\n varying vec2 topLeftTextureCoordinate;\n varying vec2 topRightTextureCoordinate;\n \n varying vec2 bottomTextureCoordinate;\n varying vec2 bottomLeftTextureCoordinate;\n varying vec2 bottomRightTextureCoordinate;\n \n uniform sampler2D inputImageTexture;\n \n uniform float intensity;\n uniform float threshold;\n uniform float quantizationLevels;\n \n const vec3 W = vec3(0.2125, 0.7154, 0.0721);\n \n void main()\n {\n vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n \n float bottomLeftIntensity = texture2D(inputImageTexture, bottomLeftTextureCoordinate).r;\n float topRightIntensity = texture2D(inputImageTexture, topRightTextureCoordinate).r;\n float topLeftIntensity = texture2D(inputImageTexture, topLeftTextureCoordinate).r;\n float bottomRightIntensity = texture2D(inputImageTexture, bottomRightTextureCoordinate).r;\n float leftIntensity = texture2D(inputImageTexture, leftTextureCoordinate).r;\n float rightIntensity = texture2D(inputImageTexture, rightTextureCoordinate).r;\n float bottomIntensity = texture2D(inputImageTexture, bottomTextureCoordinate).r;\n float topIntensity = texture2D(inputImageTexture, topTextureCoordinate).r;\n float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;\n float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;\n \n float mag = length(vec2(h, v));\n \n vec3 posterizedImageColor = floor((textureColor.rgb * quantizationLevels) + 0.5) / quantizationLevels;\n \n float thresholdTest = 1.0 - step(threshold, mag);\n \n gl_FragColor = vec4(posterizedImageColor * thresholdTest, textureColor.a);\n }\n "
public let TransformVertexShader = "attribute vec4 position;\n attribute vec4 inputTextureCoordinate;\n \n uniform mat4 transformMatrix;\n uniform mat4 orthographicMatrix;\n \n varying vec2 textureCoordinate;\n \n void main()\n {\n gl_Position = transformMatrix * vec4(position.xyz, 1.0) * orthographicMatrix;\n textureCoordinate = inputTextureCoordinate.xy;\n }\n "
public let TwoInputVertexShader = "attribute vec4 position;\n attribute vec4 inputTextureCoordinate;\n attribute vec4 inputTextureCoordinate2;\n \n varying vec2 textureCoordinate;\n varying vec2 textureCoordinate2;\n \n void main()\n {\n gl_Position = position;\n textureCoordinate = inputTextureCoordinate.xy;\n textureCoordinate2 = inputTextureCoordinate2.xy;\n }\n "
Expand Down
Loading