class Analyze
- constructor: (@c) ->
-
- calculateLevels: ->
- levels =
- r: {}
- g: {}
- b: {}
diff --git a/mig/assets/css/V3/jquery.managers.css b/mig/assets/css/V3/jquery.managers.css index 8f1302a9a..51b8afb6d 100644 --- a/mig/assets/css/V3/jquery.managers.css +++ b/mig/assets/css/V3/jquery.managers.css @@ -4,7 +4,7 @@ # --- BEGIN_HEADER --- # # jquery.managers - style overrides for multiple pages -# Copyright (C) 2003-2016 The MiG Project lead by Brian Vinter +# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH # # This file is part of MiG. # @@ -157,188 +157,6 @@ tbody tr td div.sortkey { display: none; } overflow: scroll; height: 500px; } -.fm_previews { - float: right; - width: 100%; - height: 0px; - background-color: #FFF; - visibility: hidden; -} -.fm_preview_caman { - float: right; - border: 0px; - width: 0px; - height: 0px; - background-color: #FFF; - visibility: hidden; -} -.fm_preview_paraview { - float: right; - border: 0px; - width: 0px; - height: 0px; - background-color: #FFF; - visibility: hidden; -} - -.fm_preview_menubar { - float: left; - border: 0px; - width: 20px; - height: 100%; - background-color: #EEE; -} -.fm_preview_menubar_entry { - border: 0px; - width: 19px; - margin-top: 1px; - margin-left: 1px; -} -.fm_preview_left_tile { - float: left; - border: 0px; - width: 0px; - height: 100%; - background-color: #FFF; - overflow-x: hidden; - overflow-y: auto; -} -.fm_preview_center_tile { - float: left; - border: 0px; - padding: 0px; - width: 0px; - height: 100%; - background-color: #FFF; - overflow-x: hidden; - overflow-y: hidden; -} -.fm_preview_right_tile { - float: left; - border: 0px; - width: 0px; - height: 100%; - background-color: #FFF; - overflow-x: hidden; - overflow-y: auto; -} -#fm_preview_left_tile_histogram { - display: block; - margin-top: 1%; - margin-left: 5%; - border: 0; - width : 80%; - height : 0px; - background-color: #FFF; -} -#fm_preview_left_tile_histogram_actions { - display: block; - margin-left: 5%; - border: 0; - width : 80%; - background-color: #FFF; -} -#fm_preview_left_output { - margin-left: 5%; - margin-right: 5%; - background-color: #FFF; -} -#fm_preview_right_output { - margin-left: 5%; - margin-right: 5%; - background-color: #FFF; -} -#fm_preview_left_tile .noUi-target { - border-radius: 4px; - border: 1px solid #D3D3D3; - box-shadow: inset 0 1px 1px #F0F0F0, 0 3px 6px -5px #BBB; -} -#fm_preview_left_tile .noUi-horizontal { - height: 8px !important; -} -#fm_preview_left_tile .noUi-horizontal .noUi-handle { - width: 6px; - height: 12px; - left: -3px; - top: -3px; -} -#fm_preview_left_tile .noUi-handle { - border: 1px solid #D9D9D9; - border-radius: 3px; - background: #FFF; - cursor: default; - box-shadow: inset 0 0 1px #FFF, - inset 0 1px 7px #EBEBEB, - 0 3px 6px -3px #BBB; -} -#fm_preview_left_tile .noUi-handle:before, -#fm_preview_left_tile .noUi-handle:after { - content: ""; - display: block; - position: absolute; - height: 0px; - width: 0px; - background: #E8E7E6; - left: -3px; - top: -3px; -} -#fm_preview_left_tile .noUi-handle:after { - left: 17px; -} -#fm_preview_left_tile .noUi-handle { -} -#fm_preview_histogram_min_max_slider .noUi-origin { -} -#fm_preview_histogram_min_max_slider .noUi-handle { -} -#fm_preview_histogram_min_max_slider .noUi-handle-lower { -} -#fm_preview_histogram_min_max_slider .noUi-handle-upper { -} -#fm_preview_left_tile .noUi-base { - background: lightgrey; -} -#fm_preview_histogram_min_max_slider .noUi-connect { - background: black; -} -#fm_preview_histogram_min_max_slider .noUi-background{ - background: lightgrey; -} -#fm_preview_histogram_min_max_slider .noUi-state-tap { -} -#fm_preview_histogram_min_max_slider .fm_preview_histogram_min_max_slider_tooltip { - display: block; - position: absolute; - border: 0px solid #D9D9D9; - font-size: 12px; - top: -5px; - padding: 0px; - left: -13px; - text-align: center; - width: 30px; -} -.fm_preview_min_max_slider_tooltip_org { - display: block; - position: absolute; - border: 1px solid #D9D9D9; - font: 400 12px/12px Arial; - border-radius: 3px; - background: #fff; - top: -43px; - padding: 5px; - left: -9px; - text-align: center; - width: 50px; -} -/* hide paraview SVG bar which only shows up on IE and covers refresh button */ -.fm_previews .pipelineSVG svg rect { - display: none; -} -/* avoid waiting banner from flying around */ -.fm_previews .app-wait-start-page { - position: absolute; -} - .fm_metaio_list ul.edit { list-style-image: url(/images/icons/page_white_edit.png); } diff --git a/mig/cgi-bin/imagepreview.py b/mig/cgi-bin/imagepreview.py deleted file mode 100755 index 6b2814ed9..000000000 --- a/mig/cgi-bin/imagepreview.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# --- BEGIN_HEADER --- -# -# imagepreview - front end to file metadata I/O -# Copyright (C) 2003-2015 The MiG Project lead by Brian Vinter -# -# This file is part of MiG. -# -# MiG is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# MiG is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# -- END_HEADER --- -# - -"""Image preview helper page""" -from __future__ import absolute_import - -import cgi -import cgitb -# cgitb.enable() - -from mig.shared.functionality.imagepreview import main -from mig.shared.cgiscriptstub import run_cgi_script - -run_cgi_script(main) diff --git a/mig/images/css/jquery.managers.css b/mig/images/css/jquery.managers.css index 5813f875e..bf3020661 100644 --- a/mig/images/css/jquery.managers.css +++ b/mig/images/css/jquery.managers.css @@ -4,7 +4,7 @@ # --- BEGIN_HEADER --- # # jquery.managers - style overrides for multiple pages -# Copyright (C) 2003-2016 The MiG Project lead by Brian Vinter +# Copyright (C) 2003-2025 The MiG Project by the Science HPC Center at UCPH # # This file is part of MiG. # @@ -161,188 +161,6 @@ tbody tr td div.sortkey { display: none; } overflow: scroll; height: 500px; } -.fm_previews { - float: right; - width: 100%; - height: 0px; - background-color: #FFF; - visibility: hidden; -} -.fm_preview_caman { - float: right; - border: 0px; - width: 0px; - height: 0px; - background-color: #FFF; - visibility: hidden; -} -.fm_preview_paraview { - float: right; - border: 0px; - width: 0px; - height: 0px; - background-color: #FFF; - visibility: hidden; -} - -.fm_preview_menubar { - float: left; - border: 0px; - width: 20px; - height: 100%; - background-color: #EEE; -} -.fm_preview_menubar_entry { - border: 0px; - width: 19px; - margin-top: 1px; - margin-left: 1px; -} -.fm_preview_left_tile { - float: left; - border: 0px; - width: 0px; - height: 100%; - background-color: #FFF; - overflow-x: hidden; - overflow-y: auto; -} -.fm_preview_center_tile { - float: left; - border: 0px; - padding: 0px; - width: 0px; - height: 100%; - background-color: #FFF; - overflow-x: hidden; - overflow-y: hidden; -} -.fm_preview_right_tile { - float: left; - border: 0px; - width: 0px; - height: 100%; - background-color: #FFF; - overflow-x: hidden; - overflow-y: auto; -} -#fm_preview_left_tile_histogram { - display: block; - margin-top: 1%; - margin-left: 5%; - border: 0; - width : 80%; - height : 0px; - background-color: #FFF; -} -#fm_preview_left_tile_histogram_actions { - display: block; - margin-left: 5%; - border: 0; - width : 80%; - background-color: #FFF; -} -#fm_preview_left_output { - margin-left: 5%; - margin-right: 5%; - background-color: #FFF; -} -#fm_preview_right_output { - margin-left: 5%; - margin-right: 5%; - background-color: #FFF; -} -#fm_preview_left_tile .noUi-target { - border-radius: 4px; - border: 1px solid #D3D3D3; - box-shadow: inset 0 1px 1px #F0F0F0, 0 3px 6px -5px #BBB; -} -#fm_preview_left_tile .noUi-horizontal { - height: 8px !important; -} -#fm_preview_left_tile .noUi-horizontal .noUi-handle { - width: 6px; - height: 12px; - left: -3px; - top: -3px; -} -#fm_preview_left_tile .noUi-handle { - border: 1px solid #D9D9D9; - border-radius: 3px; - background: #FFF; - cursor: default; - box-shadow: inset 0 0 1px #FFF, - inset 0 1px 7px #EBEBEB, - 0 3px 6px -3px #BBB; -} -#fm_preview_left_tile .noUi-handle:before, -#fm_preview_left_tile .noUi-handle:after { - content: ""; - display: block; - position: absolute; - height: 0px; - width: 0px; - background: #E8E7E6; - left: -3px; - top: -3px; -} -#fm_preview_left_tile .noUi-handle:after { - left: 17px; -} -#fm_preview_left_tile .noUi-handle { -} -#fm_preview_histogram_min_max_slider .noUi-origin { -} -#fm_preview_histogram_min_max_slider .noUi-handle { -} -#fm_preview_histogram_min_max_slider .noUi-handle-lower { -} -#fm_preview_histogram_min_max_slider .noUi-handle-upper { -} -#fm_preview_left_tile .noUi-base { - background: lightgrey; -} -#fm_preview_histogram_min_max_slider .noUi-connect { - background: black; -} -#fm_preview_histogram_min_max_slider .noUi-background{ - background: lightgrey; -} -#fm_preview_histogram_min_max_slider .noUi-state-tap { -} -#fm_preview_histogram_min_max_slider .fm_preview_histogram_min_max_slider_tooltip { - display: block; - position: absolute; - border: 0px solid #D9D9D9; - font-size: 12px; - top: -5px; - padding: 0px; - left: -13px; - text-align: center; - width: 30px; -} -.fm_preview_min_max_slider_tooltip_org { - display: block; - position: absolute; - border: 1px solid #D9D9D9; - font: 400 12px/12px Arial; - border-radius: 3px; - background: #fff; - top: -43px; - padding: 5px; - left: -9px; - text-align: center; - width: 50px; -} -/* hide paraview SVG bar which only shows up on IE and covers refresh button */ -.fm_previews .pipelineSVG svg rect { - display: none; -} -/* avoid waiting banner from flying around */ -.fm_previews .app-wait-start-page { - position: absolute; -} - .fm_metaio_list ul.edit { list-style-image: url(/images/icons/page_white_edit.png); } diff --git a/mig/images/js/jquery.filemanager.js b/mig/images/js/jquery.filemanager.js index 4cf082805..3d86bb0ea 100644 --- a/mig/images/js/jquery.filemanager.js +++ b/mig/images/js/jquery.filemanager.js @@ -577,9 +577,6 @@ if (jQuery) (function($){ buttonSpacing: buttonSpacing } }; - if (options['imagesettings']) { - layout = preview.update_fm_layout(layout); - } return layout; } @@ -611,14 +608,6 @@ if (jQuery) (function($){ return layout; } - function clickEvent(el) { - if (!options['imagesettings']) { - console.debug('clickEvent: imagesettings is false'); - } else { - preview.open($(el).attr(pathAttribute)); - } - } - function doubleClickEvent(el) { if (clickaction !== undefined) { clickaction(el); @@ -1691,11 +1680,6 @@ if (jQuery) (function($){ }); $("#import_freeze_dialog").dialog('open'); $("#import_freeze_form input[name='freeze_id']").focus(); - }, - imagesettings: function(action, el, pos) { - var rel_path = $(el).attr(pathAttribute); - var open_dialog = mig_imagesettings_init("imagesettings_dialog", rel_path, options); - open_dialog("Image Settings"); } }; @@ -1722,7 +1706,6 @@ if (jQuery) (function($){ refreshLayoutOnInit: false, enableSubmit: true, selectOnly: false, - imagesettings: false, enableGDP: false, maxStreamSize: 64*1024*1024 }; @@ -1739,40 +1722,13 @@ if (jQuery) (function($){ } */ }); - // Initiate preview - - var preview = null; - if (options['imagesettings']) { - preview = new Preview( - get_fm_layout, - options, - enable_debug); - } - - $.fn.refresh_fm_layout = refresh_fm_layout; - // Define window behavior $(window).on("resize", function() { - if (options['imagesettings']) { - preview.set_visibility('hidden'); - } console.debug("refresh layout on resize"); $.fn.refresh_fm_layout(); }); - $(window).on("debouncedresize", function() { - if (options['imagesettings']) { - preview.refresh(); - } - - }); - - $(window).on('beforeunload', function(){ - if (options['imagesettings']) { - preview.close(); - } - }); // reestablish defaults for undefined actions: $.each(callbacks, function(name, fct) { @@ -2263,8 +2219,6 @@ if (jQuery) (function($){ "sep2": "---------", "rename": {name: "Rename", icon: "rename"}, "sep3": "---------", - "imagesettings": {name: "Image Settings", icon: "edit"}, - "sep4": "---------", "sharelinks": {"name": "Share Link", icon: "sharelink", "items": {"createsharelink": {name: "Create", icon: "createsharelink"}, "importsharelink": {name: "Import", icon: "importsharelink"} @@ -2349,10 +2303,6 @@ if (jQuery) (function($){ } } }; - if (!options["imagesettings"]) { - delete directory_menu["imagesettings"]; - delete directory_menu["sep4"]; - } if (!options["enableSubmit"]) { delete file_menu["submit-sep"]; delete file_menu["submit"]; @@ -2456,13 +2406,6 @@ if (jQuery) (function($){ function bindHandlers(folder_pane) { bindContextMenus(); - console.debug("add click handler"); - $.fn.fmSelect("").on("click", - "tr.file", - function(event) { - clickEvent(this); - }); - console.debug("add dblclick handler"); //$.fn.fmSelect("").off("dblclick", "tr.file, tr.directory"); $.fn.fmSelect("").on("dblclick", @@ -2606,15 +2549,6 @@ if (jQuery) (function($){ bindHandlers(folder_pane); showBranch(folder_pane, encodeURI(options.root)); - - /* - * Bind preview buttons - */ - - if (options['imagesettings']) { - preview.bind_buttons(); - } - /** * Bind handlers for forms. This is ridiculous and tedious repetitive code. @@ -2913,7 +2847,6 @@ function mig_filechooser_init(name, callback, files_only, start_path) { datatransfersbutton: false, datasafetypopup: false, refreshLayoutOnInit: true, - imagesettings: false, selectOnly: true }, // doubleclick callback action @@ -3442,512 +3375,3 @@ function mig_fancyuploadchunked_init(name, options, callback) { return do_d; } - -/* Image settings dialog */ - -function mig_imagesettings_init(name, path, options) { - var edit_form_values = { - extension: '', - settings_status: '', - settings_recursive: '', - image_type: '', - data_type: '', - volume_slice_filepattern: '', - offset: 0, - x_dimension: 0, - y_dimension: 0, - z_dimension: 0, - preview_cutoff_min: 0.0, - preview_cutoff_max: 0.0, - }; - - init_html_and_handlers(); - - $("#" + name).dialog( - // see http://jqueryui.com/docs/dialog/ for options - { - autoOpen: false, - modal: true, - width: 480, - position: { my: "top", at: "top+100px", of: window}, - buttons: dialog_list_buttons() - }); - - function dialog_list_buttons() { - return { - 'New': function() { - edit(null); - }, - 'Clear': function() { - remove_all(); - }, - 'Refresh': function() { - show_list(); - }, - 'Close': function() { - $("#" + name).dialog("close"); - } - }; - } - - function dialog_new_buttons() { - return { - 'Create': function() { - $("#imagesettings_form").submit(); - }, - 'Cancel': function() { - show_list(); - }, - 'Close': function() { - $("#" + name).dialog("close"); - } - }; - } - - function dialog_edit_buttons() { - return { - 'Update': function() { - $("#imagesettings_form").submit(); - }, - 'Remove': function() { - remove(); - }, - 'Back': function() { - show_list(); - }, - 'Close': function() { - $("#" + name).dialog("close"); - } - }; - } - - // Initializes html and handlers - - function init_html_and_handlers() { - $("#imagesettings_edit").hide(); - $("#imagesettings_edit_tabs").hide(); - - // Handle image settings file form submit - - $("#imagesettings_form").ajaxForm({ - target: '#imagesettings_output', dataType: 'json', - success: function(responseObject, statusText) { - var msg; - var errors = $(this).renderError(responseObject); - var warnings = $(this).renderWarning(responseObject); - if (errors.length > 0) { - msg = errors; - console.debug(errors); - } else if (warnings.length > 0) { - msg = warnings; - console.debug(warnings); - } else { - msg = 'Image file settings updated'; - } - show_list(msg); - }, - error: function(jqXHR, textStatus, errorThrown) { - console.error("imagesettings_form error: "+ textStatus); - console.debug("imagesettings_form error: "+ errorThrown); - } - }); - - // Changes based on image_type - - $("#imagesettings_form select[name='image_type']").on('change', function() { - var image_type_value = $("#imagesettings_form select[name='image_type']").val(); - var data_type_value = $("#imagesettings_form select[name='data_type']").val(); - - // Set data_type based on image_type - - if (image_type_value === 'tiff') { - data_type_value = 'uint16'; - } - else { - data_type_value = edit_form_values['data_type']; - } - $("#imagesettings_form select[name='data_type']").val(data_type_value).prop('selected', true); - - // Only display options for raw data if 'raw' is selected' - - if (image_type_value === 'raw') { - $("#imagesettings_edit_image_type_raw").show({duration: options.expandSpeed, - easing: options.expandEasing }); - } - else { - $("#imagesettings_edit_image_type_raw").hide({duration: options.expandSpeed, - easing: options.expandEasing }); - } - }); - - // Change Sub-folder checkbox value when checked/unchecked - - $("#imagesettings_form input[name='settings_recursive']").on('change', function() { - if ($("#imagesettings_form input[name='settings_recursive']").prop("checked")) { - $("#imagesettings_form input[name='settings_recursive']").val('True'); - } - else { - $("#imagesettings_form input[name='settings_recursive']").val('False'); - } - }); - } - - function show_list(output_msg) { - - // Generate image extension list - var html_out; - - if (output_msg === undefined) { - html_out = ''; - } - else { - html_out = '
' + output_msg + '
'; - } - $("#imagesettings_output").html(html_out); - $("#imagesettings_list").hide(({duration: options.expandSpeed, - easing: options.expandEasing })); - $("#imagesettings_edit_tabs").hide(({duration: options.expandSpeed, - easing: options.expandEasing })); - - // Retrieve image settings list - - $.ajax({ - url: 'imagepreview.py', - data: { path: path, - output_format: 'json' , - action: 'list_settings'}, - type: "GET", - dataType: "json", - cache: false, - success: function (jsonRes) { - var errors = $(this).renderError(jsonRes); - var warnings = $(this).renderWarning(jsonRes); - if (errors.length > 0) { - console.debug(errors); - } else if (warnings.length > 0) { - console.debug(warnings); - } - - var i; - var extension_list = []; - var image_settings_status_list = []; - var image_settings_progress_list = []; - var image_count_list = []; - var volume_settings_status_list = []; - var volume_settings_progress_list = []; - var volume_count_list = []; - - // Generate extension, status, progress and count lists for each entry - - for (i = 0; i < jsonRes.length; i++) { - if (jsonRes[i].object_type === 'image_settings_list') { - extension_list = extension_list.concat(jsonRes[i].extension_list); - image_settings_status_list = image_settings_status_list.concat(jsonRes[i].image_settings_status_list); - image_settings_progress_list = image_settings_progress_list.concat(jsonRes[i].image_settings_progress_list); - image_count_list = image_count_list.concat(jsonRes[i].image_count_list); - volume_settings_status_list = image_settings_status_list.concat(jsonRes[i].image_settings_status_list); - volume_settings_progress_list = volume_settings_progress_list.concat(jsonRes[i].volume_settings_progress_list); - volume_count_list = volume_count_list.concat(jsonRes[i].volume_count_list); - } - } - - // Generate html for each entry - - var html_out = 'Image file extensions:
'; - if (image_settings_status_list.length === 0) { - html_out += '-- No folder image settings configured --
'; - } - - for (i = 0; i < image_settings_status_list.length; i++) { - if (image_settings_status_list[i].toLowerCase() === 'ready' || - image_settings_status_list[i].toLowerCase() === 'failed') { - html_out += 'Image: ' + image_filepath + '
' - + 'Image Type: ' + image_meta.image_type + '
' - + 'Data Type: ' + image_meta.data_type + '
' - + 'Offset: ' + image_meta.offset + '
' - + 'X Dimension: ' + image_meta.x_dimension + '
' - + 'Y Dimension: ' + image_meta.y_dimension + '
' - + 'Min Value: ' + Number(image_meta.min_value).toExponential(this.get_format_decimals()) + '
' - + 'Max Value: ' + Number(image_meta.max_value).toExponential(this.get_format_decimals()) + '
' - + 'Mean Value: ' + Number(image_meta.mean_value).toExponential(this.get_format_decimals()) + '
' - + 'Median Value: ' + Number(image_meta.median_value).toExponential(this.get_format_decimals()) + '
'; - - // TODO: Move input fields HTML to fileman.py ? - - left_html_out += '' - + '' - + '' - + '' - + '' - + '' - + '
' - + '
' - - $("#fm_preview_right_output").html(right_html_out); - $("#fm_preview_left_output").html(left_html_out); -} - -Preview.prototype.update_fm_layout = function(layout) { - var fileManagerInnerHeight = layout.fm.innerHeight; - var fileFolderInnerHeight = layout.fm.fileFolderInnerHeight; - var previewInnerHeight = 0; - var previewInnerHeightFrac = 0.50; - var centerTileWidthFrac = 0.40; - var previewWidth = $("#fm_filemanager .fm_previews").outerWidth() - /* NOTE: compensate for rounding(?) with a couple of extra pixels. - preview overflow with FF@Linux in 1920x1080 resolution with zoom twice - */ - var previewInnerWidth = $("#fm_filemanager .fm_previews").width() - - $("#fm_filemanager .fm_preview_menubar").outerWidth() - 2; - var previewCenterTileWidth = Math.floor(previewInnerWidth * centerTileWidthFrac); - var previewLeftTileWidth = Math.floor((previewInnerWidth - previewCenterTileWidth)/2); - var previewRightTileWidth = previewLeftTileWidth; - - - if (this.settings.zoom == 0) { - previewInnerHeight = 0; - fileFolderInnerHeight = fileManagerInnerHeight; - } - else if (this.settings.zoom == 1) { - previewInnerHeight = fileFolderInnerHeight * previewInnerHeightFrac; - - fileFolderInnerHeight = fileManagerInnerHeight - previewInnerHeight; - } - else if (this.settings.zoom > 1) { - previewInnerHeight = fileManagerInnerHeight; - fileFolderInnerHeight = 0; - } - - layout.fm.fileFolderInnerHeight = fileFolderInnerHeight; - layout.fm.preview = { - width: previewWidth, - innerHeight: previewInnerHeight, - innerWidth: previewInnerWidth, - leftTileWidth: previewLeftTileWidth, - centerTileWidth: previewCenterTileWidth, - rightTileWidth: previewRightTileWidth - }; - return layout; -} - -Preview.prototype.update_html_layout = function(callback) { - var layout = (typeof this.settings.layout === "function") ? - _this.settings.layout() : - _this.settings.layout; - $("#fm_filemanager .fm_previews").css("height", layout.fm.preview.innerHeight + "px"); - $("#fm_filemanager .fm_preview_caman").css("width", layout.fm.preview.innerWidth + "px"); - $("#fm_filemanager .fm_preview_left_tile").css("width", layout.fm.preview.leftTileWidth + "px"); - $("#fm_filemanager .fm_preview_center_tile").css("width", layout.fm.preview.centerTileWidth + "px"); - $("#fm_filemanager .fm_preview_right_tile").css("width", layout.fm.preview.rightTileWidth + "px"); - $("#fm_filemanager .fm_preview_paraview").css("width", layout.fm.preview.innerWidth + "px"); - $("#fm_filemanager .pv-viewport").css("width", layout.fm.preview.innerWidth + "px"); -} - -Preview.prototype.init_image_settings = function(image_settings, image_meta, volume_meta) { - - // Check if preview files are ready - - if (image_settings.image_settings_status.toLowerCase() == 'ready' ) { - $("#preview_histogram_set_cutoff_button").attr('disabled', false); - } else { - $("#preview_histogram_set_cutoff_button").attr('disabled', true); - - } - - - // Check if preview volumes are ready - - if (volume_meta != null && image_settings.volume_settings_status.toLowerCase() == 'ready') { - this.paraview.set_volume_xdmf(volume_meta.preview_xdmf_filepath); - $("#fm_preview_menubar_paraview").css('opacity', 1.0); - this.settings.volume = true; - } else { - $("#fm_preview_menubar_paraview").css('opacity', 0.5); - this.settings.volume = false; - console.debug('volume_meta is null or _NOT_ ready'); - } -} - -Preview.prototype.bind_buttons = function() { - var _this = this; - $("#fm_preview_menubar_zoom_out").on('click', - function(event) { - console.debug('fm_preview_menubar_zoom_out'); - _this.zoom_out(); - }); - - $("#fm_preview_menubar_zoom_in").on('click', - function(event) { - console.debug('fm_preview_menubar_zoom_in'); - _this.zoom_in(); - }); - - $("#fm_preview_menubar_refresh").on('click', - function(event) { - console.debug('fm_preview_menubar_refresh'); - _this.refresh(); - }); - - $("#fm_preview_menubar_paraview").on('click', - function(event) { - console.debug('fm_preview_menubar_paraview'); - _this.toggle_paraview(); - }); - - $("#preview_histogram_reset_button").on('click', - function(event) { - _this.caman.reset(); - }); - - $("#preview_histogram_set_cutoff_button").on('click', - function(event) { - - // Disable button - - $("#preview_histogram_set_cutoff_button").attr('disabled', true); - - var path = $(".fm_previews input[name=fm_preview_base_path]").val(); - var extension = $(".fm_previews input[name=fm_preview_extension]").val(); - var min_value = $("#fm_preview_left_output input[name='current_min_value']").val(); - var max_value = $("#fm_preview_left_output input[name='current_max_value']").val(); - - var error_callback = function(errors) { - $("#fm_preview_left_output").html(errors); - console.error(errors); - } - - var warning_callback = function(warnings) { - $("#fm_preview_left_output").html(warnings); - console.warn(warnings); - } - - var ok_callback = function(image_setting, image_meta, volume_meta) { - console.debug('preview_histogram_set_cutoff_button OK'); - } - _this.update_image_dir(path, - extension, - min_value, - max_value, - ok_callback, - error_callback, - warning_callback); - } - ); -} - -Preview.prototype.get_preview_histogram_data = function(image_meta) { - return new Uint32Array(image_meta.preview_histogram); -} - -Preview.prototype.get_format_decimals = function() { - var format_decimals = 4; - - return format_decimals; -} - -Preview.prototype.open = function(path) { - // Disable content depending buttons by default - var _this = this; - - var error_callback = function(errors) { - $("#fm_preview_right_output").html(errors); - $("#fm_preview_left_output").html(''); - console.error(errors); - } - var warning_callback = function(warnings) { - $("#fm_preview_right_output").html(warnings); - $("#fm_preview_left_output").html(''); - console.warn(warnings); - } - var ok_callback = function(image_setting, image_meta, volume_meta) { - var preview_histogram = _this.get_preview_histogram_data(image_meta); - _this.init_tiles_HTML(image_meta); - _this.init_image_settings(image_setting, image_meta, volume_meta); - _this.caman.set_histogram_data(preview_histogram); - _this.settings.zoom = 1; - _this.settings.caman = true; - _this.show(function() { - _this.caman.load(image_meta.preview_image_url); - }); - } - _this.get_image_file(path, ok_callback, error_callback, warning_callback); -} - -Preview.prototype.close = function() { - this.paraview.close(); -} - -Preview.prototype.refresh = function(callback) { - var _this = this; - console.debug('refresh callback: ' + callback); - - var refresh_callback = function() { - console.debug('refresh_callback: zoom: ' + _this.settings.zoom + ', caman: ' + _this.settings.caman + ', paraview: ' + _this.settings.paraview); - _this.caman.refresh(_this.settings.caman, callback); - _this.paraview.refresh(_this.settings.paraview, callback); - } - _this.show(refresh_callback); -} - -Preview.prototype.zoom_out = function(callback) { - console.debug('zoom_out: paraview: ' + this.settings.paraview); - console.debug('zoom_out: caman: ' + this.settings.caman); - console.debug('zoom_out: zoom: ' + this.settings.zoom); - if (this.settings.paraview == true) { - this.toggle_paraview(callback); - } - else if (this.settings.caman == true) { - if (this.settings.zoom > this.settings.min_zoom) { - this.settings.last_zoom = this.settings.zoom; - this.settings.zoom -= 1; - if (this.settings.zoom == this.settings.min_zoom) { - this.settings.caman = false; - } - console.debug('zoom_out check: paraview: ' + this.settings.paraview); - console.debug('zoom_out check: caman: ' + this.settings.caman); - console.debug('zoom_out check: zoom: ' + this.settings.zoom); - this.refresh(callback); - } - } -} - -Preview.prototype.zoom_in = function(callback) { - if (this.settings.paraview == false && - this.settings.zoom < this.settings.max_zoom) { - this.settings.caman = true; - this.settings.last_zoom = this.settings.zoom; - this.settings.zoom += 1; - this.refresh(callback); - } -} - -Preview.prototype.set_visibility_left_tile = function(visibility) { - $("#fm_preview_left_tile").css("visibility", visibility); - $("#fm_preview_left_tile_histogram").css("visibility", visibility); - $("#fm_preview_left_tile_histogram_actions").css("visibility", visibility); - $("#fm_preview_left_output").css("visibility", visibility); -} - -Preview.prototype.set_visibility_center_tile = function(visibility) { - $("#fm_preview_center_tile").css("visibility", visibility); -} - -Preview.prototype.set_visibility_right_tile = function(visibility) { - $("#fm_preview_right_tile").css("visibility", visibility); - $("#fm_preview_right_output").css("visibility", visibility); -} - -Preview.prototype.set_visibility_caman = function(visibility) { - $("#fm_preview_caman").css('visibility', visibility); - this.set_visibility_left_tile(visibility); - this.set_visibility_center_tile(visibility); - this.set_visibility_right_tile(visibility); -} -Preview.prototype.set_visibility_paraview = function(visibility) { - $("#fm_preview_paraview").css('visibility', visibility); -} - -Preview.prototype.set_visibility = function(visibility) { - $("#fm_preview_menubar").css('visibility', visibility); - var caman_visibility; - var paraview_visibility; - - if (this.settings.paraview == false) { - $("#fm_preview_caman").css('height', '100%'); - $("#fm_preview_paraview").css('height', '0%'); - caman_visibility = visibility; - paraview_visibility = 'hidden'; - } - else { - caman_visibility = 'hidden'; - $("#fm_preview_caman").css('height', '0%'); - $("#fm_preview_paraview").css('height', '100%'); - paraview_visibility = visibility; - } - - this.set_visibility_caman(caman_visibility); - this.set_visibility_paraview(paraview_visibility); -} - -Preview.prototype.show = function(callback) { - var _this = this; - var layout = (typeof this.settings.layout === "function") ? - _this.settings.layout() : - _this.settings.layout; - //console.debug('layout: ' + Object.keys(layout)); - //_this.debug_js_object(layout); - /*for (var key in Object.keys(layout)) { - console.debug('layout -> ' + key + ' : ' + layout[key]); - } - */ - console.debug('preview show'); - - - var visibility = (_this.settings.zoom == 0) ? - 'hidden' : - 'visible'; - var animate_speed = (_this.settings.zoom < _this.settings.last_zoom) ? - _this.settings.options.collapseSpeed : - _this.settings.options.expandSpeed; - var animate_easing = (_this.settings.zoom < _this.settings.last_zoom) ? - _this.settings.options.collapseEasing : - _this.settings.options.expandEasing; - $("#fm_filemanager .fm_folders").animate( - {height: layout.fm.fileFolderInnerHeight + 'px'}, - {duration: animate_speed, - easing: animate_easing}); - $("#fm_filemanager .fm_files").animate( - {height: layout.fm.fileFolderInnerHeight + 'px'}, - {duration: animate_speed, - easing: animate_easing}); - $("#fm_filemanager .fm_preview_caman").css("width", layout.fm.preview.innerWidth + "px"); - $("#fm_filemanager .fm_preview_left_tile").css("width", layout.fm.preview.leftTileWidth + "px"); - $("#fm_filemanager .fm_preview_center_tile").css("width", layout.fm.preview.centerTileWidth + "px"); - $("#fm_filemanager .fm_preview_right_tile").css("width", layout.fm.preview.rightTileWidth + "px"); - $("#fm_filemanager .fm_preview_paraview").css("width", layout.fm.preview.innerWidth + "px"); - $("#fm_filemanager .fm_previews").animate( - {height: layout.fm.preview.innerHeight + 'px'}, - {duration: animate_speed, - easing: animate_easing, - complete: function() { - _this.set_visibility(visibility); - if (typeof callback === "function") { - console.debug('show callback called: ' + callback); - callback(); - } - }}); -} - -Preview.prototype.toggle_paraview = function(callback) { - - console.debug('toggle_paraview'); - if (this.settings.volume == true) { - if (this.settings.paraview == true) { - this.settings.paraview = false; - this.settings.caman = true; - this.settings.zoom = this.settings.last_zoom; - //this.paraview.close(); - } - else { - this.settings.paraview = true; - this.settings.caman = false; - this.settings.last_zoom = this.settings.zoom; - this.settings.zoom = this.settings.max_zoom; - //callback = $.proxy(this.paraview.open, this.paraview); - } - - // TODO: Make contrast slider for volume instead of using the min/max from sliced view - - var min_value = $("#fm_preview_left_output input[name='current_min_value']").val(); - var max_value = $("#fm_preview_left_output input[name='current_max_value']").val(); - - var refresh_proxy = $.proxy(this.refresh, this, callback); - - console.debug('toggle_paraview zoom: ' + this.settings.zoom); - this.paraview.set_value_range(min_value, max_value, refresh_proxy); - } -} - -Preview.prototype.get_image_file = function(path, ok_callback, error_callback, warning_callback) { - console.debug('get_image_file -> path: ' + path); - - $.ajax({ - url: 'imagepreview.py', - data: { path: path, - output_format: 'json', - action: 'get', - }, - type: "GET", - dataType: "json", - cache: false, - success: function(jsonRes, textStatus) { - var i; - var errors = $(this).renderError(jsonRes); - var warnings = $(this).renderWarning(jsonRes); - var image_settings = null; - var image_meta = null; - var volume_meta = null; - - for (i = 0; i < jsonRes.length; i++) { - if (jsonRes[i].object_type == 'image_setting') { - image_setting = jsonRes[i]; - } else if (jsonRes[i].object_type == 'image_meta') { - image_meta = jsonRes[i]; - } else if (jsonRes[i].object_type == 'volume_meta') { - volume_meta = jsonRes[i]; - } - } - if (errors.length > 0) { - if (typeof error_callback === "function") { - console.error(errors); - } else { - error_callback(errors); - } - - } else if (warnings.length > 0) { - if (typeof warning_callback === "function") { - console.warn(warnings); - } else { - warning_callback(warnings); - } - } else { - if (typeof ok_callback === "function") { - ok_callback(image_setting, image_meta, volume_meta); - } - } - }, - error: function(jqXHR, textStatus, errorThrown) { - console.error("get_image_file error: " + errorThrown); - } - }); -} - - -Preview.prototype.get_image_dir = function( - path, - extension, - ok_callback, - error_callback, - warning_callback) { - - console.debug('get_image_dir -> path: ' + path + ', extension: ' + extension); - $.ajax({ - url: 'imagepreview.py', - data: { path: path, - extension: extension, - output_format: 'json', - action: 'get_setting', - }, - type: "GET", - dataType: "json", - cache: false, - success: function(jsonRes, textStatus) { - var i; - var errors = $(this).renderError(jsonRes); - var warnings = $(this).renderWarning(jsonRes); - var image_setting = null; - for (i = 0; i < jsonRes.length; i++) { - if (jsonRes[i].object_type == 'image_setting') { - image_setting = jsonRes[i]; - } - } - - if (errors.length > 0) { - if (typeof error_callback === "function") { - console.error(errors); - } else { - error_callback(errors); - } - } else if (warnings.length > 0) { - if (typeof warning_callback === "function") { - console.warn(warnings); - } else { - warning_callback(warnings); - } - } else { - if (typeof ok_callback === "function") { - ok_callback(image_setting); - } - } - }, - error: function(jqXHR, textStatus, errorThrown) { - console.error("get_image_dir error: " + errorThrown); - } - }); -} - -Preview.prototype.update_image_dir = function( - path, - extension, - preview_cutoff_min, - preview_cutoff_max, - ok_callback, - error_callback, - warning_callback) { - - console.debug('get_image_dir -> path: ' + path + ', extension: ' + extension); - - $.ajax({ - url: 'imagepreview.py', - data: { path: path, - extension: extension, - output_format: 'json', - action: 'update_setting', - preview_cutoff_min: preview_cutoff_min, - preview_cutoff_max: preview_cutoff_max, - }, - type: "POST", - dataType: "json", - cache: false, - success: function(jsonRes, textStatus) { - var i; - var errors = $(this).renderError(jsonRes); - var warnings = $(this).renderWarning(jsonRes); - var image_setting = null; - for (i = 0; i < jsonRes.length; i++) { - if (jsonRes[i].object_type == 'image_setting') { - image_setting = jsonRes[i]; - } - } - if (errors.length > 0) { - if (typeof error_callback === "function") { - console.error(errors); - } else { - error_callback(errors); - } - } else if (warnings.length > 0) { - if (typeof warning_callback === "function") { - console.warn(warnings); - } else { - warning_callback(warnings); - } - } else { - if (typeof ok_callback === "function") { - ok_callback(image_setting); - } - } - }, - error: function(jqXHR, textStatus, errorThrown) { - console.error("getImageDir error: " + errorThrown); - } - }); -} diff --git a/mig/images/lib/CamanJS b/mig/images/lib/CamanJS deleted file mode 120000 index 45dbf0c1a..000000000 --- a/mig/images/lib/CamanJS +++ /dev/null @@ -1 +0,0 @@ -CamanJS-4.1.1 \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/.gitignore b/mig/images/lib/CamanJS-4.1.1/.gitignore deleted file mode 100644 index a09ffe584..000000000 --- a/mig/images/lib/CamanJS-4.1.1/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.DS_Store -*.sublime-project -*.sublime-workspace -sftp-config.json -node_modules/ -dist/*.coffee diff --git a/mig/images/lib/CamanJS-4.1.1/.gitmodules b/mig/images/lib/CamanJS-4.1.1/.gitmodules deleted file mode 100644 index b3087377d..000000000 --- a/mig/images/lib/CamanJS-4.1.1/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "test/qunit"] - path = test/qunit - url = https://github.com/jquery/qunit.git -[submodule "src/plugins"] - path = src/plugins - url = https://github.com/meltingice/CamanJS-Plugins.git diff --git a/mig/images/lib/CamanJS-4.1.1/.travis.yml b/mig/images/lib/CamanJS-4.1.1/.travis.yml deleted file mode 100644 index c2ba3f90b..000000000 --- a/mig/images/lib/CamanJS-4.1.1/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: node_js -node_js: - - 0.8 \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/Cakefile b/mig/images/lib/CamanJS-4.1.1/Cakefile deleted file mode 100644 index c852ea731..000000000 --- a/mig/images/lib/CamanJS-4.1.1/Cakefile +++ /dev/null @@ -1,193 +0,0 @@ -fs = require 'fs' -{exec} = require 'child_process' -util = require 'util' -#{jsmin} = require 'jsmin' - -try - packer = require 'packer' -catch err - packer = null - -targetName = "caman" - -### -CoffeeScript Options -### -csSrcDir = "src" -csTargetDir = "dist" - -targetCoffee = "#{csTargetDir}/caman" - -targetCoreJS = "#{csTargetDir}/#{targetName}.js" -targetCoreMinJS = "#{csTargetDir}/#{targetName}.min.js" -targetCorePackJS = "#{csTargetDir}/#{targetName}.pack.js" -coffeeCoreOpts = "-j #{targetName}.js -o #{csTargetDir}" - -targetFullJS = "#{csTargetDir}/#{targetName}.full.js" -targetFullMinJS = "#{csTargetDir}/#{targetName}.full.min.js" -targetFullPackJS = "#{csTargetDir}/#{targetName}.full.pack.js" -coffeeFullOpts = "-j #{targetName}.full.js -o #{csTargetDir}" - -# All source files listed in include order -coffeeFiles = [ - "core/util" - - # Core library - "core/caman" - - # Everything else - "core/analyze" - "core/autoload" - "core/blender" - "core/calculate" - "core/convert" - "core/event" - "core/filter" - "core/io" - "core/layer" - "core/logger" - "core/pixelinfo" - "core/plugin" - "core/renderer" - "core/store" - - # Non-core files - "lib/blenders" - "lib/filters" - "lib/size" -] - -pluginsFolder = "src/plugins/src" - -### -Event System -### -finishedCallback = {} -finished = (type) -> - finishedCallback[type]() if finishedCallback[type]? - -finishListener = (type, cb) -> - finishedCallback[type] = cb - -getPlugins = -> - content = "" - - util.log "Gathering plugin files in #{pluginsFolder}" - pluginFiles = fs.readdirSync pluginsFolder - - util.log "Discovered #{pluginFiles.length} plugins" - for plugin in pluginFiles - continue if fs.statSync("#{pluginsFolder}/#{plugin}").isDirectory() - content += fs.readFileSync("#{pluginsFolder}/#{plugin}", "utf8") + "\n\n" - - return content - -### -Tasks -### -task 'docs', 'Generates documentation for the coffee files', -> - util.log 'Invoking docco on the CoffeeScript source files' - - files = coffeeFiles - files[i] = "src/#{files[i]}.coffee" for i in [0...files.length] - - pluginFiles = fs.readdirSync pluginsFolder - for plugin in pluginFiles - continue if fs.statSync("#{pluginsFolder}/#{plugin}").isDirectory() - files.push "#{pluginsFolder}/#{plugin}" - - exec "node_modules/docco/bin/docco -l parallel #{files.join(' ')}", (err, stdout, stderr) -> - util.log err if err - util.log "Documentation built into docs/ folder." - -task 'watch', 'Automatically recompile the CoffeeScript files when updated', -> - util.log "Watching for changes in #{csSrcDir}" - - for jsFile in coffeeFiles then do (jsFile) -> - fs.watchFile "#{csSrcDir}/#{jsFile}.coffee", (curr, prev) -> - if +curr.mtime isnt +prev.mtime - util.log "#{csSrcDir}/#{jsFile}.coffee updated" - invoke 'build' - -task 'build', 'Compile and minify all CoffeeScript source files', -> - finishListener 'js', -> invoke 'minify' - invoke 'compile' - -option '-m', '--map', 'Compile with source maps' -task 'compile', 'Compile all CoffeeScript source files', (options) -> - util.log "Building #{targetCoreJS} and #{targetFullJS}" - contents = [] - remaining = coffeeFiles.length - - util.log "Appending #{coffeeFiles.length} files to #{targetCoffee}.coffee" - - for file, index in coffeeFiles then do (file, index) -> - fs.readFile "#{csSrcDir}/#{file}.coffee", "utf8", (err, fileContents) -> - util.log err if err - - contents[index] = fileContents - util.log "[#{index + 1}] #{file}.coffee" - process() if --remaining is 0 - - process = -> - core = contents.join("\n\n") - full = core + "\n\n" + getPlugins() - - if options.map - util.log "Source map support enabled" - coreOpts = "#{coffeeCoreOpts} -m #{targetCoffee}.coffee" - fullOpts = "#{coffeeFullOpts} -m #{targetCoffee}.full.coffee" - else - coreOpts = "#{coffeeCoreOpts} -c #{targetCoffee}.coffee" - fullOpts = "#{coffeeFullOpts} -c #{targetCoffee}.full.coffee" - - fs.writeFile "#{targetCoffee}.coffee", core, "utf8", (err) -> - util.log err if err - - exec "coffee #{coreOpts}", (err, stdout, stderr) -> - util.log err if err - util.log "Compiled #{targetCoreJS}" - - if options.map - map = JSON.parse fs.readFileSync("#{targetCoffee}.map") - map.sources = ["caman.coffee"] - fs.writeFileSync "#{targetCoffee}.map", JSON.stringify(map) - - fs.writeFile "#{targetCoffee}.full.coffee", full, "utf8", (err) -> - util.log err if err - - exec "coffee #{fullOpts}", (err, stdout, stderr) -> - util.log err if err - util.log "Compiled #{targetFullJS}" - - if options.map - map = JSON.parse fs.readFileSync("#{targetCoffee}.full.map") - map.sources = ["caman.full.coffee"] - fs.writeFileSync "#{targetCoffee}.full.map", JSON.stringify(map) - - # if not err - # fs.unlink "#{targetCoffee}.full.coffee", (err) -> util.log err if err - - finished('js') - - -task 'minify', 'Minify the CoffeeScript files', -> - util.log "Minifying #{targetCoreJS}" - fs.readFile targetCoreJS, "utf8", (err, contents) -> - fs.writeFile targetCoreMinJS, jsmin(contents), "utf8", (err) -> - util.log err if err - - if packer - util.log "Packing #{targetCoreJS}" - fs.writeFile targetCorePackJS, packer.pack(contents, true), "utf8", (err) -> - util.log err if err - - util.log "Minifying #{targetFullJS}" - fs.readFile targetFullJS, "utf8", (err, contents) -> - fs.writeFile targetFullMinJS, jsmin(contents), "utf8", (err) -> - util.log err if err - - if packer - util.log "Packing #{targetFullJS}" - fs.writeFile targetFullPackJS, packer.pack(contents, true), "utf8", (err) -> - util.log err if err diff --git a/mig/images/lib/CamanJS-4.1.1/LICENSE b/mig/images/lib/CamanJS-4.1.1/LICENSE deleted file mode 100644 index c1faabd2a..000000000 --- a/mig/images/lib/CamanJS-4.1.1/LICENSE +++ /dev/null @@ -1,10 +0,0 @@ -Copyright (c) 2010, Ryan LeFevre -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Ryan LeFevre nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/README.md b/mig/images/lib/CamanJS-4.1.1/README.md deleted file mode 100644 index efa4cc22a..000000000 --- a/mig/images/lib/CamanJS-4.1.1/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# About the Project - -[](http://travis-ci.org/meltingice/CamanJS) - -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. All features that are not a part of the core CamanJS library are in a [separate plugins repository](https://github.com/meltingice/CamanJS-Plugins). - -For more information, I highly recommend taking a look at the [official website](http://camanjs.com) where there is more comprehensive documentation and interactive demos. You can also [read the wiki](https://github.com/meltingice/CamanJS/wiki) for some basic information about the project and how to use it. - -CamanJS is written in [Coffeescript](http://coffeescript.org) as of version 3.0. **It works both in-browser and in NodeJS.** - -## Usage - -Include one of the versions in the `dist/` folder, then you can run: - -```js -Caman("#image-id", function () { - this.brightness(10); - this.contrast(20); - this.render(function () { - alert("Done!"); - }); -}); -``` - -Caman also supports modifying images via the `data-caman` HTML attribute. Simply separate each instruction with a space. Images with the `data-caman` attribute will automatically be modified at page load. - -```html -
- # @param String hex The colors hex value - # @return Array The RGB representation - #- @hexToRGB: (hex) -> - hex = hex.substr(1) if hex.charAt(0) is "#" - r = parseInt hex.substr(0, 2), 16 - g = parseInt hex.substr(2, 2), 16 - b = parseInt hex.substr(4, 2), 16 - - r: r, g: g, b: b - - # Converts an RGB color to HSL. - # Assumes r, g, and b are in the set [0, 255] and - # returns h, s, and l in the set [0, 1]. - # - #
- # @param Number r Red channel - # @param Number g Green channel - # @param Number b Blue channel - # @return The HSL representation - #- @rgbToHSL: (r, g, b) -> - if typeof r is "object" - g = r.g - b = r.b - r = r.r - - r /= 255 - g /= 255 - b /= 255 - - max = Math.max r, g, b - min = Math.min r, g, b - l = (max + min) / 2 - - if max is min - h = s = 0 - else - d = max - min - s = if l > 0.5 then d / (2 - max - min) else d / (max + min) - h = switch max - when r then (g - b) / d + (if g < b then 6 else 0) - when g then (b - r) / d + 2 - when b then (r - g) / d + 4 - - h /= 6 - - h: h, s: s, l: l - - # Converts an HSL color value to RGB. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSL_color_space. - # Assumes h, s, and l are contained in the set [0, 1] and - # returns r, g, and b in the set [0, 255]. - # - #
- # @param Number h The hue - # @param Number s The saturation - # @param Number l The lightness - # @return Array The RGB representation - #- @hslToRGB: (h, s, l) -> - if typeof h is "object" - s = h.s - l = h.l - h = h.h - - if s is 0 - r = g = b = l - else - q = if l < 0.5 then l * (1 + s) else l + s - l * s - p = 2 * l - q - - r = @hueToRGB p, q, h + 1/3 - g = @hueToRGB p, q, h - b = @hueToRGB p, q, h - 1/3 - - r: r * 255, g: g * 255, b: b * 255 - - # Converts from the hue color space back to RGB - @hueToRGB: (p, q, t) -> - if t < 0 then t += 1 - if t > 1 then t -= 1 - if t < 1/6 then return p + (q - p) * 6 * t - if t < 1/2 then return q - if t < 2/3 then return p + (q - p) * (2/3 - t) * 6 - return p - - # Converts an RGB color value to HSV. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSV_color_space. - # Assumes r, g, and b are contained in the set [0, 255] and - # returns h, s, and v in the set [0, 1]. - # - #
- # @param Number r The red color value - # @param Number g The green color value - # @param Number b The blue color value - # @return Array The HSV representation - #- @rgbToHSV: (r, g, b) -> - r /= 255 - g /= 255 - b /= 255 - - max = Math.max r, g, b - min = Math.min r, g, b - v = max - d = max - min - - s = if max is 0 then 0 else d / max - - if max is min - h = 0 - else - h = switch max - when r then (g - b) / d + (if g < b then 6 else 0) - when g then (b - r) / d + 2 - when b then (r - g) / d + 4 - - h /= 6 - - h: h, s: s, v: v - - # Converts an HSV color value to RGB. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSV_color_space. - # Assumes h, s, and v are contained in the set [0, 1] and - # returns r, g, and b in the set [0, 255]. - # - #
- # @param Number h The hue - # @param Number s The saturation - # @param Number v The value - # @return Array The RGB representation - #- @hsvToRGB: (h, s, v) -> - i = Math.floor h * 6 - f = h * 6 - i - p = v * (1 - s) - q = v * (1 - f * s) - t = v * (1 - (1 - f) * s) - - switch i % 6 - when 0 - r = v - g = t - b = p - when 1 - r = q - g = v - b = p - when 2 - r = p - g = v - b = t - when 3 - r = p - g = q - b = v - when 4 - r = t - g = p - b = v - when 5 - r = v - g = p - b = q - - r: r * 255, g: g * 255, b: b * 255 - - # Converts a RGB color value to the XYZ color space. Formulas - # are based on http://en.wikipedia.org/wiki/SRGB assuming that - # RGB values are sRGB. - # - # Assumes r, g, and b are contained in the set [0, 255] and - # returns x, y, and z. - # - #
- # @param Number r The red color value - # @param Number g The green color value - # @param Number b The blue color value - # @return Array The XYZ representation - #- @rgbToXYZ: (r, g, b) -> - r /= 255 - g /= 255 - b /= 255 - - if r > 0.04045 - r = Math.pow((r + 0.055) / 1.055, 2.4) - else - r /= 12.92 - - if g > 0.04045 - g = Math.pow((g + 0.055) / 1.055, 2.4) - else - g /= 12.92 - - if b > 0.04045 - b = Math.pow((b + 0.055) / 1.055, 2.4) - else - b /= 12.92 - - x = r * 0.4124 + g * 0.3576 + b * 0.1805; - y = r * 0.2126 + g * 0.7152 + b * 0.0722; - z = r * 0.0193 + g * 0.1192 + b * 0.9505; - - x: x * 100, y: y * 100, z: z * 100 - - # Converts a XYZ color value to the sRGB color space. Formulas - # are based on http://en.wikipedia.org/wiki/SRGB and the resulting - # RGB value will be in the sRGB color space. - # Assumes x, y and z values are whatever they are and returns - # r, g and b in the set [0, 255]. - # - #
- # @param Number x The X value - # @param Number y The Y value - # @param Number z The Z value - # @return Array The RGB representation - #- @xyzToRGB: (x, y, z) -> - x /= 100 - y /= 100 - z /= 100 - - r = (3.2406 * x) + (-1.5372 * y) + (-0.4986 * z) - g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z) - b = (0.0557 * x) + (-0.2040 * y) + (1.0570 * z) - - if r > 0.0031308 - r = (1.055 * Math.pow(r, 0.4166666667)) - 0.055 - else - r *= 12.92 - - if g > 0.0031308 - g = (1.055 * Math.pow(g, 0.4166666667)) - 0.055 - else - g *= 12.92 - - if b > 0.0031308 - b = (1.055 * Math.pow(b, 0.4166666667)) - 0.055 - else - b *= 12.92 - - r: r * 255, g: g * 255, b: b * 255 - - # Converts a XYZ color value to the CIELAB color space. Formulas - # are based on http://en.wikipedia.org/wiki/Lab_color_space - # The reference white point used in the conversion is D65. - # Assumes x, y and z values are whatever they are and returns - # L*, a* and b* values - # - #
- # @param Number x The X value - # @param Number y The Y value - # @param Number z The Z value - # @return Array The Lab representation - #- @xyzToLab: (x, y, z) -> - if typeof x is "object" - y = x.y - z = x.z - x = x.x - - whiteX = 95.047 - whiteY = 100.0 - whiteZ = 108.883 - - x /= whiteX - y /= whiteY - z /= whiteZ - - if x > 0.008856451679 - x = Math.pow(x, 0.3333333333) - else - x = (7.787037037 * x) + 0.1379310345 - - if y > 0.008856451679 - y = Math.pow(y, 0.3333333333) - else - y = (7.787037037 * y) + 0.1379310345 - - if z > 0.008856451679 - z = Math.pow(z, 0.3333333333) - else - z = (7.787037037 * z) + 0.1379310345 - - l = 116 * y - 16 - a = 500 * (x - y) - b = 200 * (y - z) - - l: l, a: a, b: b - - # Converts a L*, a*, b* color values from the CIELAB color space - # to the XYZ color space. Formulas are based on - # http://en.wikipedia.org/wiki/Lab_color_space - # - # The reference white point used in the conversion is D65. - # Assumes L*, a* and b* values are whatever they are and returns - # x, y and z values. - # - #
- # @param Number l The L* value - # @param Number a The a* value - # @param Number b The b* value - # @return Array The XYZ representation - #- @labToXYZ: (l, a, b) -> - if typeof l is "object" - a = l.a - b = l.b - l = l.l - - y = (l + 16) / 116 - x = y + (a / 500) - z = y - (b / 200) - - if x > 0.2068965517 - x = x * x * x - else - x = 0.1284185493 * (x - 0.1379310345) - - if y > 0.2068965517 - y = y * y * y - else - y = 0.1284185493 * (y - 0.1379310345) - - if z > 0.2068965517 - z = z * z * z - else - z = 0.1284185493 * (z - 0.1379310345) - - # D65 reference white point - x: x * 95.047, y: y * 100.0, z: z * 108.883 - - @rgbToLab: (r, g, b) -> - if typeof r is "object" - g = r.g - b = r.b - r = r.r - - xyz = @rgbToXYZ(r, g, b) - @xyzToLab xyz - - @labToRGB: (l, a, b) -> - - -# Event system that can be used to register callbacks that get fired -# during certain times in the render process. -class Event - @events = {} - - # All of the supported event types - @types = [ - "processStart" - "processComplete" - "renderStart" - "renderFinished" - "blockStarted" - "blockFinished" - ] - - # Trigger an event - @trigger: (target, type, data) -> - if @events[type] and @events[type].length - for event in @events[type] - if event.target is null or target.id is event.target.id - event.fn.call target, data - - # Listen for an event. Optionally bind the listen to a single CamanInstance - # or all CamanInstances. - @listen: (target, type, fn) -> - # Adjust arguments if target is omitted - if typeof target is "string" - _type = target - _fn = type - - target = null - type = _type - fn = _fn - - # Validation - return false if type not in @types - - @events[type] = [] if not @events[type] - @events[type].push target: target, fn: fn - - return true - -Caman.Event = Event - -# Responsible for storing all of the filters -Caman.Filter = class Filter - # All of the different render operatives - @Type = - Single: 1 - Kernel: 2 - LayerDequeue: 3 - LayerFinished: 4 - LoadOverlay: 5 - Plugin: 6 - - # Registers a filter function - @register: (name, filterFunc) -> Caman::[name] = filterFunc - -# Various I/O based operations -Caman.IO = class IO - @domainRegex = /(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/ - - # Is the given URL remote? - # If a cross-origin setting is set, we assume you have CORS - # properly configured. - @isRemote: (img) -> - return false unless img? - return false if @corsEnabled(img) - return @isURLRemote img.src - - @corsEnabled: (img) -> - img.crossOrigin? and img.crossOrigin.toLowerCase() in ['anonymous', 'use-credentials'] - - @isURLRemote: (url) -> - matches = url.match @domainRegex - return if matches then matches[1] isnt document.domain else false - - @remoteCheck: (src) -> - if @isURLRemote src - if not Caman.remoteProxy.length - Log.info "Attempting to load a remote image without a configured proxy. URL: #{src}" - return - else - if Caman.isURLRemote Caman.remoteProxy - Log.info "Cannot use a remote proxy for loading images." - return - - "#{Caman.remoteProxy}?camanProxyUrl=#{encodeURIComponent(src)}" - - - @proxyUrl: (src) -> - "#{Caman.remoteProxy}?#{Caman.proxyParam}=#{encodeURIComponent(src)}" - - # Shortcut for using one of the bundled proxies. - @useProxy: (lang) -> - langToExt = - ruby: 'rb' - python: 'py' - perl: 'pl' - javascript: 'js' - - lang = lang.toLowerCase() - lang = langToExt[lang] if langToExt[lang]? - "proxies/caman_proxy.#{lang}" - -# Grabs the canvas data, encodes it to Base64, then sets the browser location to -# the encoded data so that the user will be prompted to download it. -Caman::save = -> - if exports? - @nodeSave.apply @, arguments - else - @browserSave.apply @, arguments - -Caman::browserSave = (type = "png") -> - type = type.toLowerCase() - - # Force download (its a bit hackish) - image = @toBase64(type).replace "image/#{type}", "image/octet-stream" - document.location.href = image - -Caman::nodeSave = (file, overwrite = true) -> - try - stats = fs.statSync file - return false if stats.isFile() and not overwrite - catch e - Log.debug "Creating output file #{file}" - - fs.writeFile file, @canvas.toBuffer(), -> - Log.debug "Finished writing to #{file}" - -# Takes the current canvas data, converts it to Base64, then sets it as the source -# of a new Image object and returns it. -Caman::toImage = (type) -> - img = document.createElement 'img' - img.src = @toBase64 type - img.width = @dimensions.width - img.height = @dimensions.height - - if window.devicePixelRatio - img.width /= window.devicePixelRatio - img.height /= window.devicePixelRatio - - return img - -# Base64 encodes the current canvas -Caman::toBase64 = (type = "png") -> - type = type.toLowerCase() - return @canvas.toDataURL "image/#{type}" - - -# The entire layering system for Caman resides in this file. Layers get their own canvasLayer -# objectwhich is created when newLayer() is called. For extensive information regarding the -# specifics of howthe layering system works, there is an in-depth blog post on this very topic. -# Instead of copying the entirety of that post, I'll simply point you towards the -# [blog link](http://blog.meltingice.net/programming/implementing-layers-camanjs). -# -# However, the gist of the layering system is that, for each layer, it creates a new canvas -# element and then either copies the parent layer's data or applies a solid color to the new -# layer. After some (optional) effects are applied, the layer is blended back into the parent -# canvas layer using one of many different blending algorithms. -# -# You can also load an image (local or remote, with a proxy) into a canvas layer, which is useful -# if you want to add textures to an image. -class Layer - constructor: (@c) -> - # Compatibility - @filter = @c - - @options = - blendingMode: 'normal' - opacity: 1.0 - - # Each layer gets its own unique ID - @layerID = Util.uniqid.get() - - # Create the canvas for this layer - @canvas = if exports? then new Canvas() else document.createElement('canvas') - - @canvas.width = @c.dimensions.width - @canvas.height = @c.dimensions.height - - @context = @canvas.getContext('2d') - @context.createImageData @canvas.width, @canvas.height - @imageData = @context.getImageData 0, 0, @canvas.width, @canvas.height - @pixelData = @imageData.data - - # If you want to create nested layers - newLayer: (cb) -> @c.newLayer.call @c, cb - - # Sets the blending mode of this layer. The mode is the name of a blender function. - setBlendingMode: (mode) -> - @options.blendingMode = mode - return @ - - # Sets the opacity of this layer. This affects how much of this layer is applied to the parent - # layer at render time. - opacity: (opacity) -> - @options.opacity = opacity / 100 - return @ - - # Copies the contents of the parent layer to this layer - copyParent: -> - parentData = @c.pixelData - - for i in [0...@c.pixelData.length] by 4 - @pixelData[i] = parentData[i] - @pixelData[i+1] = parentData[i+1] - @pixelData[i+2] = parentData[i+2] - @pixelData[i+3] = parentData[i+3] - - return @ - - # Fills this layer with a single color - fillColor: -> @c.fillColor.apply @c, arguments - - # Loads and overlays an image onto this layer - overlayImage: (image) -> - if typeof image is "object" - image = image.src - else if typeof image is "string" and image[0] is "#" - image = $(image).src - - return @ if not image - - @c.renderer.renderQueue.push - type: Filter.Type.LoadOverlay - src: image - layer: @ - - return @ - - # Takes the contents of this layer and applies them to the parent layer at render time. This - # should never be called explicitly by the user. - applyToParent: -> - parentData = @c.pixelStack[@c.pixelStack.length - 1] - layerData = @c.pixelData - - for i in [0...layerData.length] by 4 - rgbaParent = - r: parentData[i] - g: parentData[i+1] - b: parentData[i+2] - a: parentData[i+3] - - rgbaLayer = - r: layerData[i] - g: layerData[i+1] - b: layerData[i+2] - a: layerData[i+3] - - result = Blender.execute @options.blendingMode, rgbaLayer, rgbaParent - - result.r = Util.clampRGB result.r - result.g = Util.clampRGB result.g - result.b = Util.clampRGB result.b - result.a = rgbaLayer.a if not result.a? - - parentData[i] = rgbaParent.r - ( - (rgbaParent.r - result.r) * (@options.opacity * (result.a / 255)) - ) - parentData[i+1] = rgbaParent.g - ( - (rgbaParent.g - result.g) * (@options.opacity * (result.a / 255)) - ) - parentData[i+2] = rgbaParent.b - ( - (rgbaParent.b - result.b) * (@options.opacity * (result.a / 255)) - ) - - -# Simple console logger class that can be toggled on and off based on Caman.DEBUG -class Logger - constructor: -> - for name in ['log', 'info', 'warn', 'error'] - @[name] = do (name) -> - (args...) -> - return if not Caman.DEBUG - try - console[name].apply console, args - catch e - # We're probably using IE9 or earlier - console[name] args - - @debug = @log - -Log = new Logger() - -# This object is available inside of the process() loop, and it lets filter developers have simple -# access to any arbitrary pixel in the image, as well as information about the current pixel in -# the loop. -class PixelInfo - @coordinatesToLocation: (x, y, width) -> - (y * width + x) * 4 - - @locationToCoordinates: (loc, width) -> - y = Math.floor(loc / (width * 4)) - x = (loc % (width * 4)) / 4 - - return x: x, y: y - - constructor: (@c) -> @loc = 0 - - # Retrieves the X, Y location of the current pixel. The origin is at the bottom left corner of - # the image, like a normal coordinate system. - locationXY: -> - y = @c.dimensions.height - Math.floor(@loc / (@c.dimensions.width * 4)) - x = (@loc % (@c.dimensions.width * 4)) / 4 - - return x: x, y: y - - # Returns an RGBA object for a pixel whose location is specified in relation to the current - # pixel. - getPixelRelative: (horiz, vert) -> - # We invert the vert_offset in order to make the coordinate system non-inverted. In laymans - # terms: -1 means down and +1 means up. - newLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz) - - if newLoc > @c.pixelData.length or newLoc < 0 - return r: 0, g: 0, b: 0, a: 0 - - return { - r: @c.pixelData[newLoc] - g: @c.pixelData[newLoc + 1] - b: @c.pixelData[newLoc + 2] - a: @c.pixelData[newLoc + 3] - } - - # The counterpart to getPixelRelative, this updates the value of a pixel whose location is - # specified in relation to the current pixel. - putPixelRelative: (horiz, vert, rgba) -> - nowLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz) - - return if newLoc > @c.pixelData.length or newLoc < 0 - - @c.pixelData[newLoc] = rgba.r - @c.pixelData[newLoc + 1] = rgba.g - @c.pixelData[newLoc + 2] = rgba.b - @c.pixelData[newLoc + 3] = rgba.a - - return true - - # Gets an RGBA object for an arbitrary pixel in the canvas specified by absolute X, Y coordinates - getPixel: (x, y) -> - loc = @coordinatesToLocation(x, y, @width) - - return { - r: @c.pixelData[loc] - g: @c.pixelData[loc + 1] - b: @c.pixelData[loc + 2] - a: @c.pixelData[loc + 3] - } - - # Updates the pixel at the given X, Y coordinate - putPixel: (x, y, rgba) -> - loc = @coordinatesToLocation(x, y, @width) - - @c.pixelData[loc] = rgba.r - @c.pixelData[loc + 1] = rgba.g - @c.pixelData[loc + 2] = rgba.b - @c.pixelData[loc + 3] = rgba.a - - -# Stores and registers standalone plugins -class Plugin - @plugins = {} - - @register: (name, plugin) -> @plugins[name] = plugin - @execute: (context, name, args) -> @plugins[name].apply context, args - -Caman.Plugin = Plugin - -# Handles all of the various rendering methods in Caman. Most of the image modification happens -# here. A new Renderer object is created for every render operation. -Caman.Renderer = class Renderer - # The number of blocks to split the image into during the render process to simulate - # concurrency. This also helps the browser manage the (possibly) long running render jobs. - @Blocks = if Caman.NodeJS then require('os').cpus().length else 4 - - constructor: (@c) -> - @renderQueue = [] - @modPixelData = null - - add: (job) -> - return unless job? - @renderQueue.push job - - # Grabs the next operation from the render queue and passes it to Renderer - # for execution - processNext: => - # If the queue is empty, fire the finished callback - if @renderQueue.length is 0 - Event.trigger @, "renderFinished" - @finishedFn.call(@c) if @finishedFn? - - return @ - - @currentJob = @renderQueue.shift() - - switch @currentJob.type - when Filter.Type.LayerDequeue - layer = @c.canvasQueue.shift() - @c.executeLayer layer - @processNext() - when Filter.Type.LayerFinished - @c.applyCurrentLayer() - @c.popContext() - @processNext() - when Filter.Type.LoadOverlay - @loadOverlay @currentJob.layer, @currentJob.src - when Filter.Type.Plugin - @executePlugin() - else - @executeFilter() - - execute: (callback) -> - @finishedFn = callback - @modPixelData = Util.dataArray(@c.pixelData.length) - - @processNext() - - eachBlock: (fn) -> - # Prepare all the required render data - @blocksDone = 0 - - n = @c.pixelData.length - blockPixelLength = Math.floor (n / 4) / Renderer.Blocks - blockN = blockPixelLength * 4 - lastBlockN = blockN + ((n / 4) % Renderer.Blocks) * 4 - - for i in [0...Renderer.Blocks] - start = i * blockN - end = start + (if i is Renderer.Blocks - 1 then lastBlockN else blockN) - - if Caman.NodeJS - f = Fiber => fn.call(@, i, start, end) - bnum = f.run() - @blockFinished(bnum) - else - setTimeout do (i, start, end) => - => fn.call(@, i, start, end) - , 0 - - # The core of the image rendering, this function executes the provided filter. - # - # NOTE: this does not write the updated pixel data to the canvas. That happens when all filters - # are finished rendering in order to be as fast as possible. - executeFilter: -> - Event.trigger @c, "processStart", @currentJob - - if @currentJob.type is Filter.Type.Single - @eachBlock @renderBlock - else - @eachBlock @renderKernel - - # Executes a standalone plugin - executePlugin: -> - Log.debug "Executing plugin #{@currentJob.plugin}" - Plugin.execute @c, @currentJob.plugin, @currentJob.args - Log.debug "Plugin #{@currentJob.plugin} finished!" - - @processNext() - - # Renders a single block of the canvas with the current filter function - renderBlock: (bnum, start, end) -> - Log.debug "Block ##{bnum} - Filter: #{@currentJob.name}, Start: #{start}, End: #{end}" - Event.trigger @c, "blockStarted", - blockNum: bnum - totalBlocks: Renderer.Blocks - startPixel: start - endPixel: end - - data = r: 0, g: 0, b: 0, a: 0 - pixelInfo = new PixelInfo @c - - for i in [start...end] by 4 - pixelInfo.loc = i - - data.r = @c.pixelData[i] - data.g = @c.pixelData[i+1] - data.b = @c.pixelData[i+2] - data.a = @c.pixelData[i+3] - - res = @currentJob.processFn.call pixelInfo, data - res.a = data.a if not res.a? - - @c.pixelData[i] = Util.clampRGB res.r - @c.pixelData[i+1] = Util.clampRGB res.g - @c.pixelData[i+2] = Util.clampRGB res.b - @c.pixelData[i+3] = Util.clampRGB res.a - - if Caman.NodeJS - Fiber.yield(bnum) - else - @blockFinished bnum - - # Applies an image kernel to the canvas - renderKernel: (bnum, start, end) -> - name = @currentJob.name - bias = @currentJob.bias - divisor = @currentJob.divisor - n = @c.pixelData.length - - adjust = @currentJob.adjust - adjustSize = Math.sqrt adjust.length - - kernel = [] - - Log.debug "Rendering kernel - Filter: #{@currentJob.name}" - - start = Math.max start, @c.dimensions.width * 4 * ((adjustSize - 1) / 2) - end = Math.min end, n - (@c.dimensions.width * 4 * ((adjustSize - 1) / 2)) - - builder = (adjustSize - 1) / 2 - - pixelInfo = new PixelInfo @c - - for i in [start...end] by 4 - pixelInfo.loc = i - builderIndex = 0 - - for j in [-builder..builder] - for k in [builder..-builder] - pixel = pixelInfo.getPixelRelative j, k - kernel[builderIndex * 3] = pixel.r - kernel[builderIndex * 3 + 1] = pixel.g - kernel[builderIndex * 3 + 2] = pixel.b - - builderIndex++ - - res = @processKernel adjust, kernel, divisor, bias - - @modPixelData[i] = Util.clampRGB(res.r) - @modPixelData[i+1] = Util.clampRGB(res.g) - @modPixelData[i+2] = Util.clampRGB(res.b) - @modPixelData[i+3] = @c.pixelData[i+3] - - if Caman.NodeJS - Fiber.yield(bnum) - else - @blockFinished bnum - - # Called when a single block is finished rendering. Once all blocks are done, we signal that this - # filter is finished rendering and continue to the next step. - blockFinished: (bnum) -> - Log.debug "Block ##{bnum} finished! Filter: #{@currentJob.name}" if bnum >= 0 - @blocksDone++ - - Event.trigger @c, "blockFinished", - blockNum: bnum - blocksFinished: @blocksDone - totalBlocks: Renderer.Blocks - - if @blocksDone is Renderer.Blocks - if @currentJob.type is Filter.Type.Kernel - for i in [0...@c.pixelData.length] - @c.pixelData[i] = @modPixelData[i] - - Log.debug "Filter #{@currentJob.name} finished!" if bnum >=0 - Event.trigger @c, "processComplete", @currentJob - - @processNext() - - # The "filter function" for kernel adjustments. - processKernel: (adjust, kernel, divisor, bias) -> - val = r: 0, g: 0, b: 0 - - for i in [0...adjust.length] - val.r += adjust[i] * kernel[i * 3] - val.g += adjust[i] * kernel[i * 3 + 1] - val.b += adjust[i] * kernel[i * 3 + 2] - - val.r = (val.r / divisor) + bias - val.g = (val.g / divisor) + bias - val.b = (val.b / divisor) + bias - val - - # Loads an image onto the current canvas - loadOverlay: (layer, src) -> - img = document.createElement 'img' - img.onload = => - layer.context.drawImage img, 0, 0, @c.dimensions.width, @c.dimensions.height - layer.imageData = layer.context.getImageData 0, 0, @c.dimensions.width, @c.dimensions.height - layer.pixelData = layer.imageData.data - - @c.pixelData = layer.pixelData - - @processNext() - - proxyUrl = IO.remoteCheck src - img.src = if proxyUrl? then proxyUrl else src - -# Used for storing instances of CamanInstance objects such that, when Caman() is called on an -# already initialized element, it returns that object instead of re-initializing. -Caman.Store = class Store - @items = {} - - @has: (search) -> @items[search]? - @get: (search) -> @items[search] - @put: (name, obj) -> @items[name] = obj - @execute: (search, callback) -> - setTimeout => - callback.call @get(search), @get(search) - , 0 - - return @get(search) - - @flush: (name = false) -> - if name then delete @items[name] else @items = {} - -# Directly apply the child layer's pixels to the parent layer with no special changes -Blender.register "normal", (rgbaLayer, rgbaParent) -> - r: rgbaLayer.r - g: rgbaLayer.g - b: rgbaLayer.b - -# Apply the child to the parent by multiplying the color values. This generally creates contrast. -Blender.register "multiply", (rgbaLayer, rgbaParent) -> - r: (rgbaLayer.r * rgbaParent.r) / 255 - g: (rgbaLayer.g * rgbaParent.g) / 255 - b: (rgbaLayer.b * rgbaParent.b) / 255 - -Blender.register "screen", (rgbaLayer, rgbaParent) -> - r: 255 - (((255 - rgbaLayer.r) * (255 - rgbaParent.r)) / 255) - g: 255 - (((255 - rgbaLayer.g) * (255 - rgbaParent.g)) / 255) - b: 255 - (((255 - rgbaLayer.b) * (255 - rgbaParent.b)) / 255) - - -Blender.register "overlay", (rgbaLayer, rgbaParent) -> - result = {} - result.r = - if rgbaParent.r > 128 - 255 - 2 * (255 - rgbaLayer.r) * (255 - rgbaParent.r) / 255 - else (rgbaParent.r * rgbaLayer.r * 2) / 255 - - result.g = - if rgbaParent.g > 128 - 255 - 2 * (255 - rgbaLayer.g) * (255 - rgbaParent.g) / 255 - else (rgbaParent.g * rgbaLayer.g * 2) / 255 - - result.b = - if rgbaParent.b > 128 - 255 - 2 * (255 - rgbaLayer.b) * (255 - rgbaParent.b) / 255 - else (rgbaParent.b * rgbaLayer.b * 2) / 255 - - result - -Blender.register "difference", (rgbaLayer, rgbaParent) -> - r: rgbaLayer.r - rgbaParent.r - g: rgbaLayer.g - rgbaParent.g - b: rgbaLayer.b - rgbaParent.b - -Blender.register "addition", (rgbaLayer, rgbaParent) -> - r: rgbaParent.r + rgbaLayer.r - g: rgbaParent.g + rgbaLayer.g - b: rgbaParent.b + rgbaLayer.b - -Blender.register "exclusion", (rgbaLayer, rgbaParent) -> - r: 128 - 2 * (rgbaParent.r - 128) * (rgbaLayer.r - 128) / 255 - g: 128 - 2 * (rgbaParent.g - 128) * (rgbaLayer.g - 128) / 255 - b: 128 - 2 * (rgbaParent.b - 128) * (rgbaLayer.b - 128) / 255 - -Blender.register "softLight", (rgbaLayer, rgbaParent) -> - result = {} - - result.r = - if rgbaParent.r > 128 - 255 - ((255 - rgbaParent.r) * (255 - (rgbaLayer.r - 128))) / 255 - else (rgbaParent.r * (rgbaLayer.r + 128)) / 255 - - result.g = - if rgbaParent.g > 128 - 255 - ((255 - rgbaParent.g) * (255 - (rgbaLayer.g - 128))) / 255 - else (rgbaParent.g * (rgbaLayer.g + 128)) / 255 - - result.b = - if rgbaParent.b > 128 - 255 - ((255 - rgbaParent.b) * (255 - (rgbaLayer.b - 128))) / 255 - else (rgbaParent.b * (rgbaLayer.b + 128)) / 255 - - result - -Blender.register "lighten", (rgbaLayer, rgbaParent) -> - r: if rgbaParent.r > rgbaLayer.r then rgbaParent.r else rgbaLayer.r - g: if rgbaParent.g > rgbaLayer.g then rgbaParent.g else rgbaLayer.g - b: if rgbaParent.b > rgbaLayer.b then rgbaParent.b else rgbaLayer.b - -Blender.register "darken", (rgbaLayer, rgbaParent) -> - r: if rgbaParent.r > rgbaLayer.r then rgbaLayer.r else rgbaParent.r - g: if rgbaParent.g > rgbaLayer.g then rgbaLayer.g else rgbaParent.g - b: if rgbaParent.b > rgbaLayer.b then rgbaLayer.b else rgbaParent.b - -# 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](http://camanjs.com/docs/plugin-creation) page, and for information on using -# the plugins, check out the [Built-In Functionality](http://camanjs.com/docs/built-in) 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](http://en.wikipedia.org/wiki/Hue). -# -# ### 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 - - rgb = Convert.hsvToRGB hsv.h, hsv.s, hsv.v - rgb.a = rgba.a - rgb - -# ## 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] - - - -# Allows us to crop the canvas and produce a new smaller -# canvas. -Caman.Plugin.register "crop", (width, height, x = 0, y = 0) -> - # Create our new canvas element - if exports? - canvas = new Canvas width, height - else - canvas = document.createElement 'canvas' - Util.copyAttributes @canvas, canvas - - canvas.width = width - canvas.height = height - - ctx = canvas.getContext '2d' - - # Perform the cropping by drawing to the new canvas - ctx.drawImage @canvas, x, y, width, height, 0, 0, width, height - - @cropCoordinates = x: x, y: y - - # Update all of the references - @cropped = true - @replaceCanvas canvas - -# Resize the canvas and the image to a new size -Caman.Plugin.register "resize", (newDims = null) -> - # Calculate new size - if newDims is null or (!newDims.width? and !newDims.height?) - Log.error "Invalid or missing dimensions given for resize" - return - - if not newDims.width? - # Calculate width - newDims.width = @canvas.width * newDims.height / @canvas.height - else if not newDims.height? - # Calculate height - newDims.height = @canvas.height * newDims.width / @canvas.width - - if exports? - canvas = new Canvas newDims.width, newDims.height - else - canvas = document.createElement 'canvas' - Util.copyAttributes @canvas, canvas - - canvas.width = newDims.width - canvas.height = newDims.height - - ctx = canvas.getContext '2d' - - ctx.drawImage @canvas, - 0, 0, - @canvas.width, @canvas.height, - 0, 0, - newDims.width, newDims.height - - @resized = true - @replaceCanvas canvas - -Caman.Filter.register "crop", -> - @processPlugin "crop", Array.prototype.slice.call(arguments, 0) - -Caman.Filter.register "resize", -> - @processPlugin "resize", Array.prototype.slice.call(arguments, 0) \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/dist/caman.full.coffee b/mig/images/lib/CamanJS-4.1.1/dist/caman.full.coffee deleted file mode 100644 index 05951d081..000000000 --- a/mig/images/lib/CamanJS-4.1.1/dist/caman.full.coffee +++ /dev/null @@ -1,2369 +0,0 @@ -# Look what you make me do Javascript -slice = Array::slice - -# DOM simplifier (no jQuery dependency) -# NodeJS compatible -$ = (sel, root = document) -> - return sel if typeof sel is "object" or exports? - root.querySelector sel - -class Util - # Unique value utility - @uniqid = do -> - id = 0 - get: -> id++ - - # Helper function that extends one object with all the properies of other objects - @extend = (obj) -> - dest = obj - src = slice.call arguments, 1 - - for copy in src - for own prop of copy - dest[prop] = copy[prop] - - return dest - - # In order to stay true to the latest spec, RGB values must be clamped between - # 0 and 255. If we don't do this, weird things happen. - @clampRGB = (val) -> - return 0 if val < 0 - return 255 if val > 255 - val - - @copyAttributes: (from, to, opts={}) -> - for attr in from.attributes - continue if opts.except? and attr.nodeName in opts.except - to.setAttribute(attr.nodeName, attr.nodeValue) - - # Support for browsers that don't know Uint8Array (such as IE9) - @dataArray: (length = 0) -> - return new Uint8Array(length) if Caman.NodeJS or window.Uint8Array? - return new Array(length) - -# NodeJS compatibility -if exports? - Root = exports - Canvas = require 'canvas' - Image = Canvas.Image - - Fiber = require 'fibers' - - fs = require 'fs' -else - Root = window - -# Here it begins. Caman is defined. -# There are many different initialization for Caman, which are described on the -# [Basic Usage](http://camanjs.com/docs) page. -# -# Initialization is tricky because we need to make sure everything we need is actually fully -# loaded in the DOM before proceeding. When initialized on an image, we need to make sure that the -# image is done loading before converting it to a canvas element and writing the pixel data. If we -# do this prematurely, the browser will throw a DOM Error, and chaos will ensue. In the event that -# we initialize Caman on a canvas element while specifying an image URL, we need to create a new -# image element, load the image, then continue with initialization. -# -# The main goal for Caman was simplicity, so all of this is handled transparently to the end-user. -Root.Caman = class Caman - @version: - release: "4.1.1" - date: "4/8/2013" - - # Debug mode enables console logging - @DEBUG: false - - # Are we in a NodeJS environment? - @NodeJS: exports? - - # Should we check the DOM for images with Caman instructions? - @autoload: not Caman.NodeJS - - # Allow reverting the canvas? - # If your JS process is running out of memory, disabling - # this could help drastically. - @allowRevert: true - - # Default cross-origin policy - @crossOrigin: "anonymous" - - @toString: -> - "Version " + Caman.version.release + ", Released " + Caman.version.date; - - # Set the URL of the image proxy script - @remoteProxy: "" - - # Change the GET param used with the proxy script if needed - @proxyParam: "camanProxyUrl" - - @getAttrId: (canvas) -> - return true if Caman.NodeJS - - if typeof canvas is "string" - canvas = $(canvas) - - return null unless canvas? and canvas.getAttribute? - canvas.getAttribute 'data-caman-id' - - constructor: -> - throw "Invalid arguments" if arguments.length is 0 - - if @ instanceof Caman - # We have to do this to avoid polluting the global scope - # because of how Coffeescript binds functions specified - # with => and the fact that Caman can be invoked as both - # a function and as a 'new' object. - @finishInit = @finishInit.bind(@) - @imageLoaded = @imageLoaded.bind(@) - - args = arguments[0] - - unless Caman.NodeJS - id = parseInt Caman.getAttrId(args[0]), 10 - callback = if typeof args[1] is "function" - args[1] - else if typeof args[2] is "function" - args[2] - else - -> - - if !isNaN(id) and Store.has(id) - return Store.execute(id, callback) - - # Every instance gets a unique ID. Makes it much simpler to check if two variables are the - # same instance. - @id = Util.uniqid.get() - - @initializedPixelData = @originalPixelData = null - @cropCoordinates = x: 0, y: 0 - @cropped = false - @resized = false - - @pixelStack = [] # Stores the pixel layers - @layerStack = [] # Stores all of the layers waiting to be rendered - @canvasQueue = [] # Stores all of the canvases to be processed - @currentLayer = null - @scaled = false - - @analyze = new Analyze @ - @renderer = new Renderer @ - - @domIsLoaded => - @parseArguments(args) - @setup() - - return @ - else - return new Caman(arguments) - - domIsLoaded: (cb) -> - if Caman.NodeJS - setTimeout => - cb.call(@) - , 0 - else - if document.readyState is "complete" - Log.debug "DOM initialized" - setTimeout => - cb.call(@) - , 0 - else - listener = => - if document.readyState is "complete" - Log.debug "DOM initialized" - cb.call(@) - - document.addEventListener "readystatechange", listener, false - - # All possible combinations: - # - # **1 argument** - # - Image selector - # - Image object - # - Canvas selector - # - Canvas object - # - # **2 arguments** - # - Image selector + callback - # - Image object + callback - # - Canvas selector + URL - # - Canvas object + URL - # - # **3 arguments** - # - Canvas selector + URL + callback - # - Canvas object + URL + callback - # - # **NodeJS** - # - file path - # - file object - # - file path + callback - # - file object + callback - parseArguments: (args) -> - throw "Invalid arguments given" if args.length is 0 - - # Defaults - @initObj = null - @initType = null - @imageUrl = null - @callback = -> - - # First argument is always our canvas/image - @setInitObject args[0] - return if args.length is 1 - - switch typeof args[1] - when "string" then @imageUrl = args[1] - when "function" then @callback = args[1] - - return if args.length is 2 - - @callback = args[2] - - if args.length is 4 - @options[key] = val for own key, val of args[4] - - setInitObject: (obj) -> - if Caman.NodeJS - @initObj = obj - @initType = 'node' - return - - if typeof obj is "object" - @initObj = obj - else - @initObj = $(obj) - - throw "Could not find image or canvas for initialization." unless @initObj? - - @initType = @initObj.nodeName.toLowerCase() - - setup: -> - switch @initType - when "node" then @initNode() - when "img" then @initImage() - when "canvas" then @initCanvas() - - initNode: -> - Log.debug "Initializing for NodeJS" - - @image = new Image() - @image.onload = => - Log.debug "Image loaded. Width = #{@imageWidth()}, Height = #{@imageHeight()}" - @canvas = new Canvas @imageWidth(), @imageHeight() - @finishInit() - - @image.onerror = (err) -> throw err - @image.src = @initObj - - initImage: -> - @image = @initObj - @canvas = document.createElement 'canvas' - @context = @canvas.getContext '2d' - Util.copyAttributes @image, @canvas, except: ['src'] - - @image.parentNode.replaceChild @canvas, @image - - @imageAdjustments() - @waitForImageLoaded() - - initCanvas: -> - @canvas = @initObj - @context = @canvas.getContext '2d' - - if @imageUrl? - @image = document.createElement 'img' - @image.src = @imageUrl - - @imageAdjustments() - @waitForImageLoaded() - else - @finishInit() - - imageAdjustments: -> - if @needsHiDPISwap() - Log.debug @image.src, "->", @hiDPIReplacement() - - @swapped = true - @image.src = @hiDPIReplacement() - - if IO.isRemote(@image) - @image.src = IO.proxyUrl(@image.src) - Log.debug "Remote image detected, using URL = #{@image.src}" - - waitForImageLoaded: -> - if @isImageLoaded() - @imageLoaded() - else - @image.onload = @imageLoaded - - isImageLoaded: -> - return false unless @image.complete - return false if @image.naturalWidth? and @image.naturalWidth is 0 - return true - - # Internet Explorer has issues figuring out image dimensions when they aren't - # explicitly defined apparently. We check the normal width/height properties first, - # but fall back to natural sizes if they are 0. - imageWidth: -> @image.width or @image.naturalWidth - imageHeight: -> @image.height or @image.naturalHeight - - imageLoaded: -> - Log.debug "Image loaded. Width = #{@imageWidth()}, Height = #{@imageHeight()}" - - if @swapped - @canvas.width = @imageWidth() / @hiDPIRatio() - @canvas.height = @imageHeight() / @hiDPIRatio() - else - @canvas.width = @imageWidth() - @canvas.height = @imageHeight() - - @finishInit() - - finishInit: -> - @context = @canvas.getContext '2d' unless @context? - - @originalWidth = @preScaledWidth = @width = @canvas.width - @originalHeight = @preScaledHeight = @height = @canvas.height - - @hiDPIAdjustments() - @assignId() unless @hasId() - - if @image? - @context.drawImage @image, - 0, 0, - @imageWidth(), @imageHeight(), - 0, 0, - @preScaledWidth, @preScaledHeight - - @reloadCanvasData() - - if Caman.allowRevert - @initializedPixelData = Util.dataArray(@pixelData.length) - @originalPixelData = Util.dataArray(@pixelData.length) - - for pixel, i in @pixelData - @initializedPixelData[i] = pixel - @originalPixelData[i] = pixel - - @dimensions = - width: @canvas.width - height: @canvas.height - - Store.put @id, @ - - @callback.call @,@ - - # Reset the callback so re-initialization doesn't - # trigger it again. - @callback = -> - - # If you have a separate context reference to this canvas outside of CamanJS - # and you make a change to the canvas outside of CamanJS, you will have to call - # this function to update our context reference to include those changes. - reloadCanvasData: -> - @imageData = @context.getImageData 0, 0, @canvas.width, @canvas.height - @pixelData = @imageData.data - - resetOriginalPixelData: -> - throw "Revert disabled" unless Caman.allowRevert - - @originalPixelData = Util.dataArray(@pixelData.length) - @originalPixelData.push pixel for pixel in @pixelData - - hasId: -> Caman.getAttrId(@canvas)? - - assignId: -> - return if Caman.NodeJS or @canvas.getAttribute 'data-caman-id' - @canvas.setAttribute 'data-caman-id', @id - - hiDPIDisabled: -> - @canvas.getAttribute('data-caman-hidpi-disabled') isnt null - - hiDPIAdjustments: -> - return if Caman.NodeJS or @hiDPIDisabled() - - ratio = @hiDPIRatio() - - if ratio isnt 1 - Log.debug "HiDPI ratio = #{ratio}" - @scaled = true - - @preScaledWidth = @canvas.width - @preScaledHeight = @canvas.height - - @canvas.width = @preScaledWidth * ratio - @canvas.height = @preScaledHeight * ratio - @canvas.style.width = "#{@preScaledWidth}px" - @canvas.style.height = "#{@preScaledHeight}px" - - @context.scale ratio, ratio - - @width = @originalWidth = @canvas.width - @height = @originalHeight = @canvas.height - - hiDPIRatio: -> - devicePixelRatio = window.devicePixelRatio or 1 - backingStoreRatio = @context.webkitBackingStorePixelRatio or - @context.mozBackingStorePixelRatio or - @context.msBackingStorePixelRatio or - @context.oBackingStorePixelRatio or - @context.backingStorePixelRatio or 1 - - devicePixelRatio / backingStoreRatio - - hiDPICapable: -> window.devicePixelRatio? and window.devicePixelRatio isnt 1 - - needsHiDPISwap: -> - return false if @hiDPIDisabled() or !@hiDPICapable() - @hiDPIReplacement() isnt null - - hiDPIReplacement: -> - return null unless @image? - @image.getAttribute 'data-caman-hidpi' - - replaceCanvas: (newCanvas) -> - oldCanvas = @canvas - @canvas = newCanvas - @context = @canvas.getContext '2d' - - oldCanvas.parentNode.replaceChild @canvas, oldCanvas - - @width = @canvas.width - @height = @canvas.height - - @reloadCanvasData() - - @dimensions = - width: @canvas.width - height: @canvas.height - - # Begins the rendering process - render: (callback = ->) -> - Event.trigger @, "renderStart" - - @renderer.execute => - @context.putImageData @imageData, 0, 0 - callback.call @ - - # Reverts the canvas back to it's original state while - # maintaining any cropped or resized dimensions. - revert: -> - throw "Revert disabled" unless Caman.allowRevert - - @pixelData[i] = pixel for pixel, i in @originalVisiblePixels() - @context.putImageData @imageData, 0, 0 - - # Completely resets the canvas back to it's original state. - # Any size adjustments will also be reset. - reset: -> - canvas = document.createElement('canvas') - Util.copyAttributes(@canvas, canvas) - - canvas.width = @originalWidth - canvas.height = @originalHeight - - ctx = canvas.getContext('2d') - imageData = ctx.getImageData 0, 0, canvas.width, canvas.height - pixelData = imageData.data - - pixelData[i] = pixel for pixel, i in @initializedPixelData - - ctx.putImageData imageData, 0, 0 - - @cropCoordinates = x: 0, y: 0 - @resized = false - - @replaceCanvas(canvas) - - # Returns the original pixel data while maintaining any - # cropping or resizing that may have occured. - originalVisiblePixels: -> - throw "Revert disabled" unless Caman.allowRevert - - pixels = [] - - startX = @cropCoordinates.x - endX = startX + @width - startY = @cropCoordinates.y - endY = startY + @height - - if @resized - canvas = document.createElement('canvas') - canvas.width = @originalWidth - canvas.height = @originalHeight - - ctx = canvas.getContext('2d') - imageData = ctx.getImageData 0, 0, canvas.width, canvas.height - pixelData = imageData.data - - pixelData[i] = pixel for pixel, i in @originalPixelData - - ctx.putImageData imageData, 0, 0 - - scaledCanvas = document.createElement('canvas') - scaledCanvas.width = @width - scaledCanvas.height = @height - - ctx = scaledCanvas.getContext('2d') - ctx.drawImage canvas, 0, 0, @originalWidth, @originalHeight, 0, 0, @width, @height - - pixelData = ctx.getImageData(0, 0, @width, @height).data - width = @width - else - pixelData = @originalPixelData - width = @originalWidth - - for i in [0...pixelData.length] by 4 - coord = PixelInfo.locationToCoordinates(i, width) - if (startX <= coord.x < endX) and (startY <= coord.y < endY) - pixels.push pixelData[i], - pixelData[i+1], - pixelData[i+2], - pixelData[i+3] - - pixels - - # Pushes the filter callback that modifies the RGBA object into the - # render queue - process: (name, processFn) -> - @renderer.add - type: Filter.Type.Single - name: name - processFn: processFn - - return @ - - # Pushes the kernel into the render queue - processKernel: (name, adjust, divisor, bias) -> - if not divisor - divisor = 0 - divisor += adjust[i] for i in [0...adjust.length] - - @renderer.add - type: Filter.Type.Kernel - name: name - adjust: adjust - divisor: divisor - bias: bias or 0 - - return @ - - # Adds a standalone plugin into the render queue - processPlugin: (plugin, args) -> - @renderer.add - type: Filter.Type.Plugin - plugin: plugin - args: args - - return @ - - # Pushes a new layer operation into the render queue and calls the layer - # callback - newLayer: (callback) -> - layer = new Layer @ - @canvasQueue.push layer - @renderer.add type: Filter.Type.LayerDequeue - - callback.call layer - - @renderer.add type: Filter.Type.LayerFinished - return @ - - # Pushes the layer context and moves to the next operation - executeLayer: (layer) -> @pushContext layer - - # Set all of the relevant data to the new layer - pushContext: (layer) -> - @layerStack.push @currentLayer - @pixelStack.push @pixelData - @currentLayer = layer - @pixelData = layer.pixelData - - # Restore the previous layer context - popContext: -> - @pixelData = @pixelStack.pop() - @currentLayer = @layerStack.pop() - - # Applies the current layer to its parent layer - applyCurrentLayer: -> @currentLayer.applyToParent() - - -class Analyze - constructor: (@c) -> - - calculateLevels: -> - levels = - r: {} - g: {} - b: {} - - # Initialize all values to 0 first so there are no data gaps - for i in [0..255] - levels.r[i] = 0 - levels.g[i] = 0 - levels.b[i] = 0 - - # Iterate through each pixel block and increment the level counters - for i in [0...@c.pixelData.length] by 4 - levels.r[@c.pixelData[i]]++ - levels.g[@c.pixelData[i+1]]++ - levels.b[@c.pixelData[i+2]]++ - - # Normalize all of the numbers by converting them to percentages between - # 0 and 1.0 - numPixels = @c.pixelData.length / 4 - - for i in [0..255] - levels.r[i] /= numPixels - levels.g[i] /= numPixels - levels.b[i] /= numPixels - - levels - -Caman.DOMUpdated = -> - imgs = document.querySelectorAll("img[data-caman]") - return unless imgs.length > 0 - - for img in imgs - parser = new CamanParser img, -> - @parse() - @execute() - -# If enabled, we check the page to see if there are any -# images with Caman instructions provided using HTML5 -# data attributes. -if Caman.autoload then do -> - if document.readyState is "complete" - Caman.DOMUpdated() - else - document.addEventListener "DOMContentLoaded", Caman.DOMUpdated, false - -# Parses Caman instructions embedded in the HTML data-caman attribute -class CamanParser - INST_REGEX = "(\\w+)\\((.*?)\\)" - - constructor: (ele, ready) -> - @dataStr = ele.getAttribute('data-caman') - @caman = Caman ele, ready.bind(@) - - parse: -> - @ele = @caman.canvas - - # First we find each instruction as a whole using a global - # regex search. - r = new RegExp(INST_REGEX, 'g') - unparsedInstructions = @dataStr.match r - return unless unparsedInstructions.length > 0 - - # Once we gather all the instructions, we go through each one - # and parse out the filter name + it's parameters. - r = new RegExp(INST_REGEX) - for inst in unparsedInstructions - [m, filter, args] = inst.match(r) - - # Create a factory function so we can catch any errors that - # are produced when running the filters. This also makes it very - # simple to support multiple/complex filter arguments. - instFunc = new Function("return function() { - this.#{filter}(#{args}); - };") - - try - func = instFunc() - func.call @caman - catch e - Log.debug e - - execute: -> - ele = @ele - @caman.render -> - ele.parentNode.replaceChild @toImage(), ele - -# Built-in layer blenders. Many of these mimic Photoshop blend modes. -Caman.Blender = class Blender - @blenders = {} - - # Registers a blender. Can be used to add your own blenders outside of - # the core library, if needed. - @register: (name, func) -> @blenders[name] = func - - # Executes a blender to combine a layer with its parent. - @execute: (name, rgbaLayer, rgbaParent) -> - @blenders[name](rgbaLayer, rgbaParent) - -# Various math-heavy helpers -Caman.Calculate = class Calculate - # Calculates the distance between two points - @distance: (x1, y1, x2, y2) -> - Math.sqrt Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) - - # Generates a pseudorandom number that lies within the max - mix range. The number can be either - # an integer or a float depending on what the user specifies. - @randomRange: (min, max, getFloat = false) -> - rand = min + (Math.random() * (max - min)) - return if getFloat then rand.toFixed(getFloat) else Math.round(rand) - - # Calculates the luminance of a single pixel using a special weighted sum - @luminance: (rgba) -> (0.299 * rgba.r) + (0.587 * rgba.g) + (0.114 * rgba.b) - - # Generates a bezier curve given a start and end point, with two control points in between. - # Can also optionally bound the y values between a low and high bound. - # - # This is different than most bezier curve functions because it attempts to construct it in such - # a way that we can use it more like a simple input -> output system, or a one-to-one function. - # In other words we can provide an input color value, and immediately receive an output modified - # color value. - # - # Note that, by design, this does not force X values to be in the range [0..255]. This is to - # generalize the function a bit more. If you give it a starting X value that isn't 0, and/or a - # ending X value that isn't 255, you may run into problems with your filter! - @bezier: (start, ctrl1, ctrl2, end, lowBound, highBound) -> - x0 = start[0] - y0 = start[1] - x1 = ctrl1[0] - y1 = ctrl1[1] - x2 = ctrl2[0] - y2 = ctrl2[1] - x3 = end[0] - y3 = end[1] - bezier = {} - - # Calculate our X/Y coefficients - Cx = parseInt(3 * (x1 - x0), 10) - Bx = 3 * (x2 - x1) - Cx - Ax = x3 - x0 - Cx - Bx - - Cy = 3 * (y1 - y0) - By = 3 * (y2 - y1) - Cy - Ay = y3 - y0 - Cy - By - - # 1000 is actually arbitrary. We need to make sure we do enough - # calculations between 0 and 255 that, in even the more extreme - # circumstances, we calculate as many values as possible. In the event - # that an X value is skipped, it will be found later on using linear - # interpolation. - for i in [0...1000] - t = i / 1000 - - curveX = Math.round (Ax * Math.pow(t, 3)) + (Bx * Math.pow(t, 2)) + (Cx * t) + x0 - curveY = Math.round (Ay * Math.pow(t, 3)) + (By * Math.pow(t, 2)) + (Cy * t) + y0 - - if lowBound and curveY < lowBound - curveY = lowBound - else if highBound and curveY > highBound - curveY = highBound - - bezier[curveX] = curveY - - # Do a search for missing values in the bezier array and use linear - # interpolation to approximate their values - if bezier.length < end[0] + 1 - for i in [0..end[0]] - if not bezier[i]? - leftCoord = [i-1, bezier[i-1]] - - # Find the first value to the right. Ideally this loop will break - # very quickly. - for j in [i..end[0]] - if bezier[j]? - rightCoord = [j, bezier[j]] - break - - bezier[i] = leftCoord[1] + - ((rightCoord[1] - leftCoord[1]) / (rightCoord[0] - leftCoord[0])) * - (i - leftCoord[0]) - - # Edge case - bezier[end[0]] = bezier[end[0] - 1] if not bezier[end[0]]? - - return bezier - - -# Tons of color conversion functions -class Convert - # Converts the hex representation of a color to RGB values. - # Hex value can optionally start with the hash (#). - # - #
- # @param String hex The colors hex value - # @return Array The RGB representation - #- @hexToRGB: (hex) -> - hex = hex.substr(1) if hex.charAt(0) is "#" - r = parseInt hex.substr(0, 2), 16 - g = parseInt hex.substr(2, 2), 16 - b = parseInt hex.substr(4, 2), 16 - - r: r, g: g, b: b - - # Converts an RGB color to HSL. - # Assumes r, g, and b are in the set [0, 255] and - # returns h, s, and l in the set [0, 1]. - # - #
- # @param Number r Red channel - # @param Number g Green channel - # @param Number b Blue channel - # @return The HSL representation - #- @rgbToHSL: (r, g, b) -> - if typeof r is "object" - g = r.g - b = r.b - r = r.r - - r /= 255 - g /= 255 - b /= 255 - - max = Math.max r, g, b - min = Math.min r, g, b - l = (max + min) / 2 - - if max is min - h = s = 0 - else - d = max - min - s = if l > 0.5 then d / (2 - max - min) else d / (max + min) - h = switch max - when r then (g - b) / d + (if g < b then 6 else 0) - when g then (b - r) / d + 2 - when b then (r - g) / d + 4 - - h /= 6 - - h: h, s: s, l: l - - # Converts an HSL color value to RGB. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSL_color_space. - # Assumes h, s, and l are contained in the set [0, 1] and - # returns r, g, and b in the set [0, 255]. - # - #
- # @param Number h The hue - # @param Number s The saturation - # @param Number l The lightness - # @return Array The RGB representation - #- @hslToRGB: (h, s, l) -> - if typeof h is "object" - s = h.s - l = h.l - h = h.h - - if s is 0 - r = g = b = l - else - q = if l < 0.5 then l * (1 + s) else l + s - l * s - p = 2 * l - q - - r = @hueToRGB p, q, h + 1/3 - g = @hueToRGB p, q, h - b = @hueToRGB p, q, h - 1/3 - - r: r * 255, g: g * 255, b: b * 255 - - # Converts from the hue color space back to RGB - @hueToRGB: (p, q, t) -> - if t < 0 then t += 1 - if t > 1 then t -= 1 - if t < 1/6 then return p + (q - p) * 6 * t - if t < 1/2 then return q - if t < 2/3 then return p + (q - p) * (2/3 - t) * 6 - return p - - # Converts an RGB color value to HSV. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSV_color_space. - # Assumes r, g, and b are contained in the set [0, 255] and - # returns h, s, and v in the set [0, 1]. - # - #
- # @param Number r The red color value - # @param Number g The green color value - # @param Number b The blue color value - # @return Array The HSV representation - #- @rgbToHSV: (r, g, b) -> - r /= 255 - g /= 255 - b /= 255 - - max = Math.max r, g, b - min = Math.min r, g, b - v = max - d = max - min - - s = if max is 0 then 0 else d / max - - if max is min - h = 0 - else - h = switch max - when r then (g - b) / d + (if g < b then 6 else 0) - when g then (b - r) / d + 2 - when b then (r - g) / d + 4 - - h /= 6 - - h: h, s: s, v: v - - # Converts an HSV color value to RGB. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSV_color_space. - # Assumes h, s, and v are contained in the set [0, 1] and - # returns r, g, and b in the set [0, 255]. - # - #
- # @param Number h The hue - # @param Number s The saturation - # @param Number v The value - # @return Array The RGB representation - #- @hsvToRGB: (h, s, v) -> - i = Math.floor h * 6 - f = h * 6 - i - p = v * (1 - s) - q = v * (1 - f * s) - t = v * (1 - (1 - f) * s) - - switch i % 6 - when 0 - r = v - g = t - b = p - when 1 - r = q - g = v - b = p - when 2 - r = p - g = v - b = t - when 3 - r = p - g = q - b = v - when 4 - r = t - g = p - b = v - when 5 - r = v - g = p - b = q - - r: r * 255, g: g * 255, b: b * 255 - - # Converts a RGB color value to the XYZ color space. Formulas - # are based on http://en.wikipedia.org/wiki/SRGB assuming that - # RGB values are sRGB. - # - # Assumes r, g, and b are contained in the set [0, 255] and - # returns x, y, and z. - # - #
- # @param Number r The red color value - # @param Number g The green color value - # @param Number b The blue color value - # @return Array The XYZ representation - #- @rgbToXYZ: (r, g, b) -> - r /= 255 - g /= 255 - b /= 255 - - if r > 0.04045 - r = Math.pow((r + 0.055) / 1.055, 2.4) - else - r /= 12.92 - - if g > 0.04045 - g = Math.pow((g + 0.055) / 1.055, 2.4) - else - g /= 12.92 - - if b > 0.04045 - b = Math.pow((b + 0.055) / 1.055, 2.4) - else - b /= 12.92 - - x = r * 0.4124 + g * 0.3576 + b * 0.1805; - y = r * 0.2126 + g * 0.7152 + b * 0.0722; - z = r * 0.0193 + g * 0.1192 + b * 0.9505; - - x: x * 100, y: y * 100, z: z * 100 - - # Converts a XYZ color value to the sRGB color space. Formulas - # are based on http://en.wikipedia.org/wiki/SRGB and the resulting - # RGB value will be in the sRGB color space. - # Assumes x, y and z values are whatever they are and returns - # r, g and b in the set [0, 255]. - # - #
- # @param Number x The X value - # @param Number y The Y value - # @param Number z The Z value - # @return Array The RGB representation - #- @xyzToRGB: (x, y, z) -> - x /= 100 - y /= 100 - z /= 100 - - r = (3.2406 * x) + (-1.5372 * y) + (-0.4986 * z) - g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z) - b = (0.0557 * x) + (-0.2040 * y) + (1.0570 * z) - - if r > 0.0031308 - r = (1.055 * Math.pow(r, 0.4166666667)) - 0.055 - else - r *= 12.92 - - if g > 0.0031308 - g = (1.055 * Math.pow(g, 0.4166666667)) - 0.055 - else - g *= 12.92 - - if b > 0.0031308 - b = (1.055 * Math.pow(b, 0.4166666667)) - 0.055 - else - b *= 12.92 - - r: r * 255, g: g * 255, b: b * 255 - - # Converts a XYZ color value to the CIELAB color space. Formulas - # are based on http://en.wikipedia.org/wiki/Lab_color_space - # The reference white point used in the conversion is D65. - # Assumes x, y and z values are whatever they are and returns - # L*, a* and b* values - # - #
- # @param Number x The X value - # @param Number y The Y value - # @param Number z The Z value - # @return Array The Lab representation - #- @xyzToLab: (x, y, z) -> - if typeof x is "object" - y = x.y - z = x.z - x = x.x - - whiteX = 95.047 - whiteY = 100.0 - whiteZ = 108.883 - - x /= whiteX - y /= whiteY - z /= whiteZ - - if x > 0.008856451679 - x = Math.pow(x, 0.3333333333) - else - x = (7.787037037 * x) + 0.1379310345 - - if y > 0.008856451679 - y = Math.pow(y, 0.3333333333) - else - y = (7.787037037 * y) + 0.1379310345 - - if z > 0.008856451679 - z = Math.pow(z, 0.3333333333) - else - z = (7.787037037 * z) + 0.1379310345 - - l = 116 * y - 16 - a = 500 * (x - y) - b = 200 * (y - z) - - l: l, a: a, b: b - - # Converts a L*, a*, b* color values from the CIELAB color space - # to the XYZ color space. Formulas are based on - # http://en.wikipedia.org/wiki/Lab_color_space - # - # The reference white point used in the conversion is D65. - # Assumes L*, a* and b* values are whatever they are and returns - # x, y and z values. - # - #
- # @param Number l The L* value - # @param Number a The a* value - # @param Number b The b* value - # @return Array The XYZ representation - #- @labToXYZ: (l, a, b) -> - if typeof l is "object" - a = l.a - b = l.b - l = l.l - - y = (l + 16) / 116 - x = y + (a / 500) - z = y - (b / 200) - - if x > 0.2068965517 - x = x * x * x - else - x = 0.1284185493 * (x - 0.1379310345) - - if y > 0.2068965517 - y = y * y * y - else - y = 0.1284185493 * (y - 0.1379310345) - - if z > 0.2068965517 - z = z * z * z - else - z = 0.1284185493 * (z - 0.1379310345) - - # D65 reference white point - x: x * 95.047, y: y * 100.0, z: z * 108.883 - - @rgbToLab: (r, g, b) -> - if typeof r is "object" - g = r.g - b = r.b - r = r.r - - xyz = @rgbToXYZ(r, g, b) - @xyzToLab xyz - - @labToRGB: (l, a, b) -> - - -# Event system that can be used to register callbacks that get fired -# during certain times in the render process. -class Event - @events = {} - - # All of the supported event types - @types = [ - "processStart" - "processComplete" - "renderStart" - "renderFinished" - "blockStarted" - "blockFinished" - ] - - # Trigger an event - @trigger: (target, type, data) -> - if @events[type] and @events[type].length - for event in @events[type] - if event.target is null or target.id is event.target.id - event.fn.call target, data - - # Listen for an event. Optionally bind the listen to a single CamanInstance - # or all CamanInstances. - @listen: (target, type, fn) -> - # Adjust arguments if target is omitted - if typeof target is "string" - _type = target - _fn = type - - target = null - type = _type - fn = _fn - - # Validation - return false if type not in @types - - @events[type] = [] if not @events[type] - @events[type].push target: target, fn: fn - - return true - -Caman.Event = Event - -# Responsible for storing all of the filters -Caman.Filter = class Filter - # All of the different render operatives - @Type = - Single: 1 - Kernel: 2 - LayerDequeue: 3 - LayerFinished: 4 - LoadOverlay: 5 - Plugin: 6 - - # Registers a filter function - @register: (name, filterFunc) -> Caman::[name] = filterFunc - -# Various I/O based operations -Caman.IO = class IO - @domainRegex = /(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/ - - # Is the given URL remote? - # If a cross-origin setting is set, we assume you have CORS - # properly configured. - @isRemote: (img) -> - return false unless img? - return false if @corsEnabled(img) - return @isURLRemote img.src - - @corsEnabled: (img) -> - img.crossOrigin? and img.crossOrigin.toLowerCase() in ['anonymous', 'use-credentials'] - - @isURLRemote: (url) -> - matches = url.match @domainRegex - return if matches then matches[1] isnt document.domain else false - - @remoteCheck: (src) -> - if @isURLRemote src - if not Caman.remoteProxy.length - Log.info "Attempting to load a remote image without a configured proxy. URL: #{src}" - return - else - if Caman.isURLRemote Caman.remoteProxy - Log.info "Cannot use a remote proxy for loading images." - return - - "#{Caman.remoteProxy}?camanProxyUrl=#{encodeURIComponent(src)}" - - - @proxyUrl: (src) -> - "#{Caman.remoteProxy}?#{Caman.proxyParam}=#{encodeURIComponent(src)}" - - # Shortcut for using one of the bundled proxies. - @useProxy: (lang) -> - langToExt = - ruby: 'rb' - python: 'py' - perl: 'pl' - javascript: 'js' - - lang = lang.toLowerCase() - lang = langToExt[lang] if langToExt[lang]? - "proxies/caman_proxy.#{lang}" - -# Grabs the canvas data, encodes it to Base64, then sets the browser location to -# the encoded data so that the user will be prompted to download it. -Caman::save = -> - if exports? - @nodeSave.apply @, arguments - else - @browserSave.apply @, arguments - -Caman::browserSave = (type = "png") -> - type = type.toLowerCase() - - # Force download (its a bit hackish) - image = @toBase64(type).replace "image/#{type}", "image/octet-stream" - document.location.href = image - -Caman::nodeSave = (file, overwrite = true) -> - try - stats = fs.statSync file - return false if stats.isFile() and not overwrite - catch e - Log.debug "Creating output file #{file}" - - fs.writeFile file, @canvas.toBuffer(), -> - Log.debug "Finished writing to #{file}" - -# Takes the current canvas data, converts it to Base64, then sets it as the source -# of a new Image object and returns it. -Caman::toImage = (type) -> - img = document.createElement 'img' - img.src = @toBase64 type - img.width = @dimensions.width - img.height = @dimensions.height - - if window.devicePixelRatio - img.width /= window.devicePixelRatio - img.height /= window.devicePixelRatio - - return img - -# Base64 encodes the current canvas -Caman::toBase64 = (type = "png") -> - type = type.toLowerCase() - return @canvas.toDataURL "image/#{type}" - - -# The entire layering system for Caman resides in this file. Layers get their own canvasLayer -# objectwhich is created when newLayer() is called. For extensive information regarding the -# specifics of howthe layering system works, there is an in-depth blog post on this very topic. -# Instead of copying the entirety of that post, I'll simply point you towards the -# [blog link](http://blog.meltingice.net/programming/implementing-layers-camanjs). -# -# However, the gist of the layering system is that, for each layer, it creates a new canvas -# element and then either copies the parent layer's data or applies a solid color to the new -# layer. After some (optional) effects are applied, the layer is blended back into the parent -# canvas layer using one of many different blending algorithms. -# -# You can also load an image (local or remote, with a proxy) into a canvas layer, which is useful -# if you want to add textures to an image. -class Layer - constructor: (@c) -> - # Compatibility - @filter = @c - - @options = - blendingMode: 'normal' - opacity: 1.0 - - # Each layer gets its own unique ID - @layerID = Util.uniqid.get() - - # Create the canvas for this layer - @canvas = if exports? then new Canvas() else document.createElement('canvas') - - @canvas.width = @c.dimensions.width - @canvas.height = @c.dimensions.height - - @context = @canvas.getContext('2d') - @context.createImageData @canvas.width, @canvas.height - @imageData = @context.getImageData 0, 0, @canvas.width, @canvas.height - @pixelData = @imageData.data - - # If you want to create nested layers - newLayer: (cb) -> @c.newLayer.call @c, cb - - # Sets the blending mode of this layer. The mode is the name of a blender function. - setBlendingMode: (mode) -> - @options.blendingMode = mode - return @ - - # Sets the opacity of this layer. This affects how much of this layer is applied to the parent - # layer at render time. - opacity: (opacity) -> - @options.opacity = opacity / 100 - return @ - - # Copies the contents of the parent layer to this layer - copyParent: -> - parentData = @c.pixelData - - for i in [0...@c.pixelData.length] by 4 - @pixelData[i] = parentData[i] - @pixelData[i+1] = parentData[i+1] - @pixelData[i+2] = parentData[i+2] - @pixelData[i+3] = parentData[i+3] - - return @ - - # Fills this layer with a single color - fillColor: -> @c.fillColor.apply @c, arguments - - # Loads and overlays an image onto this layer - overlayImage: (image) -> - if typeof image is "object" - image = image.src - else if typeof image is "string" and image[0] is "#" - image = $(image).src - - return @ if not image - - @c.renderer.renderQueue.push - type: Filter.Type.LoadOverlay - src: image - layer: @ - - return @ - - # Takes the contents of this layer and applies them to the parent layer at render time. This - # should never be called explicitly by the user. - applyToParent: -> - parentData = @c.pixelStack[@c.pixelStack.length - 1] - layerData = @c.pixelData - - for i in [0...layerData.length] by 4 - rgbaParent = - r: parentData[i] - g: parentData[i+1] - b: parentData[i+2] - a: parentData[i+3] - - rgbaLayer = - r: layerData[i] - g: layerData[i+1] - b: layerData[i+2] - a: layerData[i+3] - - result = Blender.execute @options.blendingMode, rgbaLayer, rgbaParent - - result.r = Util.clampRGB result.r - result.g = Util.clampRGB result.g - result.b = Util.clampRGB result.b - result.a = rgbaLayer.a if not result.a? - - parentData[i] = rgbaParent.r - ( - (rgbaParent.r - result.r) * (@options.opacity * (result.a / 255)) - ) - parentData[i+1] = rgbaParent.g - ( - (rgbaParent.g - result.g) * (@options.opacity * (result.a / 255)) - ) - parentData[i+2] = rgbaParent.b - ( - (rgbaParent.b - result.b) * (@options.opacity * (result.a / 255)) - ) - - -# Simple console logger class that can be toggled on and off based on Caman.DEBUG -class Logger - constructor: -> - for name in ['log', 'info', 'warn', 'error'] - @[name] = do (name) -> - (args...) -> - return if not Caman.DEBUG - try - console[name].apply console, args - catch e - # We're probably using IE9 or earlier - console[name] args - - @debug = @log - -Log = new Logger() - -# This object is available inside of the process() loop, and it lets filter developers have simple -# access to any arbitrary pixel in the image, as well as information about the current pixel in -# the loop. -class PixelInfo - @coordinatesToLocation: (x, y, width) -> - (y * width + x) * 4 - - @locationToCoordinates: (loc, width) -> - y = Math.floor(loc / (width * 4)) - x = (loc % (width * 4)) / 4 - - return x: x, y: y - - constructor: (@c) -> @loc = 0 - - # Retrieves the X, Y location of the current pixel. The origin is at the bottom left corner of - # the image, like a normal coordinate system. - locationXY: -> - y = @c.dimensions.height - Math.floor(@loc / (@c.dimensions.width * 4)) - x = (@loc % (@c.dimensions.width * 4)) / 4 - - return x: x, y: y - - # Returns an RGBA object for a pixel whose location is specified in relation to the current - # pixel. - getPixelRelative: (horiz, vert) -> - # We invert the vert_offset in order to make the coordinate system non-inverted. In laymans - # terms: -1 means down and +1 means up. - newLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz) - - if newLoc > @c.pixelData.length or newLoc < 0 - return r: 0, g: 0, b: 0, a: 0 - - return { - r: @c.pixelData[newLoc] - g: @c.pixelData[newLoc + 1] - b: @c.pixelData[newLoc + 2] - a: @c.pixelData[newLoc + 3] - } - - # The counterpart to getPixelRelative, this updates the value of a pixel whose location is - # specified in relation to the current pixel. - putPixelRelative: (horiz, vert, rgba) -> - nowLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz) - - return if newLoc > @c.pixelData.length or newLoc < 0 - - @c.pixelData[newLoc] = rgba.r - @c.pixelData[newLoc + 1] = rgba.g - @c.pixelData[newLoc + 2] = rgba.b - @c.pixelData[newLoc + 3] = rgba.a - - return true - - # Gets an RGBA object for an arbitrary pixel in the canvas specified by absolute X, Y coordinates - getPixel: (x, y) -> - loc = @coordinatesToLocation(x, y, @width) - - return { - r: @c.pixelData[loc] - g: @c.pixelData[loc + 1] - b: @c.pixelData[loc + 2] - a: @c.pixelData[loc + 3] - } - - # Updates the pixel at the given X, Y coordinate - putPixel: (x, y, rgba) -> - loc = @coordinatesToLocation(x, y, @width) - - @c.pixelData[loc] = rgba.r - @c.pixelData[loc + 1] = rgba.g - @c.pixelData[loc + 2] = rgba.b - @c.pixelData[loc + 3] = rgba.a - - -# Stores and registers standalone plugins -class Plugin - @plugins = {} - - @register: (name, plugin) -> @plugins[name] = plugin - @execute: (context, name, args) -> @plugins[name].apply context, args - -Caman.Plugin = Plugin - -# Handles all of the various rendering methods in Caman. Most of the image modification happens -# here. A new Renderer object is created for every render operation. -Caman.Renderer = class Renderer - # The number of blocks to split the image into during the render process to simulate - # concurrency. This also helps the browser manage the (possibly) long running render jobs. - @Blocks = if Caman.NodeJS then require('os').cpus().length else 4 - - constructor: (@c) -> - @renderQueue = [] - @modPixelData = null - - add: (job) -> - return unless job? - @renderQueue.push job - - # Grabs the next operation from the render queue and passes it to Renderer - # for execution - processNext: => - # If the queue is empty, fire the finished callback - if @renderQueue.length is 0 - Event.trigger @, "renderFinished" - @finishedFn.call(@c) if @finishedFn? - - return @ - - @currentJob = @renderQueue.shift() - - switch @currentJob.type - when Filter.Type.LayerDequeue - layer = @c.canvasQueue.shift() - @c.executeLayer layer - @processNext() - when Filter.Type.LayerFinished - @c.applyCurrentLayer() - @c.popContext() - @processNext() - when Filter.Type.LoadOverlay - @loadOverlay @currentJob.layer, @currentJob.src - when Filter.Type.Plugin - @executePlugin() - else - @executeFilter() - - execute: (callback) -> - @finishedFn = callback - @modPixelData = Util.dataArray(@c.pixelData.length) - - @processNext() - - eachBlock: (fn) -> - # Prepare all the required render data - @blocksDone = 0 - - n = @c.pixelData.length - blockPixelLength = Math.floor (n / 4) / Renderer.Blocks - blockN = blockPixelLength * 4 - lastBlockN = blockN + ((n / 4) % Renderer.Blocks) * 4 - - for i in [0...Renderer.Blocks] - start = i * blockN - end = start + (if i is Renderer.Blocks - 1 then lastBlockN else blockN) - - if Caman.NodeJS - f = Fiber => fn.call(@, i, start, end) - bnum = f.run() - @blockFinished(bnum) - else - setTimeout do (i, start, end) => - => fn.call(@, i, start, end) - , 0 - - # The core of the image rendering, this function executes the provided filter. - # - # NOTE: this does not write the updated pixel data to the canvas. That happens when all filters - # are finished rendering in order to be as fast as possible. - executeFilter: -> - Event.trigger @c, "processStart", @currentJob - - if @currentJob.type is Filter.Type.Single - @eachBlock @renderBlock - else - @eachBlock @renderKernel - - # Executes a standalone plugin - executePlugin: -> - Log.debug "Executing plugin #{@currentJob.plugin}" - Plugin.execute @c, @currentJob.plugin, @currentJob.args - Log.debug "Plugin #{@currentJob.plugin} finished!" - - @processNext() - - # Renders a single block of the canvas with the current filter function - renderBlock: (bnum, start, end) -> - Log.debug "Block ##{bnum} - Filter: #{@currentJob.name}, Start: #{start}, End: #{end}" - Event.trigger @c, "blockStarted", - blockNum: bnum - totalBlocks: Renderer.Blocks - startPixel: start - endPixel: end - - data = r: 0, g: 0, b: 0, a: 0 - pixelInfo = new PixelInfo @c - - for i in [start...end] by 4 - pixelInfo.loc = i - - data.r = @c.pixelData[i] - data.g = @c.pixelData[i+1] - data.b = @c.pixelData[i+2] - data.a = @c.pixelData[i+3] - - res = @currentJob.processFn.call pixelInfo, data - res.a = data.a if not res.a? - - @c.pixelData[i] = Util.clampRGB res.r - @c.pixelData[i+1] = Util.clampRGB res.g - @c.pixelData[i+2] = Util.clampRGB res.b - @c.pixelData[i+3] = Util.clampRGB res.a - - if Caman.NodeJS - Fiber.yield(bnum) - else - @blockFinished bnum - - # Applies an image kernel to the canvas - renderKernel: (bnum, start, end) -> - name = @currentJob.name - bias = @currentJob.bias - divisor = @currentJob.divisor - n = @c.pixelData.length - - adjust = @currentJob.adjust - adjustSize = Math.sqrt adjust.length - - kernel = [] - - Log.debug "Rendering kernel - Filter: #{@currentJob.name}" - - start = Math.max start, @c.dimensions.width * 4 * ((adjustSize - 1) / 2) - end = Math.min end, n - (@c.dimensions.width * 4 * ((adjustSize - 1) / 2)) - - builder = (adjustSize - 1) / 2 - - pixelInfo = new PixelInfo @c - - for i in [start...end] by 4 - pixelInfo.loc = i - builderIndex = 0 - - for j in [-builder..builder] - for k in [builder..-builder] - pixel = pixelInfo.getPixelRelative j, k - kernel[builderIndex * 3] = pixel.r - kernel[builderIndex * 3 + 1] = pixel.g - kernel[builderIndex * 3 + 2] = pixel.b - - builderIndex++ - - res = @processKernel adjust, kernel, divisor, bias - - @modPixelData[i] = Util.clampRGB(res.r) - @modPixelData[i+1] = Util.clampRGB(res.g) - @modPixelData[i+2] = Util.clampRGB(res.b) - @modPixelData[i+3] = @c.pixelData[i+3] - - if Caman.NodeJS - Fiber.yield(bnum) - else - @blockFinished bnum - - # Called when a single block is finished rendering. Once all blocks are done, we signal that this - # filter is finished rendering and continue to the next step. - blockFinished: (bnum) -> - Log.debug "Block ##{bnum} finished! Filter: #{@currentJob.name}" if bnum >= 0 - @blocksDone++ - - Event.trigger @c, "blockFinished", - blockNum: bnum - blocksFinished: @blocksDone - totalBlocks: Renderer.Blocks - - if @blocksDone is Renderer.Blocks - if @currentJob.type is Filter.Type.Kernel - for i in [0...@c.pixelData.length] - @c.pixelData[i] = @modPixelData[i] - - Log.debug "Filter #{@currentJob.name} finished!" if bnum >=0 - Event.trigger @c, "processComplete", @currentJob - - @processNext() - - # The "filter function" for kernel adjustments. - processKernel: (adjust, kernel, divisor, bias) -> - val = r: 0, g: 0, b: 0 - - for i in [0...adjust.length] - val.r += adjust[i] * kernel[i * 3] - val.g += adjust[i] * kernel[i * 3 + 1] - val.b += adjust[i] * kernel[i * 3 + 2] - - val.r = (val.r / divisor) + bias - val.g = (val.g / divisor) + bias - val.b = (val.b / divisor) + bias - val - - # Loads an image onto the current canvas - loadOverlay: (layer, src) -> - img = document.createElement 'img' - img.onload = => - layer.context.drawImage img, 0, 0, @c.dimensions.width, @c.dimensions.height - layer.imageData = layer.context.getImageData 0, 0, @c.dimensions.width, @c.dimensions.height - layer.pixelData = layer.imageData.data - - @c.pixelData = layer.pixelData - - @processNext() - - proxyUrl = IO.remoteCheck src - img.src = if proxyUrl? then proxyUrl else src - -# Used for storing instances of CamanInstance objects such that, when Caman() is called on an -# already initialized element, it returns that object instead of re-initializing. -Caman.Store = class Store - @items = {} - - @has: (search) -> @items[search]? - @get: (search) -> @items[search] - @put: (name, obj) -> @items[name] = obj - @execute: (search, callback) -> - setTimeout => - callback.call @get(search), @get(search) - , 0 - - return @get(search) - - @flush: (name = false) -> - if name then delete @items[name] else @items = {} - -# Directly apply the child layer's pixels to the parent layer with no special changes -Blender.register "normal", (rgbaLayer, rgbaParent) -> - r: rgbaLayer.r - g: rgbaLayer.g - b: rgbaLayer.b - -# Apply the child to the parent by multiplying the color values. This generally creates contrast. -Blender.register "multiply", (rgbaLayer, rgbaParent) -> - r: (rgbaLayer.r * rgbaParent.r) / 255 - g: (rgbaLayer.g * rgbaParent.g) / 255 - b: (rgbaLayer.b * rgbaParent.b) / 255 - -Blender.register "screen", (rgbaLayer, rgbaParent) -> - r: 255 - (((255 - rgbaLayer.r) * (255 - rgbaParent.r)) / 255) - g: 255 - (((255 - rgbaLayer.g) * (255 - rgbaParent.g)) / 255) - b: 255 - (((255 - rgbaLayer.b) * (255 - rgbaParent.b)) / 255) - - -Blender.register "overlay", (rgbaLayer, rgbaParent) -> - result = {} - result.r = - if rgbaParent.r > 128 - 255 - 2 * (255 - rgbaLayer.r) * (255 - rgbaParent.r) / 255 - else (rgbaParent.r * rgbaLayer.r * 2) / 255 - - result.g = - if rgbaParent.g > 128 - 255 - 2 * (255 - rgbaLayer.g) * (255 - rgbaParent.g) / 255 - else (rgbaParent.g * rgbaLayer.g * 2) / 255 - - result.b = - if rgbaParent.b > 128 - 255 - 2 * (255 - rgbaLayer.b) * (255 - rgbaParent.b) / 255 - else (rgbaParent.b * rgbaLayer.b * 2) / 255 - - result - -Blender.register "difference", (rgbaLayer, rgbaParent) -> - r: rgbaLayer.r - rgbaParent.r - g: rgbaLayer.g - rgbaParent.g - b: rgbaLayer.b - rgbaParent.b - -Blender.register "addition", (rgbaLayer, rgbaParent) -> - r: rgbaParent.r + rgbaLayer.r - g: rgbaParent.g + rgbaLayer.g - b: rgbaParent.b + rgbaLayer.b - -Blender.register "exclusion", (rgbaLayer, rgbaParent) -> - r: 128 - 2 * (rgbaParent.r - 128) * (rgbaLayer.r - 128) / 255 - g: 128 - 2 * (rgbaParent.g - 128) * (rgbaLayer.g - 128) / 255 - b: 128 - 2 * (rgbaParent.b - 128) * (rgbaLayer.b - 128) / 255 - -Blender.register "softLight", (rgbaLayer, rgbaParent) -> - result = {} - - result.r = - if rgbaParent.r > 128 - 255 - ((255 - rgbaParent.r) * (255 - (rgbaLayer.r - 128))) / 255 - else (rgbaParent.r * (rgbaLayer.r + 128)) / 255 - - result.g = - if rgbaParent.g > 128 - 255 - ((255 - rgbaParent.g) * (255 - (rgbaLayer.g - 128))) / 255 - else (rgbaParent.g * (rgbaLayer.g + 128)) / 255 - - result.b = - if rgbaParent.b > 128 - 255 - ((255 - rgbaParent.b) * (255 - (rgbaLayer.b - 128))) / 255 - else (rgbaParent.b * (rgbaLayer.b + 128)) / 255 - - result - -Blender.register "lighten", (rgbaLayer, rgbaParent) -> - r: if rgbaParent.r > rgbaLayer.r then rgbaParent.r else rgbaLayer.r - g: if rgbaParent.g > rgbaLayer.g then rgbaParent.g else rgbaLayer.g - b: if rgbaParent.b > rgbaLayer.b then rgbaParent.b else rgbaLayer.b - -Blender.register "darken", (rgbaLayer, rgbaParent) -> - r: if rgbaParent.r > rgbaLayer.r then rgbaLayer.r else rgbaParent.r - g: if rgbaParent.g > rgbaLayer.g then rgbaLayer.g else rgbaParent.g - b: if rgbaParent.b > rgbaLayer.b then rgbaLayer.b else rgbaParent.b - -# 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](http://camanjs.com/docs/plugin-creation) page, and for information on using -# the plugins, check out the [Built-In Functionality](http://camanjs.com/docs/built-in) 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](http://en.wikipedia.org/wiki/Hue). -# -# ### 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 - - rgb = Convert.hsvToRGB hsv.h, hsv.s, hsv.v - rgb.a = rgba.a - rgb - -# ## 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] - - - -# Allows us to crop the canvas and produce a new smaller -# canvas. -Caman.Plugin.register "crop", (width, height, x = 0, y = 0) -> - # Create our new canvas element - if exports? - canvas = new Canvas width, height - else - canvas = document.createElement 'canvas' - Util.copyAttributes @canvas, canvas - - canvas.width = width - canvas.height = height - - ctx = canvas.getContext '2d' - - # Perform the cropping by drawing to the new canvas - ctx.drawImage @canvas, x, y, width, height, 0, 0, width, height - - @cropCoordinates = x: x, y: y - - # Update all of the references - @cropped = true - @replaceCanvas canvas - -# Resize the canvas and the image to a new size -Caman.Plugin.register "resize", (newDims = null) -> - # Calculate new size - if newDims is null or (!newDims.width? and !newDims.height?) - Log.error "Invalid or missing dimensions given for resize" - return - - if not newDims.width? - # Calculate width - newDims.width = @canvas.width * newDims.height / @canvas.height - else if not newDims.height? - # Calculate height - newDims.height = @canvas.height * newDims.width / @canvas.width - - if exports? - canvas = new Canvas newDims.width, newDims.height - else - canvas = document.createElement 'canvas' - Util.copyAttributes @canvas, canvas - - canvas.width = newDims.width - canvas.height = newDims.height - - ctx = canvas.getContext '2d' - - ctx.drawImage @canvas, - 0, 0, - @canvas.width, @canvas.height, - 0, 0, - newDims.width, newDims.height - - @resized = true - @replaceCanvas canvas - -Caman.Filter.register "crop", -> - @processPlugin "crop", Array.prototype.slice.call(arguments, 0) - -Caman.Filter.register "resize", -> - @processPlugin "resize", Array.prototype.slice.call(arguments, 0) - -### -IDMC plugins -### - -# resetOriginalPixelData is broken, this is the way it should be - -Caman.Filter.register "idmc_reset_original_pixeldata", () -> - @processPlugin "idmc_reset_original_pixeldata", null - -Caman.Plugin.register "idmc_reset_original_pixeldata", () -> - - Log.debug "idmc_reset_original_pixeldata" - - @originalPixelData = Util.dataArray(@pixelData.length) - @originalPixelData[i] = pixel for pixel, i in @pixelData - @ - -# Adjust minimum and maximim pixel values - -Caman.Filter.register "idmc_set_min_max_pixel_values", (min_pixel_value, max_pixel_value) -> - @processPlugin "idmc_set_min_max_pixel_values", [min_pixel_value, max_pixel_value] - - -Caman.Plugin.register "idmc_set_min_max_pixel_values", (min_pixel_value, max_pixel_value) -> - org_pixels = @originalPixelData - pixels = @pixelData - width = @dimensions.width - height = @dimensions.height - - if not @idmc_set_min_max_pixel_values_r_colormap? - @idmc_set_min_max_pixel_values_r_colormap = (i for i in [0...256]) - - if not @idmc_set_min_max_pixel_values_g_colormap? - @idmc_set_min_max_pixel_values_g_colormap = (i for i in [0...256]) - - if not @idmc_set_min_max_pixel_values_b_colormap? - @idmc_set_min_max_pixel_values_b_colormap = (i for i in [0...256]) - - r_colormap = @idmc_set_min_max_pixel_values_r_colormap - g_colormap = @idmc_set_min_max_pixel_values_g_colormap - b_colormap = @idmc_set_min_max_pixel_values_b_colormap - - - idx = (x,y) => (y*width + x) * 4 - - for i in [0...256] - #Log.debug "i: " +i+ ", " +min_pixel_value+ ", " + "max_pixel_value" - - - index = Math.round((256 * (i - min_pixel_value)) / (max_pixel_value - min_pixel_value)); - #Log.debug "index1: " +index - - index = if index < 0 - 0 - else if i > 255 - 255 - else - index - - #Log.debug "index2: " +index - - r_colormap[i] = if i < min_pixel_value - 0 - else if i > max_pixel_value - 255 - else - index - - g_colormap[i] = if i < min_pixel_value - 0 - else if i > max_pixel_value - 255 - else - index - - b_colormap[i] = if i < min_pixel_value - 0 - else if i > max_pixel_value - 255 - else - index - - - for y in [0...height] - for x in [0...width] - #Log.debug "org_pixels[" +x+ "," +y+ "]: " + org_pixels[idx(x,y)] - - r = org_pixels[idx(x,y)] - g = org_pixels[idx(x,y) + 1] - b = org_pixels[idx(x,y) + 2] - a = org_pixels[idx(x,y) + 3] - - pixels[idx(x,y)] = r_colormap[r] - pixels[idx(x,y) + 1] = g_colormap[g] - pixels[idx(x,y) + 2] = b_colormap[b] - pixels[idx(x,y) + 3] = 255 - - @ - -Caman.Filter.register "idmc_test", () -> - @processPlugin "idmc_test", [] - -Caman.Plugin.register "idmc_test", () -> - @process "idmc_test", (rgba) -> - Log.debug "IDMC test func" - rgba -@ - diff --git a/mig/images/lib/CamanJS-4.1.1/dist/caman.full.js b/mig/images/lib/CamanJS-4.1.1/dist/caman.full.js deleted file mode 100644 index b58ea1f01..000000000 --- a/mig/images/lib/CamanJS-4.1.1/dist/caman.full.js +++ /dev/null @@ -1,2447 +0,0 @@ -// Generated by CoffeeScript 1.6.3 -(function() { - var $, Analyze, Blender, Calculate, Caman, CamanParser, Canvas, Convert, Event, Fiber, Filter, IO, Image, Layer, Log, Logger, PixelInfo, Plugin, Renderer, Root, Store, Util, fs, slice, - __hasProp = {}.hasOwnProperty, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, - __slice = [].slice, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - - slice = Array.prototype.slice; - - $ = function(sel, root) { - if (root == null) { - root = document; - } - if (typeof sel === "object" || (typeof exports !== "undefined" && exports !== null)) { - return sel; - } - return root.querySelector(sel); - }; - - Util = (function() { - function Util() {} - - Util.uniqid = (function() { - var id; - id = 0; - return { - get: function() { - return id++; - } - }; - })(); - - Util.extend = function(obj) { - var copy, dest, prop, src, _i, _len; - dest = obj; - src = slice.call(arguments, 1); - for (_i = 0, _len = src.length; _i < _len; _i++) { - copy = src[_i]; - for (prop in copy) { - if (!__hasProp.call(copy, prop)) continue; - dest[prop] = copy[prop]; - } - } - return dest; - }; - - Util.clampRGB = function(val) { - if (val < 0) { - return 0; - } - if (val > 255) { - return 255; - } - return val; - }; - - Util.copyAttributes = function(from, to, opts) { - var attr, _i, _len, _ref, _ref1, _results; - if (opts == null) { - opts = {}; - } - _ref = from.attributes; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - attr = _ref[_i]; - if ((opts.except != null) && (_ref1 = attr.nodeName, __indexOf.call(opts.except, _ref1) >= 0)) { - continue; - } - _results.push(to.setAttribute(attr.nodeName, attr.nodeValue)); - } - return _results; - }; - - Util.dataArray = function(length) { - if (length == null) { - length = 0; - } - if (Caman.NodeJS || (window.Uint8Array != null)) { - return new Uint8Array(length); - } - return new Array(length); - }; - - return Util; - - })(); - - if (typeof exports !== "undefined" && exports !== null) { - Root = exports; - Canvas = require('canvas'); - Image = Canvas.Image; - Fiber = require('fibers'); - fs = require('fs'); - } else { - Root = window; - } - - Root.Caman = Caman = (function() { - Caman.version = { - release: "4.1.1", - date: "4/8/2013" - }; - - Caman.DEBUG = false; - - Caman.NodeJS = typeof exports !== "undefined" && exports !== null; - - Caman.autoload = !Caman.NodeJS; - - Caman.allowRevert = true; - - Caman.crossOrigin = "anonymous"; - - Caman.toString = function() { - return "Version " + Caman.version.release + ", Released " + Caman.version.date; - }; - - Caman.remoteProxy = ""; - - Caman.proxyParam = "camanProxyUrl"; - - Caman.getAttrId = function(canvas) { - if (Caman.NodeJS) { - return true; - } - if (typeof canvas === "string") { - canvas = $(canvas); - } - if (!((canvas != null) && (canvas.getAttribute != null))) { - return null; - } - return canvas.getAttribute('data-caman-id'); - }; - - function Caman() { - var args, callback, id, - _this = this; - if (arguments.length === 0) { - throw "Invalid arguments"; - } - if (this instanceof Caman) { - this.finishInit = this.finishInit.bind(this); - this.imageLoaded = this.imageLoaded.bind(this); - args = arguments[0]; - if (!Caman.NodeJS) { - id = parseInt(Caman.getAttrId(args[0]), 10); - callback = typeof args[1] === "function" ? args[1] : typeof args[2] === "function" ? args[2] : function() {}; - if (!isNaN(id) && Store.has(id)) { - return Store.execute(id, callback); - } - } - this.id = Util.uniqid.get(); - this.initializedPixelData = this.originalPixelData = null; - this.cropCoordinates = { - x: 0, - y: 0 - }; - this.cropped = false; - this.resized = false; - this.pixelStack = []; - this.layerStack = []; - this.canvasQueue = []; - this.currentLayer = null; - this.scaled = false; - this.analyze = new Analyze(this); - this.renderer = new Renderer(this); - this.domIsLoaded(function() { - _this.parseArguments(args); - return _this.setup(); - }); - return this; - } else { - return new Caman(arguments); - } - } - - Caman.prototype.domIsLoaded = function(cb) { - var listener, - _this = this; - if (Caman.NodeJS) { - return setTimeout(function() { - return cb.call(_this); - }, 0); - } else { - if (document.readyState === "complete") { - Log.debug("DOM initialized"); - return setTimeout(function() { - return cb.call(_this); - }, 0); - } else { - listener = function() { - if (document.readyState === "complete") { - Log.debug("DOM initialized"); - return cb.call(_this); - } - }; - return document.addEventListener("readystatechange", listener, false); - } - } - }; - - Caman.prototype.parseArguments = function(args) { - var key, val, _ref, _results; - if (args.length === 0) { - throw "Invalid arguments given"; - } - this.initObj = null; - this.initType = null; - this.imageUrl = null; - this.callback = function() {}; - this.setInitObject(args[0]); - if (args.length === 1) { - return; - } - switch (typeof args[1]) { - case "string": - this.imageUrl = args[1]; - break; - case "function": - this.callback = args[1]; - } - if (args.length === 2) { - return; - } - this.callback = args[2]; - if (args.length === 4) { - _ref = args[4]; - _results = []; - for (key in _ref) { - if (!__hasProp.call(_ref, key)) continue; - val = _ref[key]; - _results.push(this.options[key] = val); - } - return _results; - } - }; - - Caman.prototype.setInitObject = function(obj) { - if (Caman.NodeJS) { - this.initObj = obj; - this.initType = 'node'; - return; - } - if (typeof obj === "object") { - this.initObj = obj; - } else { - this.initObj = $(obj); - } - if (this.initObj == null) { - throw "Could not find image or canvas for initialization."; - } - return this.initType = this.initObj.nodeName.toLowerCase(); - }; - - Caman.prototype.setup = function() { - switch (this.initType) { - case "node": - return this.initNode(); - case "img": - return this.initImage(); - case "canvas": - return this.initCanvas(); - } - }; - - Caman.prototype.initNode = function() { - var _this = this; - Log.debug("Initializing for NodeJS"); - this.image = new Image(); - this.image.onload = function() { - Log.debug("Image loaded. Width = " + (_this.imageWidth()) + ", Height = " + (_this.imageHeight())); - _this.canvas = new Canvas(_this.imageWidth(), _this.imageHeight()); - return _this.finishInit(); - }; - this.image.onerror = function(err) { - throw err; - }; - return this.image.src = this.initObj; - }; - - Caman.prototype.initImage = function() { - this.image = this.initObj; - this.canvas = document.createElement('canvas'); - this.context = this.canvas.getContext('2d'); - Util.copyAttributes(this.image, this.canvas, { - except: ['src'] - }); - this.image.parentNode.replaceChild(this.canvas, this.image); - this.imageAdjustments(); - return this.waitForImageLoaded(); - }; - - Caman.prototype.initCanvas = function() { - this.canvas = this.initObj; - this.context = this.canvas.getContext('2d'); - if (this.imageUrl != null) { - this.image = document.createElement('img'); - this.image.src = this.imageUrl; - this.imageAdjustments(); - return this.waitForImageLoaded(); - } else { - return this.finishInit(); - } - }; - - Caman.prototype.imageAdjustments = function() { - if (this.needsHiDPISwap()) { - Log.debug(this.image.src, "->", this.hiDPIReplacement()); - this.swapped = true; - this.image.src = this.hiDPIReplacement(); - } - if (IO.isRemote(this.image)) { - this.image.src = IO.proxyUrl(this.image.src); - return Log.debug("Remote image detected, using URL = " + this.image.src); - } - }; - - Caman.prototype.waitForImageLoaded = function() { - if (this.isImageLoaded()) { - return this.imageLoaded(); - } else { - return this.image.onload = this.imageLoaded; - } - }; - - Caman.prototype.isImageLoaded = function() { - if (!this.image.complete) { - return false; - } - if ((this.image.naturalWidth != null) && this.image.naturalWidth === 0) { - return false; - } - return true; - }; - - Caman.prototype.imageWidth = function() { - return this.image.width || this.image.naturalWidth; - }; - - Caman.prototype.imageHeight = function() { - return this.image.height || this.image.naturalHeight; - }; - - Caman.prototype.imageLoaded = function() { - Log.debug("Image loaded. Width = " + (this.imageWidth()) + ", Height = " + (this.imageHeight())); - if (this.swapped) { - this.canvas.width = this.imageWidth() / this.hiDPIRatio(); - this.canvas.height = this.imageHeight() / this.hiDPIRatio(); - } else { - this.canvas.width = this.imageWidth(); - this.canvas.height = this.imageHeight(); - } - return this.finishInit(); - }; - - Caman.prototype.finishInit = function() { - var i, pixel, _i, _len, _ref; - if (this.context == null) { - this.context = this.canvas.getContext('2d'); - } - this.originalWidth = this.preScaledWidth = this.width = this.canvas.width; - this.originalHeight = this.preScaledHeight = this.height = this.canvas.height; - this.hiDPIAdjustments(); - if (!this.hasId()) { - this.assignId(); - } - if (this.image != null) { - this.context.drawImage(this.image, 0, 0, this.imageWidth(), this.imageHeight(), 0, 0, this.preScaledWidth, this.preScaledHeight); - } - this.reloadCanvasData(); - if (Caman.allowRevert) { - this.initializedPixelData = Util.dataArray(this.pixelData.length); - this.originalPixelData = Util.dataArray(this.pixelData.length); - _ref = this.pixelData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - this.initializedPixelData[i] = pixel; - this.originalPixelData[i] = pixel; - } - } - this.dimensions = { - width: this.canvas.width, - height: this.canvas.height - }; - Store.put(this.id, this); - this.callback.call(this, this); - return this.callback = function() {}; - }; - - Caman.prototype.reloadCanvasData = function() { - this.imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); - return this.pixelData = this.imageData.data; - }; - - Caman.prototype.resetOriginalPixelData = function() { - var pixel, _i, _len, _ref, _results; - if (!Caman.allowRevert) { - throw "Revert disabled"; - } - this.originalPixelData = Util.dataArray(this.pixelData.length); - _ref = this.pixelData; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - pixel = _ref[_i]; - _results.push(this.originalPixelData.push(pixel)); - } - return _results; - }; - - Caman.prototype.hasId = function() { - return Caman.getAttrId(this.canvas) != null; - }; - - Caman.prototype.assignId = function() { - if (Caman.NodeJS || this.canvas.getAttribute('data-caman-id')) { - return; - } - return this.canvas.setAttribute('data-caman-id', this.id); - }; - - Caman.prototype.hiDPIDisabled = function() { - return this.canvas.getAttribute('data-caman-hidpi-disabled') !== null; - }; - - Caman.prototype.hiDPIAdjustments = function() { - var ratio; - if (Caman.NodeJS || this.hiDPIDisabled()) { - return; - } - ratio = this.hiDPIRatio(); - if (ratio !== 1) { - Log.debug("HiDPI ratio = " + ratio); - this.scaled = true; - this.preScaledWidth = this.canvas.width; - this.preScaledHeight = this.canvas.height; - this.canvas.width = this.preScaledWidth * ratio; - this.canvas.height = this.preScaledHeight * ratio; - this.canvas.style.width = "" + this.preScaledWidth + "px"; - this.canvas.style.height = "" + this.preScaledHeight + "px"; - this.context.scale(ratio, ratio); - this.width = this.originalWidth = this.canvas.width; - return this.height = this.originalHeight = this.canvas.height; - } - }; - - Caman.prototype.hiDPIRatio = function() { - var backingStoreRatio, devicePixelRatio; - devicePixelRatio = window.devicePixelRatio || 1; - backingStoreRatio = this.context.webkitBackingStorePixelRatio || this.context.mozBackingStorePixelRatio || this.context.msBackingStorePixelRatio || this.context.oBackingStorePixelRatio || this.context.backingStorePixelRatio || 1; - return devicePixelRatio / backingStoreRatio; - }; - - Caman.prototype.hiDPICapable = function() { - return (window.devicePixelRatio != null) && window.devicePixelRatio !== 1; - }; - - Caman.prototype.needsHiDPISwap = function() { - if (this.hiDPIDisabled() || !this.hiDPICapable()) { - return false; - } - return this.hiDPIReplacement() !== null; - }; - - Caman.prototype.hiDPIReplacement = function() { - if (this.image == null) { - return null; - } - return this.image.getAttribute('data-caman-hidpi'); - }; - - Caman.prototype.replaceCanvas = function(newCanvas) { - var oldCanvas; - oldCanvas = this.canvas; - this.canvas = newCanvas; - this.context = this.canvas.getContext('2d'); - oldCanvas.parentNode.replaceChild(this.canvas, oldCanvas); - this.width = this.canvas.width; - this.height = this.canvas.height; - this.reloadCanvasData(); - return this.dimensions = { - width: this.canvas.width, - height: this.canvas.height - }; - }; - - Caman.prototype.render = function(callback) { - var _this = this; - if (callback == null) { - callback = function() {}; - } - Event.trigger(this, "renderStart"); - return this.renderer.execute(function() { - _this.context.putImageData(_this.imageData, 0, 0); - return callback.call(_this); - }); - }; - - Caman.prototype.revert = function() { - var i, pixel, _i, _len, _ref; - if (!Caman.allowRevert) { - throw "Revert disabled"; - } - _ref = this.originalVisiblePixels(); - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - this.pixelData[i] = pixel; - } - return this.context.putImageData(this.imageData, 0, 0); - }; - - Caman.prototype.reset = function() { - var canvas, ctx, i, imageData, pixel, pixelData, _i, _len, _ref; - canvas = document.createElement('canvas'); - Util.copyAttributes(this.canvas, canvas); - canvas.width = this.originalWidth; - canvas.height = this.originalHeight; - ctx = canvas.getContext('2d'); - imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - pixelData = imageData.data; - _ref = this.initializedPixelData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - pixelData[i] = pixel; - } - ctx.putImageData(imageData, 0, 0); - this.cropCoordinates = { - x: 0, - y: 0 - }; - this.resized = false; - return this.replaceCanvas(canvas); - }; - - Caman.prototype.originalVisiblePixels = function() { - var canvas, coord, ctx, endX, endY, i, imageData, pixel, pixelData, pixels, scaledCanvas, startX, startY, width, _i, _j, _len, _ref, _ref1, _ref2, _ref3; - if (!Caman.allowRevert) { - throw "Revert disabled"; - } - pixels = []; - startX = this.cropCoordinates.x; - endX = startX + this.width; - startY = this.cropCoordinates.y; - endY = startY + this.height; - if (this.resized) { - canvas = document.createElement('canvas'); - canvas.width = this.originalWidth; - canvas.height = this.originalHeight; - ctx = canvas.getContext('2d'); - imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - pixelData = imageData.data; - _ref = this.originalPixelData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - pixelData[i] = pixel; - } - ctx.putImageData(imageData, 0, 0); - scaledCanvas = document.createElement('canvas'); - scaledCanvas.width = this.width; - scaledCanvas.height = this.height; - ctx = scaledCanvas.getContext('2d'); - ctx.drawImage(canvas, 0, 0, this.originalWidth, this.originalHeight, 0, 0, this.width, this.height); - pixelData = ctx.getImageData(0, 0, this.width, this.height).data; - width = this.width; - } else { - pixelData = this.originalPixelData; - width = this.originalWidth; - } - for (i = _j = 0, _ref1 = pixelData.length; _j < _ref1; i = _j += 4) { - coord = PixelInfo.locationToCoordinates(i, width); - if (((startX <= (_ref2 = coord.x) && _ref2 < endX)) && ((startY <= (_ref3 = coord.y) && _ref3 < endY))) { - pixels.push(pixelData[i], pixelData[i + 1], pixelData[i + 2], pixelData[i + 3]); - } - } - return pixels; - }; - - Caman.prototype.process = function(name, processFn) { - this.renderer.add({ - type: Filter.Type.Single, - name: name, - processFn: processFn - }); - return this; - }; - - Caman.prototype.processKernel = function(name, adjust, divisor, bias) { - var i, _i, _ref; - if (!divisor) { - divisor = 0; - for (i = _i = 0, _ref = adjust.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - divisor += adjust[i]; - } - } - this.renderer.add({ - type: Filter.Type.Kernel, - name: name, - adjust: adjust, - divisor: divisor, - bias: bias || 0 - }); - return this; - }; - - Caman.prototype.processPlugin = function(plugin, args) { - this.renderer.add({ - type: Filter.Type.Plugin, - plugin: plugin, - args: args - }); - return this; - }; - - Caman.prototype.newLayer = function(callback) { - var layer; - layer = new Layer(this); - this.canvasQueue.push(layer); - this.renderer.add({ - type: Filter.Type.LayerDequeue - }); - callback.call(layer); - this.renderer.add({ - type: Filter.Type.LayerFinished - }); - return this; - }; - - Caman.prototype.executeLayer = function(layer) { - return this.pushContext(layer); - }; - - Caman.prototype.pushContext = function(layer) { - this.layerStack.push(this.currentLayer); - this.pixelStack.push(this.pixelData); - this.currentLayer = layer; - return this.pixelData = layer.pixelData; - }; - - Caman.prototype.popContext = function() { - this.pixelData = this.pixelStack.pop(); - return this.currentLayer = this.layerStack.pop(); - }; - - Caman.prototype.applyCurrentLayer = function() { - return this.currentLayer.applyToParent(); - }; - - return Caman; - - })(); - - Analyze = (function() { - function Analyze(c) { - this.c = c; - } - - Analyze.prototype.calculateLevels = function() { - var i, levels, numPixels, _i, _j, _k, _ref; - levels = { - r: {}, - g: {}, - b: {} - }; - for (i = _i = 0; _i <= 255; i = ++_i) { - levels.r[i] = 0; - levels.g[i] = 0; - levels.b[i] = 0; - } - for (i = _j = 0, _ref = this.c.pixelData.length; _j < _ref; i = _j += 4) { - levels.r[this.c.pixelData[i]]++; - levels.g[this.c.pixelData[i + 1]]++; - levels.b[this.c.pixelData[i + 2]]++; - } - numPixels = this.c.pixelData.length / 4; - for (i = _k = 0; _k <= 255; i = ++_k) { - levels.r[i] /= numPixels; - levels.g[i] /= numPixels; - levels.b[i] /= numPixels; - } - return levels; - }; - - return Analyze; - - })(); - - Caman.DOMUpdated = function() { - var img, imgs, parser, _i, _len, _results; - imgs = document.querySelectorAll("img[data-caman]"); - if (!(imgs.length > 0)) { - return; - } - _results = []; - for (_i = 0, _len = imgs.length; _i < _len; _i++) { - img = imgs[_i]; - _results.push(parser = new CamanParser(img, function() { - this.parse(); - return this.execute(); - })); - } - return _results; - }; - - if (Caman.autoload) { - (function() { - if (document.readyState === "complete") { - return Caman.DOMUpdated(); - } else { - return document.addEventListener("DOMContentLoaded", Caman.DOMUpdated, false); - } - })(); - } - - CamanParser = (function() { - var INST_REGEX; - - INST_REGEX = "(\\w+)\\((.*?)\\)"; - - function CamanParser(ele, ready) { - this.dataStr = ele.getAttribute('data-caman'); - this.caman = Caman(ele, ready.bind(this)); - } - - CamanParser.prototype.parse = function() { - var args, e, filter, func, inst, instFunc, m, r, unparsedInstructions, _i, _len, _ref, _results; - this.ele = this.caman.canvas; - r = new RegExp(INST_REGEX, 'g'); - unparsedInstructions = this.dataStr.match(r); - if (!(unparsedInstructions.length > 0)) { - return; - } - r = new RegExp(INST_REGEX); - _results = []; - for (_i = 0, _len = unparsedInstructions.length; _i < _len; _i++) { - inst = unparsedInstructions[_i]; - _ref = inst.match(r), m = _ref[0], filter = _ref[1], args = _ref[2]; - instFunc = new Function("return function() { this." + filter + "(" + args + "); };"); - try { - func = instFunc(); - _results.push(func.call(this.caman)); - } catch (_error) { - e = _error; - _results.push(Log.debug(e)); - } - } - return _results; - }; - - CamanParser.prototype.execute = function() { - var ele; - ele = this.ele; - return this.caman.render(function() { - return ele.parentNode.replaceChild(this.toImage(), ele); - }); - }; - - return CamanParser; - - })(); - - Caman.Blender = Blender = (function() { - function Blender() {} - - Blender.blenders = {}; - - Blender.register = function(name, func) { - return this.blenders[name] = func; - }; - - Blender.execute = function(name, rgbaLayer, rgbaParent) { - return this.blenders[name](rgbaLayer, rgbaParent); - }; - - return Blender; - - })(); - - Caman.Calculate = Calculate = (function() { - function Calculate() {} - - Calculate.distance = function(x1, y1, x2, y2) { - return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); - }; - - Calculate.randomRange = function(min, max, getFloat) { - var rand; - if (getFloat == null) { - getFloat = false; - } - rand = min + (Math.random() * (max - min)); - if (getFloat) { - return rand.toFixed(getFloat); - } else { - return Math.round(rand); - } - }; - - Calculate.luminance = function(rgba) { - return (0.299 * rgba.r) + (0.587 * rgba.g) + (0.114 * rgba.b); - }; - - Calculate.bezier = function(start, ctrl1, ctrl2, end, lowBound, highBound) { - var Ax, Ay, Bx, By, Cx, Cy, bezier, curveX, curveY, i, j, leftCoord, rightCoord, t, x0, x1, x2, x3, y0, y1, y2, y3, _i, _j, _k, _ref, _ref1; - x0 = start[0]; - y0 = start[1]; - x1 = ctrl1[0]; - y1 = ctrl1[1]; - x2 = ctrl2[0]; - y2 = ctrl2[1]; - x3 = end[0]; - y3 = end[1]; - bezier = {}; - Cx = parseInt(3 * (x1 - x0), 10); - Bx = 3 * (x2 - x1) - Cx; - Ax = x3 - x0 - Cx - Bx; - Cy = 3 * (y1 - y0); - By = 3 * (y2 - y1) - Cy; - Ay = y3 - y0 - Cy - By; - for (i = _i = 0; _i < 1000; i = ++_i) { - t = i / 1000; - curveX = Math.round((Ax * Math.pow(t, 3)) + (Bx * Math.pow(t, 2)) + (Cx * t) + x0); - curveY = Math.round((Ay * Math.pow(t, 3)) + (By * Math.pow(t, 2)) + (Cy * t) + y0); - if (lowBound && curveY < lowBound) { - curveY = lowBound; - } else if (highBound && curveY > highBound) { - curveY = highBound; - } - bezier[curveX] = curveY; - } - if (bezier.length < end[0] + 1) { - for (i = _j = 0, _ref = end[0]; 0 <= _ref ? _j <= _ref : _j >= _ref; i = 0 <= _ref ? ++_j : --_j) { - if (bezier[i] == null) { - leftCoord = [i - 1, bezier[i - 1]]; - for (j = _k = i, _ref1 = end[0]; i <= _ref1 ? _k <= _ref1 : _k >= _ref1; j = i <= _ref1 ? ++_k : --_k) { - if (bezier[j] != null) { - rightCoord = [j, bezier[j]]; - break; - } - } - bezier[i] = leftCoord[1] + ((rightCoord[1] - leftCoord[1]) / (rightCoord[0] - leftCoord[0])) * (i - leftCoord[0]); - } - } - } - if (bezier[end[0]] == null) { - bezier[end[0]] = bezier[end[0] - 1]; - } - return bezier; - }; - - return Calculate; - - })(); - - Convert = (function() { - function Convert() {} - - Convert.hexToRGB = function(hex) { - var b, g, r; - if (hex.charAt(0) === "#") { - hex = hex.substr(1); - } - r = parseInt(hex.substr(0, 2), 16); - g = parseInt(hex.substr(2, 2), 16); - b = parseInt(hex.substr(4, 2), 16); - return { - r: r, - g: g, - b: b - }; - }; - - Convert.rgbToHSL = function(r, g, b) { - var d, h, l, max, min, s; - if (typeof r === "object") { - g = r.g; - b = r.b; - r = r.r; - } - r /= 255; - g /= 255; - b /= 255; - max = Math.max(r, g, b); - min = Math.min(r, g, b); - l = (max + min) / 2; - if (max === min) { - h = s = 0; - } else { - d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - h = (function() { - switch (max) { - case r: - return (g - b) / d + (g < b ? 6 : 0); - case g: - return (b - r) / d + 2; - case b: - return (r - g) / d + 4; - } - })(); - h /= 6; - } - return { - h: h, - s: s, - l: l - }; - }; - - Convert.hslToRGB = function(h, s, l) { - var b, g, p, q, r; - if (typeof h === "object") { - s = h.s; - l = h.l; - h = h.h; - } - if (s === 0) { - r = g = b = l; - } else { - q = l < 0.5 ? l * (1 + s) : l + s - l * s; - p = 2 * l - q; - r = this.hueToRGB(p, q, h + 1 / 3); - g = this.hueToRGB(p, q, h); - b = this.hueToRGB(p, q, h - 1 / 3); - } - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - }; - - Convert.hueToRGB = function(p, q, t) { - if (t < 0) { - t += 1; - } - if (t > 1) { - t -= 1; - } - if (t < 1 / 6) { - return p + (q - p) * 6 * t; - } - if (t < 1 / 2) { - return q; - } - if (t < 2 / 3) { - return p + (q - p) * (2 / 3 - t) * 6; - } - return p; - }; - - Convert.rgbToHSV = function(r, g, b) { - var d, h, max, min, s, v; - r /= 255; - g /= 255; - b /= 255; - max = Math.max(r, g, b); - min = Math.min(r, g, b); - v = max; - d = max - min; - s = max === 0 ? 0 : d / max; - if (max === min) { - h = 0; - } else { - h = (function() { - switch (max) { - case r: - return (g - b) / d + (g < b ? 6 : 0); - case g: - return (b - r) / d + 2; - case b: - return (r - g) / d + 4; - } - })(); - h /= 6; - } - return { - h: h, - s: s, - v: v - }; - }; - - Convert.hsvToRGB = function(h, s, v) { - var b, f, g, i, p, q, r, t; - i = Math.floor(h * 6); - f = h * 6 - i; - p = v * (1 - s); - q = v * (1 - f * s); - t = v * (1 - (1 - f) * s); - switch (i % 6) { - case 0: - r = v; - g = t; - b = p; - break; - case 1: - r = q; - g = v; - b = p; - break; - case 2: - r = p; - g = v; - b = t; - break; - case 3: - r = p; - g = q; - b = v; - break; - case 4: - r = t; - g = p; - b = v; - break; - case 5: - r = v; - g = p; - b = q; - } - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - }; - - Convert.rgbToXYZ = function(r, g, b) { - var x, y, z; - r /= 255; - g /= 255; - b /= 255; - if (r > 0.04045) { - r = Math.pow((r + 0.055) / 1.055, 2.4); - } else { - r /= 12.92; - } - if (g > 0.04045) { - g = Math.pow((g + 0.055) / 1.055, 2.4); - } else { - g /= 12.92; - } - if (b > 0.04045) { - b = Math.pow((b + 0.055) / 1.055, 2.4); - } else { - b /= 12.92; - } - x = r * 0.4124 + g * 0.3576 + b * 0.1805; - y = r * 0.2126 + g * 0.7152 + b * 0.0722; - z = r * 0.0193 + g * 0.1192 + b * 0.9505; - return { - x: x * 100, - y: y * 100, - z: z * 100 - }; - }; - - Convert.xyzToRGB = function(x, y, z) { - var b, g, r; - x /= 100; - y /= 100; - z /= 100; - r = (3.2406 * x) + (-1.5372 * y) + (-0.4986 * z); - g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z); - b = (0.0557 * x) + (-0.2040 * y) + (1.0570 * z); - if (r > 0.0031308) { - r = (1.055 * Math.pow(r, 0.4166666667)) - 0.055; - } else { - r *= 12.92; - } - if (g > 0.0031308) { - g = (1.055 * Math.pow(g, 0.4166666667)) - 0.055; - } else { - g *= 12.92; - } - if (b > 0.0031308) { - b = (1.055 * Math.pow(b, 0.4166666667)) - 0.055; - } else { - b *= 12.92; - } - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - }; - - Convert.xyzToLab = function(x, y, z) { - var a, b, l, whiteX, whiteY, whiteZ; - if (typeof x === "object") { - y = x.y; - z = x.z; - x = x.x; - } - whiteX = 95.047; - whiteY = 100.0; - whiteZ = 108.883; - x /= whiteX; - y /= whiteY; - z /= whiteZ; - if (x > 0.008856451679) { - x = Math.pow(x, 0.3333333333); - } else { - x = (7.787037037 * x) + 0.1379310345; - } - if (y > 0.008856451679) { - y = Math.pow(y, 0.3333333333); - } else { - y = (7.787037037 * y) + 0.1379310345; - } - if (z > 0.008856451679) { - z = Math.pow(z, 0.3333333333); - } else { - z = (7.787037037 * z) + 0.1379310345; - } - l = 116 * y - 16; - a = 500 * (x - y); - b = 200 * (y - z); - return { - l: l, - a: a, - b: b - }; - }; - - Convert.labToXYZ = function(l, a, b) { - var x, y, z; - if (typeof l === "object") { - a = l.a; - b = l.b; - l = l.l; - } - y = (l + 16) / 116; - x = y + (a / 500); - z = y - (b / 200); - if (x > 0.2068965517) { - x = x * x * x; - } else { - x = 0.1284185493 * (x - 0.1379310345); - } - if (y > 0.2068965517) { - y = y * y * y; - } else { - y = 0.1284185493 * (y - 0.1379310345); - } - if (z > 0.2068965517) { - z = z * z * z; - } else { - z = 0.1284185493 * (z - 0.1379310345); - } - return { - x: x * 95.047, - y: y * 100.0, - z: z * 108.883 - }; - }; - - Convert.rgbToLab = function(r, g, b) { - var xyz; - if (typeof r === "object") { - g = r.g; - b = r.b; - r = r.r; - } - xyz = this.rgbToXYZ(r, g, b); - return this.xyzToLab(xyz); - }; - - Convert.labToRGB = function(l, a, b) {}; - - return Convert; - - })(); - - Event = (function() { - function Event() {} - - Event.events = {}; - - Event.types = ["processStart", "processComplete", "renderStart", "renderFinished", "blockStarted", "blockFinished"]; - - Event.trigger = function(target, type, data) { - var event, _i, _len, _ref, _results; - if (this.events[type] && this.events[type].length) { - _ref = this.events[type]; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - event = _ref[_i]; - if (event.target === null || target.id === event.target.id) { - _results.push(event.fn.call(target, data)); - } else { - _results.push(void 0); - } - } - return _results; - } - }; - - Event.listen = function(target, type, fn) { - var _fn, _type; - if (typeof target === "string") { - _type = target; - _fn = type; - target = null; - type = _type; - fn = _fn; - } - if (__indexOf.call(this.types, type) < 0) { - return false; - } - if (!this.events[type]) { - this.events[type] = []; - } - this.events[type].push({ - target: target, - fn: fn - }); - return true; - }; - - return Event; - - })(); - - Caman.Event = Event; - - Caman.Filter = Filter = (function() { - function Filter() {} - - Filter.Type = { - Single: 1, - Kernel: 2, - LayerDequeue: 3, - LayerFinished: 4, - LoadOverlay: 5, - Plugin: 6 - }; - - Filter.register = function(name, filterFunc) { - return Caman.prototype[name] = filterFunc; - }; - - return Filter; - - })(); - - Caman.IO = IO = (function() { - function IO() {} - - IO.domainRegex = /(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/; - - IO.isRemote = function(img) { - if (img == null) { - return false; - } - if (this.corsEnabled(img)) { - return false; - } - return this.isURLRemote(img.src); - }; - - IO.corsEnabled = function(img) { - var _ref; - return (img.crossOrigin != null) && ((_ref = img.crossOrigin.toLowerCase()) === 'anonymous' || _ref === 'use-credentials'); - }; - - IO.isURLRemote = function(url) { - var matches; - matches = url.match(this.domainRegex); - if (matches) { - return matches[1] !== document.domain; - } else { - return false; - } - }; - - IO.remoteCheck = function(src) { - if (this.isURLRemote(src)) { - if (!Caman.remoteProxy.length) { - Log.info("Attempting to load a remote image without a configured proxy. URL: " + src); - } else { - if (Caman.isURLRemote(Caman.remoteProxy)) { - Log.info("Cannot use a remote proxy for loading images."); - return; - } - return "" + Caman.remoteProxy + "?camanProxyUrl=" + (encodeURIComponent(src)); - } - } - }; - - IO.proxyUrl = function(src) { - return "" + Caman.remoteProxy + "?" + Caman.proxyParam + "=" + (encodeURIComponent(src)); - }; - - IO.useProxy = function(lang) { - var langToExt; - langToExt = { - ruby: 'rb', - python: 'py', - perl: 'pl', - javascript: 'js' - }; - lang = lang.toLowerCase(); - if (langToExt[lang] != null) { - lang = langToExt[lang]; - } - return "proxies/caman_proxy." + lang; - }; - - return IO; - - })(); - - Caman.prototype.save = function() { - if (typeof exports !== "undefined" && exports !== null) { - return this.nodeSave.apply(this, arguments); - } else { - return this.browserSave.apply(this, arguments); - } - }; - - Caman.prototype.browserSave = function(type) { - var image; - if (type == null) { - type = "png"; - } - type = type.toLowerCase(); - image = this.toBase64(type).replace("image/" + type, "image/octet-stream"); - return document.location.href = image; - }; - - Caman.prototype.nodeSave = function(file, overwrite) { - var e, stats; - if (overwrite == null) { - overwrite = true; - } - try { - stats = fs.statSync(file); - if (stats.isFile() && !overwrite) { - return false; - } - } catch (_error) { - e = _error; - Log.debug("Creating output file " + file); - } - return fs.writeFile(file, this.canvas.toBuffer(), function() { - return Log.debug("Finished writing to " + file); - }); - }; - - Caman.prototype.toImage = function(type) { - var img; - img = document.createElement('img'); - img.src = this.toBase64(type); - img.width = this.dimensions.width; - img.height = this.dimensions.height; - if (window.devicePixelRatio) { - img.width /= window.devicePixelRatio; - img.height /= window.devicePixelRatio; - } - return img; - }; - - Caman.prototype.toBase64 = function(type) { - if (type == null) { - type = "png"; - } - type = type.toLowerCase(); - return this.canvas.toDataURL("image/" + type); - }; - - Layer = (function() { - function Layer(c) { - this.c = c; - this.filter = this.c; - this.options = { - blendingMode: 'normal', - opacity: 1.0 - }; - this.layerID = Util.uniqid.get(); - this.canvas = typeof exports !== "undefined" && exports !== null ? new Canvas() : document.createElement('canvas'); - this.canvas.width = this.c.dimensions.width; - this.canvas.height = this.c.dimensions.height; - this.context = this.canvas.getContext('2d'); - this.context.createImageData(this.canvas.width, this.canvas.height); - this.imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); - this.pixelData = this.imageData.data; - } - - Layer.prototype.newLayer = function(cb) { - return this.c.newLayer.call(this.c, cb); - }; - - Layer.prototype.setBlendingMode = function(mode) { - this.options.blendingMode = mode; - return this; - }; - - Layer.prototype.opacity = function(opacity) { - this.options.opacity = opacity / 100; - return this; - }; - - Layer.prototype.copyParent = function() { - var i, parentData, _i, _ref; - parentData = this.c.pixelData; - for (i = _i = 0, _ref = this.c.pixelData.length; _i < _ref; i = _i += 4) { - this.pixelData[i] = parentData[i]; - this.pixelData[i + 1] = parentData[i + 1]; - this.pixelData[i + 2] = parentData[i + 2]; - this.pixelData[i + 3] = parentData[i + 3]; - } - return this; - }; - - Layer.prototype.fillColor = function() { - return this.c.fillColor.apply(this.c, arguments); - }; - - Layer.prototype.overlayImage = function(image) { - if (typeof image === "object") { - image = image.src; - } else if (typeof image === "string" && image[0] === "#") { - image = $(image).src; - } - if (!image) { - return this; - } - this.c.renderer.renderQueue.push({ - type: Filter.Type.LoadOverlay, - src: image, - layer: this - }); - return this; - }; - - Layer.prototype.applyToParent = function() { - var i, layerData, parentData, result, rgbaLayer, rgbaParent, _i, _ref, _results; - parentData = this.c.pixelStack[this.c.pixelStack.length - 1]; - layerData = this.c.pixelData; - _results = []; - for (i = _i = 0, _ref = layerData.length; _i < _ref; i = _i += 4) { - rgbaParent = { - r: parentData[i], - g: parentData[i + 1], - b: parentData[i + 2], - a: parentData[i + 3] - }; - rgbaLayer = { - r: layerData[i], - g: layerData[i + 1], - b: layerData[i + 2], - a: layerData[i + 3] - }; - result = Blender.execute(this.options.blendingMode, rgbaLayer, rgbaParent); - result.r = Util.clampRGB(result.r); - result.g = Util.clampRGB(result.g); - result.b = Util.clampRGB(result.b); - if (result.a == null) { - result.a = rgbaLayer.a; - } - parentData[i] = rgbaParent.r - ((rgbaParent.r - result.r) * (this.options.opacity * (result.a / 255))); - parentData[i + 1] = rgbaParent.g - ((rgbaParent.g - result.g) * (this.options.opacity * (result.a / 255))); - _results.push(parentData[i + 2] = rgbaParent.b - ((rgbaParent.b - result.b) * (this.options.opacity * (result.a / 255)))); - } - return _results; - }; - - return Layer; - - })(); - - Logger = (function() { - function Logger() { - var name, _i, _len, _ref; - _ref = ['log', 'info', 'warn', 'error']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - this[name] = (function(name) { - return function() { - var args, e; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - if (!Caman.DEBUG) { - return; - } - try { - return console[name].apply(console, args); - } catch (_error) { - e = _error; - return console[name](args); - } - }; - })(name); - } - this.debug = this.log; - } - - return Logger; - - })(); - - Log = new Logger(); - - PixelInfo = (function() { - PixelInfo.coordinatesToLocation = function(x, y, width) { - return (y * width + x) * 4; - }; - - PixelInfo.locationToCoordinates = function(loc, width) { - var x, y; - y = Math.floor(loc / (width * 4)); - x = (loc % (width * 4)) / 4; - return { - x: x, - y: y - }; - }; - - function PixelInfo(c) { - this.c = c; - this.loc = 0; - } - - PixelInfo.prototype.locationXY = function() { - var x, y; - y = this.c.dimensions.height - Math.floor(this.loc / (this.c.dimensions.width * 4)); - x = (this.loc % (this.c.dimensions.width * 4)) / 4; - return { - x: x, - y: y - }; - }; - - PixelInfo.prototype.getPixelRelative = function(horiz, vert) { - var newLoc; - newLoc = this.loc + (this.c.dimensions.width * 4 * (vert * -1)) + (4 * horiz); - if (newLoc > this.c.pixelData.length || newLoc < 0) { - return { - r: 0, - g: 0, - b: 0, - a: 0 - }; - } - return { - r: this.c.pixelData[newLoc], - g: this.c.pixelData[newLoc + 1], - b: this.c.pixelData[newLoc + 2], - a: this.c.pixelData[newLoc + 3] - }; - }; - - PixelInfo.prototype.putPixelRelative = function(horiz, vert, rgba) { - var nowLoc; - nowLoc = this.loc + (this.c.dimensions.width * 4 * (vert * -1)) + (4 * horiz); - if (newLoc > this.c.pixelData.length || newLoc < 0) { - return; - } - this.c.pixelData[newLoc] = rgba.r; - this.c.pixelData[newLoc + 1] = rgba.g; - this.c.pixelData[newLoc + 2] = rgba.b; - this.c.pixelData[newLoc + 3] = rgba.a; - return true; - }; - - PixelInfo.prototype.getPixel = function(x, y) { - var loc; - loc = this.coordinatesToLocation(x, y, this.width); - return { - r: this.c.pixelData[loc], - g: this.c.pixelData[loc + 1], - b: this.c.pixelData[loc + 2], - a: this.c.pixelData[loc + 3] - }; - }; - - PixelInfo.prototype.putPixel = function(x, y, rgba) { - var loc; - loc = this.coordinatesToLocation(x, y, this.width); - this.c.pixelData[loc] = rgba.r; - this.c.pixelData[loc + 1] = rgba.g; - this.c.pixelData[loc + 2] = rgba.b; - return this.c.pixelData[loc + 3] = rgba.a; - }; - - return PixelInfo; - - })(); - - Plugin = (function() { - function Plugin() {} - - Plugin.plugins = {}; - - Plugin.register = function(name, plugin) { - return this.plugins[name] = plugin; - }; - - Plugin.execute = function(context, name, args) { - return this.plugins[name].apply(context, args); - }; - - return Plugin; - - })(); - - Caman.Plugin = Plugin; - - Caman.Renderer = Renderer = (function() { - Renderer.Blocks = Caman.NodeJS ? require('os').cpus().length : 4; - - function Renderer(c) { - this.c = c; - this.processNext = __bind(this.processNext, this); - this.renderQueue = []; - this.modPixelData = null; - } - - Renderer.prototype.add = function(job) { - if (job == null) { - return; - } - return this.renderQueue.push(job); - }; - - Renderer.prototype.processNext = function() { - var layer; - if (this.renderQueue.length === 0) { - Event.trigger(this, "renderFinished"); - if (this.finishedFn != null) { - this.finishedFn.call(this.c); - } - return this; - } - this.currentJob = this.renderQueue.shift(); - switch (this.currentJob.type) { - case Filter.Type.LayerDequeue: - layer = this.c.canvasQueue.shift(); - this.c.executeLayer(layer); - return this.processNext(); - case Filter.Type.LayerFinished: - this.c.applyCurrentLayer(); - this.c.popContext(); - return this.processNext(); - case Filter.Type.LoadOverlay: - return this.loadOverlay(this.currentJob.layer, this.currentJob.src); - case Filter.Type.Plugin: - return this.executePlugin(); - default: - return this.executeFilter(); - } - }; - - Renderer.prototype.execute = function(callback) { - this.finishedFn = callback; - this.modPixelData = Util.dataArray(this.c.pixelData.length); - return this.processNext(); - }; - - Renderer.prototype.eachBlock = function(fn) { - var blockN, blockPixelLength, bnum, end, f, i, lastBlockN, n, start, _i, _ref, _results, - _this = this; - this.blocksDone = 0; - n = this.c.pixelData.length; - blockPixelLength = Math.floor((n / 4) / Renderer.Blocks); - blockN = blockPixelLength * 4; - lastBlockN = blockN + ((n / 4) % Renderer.Blocks) * 4; - _results = []; - for (i = _i = 0, _ref = Renderer.Blocks; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - start = i * blockN; - end = start + (i === Renderer.Blocks - 1 ? lastBlockN : blockN); - if (Caman.NodeJS) { - f = Fiber(function() { - return fn.call(_this, i, start, end); - }); - bnum = f.run(); - _results.push(this.blockFinished(bnum)); - } else { - _results.push(setTimeout((function(i, start, end) { - return function() { - return fn.call(_this, i, start, end); - }; - })(i, start, end), 0)); - } - } - return _results; - }; - - Renderer.prototype.executeFilter = function() { - Event.trigger(this.c, "processStart", this.currentJob); - if (this.currentJob.type === Filter.Type.Single) { - return this.eachBlock(this.renderBlock); - } else { - return this.eachBlock(this.renderKernel); - } - }; - - Renderer.prototype.executePlugin = function() { - Log.debug("Executing plugin " + this.currentJob.plugin); - Plugin.execute(this.c, this.currentJob.plugin, this.currentJob.args); - Log.debug("Plugin " + this.currentJob.plugin + " finished!"); - return this.processNext(); - }; - - Renderer.prototype.renderBlock = function(bnum, start, end) { - var data, i, pixelInfo, res, _i; - Log.debug("Block #" + bnum + " - Filter: " + this.currentJob.name + ", Start: " + start + ", End: " + end); - Event.trigger(this.c, "blockStarted", { - blockNum: bnum, - totalBlocks: Renderer.Blocks, - startPixel: start, - endPixel: end - }); - data = { - r: 0, - g: 0, - b: 0, - a: 0 - }; - pixelInfo = new PixelInfo(this.c); - for (i = _i = start; _i < end; i = _i += 4) { - pixelInfo.loc = i; - data.r = this.c.pixelData[i]; - data.g = this.c.pixelData[i + 1]; - data.b = this.c.pixelData[i + 2]; - data.a = this.c.pixelData[i + 3]; - res = this.currentJob.processFn.call(pixelInfo, data); - if (res.a == null) { - res.a = data.a; - } - this.c.pixelData[i] = Util.clampRGB(res.r); - this.c.pixelData[i + 1] = Util.clampRGB(res.g); - this.c.pixelData[i + 2] = Util.clampRGB(res.b); - this.c.pixelData[i + 3] = Util.clampRGB(res.a); - } - if (Caman.NodeJS) { - return Fiber["yield"](bnum); - } else { - return this.blockFinished(bnum); - } - }; - - Renderer.prototype.renderKernel = function(bnum, start, end) { - var adjust, adjustSize, bias, builder, builderIndex, divisor, i, j, k, kernel, n, name, pixel, pixelInfo, res, _i, _j, _k; - name = this.currentJob.name; - bias = this.currentJob.bias; - divisor = this.currentJob.divisor; - n = this.c.pixelData.length; - adjust = this.currentJob.adjust; - adjustSize = Math.sqrt(adjust.length); - kernel = []; - Log.debug("Rendering kernel - Filter: " + this.currentJob.name); - start = Math.max(start, this.c.dimensions.width * 4 * ((adjustSize - 1) / 2)); - end = Math.min(end, n - (this.c.dimensions.width * 4 * ((adjustSize - 1) / 2))); - builder = (adjustSize - 1) / 2; - pixelInfo = new PixelInfo(this.c); - for (i = _i = start; _i < end; i = _i += 4) { - pixelInfo.loc = i; - builderIndex = 0; - for (j = _j = -builder; -builder <= builder ? _j <= builder : _j >= builder; j = -builder <= builder ? ++_j : --_j) { - for (k = _k = builder; builder <= -builder ? _k <= -builder : _k >= -builder; k = builder <= -builder ? ++_k : --_k) { - pixel = pixelInfo.getPixelRelative(j, k); - kernel[builderIndex * 3] = pixel.r; - kernel[builderIndex * 3 + 1] = pixel.g; - kernel[builderIndex * 3 + 2] = pixel.b; - builderIndex++; - } - } - res = this.processKernel(adjust, kernel, divisor, bias); - this.modPixelData[i] = Util.clampRGB(res.r); - this.modPixelData[i + 1] = Util.clampRGB(res.g); - this.modPixelData[i + 2] = Util.clampRGB(res.b); - this.modPixelData[i + 3] = this.c.pixelData[i + 3]; - } - if (Caman.NodeJS) { - return Fiber["yield"](bnum); - } else { - return this.blockFinished(bnum); - } - }; - - Renderer.prototype.blockFinished = function(bnum) { - var i, _i, _ref; - if (bnum >= 0) { - Log.debug("Block #" + bnum + " finished! Filter: " + this.currentJob.name); - } - this.blocksDone++; - Event.trigger(this.c, "blockFinished", { - blockNum: bnum, - blocksFinished: this.blocksDone, - totalBlocks: Renderer.Blocks - }); - if (this.blocksDone === Renderer.Blocks) { - if (this.currentJob.type === Filter.Type.Kernel) { - for (i = _i = 0, _ref = this.c.pixelData.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - this.c.pixelData[i] = this.modPixelData[i]; - } - } - if (bnum >= 0) { - Log.debug("Filter " + this.currentJob.name + " finished!"); - } - Event.trigger(this.c, "processComplete", this.currentJob); - return this.processNext(); - } - }; - - Renderer.prototype.processKernel = function(adjust, kernel, divisor, bias) { - var i, val, _i, _ref; - val = { - r: 0, - g: 0, - b: 0 - }; - for (i = _i = 0, _ref = adjust.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - val.r += adjust[i] * kernel[i * 3]; - val.g += adjust[i] * kernel[i * 3 + 1]; - val.b += adjust[i] * kernel[i * 3 + 2]; - } - val.r = (val.r / divisor) + bias; - val.g = (val.g / divisor) + bias; - val.b = (val.b / divisor) + bias; - return val; - }; - - Renderer.prototype.loadOverlay = function(layer, src) { - var img, proxyUrl, - _this = this; - img = document.createElement('img'); - img.onload = function() { - layer.context.drawImage(img, 0, 0, _this.c.dimensions.width, _this.c.dimensions.height); - layer.imageData = layer.context.getImageData(0, 0, _this.c.dimensions.width, _this.c.dimensions.height); - layer.pixelData = layer.imageData.data; - _this.c.pixelData = layer.pixelData; - return _this.processNext(); - }; - proxyUrl = IO.remoteCheck(src); - return img.src = proxyUrl != null ? proxyUrl : src; - }; - - return Renderer; - - })(); - - Caman.Store = Store = (function() { - function Store() {} - - Store.items = {}; - - Store.has = function(search) { - return this.items[search] != null; - }; - - Store.get = function(search) { - return this.items[search]; - }; - - Store.put = function(name, obj) { - return this.items[name] = obj; - }; - - Store.execute = function(search, callback) { - var _this = this; - setTimeout(function() { - return callback.call(_this.get(search), _this.get(search)); - }, 0); - return this.get(search); - }; - - Store.flush = function(name) { - if (name == null) { - name = false; - } - if (name) { - return delete this.items[name]; - } else { - return this.items = {}; - } - }; - - return Store; - - })(); - - Blender.register("normal", function(rgbaLayer, rgbaParent) { - return { - r: rgbaLayer.r, - g: rgbaLayer.g, - b: rgbaLayer.b - }; - }); - - Blender.register("multiply", function(rgbaLayer, rgbaParent) { - return { - r: (rgbaLayer.r * rgbaParent.r) / 255, - g: (rgbaLayer.g * rgbaParent.g) / 255, - b: (rgbaLayer.b * rgbaParent.b) / 255 - }; - }); - - Blender.register("screen", function(rgbaLayer, rgbaParent) { - return { - r: 255 - (((255 - rgbaLayer.r) * (255 - rgbaParent.r)) / 255), - g: 255 - (((255 - rgbaLayer.g) * (255 - rgbaParent.g)) / 255), - b: 255 - (((255 - rgbaLayer.b) * (255 - rgbaParent.b)) / 255) - }; - }); - - Blender.register("overlay", function(rgbaLayer, rgbaParent) { - var result; - result = {}; - result.r = rgbaParent.r > 128 ? 255 - 2 * (255 - rgbaLayer.r) * (255 - rgbaParent.r) / 255 : (rgbaParent.r * rgbaLayer.r * 2) / 255; - result.g = rgbaParent.g > 128 ? 255 - 2 * (255 - rgbaLayer.g) * (255 - rgbaParent.g) / 255 : (rgbaParent.g * rgbaLayer.g * 2) / 255; - result.b = rgbaParent.b > 128 ? 255 - 2 * (255 - rgbaLayer.b) * (255 - rgbaParent.b) / 255 : (rgbaParent.b * rgbaLayer.b * 2) / 255; - return result; - }); - - Blender.register("difference", function(rgbaLayer, rgbaParent) { - return { - r: rgbaLayer.r - rgbaParent.r, - g: rgbaLayer.g - rgbaParent.g, - b: rgbaLayer.b - rgbaParent.b - }; - }); - - Blender.register("addition", function(rgbaLayer, rgbaParent) { - return { - r: rgbaParent.r + rgbaLayer.r, - g: rgbaParent.g + rgbaLayer.g, - b: rgbaParent.b + rgbaLayer.b - }; - }); - - Blender.register("exclusion", function(rgbaLayer, rgbaParent) { - return { - r: 128 - 2 * (rgbaParent.r - 128) * (rgbaLayer.r - 128) / 255, - g: 128 - 2 * (rgbaParent.g - 128) * (rgbaLayer.g - 128) / 255, - b: 128 - 2 * (rgbaParent.b - 128) * (rgbaLayer.b - 128) / 255 - }; - }); - - Blender.register("softLight", function(rgbaLayer, rgbaParent) { - var result; - result = {}; - result.r = rgbaParent.r > 128 ? 255 - ((255 - rgbaParent.r) * (255 - (rgbaLayer.r - 128))) / 255 : (rgbaParent.r * (rgbaLayer.r + 128)) / 255; - result.g = rgbaParent.g > 128 ? 255 - ((255 - rgbaParent.g) * (255 - (rgbaLayer.g - 128))) / 255 : (rgbaParent.g * (rgbaLayer.g + 128)) / 255; - result.b = rgbaParent.b > 128 ? 255 - ((255 - rgbaParent.b) * (255 - (rgbaLayer.b - 128))) / 255 : (rgbaParent.b * (rgbaLayer.b + 128)) / 255; - return result; - }); - - Blender.register("lighten", function(rgbaLayer, rgbaParent) { - return { - r: rgbaParent.r > rgbaLayer.r ? rgbaParent.r : rgbaLayer.r, - g: rgbaParent.g > rgbaLayer.g ? rgbaParent.g : rgbaLayer.g, - b: rgbaParent.b > rgbaLayer.b ? rgbaParent.b : rgbaLayer.b - }; - }); - - Blender.register("darken", function(rgbaLayer, rgbaParent) { - return { - r: rgbaParent.r > rgbaLayer.r ? rgbaLayer.r : rgbaParent.r, - g: rgbaParent.g > rgbaLayer.g ? rgbaLayer.g : rgbaParent.g, - b: rgbaParent.b > rgbaLayer.b ? rgbaLayer.b : rgbaParent.b - }; - }); - - Filter.register("fillColor", function() { - var color; - if (arguments.length === 1) { - color = Convert.hexToRGB(arguments[0]); - } else { - color = { - r: arguments[0], - g: arguments[1], - b: arguments[2] - }; - } - return this.process("fillColor", function(rgba) { - rgba.r = color.r; - rgba.g = color.g; - rgba.b = color.b; - rgba.a = 255; - return rgba; - }); - }); - - Filter.register("brightness", function(adjust) { - adjust = Math.floor(255 * (adjust / 100)); - return this.process("brightness", function(rgba) { - rgba.r += adjust; - rgba.g += adjust; - rgba.b += adjust; - return rgba; - }); - }); - - Filter.register("saturation", function(adjust) { - adjust *= -0.01; - return this.process("saturation", function(rgba) { - var max; - max = Math.max(rgba.r, rgba.g, rgba.b); - if (rgba.r !== max) { - rgba.r += (max - rgba.r) * adjust; - } - if (rgba.g !== max) { - rgba.g += (max - rgba.g) * adjust; - } - if (rgba.b !== max) { - rgba.b += (max - rgba.b) * adjust; - } - return rgba; - }); - }); - - Filter.register("vibrance", function(adjust) { - adjust *= -1; - return this.process("vibrance", function(rgba) { - var amt, avg, max; - 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; - if (rgba.r !== max) { - rgba.r += (max - rgba.r) * amt; - } - if (rgba.g !== max) { - rgba.g += (max - rgba.g) * amt; - } - if (rgba.b !== max) { - rgba.b += (max - rgba.b) * amt; - } - return rgba; - }); - }); - - Filter.register("greyscale", function(adjust) { - return this.process("greyscale", function(rgba) { - var avg; - avg = Calculate.luminance(rgba); - rgba.r = avg; - rgba.g = avg; - rgba.b = avg; - return rgba; - }); - }); - - Filter.register("contrast", function(adjust) { - adjust = Math.pow((adjust + 100) / 100, 2); - return this.process("contrast", function(rgba) { - rgba.r /= 255; - rgba.r -= 0.5; - rgba.r *= adjust; - rgba.r += 0.5; - rgba.r *= 255; - rgba.g /= 255; - rgba.g -= 0.5; - rgba.g *= adjust; - rgba.g += 0.5; - rgba.g *= 255; - rgba.b /= 255; - rgba.b -= 0.5; - rgba.b *= adjust; - rgba.b += 0.5; - rgba.b *= 255; - return rgba; - }); - }); - - Filter.register("hue", function(adjust) { - return this.process("hue", function(rgba) { - var h, hsv, rgb; - 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; - rgb = Convert.hsvToRGB(hsv.h, hsv.s, hsv.v); - rgb.a = rgba.a; - return rgb; - }); - }); - - Filter.register("colorize", function() { - var level, rgb; - if (arguments.length === 2) { - rgb = Convert.hexToRGB(arguments[0]); - level = arguments[1]; - } else if (arguments.length === 4) { - rgb = { - r: arguments[0], - g: arguments[1], - b: arguments[2] - }; - level = arguments[3]; - } - return this.process("colorize", function(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); - return rgba; - }); - }); - - Filter.register("invert", function() { - return this.process("invert", function(rgba) { - rgba.r = 255 - rgba.r; - rgba.g = 255 - rgba.g; - rgba.b = 255 - rgba.b; - return rgba; - }); - }); - - Filter.register("sepia", function(adjust) { - if (adjust == null) { - adjust = 100; - } - adjust /= 100; - return this.process("sepia", function(rgba) { - 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)))); - return rgba; - }); - }); - - Filter.register("gamma", function(adjust) { - return this.process("gamma", function(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; - return rgba; - }); - }); - - Filter.register("noise", function(adjust) { - adjust = Math.abs(adjust) * 2.55; - return this.process("noise", function(rgba) { - var rand; - rand = Calculate.randomRange(adjust * -1, adjust); - rgba.r += rand; - rgba.g += rand; - rgba.b += rand; - return rgba; - }); - }); - - Filter.register("clip", function(adjust) { - adjust = Math.abs(adjust) * 2.55; - return this.process("clip", function(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; - } - return rgba; - }); - }); - - Filter.register("channels", function(options) { - var chan, value; - if (typeof options !== "object") { - return this; - } - for (chan in options) { - if (!__hasProp.call(options, chan)) continue; - value = options[chan]; - if (value === 0) { - delete options[chan]; - continue; - } - options[chan] /= 100; - } - if (options.length === 0) { - return this; - } - return this.process("channels", function(rgba) { - if (options.red != null) { - if (options.red > 0) { - rgba.r += (255 - rgba.r) * options.red; - } else { - rgba.r -= rgba.r * Math.abs(options.red); - } - } - if (options.green != null) { - if (options.green > 0) { - rgba.g += (255 - rgba.g) * options.green; - } else { - rgba.g -= rgba.g * Math.abs(options.green); - } - } - if (options.blue != null) { - if (options.blue > 0) { - rgba.b += (255 - rgba.b) * options.blue; - } else { - rgba.b -= rgba.b * Math.abs(options.blue); - } - } - return rgba; - }); - }); - - Filter.register("curves", function() { - var bezier, chans, cps, ctrl1, ctrl2, end, i, start, _i, _j, _ref, _ref1; - chans = arguments[0], cps = 2 <= arguments.length ? __slice.call(arguments, 1) : []; - if (typeof chans === "string") { - chans = chans.split(""); - } - if (chans[0] === "v") { - chans = ['r', 'g', 'b']; - } - if (cps.length < 3 || cps.length > 4) { - throw "Invalid number of arguments to curves filter"; - } - start = cps[0]; - ctrl1 = cps[1]; - ctrl2 = cps.length === 4 ? cps[2] : cps[1]; - end = cps[cps.length - 1]; - bezier = Calculate.bezier(start, ctrl1, ctrl2, end, 0, 255); - if (start[0] > 0) { - for (i = _i = 0, _ref = start[0]; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - bezier[i] = start[1]; - } - } - if (end[0] < 255) { - for (i = _j = _ref1 = end[0]; _ref1 <= 255 ? _j <= 255 : _j >= 255; i = _ref1 <= 255 ? ++_j : --_j) { - bezier[i] = end[1]; - } - } - return this.process("curves", function(rgba) { - var _k, _ref2; - for (i = _k = 0, _ref2 = chans.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; i = 0 <= _ref2 ? ++_k : --_k) { - rgba[chans[i]] = bezier[rgba[chans[i]]]; - } - return rgba; - }); - }); - - Filter.register("exposure", function(adjust) { - var ctrl1, ctrl2, p; - p = Math.abs(adjust) / 100; - ctrl1 = [0, 255 * p]; - ctrl2 = [255 - (255 * p), 255]; - if (adjust < 0) { - ctrl1 = ctrl1.reverse(); - ctrl2 = ctrl2.reverse(); - } - return this.curves('rgb', [0, 0], ctrl1, ctrl2, [255, 255]); - }); - - Caman.Plugin.register("crop", function(width, height, x, y) { - var canvas, ctx; - if (x == null) { - x = 0; - } - if (y == null) { - y = 0; - } - if (typeof exports !== "undefined" && exports !== null) { - canvas = new Canvas(width, height); - } else { - canvas = document.createElement('canvas'); - Util.copyAttributes(this.canvas, canvas); - canvas.width = width; - canvas.height = height; - } - ctx = canvas.getContext('2d'); - ctx.drawImage(this.canvas, x, y, width, height, 0, 0, width, height); - this.cropCoordinates = { - x: x, - y: y - }; - this.cropped = true; - return this.replaceCanvas(canvas); - }); - - Caman.Plugin.register("resize", function(newDims) { - var canvas, ctx; - if (newDims == null) { - newDims = null; - } - if (newDims === null || ((newDims.width == null) && (newDims.height == null))) { - Log.error("Invalid or missing dimensions given for resize"); - return; - } - if (newDims.width == null) { - newDims.width = this.canvas.width * newDims.height / this.canvas.height; - } else if (newDims.height == null) { - newDims.height = this.canvas.height * newDims.width / this.canvas.width; - } - if (typeof exports !== "undefined" && exports !== null) { - canvas = new Canvas(newDims.width, newDims.height); - } else { - canvas = document.createElement('canvas'); - Util.copyAttributes(this.canvas, canvas); - canvas.width = newDims.width; - canvas.height = newDims.height; - } - ctx = canvas.getContext('2d'); - ctx.drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, newDims.width, newDims.height); - this.resized = true; - return this.replaceCanvas(canvas); - }); - - Caman.Filter.register("crop", function() { - return this.processPlugin("crop", Array.prototype.slice.call(arguments, 0)); - }); - - Caman.Filter.register("resize", function() { - return this.processPlugin("resize", Array.prototype.slice.call(arguments, 0)); - }); - - /* - IDMC plugins - */ - - - Caman.Filter.register("idmc_reset_original_pixeldata", function() { - return this.processPlugin("idmc_reset_original_pixeldata", null); - }); - - Caman.Plugin.register("idmc_reset_original_pixeldata", function() { - var i, pixel, _i, _len, _ref; - Log.debug("idmc_reset_original_pixeldata"); - this.originalPixelData = Util.dataArray(this.pixelData.length); - _ref = this.pixelData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - this.originalPixelData[i] = pixel; - } - return this; - }); - - Caman.Filter.register("idmc_set_min_max_pixel_values", function(min_pixel_value, max_pixel_value) { - return this.processPlugin("idmc_set_min_max_pixel_values", [min_pixel_value, max_pixel_value]); - }); - - Caman.Plugin.register("idmc_set_min_max_pixel_values", function(min_pixel_value, max_pixel_value) { - var a, b, b_colormap, g, g_colormap, height, i, idx, index, org_pixels, pixels, r, r_colormap, width, x, y, _i, _j, _k, - _this = this, tmp; - tmp = []; - for (i = _i = 0; _i < 256; i = ++_i) { - tmp.push(0); - } - - org_pixels = this.originalPixelData; - pixels = this.pixelData; - width = this.dimensions.width; - height = this.dimensions.height; - - console.debug('idmc_set_min_max_pixel_values: width: ' + width); - console.debug('idmc_set_min_max_pixel_values: height: ' + height); - if (this.idmc_set_min_max_pixel_values_r_colormap == null) { - this.idmc_set_min_max_pixel_values_r_colormap = (function() { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 256; i = ++_i) { - _results.push(i); - } - return _results; - })(); - } - if (this.idmc_set_min_max_pixel_values_g_colormap == null) { - this.idmc_set_min_max_pixel_values_g_colormap = (function() { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 256; i = ++_i) { - _results.push(i); - } - return _results; - })(); - } - if (this.idmc_set_min_max_pixel_values_b_colormap == null) { - this.idmc_set_min_max_pixel_values_b_colormap = (function() { - var _i, _results; - _results = []; - for (i = _i = 0; _i < 256; i = ++_i) { - _results.push(i); - } - return _results; - })(); - } - r_colormap = this.idmc_set_min_max_pixel_values_r_colormap; - g_colormap = this.idmc_set_min_max_pixel_values_g_colormap; - b_colormap = this.idmc_set_min_max_pixel_values_b_colormap; - - - idx = function(x, y) { - return (y * width + x) * 4; - }; - for (i = _i = 0; _i < 256; i = ++_i) { - index = Math.round((256 * (i - min_pixel_value)) / (max_pixel_value - min_pixel_value)); - index = index < 0 ? 0 : i > 255 ? 255 : index; - r_colormap[i] = i < min_pixel_value ? 0 : i > max_pixel_value ? 255 : index; - g_colormap[i] = i < min_pixel_value ? 0 : i > max_pixel_value ? 255 : index; - b_colormap[i] = i < min_pixel_value ? 0 : i > max_pixel_value ? 255 : index; - } - for (y = _j = 0; 0 <= height ? _j < height : _j > height; y = 0 <= height ? ++_j : --_j) { - for (x = _k = 0; 0 <= width ? _k < width : _k > width; x = 0 <= width ? ++_k : --_k) { - r = org_pixels[idx(x, y)]; - g = org_pixels[idx(x, y) + 1]; - b = org_pixels[idx(x, y) + 2]; - a = org_pixels[idx(x, y) + 3]; - pixels[idx(x, y)] = r_colormap[r]; - pixels[idx(x, y) + 1] = g_colormap[g]; - pixels[idx(x, y) + 2] = b_colormap[b]; - pixels[idx(x, y) + 3] = 255; - //tmp[r] += 1; - } - } - - for (var i=0; i < height * width; i = i + 4) { - tmp[org_pixels[i]] += 1; - tmp[org_pixels[i+1]] += 1; - tmp[org_pixels[i+2]] += 1; - } - - /* - alert('caman heigt: ' + height + - '\ncaman width: ' + width); - */ - //alert(tmp[30]); - return this; - }); - - Caman.Filter.register("idmc_test", function() { - return this.processPlugin("idmc_test", []); - }); - - Caman.Plugin.register("idmc_test", function() { - return this.process("idmc_test", function(rgba) { - Log.debug("IDMC test func"); - return rgba; - }); - }); - - this; - -}).call(this); diff --git a/mig/images/lib/CamanJS-4.1.1/dist/caman.js b/mig/images/lib/CamanJS-4.1.1/dist/caman.js deleted file mode 100644 index b9a578865..000000000 --- a/mig/images/lib/CamanJS-4.1.1/dist/caman.js +++ /dev/null @@ -1,2321 +0,0 @@ -// Generated by CoffeeScript 1.6.3 -(function() { - var $, Analyze, Blender, Calculate, Caman, CamanParser, Canvas, Convert, Event, Fiber, Filter, IO, Image, Layer, Log, Logger, PixelInfo, Plugin, Renderer, Root, Store, Util, fs, slice, - __hasProp = {}.hasOwnProperty, - __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, - __slice = [].slice, - __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - - slice = Array.prototype.slice; - - $ = function(sel, root) { - if (root == null) { - root = document; - } - if (typeof sel === "object" || (typeof exports !== "undefined" && exports !== null)) { - return sel; - } - return root.querySelector(sel); - }; - - Util = (function() { - function Util() {} - - Util.uniqid = (function() { - var id; - id = 0; - return { - get: function() { - return id++; - } - }; - })(); - - Util.extend = function(obj) { - var copy, dest, prop, src, _i, _len; - dest = obj; - src = slice.call(arguments, 1); - for (_i = 0, _len = src.length; _i < _len; _i++) { - copy = src[_i]; - for (prop in copy) { - if (!__hasProp.call(copy, prop)) continue; - dest[prop] = copy[prop]; - } - } - return dest; - }; - - Util.clampRGB = function(val) { - if (val < 0) { - return 0; - } - if (val > 255) { - return 255; - } - return val; - }; - - Util.copyAttributes = function(from, to, opts) { - var attr, _i, _len, _ref, _ref1, _results; - if (opts == null) { - opts = {}; - } - _ref = from.attributes; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - attr = _ref[_i]; - if ((opts.except != null) && (_ref1 = attr.nodeName, __indexOf.call(opts.except, _ref1) >= 0)) { - continue; - } - _results.push(to.setAttribute(attr.nodeName, attr.nodeValue)); - } - return _results; - }; - - Util.dataArray = function(length) { - if (length == null) { - length = 0; - } - if (Caman.NodeJS || (window.Uint8Array != null)) { - return new Uint8Array(length); - } - return new Array(length); - }; - - return Util; - - })(); - - if (typeof exports !== "undefined" && exports !== null) { - Root = exports; - Canvas = require('canvas'); - Image = Canvas.Image; - Fiber = require('fibers'); - fs = require('fs'); - } else { - Root = window; - } - - Root.Caman = Caman = (function() { - Caman.version = { - release: "4.1.1", - date: "4/8/2013" - }; - - Caman.DEBUG = false; - - Caman.NodeJS = typeof exports !== "undefined" && exports !== null; - - Caman.autoload = !Caman.NodeJS; - - Caman.allowRevert = true; - - Caman.crossOrigin = "anonymous"; - - Caman.toString = function() { - return "Version " + Caman.version.release + ", Released " + Caman.version.date; - }; - - Caman.remoteProxy = ""; - - Caman.proxyParam = "camanProxyUrl"; - - Caman.getAttrId = function(canvas) { - if (Caman.NodeJS) { - return true; - } - if (typeof canvas === "string") { - canvas = $(canvas); - } - if (!((canvas != null) && (canvas.getAttribute != null))) { - return null; - } - return canvas.getAttribute('data-caman-id'); - }; - - function Caman() { - var args, callback, id, - _this = this; - if (arguments.length === 0) { - throw "Invalid arguments"; - } - if (this instanceof Caman) { - this.finishInit = this.finishInit.bind(this); - this.imageLoaded = this.imageLoaded.bind(this); - args = arguments[0]; - if (!Caman.NodeJS) { - id = parseInt(Caman.getAttrId(args[0]), 10); - callback = typeof args[1] === "function" ? args[1] : typeof args[2] === "function" ? args[2] : function() {}; - if (!isNaN(id) && Store.has(id)) { - return Store.execute(id, callback); - } - } - this.id = Util.uniqid.get(); - this.initializedPixelData = this.originalPixelData = null; - this.cropCoordinates = { - x: 0, - y: 0 - }; - this.cropped = false; - this.resized = false; - this.pixelStack = []; - this.layerStack = []; - this.canvasQueue = []; - this.currentLayer = null; - this.scaled = false; - this.analyze = new Analyze(this); - this.renderer = new Renderer(this); - this.domIsLoaded(function() { - _this.parseArguments(args); - return _this.setup(); - }); - return this; - } else { - return new Caman(arguments); - } - } - - Caman.prototype.domIsLoaded = function(cb) { - var listener, - _this = this; - if (Caman.NodeJS) { - return setTimeout(function() { - return cb.call(_this); - }, 0); - } else { - if (document.readyState === "complete") { - Log.debug("DOM initialized"); - return setTimeout(function() { - return cb.call(_this); - }, 0); - } else { - listener = function() { - if (document.readyState === "complete") { - Log.debug("DOM initialized"); - return cb.call(_this); - } - }; - return document.addEventListener("readystatechange", listener, false); - } - } - }; - - Caman.prototype.parseArguments = function(args) { - var key, val, _ref, _results; - if (args.length === 0) { - throw "Invalid arguments given"; - } - this.initObj = null; - this.initType = null; - this.imageUrl = null; - this.callback = function() {}; - this.setInitObject(args[0]); - if (args.length === 1) { - return; - } - switch (typeof args[1]) { - case "string": - this.imageUrl = args[1]; - break; - case "function": - this.callback = args[1]; - } - if (args.length === 2) { - return; - } - this.callback = args[2]; - if (args.length === 4) { - _ref = args[4]; - _results = []; - for (key in _ref) { - if (!__hasProp.call(_ref, key)) continue; - val = _ref[key]; - _results.push(this.options[key] = val); - } - return _results; - } - }; - - Caman.prototype.setInitObject = function(obj) { - if (Caman.NodeJS) { - this.initObj = obj; - this.initType = 'node'; - return; - } - if (typeof obj === "object") { - this.initObj = obj; - } else { - this.initObj = $(obj); - } - if (this.initObj == null) { - throw "Could not find image or canvas for initialization."; - } - return this.initType = this.initObj.nodeName.toLowerCase(); - }; - - Caman.prototype.setup = function() { - switch (this.initType) { - case "node": - return this.initNode(); - case "img": - return this.initImage(); - case "canvas": - return this.initCanvas(); - } - }; - - Caman.prototype.initNode = function() { - var _this = this; - Log.debug("Initializing for NodeJS"); - this.image = new Image(); - this.image.onload = function() { - Log.debug("Image loaded. Width = " + (_this.imageWidth()) + ", Height = " + (_this.imageHeight())); - _this.canvas = new Canvas(_this.imageWidth(), _this.imageHeight()); - return _this.finishInit(); - }; - this.image.onerror = function(err) { - throw err; - }; - return this.image.src = this.initObj; - }; - - Caman.prototype.initImage = function() { - this.image = this.initObj; - this.canvas = document.createElement('canvas'); - this.context = this.canvas.getContext('2d'); - Util.copyAttributes(this.image, this.canvas, { - except: ['src'] - }); - this.image.parentNode.replaceChild(this.canvas, this.image); - this.imageAdjustments(); - return this.waitForImageLoaded(); - }; - - Caman.prototype.initCanvas = function() { - this.canvas = this.initObj; - this.context = this.canvas.getContext('2d'); - if (this.imageUrl != null) { - this.image = document.createElement('img'); - this.image.src = this.imageUrl; - this.imageAdjustments(); - return this.waitForImageLoaded(); - } else { - return this.finishInit(); - } - }; - - Caman.prototype.imageAdjustments = function() { - if (this.needsHiDPISwap()) { - Log.debug(this.image.src, "->", this.hiDPIReplacement()); - this.swapped = true; - this.image.src = this.hiDPIReplacement(); - } - if (IO.isRemote(this.image)) { - this.image.src = IO.proxyUrl(this.image.src); - return Log.debug("Remote image detected, using URL = " + this.image.src); - } - }; - - Caman.prototype.waitForImageLoaded = function() { - if (this.isImageLoaded()) { - return this.imageLoaded(); - } else { - return this.image.onload = this.imageLoaded; - } - }; - - Caman.prototype.isImageLoaded = function() { - if (!this.image.complete) { - return false; - } - if ((this.image.naturalWidth != null) && this.image.naturalWidth === 0) { - return false; - } - return true; - }; - - Caman.prototype.imageWidth = function() { - return this.image.width || this.image.naturalWidth; - }; - - Caman.prototype.imageHeight = function() { - return this.image.height || this.image.naturalHeight; - }; - - Caman.prototype.imageLoaded = function() { - Log.debug("Image loaded. Width = " + (this.imageWidth()) + ", Height = " + (this.imageHeight())); - if (this.swapped) { - this.canvas.width = this.imageWidth() / this.hiDPIRatio(); - this.canvas.height = this.imageHeight() / this.hiDPIRatio(); - } else { - this.canvas.width = this.imageWidth(); - this.canvas.height = this.imageHeight(); - } - return this.finishInit(); - }; - - Caman.prototype.finishInit = function() { - var i, pixel, _i, _len, _ref; - if (this.context == null) { - this.context = this.canvas.getContext('2d'); - } - this.originalWidth = this.preScaledWidth = this.width = this.canvas.width; - this.originalHeight = this.preScaledHeight = this.height = this.canvas.height; - this.hiDPIAdjustments(); - if (!this.hasId()) { - this.assignId(); - } - if (this.image != null) { - this.context.drawImage(this.image, 0, 0, this.imageWidth(), this.imageHeight(), 0, 0, this.preScaledWidth, this.preScaledHeight); - } - this.reloadCanvasData(); - if (Caman.allowRevert) { - this.initializedPixelData = Util.dataArray(this.pixelData.length); - this.originalPixelData = Util.dataArray(this.pixelData.length); - _ref = this.pixelData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - this.initializedPixelData[i] = pixel; - this.originalPixelData[i] = pixel; - } - } - this.dimensions = { - width: this.canvas.width, - height: this.canvas.height - }; - Store.put(this.id, this); - this.callback.call(this, this); - return this.callback = function() {}; - }; - - Caman.prototype.reloadCanvasData = function() { - this.imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); - return this.pixelData = this.imageData.data; - }; - - Caman.prototype.resetOriginalPixelData = function() { - var pixel, _i, _len, _ref, _results; - if (!Caman.allowRevert) { - throw "Revert disabled"; - } - this.originalPixelData = Util.dataArray(this.pixelData.length); - _ref = this.pixelData; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - pixel = _ref[_i]; - _results.push(this.originalPixelData.push(pixel)); - } - return _results; - }; - - Caman.prototype.hasId = function() { - return Caman.getAttrId(this.canvas) != null; - }; - - Caman.prototype.assignId = function() { - if (Caman.NodeJS || this.canvas.getAttribute('data-caman-id')) { - return; - } - return this.canvas.setAttribute('data-caman-id', this.id); - }; - - Caman.prototype.hiDPIDisabled = function() { - return this.canvas.getAttribute('data-caman-hidpi-disabled') !== null; - }; - - Caman.prototype.hiDPIAdjustments = function() { - var ratio; - if (Caman.NodeJS || this.hiDPIDisabled()) { - return; - } - ratio = this.hiDPIRatio(); - if (ratio !== 1) { - Log.debug("HiDPI ratio = " + ratio); - this.scaled = true; - this.preScaledWidth = this.canvas.width; - this.preScaledHeight = this.canvas.height; - this.canvas.width = this.preScaledWidth * ratio; - this.canvas.height = this.preScaledHeight * ratio; - this.canvas.style.width = "" + this.preScaledWidth + "px"; - this.canvas.style.height = "" + this.preScaledHeight + "px"; - this.context.scale(ratio, ratio); - this.width = this.originalWidth = this.canvas.width; - return this.height = this.originalHeight = this.canvas.height; - } - }; - - Caman.prototype.hiDPIRatio = function() { - var backingStoreRatio, devicePixelRatio; - devicePixelRatio = window.devicePixelRatio || 1; - backingStoreRatio = this.context.webkitBackingStorePixelRatio || this.context.mozBackingStorePixelRatio || this.context.msBackingStorePixelRatio || this.context.oBackingStorePixelRatio || this.context.backingStorePixelRatio || 1; - return devicePixelRatio / backingStoreRatio; - }; - - Caman.prototype.hiDPICapable = function() { - return (window.devicePixelRatio != null) && window.devicePixelRatio !== 1; - }; - - Caman.prototype.needsHiDPISwap = function() { - if (this.hiDPIDisabled() || !this.hiDPICapable()) { - return false; - } - return this.hiDPIReplacement() !== null; - }; - - Caman.prototype.hiDPIReplacement = function() { - if (this.image == null) { - return null; - } - return this.image.getAttribute('data-caman-hidpi'); - }; - - Caman.prototype.replaceCanvas = function(newCanvas) { - var oldCanvas; - oldCanvas = this.canvas; - this.canvas = newCanvas; - this.context = this.canvas.getContext('2d'); - oldCanvas.parentNode.replaceChild(this.canvas, oldCanvas); - this.width = this.canvas.width; - this.height = this.canvas.height; - this.reloadCanvasData(); - return this.dimensions = { - width: this.canvas.width, - height: this.canvas.height - }; - }; - - Caman.prototype.render = function(callback) { - var _this = this; - if (callback == null) { - callback = function() {}; - } - Event.trigger(this, "renderStart"); - return this.renderer.execute(function() { - _this.context.putImageData(_this.imageData, 0, 0); - return callback.call(_this); - }); - }; - - Caman.prototype.revert = function() { - var i, pixel, _i, _len, _ref; - if (!Caman.allowRevert) { - throw "Revert disabled"; - } - _ref = this.originalVisiblePixels(); - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - this.pixelData[i] = pixel; - } - return this.context.putImageData(this.imageData, 0, 0); - }; - - Caman.prototype.reset = function() { - var canvas, ctx, i, imageData, pixel, pixelData, _i, _len, _ref; - canvas = document.createElement('canvas'); - Util.copyAttributes(this.canvas, canvas); - canvas.width = this.originalWidth; - canvas.height = this.originalHeight; - ctx = canvas.getContext('2d'); - imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - pixelData = imageData.data; - _ref = this.initializedPixelData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - pixelData[i] = pixel; - } - ctx.putImageData(imageData, 0, 0); - this.cropCoordinates = { - x: 0, - y: 0 - }; - this.resized = false; - return this.replaceCanvas(canvas); - }; - - Caman.prototype.originalVisiblePixels = function() { - var canvas, coord, ctx, endX, endY, i, imageData, pixel, pixelData, pixels, scaledCanvas, startX, startY, width, _i, _j, _len, _ref, _ref1, _ref2, _ref3; - if (!Caman.allowRevert) { - throw "Revert disabled"; - } - pixels = []; - startX = this.cropCoordinates.x; - endX = startX + this.width; - startY = this.cropCoordinates.y; - endY = startY + this.height; - if (this.resized) { - canvas = document.createElement('canvas'); - canvas.width = this.originalWidth; - canvas.height = this.originalHeight; - ctx = canvas.getContext('2d'); - imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); - pixelData = imageData.data; - _ref = this.originalPixelData; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - pixel = _ref[i]; - pixelData[i] = pixel; - } - ctx.putImageData(imageData, 0, 0); - scaledCanvas = document.createElement('canvas'); - scaledCanvas.width = this.width; - scaledCanvas.height = this.height; - ctx = scaledCanvas.getContext('2d'); - ctx.drawImage(canvas, 0, 0, this.originalWidth, this.originalHeight, 0, 0, this.width, this.height); - pixelData = ctx.getImageData(0, 0, this.width, this.height).data; - width = this.width; - } else { - pixelData = this.originalPixelData; - width = this.originalWidth; - } - for (i = _j = 0, _ref1 = pixelData.length; _j < _ref1; i = _j += 4) { - coord = PixelInfo.locationToCoordinates(i, width); - if (((startX <= (_ref2 = coord.x) && _ref2 < endX)) && ((startY <= (_ref3 = coord.y) && _ref3 < endY))) { - pixels.push(pixelData[i], pixelData[i + 1], pixelData[i + 2], pixelData[i + 3]); - } - } - return pixels; - }; - - Caman.prototype.process = function(name, processFn) { - this.renderer.add({ - type: Filter.Type.Single, - name: name, - processFn: processFn - }); - return this; - }; - - Caman.prototype.processKernel = function(name, adjust, divisor, bias) { - var i, _i, _ref; - if (!divisor) { - divisor = 0; - for (i = _i = 0, _ref = adjust.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - divisor += adjust[i]; - } - } - this.renderer.add({ - type: Filter.Type.Kernel, - name: name, - adjust: adjust, - divisor: divisor, - bias: bias || 0 - }); - return this; - }; - - Caman.prototype.processPlugin = function(plugin, args) { - this.renderer.add({ - type: Filter.Type.Plugin, - plugin: plugin, - args: args - }); - return this; - }; - - Caman.prototype.newLayer = function(callback) { - var layer; - layer = new Layer(this); - this.canvasQueue.push(layer); - this.renderer.add({ - type: Filter.Type.LayerDequeue - }); - callback.call(layer); - this.renderer.add({ - type: Filter.Type.LayerFinished - }); - return this; - }; - - Caman.prototype.executeLayer = function(layer) { - return this.pushContext(layer); - }; - - Caman.prototype.pushContext = function(layer) { - this.layerStack.push(this.currentLayer); - this.pixelStack.push(this.pixelData); - this.currentLayer = layer; - return this.pixelData = layer.pixelData; - }; - - Caman.prototype.popContext = function() { - this.pixelData = this.pixelStack.pop(); - return this.currentLayer = this.layerStack.pop(); - }; - - Caman.prototype.applyCurrentLayer = function() { - return this.currentLayer.applyToParent(); - }; - - return Caman; - - })(); - - Analyze = (function() { - function Analyze(c) { - this.c = c; - } - - Analyze.prototype.calculateLevels = function() { - var i, levels, numPixels, _i, _j, _k, _ref; - levels = { - r: {}, - g: {}, - b: {} - }; - for (i = _i = 0; _i <= 255; i = ++_i) { - levels.r[i] = 0; - levels.g[i] = 0; - levels.b[i] = 0; - } - for (i = _j = 0, _ref = this.c.pixelData.length; _j < _ref; i = _j += 4) { - levels.r[this.c.pixelData[i]]++; - levels.g[this.c.pixelData[i + 1]]++; - levels.b[this.c.pixelData[i + 2]]++; - } - numPixels = this.c.pixelData.length / 4; - for (i = _k = 0; _k <= 255; i = ++_k) { - levels.r[i] /= numPixels; - levels.g[i] /= numPixels; - levels.b[i] /= numPixels; - } - return levels; - }; - - return Analyze; - - })(); - - Caman.DOMUpdated = function() { - var img, imgs, parser, _i, _len, _results; - imgs = document.querySelectorAll("img[data-caman]"); - if (!(imgs.length > 0)) { - return; - } - _results = []; - for (_i = 0, _len = imgs.length; _i < _len; _i++) { - img = imgs[_i]; - _results.push(parser = new CamanParser(img, function() { - this.parse(); - return this.execute(); - })); - } - return _results; - }; - - if (Caman.autoload) { - (function() { - if (document.readyState === "complete") { - return Caman.DOMUpdated(); - } else { - return document.addEventListener("DOMContentLoaded", Caman.DOMUpdated, false); - } - })(); - } - - CamanParser = (function() { - var INST_REGEX; - - INST_REGEX = "(\\w+)\\((.*?)\\)"; - - function CamanParser(ele, ready) { - this.dataStr = ele.getAttribute('data-caman'); - this.caman = Caman(ele, ready.bind(this)); - } - - CamanParser.prototype.parse = function() { - var args, e, filter, func, inst, instFunc, m, r, unparsedInstructions, _i, _len, _ref, _results; - this.ele = this.caman.canvas; - r = new RegExp(INST_REGEX, 'g'); - unparsedInstructions = this.dataStr.match(r); - if (!(unparsedInstructions.length > 0)) { - return; - } - r = new RegExp(INST_REGEX); - _results = []; - for (_i = 0, _len = unparsedInstructions.length; _i < _len; _i++) { - inst = unparsedInstructions[_i]; - _ref = inst.match(r), m = _ref[0], filter = _ref[1], args = _ref[2]; - instFunc = new Function("return function() { this." + filter + "(" + args + "); };"); - try { - func = instFunc(); - _results.push(func.call(this.caman)); - } catch (_error) { - e = _error; - _results.push(Log.debug(e)); - } - } - return _results; - }; - - CamanParser.prototype.execute = function() { - var ele; - ele = this.ele; - return this.caman.render(function() { - return ele.parentNode.replaceChild(this.toImage(), ele); - }); - }; - - return CamanParser; - - })(); - - Caman.Blender = Blender = (function() { - function Blender() {} - - Blender.blenders = {}; - - Blender.register = function(name, func) { - return this.blenders[name] = func; - }; - - Blender.execute = function(name, rgbaLayer, rgbaParent) { - return this.blenders[name](rgbaLayer, rgbaParent); - }; - - return Blender; - - })(); - - Caman.Calculate = Calculate = (function() { - function Calculate() {} - - Calculate.distance = function(x1, y1, x2, y2) { - return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); - }; - - Calculate.randomRange = function(min, max, getFloat) { - var rand; - if (getFloat == null) { - getFloat = false; - } - rand = min + (Math.random() * (max - min)); - if (getFloat) { - return rand.toFixed(getFloat); - } else { - return Math.round(rand); - } - }; - - Calculate.luminance = function(rgba) { - return (0.299 * rgba.r) + (0.587 * rgba.g) + (0.114 * rgba.b); - }; - - Calculate.bezier = function(start, ctrl1, ctrl2, end, lowBound, highBound) { - var Ax, Ay, Bx, By, Cx, Cy, bezier, curveX, curveY, i, j, leftCoord, rightCoord, t, x0, x1, x2, x3, y0, y1, y2, y3, _i, _j, _k, _ref, _ref1; - x0 = start[0]; - y0 = start[1]; - x1 = ctrl1[0]; - y1 = ctrl1[1]; - x2 = ctrl2[0]; - y2 = ctrl2[1]; - x3 = end[0]; - y3 = end[1]; - bezier = {}; - Cx = parseInt(3 * (x1 - x0), 10); - Bx = 3 * (x2 - x1) - Cx; - Ax = x3 - x0 - Cx - Bx; - Cy = 3 * (y1 - y0); - By = 3 * (y2 - y1) - Cy; - Ay = y3 - y0 - Cy - By; - for (i = _i = 0; _i < 1000; i = ++_i) { - t = i / 1000; - curveX = Math.round((Ax * Math.pow(t, 3)) + (Bx * Math.pow(t, 2)) + (Cx * t) + x0); - curveY = Math.round((Ay * Math.pow(t, 3)) + (By * Math.pow(t, 2)) + (Cy * t) + y0); - if (lowBound && curveY < lowBound) { - curveY = lowBound; - } else if (highBound && curveY > highBound) { - curveY = highBound; - } - bezier[curveX] = curveY; - } - if (bezier.length < end[0] + 1) { - for (i = _j = 0, _ref = end[0]; 0 <= _ref ? _j <= _ref : _j >= _ref; i = 0 <= _ref ? ++_j : --_j) { - if (bezier[i] == null) { - leftCoord = [i - 1, bezier[i - 1]]; - for (j = _k = i, _ref1 = end[0]; i <= _ref1 ? _k <= _ref1 : _k >= _ref1; j = i <= _ref1 ? ++_k : --_k) { - if (bezier[j] != null) { - rightCoord = [j, bezier[j]]; - break; - } - } - bezier[i] = leftCoord[1] + ((rightCoord[1] - leftCoord[1]) / (rightCoord[0] - leftCoord[0])) * (i - leftCoord[0]); - } - } - } - if (bezier[end[0]] == null) { - bezier[end[0]] = bezier[end[0] - 1]; - } - return bezier; - }; - - return Calculate; - - })(); - - Convert = (function() { - function Convert() {} - - Convert.hexToRGB = function(hex) { - var b, g, r; - if (hex.charAt(0) === "#") { - hex = hex.substr(1); - } - r = parseInt(hex.substr(0, 2), 16); - g = parseInt(hex.substr(2, 2), 16); - b = parseInt(hex.substr(4, 2), 16); - return { - r: r, - g: g, - b: b - }; - }; - - Convert.rgbToHSL = function(r, g, b) { - var d, h, l, max, min, s; - if (typeof r === "object") { - g = r.g; - b = r.b; - r = r.r; - } - r /= 255; - g /= 255; - b /= 255; - max = Math.max(r, g, b); - min = Math.min(r, g, b); - l = (max + min) / 2; - if (max === min) { - h = s = 0; - } else { - d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - h = (function() { - switch (max) { - case r: - return (g - b) / d + (g < b ? 6 : 0); - case g: - return (b - r) / d + 2; - case b: - return (r - g) / d + 4; - } - })(); - h /= 6; - } - return { - h: h, - s: s, - l: l - }; - }; - - Convert.hslToRGB = function(h, s, l) { - var b, g, p, q, r; - if (typeof h === "object") { - s = h.s; - l = h.l; - h = h.h; - } - if (s === 0) { - r = g = b = l; - } else { - q = l < 0.5 ? l * (1 + s) : l + s - l * s; - p = 2 * l - q; - r = this.hueToRGB(p, q, h + 1 / 3); - g = this.hueToRGB(p, q, h); - b = this.hueToRGB(p, q, h - 1 / 3); - } - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - }; - - Convert.hueToRGB = function(p, q, t) { - if (t < 0) { - t += 1; - } - if (t > 1) { - t -= 1; - } - if (t < 1 / 6) { - return p + (q - p) * 6 * t; - } - if (t < 1 / 2) { - return q; - } - if (t < 2 / 3) { - return p + (q - p) * (2 / 3 - t) * 6; - } - return p; - }; - - Convert.rgbToHSV = function(r, g, b) { - var d, h, max, min, s, v; - r /= 255; - g /= 255; - b /= 255; - max = Math.max(r, g, b); - min = Math.min(r, g, b); - v = max; - d = max - min; - s = max === 0 ? 0 : d / max; - if (max === min) { - h = 0; - } else { - h = (function() { - switch (max) { - case r: - return (g - b) / d + (g < b ? 6 : 0); - case g: - return (b - r) / d + 2; - case b: - return (r - g) / d + 4; - } - })(); - h /= 6; - } - return { - h: h, - s: s, - v: v - }; - }; - - Convert.hsvToRGB = function(h, s, v) { - var b, f, g, i, p, q, r, t; - i = Math.floor(h * 6); - f = h * 6 - i; - p = v * (1 - s); - q = v * (1 - f * s); - t = v * (1 - (1 - f) * s); - switch (i % 6) { - case 0: - r = v; - g = t; - b = p; - break; - case 1: - r = q; - g = v; - b = p; - break; - case 2: - r = p; - g = v; - b = t; - break; - case 3: - r = p; - g = q; - b = v; - break; - case 4: - r = t; - g = p; - b = v; - break; - case 5: - r = v; - g = p; - b = q; - } - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - }; - - Convert.rgbToXYZ = function(r, g, b) { - var x, y, z; - r /= 255; - g /= 255; - b /= 255; - if (r > 0.04045) { - r = Math.pow((r + 0.055) / 1.055, 2.4); - } else { - r /= 12.92; - } - if (g > 0.04045) { - g = Math.pow((g + 0.055) / 1.055, 2.4); - } else { - g /= 12.92; - } - if (b > 0.04045) { - b = Math.pow((b + 0.055) / 1.055, 2.4); - } else { - b /= 12.92; - } - x = r * 0.4124 + g * 0.3576 + b * 0.1805; - y = r * 0.2126 + g * 0.7152 + b * 0.0722; - z = r * 0.0193 + g * 0.1192 + b * 0.9505; - return { - x: x * 100, - y: y * 100, - z: z * 100 - }; - }; - - Convert.xyzToRGB = function(x, y, z) { - var b, g, r; - x /= 100; - y /= 100; - z /= 100; - r = (3.2406 * x) + (-1.5372 * y) + (-0.4986 * z); - g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z); - b = (0.0557 * x) + (-0.2040 * y) + (1.0570 * z); - if (r > 0.0031308) { - r = (1.055 * Math.pow(r, 0.4166666667)) - 0.055; - } else { - r *= 12.92; - } - if (g > 0.0031308) { - g = (1.055 * Math.pow(g, 0.4166666667)) - 0.055; - } else { - g *= 12.92; - } - if (b > 0.0031308) { - b = (1.055 * Math.pow(b, 0.4166666667)) - 0.055; - } else { - b *= 12.92; - } - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - }; - - Convert.xyzToLab = function(x, y, z) { - var a, b, l, whiteX, whiteY, whiteZ; - if (typeof x === "object") { - y = x.y; - z = x.z; - x = x.x; - } - whiteX = 95.047; - whiteY = 100.0; - whiteZ = 108.883; - x /= whiteX; - y /= whiteY; - z /= whiteZ; - if (x > 0.008856451679) { - x = Math.pow(x, 0.3333333333); - } else { - x = (7.787037037 * x) + 0.1379310345; - } - if (y > 0.008856451679) { - y = Math.pow(y, 0.3333333333); - } else { - y = (7.787037037 * y) + 0.1379310345; - } - if (z > 0.008856451679) { - z = Math.pow(z, 0.3333333333); - } else { - z = (7.787037037 * z) + 0.1379310345; - } - l = 116 * y - 16; - a = 500 * (x - y); - b = 200 * (y - z); - return { - l: l, - a: a, - b: b - }; - }; - - Convert.labToXYZ = function(l, a, b) { - var x, y, z; - if (typeof l === "object") { - a = l.a; - b = l.b; - l = l.l; - } - y = (l + 16) / 116; - x = y + (a / 500); - z = y - (b / 200); - if (x > 0.2068965517) { - x = x * x * x; - } else { - x = 0.1284185493 * (x - 0.1379310345); - } - if (y > 0.2068965517) { - y = y * y * y; - } else { - y = 0.1284185493 * (y - 0.1379310345); - } - if (z > 0.2068965517) { - z = z * z * z; - } else { - z = 0.1284185493 * (z - 0.1379310345); - } - return { - x: x * 95.047, - y: y * 100.0, - z: z * 108.883 - }; - }; - - Convert.rgbToLab = function(r, g, b) { - var xyz; - if (typeof r === "object") { - g = r.g; - b = r.b; - r = r.r; - } - xyz = this.rgbToXYZ(r, g, b); - return this.xyzToLab(xyz); - }; - - Convert.labToRGB = function(l, a, b) {}; - - return Convert; - - })(); - - Event = (function() { - function Event() {} - - Event.events = {}; - - Event.types = ["processStart", "processComplete", "renderStart", "renderFinished", "blockStarted", "blockFinished"]; - - Event.trigger = function(target, type, data) { - var event, _i, _len, _ref, _results; - if (this.events[type] && this.events[type].length) { - _ref = this.events[type]; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - event = _ref[_i]; - if (event.target === null || target.id === event.target.id) { - _results.push(event.fn.call(target, data)); - } else { - _results.push(void 0); - } - } - return _results; - } - }; - - Event.listen = function(target, type, fn) { - var _fn, _type; - if (typeof target === "string") { - _type = target; - _fn = type; - target = null; - type = _type; - fn = _fn; - } - if (__indexOf.call(this.types, type) < 0) { - return false; - } - if (!this.events[type]) { - this.events[type] = []; - } - this.events[type].push({ - target: target, - fn: fn - }); - return true; - }; - - return Event; - - })(); - - Caman.Event = Event; - - Caman.Filter = Filter = (function() { - function Filter() {} - - Filter.Type = { - Single: 1, - Kernel: 2, - LayerDequeue: 3, - LayerFinished: 4, - LoadOverlay: 5, - Plugin: 6 - }; - - Filter.register = function(name, filterFunc) { - return Caman.prototype[name] = filterFunc; - }; - - return Filter; - - })(); - - Caman.IO = IO = (function() { - function IO() {} - - IO.domainRegex = /(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/; - - IO.isRemote = function(img) { - if (img == null) { - return false; - } - if (this.corsEnabled(img)) { - return false; - } - return this.isURLRemote(img.src); - }; - - IO.corsEnabled = function(img) { - var _ref; - return (img.crossOrigin != null) && ((_ref = img.crossOrigin.toLowerCase()) === 'anonymous' || _ref === 'use-credentials'); - }; - - IO.isURLRemote = function(url) { - var matches; - matches = url.match(this.domainRegex); - if (matches) { - return matches[1] !== document.domain; - } else { - return false; - } - }; - - IO.remoteCheck = function(src) { - if (this.isURLRemote(src)) { - if (!Caman.remoteProxy.length) { - Log.info("Attempting to load a remote image without a configured proxy. URL: " + src); - } else { - if (Caman.isURLRemote(Caman.remoteProxy)) { - Log.info("Cannot use a remote proxy for loading images."); - return; - } - return "" + Caman.remoteProxy + "?camanProxyUrl=" + (encodeURIComponent(src)); - } - } - }; - - IO.proxyUrl = function(src) { - return "" + Caman.remoteProxy + "?" + Caman.proxyParam + "=" + (encodeURIComponent(src)); - }; - - IO.useProxy = function(lang) { - var langToExt; - langToExt = { - ruby: 'rb', - python: 'py', - perl: 'pl', - javascript: 'js' - }; - lang = lang.toLowerCase(); - if (langToExt[lang] != null) { - lang = langToExt[lang]; - } - return "proxies/caman_proxy." + lang; - }; - - return IO; - - })(); - - Caman.prototype.save = function() { - if (typeof exports !== "undefined" && exports !== null) { - return this.nodeSave.apply(this, arguments); - } else { - return this.browserSave.apply(this, arguments); - } - }; - - Caman.prototype.browserSave = function(type) { - var image; - if (type == null) { - type = "png"; - } - type = type.toLowerCase(); - image = this.toBase64(type).replace("image/" + type, "image/octet-stream"); - return document.location.href = image; - }; - - Caman.prototype.nodeSave = function(file, overwrite) { - var e, stats; - if (overwrite == null) { - overwrite = true; - } - try { - stats = fs.statSync(file); - if (stats.isFile() && !overwrite) { - return false; - } - } catch (_error) { - e = _error; - Log.debug("Creating output file " + file); - } - return fs.writeFile(file, this.canvas.toBuffer(), function() { - return Log.debug("Finished writing to " + file); - }); - }; - - Caman.prototype.toImage = function(type) { - var img; - img = document.createElement('img'); - img.src = this.toBase64(type); - img.width = this.dimensions.width; - img.height = this.dimensions.height; - if (window.devicePixelRatio) { - img.width /= window.devicePixelRatio; - img.height /= window.devicePixelRatio; - } - return img; - }; - - Caman.prototype.toBase64 = function(type) { - if (type == null) { - type = "png"; - } - type = type.toLowerCase(); - return this.canvas.toDataURL("image/" + type); - }; - - Layer = (function() { - function Layer(c) { - this.c = c; - this.filter = this.c; - this.options = { - blendingMode: 'normal', - opacity: 1.0 - }; - this.layerID = Util.uniqid.get(); - this.canvas = typeof exports !== "undefined" && exports !== null ? new Canvas() : document.createElement('canvas'); - this.canvas.width = this.c.dimensions.width; - this.canvas.height = this.c.dimensions.height; - this.context = this.canvas.getContext('2d'); - this.context.createImageData(this.canvas.width, this.canvas.height); - this.imageData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); - this.pixelData = this.imageData.data; - } - - Layer.prototype.newLayer = function(cb) { - return this.c.newLayer.call(this.c, cb); - }; - - Layer.prototype.setBlendingMode = function(mode) { - this.options.blendingMode = mode; - return this; - }; - - Layer.prototype.opacity = function(opacity) { - this.options.opacity = opacity / 100; - return this; - }; - - Layer.prototype.copyParent = function() { - var i, parentData, _i, _ref; - parentData = this.c.pixelData; - for (i = _i = 0, _ref = this.c.pixelData.length; _i < _ref; i = _i += 4) { - this.pixelData[i] = parentData[i]; - this.pixelData[i + 1] = parentData[i + 1]; - this.pixelData[i + 2] = parentData[i + 2]; - this.pixelData[i + 3] = parentData[i + 3]; - } - return this; - }; - - Layer.prototype.fillColor = function() { - return this.c.fillColor.apply(this.c, arguments); - }; - - Layer.prototype.overlayImage = function(image) { - if (typeof image === "object") { - image = image.src; - } else if (typeof image === "string" && image[0] === "#") { - image = $(image).src; - } - if (!image) { - return this; - } - this.c.renderer.renderQueue.push({ - type: Filter.Type.LoadOverlay, - src: image, - layer: this - }); - return this; - }; - - Layer.prototype.applyToParent = function() { - var i, layerData, parentData, result, rgbaLayer, rgbaParent, _i, _ref, _results; - parentData = this.c.pixelStack[this.c.pixelStack.length - 1]; - layerData = this.c.pixelData; - _results = []; - for (i = _i = 0, _ref = layerData.length; _i < _ref; i = _i += 4) { - rgbaParent = { - r: parentData[i], - g: parentData[i + 1], - b: parentData[i + 2], - a: parentData[i + 3] - }; - rgbaLayer = { - r: layerData[i], - g: layerData[i + 1], - b: layerData[i + 2], - a: layerData[i + 3] - }; - result = Blender.execute(this.options.blendingMode, rgbaLayer, rgbaParent); - result.r = Util.clampRGB(result.r); - result.g = Util.clampRGB(result.g); - result.b = Util.clampRGB(result.b); - if (result.a == null) { - result.a = rgbaLayer.a; - } - parentData[i] = rgbaParent.r - ((rgbaParent.r - result.r) * (this.options.opacity * (result.a / 255))); - parentData[i + 1] = rgbaParent.g - ((rgbaParent.g - result.g) * (this.options.opacity * (result.a / 255))); - _results.push(parentData[i + 2] = rgbaParent.b - ((rgbaParent.b - result.b) * (this.options.opacity * (result.a / 255)))); - } - return _results; - }; - - return Layer; - - })(); - - Logger = (function() { - function Logger() { - var name, _i, _len, _ref; - _ref = ['log', 'info', 'warn', 'error']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - this[name] = (function(name) { - return function() { - var args, e; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - if (!Caman.DEBUG) { - return; - } - try { - return console[name].apply(console, args); - } catch (_error) { - e = _error; - return console[name](args); - } - }; - })(name); - } - this.debug = this.log; - } - - return Logger; - - })(); - - Log = new Logger(); - - PixelInfo = (function() { - PixelInfo.coordinatesToLocation = function(x, y, width) { - return (y * width + x) * 4; - }; - - PixelInfo.locationToCoordinates = function(loc, width) { - var x, y; - y = Math.floor(loc / (width * 4)); - x = (loc % (width * 4)) / 4; - return { - x: x, - y: y - }; - }; - - function PixelInfo(c) { - this.c = c; - this.loc = 0; - } - - PixelInfo.prototype.locationXY = function() { - var x, y; - y = this.c.dimensions.height - Math.floor(this.loc / (this.c.dimensions.width * 4)); - x = (this.loc % (this.c.dimensions.width * 4)) / 4; - return { - x: x, - y: y - }; - }; - - PixelInfo.prototype.getPixelRelative = function(horiz, vert) { - var newLoc; - newLoc = this.loc + (this.c.dimensions.width * 4 * (vert * -1)) + (4 * horiz); - if (newLoc > this.c.pixelData.length || newLoc < 0) { - return { - r: 0, - g: 0, - b: 0, - a: 0 - }; - } - return { - r: this.c.pixelData[newLoc], - g: this.c.pixelData[newLoc + 1], - b: this.c.pixelData[newLoc + 2], - a: this.c.pixelData[newLoc + 3] - }; - }; - - PixelInfo.prototype.putPixelRelative = function(horiz, vert, rgba) { - var nowLoc; - nowLoc = this.loc + (this.c.dimensions.width * 4 * (vert * -1)) + (4 * horiz); - if (newLoc > this.c.pixelData.length || newLoc < 0) { - return; - } - this.c.pixelData[newLoc] = rgba.r; - this.c.pixelData[newLoc + 1] = rgba.g; - this.c.pixelData[newLoc + 2] = rgba.b; - this.c.pixelData[newLoc + 3] = rgba.a; - return true; - }; - - PixelInfo.prototype.getPixel = function(x, y) { - var loc; - loc = this.coordinatesToLocation(x, y, this.width); - return { - r: this.c.pixelData[loc], - g: this.c.pixelData[loc + 1], - b: this.c.pixelData[loc + 2], - a: this.c.pixelData[loc + 3] - }; - }; - - PixelInfo.prototype.putPixel = function(x, y, rgba) { - var loc; - loc = this.coordinatesToLocation(x, y, this.width); - this.c.pixelData[loc] = rgba.r; - this.c.pixelData[loc + 1] = rgba.g; - this.c.pixelData[loc + 2] = rgba.b; - return this.c.pixelData[loc + 3] = rgba.a; - }; - - return PixelInfo; - - })(); - - Plugin = (function() { - function Plugin() {} - - Plugin.plugins = {}; - - Plugin.register = function(name, plugin) { - return this.plugins[name] = plugin; - }; - - Plugin.execute = function(context, name, args) { - return this.plugins[name].apply(context, args); - }; - - return Plugin; - - })(); - - Caman.Plugin = Plugin; - - Caman.Renderer = Renderer = (function() { - Renderer.Blocks = Caman.NodeJS ? require('os').cpus().length : 4; - - function Renderer(c) { - this.c = c; - this.processNext = __bind(this.processNext, this); - this.renderQueue = []; - this.modPixelData = null; - } - - Renderer.prototype.add = function(job) { - if (job == null) { - return; - } - return this.renderQueue.push(job); - }; - - Renderer.prototype.processNext = function() { - var layer; - if (this.renderQueue.length === 0) { - Event.trigger(this, "renderFinished"); - if (this.finishedFn != null) { - this.finishedFn.call(this.c); - } - return this; - } - this.currentJob = this.renderQueue.shift(); - switch (this.currentJob.type) { - case Filter.Type.LayerDequeue: - layer = this.c.canvasQueue.shift(); - this.c.executeLayer(layer); - return this.processNext(); - case Filter.Type.LayerFinished: - this.c.applyCurrentLayer(); - this.c.popContext(); - return this.processNext(); - case Filter.Type.LoadOverlay: - return this.loadOverlay(this.currentJob.layer, this.currentJob.src); - case Filter.Type.Plugin: - return this.executePlugin(); - default: - return this.executeFilter(); - } - }; - - Renderer.prototype.execute = function(callback) { - this.finishedFn = callback; - this.modPixelData = Util.dataArray(this.c.pixelData.length); - return this.processNext(); - }; - - Renderer.prototype.eachBlock = function(fn) { - var blockN, blockPixelLength, bnum, end, f, i, lastBlockN, n, start, _i, _ref, _results, - _this = this; - this.blocksDone = 0; - n = this.c.pixelData.length; - blockPixelLength = Math.floor((n / 4) / Renderer.Blocks); - blockN = blockPixelLength * 4; - lastBlockN = blockN + ((n / 4) % Renderer.Blocks) * 4; - _results = []; - for (i = _i = 0, _ref = Renderer.Blocks; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - start = i * blockN; - end = start + (i === Renderer.Blocks - 1 ? lastBlockN : blockN); - if (Caman.NodeJS) { - f = Fiber(function() { - return fn.call(_this, i, start, end); - }); - bnum = f.run(); - _results.push(this.blockFinished(bnum)); - } else { - _results.push(setTimeout((function(i, start, end) { - return function() { - return fn.call(_this, i, start, end); - }; - })(i, start, end), 0)); - } - } - return _results; - }; - - Renderer.prototype.executeFilter = function() { - Event.trigger(this.c, "processStart", this.currentJob); - if (this.currentJob.type === Filter.Type.Single) { - return this.eachBlock(this.renderBlock); - } else { - return this.eachBlock(this.renderKernel); - } - }; - - Renderer.prototype.executePlugin = function() { - Log.debug("Executing plugin " + this.currentJob.plugin); - Plugin.execute(this.c, this.currentJob.plugin, this.currentJob.args); - Log.debug("Plugin " + this.currentJob.plugin + " finished!"); - return this.processNext(); - }; - - Renderer.prototype.renderBlock = function(bnum, start, end) { - var data, i, pixelInfo, res, _i; - Log.debug("Block #" + bnum + " - Filter: " + this.currentJob.name + ", Start: " + start + ", End: " + end); - Event.trigger(this.c, "blockStarted", { - blockNum: bnum, - totalBlocks: Renderer.Blocks, - startPixel: start, - endPixel: end - }); - data = { - r: 0, - g: 0, - b: 0, - a: 0 - }; - pixelInfo = new PixelInfo(this.c); - for (i = _i = start; _i < end; i = _i += 4) { - pixelInfo.loc = i; - data.r = this.c.pixelData[i]; - data.g = this.c.pixelData[i + 1]; - data.b = this.c.pixelData[i + 2]; - data.a = this.c.pixelData[i + 3]; - res = this.currentJob.processFn.call(pixelInfo, data); - if (res.a == null) { - res.a = data.a; - } - this.c.pixelData[i] = Util.clampRGB(res.r); - this.c.pixelData[i + 1] = Util.clampRGB(res.g); - this.c.pixelData[i + 2] = Util.clampRGB(res.b); - this.c.pixelData[i + 3] = Util.clampRGB(res.a); - } - if (Caman.NodeJS) { - return Fiber["yield"](bnum); - } else { - return this.blockFinished(bnum); - } - }; - - Renderer.prototype.renderKernel = function(bnum, start, end) { - var adjust, adjustSize, bias, builder, builderIndex, divisor, i, j, k, kernel, n, name, pixel, pixelInfo, res, _i, _j, _k; - name = this.currentJob.name; - bias = this.currentJob.bias; - divisor = this.currentJob.divisor; - n = this.c.pixelData.length; - adjust = this.currentJob.adjust; - adjustSize = Math.sqrt(adjust.length); - kernel = []; - Log.debug("Rendering kernel - Filter: " + this.currentJob.name); - start = Math.max(start, this.c.dimensions.width * 4 * ((adjustSize - 1) / 2)); - end = Math.min(end, n - (this.c.dimensions.width * 4 * ((adjustSize - 1) / 2))); - builder = (adjustSize - 1) / 2; - pixelInfo = new PixelInfo(this.c); - for (i = _i = start; _i < end; i = _i += 4) { - pixelInfo.loc = i; - builderIndex = 0; - for (j = _j = -builder; -builder <= builder ? _j <= builder : _j >= builder; j = -builder <= builder ? ++_j : --_j) { - for (k = _k = builder; builder <= -builder ? _k <= -builder : _k >= -builder; k = builder <= -builder ? ++_k : --_k) { - pixel = pixelInfo.getPixelRelative(j, k); - kernel[builderIndex * 3] = pixel.r; - kernel[builderIndex * 3 + 1] = pixel.g; - kernel[builderIndex * 3 + 2] = pixel.b; - builderIndex++; - } - } - res = this.processKernel(adjust, kernel, divisor, bias); - this.modPixelData[i] = Util.clampRGB(res.r); - this.modPixelData[i + 1] = Util.clampRGB(res.g); - this.modPixelData[i + 2] = Util.clampRGB(res.b); - this.modPixelData[i + 3] = this.c.pixelData[i + 3]; - } - if (Caman.NodeJS) { - return Fiber["yield"](bnum); - } else { - return this.blockFinished(bnum); - } - }; - - Renderer.prototype.blockFinished = function(bnum) { - var i, _i, _ref; - if (bnum >= 0) { - Log.debug("Block #" + bnum + " finished! Filter: " + this.currentJob.name); - } - this.blocksDone++; - Event.trigger(this.c, "blockFinished", { - blockNum: bnum, - blocksFinished: this.blocksDone, - totalBlocks: Renderer.Blocks - }); - if (this.blocksDone === Renderer.Blocks) { - if (this.currentJob.type === Filter.Type.Kernel) { - for (i = _i = 0, _ref = this.c.pixelData.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - this.c.pixelData[i] = this.modPixelData[i]; - } - } - if (bnum >= 0) { - Log.debug("Filter " + this.currentJob.name + " finished!"); - } - Event.trigger(this.c, "processComplete", this.currentJob); - return this.processNext(); - } - }; - - Renderer.prototype.processKernel = function(adjust, kernel, divisor, bias) { - var i, val, _i, _ref; - val = { - r: 0, - g: 0, - b: 0 - }; - for (i = _i = 0, _ref = adjust.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - val.r += adjust[i] * kernel[i * 3]; - val.g += adjust[i] * kernel[i * 3 + 1]; - val.b += adjust[i] * kernel[i * 3 + 2]; - } - val.r = (val.r / divisor) + bias; - val.g = (val.g / divisor) + bias; - val.b = (val.b / divisor) + bias; - return val; - }; - - Renderer.prototype.loadOverlay = function(layer, src) { - var img, proxyUrl, - _this = this; - img = document.createElement('img'); - img.onload = function() { - layer.context.drawImage(img, 0, 0, _this.c.dimensions.width, _this.c.dimensions.height); - layer.imageData = layer.context.getImageData(0, 0, _this.c.dimensions.width, _this.c.dimensions.height); - layer.pixelData = layer.imageData.data; - _this.c.pixelData = layer.pixelData; - return _this.processNext(); - }; - proxyUrl = IO.remoteCheck(src); - return img.src = proxyUrl != null ? proxyUrl : src; - }; - - return Renderer; - - })(); - - Caman.Store = Store = (function() { - function Store() {} - - Store.items = {}; - - Store.has = function(search) { - return this.items[search] != null; - }; - - Store.get = function(search) { - return this.items[search]; - }; - - Store.put = function(name, obj) { - return this.items[name] = obj; - }; - - Store.execute = function(search, callback) { - var _this = this; - setTimeout(function() { - return callback.call(_this.get(search), _this.get(search)); - }, 0); - return this.get(search); - }; - - Store.flush = function(name) { - if (name == null) { - name = false; - } - if (name) { - return delete this.items[name]; - } else { - return this.items = {}; - } - }; - - return Store; - - })(); - - Blender.register("normal", function(rgbaLayer, rgbaParent) { - return { - r: rgbaLayer.r, - g: rgbaLayer.g, - b: rgbaLayer.b - }; - }); - - Blender.register("multiply", function(rgbaLayer, rgbaParent) { - return { - r: (rgbaLayer.r * rgbaParent.r) / 255, - g: (rgbaLayer.g * rgbaParent.g) / 255, - b: (rgbaLayer.b * rgbaParent.b) / 255 - }; - }); - - Blender.register("screen", function(rgbaLayer, rgbaParent) { - return { - r: 255 - (((255 - rgbaLayer.r) * (255 - rgbaParent.r)) / 255), - g: 255 - (((255 - rgbaLayer.g) * (255 - rgbaParent.g)) / 255), - b: 255 - (((255 - rgbaLayer.b) * (255 - rgbaParent.b)) / 255) - }; - }); - - Blender.register("overlay", function(rgbaLayer, rgbaParent) { - var result; - result = {}; - result.r = rgbaParent.r > 128 ? 255 - 2 * (255 - rgbaLayer.r) * (255 - rgbaParent.r) / 255 : (rgbaParent.r * rgbaLayer.r * 2) / 255; - result.g = rgbaParent.g > 128 ? 255 - 2 * (255 - rgbaLayer.g) * (255 - rgbaParent.g) / 255 : (rgbaParent.g * rgbaLayer.g * 2) / 255; - result.b = rgbaParent.b > 128 ? 255 - 2 * (255 - rgbaLayer.b) * (255 - rgbaParent.b) / 255 : (rgbaParent.b * rgbaLayer.b * 2) / 255; - return result; - }); - - Blender.register("difference", function(rgbaLayer, rgbaParent) { - return { - r: rgbaLayer.r - rgbaParent.r, - g: rgbaLayer.g - rgbaParent.g, - b: rgbaLayer.b - rgbaParent.b - }; - }); - - Blender.register("addition", function(rgbaLayer, rgbaParent) { - return { - r: rgbaParent.r + rgbaLayer.r, - g: rgbaParent.g + rgbaLayer.g, - b: rgbaParent.b + rgbaLayer.b - }; - }); - - Blender.register("exclusion", function(rgbaLayer, rgbaParent) { - return { - r: 128 - 2 * (rgbaParent.r - 128) * (rgbaLayer.r - 128) / 255, - g: 128 - 2 * (rgbaParent.g - 128) * (rgbaLayer.g - 128) / 255, - b: 128 - 2 * (rgbaParent.b - 128) * (rgbaLayer.b - 128) / 255 - }; - }); - - Blender.register("softLight", function(rgbaLayer, rgbaParent) { - var result; - result = {}; - result.r = rgbaParent.r > 128 ? 255 - ((255 - rgbaParent.r) * (255 - (rgbaLayer.r - 128))) / 255 : (rgbaParent.r * (rgbaLayer.r + 128)) / 255; - result.g = rgbaParent.g > 128 ? 255 - ((255 - rgbaParent.g) * (255 - (rgbaLayer.g - 128))) / 255 : (rgbaParent.g * (rgbaLayer.g + 128)) / 255; - result.b = rgbaParent.b > 128 ? 255 - ((255 - rgbaParent.b) * (255 - (rgbaLayer.b - 128))) / 255 : (rgbaParent.b * (rgbaLayer.b + 128)) / 255; - return result; - }); - - Blender.register("lighten", function(rgbaLayer, rgbaParent) { - return { - r: rgbaParent.r > rgbaLayer.r ? rgbaParent.r : rgbaLayer.r, - g: rgbaParent.g > rgbaLayer.g ? rgbaParent.g : rgbaLayer.g, - b: rgbaParent.b > rgbaLayer.b ? rgbaParent.b : rgbaLayer.b - }; - }); - - Blender.register("darken", function(rgbaLayer, rgbaParent) { - return { - r: rgbaParent.r > rgbaLayer.r ? rgbaLayer.r : rgbaParent.r, - g: rgbaParent.g > rgbaLayer.g ? rgbaLayer.g : rgbaParent.g, - b: rgbaParent.b > rgbaLayer.b ? rgbaLayer.b : rgbaParent.b - }; - }); - - Filter.register("fillColor", function() { - var color; - if (arguments.length === 1) { - color = Convert.hexToRGB(arguments[0]); - } else { - color = { - r: arguments[0], - g: arguments[1], - b: arguments[2] - }; - } - return this.process("fillColor", function(rgba) { - rgba.r = color.r; - rgba.g = color.g; - rgba.b = color.b; - rgba.a = 255; - return rgba; - }); - }); - - Filter.register("brightness", function(adjust) { - adjust = Math.floor(255 * (adjust / 100)); - return this.process("brightness", function(rgba) { - rgba.r += adjust; - rgba.g += adjust; - rgba.b += adjust; - return rgba; - }); - }); - - Filter.register("saturation", function(adjust) { - adjust *= -0.01; - return this.process("saturation", function(rgba) { - var max; - max = Math.max(rgba.r, rgba.g, rgba.b); - if (rgba.r !== max) { - rgba.r += (max - rgba.r) * adjust; - } - if (rgba.g !== max) { - rgba.g += (max - rgba.g) * adjust; - } - if (rgba.b !== max) { - rgba.b += (max - rgba.b) * adjust; - } - return rgba; - }); - }); - - Filter.register("vibrance", function(adjust) { - adjust *= -1; - return this.process("vibrance", function(rgba) { - var amt, avg, max; - 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; - if (rgba.r !== max) { - rgba.r += (max - rgba.r) * amt; - } - if (rgba.g !== max) { - rgba.g += (max - rgba.g) * amt; - } - if (rgba.b !== max) { - rgba.b += (max - rgba.b) * amt; - } - return rgba; - }); - }); - - Filter.register("greyscale", function(adjust) { - return this.process("greyscale", function(rgba) { - var avg; - avg = Calculate.luminance(rgba); - rgba.r = avg; - rgba.g = avg; - rgba.b = avg; - return rgba; - }); - }); - - Filter.register("contrast", function(adjust) { - adjust = Math.pow((adjust + 100) / 100, 2); - return this.process("contrast", function(rgba) { - rgba.r /= 255; - rgba.r -= 0.5; - rgba.r *= adjust; - rgba.r += 0.5; - rgba.r *= 255; - rgba.g /= 255; - rgba.g -= 0.5; - rgba.g *= adjust; - rgba.g += 0.5; - rgba.g *= 255; - rgba.b /= 255; - rgba.b -= 0.5; - rgba.b *= adjust; - rgba.b += 0.5; - rgba.b *= 255; - return rgba; - }); - }); - - Filter.register("hue", function(adjust) { - return this.process("hue", function(rgba) { - var h, hsv, rgb; - 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; - rgb = Convert.hsvToRGB(hsv.h, hsv.s, hsv.v); - rgb.a = rgba.a; - return rgb; - }); - }); - - Filter.register("colorize", function() { - var level, rgb; - if (arguments.length === 2) { - rgb = Convert.hexToRGB(arguments[0]); - level = arguments[1]; - } else if (arguments.length === 4) { - rgb = { - r: arguments[0], - g: arguments[1], - b: arguments[2] - }; - level = arguments[3]; - } - return this.process("colorize", function(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); - return rgba; - }); - }); - - Filter.register("invert", function() { - return this.process("invert", function(rgba) { - rgba.r = 255 - rgba.r; - rgba.g = 255 - rgba.g; - rgba.b = 255 - rgba.b; - return rgba; - }); - }); - - Filter.register("sepia", function(adjust) { - if (adjust == null) { - adjust = 100; - } - adjust /= 100; - return this.process("sepia", function(rgba) { - 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)))); - return rgba; - }); - }); - - Filter.register("gamma", function(adjust) { - return this.process("gamma", function(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; - return rgba; - }); - }); - - Filter.register("noise", function(adjust) { - adjust = Math.abs(adjust) * 2.55; - return this.process("noise", function(rgba) { - var rand; - rand = Calculate.randomRange(adjust * -1, adjust); - rgba.r += rand; - rgba.g += rand; - rgba.b += rand; - return rgba; - }); - }); - - Filter.register("clip", function(adjust) { - adjust = Math.abs(adjust) * 2.55; - return this.process("clip", function(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; - } - return rgba; - }); - }); - - Filter.register("channels", function(options) { - var chan, value; - if (typeof options !== "object") { - return this; - } - for (chan in options) { - if (!__hasProp.call(options, chan)) continue; - value = options[chan]; - if (value === 0) { - delete options[chan]; - continue; - } - options[chan] /= 100; - } - if (options.length === 0) { - return this; - } - return this.process("channels", function(rgba) { - if (options.red != null) { - if (options.red > 0) { - rgba.r += (255 - rgba.r) * options.red; - } else { - rgba.r -= rgba.r * Math.abs(options.red); - } - } - if (options.green != null) { - if (options.green > 0) { - rgba.g += (255 - rgba.g) * options.green; - } else { - rgba.g -= rgba.g * Math.abs(options.green); - } - } - if (options.blue != null) { - if (options.blue > 0) { - rgba.b += (255 - rgba.b) * options.blue; - } else { - rgba.b -= rgba.b * Math.abs(options.blue); - } - } - return rgba; - }); - }); - - Filter.register("curves", function() { - var bezier, chans, cps, ctrl1, ctrl2, end, i, start, _i, _j, _ref, _ref1; - chans = arguments[0], cps = 2 <= arguments.length ? __slice.call(arguments, 1) : []; - if (typeof chans === "string") { - chans = chans.split(""); - } - if (chans[0] === "v") { - chans = ['r', 'g', 'b']; - } - if (cps.length < 3 || cps.length > 4) { - throw "Invalid number of arguments to curves filter"; - } - start = cps[0]; - ctrl1 = cps[1]; - ctrl2 = cps.length === 4 ? cps[2] : cps[1]; - end = cps[cps.length - 1]; - bezier = Calculate.bezier(start, ctrl1, ctrl2, end, 0, 255); - if (start[0] > 0) { - for (i = _i = 0, _ref = start[0]; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - bezier[i] = start[1]; - } - } - if (end[0] < 255) { - for (i = _j = _ref1 = end[0]; _ref1 <= 255 ? _j <= 255 : _j >= 255; i = _ref1 <= 255 ? ++_j : --_j) { - bezier[i] = end[1]; - } - } - return this.process("curves", function(rgba) { - var _k, _ref2; - for (i = _k = 0, _ref2 = chans.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; i = 0 <= _ref2 ? ++_k : --_k) { - rgba[chans[i]] = bezier[rgba[chans[i]]]; - } - return rgba; - }); - }); - - Filter.register("exposure", function(adjust) { - var ctrl1, ctrl2, p; - p = Math.abs(adjust) / 100; - ctrl1 = [0, 255 * p]; - ctrl2 = [255 - (255 * p), 255]; - if (adjust < 0) { - ctrl1 = ctrl1.reverse(); - ctrl2 = ctrl2.reverse(); - } - return this.curves('rgb', [0, 0], ctrl1, ctrl2, [255, 255]); - }); - - Caman.Plugin.register("crop", function(width, height, x, y) { - var canvas, ctx; - if (x == null) { - x = 0; - } - if (y == null) { - y = 0; - } - if (typeof exports !== "undefined" && exports !== null) { - canvas = new Canvas(width, height); - } else { - canvas = document.createElement('canvas'); - Util.copyAttributes(this.canvas, canvas); - canvas.width = width; - canvas.height = height; - } - ctx = canvas.getContext('2d'); - ctx.drawImage(this.canvas, x, y, width, height, 0, 0, width, height); - this.cropCoordinates = { - x: x, - y: y - }; - this.cropped = true; - return this.replaceCanvas(canvas); - }); - - Caman.Plugin.register("resize", function(newDims) { - var canvas, ctx; - if (newDims == null) { - newDims = null; - } - if (newDims === null || ((newDims.width == null) && (newDims.height == null))) { - Log.error("Invalid or missing dimensions given for resize"); - return; - } - if (newDims.width == null) { - newDims.width = this.canvas.width * newDims.height / this.canvas.height; - } else if (newDims.height == null) { - newDims.height = this.canvas.height * newDims.width / this.canvas.width; - } - if (typeof exports !== "undefined" && exports !== null) { - canvas = new Canvas(newDims.width, newDims.height); - } else { - canvas = document.createElement('canvas'); - Util.copyAttributes(this.canvas, canvas); - canvas.width = newDims.width; - canvas.height = newDims.height; - } - ctx = canvas.getContext('2d'); - ctx.drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, newDims.width, newDims.height); - this.resized = true; - return this.replaceCanvas(canvas); - }); - - Caman.Filter.register("crop", function() { - return this.processPlugin("crop", Array.prototype.slice.call(arguments, 0)); - }); - - Caman.Filter.register("resize", function() { - return this.processPlugin("resize", Array.prototype.slice.call(arguments, 0)); - }); - -}).call(this); diff --git a/mig/images/lib/CamanJS-4.1.1/docs/analyze.html b/mig/images/lib/CamanJS-4.1.1/docs/analyze.html deleted file mode 100644 index ab8dbe728..000000000 --- a/mig/images/lib/CamanJS-4.1.1/docs/analyze.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - -
class Analyze
- constructor: (@c) ->
-
- calculateLevels: ->
- levels =
- r: {}
- g: {}
- b: {}
Initialize all values to 0 first so there are no data gaps
- - for i in [0..255]
- levels.r[i] = 0
- levels.g[i] = 0
- levels.b[i] = 0
Iterate through each pixel block and increment the level counters
- - for i in [0...@c.pixelData.length] by 4
- levels.r[@c.pixelData[i]]++
- levels.g[@c.pixelData[i+1]]++
- levels.b[@c.pixelData[i+2]]++
Normalize all of the numbers by converting them to percentages between -0 and 1.0
- - numPixels = @c.pixelData.length / 4
-
- for i in [0..255]
- levels.r[i] /= numPixels
- levels.g[i] /= numPixels
- levels.b[i] /= numPixels
-
- levels
-
-
Caman.DOMUpdated = ->
- imgs = document.querySelectorAll("img[data-caman]")
- return unless imgs.length > 0
-
- for img in imgs
- parser = new CamanParser img, ->
- @parse()
- @execute()
If enabled, we check the page to see if there are any -images with Caman instructions provided using HTML5 -data attributes.
- -if Caman.autoload then do ->
- if document.readyState is "complete"
- Caman.DOMUpdated()
- else
- document.addEventListener "DOMContentLoaded", Caman.DOMUpdated, false
Parses Caman instructions embedded in the HTML data-caman attribute
- -class CamanParser
- INST_REGEX = "(\\w+)\\((.*?)\\)"
-
- constructor: (ele, ready) ->
- @dataStr = ele.getAttribute('data-caman')
- @caman = Caman ele, ready.bind(@)
-
- parse: ->
- @ele = @caman.canvas
First we find each instruction as a whole using a global -regex search.
- - r = new RegExp(INST_REGEX, 'g')
- unparsedInstructions = @dataStr.match r
- return unless unparsedInstructions.length > 0
Once we gather all the instructions, we go through each one -and parse out the filter name + it's parameters.
- - r = new RegExp(INST_REGEX)
- for inst in unparsedInstructions
- [m, filter, args] = inst.match(r)
Create a factory function so we can catch any errors that -are produced when running the filters. This also makes it very -simple to support multiple/complex filter arguments.
- - instFunc = new Function("return function() {
- this.#{filter}(#{args});
- };")
-
- try
- func = instFunc()
- func.call @caman
- catch e
- Log.debug e
-
- execute: ->
- ele = @ele
- @caman.render ->
- ele.parentNode.replaceChild @toImage(), ele
-
-
Built-in layer blenders. Many of these mimic Photoshop blend modes.
- -Caman.Blender = class Blender
- @blenders = {}
Registers a blender. Can be used to add your own blenders outside of -the core library, if needed.
- - @register: (name, func) -> @blenders[name] = func
Executes a blender to combine a layer with its parent.
- - @execute: (name, rgbaLayer, rgbaParent) ->
- @blenders[name](rgbaLayer, rgbaParent)
-
-
Directly apply the child layer's pixels to the parent layer with no special changes
- -Blender.register "normal", (rgbaLayer, rgbaParent) ->
- r: rgbaLayer.r
- g: rgbaLayer.g
- b: rgbaLayer.b
Apply the child to the parent by multiplying the color values. This generally creates contrast.
- -Blender.register "multiply", (rgbaLayer, rgbaParent) ->
- r: (rgbaLayer.r * rgbaParent.r) / 255
- g: (rgbaLayer.g * rgbaParent.g) / 255
- b: (rgbaLayer.b * rgbaParent.b) / 255
-
-Blender.register "screen", (rgbaLayer, rgbaParent) ->
- r: 255 - (((255 - rgbaLayer.r) * (255 - rgbaParent.r)) / 255)
- g: 255 - (((255 - rgbaLayer.g) * (255 - rgbaParent.g)) / 255)
- b: 255 - (((255 - rgbaLayer.b) * (255 - rgbaParent.b)) / 255)
-
-
-Blender.register "overlay", (rgbaLayer, rgbaParent) ->
- result = {}
- result.r =
- if rgbaParent.r > 128
- 255 - 2 * (255 - rgbaLayer.r) * (255 - rgbaParent.r) / 255
- else (rgbaParent.r * rgbaLayer.r * 2) / 255
-
- result.g =
- if rgbaParent.g > 128
- 255 - 2 * (255 - rgbaLayer.g) * (255 - rgbaParent.g) / 255
- else (rgbaParent.g * rgbaLayer.g * 2) / 255
-
- result.b =
- if rgbaParent.b > 128
- 255 - 2 * (255 - rgbaLayer.b) * (255 - rgbaParent.b) / 255
- else (rgbaParent.b * rgbaLayer.b * 2) / 255
-
- result
-
-Blender.register "difference", (rgbaLayer, rgbaParent) ->
- r: rgbaLayer.r - rgbaParent.r
- g: rgbaLayer.g - rgbaParent.g
- b: rgbaLayer.b - rgbaParent.b
-
-Blender.register "addition", (rgbaLayer, rgbaParent) ->
- r: rgbaParent.r + rgbaLayer.r
- g: rgbaParent.g + rgbaLayer.g
- b: rgbaParent.b + rgbaLayer.b
-
-Blender.register "exclusion", (rgbaLayer, rgbaParent) ->
- r: 128 - 2 * (rgbaParent.r - 128) * (rgbaLayer.r - 128) / 255
- g: 128 - 2 * (rgbaParent.g - 128) * (rgbaLayer.g - 128) / 255
- b: 128 - 2 * (rgbaParent.b - 128) * (rgbaLayer.b - 128) / 255
-
-Blender.register "softLight", (rgbaLayer, rgbaParent) ->
- result = {}
-
- result.r =
- if rgbaParent.r > 128
- 255 - ((255 - rgbaParent.r) * (255 - (rgbaLayer.r - 128))) / 255
- else (rgbaParent.r * (rgbaLayer.r + 128)) / 255
-
- result.g =
- if rgbaParent.g > 128
- 255 - ((255 - rgbaParent.g) * (255 - (rgbaLayer.g - 128))) / 255
- else (rgbaParent.g * (rgbaLayer.g + 128)) / 255
-
- result.b =
- if rgbaParent.b > 128
- 255 - ((255 - rgbaParent.b) * (255 - (rgbaLayer.b - 128))) / 255
- else (rgbaParent.b * (rgbaLayer.b + 128)) / 255
-
- result
-
-Blender.register "lighten", (rgbaLayer, rgbaParent) ->
- r: if rgbaParent.r > rgbaLayer.r then rgbaParent.r else rgbaLayer.r
- g: if rgbaParent.g > rgbaLayer.g then rgbaParent.g else rgbaLayer.g
- b: if rgbaParent.b > rgbaLayer.b then rgbaParent.b else rgbaLayer.b
-
-Blender.register "darken", (rgbaLayer, rgbaParent) ->
- r: if rgbaParent.r > rgbaLayer.r then rgbaLayer.r else rgbaParent.r
- g: if rgbaParent.g > rgbaLayer.g then rgbaLayer.g else rgbaParent.g
- b: if rgbaParent.b > rgbaLayer.b then rgbaLayer.b else rgbaParent.b
-
-
Caman.Filter.register "boxBlur", ->
- @processKernel "Box Blur", [
- 1, 1, 1,
- 1, 1, 1,
- 1, 1, 1
- ]
-
-Caman.Filter.register "heavyRadialBlur", ->
- @processKernel "Heavy Radial Blur", [
- 0, 0, 1, 0, 0,
- 0, 1, 1, 1, 0,
- 1, 1, 1, 1, 1,
- 0, 1, 1, 1, 0,
- 0, 0, 1, 0, 0
- ]
-
-Caman.Filter.register "gaussianBlur", ->
- @processKernel "Gaussian Blur", [
- 1, 4, 6, 4, 1,
- 4, 16, 24, 16, 4,
- 6, 24, 36, 24, 6,
- 4, 16, 24, 16, 4,
- 1, 4, 6, 4, 1
- ]
-
-Caman.Filter.register "motionBlur", (degrees) ->
- if degrees in [0, 180]
- kernel = [
- 0, 0, 1, 0, 0,
- 0, 0, 1, 0, 0,
- 0, 0, 1, 0, 0,
- 0, 0, 1, 0, 0,
- 0, 0, 1, 0, 0
- ]
- else if (degrees > 0 && degrees < 90) || (degrees > 180 && degrees < 270)
- kernel = [
- 0, 0, 0, 0, 1,
- 0, 0, 0, 1, 0,
- 0, 0, 1, 0, 0,
- 0, 1, 0, 0, 0,
- 1, 0, 0, 0, 0
- ]
- else if degrees in [90, 270]
- kernel = [
- 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0
- ]
- else
- kernel = [
- 1, 0, 0, 0, 0,
- 0, 1, 0, 0, 0,
- 0, 0, 1, 0, 0,
- 0, 0, 0, 1, 0,
- 0, 0, 0, 0, 1
- ]
-
- @processKernel "Motion Blur", kernel
-
-Caman.Filter.register "sharpen", (amt = 100) ->
- amt /= 100
-
- @processKernel "Sharpen", [
- 0, -amt, 0,
- -amt, 4 * amt + 1, -amt,
- 0, -amt, 0
- ]
-
-
Various math-heavy helpers
- -Caman.Calculate = class Calculate
Calculates the distance between two points
- - @distance: (x1, y1, x2, y2) ->
- Math.sqrt Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)
Generates a pseudorandom number that lies within the max - mix range. The number can be either -an integer or a float depending on what the user specifies.
- - @randomRange: (min, max, getFloat = false) ->
- rand = min + (Math.random() * (max - min))
- return if getFloat then rand.toFixed(getFloat) else Math.round(rand)
Calculates the luminance of a single pixel using a special weighted sum
- - @luminance: (rgba) -> (0.299 * rgba.r) + (0.587 * rgba.g) + (0.114 * rgba.b)
Generates a bezier curve given a start and end point, with two control points in between. -Can also optionally bound the y values between a low and high bound.
-This is different than most bezier curve functions because it attempts to construct it in such -a way that we can use it more like a simple input -> output system, or a one-to-one function. -In other words we can provide an input color value, and immediately receive an output modified -color value.
-Note that, by design, this does not force X values to be in the range [0..255]. This is to -generalize the function a bit more. If you give it a starting X value that isn't 0, and/or a -ending X value that isn't 255, you may run into problems with your filter!
- - @bezier: (start, ctrl1, ctrl2, end, lowBound, highBound) ->
- x0 = start[0]
- y0 = start[1]
- x1 = ctrl1[0]
- y1 = ctrl1[1]
- x2 = ctrl2[0]
- y2 = ctrl2[1]
- x3 = end[0]
- y3 = end[1]
- bezier = {}
Calculate our X/Y coefficients
- - Cx = parseInt(3 * (x1 - x0), 10)
- Bx = 3 * (x2 - x1) - Cx
- Ax = x3 - x0 - Cx - Bx
-
- Cy = 3 * (y1 - y0)
- By = 3 * (y2 - y1) - Cy
- Ay = y3 - y0 - Cy - By
1000 is actually arbitrary. We need to make sure we do enough -calculations between 0 and 255 that, in even the more extreme -circumstances, we calculate as many values as possible. In the event -that an X value is skipped, it will be found later on using linear -interpolation.
- - for i in [0...1000]
- t = i / 1000
-
- curveX = Math.round (Ax * Math.pow(t, 3)) + (Bx * Math.pow(t, 2)) + (Cx * t) + x0
- curveY = Math.round (Ay * Math.pow(t, 3)) + (By * Math.pow(t, 2)) + (Cy * t) + y0
-
- if lowBound and curveY < lowBound
- curveY = lowBound
- else if highBound and curveY > highBound
- curveY = highBound
-
- bezier[curveX] = curveY
Do a search for missing values in the bezier array and use linear -interpolation to approximate their values
- - if bezier.length < end[0] + 1
- for i in [0..end[0]]
- if not bezier[i]?
- leftCoord = [i-1, bezier[i-1]]
Find the first value to the right. Ideally this loop will break -very quickly.
- - for j in [i..end[0]]
- if bezier[j]?
- rightCoord = [j, bezier[j]]
- break
-
- bezier[i] = leftCoord[1] +
- ((rightCoord[1] - leftCoord[1]) / (rightCoord[0] - leftCoord[0])) *
- (i - leftCoord[0])
Edge case
- - bezier[end[0]] = bezier[end[0] - 1] if not bezier[end[0]]?
-
- return bezier
-
-
-
NodeJS compatibility
- -if exports?
- Root = exports
- Canvas = require 'canvas'
- Image = Canvas.Image
-
- Fiber = require 'fibers'
-
- fs = require 'fs'
-else
- Root = window
Here it begins. Caman is defined. -There are many different initialization for Caman, which are described on the -Basic Usage page.
-Initialization is tricky because we need to make sure everything we need is actually fully -loaded in the DOM before proceeding. When initialized on an image, we need to make sure that the -image is done loading before converting it to a canvas element and writing the pixel data. If we -do this prematurely, the browser will throw a DOM Error, and chaos will ensue. In the event that -we initialize Caman on a canvas element while specifying an image URL, we need to create a new -image element, load the image, then continue with initialization.
-The main goal for Caman was simplicity, so all of this is handled transparently to the end-user.
- -Root.Caman = class Caman
- @version:
- release: "4.1.0"
- date: "2/12/2013"
Debug mode enables console logging
- - @DEBUG: false
Are we in a NodeJS environment?
- - @NodeJS: exports?
Should we check the DOM for images with Caman instructions?
- - @autoload: not Caman.NodeJS
Allow reverting the canvas? -If your JS process is running out of memory, disabling -this could help drastically.
- - @allowRevert: true
Default cross-origin policy
- - @crossOrigin: "anonymous"
-
- @toString: ->
- "Version " + Caman.version.release + ", Released " + Caman.version.date;
Set the URL of the image proxy script
- - @remoteProxy: ""
Change the GET param used with the proxy script if needed
- - @proxyParam: "camanProxyUrl"
-
- @getAttrId: (canvas) ->
- return true if Caman.NodeJS
-
- if typeof canvas is "string"
- canvas = $(canvas)
-
- return null unless canvas? and canvas.getAttribute?
- canvas.getAttribute 'data-caman-id'
-
- constructor: ->
- throw "Invalid arguments" if arguments.length is 0
-
- if @ instanceof Caman
We have to do this to avoid polluting the global scope -because of how Coffeescript binds functions specified -with => and the fact that Caman can be invoked as both -a function and as a 'new' object.
- - @finishInit = @finishInit.bind(@)
- @imageLoaded = @imageLoaded.bind(@)
-
- args = arguments[0]
-
- unless Caman.NodeJS
- id = parseInt Caman.getAttrId(args[0]), 10
- callback = if typeof args[1] is "function"
- args[1]
- else if typeof args[2] is "function"
- args[2]
- else
- ->
-
- if !isNaN(id) and Store.has(id)
- return Store.execute(id, callback)
Every instance gets a unique ID. Makes it much simpler to check if two variables are the -same instance.
- - @id = Util.uniqid.get()
-
- @initializedPixelData = @originalPixelData = null
- @cropCoordinates = x: 0, y: 0
- @cropped = false
- @resized = false
-
- @pixelStack = [] # Stores the pixel layers
- @layerStack = [] # Stores all of the layers waiting to be rendered
- @canvasQueue = [] # Stores all of the canvases to be processed
- @currentLayer = null
- @scaled = false
-
- @analyze = new Analyze @
- @renderer = new Renderer @
-
- @domIsLoaded =>
- @parseArguments(args)
- @setup()
-
- return @
- else
- return new Caman(arguments)
-
- domIsLoaded: (cb) ->
- if Caman.NodeJS
- setTimeout =>
- cb.call(@)
- , 0
- else
- if document.readyState is "complete"
- Log.debug "DOM initialized"
- setTimeout =>
- cb.call(@)
- , 0
- else
- listener = =>
- if document.readyState is "complete"
- Log.debug "DOM initialized"
- cb.call(@)
-
- document.addEventListener "readystatechange", listener, false
All possible combinations:
-1 argument - - Image selector - - Image object - - Canvas selector - - Canvas object
-2 arguments - - Image selector + callback - - Image object + callback - - Canvas selector + URL - - Canvas object + URL
-3 arguments - - Canvas selector + URL + callback - - Canvas object + URL + callback
-NodeJS - - file path - - file object - - file path + callback - - file object + callback
- - parseArguments: (args) ->
- throw "Invalid arguments given" if args.length is 0
Defaults
- - @initObj = null
- @initType = null
- @imageUrl = null
- @callback = ->
First argument is always our canvas/image
- - @setInitObject args[0]
- return if args.length is 1
-
- switch typeof args[1]
- when "string" then @imageUrl = args[1]
- when "function" then @callback = args[1]
-
- return if args.length is 2
-
- @callback = args[2]
-
- if args.length is 4
- @options[key] = val for own key, val of args[4]
-
- setInitObject: (obj) ->
- if Caman.NodeJS
- @initObj = obj
- @initType = 'node'
- return
-
- if typeof obj is "object"
- @initObj = obj
- else
- @initObj = $(obj)
-
- throw "Could not find image or canvas for initialization." unless @initObj?
-
- @initType = @initObj.nodeName.toLowerCase()
-
- setup: ->
- switch @initType
- when "node" then @initNode()
- when "img" then @initImage()
- when "canvas" then @initCanvas()
-
- initNode: ->
- Log.debug "Initializing for NodeJS"
-
- @image = new Image()
- @image.onload = =>
- Log.debug "Image loaded. Width = #{@imageWidth()}, Height = #{@imageHeight()}"
- @canvas = new Canvas @imageWidth(), @imageHeight()
- @finishInit()
-
- @image.onerror = (err) -> throw err
- @image.src = @initObj
-
- initImage: ->
- @image = @initObj
- @canvas = document.createElement 'canvas'
- @context = @canvas.getContext '2d'
- Util.copyAttributes @image, @canvas, except: ['src']
-
- @image.parentNode.replaceChild @canvas, @image
-
- @imageAdjustments()
- @waitForImageLoaded()
-
- initCanvas: ->
- @canvas = @initObj
- @context = @canvas.getContext '2d'
-
- if @imageUrl?
- @image = document.createElement 'img'
- @image.src = @imageUrl
-
- @imageAdjustments()
- @waitForImageLoaded()
- else
- @finishInit()
-
- imageAdjustments: ->
- if @needsHiDPISwap()
- Log.debug @image.src, "->", @hiDPIReplacement()
-
- @swapped = true
- @image.src = @hiDPIReplacement()
-
- if IO.isRemote(@image)
- @image.src = IO.proxyUrl(@image.src)
- Log.debug "Remote image detected, using URL = #{@image.src}"
-
- waitForImageLoaded: ->
- if @isImageLoaded()
- @imageLoaded()
- else
- @image.onload = @imageLoaded
-
- isImageLoaded: ->
- return false unless @image.complete
- return false if @image.naturalWidth? and @image.naturalWidth is 0
- return true
Internet Explorer has issues figuring out image dimensions when they aren't -explicitly defined apparently. We check the normal width/height properties first, -but fall back to natural sizes if they are 0.
- - imageWidth: -> @image.width or @image.naturalWidth
- imageHeight: -> @image.height or @image.naturalHeight
-
- imageLoaded: ->
- Log.debug "Image loaded. Width = #{@imageWidth()}, Height = #{@imageHeight()}"
-
- if @swapped
- @canvas.width = @imageWidth() / @hiDPIRatio()
- @canvas.height = @imageHeight() / @hiDPIRatio()
- else
- @canvas.width = @imageWidth()
- @canvas.height = @imageHeight()
-
- @finishInit()
-
- finishInit: ->
- @context = @canvas.getContext '2d' unless @context?
-
- @originalWidth = @preScaledWidth = @width = @canvas.width
- @originalHeight = @preScaledHeight = @height = @canvas.height
-
- @hiDPIAdjustments()
- @assignId() unless @hasId()
-
- if @image?
- @context.drawImage @image,
- 0, 0,
- @imageWidth(), @imageHeight(),
- 0, 0,
- @preScaledWidth, @preScaledHeight
-
- @reloadCanvasData()
-
- if Caman.allowRevert
- @initializedPixelData = Util.dataArray(@pixelData.length)
- @originalPixelData = Util.dataArray(@pixelData.length)
-
- for pixel, i in @pixelData
- @initializedPixelData[i] = pixel
- @originalPixelData[i] = pixel
-
- @dimensions =
- width: @canvas.width
- height: @canvas.height
-
- Store.put @id, @
-
- @callback.call @,@
Reset the callback so re-initialization doesn't -trigger it again.
- - @callback = ->
If you have a separate context reference to this canvas outside of CamanJS -and you make a change to the canvas outside of CamanJS, you will have to call -this function to update our context reference to include those changes.
- - reloadCanvasData: ->
- @imageData = @context.getImageData 0, 0, @canvas.width, @canvas.height
- @pixelData = @imageData.data
-
- resetOriginalPixelData: ->
- throw "Revert disabled" unless Caman.allowRevert
-
- @originalPixelData = Util.dataArray(@pixelData.length)
- @originalPixelData.push pixel for pixel in @pixelData
-
- hasId: -> Caman.getAttrId(@canvas)?
-
- assignId: ->
- return if Caman.NodeJS or @canvas.getAttribute 'data-caman-id'
- @canvas.setAttribute 'data-caman-id', @id
-
- hiDPIDisabled: ->
- @canvas.getAttribute('data-caman-hidpi-disabled') isnt null
-
- hiDPIAdjustments: ->
- return if Caman.NodeJS or @hiDPIDisabled()
-
- ratio = @hiDPIRatio()
-
- if ratio isnt 1
- Log.debug "HiDPI ratio = #{ratio}"
- @scaled = true
-
- @preScaledWidth = @canvas.width
- @preScaledHeight = @canvas.height
-
- @canvas.width = @preScaledWidth * ratio
- @canvas.height = @preScaledHeight * ratio
- @canvas.style.width = "#{@preScaledWidth}px"
- @canvas.style.height = "#{@preScaledHeight}px"
-
- @context.scale ratio, ratio
-
- @width = @originalWidth = @canvas.width
- @height = @originalHeight = @canvas.height
-
- hiDPIRatio: ->
- devicePixelRatio = window.devicePixelRatio or 1
- backingStoreRatio = @context.webkitBackingStorePixelRatio or
- @context.mozBackingStorePixelRatio or
- @context.msBackingStorePixelRatio or
- @context.oBackingStorePixelRatio or
- @context.backingStorePixelRatio or 1
-
- devicePixelRatio / backingStoreRatio
-
- hiDPICapable: -> window.devicePixelRatio? and window.devicePixelRatio isnt 1
-
- needsHiDPISwap: ->
- return false if @hiDPIDisabled() or !@hiDPICapable()
- @hiDPIReplacement() isnt null
-
- hiDPIReplacement: ->
- return null unless @image?
- @image.getAttribute 'data-caman-hidpi'
-
- replaceCanvas: (newCanvas) ->
- oldCanvas = @canvas
- @canvas = newCanvas
- @context = @canvas.getContext '2d'
-
- oldCanvas.parentNode.replaceChild @canvas, oldCanvas
-
- @width = @canvas.width
- @height = @canvas.height
-
- @reloadCanvasData()
-
- @dimensions =
- width: @canvas.width
- height: @canvas.height
Begins the rendering process
- - render: (callback = ->) ->
- Event.trigger @, "renderStart"
-
- @renderer.execute =>
- @context.putImageData @imageData, 0, 0
- callback.call @
Reverts the canvas back to it's original state while -maintaining any cropped or resized dimensions.
- - revert: ->
- throw "Revert disabled" unless Caman.allowRevert
-
- @pixelData[i] = pixel for pixel, i in @originalVisiblePixels()
- @context.putImageData @imageData, 0, 0
Completely resets the canvas back to it's original state. -Any size adjustments will also be reset.
- - reset: ->
- canvas = document.createElement('canvas')
- Util.copyAttributes(@canvas, canvas)
-
- canvas.width = @originalWidth
- canvas.height = @originalHeight
-
- ctx = canvas.getContext('2d')
- imageData = ctx.getImageData 0, 0, canvas.width, canvas.height
- pixelData = imageData.data
-
- pixelData[i] = pixel for pixel, i in @initializedPixelData
-
- ctx.putImageData imageData, 0, 0
-
- @cropCoordinates = x: 0, y: 0
- @resized = false
-
- @replaceCanvas(canvas)
Returns the original pixel data while maintaining any -cropping or resizing that may have occured.
- - originalVisiblePixels: ->
- throw "Revert disabled" unless Caman.allowRevert
-
- pixels = []
-
- startX = @cropCoordinates.x
- endX = startX + @width
- startY = @cropCoordinates.y
- endY = startY + @height
-
- if @resized
- canvas = document.createElement('canvas')
- canvas.width = @originalWidth
- canvas.height = @originalHeight
-
- ctx = canvas.getContext('2d')
- imageData = ctx.getImageData 0, 0, canvas.width, canvas.height
- pixelData = imageData.data
-
- pixelData[i] = pixel for pixel, i in @originalPixelData
-
- ctx.putImageData imageData, 0, 0
-
- scaledCanvas = document.createElement('canvas')
- scaledCanvas.width = @width
- scaledCanvas.height = @height
-
- ctx = scaledCanvas.getContext('2d')
- ctx.drawImage canvas, 0, 0, @originalWidth, @originalHeight, 0, 0, @width, @height
-
- pixelData = ctx.getImageData(0, 0, @width, @height).data
- width = @width
- else
- pixelData = @originalPixelData
- width = @originalWidth
-
- for i in [0...pixelData.length] by 4
- coord = PixelInfo.locationToCoordinates(i, width)
- if (startX <= coord.x < endX) and (startY <= coord.y < endY)
- pixels.push pixelData[i],
- pixelData[i+1],
- pixelData[i+2],
- pixelData[i+3]
-
- pixels
Pushes the filter callback that modifies the RGBA object into the -render queue
- - process: (name, processFn) ->
- @renderer.add
- type: Filter.Type.Single
- name: name
- processFn: processFn
-
- return @
Pushes the kernel into the render queue
- - processKernel: (name, adjust, divisor, bias) ->
- if not divisor
- divisor = 0
- divisor += adjust[i] for i in [0...adjust.length]
-
- @renderer.add
- type: Filter.Type.Kernel
- name: name
- adjust: adjust
- divisor: divisor
- bias: bias or 0
-
- return @
Adds a standalone plugin into the render queue
- - processPlugin: (plugin, args) ->
- @renderer.add
- type: Filter.Type.Plugin
- plugin: plugin
- args: args
-
- return @
Pushes a new layer operation into the render queue and calls the layer -callback
- - newLayer: (callback) ->
- layer = new Layer @
- @canvasQueue.push layer
- @renderer.add type: Filter.Type.LayerDequeue
-
- callback.call layer
-
- @renderer.add type: Filter.Type.LayerFinished
- return @
Pushes the layer context and moves to the next operation
- - executeLayer: (layer) -> @pushContext layer
Set all of the relevant data to the new layer
- - pushContext: (layer) ->
- @layerStack.push @currentLayer
- @pixelStack.push @pixelData
- @currentLayer = layer
- @pixelData = layer.pixelData
Restore the previous layer context
- - popContext: ->
- @pixelData = @pixelStack.pop()
- @currentLayer = @layerStack.pop()
Applies the current layer to its parent layer
- - applyCurrentLayer: -> @currentLayer.applyToParent()
-
-
vignetteFilters =
- brightness: (rgba, amt, opts) ->
- rgba.r = rgba.r - (rgba.r * amt * opts.strength)
- rgba.g = rgba.g - (rgba.g * amt * opts.strength)
- rgba.b = rgba.b - (rgba.b * amt * opts.strength)
- rgba
-
- gamma: (rgba, amt, opts) ->
- rgba.r = Math.pow(rgba.r / 255, Math.max(10 * amt * opts.strength, 1)) * 255
- rgba.g = Math.pow(rgba.g / 255, Math.max(10 * amt * opts.strength, 1)) * 255
- rgba.b = Math.pow(rgba.b / 255, Math.max(10 * amt * opts.strength, 1)) * 255
- rgba
-
- colorize: (rgba, amt, opts) ->
- rgba.r -= (rgba.r - opts.color.r) * amt;
- rgba.g -= (rgba.g - opts.color.g) * amt;
- rgba.b -= (rgba.b - opts.color.b) * amt;
- rgba
-
-Filter.register "vignette", (size, strength = 60) ->
- if typeof size is "string" and size.substr(-1) is "%"
- if @dimensions.height > @dimensions.width
- size = @dimensions.width * (parseInt(size.substr(0, size.length - 1), 10) / 100)
- else
- size = @dimensions.height * (parseInt(size.substr(0, size.length - 1), 10) / 100)
-
- strength /= 100
- center = [@dimensions.width / 2, @dimensions.height / 2]
- start = Math.sqrt Math.pow(center[0], 2) + Math.pow(center[1], 2)
- end = start - size
- bezier = Calculate.bezier [0, 1], [30, 30], [70, 60], [100, 80]
-
- @process "vignette", (rgba) ->
- loc = @locationXY()
- dist = Calculate.distance loc.x, loc.y, center[0], center[1]
-
- if dist > end
- div = Math.max 1, ((bezier[Math.round(((dist - end) / size) * 100)]/10) * strength)
-
- rgba.r = Math.pow(rgba.r / 255, div) * 255
- rgba.g = Math.pow(rgba.g / 255, div) * 255
- rgba.b = Math.pow(rgba.b / 255, div) * 255
-
- rgba
-
-Filter.register "rectangularVignette", (opts) ->
- defaults =
- strength: 50
- cornerRadius: 0
- method: 'brightness'
- color:
- r: 0
- g: 0
- b: 0
-
- opts = Util.extend defaults, opts
-
- if not opts.size
- return @
- else if typeof opts.size is "string"
- percent = parseInt(opts.size, 10) / 100
- opts.size =
- width: @dimensions.width * percent
- height: @dimensions.height * percent
- else if typeof opts.size is "object"
- for dim in ["width", "height"]
- if typeof opts.size[dim] is "string"
- opts.size[dim] = @dimensions[dim] * (parseInt(opts.size[dim], 10) / 100)
- else if opts.size is "number"
- size = opts.size
- opts.size =
- width: size
- height: size
-
- if typeof opts.cornerRadius is "string"
- opts.cornerRadius = (opts.size.width / 2) * (parseInt(opts.cornerRadius, 10) / 100)
-
- opts.strength /= 100
Since pixels are discreet, force size to be an int
- - opts.size.width = Math.floor opts.size.width
- opts.size.height = Math.floor opts.size.height
- opts.image =
- width: @dimensions.width
- height: @dimensions.height
-
- if opts.method is "colorize" and typeof opts.color is "string"
- opts.color = Convert.hexToRGB opts.color
-
- opts.coords =
- left: (@dimensions.width - opts.size.width) / 2
- right: @dimensions.width - opts.coords.left
- bottom: (@dimensions.height - opts.size.height) / 2
- top: @dimensions.height - opts.coords.bottom
-
- opts.corners = [
- {x: opts.coords.left + opts.cornerRadius, y: opts.coords.top - opts.cornerRadius},
- {x: opts.coords.right - opts.cornerRadius, y: opts.coords.top - opts.cornerRadius},
- {x: opts.coords.right - opts.cornerRadius, y: opts.coords.bottom + opts.cornerRadius},
- {x: opts.coords.left + opts.cornerRadius, y: opts.coords.bottom + opts.cornerRadius}
- ]
-
- opts.maxDist = Calculate.distance(0, 0, opts.corners[3].x, opts.corners[3].y) - opts.cornerRadius
-
- @process "rectangularVignette", (rgba) ->
- loc = @locationXY()
Trivial rejects
- - if (loc.x > opts.corners[0].x and loc.x < opts.corners[1].x) and (loc.y > opts.coords.bottom and loc.y < opts.coords.top)
- return rgba
- if (loc.x > opts.coords.left && loc.x < opts.coords.right) && (loc.y > opts.corners[3].y && loc.y < opts.corners[2].y)
- return rgba
Need to figure out which section we're in. First, the easy ones, then the harder ones.
- - if loc.x > opts.corners[0].x && loc.x < opts.corners[1].x && loc.y > opts.coords.top
top-middle section
- - amt = (loc.y - opts.coords.top) / opts.maxDist
- else if loc.y > opts.corners[2].y && loc.y < opts.corners[1].y && loc.x > opts.coords.right
right-middle section
- - amt = (loc.x - opts.coords.right) / opts.maxDist
- else if loc.x > opts.corners[0].x && loc.x < opts.corners[1].x && loc.y < opts.coords.bottom
bottom-middle section
- - amt = (opts.coords.bottom - loc.y) / opts.maxDist
- else if loc.y > opts.corners[2].y && loc.y < opts.corners[1].y && loc.x < opts.coords.left
left-middle section
- - amt = (opts.coords.left - loc.x) / opts.maxDist
- else if loc.x <= opts.corners[0].x && loc.y >= opts.corners[0].y
top-left corner
- - radialDist = Caman.distance(loc.x, loc.y, opts.corners[0].x, opts.corners[0].y)
- amt = (radialDist - opts.cornerRadius) / opts.maxDist
- else if loc.x >= opts.corners[1].x && loc.y >= opts.corners[1].y
top-right corner
- - radialDist = Caman.distance(loc.x, loc.y, opts.corners[1].x, opts.corners[1].y)
- amt = (radialDist - opts.cornerRadius) / opts.maxDist
- else if loc.x >= opts.corners[2].x && loc.y <= opts.corners[2].y
bottom-right corner
- - radialDist = Caman.distance(loc.x, loc.y, opts.corners[2].x, opts.corners[2].y)
- amt = (radialDist - opts.cornerRadius) / opts.maxDist
- else if loc.x <= opts.corners[3].x && loc.y <= opts.corners[3].y
bottom-left corner
- - radialDist = Caman.distance(loc.x, loc.y, opts.corners[3].x, opts.corners[3].y)
- amt = (radialDist - opts.cornerRadius) / opts.maxDist
-
- return rgba if amt < 0
- return vignetteFilters[opts.method](rgba, amt, opts)
-
-
-
CompoundBlur - Blurring with varying radii for Canvas
-
-Version: 0.1
-Author: Mario Klingemann
-Contact: mario@quasimondo.com
-Website: http://www.quasimondo.com/StackBlurForCanvas
-Twitter: @quasimondo
-Modified By: Ryan LeFevre (@meltingice)
-
-In case you find this class useful - especially in commercial projects -
-I am not totally unhappy for a small donation to my PayPal account
-mario@quasimondo.de
-
-Copyright (c) 2011 Mario Klingemann
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
Wrapping this in a closure since there's a bunch of extra functions this plugin requires -and we don't want them clogging up the global scope.
- -do ->
- mul_table = [
- 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259]
-
-
- shg_table = [
- 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24]
-
- getLinearGradientMap = (width, height, centerX, centerY, angle, length, mirrored) ->
- cnv = if exports? then new Canvas() else document.createElement('canvas')
- cnv.width = width
- cnv.height = height
-
- x1 = centerX + Math.cos(angle) * length * 0.5
- y1 = centerY + Math.sin(angle) * length * 0.5
-
- x2 = centerX - Math.cos(angle) * length * 0.5
- y2 = centerY - Math.sin(angle) * length * 0.5
-
- context = cnv.getContext("2d")
- gradient = context.createLinearGradient(x1, y1, x2, y2)
- if not mirrored
- gradient.addColorStop(0, "white")
- gradient.addColorStop(1, "black")
- else
- gradient.addColorStop(0, "white")
- gradient.addColorStop(0.5, "black")
- gradient.addColorStop(1, "white")
-
- context.fillStyle = gradient
- context.fillRect(0, 0, width, height)
- return context.getImageData(0, 0, width, height)
-
- getRadialGradientMap = (width, height, centerX, centerY, radius1, radius2) ->
- cnv = if exports? then new Canvas() else document.createElement('canvas')
- cnv.width = width
- cnv.height = height
-
- context = cnv.getContext("2d")
- gradient = context.createRadialGradient(centerX, centerY, radius1, centerX, centerY, radius2)
-
- gradient.addColorStop(1, "white")
- gradient.addColorStop(0, "black")
-
- context.fillStyle = gradient
- context.fillRect(0, 0, width, height)
- return context.getImageData(0, 0, width, height)
-
- BlurStack = ->
- @r = 0
- @g = 0
- @b = 0
- @a = 0
- @next = null
-
- Caman.Plugin.register "compoundBlur", (radiusData, radius, increaseFactor, blurLevels) ->
- width = @dimensions.width
- height = @dimensions.height
-
- imagePixels = @pixelData
- radiusPixels = radiusData.data
-
- wh = width * height
- wh4 = wh << 2
- pixels = []
-
- pixels[i] = imagePixels[i] for i in [0...wh4]
-
- currentIndex = 0
- steps = blurLevels
- blurLevels -= 1
-
- while steps-- >= 0
- iradius = (radius + 0.5) | 0
- continue if iradius is 0
- iradius = 256 if iradius > 256
-
- div = iradius + iradius + 1
- w4 = width << 2
- widthMinus1 = width - 1
- heightMinus1 = height - 1
- radiusPlus1 = iradius + 1
- sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2
-
- stackStart = new BlurStack()
- stackEnd = undefined
- stack = stackStart
-
- for i in [1...div]
- stack = stack.next = new BlurStack()
- stackEnd = stack if i is radiusPlus1
-
- stack.next = stackStart
- stackIn = null
- stackOut = null
-
- yw = yi = 0
-
- mul_sum = mul_table[iradius]
- shg_sum = shg_table[iradius]
-
- for y in [0...height]
- r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0
-
- r_out_sum = radiusPlus1 * (pr = pixels[yi])
- g_out_sum = radiusPlus1 * (pg = pixels[yi + 1])
- b_out_sum = radiusPlus1 * (pb = pixels[yi + 2])
-
- r_sum += sumFactor * pr
- g_sum += sumFactor * pg
- b_sum += sumFactor * pb
-
- stack = stackStart
-
- for i in [0...radiusPlus1]
- stack.r = pr
- stack.g = pg
- stack.b = pb
- stack = stack.next
-
- for i in [1...radiusPlus1]
- p = yi + ((if widthMinus1 < i then widthMinus1 else i) << 2)
- r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i)
- g_sum += (stack.g = (pg = pixels[p + 1])) * rbs
- b_sum += (stack.b = (pb = pixels[p + 2])) * rbs
-
- r_in_sum += pr
- g_in_sum += pg
- b_in_sum += pb
-
- stack = stack.next
-
- stackIn = stackStart
- stackOut = stackEnd
-
- for x in [0...width]
- pixels[yi] = (r_sum * mul_sum) >> shg_sum
- pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum
- pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum
-
- r_sum -= r_out_sum
- g_sum -= g_out_sum
- b_sum -= b_out_sum
-
- r_out_sum -= stackIn.r
- g_out_sum -= stackIn.g
- b_out_sum -= stackIn.b
-
- p = (yw + (if (p = x + radiusPlus1) < widthMinus1 then p else widthMinus1)) << 2
-
- r_in_sum += (stackIn.r = pixels[p])
- g_in_sum += (stackIn.g = pixels[p + 1])
- b_in_sum += (stackIn.b = pixels[p + 2])
-
- r_sum += r_in_sum
- g_sum += g_in_sum
- b_sum += b_in_sum
-
- stackIn = stackIn.next
-
- r_out_sum += (pr = stackOut.r)
- g_out_sum += (pg = stackOut.g)
- b_out_sum += (pb = stackOut.b)
-
- r_in_sum -= pr
- g_in_sum -= pg
- b_in_sum -= pb
-
- stackOut = stackOut.next
-
- yi += 4
-
- yw += width
-
- for x in [0...width]
- g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0
-
- yi = x << 2
- r_out_sum = radiusPlus1 * (pr = pixels[yi])
- g_out_sum = radiusPlus1 * (pg = pixels[yi + 1])
- b_out_sum = radiusPlus1 * (pb = pixels[yi + 2])
-
- r_sum += sumFactor * pr
- g_sum += sumFactor * pg
- b_sum += sumFactor * pb
-
- stack = stackStart
-
- for i in [0...radiusPlus1]
- stack.r = pr
- stack.g = pg
- stack.b = pb
- stack = stack.next
-
- yp = width
-
- for i in [1...radiusPlus1]
- yi = (yp + x) << 2
- r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i)
- g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs
- b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs
- r_in_sum += pr
- g_in_sum += pg
- b_in_sum += pb
- stack = stack.next
- yp += width if i < heightMinus1
-
- yi = x
- stackIn = stackStart
- stackOut = stackEnd
-
- for y in [0...height]
- p = yi << 2
- pixels[p] = (r_sum * mul_sum) >> shg_sum
- pixels[p + 1] = (g_sum * mul_sum) >> shg_sum
- pixels[p + 2] = (b_sum * mul_sum) >> shg_sum
-
- r_sum -= r_out_sum
- g_sum -= g_out_sum
- b_sum -= b_out_sum
-
- r_out_sum -= stackIn.r
- g_out_sum -= stackIn.g
- b_out_sum -= stackIn.b
-
- p = (x + ((if (p = y + radiusPlus1) < heightMinus1 then p else heightMinus1) * width)) << 2
-
- r_sum += (r_in_sum += (stackIn.r = pixels[p]))
- g_sum += (g_in_sum += (stackIn.g = pixels[p + 1]))
- b_sum += (b_in_sum += (stackIn.b = pixels[p + 2]))
-
- stackIn = stackIn.next
-
- r_out_sum += (pr = stackOut.r)
- g_out_sum += (pg = stackOut.g)
- b_out_sum += (pb = stackOut.b)
-
- r_in_sum -= pr
- g_in_sum -= pg
- b_in_sum -= pb
-
- stackOut = stackOut.next
-
- yi += width
-
- radius *= increaseFactor
-
- i = wh
- while --i > -1
- idx = i << 2
- lookupValue = (radiusPixels[idx + 2] & 0xff) / 255.0 * blurLevels
- index = lookupValue | 0
-
- if index is currentIndex
- blend = 256.0 * (lookupValue - (lookupValue | 0))
- iblend = 256 - blend
-
- imagePixels[idx] = (imagePixels[idx] * iblend + pixels[idx] * blend) >> 8
- imagePixels[idx + 1] = (imagePixels[idx + 1] * iblend + pixels[idx + 1] * blend) >> 8
- imagePixels[idx + 2] = (imagePixels[idx + 2] * iblend + pixels[idx + 2] * blend) >> 8
- else if index is currentIndex + 1
- imagePixels[idx] = pixels[idx]
- imagePixels[idx + 1] = pixels[idx + 1]
- imagePixels[idx + 2] = pixels[idx + 2]
- currentIndex++
-
- return @
-
- Caman.Filter.register "tiltShift", (opts) ->
- defaults =
- center:
- x: @dimensions.width / 2
- y: @dimensions.height / 2
- angle: 45
- focusWidth: 200
- startRadius: 3
- radiusFactor: 1.5
- steps: 3
-
- opts = Util.extend defaults, opts
- opts.angle *= Math.PI / 180
- gradient = getLinearGradientMap(@dimensions.width, @dimensions.height, opts.center.x, opts.center.y, opts.angle, opts.focusWidth, true)
-
- @processPlugin "compoundBlur", [gradient, opts.startRadius, opts.radiusFactor, opts.steps]
-
- Caman.Filter.register "radialBlur", (opts) ->
- defaults =
- size: 50
- center:
- x: @dimensions.width / 2
- y: @dimensions.height / 2
- startRadius: 3
- radiusFactor: 1.5
- steps: 3
- radius: null
-
- opts = Util.extend defaults, opts
-
- if not opts.radius
- opts.radius = if @dimensions.width < @dimensions.height then @dimensions.height else @dimensions.width
-
- radius1 = (opts.radius / 2) - opts.size
- radius2 = (opts.radius / 2)
-
- gradient = getRadialGradientMap(@dimensions.width, @dimensions.height, opts.center.x, opts.center.y, radius1, radius2)
-
- @processPlugin "compoundBlur", [gradient, opts.startRadius, opts.radiusFactor, opts.steps]
-
-
-
Tons of color conversion functions
- -class Convert
Converts the hex representation of a color to RGB values. -Hex value can optionally start with the hash (#).
--@param String hex The colors hex value -@return Array The RGB representation -- - -
@hexToRGB: (hex) ->
- hex = hex.substr(1) if hex.charAt(0) is "#"
- r = parseInt hex.substr(0, 2), 16
- g = parseInt hex.substr(2, 2), 16
- b = parseInt hex.substr(4, 2), 16
-
- r: r, g: g, b: b
Converts an RGB color to HSL. -Assumes r, g, and b are in the set [0, 255] and -returns h, s, and l in the set [0, 1].
--@param Number r Red channel -@param Number g Green channel -@param Number b Blue channel -@return The HSL representation -- - -
@rgbToHSL: (r, g, b) ->
- if typeof r is "object"
- g = r.g
- b = r.b
- r = r.r
-
- r /= 255
- g /= 255
- b /= 255
-
- max = Math.max r, g, b
- min = Math.min r, g, b
- l = (max + min) / 2
-
- if max is min
- h = s = 0
- else
- d = max - min
- s = if l > 0.5 then d / (2 - max - min) else d / (max + min)
- h = switch max
- when r then (g - b) / d + (if g < b then 6 else 0)
- when g then (b - r) / d + 2
- when b then (r - g) / d + 4
-
- h /= 6
-
- h: h, s: s, l: l
Converts an HSL color value to RGB. Conversion formula -adapted from http://en.wikipedia.org/wiki/HSL_color_space. -Assumes h, s, and l are contained in the set [0, 1] and -returns r, g, and b in the set [0, 255].
--@param Number h The hue -@param Number s The saturation -@param Number l The lightness -@return Array The RGB representation -- - -
@hslToRGB: (h, s, l) ->
- if typeof h is "object"
- s = h.s
- l = h.l
- h = h.h
-
- if s is 0
- r = g = b = l
- else
- q = if l < 0.5 then l * (1 + s) else l + s - l * s
- p = 2 * l - q
-
- r = @hueToRGB p, q, h + 1/3
- g = @hueToRGB p, q, h
- b = @hueToRGB p, q, h - 1/3
-
- r: r * 255, g: g * 255, b: b * 255
Converts from the hue color space back to RGB
- - @hueToRGB: (p, q, t) ->
- if t < 0 then t += 1
- if t > 1 then t -= 1
- if t < 1/6 then return p + (q - p) * 6 * t
- if t < 1/2 then return q
- if t < 2/3 then return p + (q - p) * (2/3 - t) * 6
- return p
Converts an RGB color value to HSV. Conversion formula -adapted from http://en.wikipedia.org/wiki/HSV_color_space. -Assumes r, g, and b are contained in the set [0, 255] and -returns h, s, and v in the set [0, 1].
--@param Number r The red color value -@param Number g The green color value -@param Number b The blue color value -@return Array The HSV representation -- - -
@rgbToHSV: (r, g, b) ->
- r /= 255
- g /= 255
- b /= 255
-
- max = Math.max r, g, b
- min = Math.min r, g, b
- v = max
- d = max - min
-
- s = if max is 0 then 0 else d / max
-
- if max is min
- h = 0
- else
- h = switch max
- when r then (g - b) / d + (if g < b then 6 else 0)
- when g then (b - r) / d + 2
- when b then (r - g) / d + 4
-
- h /= 6
-
- h: h, s: s, v: v
Converts an HSV color value to RGB. Conversion formula -adapted from http://en.wikipedia.org/wiki/HSV_color_space. -Assumes h, s, and v are contained in the set [0, 1] and -returns r, g, and b in the set [0, 255].
--@param Number h The hue -@param Number s The saturation -@param Number v The value -@return Array The RGB representation -- - -
@hsvToRGB: (h, s, v) ->
- i = Math.floor h * 6
- f = h * 6 - i
- p = v * (1 - s)
- q = v * (1 - f * s)
- t = v * (1 - (1 - f) * s)
-
- switch i % 6
- when 0
- r = v
- g = t
- b = p
- when 1
- r = q
- g = v
- b = p
- when 2
- r = p
- g = v
- b = t
- when 3
- r = p
- g = q
- b = v
- when 4
- r = t
- g = p
- b = v
- when 5
- r = v
- g = p
- b = q
-
- r: r * 255, g: g * 255, b: b * 255
Converts a RGB color value to the XYZ color space. Formulas -are based on http://en.wikipedia.org/wiki/SRGB assuming that -RGB values are sRGB.
-Assumes r, g, and b are contained in the set [0, 255] and -returns x, y, and z.
--@param Number r The red color value -@param Number g The green color value -@param Number b The blue color value -@return Array The XYZ representation -- - -
@rgbToXYZ: (r, g, b) ->
- r /= 255
- g /= 255
- b /= 255
-
- if r > 0.04045
- r = Math.pow((r + 0.055) / 1.055, 2.4)
- else
- r /= 12.92
-
- if g > 0.04045
- g = Math.pow((g + 0.055) / 1.055, 2.4)
- else
- g /= 12.92
-
- if b > 0.04045
- b = Math.pow((b + 0.055) / 1.055, 2.4)
- else
- b /= 12.92
-
- x = r * 0.4124 + g * 0.3576 + b * 0.1805;
- y = r * 0.2126 + g * 0.7152 + b * 0.0722;
- z = r * 0.0193 + g * 0.1192 + b * 0.9505;
-
- x: x * 100, y: y * 100, z: z * 100
Converts a XYZ color value to the sRGB color space. Formulas -are based on http://en.wikipedia.org/wiki/SRGB and the resulting -RGB value will be in the sRGB color space. -Assumes x, y and z values are whatever they are and returns -r, g and b in the set [0, 255].
--@param Number x The X value -@param Number y The Y value -@param Number z The Z value -@return Array The RGB representation -- - -
@xyzToRGB: (x, y, z) ->
- x /= 100
- y /= 100
- z /= 100
-
- r = (3.2406 * x) + (-1.5372 * y) + (-0.4986 * z)
- g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z)
- b = (0.0557 * x) + (-0.2040 * y) + (1.0570 * z)
-
- if r > 0.0031308
- r = (1.055 * Math.pow(r, 0.4166666667)) - 0.055
- else
- r *= 12.92
-
- if g > 0.0031308
- g = (1.055 * Math.pow(g, 0.4166666667)) - 0.055
- else
- g *= 12.92
-
- if b > 0.0031308
- b = (1.055 * Math.pow(b, 0.4166666667)) - 0.055
- else
- b *= 12.92
-
- r: r * 255, g: g * 255, b: b * 255
Converts a XYZ color value to the CIELAB color space. Formulas -are based on http://en.wikipedia.org/wiki/Lab_color_space -The reference white point used in the conversion is D65. -Assumes x, y and z values are whatever they are and returns -L, a and b* values
--@param Number x The X value -@param Number y The Y value -@param Number z The Z value -@return Array The Lab representation -- - -
@xyzToLab: (x, y, z) ->
- if typeof x is "object"
- y = x.y
- z = x.z
- x = x.x
-
- whiteX = 95.047
- whiteY = 100.0
- whiteZ = 108.883
-
- x /= whiteX
- y /= whiteY
- z /= whiteZ
-
- if x > 0.008856451679
- x = Math.pow(x, 0.3333333333)
- else
- x = (7.787037037 * x) + 0.1379310345
-
- if y > 0.008856451679
- y = Math.pow(y, 0.3333333333)
- else
- y = (7.787037037 * y) + 0.1379310345
-
- if z > 0.008856451679
- z = Math.pow(z, 0.3333333333)
- else
- z = (7.787037037 * z) + 0.1379310345
-
- l = 116 * y - 16
- a = 500 * (x - y)
- b = 200 * (y - z)
-
- l: l, a: a, b: b
Converts a L, a, b* color values from the CIELAB color space -to the XYZ color space. Formulas are based on -http://en.wikipedia.org/wiki/Lab_color_space
-The reference white point used in the conversion is D65. -Assumes L, a and b* values are whatever they are and returns -x, y and z values.
--@param Number l The L* value -@param Number a The a* value -@param Number b The b* value -@return Array The XYZ representation -- - -
@labToXYZ: (l, a, b) ->
- if typeof l is "object"
- a = l.a
- b = l.b
- l = l.l
-
- y = (l + 16) / 116
- x = y + (a / 500)
- z = y - (b / 200)
-
- if x > 0.2068965517
- x = x * x * x
- else
- x = 0.1284185493 * (x - 0.1379310345)
-
- if y > 0.2068965517
- y = y * y * y
- else
- y = 0.1284185493 * (y - 0.1379310345)
-
- if z > 0.2068965517
- z = z * z * z
- else
- z = 0.1284185493 * (z - 0.1379310345)
D65 reference white point
- - x: x * 95.047, y: y * 100.0, z: z * 108.883
-
- @rgbToLab: (r, g, b) ->
- if typeof r is "object"
- g = r.g
- b = r.b
- r = r.r
-
- xyz = @rgbToXYZ(r, g, b)
- @xyzToLab xyz
-
- @labToRGB: (l, a, b) ->
-
-
-
Caman.Filter.register "edgeEnhance", ->
- @processKernel "Edge Enhance", [
- 0, 0, 0,
- -1, 1, 0,
- 0, 0, 0
- ]
-
-Caman.Filter.register "edgeDetect", ->
- @processKernel "Edge Detect", [
- -1, -1, -1,
- -1, 8, -1,
- -1, -1, -1
- ]
-
-Caman.Filter.register "emboss", ->
- @processKernel "Emboss", [
- -2, -1, 0,
- -1, 1, 1,
- 0, 1, 2
- ]
-
-
Event system that can be used to register callbacks that get fired -during certain times in the render process.
- -class Event
- @events = {}
All of the supported event types
- - @types = [
- "processStart"
- "processComplete"
- "renderStart"
- "renderFinished"
- "blockStarted"
- "blockFinished"
- ]
Trigger an event
- - @trigger: (target, type, data) ->
- if @events[type] and @events[type].length
- for event in @events[type]
- if event.target is null or target.id is event.target.id
- event.fn.call target, data
-
Listen for an event. Optionally bind the listen to a single CamanInstance -or all CamanInstances.
- - @listen: (target, type, fn) ->
Adjust arguments if target is omitted
- - if typeof target is "string"
- _type = target
- _fn = type
-
- target = null
- type = _type
- fn = _fn
Validation
- - return false if type not in @types
-
- @events[type] = [] if not @events[type]
- @events[type].push target: target, fn: fn
-
- return true
-
-Caman.Event = Event
-
-
Responsible for storing all of the filters
- -Caman.Filter = class Filter
All of the different render operatives
- - @Type =
- Single: 1
- Kernel: 2
- LayerDequeue: 3
- LayerFinished: 4
- LoadOverlay: 5
- Plugin: 6
Registers a filter function
- - @register: (name, filterFunc) -> Caman::[name] = filterFunc
-
-
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.
-Fills the canvas with a single solid color.
-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
Simple brightness adjustment
-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
Adjusts the color saturation of the image.
-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
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.
-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
-
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
Increases or decreases the color contrast of the image.
-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
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.
-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
-
- rgb = Convert.hsvToRGB hsv.h, hsv.s, hsv.v
- rgb.a = rgba.a
- rgb
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.
-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
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
-
Applies an adjustable sepia filter to the image.
-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
Adjusts the gamma of the image.
-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
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
Clips a color to max values when it falls outside of the specified range.
-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
Lets you modify the intensity of any combination of red, green, or blue channels individually.
-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 implementation using Bezier curve equation. If you're familiar with the Curves -functionality in Photoshop, this works in a very similar fashion.
-- 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
Adjusts the exposure of the image by using the curves function.
-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]
-
-
Various I/O based operations
- -Caman.IO = class IO
- @domainRegex = /(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/
Is the given URL remote? -If a cross-origin setting is set, we assume you have CORS -properly configured.
- - @isRemote: (img) ->
- return false unless img?
- return false if @corsEnabled(img)
- return @isURLRemote img.src
-
- @corsEnabled: (img) ->
- img.crossOrigin? and img.crossOrigin.toLowerCase() in ['anonymous', 'use-credentials']
-
- @isURLRemote: (url) ->
- matches = url.match @domainRegex
- return if matches then matches[1] isnt document.domain else false
-
- @remoteCheck: (src) ->
- if @isURLRemote src
- if not Caman.remoteProxy.length
- Log.info "Attempting to load a remote image without a configured proxy. URL: #{src}"
- return
- else
- if Caman.isURLRemote Caman.remoteProxy
- Log.info "Cannot use a remote proxy for loading images."
- return
-
- "#{Caman.remoteProxy}?camanProxyUrl=#{encodeURIComponent(src)}"
-
-
- @proxyUrl: (src) ->
- "#{Caman.remoteProxy}?#{Caman.proxyParam}=#{encodeURIComponent(src)}"
Shortcut for using one of the bundled proxies.
- - @useProxy: (lang) ->
- langToExt =
- ruby: 'rb'
- python: 'py'
- perl: 'pl'
- javascript: 'js'
-
- lang = lang.toLowerCase()
- lang = langToExt[lang] if langToExt[lang]?
- "proxies/caman_proxy.#{lang}"
-
Grabs the canvas data, encodes it to Base64, then sets the browser location to -the encoded data so that the user will be prompted to download it.
- -Caman::save = ->
- if exports?
- @nodeSave.apply @, arguments
- else
- @browserSave.apply @, arguments
-
-Caman::browserSave = (type = "png") ->
- type = type.toLowerCase()
Force download (its a bit hackish)
- - image = @toBase64(type).replace "image/#{type}", "image/octet-stream"
- document.location.href = image
-
-Caman::nodeSave = (file, overwrite = true) ->
- try
- stats = fs.statSync file
- return false if stats.isFile() and not overwrite
- catch e
- Log.debug "Creating output file #{file}"
-
- fs.writeFile file, @canvas.toBuffer(), ->
- Log.debug "Finished writing to #{file}"
Takes the current canvas data, converts it to Base64, then sets it as the source -of a new Image object and returns it.
- -Caman::toImage = (type) ->
- img = document.createElement 'img'
- img.src = @toBase64 type
- img.width = @dimensions.width
- img.height = @dimensions.height
-
- if window.devicePixelRatio
- img.width /= window.devicePixelRatio
- img.height /= window.devicePixelRatio
-
- return img
Base64 encodes the current canvas
- -Caman::toBase64 = (type = "png") ->
- type = type.toLowerCase()
- return @canvas.toDataURL "image/#{type}"
-
-
The entire layering system for Caman resides in this file. Layers get their own canvasLayer -objectwhich is created when newLayer() is called. For extensive information regarding the -specifics of howthe layering system works, there is an in-depth blog post on this very topic. -Instead of copying the entirety of that post, I'll simply point you towards the -blog link.
-However, the gist of the layering system is that, for each layer, it creates a new canvas -element and then either copies the parent layer's data or applies a solid color to the new -layer. After some (optional) effects are applied, the layer is blended back into the parent -canvas layer using one of many different blending algorithms.
-You can also load an image (local or remote, with a proxy) into a canvas layer, which is useful -if you want to add textures to an image.
- -class Layer
- constructor: (@c) ->
Compatibility
- - @filter = @c
-
- @options =
- blendingMode: 'normal'
- opacity: 1.0
Each layer gets its own unique ID
- - @layerID = Util.uniqid.get()
Create the canvas for this layer
- - @canvas = if exports? then new Canvas() else document.createElement('canvas')
-
- @canvas.width = @c.dimensions.width
- @canvas.height = @c.dimensions.height
-
- @context = @canvas.getContext('2d')
- @context.createImageData @canvas.width, @canvas.height
- @imageData = @context.getImageData 0, 0, @canvas.width, @canvas.height
- @pixelData = @imageData.data
If you want to create nested layers
- - newLayer: (cb) -> @c.newLayer.call @c, cb
Sets the blending mode of this layer. The mode is the name of a blender function.
- - setBlendingMode: (mode) ->
- @options.blendingMode = mode
- return @
Sets the opacity of this layer. This affects how much of this layer is applied to the parent -layer at render time.
- - opacity: (opacity) ->
- @options.opacity = opacity / 100
- return @
Copies the contents of the parent layer to this layer
- - copyParent: ->
- parentData = @c.pixelData
-
- for i in [0...@c.pixelData.length] by 4
- @pixelData[i] = parentData[i]
- @pixelData[i+1] = parentData[i+1]
- @pixelData[i+2] = parentData[i+2]
- @pixelData[i+3] = parentData[i+3]
-
- return @
Fills this layer with a single color
- - fillColor: -> @c.fillColor.apply @c, arguments
Loads and overlays an image onto this layer
- - overlayImage: (image) ->
- if typeof image is "object"
- image = image.src
- else if typeof image is "string" and image[0] is "#"
- image = $(image).src
-
- return @ if not image
-
- @c.renderer.renderQueue.push
- type: Filter.Type.LoadOverlay
- src: image
- layer: @
-
- return @
-
Takes the contents of this layer and applies them to the parent layer at render time. This -should never be called explicitly by the user.
- - applyToParent: ->
- parentData = @c.pixelStack[@c.pixelStack.length - 1]
- layerData = @c.pixelData
-
- for i in [0...layerData.length] by 4
- rgbaParent =
- r: parentData[i]
- g: parentData[i+1]
- b: parentData[i+2]
- a: parentData[i+3]
-
- rgbaLayer =
- r: layerData[i]
- g: layerData[i+1]
- b: layerData[i+2]
- a: layerData[i+3]
-
- result = Blender.execute @options.blendingMode, rgbaLayer, rgbaParent
-
- result.r = Util.clampRGB result.r
- result.g = Util.clampRGB result.g
- result.b = Util.clampRGB result.b
- result.a = rgbaLayer.a if not result.a?
-
- parentData[i] = rgbaParent.r - (
- (rgbaParent.r - result.r) * (@options.opacity * (result.a / 255))
- )
- parentData[i+1] = rgbaParent.g - (
- (rgbaParent.g - result.g) * (@options.opacity * (result.a / 255))
- )
- parentData[i+2] = rgbaParent.b - (
- (rgbaParent.b - result.b) * (@options.opacity * (result.a / 255))
- )
-
-
Simple console logger class that can be toggled on and off based on Caman.DEBUG
- -class Logger
- constructor: ->
- for name in ['log', 'info', 'warn', 'error']
- @[name] = do (name) ->
- (args...) ->
- return if not Caman.DEBUG
- try
- console[name].apply console, args
- catch e
We're probably using IE9 or earlier
- - console[name] args
-
- @debug = @log
-
-Log = new Logger()
-
-
This object is available inside of the process() loop, and it lets filter developers have simple -access to any arbitrary pixel in the image, as well as information about the current pixel in -the loop.
- -class PixelInfo
- @coordinatesToLocation: (x, y, width) ->
- (y * width + x) * 4
-
- @locationToCoordinates: (loc, width) ->
- y = Math.floor(loc / (width * 4))
- x = (loc % (width * 4)) / 4
-
- return x: x, y: y
-
- constructor: (@c) -> @loc = 0
Retrieves the X, Y location of the current pixel. The origin is at the bottom left corner of -the image, like a normal coordinate system.
- - locationXY: ->
- y = @c.dimensions.height - Math.floor(@loc / (@c.dimensions.width * 4))
- x = (@loc % (@c.dimensions.width * 4)) / 4
-
- return x: x, y: y
Returns an RGBA object for a pixel whose location is specified in relation to the current -pixel.
- - getPixelRelative: (horiz, vert) ->
We invert the vert_offset in order to make the coordinate system non-inverted. In laymans -terms: -1 means down and +1 means up.
- - newLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz)
-
- if newLoc > @c.pixelData.length or newLoc < 0
- return r: 0, g: 0, b: 0, a: 0
-
- return {
- r: @c.pixelData[newLoc]
- g: @c.pixelData[newLoc + 1]
- b: @c.pixelData[newLoc + 2]
- a: @c.pixelData[newLoc + 3]
- }
The counterpart to getPixelRelative, this updates the value of a pixel whose location is -specified in relation to the current pixel.
- - putPixelRelative: (horiz, vert, rgba) ->
- nowLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz)
-
- return if newLoc > @c.pixelData.length or newLoc < 0
-
- @c.pixelData[newLoc] = rgba.r
- @c.pixelData[newLoc + 1] = rgba.g
- @c.pixelData[newLoc + 2] = rgba.b
- @c.pixelData[newLoc + 3] = rgba.a
-
- return true
Gets an RGBA object for an arbitrary pixel in the canvas specified by absolute X, Y coordinates
- - getPixel: (x, y) ->
- loc = @coordinatesToLocation(x, y, @width)
-
- return {
- r: @c.pixelData[loc]
- g: @c.pixelData[loc + 1]
- b: @c.pixelData[loc + 2]
- a: @c.pixelData[loc + 3]
- }
Updates the pixel at the given X, Y coordinate
- - putPixel: (x, y, rgba) ->
- loc = @coordinatesToLocation(x, y, @width)
-
- @c.pixelData[loc] = rgba.r
- @c.pixelData[loc + 1] = rgba.g
- @c.pixelData[loc + 2] = rgba.b
- @c.pixelData[loc + 3] = rgba.a
-
-
Stores and registers standalone plugins
- -class Plugin
- @plugins = {}
-
- @register: (name, plugin) -> @plugins[name] = plugin
- @execute: (context, name, args) -> @plugins[name].apply context, args
-
-Caman.Plugin = Plugin
-
-
Caman.Filter.register "posterize", (adjust) ->
- numOfAreas = 256 / adjust
- numOfValues = 255 / (adjust - 1)
-
- @process "posterize", (rgba) ->
- rgba.r = Math.floor Math.floor(rgba.r / numOfAreas) * numOfValues
- rgba.g = Math.floor Math.floor(rgba.g / numOfAreas) * numOfValues
- rgba.b = Math.floor Math.floor(rgba.b / numOfAreas) * numOfValues
- rgba
-
-
Caman.Filter.register "vintage", (vignette = true) ->
- @greyscale()
- @contrast 5
- @noise 3
- @sepia 100
- @channels red: 8, blue: 2, green: 4
- @gamma 0.87
-
- @vignette("40%", 30) if vignette
-
-Caman.Filter.register "lomo", (vignette = true) ->
- @brightness 15
- @exposure 15
- @curves 'rgb', [0, 0], [200, 0], [155, 255], [255, 255]
- @saturation -20
- @gamma 1.8
- @vignette("50%", 60) if vignette
- @brightness 5
-
-Caman.Filter.register "clarity", (grey = false) ->
- @vibrance 20
- @curves 'rgb', [5, 0], [130, 150], [190, 220], [250, 255]
- @sharpen 15
- @vignette "45%", 20
-
- if grey
- @greyscale()
- @contrast 4
-
- @
-
-Caman.Filter.register "sinCity", ->
- @contrast 100
- @brightness 15
- @exposure 10
- @posterize 80
- @clip 30
- @greyscale()
-
-Caman.Filter.register "sunrise", ->
- @exposure 3.5
- @saturation -5
- @vibrance 50
- @sepia 60
- @colorize "#e87b22", 10
- @channels red: 8, blue: 8
- @contrast 5
- @gamma 1.2
- @vignette "55%", 25
-
-Caman.Filter.register "crossProcess", ->
- @exposure 5
- @colorize "#e87b22", 4
- @sepia 20
- @channels blue: 8, red: 3
- @curves 'b', [0, 0], [100, 150], [180, 180], [255, 255]
- @contrast 15
- @vibrance 75
- @gamma 1.6
-
-Caman.Filter.register "orangePeel", ->
- @curves 'rgb', [0, 0], [100, 50], [140, 200], [255, 255]
- @vibrance -30
- @saturation -30
- @colorize '#ff9000', 30
- @contrast -5
- @gamma 1.4
-
-Caman.Filter.register "love", ->
- @brightness 5
- @exposure 8
- @contrast 4
- @colorize '#c42007', 30
- @vibrance 50
- @gamma 1.3
-
-Caman.Filter.register "grungy", ->
- @gamma 1.5
- @clip 25
- @saturation -60
- @contrast 5
- @noise 5
- @vignette "50%", 30
-
-Caman.Filter.register "jarques", ->
- @saturation -35
- @curves 'b', [20, 0], [90, 120], [186, 144], [255, 230]
- @curves 'r', [0, 0], [144, 90], [138, 120], [255, 255]
- @curves 'g', [10, 0], [115, 105], [148, 100], [255, 248]
- @curves 'rgb', [0, 0], [120, 100], [128, 140], [255, 255]
- @sharpen 20
-
-Caman.Filter.register "pinhole", ->
- @greyscale()
- @sepia 10
- @exposure 10
- @contrast 15
- @vignette "60%", 35
-
-Caman.Filter.register "oldBoot", ->
- @saturation -20
- @vibrance -50
- @gamma 1.1
- @sepia 30
- @channels red: -10, blue: 5
- @curves 'rgb', [0, 0], [80, 50], [128, 230], [255, 255]
- @vignette "60%", 30
-
-Caman.Filter.register "glowingSun", (vignette = true) ->
- @brightness 10
-
- @newLayer ->
- @setBlendingMode "multiply"
- @opacity 80
- @copyParent()
-
- @filter.gamma 0.8
- @filter.contrast 50
- @filter.exposure 10
-
- @newLayer ->
- @setBlendingMode "softLight"
- @opacity 80
- @fillColor "#f49600"
-
- @exposure 20
- @gamma 0.8
- @vignette "45%", 20 if vignette
-
-Caman.Filter.register "hazyDays", ->
- @gamma 1.2
-
- @newLayer ->
- @setBlendingMode "overlay"
- @opacity 60
- @copyParent()
-
- @filter.channels red: 5
- @filter.stackBlur 15
-
- @newLayer ->
- @setBlendingMode "addition"
- @opacity 40
- @fillColor "#6899ba"
-
- @newLayer ->
- @setBlendingMode "multiply"
- @opacity 35
- @copyParent()
-
- @filter.brightness 40
- @filter.vibrance 40
- @filter.exposure 30
- @filter.contrast 15
-
- @filter.curves 'r', [0, 40], [128, 128], [128, 128], [255, 215]
- @filter.curves 'g', [0, 40], [128, 128], [128, 128], [255, 215]
- @filter.curves 'b', [0, 40], [128, 128], [128, 128], [255, 215]
-
- @filter.stackBlur 5
-
- @curves 'r', [20, 0], [128, 158], [128, 128], [235, 255]
- @curves 'g', [20, 0], [128, 128], [128, 128], [235, 255]
- @curves 'b', [20, 0], [128, 108], [128, 128], [235, 255]
-
- @vignette "45%", 20
-
-Caman.Filter.register "herMajesty", ->
- @brightness 40
- @colorize "#ea1c5d", 10
- @curves 'b', [0, 10], [128, 180], [190, 190], [255, 255]
-
- @newLayer ->
- @setBlendingMode 'overlay'
- @opacity 50
- @copyParent()
-
- @filter.gamma 0.7
- @newLayer ->
- @setBlendingMode 'normal'
- @opacity 60
- @fillColor '#ea1c5d'
-
- @newLayer ->
- @setBlendingMode 'multiply'
- @opacity 60
- @copyParent()
-
- @filter.saturation 50
- @filter.hue 90
- @filter.contrast 10
-
- @gamma 1.4
- @vibrance -30
-
- @newLayer ->
- @opacity 10
- @fillColor '#e5f0ff'
-
- @
-
-Caman.Filter.register "nostalgia", ->
- @saturation 20
- @gamma 1.4
- @greyscale()
- @contrast 5
- @sepia 100
- @channels red: 8, blue: 2, green: 4
- @gamma 0.8
- @contrast 5
- @exposure 10
-
- @newLayer ->
- @setBlendingMode 'overlay'
- @copyParent()
- @opacity 55
-
- @filter.stackBlur 10
-
- @vignette "50%", 30
-
-Caman.Filter.register "hemingway", ->
- @greyscale()
- @contrast 10
- @gamma 0.9
-
- @newLayer ->
- @setBlendingMode "multiply"
- @opacity 40
- @copyParent()
-
- @filter.exposure 15
- @filter.contrast 15
- @filter.channels green: 10, red: 5
-
- @sepia 30
- @curves 'rgb', [0, 10], [120, 90], [180, 200], [235, 255]
- @channels red: 5, green: -2
- @exposure 15
-
-Caman.Filter.register "concentrate", ->
- @sharpen 40
- @saturation -50
- @channels red: 3
-
- @newLayer ->
- @setBlendingMode "multiply"
- @opacity 80
- @copyParent()
-
- @filter.sharpen 5
- @filter.contrast 50
- @filter.exposure 10
- @filter.channels blue: 5
-
- @brightness 10
-
-
Handles all of the various rendering methods in Caman. Most of the image modification happens -here. A new Renderer object is created for every render operation.
- -Caman.Renderer = class Renderer
The number of blocks to split the image into during the render process to simulate -concurrency. This also helps the browser manage the (possibly) long running render jobs.
- - @Blocks = if Caman.NodeJS then require('os').cpus().length else 4
-
- constructor: (@c) ->
- @renderQueue = []
- @modPixelData = null
-
- add: (job) ->
- return unless job?
- @renderQueue.push job
Grabs the next operation from the render queue and passes it to Renderer -for execution
- - processNext: =>
If the queue is empty, fire the finished callback
- - if @renderQueue.length is 0
- Event.trigger @, "renderFinished"
- @finishedFn.call(@c) if @finishedFn?
-
- return @
-
- @currentJob = @renderQueue.shift()
-
- switch @currentJob.type
- when Filter.Type.LayerDequeue
- layer = @c.canvasQueue.shift()
- @c.executeLayer layer
- @processNext()
- when Filter.Type.LayerFinished
- @c.applyCurrentLayer()
- @c.popContext()
- @processNext()
- when Filter.Type.LoadOverlay
- @loadOverlay @currentJob.layer, @currentJob.src
- when Filter.Type.Plugin
- @executePlugin()
- else
- @executeFilter()
-
- execute: (callback) ->
- @finishedFn = callback
- @modPixelData = Util.dataArray(@c.pixelData.length)
-
- @processNext()
-
- eachBlock: (fn) ->
Prepare all the required render data
- - @blocksDone = 0
-
- n = @c.pixelData.length
- blockPixelLength = Math.floor (n / 4) / Renderer.Blocks
- blockN = blockPixelLength * 4
- lastBlockN = blockN + ((n / 4) % Renderer.Blocks) * 4
-
- for i in [0...Renderer.Blocks]
- start = i * blockN
- end = start + (if i is Renderer.Blocks - 1 then lastBlockN else blockN)
-
- if Caman.NodeJS
- f = Fiber => fn.call(@, i, start, end)
- bnum = f.run()
- @blockFinished(bnum)
- else
- setTimeout do (i, start, end) =>
- => fn.call(@, i, start, end)
- , 0
The core of the image rendering, this function executes the provided filter.
-NOTE: this does not write the updated pixel data to the canvas. That happens when all filters -are finished rendering in order to be as fast as possible.
- - executeFilter: ->
- Event.trigger @c, "processStart", @currentJob
-
- if @currentJob.type is Filter.Type.Single
- @eachBlock @renderBlock
- else
- @eachBlock @renderKernel
Executes a standalone plugin
- - executePlugin: ->
- Log.debug "Executing plugin #{@currentJob.plugin}"
- Plugin.execute @c, @currentJob.plugin, @currentJob.args
- Log.debug "Plugin #{@currentJob.plugin} finished!"
-
- @processNext()
Renders a single block of the canvas with the current filter function
- - renderBlock: (bnum, start, end) ->
- Log.debug "Block ##{bnum} - Filter: #{@currentJob.name}, Start: #{start}, End: #{end}"
- Event.trigger @c, "blockStarted",
- blockNum: bnum
- totalBlocks: Renderer.Blocks
- startPixel: start
- endPixel: end
-
- data = r: 0, g: 0, b: 0, a: 0
- pixelInfo = new PixelInfo @c
-
- for i in [start...end] by 4
- pixelInfo.loc = i
-
- data.r = @c.pixelData[i]
- data.g = @c.pixelData[i+1]
- data.b = @c.pixelData[i+2]
- data.a = @c.pixelData[i+3]
-
- res = @currentJob.processFn.call pixelInfo, data
- res.a = data.a if not res.a?
-
- @c.pixelData[i] = Util.clampRGB res.r
- @c.pixelData[i+1] = Util.clampRGB res.g
- @c.pixelData[i+2] = Util.clampRGB res.b
- @c.pixelData[i+3] = Util.clampRGB res.a
-
- if Caman.NodeJS
- Fiber.yield(bnum)
- else
- @blockFinished bnum
Applies an image kernel to the canvas
- - renderKernel: (bnum, start, end) ->
- name = @currentJob.name
- bias = @currentJob.bias
- divisor = @currentJob.divisor
- n = @c.pixelData.length
-
- adjust = @currentJob.adjust
- adjustSize = Math.sqrt adjust.length
-
- kernel = []
-
- Log.debug "Rendering kernel - Filter: #{@currentJob.name}"
-
- start = Math.max start, @c.dimensions.width * 4 * ((adjustSize - 1) / 2)
- end = Math.min end, n - (@c.dimensions.width * 4 * ((adjustSize - 1) / 2))
-
- builder = (adjustSize - 1) / 2
-
- pixelInfo = new PixelInfo @c
-
- for i in [start...end] by 4
- pixelInfo.loc = i
- builderIndex = 0
-
- for j in [-builder..builder]
- for k in [builder..-builder]
- pixel = pixelInfo.getPixelRelative j, k
- kernel[builderIndex * 3] = pixel.r
- kernel[builderIndex * 3 + 1] = pixel.g
- kernel[builderIndex * 3 + 2] = pixel.b
-
- builderIndex++
-
- res = @processKernel adjust, kernel, divisor, bias
-
- @modPixelData[i] = Util.clampRGB(res.r)
- @modPixelData[i+1] = Util.clampRGB(res.g)
- @modPixelData[i+2] = Util.clampRGB(res.b)
- @modPixelData[i+3] = @c.pixelData[i+3]
-
- if Caman.NodeJS
- Fiber.yield(bnum)
- else
- @blockFinished bnum
Called when a single block is finished rendering. Once all blocks are done, we signal that this -filter is finished rendering and continue to the next step.
- - blockFinished: (bnum) ->
- Log.debug "Block ##{bnum} finished! Filter: #{@currentJob.name}" if bnum >= 0
- @blocksDone++
-
- Event.trigger @c, "blockFinished",
- blockNum: bnum
- blocksFinished: @blocksDone
- totalBlocks: Renderer.Blocks
-
- if @blocksDone is Renderer.Blocks
- if @currentJob.type is Filter.Type.Kernel
- for i in [0...@c.pixelData.length]
- @c.pixelData[i] = @modPixelData[i]
-
- Log.debug "Filter #{@currentJob.name} finished!" if bnum >=0
- Event.trigger @c, "processComplete", @currentJob
-
- @processNext()
The "filter function" for kernel adjustments.
- - processKernel: (adjust, kernel, divisor, bias) ->
- val = r: 0, g: 0, b: 0
-
- for i in [0...adjust.length]
- val.r += adjust[i] * kernel[i * 3]
- val.g += adjust[i] * kernel[i * 3 + 1]
- val.b += adjust[i] * kernel[i * 3 + 2]
-
- val.r = (val.r / divisor) + bias
- val.g = (val.g / divisor) + bias
- val.b = (val.b / divisor) + bias
- val
Loads an image onto the current canvas
- - loadOverlay: (layer, src) ->
- img = document.createElement 'img'
- img.onload = =>
- layer.context.drawImage img, 0, 0, @c.dimensions.width, @c.dimensions.height
- layer.imageData = layer.context.getImageData 0, 0, @c.dimensions.width, @c.dimensions.height
- layer.pixelData = layer.imageData.data
-
- @c.pixelData = layer.pixelData
-
- @processNext()
-
- proxyUrl = IO.remoteCheck src
- img.src = if proxyUrl? then proxyUrl else src
-
-
Allows us to crop the canvas and produce a new smaller -canvas.
- -Caman.Plugin.register "crop", (width, height, x = 0, y = 0) ->
Create our new canvas element
- - if exports?
- canvas = new Canvas width, height
- else
- canvas = document.createElement 'canvas'
- Util.copyAttributes @canvas, canvas
-
- canvas.width = width
- canvas.height = height
-
- ctx = canvas.getContext '2d'
Perform the cropping by drawing to the new canvas
- - ctx.drawImage @canvas, x, y, width, height, 0, 0, width, height
-
- @cropCoordinates = x: x, y: y
Update all of the references
- - @cropped = true
- @replaceCanvas canvas
Resize the canvas and the image to a new size
- -Caman.Plugin.register "resize", (newDims = null) ->
Calculate new size
- - if newDims is null or (!newDims.width? and !newDims.height?)
- Log.error "Invalid or missing dimensions given for resize"
- return
-
- if not newDims.width?
Calculate width
- - newDims.width = @canvas.width * newDims.height / @canvas.height
- else if not newDims.height?
Calculate height
- - newDims.height = @canvas.height * newDims.width / @canvas.width
-
- if exports?
- canvas = new Canvas newDims.width, newDims.height
- else
- canvas = document.createElement 'canvas'
- Util.copyAttributes @canvas, canvas
-
- canvas.width = newDims.width
- canvas.height = newDims.height
-
- ctx = canvas.getContext '2d'
-
- ctx.drawImage @canvas,
- 0, 0,
- @canvas.width, @canvas.height,
- 0, 0,
- newDims.width, newDims.height
-
- @resized = true
- @replaceCanvas canvas
-
-Caman.Filter.register "crop", ->
- @processPlugin "crop", Array.prototype.slice.call(arguments, 0)
-
-Caman.Filter.register "resize", ->
- @processPlugin "resize", Array.prototype.slice.call(arguments, 0)
-
-
StackBlur - a fast almost Gaussian Blur For Canvas v0.31 modified for CamanJS
-
-Version: 0.31
-Author: Mario Klingemann
-Contact: mario@quasimondo.com
-Website: http://www.quasimondo.com/StackBlurForCanvas
-Twitter: @quasimondo
-Modified By: Ryan LeFevre (@meltingice)
-
-In case you find this class useful - especially in commercial projects -
-I am not totally unhappy for a small donation to my PayPal account
-mario@quasimondo.de
-
-Or support me on flattr:
-https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
-
-Copyright (c) 2010 Mario Klingemann
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
do ->
-
- mul_table = [ 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 ]
-
- shg_table = [ 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ]
-
- BlurStack = ->
- @r = 0
- @g = 0
- @b = 0
- @a = 0
- @next = null
-
- Caman.Plugin.register "stackBlur", (radius) ->
- return if isNaN(radius) or radius < 1
- radius |= 0
-
- pixels = @pixelData
- width = @dimensions.width
- height = @dimensions.height
-
- div = radius + radius + 1
- w4 = width << 2
- widthMinus1 = width - 1
- heightMinus1 = height - 1
- radiusPlus1 = radius + 1
- sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2
-
- stackStart = new BlurStack()
- stack = stackStart
-
- for i in [1...div]
- stack = stack.next = new BlurStack()
- stackEnd = stack if i is radiusPlus1
-
- stack.next = stackStart
- stackIn = null
- stackOut = null
-
- yw = yi = 0
-
- mul_sum = mul_table[radius]
- shg_sum = shg_table[radius]
-
- for y in [0...height]
- r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0
-
- r_out_sum = radiusPlus1 * (pr = pixels[yi])
- g_out_sum = radiusPlus1 * (pg = pixels[yi + 1])
- b_out_sum = radiusPlus1 * (pb = pixels[yi + 2])
-
- r_sum += sumFactor * pr
- g_sum += sumFactor * pg
- b_sum += sumFactor * pb
-
- stack = stackStart
-
- for i in [0...radiusPlus1]
- stack.r = pr
- stack.g = pg
- stack.b = pb
- stack = stack.next
-
- for i in [1...radiusPlus1]
- p = yi + ((if widthMinus1 < i then widthMinus1 else i) << 2)
- r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i)
- g_sum += (stack.g = (pg = pixels[p + 1])) * rbs
- b_sum += (stack.b = (pb = pixels[p + 2])) * rbs
-
- r_in_sum += pr
- g_in_sum += pg
- b_in_sum += pb
-
- stack = stack.next
-
- stackIn = stackStart
- stackOut = stackEnd
-
- for x in [0...width]
- pixels[yi] = (r_sum * mul_sum) >> shg_sum
- pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum
- pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum
-
- r_sum -= r_out_sum
- g_sum -= g_out_sum
- b_sum -= b_out_sum
-
- r_out_sum -= stackIn.r
- g_out_sum -= stackIn.g
- b_out_sum -= stackIn.b
-
- p = (yw + (if (p = x + radius + 1) < widthMinus1 then p else widthMinus1)) << 2
-
- r_in_sum += (stackIn.r = pixels[p])
- g_in_sum += (stackIn.g = pixels[p + 1])
- b_in_sum += (stackIn.b = pixels[p + 2])
-
- r_sum += r_in_sum
- g_sum += g_in_sum
- b_sum += b_in_sum
-
- stackIn = stackIn.next
-
- r_out_sum += (pr = stackOut.r)
- g_out_sum += (pg = stackOut.g)
- b_out_sum += (pb = stackOut.b)
-
- r_in_sum -= pr
- g_in_sum -= pg
- b_in_sum -= pb
-
- stackOut = stackOut.next
-
- yi += 4
-
- yw += width
-
- for x in [0...width]
- g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0
-
- yi = x << 2
- r_out_sum = radiusPlus1 * (pr = pixels[yi])
- g_out_sum = radiusPlus1 * (pg = pixels[yi + 1])
- b_out_sum = radiusPlus1 * (pb = pixels[yi + 2])
-
- r_sum += sumFactor * pr
- g_sum += sumFactor * pg
- b_sum += sumFactor * pb
-
- stack = stackStart
-
- for i in [0...radiusPlus1]
- stack.r = pr
- stack.g = pg
- stack.b = pb
- stack = stack.next
-
- yp = width
-
- for i in [1..radius]
- yi = (yp + x) << 2
-
- r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i)
- g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs
- b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs
-
- r_in_sum += pr
- g_in_sum += pg
- b_in_sum += pb
-
- stack = stack.next
-
- yp += width if i < heightMinus1
-
- yi = x
- stackIn = stackStart
- stackOut = stackEnd
-
- for y in [0...height]
- p = yi << 2
- pixels[p] = (r_sum * mul_sum) >> shg_sum
- pixels[p + 1] = (g_sum * mul_sum) >> shg_sum
- pixels[p + 2] = (b_sum * mul_sum) >> shg_sum
-
- r_sum -= r_out_sum
- g_sum -= g_out_sum
- b_sum -= b_out_sum
-
- r_out_sum -= stackIn.r
- g_out_sum -= stackIn.g
- b_out_sum -= stackIn.b
-
- p = (x + ((if (p = y + radiusPlus1) < heightMinus1 then p else heightMinus1) * width)) << 2
-
- r_sum += (r_in_sum += (stackIn.r = pixels[p]))
- g_sum += (g_in_sum += (stackIn.g = pixels[p + 1]))
- b_sum += (b_in_sum += (stackIn.b = pixels[p + 2]))
-
- stackIn = stackIn.next
-
- r_out_sum += (pr = stackOut.r)
- g_out_sum += (pg = stackOut.g)
- b_out_sum += (pb = stackOut.b)
-
- r_in_sum -= pr
- g_in_sum -= pg
- b_in_sum -= pb
-
- stackOut = stackOut.next
-
- yi += width
-
-
- @
-
- Caman.Filter.register "stackBlur", (radius) ->
- @processPlugin "stackBlur", [radius]
-
-
Used for storing instances of CamanInstance objects such that, when Caman() is called on an -already initialized element, it returns that object instead of re-initializing.
- -Caman.Store = class Store
- @items = {}
-
- @has: (search) -> @items[search]?
- @get: (search) -> @items[search]
- @put: (name, obj) -> @items[name] = obj
- @execute: (search, callback) ->
- setTimeout =>
- callback.call @get(search), @get(search)
- , 0
-
- return @get(search)
-
- @flush: (name = false) ->
- if name then delete @items[name] else @items = {}
-
-
Caman.Filter.register "threshold", (adjust) ->
- @process "threshold", (rgba) ->
- luminance = (0.2126 * rgba.r) + (0.7152 * rgba.g) + (0.0722 * rgba.b)
-
- if luminance < adjust
- rgba.r = 0
- rgba.g = 0
- rgba.b = 0
- else
- rgba.r = 255
- rgba.g = 255
- rgba.b = 255
-
- rgba
-
-
Look what you make me do Javascript
- -slice = Array::slice
DOM simplifier (no jQuery dependency) -NodeJS compatible
- -$ = (sel, root = document) ->
- return sel if typeof sel is "object" or exports?
- root.querySelector sel
-
-class Util
Unique value utility
- - @uniqid = do ->
- id = 0
- get: -> id++
Helper function that extends one object with all the properies of other objects
- - @extend = (obj) ->
- dest = obj
- src = slice.call arguments, 1
-
- for copy in src
- for own prop of copy
- dest[prop] = copy[prop]
-
- return dest
In order to stay true to the latest spec, RGB values must be clamped between -0 and 255. If we don't do this, weird things happen.
- - @clampRGB = (val) ->
- return 0 if val < 0
- return 255 if val > 255
- val
-
- @copyAttributes: (from, to, opts={}) ->
- for attr in from.attributes
- continue if opts.except? and attr.nodeName in opts.except
- to.setAttribute(attr.nodeName, attr.nodeValue)
Support for browsers that don't know Uint8Array (such as IE9)
- - @dataArray: (length = 0) ->
- return new Uint8Array(length) if Caman.NodeJS or window.Uint8Array?
- return new Array(length)
-
-
- # @param String hex The colors hex value - # @return Array The RGB representation - #- @hexToRGB: (hex) -> - hex = hex.substr(1) if hex.charAt(0) is "#" - r = parseInt hex.substr(0, 2), 16 - g = parseInt hex.substr(2, 2), 16 - b = parseInt hex.substr(4, 2), 16 - - r: r, g: g, b: b - - # Converts an RGB color to HSL. - # Assumes r, g, and b are in the set [0, 255] and - # returns h, s, and l in the set [0, 1]. - # - #
- # @param Number r Red channel - # @param Number g Green channel - # @param Number b Blue channel - # @return The HSL representation - #- @rgbToHSL: (r, g, b) -> - if typeof r is "object" - g = r.g - b = r.b - r = r.r - - r /= 255 - g /= 255 - b /= 255 - - max = Math.max r, g, b - min = Math.min r, g, b - l = (max + min) / 2 - - if max is min - h = s = 0 - else - d = max - min - s = if l > 0.5 then d / (2 - max - min) else d / (max + min) - h = switch max - when r then (g - b) / d + (if g < b then 6 else 0) - when g then (b - r) / d + 2 - when b then (r - g) / d + 4 - - h /= 6 - - h: h, s: s, l: l - - # Converts an HSL color value to RGB. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSL_color_space. - # Assumes h, s, and l are contained in the set [0, 1] and - # returns r, g, and b in the set [0, 255]. - # - #
- # @param Number h The hue - # @param Number s The saturation - # @param Number l The lightness - # @return Array The RGB representation - #- @hslToRGB: (h, s, l) -> - if typeof h is "object" - s = h.s - l = h.l - h = h.h - - if s is 0 - r = g = b = l - else - q = if l < 0.5 then l * (1 + s) else l + s - l * s - p = 2 * l - q - - r = @hueToRGB p, q, h + 1/3 - g = @hueToRGB p, q, h - b = @hueToRGB p, q, h - 1/3 - - r: r * 255, g: g * 255, b: b * 255 - - # Converts from the hue color space back to RGB - @hueToRGB: (p, q, t) -> - if t < 0 then t += 1 - if t > 1 then t -= 1 - if t < 1/6 then return p + (q - p) * 6 * t - if t < 1/2 then return q - if t < 2/3 then return p + (q - p) * (2/3 - t) * 6 - return p - - # Converts an RGB color value to HSV. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSV_color_space. - # Assumes r, g, and b are contained in the set [0, 255] and - # returns h, s, and v in the set [0, 1]. - # - #
- # @param Number r The red color value - # @param Number g The green color value - # @param Number b The blue color value - # @return Array The HSV representation - #- @rgbToHSV: (r, g, b) -> - r /= 255 - g /= 255 - b /= 255 - - max = Math.max r, g, b - min = Math.min r, g, b - v = max - d = max - min - - s = if max is 0 then 0 else d / max - - if max is min - h = 0 - else - h = switch max - when r then (g - b) / d + (if g < b then 6 else 0) - when g then (b - r) / d + 2 - when b then (r - g) / d + 4 - - h /= 6 - - h: h, s: s, v: v - - # Converts an HSV color value to RGB. Conversion formula - # adapted from http://en.wikipedia.org/wiki/HSV_color_space. - # Assumes h, s, and v are contained in the set [0, 1] and - # returns r, g, and b in the set [0, 255]. - # - #
- # @param Number h The hue - # @param Number s The saturation - # @param Number v The value - # @return Array The RGB representation - #- @hsvToRGB: (h, s, v) -> - i = Math.floor h * 6 - f = h * 6 - i - p = v * (1 - s) - q = v * (1 - f * s) - t = v * (1 - (1 - f) * s) - - switch i % 6 - when 0 - r = v - g = t - b = p - when 1 - r = q - g = v - b = p - when 2 - r = p - g = v - b = t - when 3 - r = p - g = q - b = v - when 4 - r = t - g = p - b = v - when 5 - r = v - g = p - b = q - - r: r * 255, g: g * 255, b: b * 255 - - # Converts a RGB color value to the XYZ color space. Formulas - # are based on http://en.wikipedia.org/wiki/SRGB assuming that - # RGB values are sRGB. - # - # Assumes r, g, and b are contained in the set [0, 255] and - # returns x, y, and z. - # - #
- # @param Number r The red color value - # @param Number g The green color value - # @param Number b The blue color value - # @return Array The XYZ representation - #- @rgbToXYZ: (r, g, b) -> - r /= 255 - g /= 255 - b /= 255 - - if r > 0.04045 - r = Math.pow((r + 0.055) / 1.055, 2.4) - else - r /= 12.92 - - if g > 0.04045 - g = Math.pow((g + 0.055) / 1.055, 2.4) - else - g /= 12.92 - - if b > 0.04045 - b = Math.pow((b + 0.055) / 1.055, 2.4) - else - b /= 12.92 - - x = r * 0.4124 + g * 0.3576 + b * 0.1805; - y = r * 0.2126 + g * 0.7152 + b * 0.0722; - z = r * 0.0193 + g * 0.1192 + b * 0.9505; - - x: x * 100, y: y * 100, z: z * 100 - - # Converts a XYZ color value to the sRGB color space. Formulas - # are based on http://en.wikipedia.org/wiki/SRGB and the resulting - # RGB value will be in the sRGB color space. - # Assumes x, y and z values are whatever they are and returns - # r, g and b in the set [0, 255]. - # - #
- # @param Number x The X value - # @param Number y The Y value - # @param Number z The Z value - # @return Array The RGB representation - #- @xyzToRGB: (x, y, z) -> - x /= 100 - y /= 100 - z /= 100 - - r = (3.2406 * x) + (-1.5372 * y) + (-0.4986 * z) - g = (-0.9689 * x) + (1.8758 * y) + (0.0415 * z) - b = (0.0557 * x) + (-0.2040 * y) + (1.0570 * z) - - if r > 0.0031308 - r = (1.055 * Math.pow(r, 0.4166666667)) - 0.055 - else - r *= 12.92 - - if g > 0.0031308 - g = (1.055 * Math.pow(g, 0.4166666667)) - 0.055 - else - g *= 12.92 - - if b > 0.0031308 - b = (1.055 * Math.pow(b, 0.4166666667)) - 0.055 - else - b *= 12.92 - - r: r * 255, g: g * 255, b: b * 255 - - # Converts a XYZ color value to the CIELAB color space. Formulas - # are based on http://en.wikipedia.org/wiki/Lab_color_space - # The reference white point used in the conversion is D65. - # Assumes x, y and z values are whatever they are and returns - # L*, a* and b* values - # - #
- # @param Number x The X value - # @param Number y The Y value - # @param Number z The Z value - # @return Array The Lab representation - #- @xyzToLab: (x, y, z) -> - if typeof x is "object" - y = x.y - z = x.z - x = x.x - - whiteX = 95.047 - whiteY = 100.0 - whiteZ = 108.883 - - x /= whiteX - y /= whiteY - z /= whiteZ - - if x > 0.008856451679 - x = Math.pow(x, 0.3333333333) - else - x = (7.787037037 * x) + 0.1379310345 - - if y > 0.008856451679 - y = Math.pow(y, 0.3333333333) - else - y = (7.787037037 * y) + 0.1379310345 - - if z > 0.008856451679 - z = Math.pow(z, 0.3333333333) - else - z = (7.787037037 * z) + 0.1379310345 - - l = 116 * y - 16 - a = 500 * (x - y) - b = 200 * (y - z) - - l: l, a: a, b: b - - # Converts a L*, a*, b* color values from the CIELAB color space - # to the XYZ color space. Formulas are based on - # http://en.wikipedia.org/wiki/Lab_color_space - # - # The reference white point used in the conversion is D65. - # Assumes L*, a* and b* values are whatever they are and returns - # x, y and z values. - # - #
- # @param Number l The L* value - # @param Number a The a* value - # @param Number b The b* value - # @return Array The XYZ representation - #- @labToXYZ: (l, a, b) -> - if typeof l is "object" - a = l.a - b = l.b - l = l.l - - y = (l + 16) / 116 - x = y + (a / 500) - z = y - (b / 200) - - if x > 0.2068965517 - x = x * x * x - else - x = 0.1284185493 * (x - 0.1379310345) - - if y > 0.2068965517 - y = y * y * y - else - y = 0.1284185493 * (y - 0.1379310345) - - if z > 0.2068965517 - z = z * z * z - else - z = 0.1284185493 * (z - 0.1379310345) - - # D65 reference white point - x: x * 95.047, y: y * 100.0, z: z * 108.883 - - @rgbToLab: (r, g, b) -> - if typeof r is "object" - g = r.g - b = r.b - r = r.r - - xyz = @rgbToXYZ(r, g, b) - @xyzToLab xyz - - @labToRGB: (l, a, b) -> - \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/event.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/event.coffee deleted file mode 100644 index 4214cba26..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/event.coffee +++ /dev/null @@ -1,43 +0,0 @@ -# Event system that can be used to register callbacks that get fired -# during certain times in the render process. -class Event - @events = {} - - # All of the supported event types - @types = [ - "processStart" - "processComplete" - "renderStart" - "renderFinished" - "blockStarted" - "blockFinished" - ] - - # Trigger an event - @trigger: (target, type, data) -> - if @events[type] and @events[type].length - for event in @events[type] - if event.target is null or target.id is event.target.id - event.fn.call target, data - - # Listen for an event. Optionally bind the listen to a single CamanInstance - # or all CamanInstances. - @listen: (target, type, fn) -> - # Adjust arguments if target is omitted - if typeof target is "string" - _type = target - _fn = type - - target = null - type = _type - fn = _fn - - # Validation - return false if type not in @types - - @events[type] = [] if not @events[type] - @events[type].push target: target, fn: fn - - return true - -Caman.Event = Event \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/filter.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/filter.coffee deleted file mode 100644 index 0c0b408c7..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/filter.coffee +++ /dev/null @@ -1,13 +0,0 @@ -# Responsible for storing all of the filters -Caman.Filter = class Filter - # All of the different render operatives - @Type = - Single: 1 - Kernel: 2 - LayerDequeue: 3 - LayerFinished: 4 - LoadOverlay: 5 - Plugin: 6 - - # Registers a filter function - @register: (name, filterFunc) -> Caman::[name] = filterFunc \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/io.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/io.coffee deleted file mode 100644 index 483e72a31..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/io.coffee +++ /dev/null @@ -1,90 +0,0 @@ -# Various I/O based operations -Caman.IO = class IO - @domainRegex = /(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/ - - # Is the given URL remote? - # If a cross-origin setting is set, we assume you have CORS - # properly configured. - @isRemote: (img) -> - return false unless img? - return false if @corsEnabled(img) - return @isURLRemote img.src - - @corsEnabled: (img) -> - img.crossOrigin? and img.crossOrigin.toLowerCase() in ['anonymous', 'use-credentials'] - - @isURLRemote: (url) -> - matches = url.match @domainRegex - return if matches then matches[1] isnt document.domain else false - - @remoteCheck: (src) -> - if @isURLRemote src - if not Caman.remoteProxy.length - Log.info "Attempting to load a remote image without a configured proxy. URL: #{src}" - return - else - if Caman.isURLRemote Caman.remoteProxy - Log.info "Cannot use a remote proxy for loading images." - return - - "#{Caman.remoteProxy}?camanProxyUrl=#{encodeURIComponent(src)}" - - - @proxyUrl: (src) -> - "#{Caman.remoteProxy}?#{Caman.proxyParam}=#{encodeURIComponent(src)}" - - # Shortcut for using one of the bundled proxies. - @useProxy: (lang) -> - langToExt = - ruby: 'rb' - python: 'py' - perl: 'pl' - javascript: 'js' - - lang = lang.toLowerCase() - lang = langToExt[lang] if langToExt[lang]? - "proxies/caman_proxy.#{lang}" - -# Grabs the canvas data, encodes it to Base64, then sets the browser location to -# the encoded data so that the user will be prompted to download it. -Caman::save = -> - if exports? - @nodeSave.apply @, arguments - else - @browserSave.apply @, arguments - -Caman::browserSave = (type = "png") -> - type = type.toLowerCase() - - # Force download (its a bit hackish) - image = @toBase64(type).replace "image/#{type}", "image/octet-stream" - document.location.href = image - -Caman::nodeSave = (file, overwrite = true) -> - try - stats = fs.statSync file - return false if stats.isFile() and not overwrite - catch e - Log.debug "Creating output file #{file}" - - fs.writeFile file, @canvas.toBuffer(), -> - Log.debug "Finished writing to #{file}" - -# Takes the current canvas data, converts it to Base64, then sets it as the source -# of a new Image object and returns it. -Caman::toImage = (type) -> - img = document.createElement 'img' - img.src = @toBase64 type - img.width = @dimensions.width - img.height = @dimensions.height - - if window.devicePixelRatio - img.width /= window.devicePixelRatio - img.height /= window.devicePixelRatio - - return img - -# Base64 encodes the current canvas -Caman::toBase64 = (type = "png") -> - type = type.toLowerCase() - return @canvas.toDataURL "image/#{type}" diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/layer.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/layer.coffee deleted file mode 100644 index 40c43e5f4..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/layer.coffee +++ /dev/null @@ -1,116 +0,0 @@ -# The entire layering system for Caman resides in this file. Layers get their own canvasLayer -# objectwhich is created when newLayer() is called. For extensive information regarding the -# specifics of howthe layering system works, there is an in-depth blog post on this very topic. -# Instead of copying the entirety of that post, I'll simply point you towards the -# [blog link](http://blog.meltingice.net/programming/implementing-layers-camanjs). -# -# However, the gist of the layering system is that, for each layer, it creates a new canvas -# element and then either copies the parent layer's data or applies a solid color to the new -# layer. After some (optional) effects are applied, the layer is blended back into the parent -# canvas layer using one of many different blending algorithms. -# -# You can also load an image (local or remote, with a proxy) into a canvas layer, which is useful -# if you want to add textures to an image. -class Layer - constructor: (@c) -> - # Compatibility - @filter = @c - - @options = - blendingMode: 'normal' - opacity: 1.0 - - # Each layer gets its own unique ID - @layerID = Util.uniqid.get() - - # Create the canvas for this layer - @canvas = if exports? then new Canvas() else document.createElement('canvas') - - @canvas.width = @c.dimensions.width - @canvas.height = @c.dimensions.height - - @context = @canvas.getContext('2d') - @context.createImageData @canvas.width, @canvas.height - @imageData = @context.getImageData 0, 0, @canvas.width, @canvas.height - @pixelData = @imageData.data - - # If you want to create nested layers - newLayer: (cb) -> @c.newLayer.call @c, cb - - # Sets the blending mode of this layer. The mode is the name of a blender function. - setBlendingMode: (mode) -> - @options.blendingMode = mode - return @ - - # Sets the opacity of this layer. This affects how much of this layer is applied to the parent - # layer at render time. - opacity: (opacity) -> - @options.opacity = opacity / 100 - return @ - - # Copies the contents of the parent layer to this layer - copyParent: -> - parentData = @c.pixelData - - for i in [0...@c.pixelData.length] by 4 - @pixelData[i] = parentData[i] - @pixelData[i+1] = parentData[i+1] - @pixelData[i+2] = parentData[i+2] - @pixelData[i+3] = parentData[i+3] - - return @ - - # Fills this layer with a single color - fillColor: -> @c.fillColor.apply @c, arguments - - # Loads and overlays an image onto this layer - overlayImage: (image) -> - if typeof image is "object" - image = image.src - else if typeof image is "string" and image[0] is "#" - image = $(image).src - - return @ if not image - - @c.renderer.renderQueue.push - type: Filter.Type.LoadOverlay - src: image - layer: @ - - return @ - - # Takes the contents of this layer and applies them to the parent layer at render time. This - # should never be called explicitly by the user. - applyToParent: -> - parentData = @c.pixelStack[@c.pixelStack.length - 1] - layerData = @c.pixelData - - for i in [0...layerData.length] by 4 - rgbaParent = - r: parentData[i] - g: parentData[i+1] - b: parentData[i+2] - a: parentData[i+3] - - rgbaLayer = - r: layerData[i] - g: layerData[i+1] - b: layerData[i+2] - a: layerData[i+3] - - result = Blender.execute @options.blendingMode, rgbaLayer, rgbaParent - - result.r = Util.clampRGB result.r - result.g = Util.clampRGB result.g - result.b = Util.clampRGB result.b - result.a = rgbaLayer.a if not result.a? - - parentData[i] = rgbaParent.r - ( - (rgbaParent.r - result.r) * (@options.opacity * (result.a / 255)) - ) - parentData[i+1] = rgbaParent.g - ( - (rgbaParent.g - result.g) * (@options.opacity * (result.a / 255)) - ) - parentData[i+2] = rgbaParent.b - ( - (rgbaParent.b - result.b) * (@options.opacity * (result.a / 255)) - ) diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/logger.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/logger.coffee deleted file mode 100644 index 75ae67225..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/logger.coffee +++ /dev/null @@ -1,16 +0,0 @@ -# Simple console logger class that can be toggled on and off based on Caman.DEBUG -class Logger - constructor: -> - for name in ['log', 'info', 'warn', 'error'] - @[name] = do (name) -> - (args...) -> - return if not Caman.DEBUG - try - console[name].apply console, args - catch e - # We're probably using IE9 or earlier - console[name] args - - @debug = @log - -Log = new Logger() \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/pixelinfo.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/pixelinfo.coffee deleted file mode 100644 index 96108df6c..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/pixelinfo.coffee +++ /dev/null @@ -1,73 +0,0 @@ -# This object is available inside of the process() loop, and it lets filter developers have simple -# access to any arbitrary pixel in the image, as well as information about the current pixel in -# the loop. -class PixelInfo - @coordinatesToLocation: (x, y, width) -> - (y * width + x) * 4 - - @locationToCoordinates: (loc, width) -> - y = Math.floor(loc / (width * 4)) - x = (loc % (width * 4)) / 4 - - return x: x, y: y - - constructor: (@c) -> @loc = 0 - - # Retrieves the X, Y location of the current pixel. The origin is at the bottom left corner of - # the image, like a normal coordinate system. - locationXY: -> - y = @c.dimensions.height - Math.floor(@loc / (@c.dimensions.width * 4)) - x = (@loc % (@c.dimensions.width * 4)) / 4 - - return x: x, y: y - - # Returns an RGBA object for a pixel whose location is specified in relation to the current - # pixel. - getPixelRelative: (horiz, vert) -> - # We invert the vert_offset in order to make the coordinate system non-inverted. In laymans - # terms: -1 means down and +1 means up. - newLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz) - - if newLoc > @c.pixelData.length or newLoc < 0 - return r: 0, g: 0, b: 0, a: 0 - - return { - r: @c.pixelData[newLoc] - g: @c.pixelData[newLoc + 1] - b: @c.pixelData[newLoc + 2] - a: @c.pixelData[newLoc + 3] - } - - # The counterpart to getPixelRelative, this updates the value of a pixel whose location is - # specified in relation to the current pixel. - putPixelRelative: (horiz, vert, rgba) -> - nowLoc = @loc + (@c.dimensions.width * 4 * (vert * -1)) + (4 * horiz) - - return if newLoc > @c.pixelData.length or newLoc < 0 - - @c.pixelData[newLoc] = rgba.r - @c.pixelData[newLoc + 1] = rgba.g - @c.pixelData[newLoc + 2] = rgba.b - @c.pixelData[newLoc + 3] = rgba.a - - return true - - # Gets an RGBA object for an arbitrary pixel in the canvas specified by absolute X, Y coordinates - getPixel: (x, y) -> - loc = @coordinatesToLocation(x, y, @width) - - return { - r: @c.pixelData[loc] - g: @c.pixelData[loc + 1] - b: @c.pixelData[loc + 2] - a: @c.pixelData[loc + 3] - } - - # Updates the pixel at the given X, Y coordinate - putPixel: (x, y, rgba) -> - loc = @coordinatesToLocation(x, y, @width) - - @c.pixelData[loc] = rgba.r - @c.pixelData[loc + 1] = rgba.g - @c.pixelData[loc + 2] = rgba.b - @c.pixelData[loc + 3] = rgba.a diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/plugin.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/plugin.coffee deleted file mode 100644 index 30fdcc00f..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/plugin.coffee +++ /dev/null @@ -1,8 +0,0 @@ -# Stores and registers standalone plugins -class Plugin - @plugins = {} - - @register: (name, plugin) -> @plugins[name] = plugin - @execute: (context, name, args) -> @plugins[name].apply context, args - -Caman.Plugin = Plugin \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/renderer.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/renderer.coffee deleted file mode 100644 index 614ea9c97..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/renderer.coffee +++ /dev/null @@ -1,219 +0,0 @@ -# Handles all of the various rendering methods in Caman. Most of the image modification happens -# here. A new Renderer object is created for every render operation. -Caman.Renderer = class Renderer - # The number of blocks to split the image into during the render process to simulate - # concurrency. This also helps the browser manage the (possibly) long running render jobs. - @Blocks = if Caman.NodeJS then require('os').cpus().length else 4 - - constructor: (@c) -> - @renderQueue = [] - @modPixelData = null - - add: (job) -> - return unless job? - @renderQueue.push job - - # Grabs the next operation from the render queue and passes it to Renderer - # for execution - processNext: => - # If the queue is empty, fire the finished callback - if @renderQueue.length is 0 - Event.trigger @, "renderFinished" - @finishedFn.call(@c) if @finishedFn? - - return @ - - @currentJob = @renderQueue.shift() - - switch @currentJob.type - when Filter.Type.LayerDequeue - layer = @c.canvasQueue.shift() - @c.executeLayer layer - @processNext() - when Filter.Type.LayerFinished - @c.applyCurrentLayer() - @c.popContext() - @processNext() - when Filter.Type.LoadOverlay - @loadOverlay @currentJob.layer, @currentJob.src - when Filter.Type.Plugin - @executePlugin() - else - @executeFilter() - - execute: (callback) -> - @finishedFn = callback - @modPixelData = Util.dataArray(@c.pixelData.length) - - @processNext() - - eachBlock: (fn) -> - # Prepare all the required render data - @blocksDone = 0 - - n = @c.pixelData.length - blockPixelLength = Math.floor (n / 4) / Renderer.Blocks - blockN = blockPixelLength * 4 - lastBlockN = blockN + ((n / 4) % Renderer.Blocks) * 4 - - for i in [0...Renderer.Blocks] - start = i * blockN - end = start + (if i is Renderer.Blocks - 1 then lastBlockN else blockN) - - if Caman.NodeJS - f = Fiber => fn.call(@, i, start, end) - bnum = f.run() - @blockFinished(bnum) - else - setTimeout do (i, start, end) => - => fn.call(@, i, start, end) - , 0 - - # The core of the image rendering, this function executes the provided filter. - # - # NOTE: this does not write the updated pixel data to the canvas. That happens when all filters - # are finished rendering in order to be as fast as possible. - executeFilter: -> - Event.trigger @c, "processStart", @currentJob - - if @currentJob.type is Filter.Type.Single - @eachBlock @renderBlock - else - @eachBlock @renderKernel - - # Executes a standalone plugin - executePlugin: -> - Log.debug "Executing plugin #{@currentJob.plugin}" - Plugin.execute @c, @currentJob.plugin, @currentJob.args - Log.debug "Plugin #{@currentJob.plugin} finished!" - - @processNext() - - # Renders a single block of the canvas with the current filter function - renderBlock: (bnum, start, end) -> - Log.debug "Block ##{bnum} - Filter: #{@currentJob.name}, Start: #{start}, End: #{end}" - Event.trigger @c, "blockStarted", - blockNum: bnum - totalBlocks: Renderer.Blocks - startPixel: start - endPixel: end - - data = r: 0, g: 0, b: 0, a: 0 - pixelInfo = new PixelInfo @c - - for i in [start...end] by 4 - pixelInfo.loc = i - - data.r = @c.pixelData[i] - data.g = @c.pixelData[i+1] - data.b = @c.pixelData[i+2] - data.a = @c.pixelData[i+3] - - res = @currentJob.processFn.call pixelInfo, data - res.a = data.a if not res.a? - - @c.pixelData[i] = Util.clampRGB res.r - @c.pixelData[i+1] = Util.clampRGB res.g - @c.pixelData[i+2] = Util.clampRGB res.b - @c.pixelData[i+3] = Util.clampRGB res.a - - if Caman.NodeJS - Fiber.yield(bnum) - else - @blockFinished bnum - - # Applies an image kernel to the canvas - renderKernel: (bnum, start, end) -> - name = @currentJob.name - bias = @currentJob.bias - divisor = @currentJob.divisor - n = @c.pixelData.length - - adjust = @currentJob.adjust - adjustSize = Math.sqrt adjust.length - - kernel = [] - - Log.debug "Rendering kernel - Filter: #{@currentJob.name}" - - start = Math.max start, @c.dimensions.width * 4 * ((adjustSize - 1) / 2) - end = Math.min end, n - (@c.dimensions.width * 4 * ((adjustSize - 1) / 2)) - - builder = (adjustSize - 1) / 2 - - pixelInfo = new PixelInfo @c - - for i in [start...end] by 4 - pixelInfo.loc = i - builderIndex = 0 - - for j in [-builder..builder] - for k in [builder..-builder] - pixel = pixelInfo.getPixelRelative j, k - kernel[builderIndex * 3] = pixel.r - kernel[builderIndex * 3 + 1] = pixel.g - kernel[builderIndex * 3 + 2] = pixel.b - - builderIndex++ - - res = @processKernel adjust, kernel, divisor, bias - - @modPixelData[i] = Util.clampRGB(res.r) - @modPixelData[i+1] = Util.clampRGB(res.g) - @modPixelData[i+2] = Util.clampRGB(res.b) - @modPixelData[i+3] = @c.pixelData[i+3] - - if Caman.NodeJS - Fiber.yield(bnum) - else - @blockFinished bnum - - # Called when a single block is finished rendering. Once all blocks are done, we signal that this - # filter is finished rendering and continue to the next step. - blockFinished: (bnum) -> - Log.debug "Block ##{bnum} finished! Filter: #{@currentJob.name}" if bnum >= 0 - @blocksDone++ - - Event.trigger @c, "blockFinished", - blockNum: bnum - blocksFinished: @blocksDone - totalBlocks: Renderer.Blocks - - if @blocksDone is Renderer.Blocks - if @currentJob.type is Filter.Type.Kernel - for i in [0...@c.pixelData.length] - @c.pixelData[i] = @modPixelData[i] - - Log.debug "Filter #{@currentJob.name} finished!" if bnum >=0 - Event.trigger @c, "processComplete", @currentJob - - @processNext() - - # The "filter function" for kernel adjustments. - processKernel: (adjust, kernel, divisor, bias) -> - val = r: 0, g: 0, b: 0 - - for i in [0...adjust.length] - val.r += adjust[i] * kernel[i * 3] - val.g += adjust[i] * kernel[i * 3 + 1] - val.b += adjust[i] * kernel[i * 3 + 2] - - val.r = (val.r / divisor) + bias - val.g = (val.g / divisor) + bias - val.b = (val.b / divisor) + bias - val - - # Loads an image onto the current canvas - loadOverlay: (layer, src) -> - img = document.createElement 'img' - img.onload = => - layer.context.drawImage img, 0, 0, @c.dimensions.width, @c.dimensions.height - layer.imageData = layer.context.getImageData 0, 0, @c.dimensions.width, @c.dimensions.height - layer.pixelData = layer.imageData.data - - @c.pixelData = layer.pixelData - - @processNext() - - proxyUrl = IO.remoteCheck src - img.src = if proxyUrl? then proxyUrl else src \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/store.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/store.coffee deleted file mode 100644 index ed7f1b722..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/store.coffee +++ /dev/null @@ -1,17 +0,0 @@ -# Used for storing instances of CamanInstance objects such that, when Caman() is called on an -# already initialized element, it returns that object instead of re-initializing. -Caman.Store = class Store - @items = {} - - @has: (search) -> @items[search]? - @get: (search) -> @items[search] - @put: (name, obj) -> @items[name] = obj - @execute: (search, callback) -> - setTimeout => - callback.call @get(search), @get(search) - , 0 - - return @get(search) - - @flush: (name = false) -> - if name then delete @items[name] else @items = {} \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/core/util.coffee b/mig/images/lib/CamanJS-4.1.1/src/core/util.coffee deleted file mode 100644 index 38d0ce251..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/core/util.coffee +++ /dev/null @@ -1,42 +0,0 @@ -# Look what you make me do Javascript -slice = Array::slice - -# DOM simplifier (no jQuery dependency) -# NodeJS compatible -$ = (sel, root = document) -> - return sel if typeof sel is "object" or exports? - root.querySelector sel - -class Util - # Unique value utility - @uniqid = do -> - id = 0 - get: -> id++ - - # Helper function that extends one object with all the properies of other objects - @extend = (obj) -> - dest = obj - src = slice.call arguments, 1 - - for copy in src - for own prop of copy - dest[prop] = copy[prop] - - return dest - - # In order to stay true to the latest spec, RGB values must be clamped between - # 0 and 255. If we don't do this, weird things happen. - @clampRGB = (val) -> - return 0 if val < 0 - return 255 if val > 255 - val - - @copyAttributes: (from, to, opts={}) -> - for attr in from.attributes - continue if opts.except? and attr.nodeName in opts.except - to.setAttribute(attr.nodeName, attr.nodeValue) - - # Support for browsers that don't know Uint8Array (such as IE9) - @dataArray: (length = 0) -> - return new Uint8Array(length) if Caman.NodeJS or window.Uint8Array? - return new Array(length) \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/lib/blenders.coffee b/mig/images/lib/CamanJS-4.1.1/src/lib/blenders.coffee deleted file mode 100644 index 452755655..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/lib/blenders.coffee +++ /dev/null @@ -1,81 +0,0 @@ -# Directly apply the child layer's pixels to the parent layer with no special changes -Blender.register "normal", (rgbaLayer, rgbaParent) -> - r: rgbaLayer.r - g: rgbaLayer.g - b: rgbaLayer.b - -# Apply the child to the parent by multiplying the color values. This generally creates contrast. -Blender.register "multiply", (rgbaLayer, rgbaParent) -> - r: (rgbaLayer.r * rgbaParent.r) / 255 - g: (rgbaLayer.g * rgbaParent.g) / 255 - b: (rgbaLayer.b * rgbaParent.b) / 255 - -Blender.register "screen", (rgbaLayer, rgbaParent) -> - r: 255 - (((255 - rgbaLayer.r) * (255 - rgbaParent.r)) / 255) - g: 255 - (((255 - rgbaLayer.g) * (255 - rgbaParent.g)) / 255) - b: 255 - (((255 - rgbaLayer.b) * (255 - rgbaParent.b)) / 255) - - -Blender.register "overlay", (rgbaLayer, rgbaParent) -> - result = {} - result.r = - if rgbaParent.r > 128 - 255 - 2 * (255 - rgbaLayer.r) * (255 - rgbaParent.r) / 255 - else (rgbaParent.r * rgbaLayer.r * 2) / 255 - - result.g = - if rgbaParent.g > 128 - 255 - 2 * (255 - rgbaLayer.g) * (255 - rgbaParent.g) / 255 - else (rgbaParent.g * rgbaLayer.g * 2) / 255 - - result.b = - if rgbaParent.b > 128 - 255 - 2 * (255 - rgbaLayer.b) * (255 - rgbaParent.b) / 255 - else (rgbaParent.b * rgbaLayer.b * 2) / 255 - - result - -Blender.register "difference", (rgbaLayer, rgbaParent) -> - r: rgbaLayer.r - rgbaParent.r - g: rgbaLayer.g - rgbaParent.g - b: rgbaLayer.b - rgbaParent.b - -Blender.register "addition", (rgbaLayer, rgbaParent) -> - r: rgbaParent.r + rgbaLayer.r - g: rgbaParent.g + rgbaLayer.g - b: rgbaParent.b + rgbaLayer.b - -Blender.register "exclusion", (rgbaLayer, rgbaParent) -> - r: 128 - 2 * (rgbaParent.r - 128) * (rgbaLayer.r - 128) / 255 - g: 128 - 2 * (rgbaParent.g - 128) * (rgbaLayer.g - 128) / 255 - b: 128 - 2 * (rgbaParent.b - 128) * (rgbaLayer.b - 128) / 255 - -Blender.register "softLight", (rgbaLayer, rgbaParent) -> - result = {} - - result.r = - if rgbaParent.r > 128 - 255 - ((255 - rgbaParent.r) * (255 - (rgbaLayer.r - 128))) / 255 - else (rgbaParent.r * (rgbaLayer.r + 128)) / 255 - - result.g = - if rgbaParent.g > 128 - 255 - ((255 - rgbaParent.g) * (255 - (rgbaLayer.g - 128))) / 255 - else (rgbaParent.g * (rgbaLayer.g + 128)) / 255 - - result.b = - if rgbaParent.b > 128 - 255 - ((255 - rgbaParent.b) * (255 - (rgbaLayer.b - 128))) / 255 - else (rgbaParent.b * (rgbaLayer.b + 128)) / 255 - - result - -Blender.register "lighten", (rgbaLayer, rgbaParent) -> - r: if rgbaParent.r > rgbaLayer.r then rgbaParent.r else rgbaLayer.r - g: if rgbaParent.g > rgbaLayer.g then rgbaParent.g else rgbaLayer.g - b: if rgbaParent.b > rgbaLayer.b then rgbaParent.b else rgbaLayer.b - -Blender.register "darken", (rgbaLayer, rgbaParent) -> - r: if rgbaParent.r > rgbaLayer.r then rgbaLayer.r else rgbaParent.r - g: if rgbaParent.g > rgbaLayer.g then rgbaLayer.g else rgbaParent.g - b: if rgbaParent.b > rgbaLayer.b then rgbaLayer.b else rgbaParent.b \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/lib/filters.coffee b/mig/images/lib/CamanJS-4.1.1/src/lib/filters.coffee deleted file mode 100644 index c62cde4bb..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/lib/filters.coffee +++ /dev/null @@ -1,373 +0,0 @@ -# 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](http://camanjs.com/docs/plugin-creation) page, and for information on using -# the plugins, check out the [Built-In Functionality](http://camanjs.com/docs/built-in) 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](http://en.wikipedia.org/wiki/Hue). -# -# ### 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 - - rgb = Convert.hsvToRGB hsv.h, hsv.s, hsv.v - rgb.a = rgba.a - rgb - -# ## 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] - diff --git a/mig/images/lib/CamanJS-4.1.1/src/lib/size.coffee b/mig/images/lib/CamanJS-4.1.1/src/lib/size.coffee deleted file mode 100644 index 4ff7477ec..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/lib/size.coffee +++ /dev/null @@ -1,63 +0,0 @@ -# Allows us to crop the canvas and produce a new smaller -# canvas. -Caman.Plugin.register "crop", (width, height, x = 0, y = 0) -> - # Create our new canvas element - if exports? - canvas = new Canvas width, height - else - canvas = document.createElement 'canvas' - Util.copyAttributes @canvas, canvas - - canvas.width = width - canvas.height = height - - ctx = canvas.getContext '2d' - - # Perform the cropping by drawing to the new canvas - ctx.drawImage @canvas, x, y, width, height, 0, 0, width, height - - @cropCoordinates = x: x, y: y - - # Update all of the references - @cropped = true - @replaceCanvas canvas - -# Resize the canvas and the image to a new size -Caman.Plugin.register "resize", (newDims = null) -> - # Calculate new size - if newDims is null or (!newDims.width? and !newDims.height?) - Log.error "Invalid or missing dimensions given for resize" - return - - if not newDims.width? - # Calculate width - newDims.width = @canvas.width * newDims.height / @canvas.height - else if not newDims.height? - # Calculate height - newDims.height = @canvas.height * newDims.width / @canvas.width - - if exports? - canvas = new Canvas newDims.width, newDims.height - else - canvas = document.createElement 'canvas' - Util.copyAttributes @canvas, canvas - - canvas.width = newDims.width - canvas.height = newDims.height - - ctx = canvas.getContext '2d' - - ctx.drawImage @canvas, - 0, 0, - @canvas.width, @canvas.height, - 0, 0, - newDims.width, newDims.height - - @resized = true - @replaceCanvas canvas - -Caman.Filter.register "crop", -> - @processPlugin "crop", Array.prototype.slice.call(arguments, 0) - -Caman.Filter.register "resize", -> - @processPlugin "resize", Array.prototype.slice.call(arguments, 0) \ No newline at end of file diff --git a/mig/images/lib/CamanJS-4.1.1/src/plugins/src/idmc.coffee b/mig/images/lib/CamanJS-4.1.1/src/plugins/src/idmc.coffee deleted file mode 100644 index bc69b234b..000000000 --- a/mig/images/lib/CamanJS-4.1.1/src/plugins/src/idmc.coffee +++ /dev/null @@ -1,107 +0,0 @@ -### -IDMC plugins -### - -# resetOriginalPixelData is broken, this is the way it should be - -Caman.Filter.register "idmc_reset_original_pixeldata", () -> - @processPlugin "idmc_reset_original_pixeldata", null - -Caman.Plugin.register "idmc_reset_original_pixeldata", () -> - - Log.debug "idmc_reset_original_pixeldata" - - @originalPixelData = Util.dataArray(@pixelData.length) - @originalPixelData[i] = pixel for pixel, i in @pixelData - @ - -# Adjust minimum and maximim pixel values - -Caman.Filter.register "idmc_set_min_max_pixel_values", (min_pixel_value, max_pixel_value) -> - @processPlugin "idmc_set_min_max_pixel_values", [min_pixel_value, max_pixel_value] - - -Caman.Plugin.register "idmc_set_min_max_pixel_values", (min_pixel_value, max_pixel_value) -> - org_pixels = @originalPixelData - pixels = @pixelData - width = @dimensions.width - height = @dimensions.height - - if not @idmc_set_min_max_pixel_values_r_colormap? - @idmc_set_min_max_pixel_values_r_colormap = (i for i in [0...256]) - - if not @idmc_set_min_max_pixel_values_g_colormap? - @idmc_set_min_max_pixel_values_g_colormap = (i for i in [0...256]) - - if not @idmc_set_min_max_pixel_values_b_colormap? - @idmc_set_min_max_pixel_values_b_colormap = (i for i in [0...256]) - - r_colormap = @idmc_set_min_max_pixel_values_r_colormap - g_colormap = @idmc_set_min_max_pixel_values_g_colormap - b_colormap = @idmc_set_min_max_pixel_values_b_colormap - - - idx = (x,y) => (y*width + x) * 4 - - for i in [0...256] - #Log.debug "i: " +i+ ", " +min_pixel_value+ ", " + "max_pixel_value" - - - index = Math.round((256 * (i - min_pixel_value)) / (max_pixel_value - min_pixel_value)); - #Log.debug "index1: " +index - - index = if index < 0 - 0 - else if i > 255 - 255 - else - index - - #Log.debug "index2: " +index - - r_colormap[i] = if i < min_pixel_value - 0 - else if i > max_pixel_value - 255 - else - index - - g_colormap[i] = if i < min_pixel_value - 0 - else if i > max_pixel_value - 255 - else - index - - b_colormap[i] = if i < min_pixel_value - 0 - else if i > max_pixel_value - 255 - else - index - - - for y in [0...height] - for x in [0...width] - #Log.debug "org_pixels[" +x+ "," +y+ "]: " + org_pixels[idx(x,y)] - - r = org_pixels[idx(x,y)] - g = org_pixels[idx(x,y) + 1] - b = org_pixels[idx(x,y) + 2] - a = org_pixels[idx(x,y) + 3] - - pixels[idx(x,y)] = r_colormap[r] - pixels[idx(x,y) + 1] = g_colormap[g] - pixels[idx(x,y) + 2] = b_colormap[b] - pixels[idx(x,y) + 3] = 255 - - @ - -Caman.Filter.register "idmc_test", () -> - @processPlugin "idmc_test", [] - -Caman.Plugin.register "idmc_test", () -> - @process "idmc_test", (rgba) -> - Log.debug "IDMC test func" - rgba -@ \ No newline at end of file diff --git a/mig/images/lib/ParaView b/mig/images/lib/ParaView deleted file mode 120000 index e5c896f8d..000000000 --- a/mig/images/lib/ParaView +++ /dev/null @@ -1 +0,0 @@ -ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit \ No newline at end of file diff --git a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/Visualizer b/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/Visualizer deleted file mode 120000 index 15c8f728d..000000000 --- a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/Visualizer +++ /dev/null @@ -1 +0,0 @@ -share/paraview-4.3/www/apps/Visualizer \ No newline at end of file diff --git a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/ext b/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/ext deleted file mode 120000 index 01b65eae8..000000000 --- a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/ext +++ /dev/null @@ -1 +0,0 @@ -share/paraview-4.3/www/ext \ No newline at end of file diff --git a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/lib b/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/lib deleted file mode 120000 index b5d20949c..000000000 --- a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/lib +++ /dev/null @@ -1 +0,0 @@ -share/paraview-4.3/www/lib \ No newline at end of file diff --git a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/share/appdata/paraview.appdata.xml b/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/share/appdata/paraview.appdata.xml deleted file mode 100644 index 308a703d9..000000000 --- a/mig/images/lib/ParaView-4.3.1-965-g513d15e-Qt4-Linux-64bit/share/appdata/paraview.appdata.xml +++ /dev/null @@ -1,30 +0,0 @@ - - -
- ParaView is an open-source, multi-platform data analysis and visualization - application. ParaView users can quickly build visualizations to analyze - their data using qualitative and quantitative techniques. The data - exploration can be done interactively in 3D or programmatically using - ParaView’s batch processing capabilities. -
-- ParaView was developed to analyze extremely large datasets using distributed - memory computing resources. It can be run on supercomputers to analyze - datasets of exascale size as well as on laptops for smaller data. -
-- Select dataset to load for probing. Any previously loaded dataset - will be unloaded. -
--
Connecting ...
-