• Jump To … +
    analyze.coffee autoload.coffee blender.coffee calculate.coffee caman.coffee convert.coffee event.coffee filter.coffee io.coffee layer.coffee logger.coffee module.coffee pixel.coffee plugin.coffee renderer.coffee store.coffee util.coffee blenders.coffee filters.coffee size.coffee blur.coffee camera.coffee compoundBlur.coffee edges.coffee posterize.coffee presets.coffee rotate.coffee stackBlur.coffee threshold.coffee
  • filters.coffee

  • ¶

    The filters define all of the built-in functionality that comes with Caman (as opposed to being provided by a plugin). All of these filters are ratherbasic, but are extremely powerful when many are combined. For information on creating plugins, check out the Plugin Creation page, and for information on using the plugins, check out the Built-In Functionality page.

    Fill Color

    Fills the canvas with a single solid color.

    Arguments

    Can take either separate R, G, and B values as arguments, or a single hex color value.

    Filter.register "fillColor", ->
      if arguments.length is 1
        color = Convert.hexToRGB arguments[0]
      else
        color =
          r: arguments[0]
          g: arguments[1]
          b: arguments[2]
    
      @process "fillColor", (rgba) ->
        rgba.r = color.r
        rgba.g = color.g
        rgba.b = color.b
        rgba.a = 255
        rgba
  • ¶

    Brightness

    Simple brightness adjustment

    Arguments

    Range is -100 to 100. Values < 0 will darken image while values > 0 will brighten.

    Filter.register "brightness", (adjust) ->
      adjust = Math.floor 255 * (adjust / 100)
    
      @process "brightness", (rgba) ->
        rgba.r += adjust
        rgba.g += adjust
        rgba.b += adjust
        rgba
  • ¶

    Saturation

    Adjusts the color saturation of the image.

    Arguments

    Range is -100 to 100. Values < 0 will desaturate the image while values > 0 will saturate it. If you want to completely desaturate the image, using the greyscale filter is highly recommended because it will yield better results.

    Filter.register "saturation", (adjust) ->
      adjust *= -0.01
    
      @process "saturation", (rgba) ->
        max = Math.max rgba.r, rgba.g, rgba.b
    
        rgba.r += (max - rgba.r) * adjust if rgba.r isnt max
        rgba.g += (max - rgba.g) * adjust if rgba.g isnt max
        rgba.b += (max - rgba.b) * adjust if rgba.b isnt max
        rgba
  • ¶

    Vibrance

    Similar to saturation, but adjusts the saturation levels in a slightly smarter, more subtle way. Vibrance will attempt to boost colors that are less saturated more and boost already saturated colors less, while saturation boosts all colors by the same level.

    Arguments

    Range is -100 to 100. Values < 0 will desaturate the image while values > 0 will saturate it. If you want to completely desaturate the image, using the greyscale filter is highly recommended because it will yield better results.

    Filter.register "vibrance", (adjust) ->
      adjust *= -1
    
      @process "vibrance", (rgba) ->
        max = Math.max rgba.r, rgba.g, rgba.b
        avg = (rgba.r + rgba.g + rgba.b) / 3
        amt = ((Math.abs(max - avg) * 2 / 255) * adjust) / 100
    
        rgba.r += (max - rgba.r) * amt if rgba.r isnt max
        rgba.g += (max - rgba.g) * amt if rgba.g isnt max
        rgba.b += (max - rgba.b) * amt if rgba.b isnt max
        rgba
  • ¶

    Greyscale

    An improved greyscale function that should make prettier results than simply using the saturation filter to remove color. It does so by using factors that directly relate to how the human eye perceves color and values. There are no arguments, it simply makes the image greyscale with no in-between.

    Algorithm adopted from http://www.phpied.com/image-fun/

    Filter.register "greyscale", (adjust) ->
      @process "greyscale", (rgba) ->
  • ¶

    Calculate the average value of the 3 color channels using the special factors

        avg = Calculate.luminance(rgba)
    
        rgba.r = avg
        rgba.g = avg
        rgba.b = avg
        rgba
  • ¶

    Contrast

    Increases or decreases the color contrast of the image.

    Arguments

    Range is -100 to 100. Values < 0 will decrease contrast while values > 0 will increase contrast. The contrast adjustment values are a bit sensitive. While unrestricted, sane adjustment values are usually around 5-10.

    Filter.register "contrast", (adjust) ->
      adjust = Math.pow((adjust + 100) / 100, 2)
    
      @process "contrast", (rgba) ->
  • ¶

    Red channel

        rgba.r /= 255;
        rgba.r -= 0.5;
        rgba.r *= adjust;
        rgba.r += 0.5;
        rgba.r *= 255;
  • ¶

    Green channel

        rgba.g /= 255;
        rgba.g -= 0.5;
        rgba.g *= adjust;
        rgba.g += 0.5;
        rgba.g *= 255;
  • ¶

    Blue channel

        rgba.b /= 255;
        rgba.b -= 0.5;
        rgba.b *= adjust;
        rgba.b += 0.5;
        rgba.b *= 255;
    
        rgba
  • ¶

    Hue

    Adjusts the hue of the image. It can be used to shift the colors in an image in a uniform fashion. If you are unfamiliar with Hue, I recommend reading this Wikipedia article.

    Arguments

    Range is 0 to 100 Sometimes, Hue is expressed in the range of 0 to 360. If that's the terminology you're used to, think of 0 to 100 representing the percentage of Hue shift in the 0 to 360 range.

    Filter.register "hue", (adjust) ->
      @process "hue", (rgba) ->
        hsv = Convert.rgbToHSV rgba.r, rgba.g, rgba.b
        
        h = hsv.h * 100
        h += Math.abs adjust
        h = h % 100
        h /= 100
        hsv.h = h
    
        {r, g, b} = Convert.hsvToRGB hsv.h, hsv.s, hsv.v
        rgba.r = r; rgba.g = g; rgba.b = b
        rgba
  • ¶

    Colorize

    Uniformly shifts the colors in an image towards the given color. The adjustment range is from 0 to 100. The higher the value, the closer the colors in the image shift towards the given adjustment color.

    Arguments

    This filter is polymorphic and can take two different sets of arguments. Either a hex color string and an adjustment value, or RGB colors and an adjustment value.

    Filter.register "colorize", ->
      if arguments.length is 2
        rgb = Convert.hexToRGB(arguments[0])
        level = arguments[1]
      else if arguments.length is 4
        rgb =
          r: arguments[0]
          g: arguments[1]
          b: arguments[2]
    
        level = arguments[3]
    
      @process "colorize", (rgba) ->
        rgba.r -= (rgba.r - rgb.r) * (level / 100)
        rgba.g -= (rgba.g - rgb.g) * (level / 100)
        rgba.b -= (rgba.b - rgb.b) * (level / 100)
        rgba
  • ¶

    Invert

    Inverts all colors in the image by subtracting each color channel value from 255. No arguments.

    Filter.register "invert", ->
      @process "invert", (rgba) ->
        rgba.r = 255 - rgba.r
        rgba.g = 255 - rgba.g
        rgba.b = 255 - rgba.b
        rgba
  • ¶

    Sepia

    Applies an adjustable sepia filter to the image.

    Arguments

    Assumes adjustment is between 0 and 100, which represents how much the sepia filter is applied.

    Filter.register "sepia", (adjust = 100) ->
      adjust /= 100
    
      @process "sepia", (rgba) ->
  • ¶

    All three color channels have special conversion factors that define what sepia is. Here we adjust each channel individually, with the twist that you can partially apply the sepia filter.

        rgba.r = Math.min(255, (rgba.r * (1 - (0.607 * adjust))) + (rgba.g * (0.769 * adjust)) + (rgba.b * (0.189 * adjust)));
        rgba.g = Math.min(255, (rgba.r * (0.349 * adjust)) + (rgba.g * (1 - (0.314 * adjust))) + (rgba.b * (0.168 * adjust)));
        rgba.b = Math.min(255, (rgba.r * (0.272 * adjust)) + (rgba.g * (0.534 * adjust)) + (rgba.b * (1- (0.869 * adjust))));
    
        rgba
  • ¶

    Gamma

    Adjusts the gamma of the image.

    Arguments

    Range is from 0 to infinity, although sane values are from 0 to 4 or 5. Values between 0 and 1 will lessen the contrast while values greater than 1 will increase it.

    Filter.register "gamma", (adjust) ->
      @process "gamma", (rgba) ->
        rgba.r = Math.pow(rgba.r / 255, adjust) * 255
        rgba.g = Math.pow(rgba.g / 255, adjust) * 255
        rgba.b = Math.pow(rgba.b / 255, adjust) * 255
        rgba
  • ¶

    Noise

    Adds noise to the image on a scale from 1 - 100. However, the scale isn't constrained, so you can specify a value > 100 if you want a LOT of noise.

    Filter.register "noise", (adjust) ->
      adjust = Math.abs(adjust) * 2.55
      
      @process "noise", (rgba) ->
        rand = Calculate.randomRange adjust * -1, adjust
    
        rgba.r += rand
        rgba.g += rand
        rgba.b += rand
        rgba
  • ¶

    Clip

    Clips a color to max values when it falls outside of the specified range.

    Arguments

    Supplied value should be between 0 and 100.

    Filter.register "clip", (adjust) ->
      adjust = Math.abs(adjust) * 2.55
    
      @process "clip", (rgba) ->
        if rgba.r > 255 - adjust
          rgba.r = 255
        else if rgba.r < adjust
          rgba.r = 0
    
        if rgba.g > 255 - adjust
          rgba.g = 255
        else if rgba.g < adjust
          rgba.g = 0
          
        if rgba.b > 255 - adjust
          rgba.b = 255
        else if rgba.b < adjust
          rgba.b = 0
    
        rgba
  • ¶

    Channels

    Lets you modify the intensity of any combination of red, green, or blue channels individually.

    Arguments

    Must be given at least one color channel to adjust in order to work. Options format (must specify 1 - 3 colors):

    {
      red: 20,
      green: -5,
      blue: -40
    }
    Filter.register "channels", (options) ->
      return @ if typeof options isnt "object"
    
      for own chan, value of options
        if value is 0
          delete options[chan]
          continue
    
        options[chan] /= 100
    
      return @ if options.length is 0
    
      @process "channels", (rgba) ->
        if options.red?
          if options.red > 0
            rgba.r += (255 - rgba.r) * options.red
          else
            rgba.r -= rgba.r * Math.abs(options.red)
    
        if options.green?
          if options.green > 0
            rgba.g += (255 - rgba.g) * options.green
          else
            rgba.g -= rgba.g * Math.abs(options.green)
    
        if options.blue?
          if options.blue > 0
            rgba.b += (255 - rgba.b) * options.blue
          else
            rgba.b -= rgba.b * Math.abs(options.blue)
    
        rgba
  • ¶

    Curves

    Curves implementation using Bezier curve equation. If you're familiar with the Curves functionality in Photoshop, this works in a very similar fashion.

    Arguments.

      chan - [r, g, b, rgb]
      start - [x, y] (start of curve; 0 - 255)
      ctrl1 - [x, y] (control point 1; 0 - 255)
      ctrl2 - [x, y] (control point 2; 0 - 255)
      end   - [x, y] (end of curve; 0 - 255)
    

    The first argument represents the channels you wish to modify with the filter. It can be an array of channels or a string (for a single channel). The rest of the arguments are 2-element arrays that represent point coordinates. They are specified in the same order as shown in this image to the right. The coordinates are in the range of 0 to 255 for both X and Y values.

    The x-axis represents the input value for a single channel, while the y-axis represents the output value.

    Filter.register "curves", (chans, cps...) ->
  • ¶

    If channels are in a string, split to an array

      chans = chans.split("") if typeof chans is "string"
      chans = ['r', 'g', 'b'] if chans[0] == "v"
    
      if cps.length < 3 or cps.length > 4
  • ¶

    might want to give a warning now

        throw "Invalid number of arguments to curves filter"
    
      start = cps[0]
      ctrl1 = cps[1]
      ctrl2 = if cps.length == 4 then cps[2] else cps[1]
      end = cps[cps.length - 1]
  • ¶

    Generate a bezier curve

      bezier = Calculate.bezier start, ctrl1, ctrl2, end, 0, 255
  • ¶

    If the curve starts after x = 0, initialize it with a flat line until the curve begins.

      bezier[i] = start[1] for i in [0...start[0]] if start[0] > 0
  • ¶

    ... and the same with the end point

      bezier[i] = end[1] for i in [end[0]..255] if end[0] < 255
    
      @process "curves", (rgba) ->
  • ¶

    Now that we have the bezier curve, we do a basic hashmap lookup to find and replace color values.

        rgba[chans[i]] = bezier[rgba[chans[i]]] for i in [0...chans.length]
        rgba
  • ¶

    Exposure

    Adjusts the exposure of the image by using the curves function.

    Arguments

    Range is -100 to 100. Values < 0 will decrease exposure while values > 0 will increase exposure.

    Filter.register "exposure", (adjust) ->
      p = Math.abs(adjust) / 100
    
      ctrl1 = [0, 255 * p]
      ctrl2 = [255 - (255 * p), 255]
    
      if adjust < 0
        ctrl1 = ctrl1.reverse()
        ctrl2 = ctrl2.reverse()
    
      @curves 'rgb', [0, 0], ctrl1, ctrl2, [255, 255]