LvJing 0.6.alpha.2

LvJing 0.6.alpha.2

Maintained by Ke Yang.



LvJing 0.6.alpha.2

  • By
  • Ke Yang

LvJing

[![CI Status](https://img.shields.io/travis/Ke Yang/LvJing.svg?style=flat)](https://travis-ci.org/Ke Yang/LvJing) Version License Platform

LvJing is a minimum graphic filter kernel based on Metal, it offers extensible interface and protocol for inheritance and assembling. The architecture is inspired by Metal by Tutorials (Caroline Begbie & Marius Horga, raywenderlich.com) and famous-big-brother GPUImage. LvJing merely acts as a carriage of Metal shaders, it's mathematics and, behind all, your creativity that really shine. Have fun.

Example

Example contains a single view demonstrating a binary filter tree consists of several basic filters inherit from LvJing filter. To run the example project, clone the repo, and run pod install from the Example directory first.

screenshot

Write your own filter?

Let's build a gamma filter with its own shaders:

// Gamma.metal

#include <metal_stdlib>
using namespace metal;

struct VertexOut {
   float4 position [[position]];
   float2 uv;
};

float4 adjustGamma(float4 in, float power) {
   return float4(pow(in.rgb, power), in.a);
}

vertex VertexOut vertex_main(const constant float4 *vertexArray [[buffer(0)]],
                             unsigned int vid [[vertex_id]])
{
   float4 currentVertex = vertexArray[vid];
   VertexOut out {
      .position = float4(currentVertex.x, currentVertex.y, 0, 1),
      .uv = float2(currentVertex.z, currentVertex.w)
   };
   return out;
}

fragment float4 fragment_gamma(VertexOut in [[stage_in]],
                               constant float &gamma [[buffer(0)]],
                               texture2d<float> colorTexture [[texture(0)]],
                               sampler textureSampler)
{
   float4 originColor = colorTexture.sample(textureSampler, in.uv);
   if (gamma == 1.0) {
      return originColor;
   }
   return adjustGamma(originColor, gamma);
}
// GammaFilter.swift

import MetalKit
import LvJing

public final class GammaFilter: LvJing {
   
   public var gamma: Float = 1.0
   
   public init(resolution: CGSize) {
      super.init(
         resolution: resolution,
         libraryURL: nil,
         vertexFunctionName: "vertex_main",
         fragmentFunctionName: "fragment_gamma")
   }
   
   public override func setFragmentBytesFor(encoder: MTLRenderCommandEncoder) {
      encoder.setFragmentBytes(
         &gamma,
         length: MemoryLayout<Float>.stride,
         index: 0)
   }
}

Chain them together?

struct BinaryTreePreview: View {
   
   @State private var originImageA: CGImage!
   
   @State private var originImageB: CGImage!
   
   @State private var inputA: InputPlaceholder!
   
   @State private var inputB: InputPlaceholder!
   
   @State private var alphaFilterA: AffineTransformFilter!
   
   @State private var alphaFilterB: GammaFilter!
   
   @State private var betaFilterA: AffineTransformFilter!
   
   @State private var betaFilterB: LuminosityFilter!
   
   @State private var terminal: SwipeTransitionFilter!

   @State private var bufferPool: CVPixelBufferPool!
   
   @State private var finalOuput: CGImage!

   func buildUp() {
      self.inputA = InputPlaceholder()
      self.inputB = InputPlaceholder()
      // alpha branch
      self.originImageA => self.inputA +> self.alphaFilterA +> self.alphaFilterB +> self.terminal
      // beta branch
      self.originImageB => self.inputB +> self.betaFilterA +> self.betaFilterB +> self.terminal
   }

   func process() {
      var buffer: CVPixelBuffer?
      let success = CVPixelBufferPoolCreatePixelBuffer(
         nil,
         self.bufferPool!,
         &buffer)
      guard success == kCVReturnSuccess else { return }

      // make filter tree propagates from its entrances;
      self.terminal.propagate()
      // draw final image into buffer;
      self.terminal => buffer!
      // do what you want with it...
   }

   func breakDown() {
      self.terminal.disconnect()
   }
}

Requirements

  • Swift ONLY
  • iOS 9.0+
  • macOS 10.15.5
  • Catalyst compatible

Installation

LvJing is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'LvJing'

Author

Ke Yang, [email protected]

License

LvJing is available under the MIT license. See the LICENSE file for more info.