Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Javascript HTML5 (Ca)nvas (Man)ipulation
CoffeeScript CSS JavaScript PHP

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
demo
plugins
proxies
test
LICENSE
README.md
caman.js
caman.min.js

README.md

About the Project

The main focus of CamanJS is manipulating images using the HTML5 canvas and Javascript. It's a combination of a simple-to-use interface with advanced and efficient image/canvas editing techniques. It is also completely library independent and can be safely used next to jQuery, YUI, Scriptaculous, MooTools, etc.

CamanJS is very easy to extend with new filters and plugins, and it comes with a wide array of image editing functionality, which is only growing as the community makes more plugins.

For more information, I highly recommend taking a look at the official website where there is more comprehensive documentation and interactive demos.

NodeJS Compatibility

There is now a version of CamanJS that is made to work with NodeJS. It has all of the functionality of the normal browser version, including plugins. Take a look at the node branch for more information.

Block Renderer

CamanJS now uses a block rendering system, which gives some notable benefits:

  • Image rendering is now asynchronous, which means page rendering is no longer blocked.
  • Slower filters get a nice speed improvement due to (fake) render concurrency.

The way the renderer works is:

  1. When each filter function is called, instead of rendering immediately, they are added to a render queue.
  2. When the render() function is called, THEN the rendering actually begins.
  3. The next filter is shifted off of the render queue and sent to the rendering function.
  4. The image is divided into X number of blocks, which are simply horizontal partitions in the image.
  5. The filter is performed on each block simultaneously by using setTimeout() with a 0ms timeout value. Using setTimeout() is what forces each block to render asynchronously, even if the timeout value is 0.
  6. When a block is finished being rendered, it calls a function that tracks the # of blocks that have finished. When all blocks have finished, it signals that the filter is finished rendering.
  7. If there are more filters in the render queue, then steps 3-6 happen for each until the queue empties.
  8. When the queue empties, the callback supplied to render() is called.

The concurrency defaults to 4 blocks, but you can change this to whatever number you want by changing the Caman global variable renderBlocks:

Caman.renderBlocks = 8; // even number recommended

How to Use

Using CamanJS is simple. It goes something like this:

Caman('path/to/image.jpg', '#canvas-id', function () {
    this.brightness(10);
    this.contrast(-5);
    this.saturation(-50);
    // and so on...
    
    this.render();
});

You can also directly point to an image if you don't want to create a separate canvas element. In this case, the image element will be replaced with the canvas element, and the canvas will be drawn with the image content:

Caman("#image-id", function () {
    this.contrast(-5).render();
});

Finally, you can also use it like this:

Caman({
    src: 'path/to/image.jpg',
    canvas: '#canvas-id',
    ready: function () {
        this.brightness(10);
        this.contrast(-5);
        this.saturation(-50);
        // and so on...
        
        this.render();
    }
});

You can now even save images after they've been modified! With the current implementation, users will have to rename the file to something.(png|jpg) since they get redirected to the base64 encoding of the image and the browser doesn't know the file type. The save() function defaults to png, but you can override this and specify either png or jpg.

Caman('img/example.jpg', '#image-caman', function () {
  this.saturation(-20);
  this.brightness(10);
  
  this.render(function () {
    this.save('png'); // shows a download file prompt

    // or...
    this.toBase64(); //  base64 data URL representation of the image. useful if you want to upload the modified image.
  });
});

Editing Remote Images

CamanJS can even edit images that are stored remotely. Since the browser enforces a same-origin policy for editing canvas data, we have to load the image data via a local proxy.

CamanJS comes with a PHP proxy (you're welcome to add a proxy in the language of your choice) that you can use in the proxies folder. Before you use CamanJS for editing, all you have to do to enable the proxy is:

// Will use the PHP proxy in the proxies folder. You can also specify a URL instead of calling useProxy().
Caman.remoteProxy = Caman.useProxy('php');

If no proxy is defined when a remote image is attempted to be edited, an error will be thrown.

Caman Events

Currently CamanJS has three different events you can listen for, and it is very simple to add new events if you need to.

  • processStart
    • fired when a single image filter begins manipulating an image
  • processComplete
    • fired when a single image filter finishes manipulating an image
  • renderFinished
    • fired when all image filters are done and an image is finished being rendered

You may also find this extremely handy:

Caman('img/example.jpg', '#image-caman', function () {
  this.contrast(25);
  this.hue(5);
  this.colorize('#AF3D15', 25);
  this.saturation(-30);
  this.brightness(5);
  
  // Callback to render() fires when this image is done rendering.
  this.render(function () {
    console.log("Image finished rendering!");
  });
});

How to Extend

Extending CamanJS is easy as well. It's accomplished by adding functions onto the manip object. Below is a simple example of how to do so:

(function (Caman) {
    // Pixel-wise manipulation
    Caman.manip.fancy_filter = function (adjust) {
    
        // === IMPORTANT ===
        // this.process() will be run in a loop, and the
        // rgba object represents the current pixel's rgba
        // values. you *must* return the modified rgba object
        // for it to work properly and you *must* name the function
        // passed in the 2nd argument to process() the same name as
        // this filter.
        
        return this.process(adjust, function fancy_filter(rgba) {
            rgba.r += adjust;
            rgba.g -= adjust;
            rgba.b += adjust * 2;
            rgba.a = 0.9;
            
            // to get data about a pixel relative to this one currently
            // being processed, you can use getPixel([horiz_offset], [vert_offset]);
            var topLeft         = this.getPixelRelative(-1, 1);
            var topRight        = this.getPixelRelative(1, 1);
            var bottomLeft  = this.getPixelRelative(-1, -1);
            var bottomRight = this.getPixelRelative(1, -1);
            
            // gets a pixel from the canvas at the specified absolute coordinates
            var absPixel        = this.getPixel(200, 300);
            
            return rgba;
        });
        
        // If you want to use a convolution kernel instead of manipulating each
        // kernel directly, you can easily do it like this:
        Caman.manip.convolutionFilter = function () {
            return this.processKernel('Convolution Filter', [
                [1, 1, 1],
                [1, 1, 1],
                [1, 1, 1]
            ], 9);
        };
    };
}(Caman));

The arguments to processKernel are:

this.processKernel( Filter Name, Convolution Matrix (3x3 or 5x5), [Divisor], [Bias] );

Utility Functions

CamanJS comes with a set of utility functions that you may find very useful when extending it. In the main body of the function thats extending CamanJS, you can simply access them through Caman, e.g. Caman.rgb_to_hsl(). Their names should be pretty self explanatory:

  • rgb_to_hsl()
  • hsl_to_rgb()
  • rgb_to_hsv()
  • hsv_to_rgb()
  • rgb_to_xyz()
  • xyz_to_rgb()
  • xyz_to_lab()
  • lab_to_xyz()
  • hex_to_rgb()

Testing

CamanJS has both QUnit unit testing and a custom benchmarking page to monitor render times on a per-filter basis. Simply open test/index.html for the QUnit tests, and test/benchmark.html for the benchmarking tests.

If you add a filter, please edit test/benchmark/benchmark.js and add your filter (with appropriate args) to the list at the top of the file.

Project To-do

  • Implement a way to specify canvas elements by class instead of id, and apply effects to all found canvases.
  • Add lots more adjustments/effects

Project Contributors

Something went wrong with that request. Please try again.