├── .gitignore
├── demo
├── images
│ ├── plus.png
│ ├── minus.png
│ └── help-browser-2.png
├── static
│ └── css
│ │ ├── nist_logo.png
│ │ ├── nist_logo_mark.png
│ │ └── NISTPages.css
├── style.css
├── spinner.js
├── spinner-slider.js
├── index.html
└── demo.js
├── .eslintrc.json
├── LICENSE
├── package.json
├── webpack.config.js
├── README.md
└── openseadragon-filtering.js
/.gitignore:
--------------------------------------------------------------------------------
1 | nbproject/
2 | .directory
3 | dist/
4 | node_modules/
5 |
--------------------------------------------------------------------------------
/demo/images/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/images/plus.png
--------------------------------------------------------------------------------
/demo/images/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/images/minus.png
--------------------------------------------------------------------------------
/demo/images/help-browser-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/images/help-browser-2.png
--------------------------------------------------------------------------------
/demo/static/css/nist_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/static/css/nist_logo.png
--------------------------------------------------------------------------------
/demo/static/css/nist_logo_mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/static/css/nist_logo_mark.png
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./node_modules/eslint-config-defaults/configurations/google.js",
3 | "rules": {
4 | "indent": [2, 4],
5 | "newline-after-var": 0,
6 | "dot-location": [2, "property"]
7 | },
8 | "globals": {
9 | "window": true,
10 | "define": true,
11 | "require": true,
12 | "Caman": true
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This software was developed at the National Institute of Standards and
2 | Technology by employees of the Federal Government in the course of
3 | their official duties. Pursuant to title 17 Section 105 of the United
4 | States Code this software is not subject to copyright protection and is
5 | in the public domain. This software is an experimental system. NIST assumes
6 | no responsibility whatsoever for its use by other parties, and makes no
7 | guarantees, expressed or implied, about its quality, reliability, or
8 | any other characteristic. We would appreciate acknowledgement if the
9 | software is used.
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "openseadragon-filtering",
3 | "version": "1.0.0",
4 | "description": "OpenSeadragon plugin providing filtering capabilities to the images.",
5 | "main": "openseadragon-filtering.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/usnistgov/OpenSeadragonFiltering.git"
9 | },
10 | "keywords": [
11 | "openseadragon",
12 | "filtering",
13 | "image",
14 | "processing",
15 | "zoom",
16 | "seadragon",
17 | "deepzoom"
18 | ],
19 | "author": "Antoine Vandecreme",
20 | "license": "SEE LICENSE IN LICENSE",
21 | "bugs": {
22 | "url": "https://github.com/usnistgov/OpenSeadragonFiltering/issues"
23 | },
24 | "homepage": "https://github.com/usnistgov/OpenSeadragonFiltering#readme",
25 | "dependencies": {
26 | "openseadragon": ">=2.1"
27 | },
28 | "devDependencies": {
29 | "copy-webpack-plugin": "^5.0.0",
30 | "css-loader": "^0.23.1",
31 | "eslint": "^2.1.0",
32 | "eslint-config-defaults": "^9.0.0",
33 | "eslint-loader": "^1.3.0",
34 | "file-loader": "^3.0.1",
35 | "jquery": "^3.3.0",
36 | "style-loader": "^0.13.0",
37 | "webpack": "^4.29.5",
38 | "webpack-cli": "^3.2.3",
39 | "webpack-jquery-ui": "^2.0.1"
40 | },
41 | "scripts": {
42 | "build": "webpack",
43 | "watch": "webpack --watch",
44 | "clean": "rm -rf dist"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/demo/style.css:
--------------------------------------------------------------------------------
1 | /*
2 | This software was developed at the National Institute of Standards and
3 | Technology by employees of the Federal Government in the course of
4 | their official duties. Pursuant to title 17 Section 105 of the United
5 | States Code this software is not subject to copyright protection and is
6 | in the public domain. This software is an experimental system. NIST assumes
7 | no responsibility whatsoever for its use by other parties, and makes no
8 | guarantees, expressed or implied, about its quality, reliability, or
9 | any other characteristic. We would appreciate acknowledgement if the
10 | software is used.
11 | */
12 | .demo {
13 | line-height: normal;
14 | }
15 |
16 | .demo h3 {
17 | margin-top: 5px;
18 | margin-bottom: 5px;
19 | }
20 |
21 | #openseadragon {
22 | width: 100%;
23 | height: 700px;
24 | background-color: black;
25 | }
26 |
27 | .wdzt-table-layout {
28 | display: table;
29 | }
30 |
31 | .wdzt-row-layout {
32 | display: table-row;
33 | }
34 |
35 | .wdzt-cell-layout {
36 | display: table-cell;
37 | }
38 |
39 | .wdzt-full-width {
40 | width: 100%;
41 | }
42 |
43 | .wdzt-menu-slider {
44 | margin-left: 10px;
45 | margin-right: 10px;
46 | }
47 |
48 | .column-2 {
49 | width: 50%;
50 | vertical-align: top;
51 | padding: 3px;
52 | }
53 |
54 | #available {
55 | list-style-type: none;
56 | }
57 |
58 | ul {
59 | padding: 0;
60 | border: 1px solid black;
61 | min-height: 25px;
62 | }
63 |
64 | li {
65 | padding: 3px;
66 | }
67 |
68 | #selected {
69 | list-style-type: none;
70 | }
71 |
72 | .button {
73 | cursor: pointer;
74 | vertical-align: text-top;
75 | }
76 |
77 | .filterLabel {
78 | min-width: 120px;
79 | }
80 |
81 | #selected .filterLabel {
82 | cursor: move;
83 | }
84 |
--------------------------------------------------------------------------------
/demo/static/css/NISTPages.css:
--------------------------------------------------------------------------------
1 | .nist {
2 | margin: 0;
3 | padding: 0;
4 | }
5 | .nist-header {
6 | box-sizing: border-box;
7 | background: #000;
8 | color: #fff;
9 | padding: 10px 20px 4px;
10 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
11 | position: relative;
12 | margin-bottom: 0px;
13 | }
14 | .nist-header:after {
15 | content: "";
16 | display: table;
17 | clear: both;
18 | }
19 | .nist-header * {
20 | box-sizing: inherit;
21 | }
22 | .nist-header a, .nist-header a:link, .nist-header a:visited {
23 | color: #EEE;
24 | text-decoration: none;
25 | }
26 | .nist-header h1 {
27 | margin: 0 10px 0 0;
28 | float: left;
29 | font-size: 0;
30 | }
31 | .nist-header h1 a {
32 | font-weight: normal;
33 | font-size: 20px;
34 | height: 40px;
35 | width: 280px;
36 | background: url('nist_logo.png') no-repeat;
37 | background-size: 280px auto;
38 | text-indent: -9999px;
39 | overflow: hidden;
40 | display: inline-block;
41 | }
42 | .nist-header-text {
43 | position: absolute;
44 | left: -9999px;
45 | background: yellow;
46 | }
47 | .nist-links {
48 | float: right;
49 | }
50 | a.nist-links-button {
51 | display: inline-block;
52 | padding: 6px;
53 | margin: 0 2px;
54 | border-radius: 3px;
55 | border: 1px solid #666;
56 | font-size: 14px;
57 | }
58 | .nist-header a.nist-links-button:hover,
59 | .nist-header a.nist-links-button:focus {
60 | background: #ccc;
61 | color: #333;
62 | }
63 |
64 | @media screen and (max-width: 700px) {
65 | .nist-header {
66 | padding: 6px 10px;
67 | }
68 | .nist-header h1 a {
69 | height: 26px;
70 | width: 80px;
71 | background: url('nist_logo_mark.png') 0 4px no-repeat;
72 | background-size: 80px auto;
73 | }
74 | .mobile-hide {
75 | display:none !important;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/demo/spinner.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This software was developed at the National Institute of Standards and
3 | * Technology by employees of the Federal Government in the course of
4 | * their official duties. Pursuant to title 17 Section 105 of the United
5 | * States Code this software is not subject to copyright protection and is
6 | * in the public domain. This software is an experimental system. NIST assumes
7 | * no responsibility whatsoever for its use by other parties, and makes no
8 | * guarantees, expressed or implied, about its quality, reliability, or
9 | * any other characteristic. We would appreciate acknowledgement if the
10 | * software is used.
11 | */
12 |
13 | define('spinner', function() {
14 | /**
15 | * This class is an improvement over the basic jQuery spinner to support
16 | * 'Enter' to update the value (with validity checks).
17 | * @param {Object} options Options object
18 | * @return {Spinner} A spinner object
19 | */
20 | return function(options) {
21 |
22 | options.$element.html('');
24 |
25 | var $spinner = options.$element.find('input');
26 | var value = options.init;
27 | $spinner.spinner({
28 | min: options.min,
29 | max: options.max,
30 | step: options.step,
31 | spin: function(event, ui) {
32 | /*jshint unused:true */
33 | value = ui.value;
34 | options.updateCallback(value);
35 | }
36 | });
37 | $spinner.val(value);
38 | $spinner.keyup(function(e) {
39 | if (e.which === 13) {
40 | if (!this.value.match(/^-?\d?\.?\d*$/)) {
41 | this.value = options.init;
42 | } else if (options.min !== undefined &&
43 | this.value < options.min) {
44 | this.value = options.min;
45 | } else if (options.max !== undefined &&
46 | this.value > options.max) {
47 | this.value = options.max;
48 | }
49 | value = this.value;
50 | options.updateCallback(value);
51 | }
52 | });
53 |
54 | this.getValue = function() {
55 | return value;
56 | };
57 |
58 | };
59 |
60 | });
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var path = require('path');
3 | var CopyWebpackPlugin = require('copy-webpack-plugin');
4 |
5 | module.exports = {
6 | mode: "development",
7 | context: __dirname,
8 | entry: ['./demo/demo.js'],
9 | output: {
10 | filename: 'demo-bundle.js',
11 | path: path.resolve(__dirname, 'dist'),
12 | publicPath:'dist/'
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.js$/,
18 | enforce: "pre",
19 | loader: 'eslint-loader',
20 | exclude: /node_modules/,
21 | options: {
22 | configFile: '.eslintrc.json'
23 | }
24 | },
25 | {
26 | test: /\.(jpe?g|png|gif)$/i,
27 | loader: 'file-loader',
28 | query: {
29 | name: '[name].[ext]',
30 | outputPath: 'images/'
31 | },
32 | },
33 | {
34 | test: /\.css$/,
35 | include: /node_modules/,
36 | use: ['style-loader', 'css-loader']
37 | },
38 | {
39 | test: /\.css$/,
40 | exclude: /node_modules/,
41 | use: ['style-loader', 'css-loader']
42 | }
43 | ]
44 | },
45 | resolve: {
46 | // options for resolving module requests
47 | // (does not apply to resolving to loaders)
48 | modules: [
49 | "node_modules",
50 | path.join(__dirname, "demo")
51 | ],
52 | // directories where to look for modules
53 | extensions: [".js", ".json", ".jsx", ".css"]
54 | },
55 | plugins: [
56 | new CopyWebpackPlugin([{
57 | from: 'node_modules/openseadragon/build/openseadragon/images',
58 | to: 'images'
59 | }, {
60 | from: 'demo/images',
61 | to: 'images'
62 | }, {
63 | from: 'demo/static',
64 | to: 'static'
65 | }, {
66 | from: 'demo/*'
67 | }
68 | ]),
69 | new webpack.LoaderOptionsPlugin({
70 | test: /\.js$/,
71 | context: __dirname,
72 | debug: true,
73 | options: {
74 | eslint: {
75 | configFile: '.eslintrc.json'
76 | }
77 | }
78 | }),
79 | new webpack.ProvidePlugin(
80 | {
81 | $: 'jquery',
82 | jQuery: 'jquery',
83 | 'window.jQuery': 'jquery',
84 | 'window.$': 'jquery'
85 | }
86 | )
87 | ]
88 | };
89 |
--------------------------------------------------------------------------------
/demo/spinner-slider.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This software was developed at the National Institute of Standards and
3 | * Technology by employees of the Federal Government in the course of
4 | * their official duties. Pursuant to title 17 Section 105 of the United
5 | * States Code this software is not subject to copyright protection and is
6 | * in the public domain. This software is an experimental system. NIST assumes
7 | * no responsibility whatsoever for its use by other parties, and makes no
8 | * guarantees, expressed or implied, about its quality, reliability, or
9 | * any other characteristic. We would appreciate acknowledgement if the
10 | * software is used.
11 | */
12 |
13 | define('spinner-slider', function() {
14 |
15 | var idIncrement = 0;
16 |
17 | return function(options) {
18 |
19 | this.hash = idIncrement++;
20 |
21 | var spinnerId = 'wdzt-spinner-slider-spinner-' + this.hash;
22 | var sliderId = 'wdzt-spinner-slider-slider-' + this.hash;
23 |
24 | var value = options.init;
25 |
26 |
27 | options.$element.html(
28 | '
' +
29 | '
' +
30 | '
' +
31 | ' ' +
33 | '
' +
34 | '
' +
35 | ' ' +
37 | '
' +
38 | '
' +
39 | '
');
40 |
41 | var $slider = options.$element.find('#' + sliderId)
42 | .slider({
43 | min: options.min,
44 | max: options.sliderMax !== undefined ?
45 | options.sliderMax : options.max,
46 | step: options.step,
47 | value: value,
48 | slide: function(event, ui) {
49 | /*jshint unused:true */
50 | value = ui.value;
51 | $spinner.spinner('value', value);
52 | options.updateCallback(value);
53 | }
54 | });
55 | var $spinner = options.$element.find('#' + spinnerId)
56 | .spinner({
57 | min: options.min,
58 | max: options.max,
59 | step: options.step,
60 | spin: function(event, ui) {
61 | /*jshint unused:true */
62 | value = ui.value;
63 | $slider.slider('value', value);
64 | options.updateCallback(value);
65 | }
66 | });
67 | $spinner.val(value);
68 | $spinner.keyup(function(e) {
69 | if (e.which === 13) {
70 | value = $spinner.spinner('value');
71 | $slider.slider('value', value);
72 | options.updateCallback(value);
73 | }
74 | });
75 |
76 |
77 | this.getValue = function() {
78 | return value;
79 | };
80 |
81 | };
82 | });
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 | OpenSeadragon Filtering
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
37 |
38 |
39 | OpenSeadragon filtering plugin demo.
40 |
41 |
42 |
43 |
44 | Demo of the OpenSeadragon filtering plugin.
45 | Code and documentation are available on
46 | GitHub.
47 |
48 |
49 | Add/remove filters to visualize the effects.
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
Available filters
61 |
63 |
64 |
Selected filters
65 |
66 |
67 |
Drag and drop the selected filters to set their order.
68 |
69 |
70 |
71 |
72 |
73 |
83 |
84 |
85 |
95 |
96 |
97 |
98 |
99 |
100 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This [OpenSeadragon](http://openseadragon.github.io/) plugin provides
2 | the capability to add filters to the images.
3 |
4 | A demo is available [here](https://pages.nist.gov/OpenSeadragonFiltering/).
5 |
6 | This plugin requires OpenSeadragon 2.1+.
7 |
8 | ### Basic usage
9 |
10 | Increase the brightness:
11 | `````javascript
12 | var viewer = new OpenSeadragon.Viewer(...);
13 | viewer.setFilterOptions({
14 | filters: {
15 | processors: OpenSeadragon.Filters.BRIGHTNESS(50)
16 | }
17 | });
18 | `````
19 |
20 | Decrease the brightness and invert the image:
21 | `````javascript
22 | var viewer = new OpenSeadragon.Viewer(...);
23 | viewer.setFilterOptions({
24 | filters: {
25 | processors: [
26 | OpenSeadragon.Filters.BRIGHTNESS(-50),
27 | OpenSeadragon.Filters.INVERT()
28 | ]
29 | }
30 | });
31 | `````
32 |
33 | ### Specify on which items (TiledImage) to apply the filters
34 |
35 | Increase the brightness on item 0 and invert items 1 and 2:
36 | `````javascript
37 | var viewer = new OpenSeadragon.Viewer(...);
38 | viewer.setFilterOptions({
39 | filters: [{
40 | items: viewer.world.getItemAt(0),
41 | processors: [
42 | OpenSeadragon.Filters.BRIGHTNESS(50)
43 | ]
44 | }, {
45 | items: [viewer.world.getItemAt(1), viewer.world.getItemAt(2)],
46 | processors: [
47 | OpenSeadragon.Filters.INVERT()
48 | ]
49 | }]
50 | });
51 | `````
52 | Note the items property. If it is not specified, the filter is applied to all
53 | items.
54 |
55 | ### Load mode optimization
56 |
57 | By default, the filters are applied asynchronously. This means that the tiles are
58 | cleared from the canvas and re-downloaded (note that the browser probably cached
59 | them though) before having the filter applied. This avoids to hang the browser
60 | if the filtering operation is slow. It also allows to use asynchronous filters
61 | like the ones provided by [CamanJS](http://camanjs.com).
62 |
63 | However, if you have only fast and synchronous filters, you can force the
64 | synchronous mode by setting `loadMode: 'sync'`:
65 |
66 | `````javascript
67 | var viewer = new OpenSeadragon.Viewer(...);
68 | viewer.setFilterOptions({
69 | filters: {
70 | processors: OpenSeadragon.Filters.BRIGHTNESS(50)
71 | },
72 | loadMode: 'sync'
73 | });
74 | `````
75 |
76 | To visualize the difference between async and sync mode, one can compare how
77 | the brightness (sync) and contrast (async) filters load in the
78 | [demo](https://pages.nist.gov/OpenSeadragonFiltering/).
79 |
80 | ### Provided filters
81 |
82 | This plugin already include some filters which are accessible via
83 | OpenSeadragon.Filters:
84 |
85 | * Thresholding
86 |
87 | Set all pixels equals or above the specified threshold to white and the others
88 | to black. For colored images, the average of the 3 channels is compared to the
89 | threshold. The specified threshold must be between 0 and 255.
90 |
91 | `````javascript
92 | var viewer = new OpenSeadragon.Viewer(...);
93 | viewer.setFilterOptions({
94 | filters: {
95 | processors: OpenSeadragon.Filters.THRESHOLDING(threshold)
96 | }
97 | });
98 | `````
99 |
100 | * Brightness
101 |
102 | Shift the intensity of the pixels by the specified adjustment
103 | (between -255 and 255).
104 |
105 | `````javascript
106 | var viewer = new OpenSeadragon.Viewer(...);
107 | viewer.setFilterOptions({
108 | filters: {
109 | processors: OpenSeadragon.Filters.BRIGHTNESS(adjustment)
110 | }
111 | });
112 | `````
113 |
114 | * Invert
115 |
116 | Invert the colors of the image.
117 |
118 | `````javascript
119 | var viewer = new OpenSeadragon.Viewer(...);
120 | viewer.setFilterOptions({
121 | filters: {
122 | processors: OpenSeadragon.Filters.INVERT()
123 | }
124 | });
125 | `````
126 |
127 | * Morphological operations
128 |
129 | [Erosion](https://en.wikipedia.org/wiki/Erosion_%28morphology%29)
130 | and [dilation](https://en.wikipedia.org/wiki/Dilation_%28morphology%29)
131 | over a square kernel are supported by the generic
132 | [morphological operation](https://en.wikipedia.org/wiki/Mathematical_morphology)
133 | filter
134 |
135 | `````javascript
136 | var viewer = new OpenSeadragon.Viewer(...);
137 | viewer.setFilterOptions({
138 | filters: {
139 | processors: [
140 | // Erosion over a 3x3 kernel
141 | OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(3, Math.min),
142 |
143 | // Dilation over a 5x5 kernel
144 | OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(5, Math.max),
145 | ]
146 | }
147 | });
148 | `````
149 |
150 | * Convolution
151 |
152 | Apply a [convolution kernel](https://en.wikipedia.org/wiki/Kernel_%28image_processing%29#Convolution).
153 |
154 | `````javascript
155 | var viewer = new OpenSeadragon.Viewer(...);
156 | viewer.setFilterOptions({
157 | filters: {
158 | processors: OpenSeadragon.Filters.CONVOLUTION([
159 | 0, -1, 0,
160 | -1, 5, -1,
161 | 0, -1, 0])
162 | }
163 | });
164 | `````
165 |
166 | * Colormap
167 |
168 | Apply a [colormap](http://cxc.harvard.edu/ciao/threads/auxlut/index.html#auxlut) to the averaged RGB values of the image.
169 | Colormaps are defined by a series of [R,G,B] stops.
170 | Grayscale values of 0-255 are mapped to the interpolated stop values.
171 | A variable centerpoint allows adjustment of the colormap.
172 |
173 | `````javascript
174 | var viewer = new OpenSeadragon.Viewer(...);
175 | viewer.setFilterOptions({
176 | filters: {
177 | processors: OpenSeadragon.Filters.COLORMAP([
178 | [0, 0, 0],
179 | [0, 128, 0],
180 | [0, 250, 0],
181 | [0, 255, 0]], 128)
182 | }
183 | });
184 | `````
185 |
186 | ### Integration with CamanJS
187 |
188 | [CamanJS](http://camanjs.com) supports a wide range of filters. They can be
189 | reused with this plugin like this:
190 |
191 | `````javascript
192 | var viewer = new OpenSeadragon.Viewer(...);
193 | viewer.setFilterOptions({
194 | filters: {
195 | processors: function(context, callback) {
196 | Caman(context.canvas, function() {
197 | this.sepia(50);
198 | this.vibrance(40);
199 | // Do not forget to call this.render with the callback
200 | this.render(callback);
201 | });
202 | }
203 | }
204 | });
205 | `````
206 |
207 | Note: Caman is caching every canvas it processes. This causes two issues with
208 | this plugin:
209 | 1. It creates a memory leak because OpenSeadragon creates a lot of canvases
210 | which do not get garbage collected anymore.
211 | 2. Non-caman filters in between 2 camans filters get ignored.
212 |
213 | There isn't any clean way to
214 | [disable the cache system](https://github.com/meltingice/CamanJS/issues/185),
215 | however one can use this hack to prevent any caching:
216 |
217 | `````javascript
218 | Caman.Store.put = function() {};
219 |
220 | var viewer = new OpenSeadragon.Viewer(...);
221 | viewer.setFilterOptions({
222 | filters: {
223 | processors: [
224 | function(context, callback) {
225 | Caman(context.canvas, function() {
226 | this.sepia(50);
227 | // Do not forget to call this.render with the callback
228 | this.render(callback);
229 | });
230 | },
231 | OpenSeadragon.Filters.INVERT(),
232 | function(context, callback) {
233 | Caman(context.canvas, function() {
234 | this.vibrance(40);
235 | // Do not forget to call this.render with the callback
236 | this.render(callback);
237 | });
238 | }
239 | ]
240 | }
241 | });
242 | `````
243 |
244 | ### Implementing customs filters
245 |
246 | To implement a custom filter, one need to create a function taking a
247 | [2D context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D)
248 | and a callback as parameters. When that function is called by the plugin,
249 | the context will be a tile's canvas context. One should use
250 | [context.getImageData](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData)
251 | to retrieve the pixels values and
252 | [context.putImageData](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData)
253 | to save the modified pixels.
254 | The callback method must be called when the filtering is done. The provided
255 | filters are good examples for such implementations.
256 |
257 | ### Edge effects
258 |
259 | This plugin is working on tiles and does not currently handle tiles edges.
260 | This means that if you are using kernel based filters, you should expect
261 | edge effects around tiles.
262 |
263 | ### Build the demo
264 |
265 | To build the demo run `npm install` and then `npm run-script build`.
266 | The result of the build will be in the `dist` folder.
267 |
268 | ### Disclaimer:
269 |
270 | This software was developed at the National Institute of Standards and
271 | Technology by employees of the Federal Government in the course of
272 | their official duties. Pursuant to title 17 Section 105 of the United
273 | States Code this software is not subject to copyright protection and is
274 | in the public domain. This software is an experimental system. NIST assumes
275 | no responsibility whatsoever for its use by other parties, and makes no
276 | guarantees, expressed or implied, about its quality, reliability, or
277 | any other characteristic. We would appreciate acknowledgement if the
278 | software is used.
279 |
--------------------------------------------------------------------------------
/openseadragon-filtering.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This software was developed at the National Institute of Standards and
3 | * Technology by employees of the Federal Government in the course of
4 | * their official duties. Pursuant to title 17 Section 105 of the United
5 | * States Code this software is not subject to copyright protection and is
6 | * in the public domain. This software is an experimental system. NIST assumes
7 | * no responsibility whatsoever for its use by other parties, and makes no
8 | * guarantees, expressed or implied, about its quality, reliability, or
9 | * any other characteristic. We would appreciate acknowledgement if the
10 | * software is used.
11 | */
12 |
13 | /**
14 | *
15 | * @author Antoine Vandecreme
16 | */
17 | (function() {
18 |
19 | 'use strict';
20 |
21 | var $ = window.OpenSeadragon;
22 | if (!$) {
23 | $ = require('openseadragon');
24 | if (!$) {
25 | throw new Error('OpenSeadragon is missing.');
26 | }
27 | }
28 | // Requires OpenSeadragon >=2.1
29 | if (!$.version || $.version.major < 2 ||
30 | $.version.major === 2 && $.version.minor < 1) {
31 | throw new Error(
32 | 'Filtering plugin requires OpenSeadragon version >= 2.1');
33 | }
34 |
35 | $.Viewer.prototype.setFilterOptions = function(options) {
36 | if (!this.filterPluginInstance) {
37 | options = options || {};
38 | options.viewer = this;
39 | this.filterPluginInstance = new $.FilterPlugin(options);
40 | } else {
41 | setOptions(this.filterPluginInstance, options);
42 | }
43 | };
44 |
45 | /**
46 | * @class FilterPlugin
47 | * @param {Object} options The options
48 | * @param {OpenSeadragon.Viewer} options.viewer The viewer to attach this
49 | * plugin to.
50 | * @param {String} [options.loadMode='async'] Set to sync to have the filters
51 | * applied synchronously. It will only work if the filters are all synchronous.
52 | * Note that depending on how complex the filters are, it may also hang the browser.
53 | * @param {Object[]} options.filters The filters to apply to the images.
54 | * @param {OpenSeadragon.TiledImage[]} options.filters[x].items The tiled images
55 | * on which to apply the filter.
56 | * @param {function|function[]} options.filters[x].processors The processing
57 | * function(s) to apply to the images. The parameters of this function are
58 | * the context to modify and a callback to call upon completion.
59 | */
60 | $.FilterPlugin = function(options) {
61 | options = options || {};
62 | if (!options.viewer) {
63 | throw new Error('A viewer must be specified.');
64 | }
65 | var self = this;
66 | this.viewer = options.viewer;
67 |
68 | this.viewer.addHandler('tile-loaded', tileLoadedHandler);
69 | this.viewer.addHandler('tile-drawing', tileDrawingHandler);
70 |
71 | // filterIncrement allows to determine whether a tile contains the
72 | // latest filters results.
73 | this.filterIncrement = 0;
74 |
75 | setOptions(this, options);
76 |
77 |
78 | function tileLoadedHandler(event) {
79 | var processors = getFiltersProcessors(self, event.tiledImage);
80 | if (processors.length === 0) {
81 | return;
82 | }
83 | var tile = event.tile;
84 | var image = event.image;
85 | if (image !== null && image !== undefined) {
86 | var canvas = window.document.createElement('canvas');
87 | canvas.width = image.width;
88 | canvas.height = image.height;
89 | var context = canvas.getContext('2d');
90 | context.drawImage(image, 0, 0);
91 | tile._renderedContext = context;
92 | var callback = event.getCompletionCallback();
93 | applyFilters(context, processors, callback);
94 | tile._filterIncrement = self.filterIncrement;
95 | }
96 | }
97 |
98 |
99 | function applyFilters(context, filtersProcessors, callback) {
100 | if (callback) {
101 | var currentIncrement = self.filterIncrement;
102 | var callbacks = [];
103 | for (var i = 0; i < filtersProcessors.length - 1; i++) {
104 | (function(i) {
105 | callbacks[i] = function() {
106 | // If the increment has changed, stop the computation
107 | // chain immediately.
108 | if (self.filterIncrement !== currentIncrement) {
109 | return;
110 | }
111 | filtersProcessors[i + 1](context, callbacks[i + 1]);
112 | };
113 | })(i);
114 | }
115 | callbacks[filtersProcessors.length - 1] = function() {
116 | // If the increment has changed, do not call the callback.
117 | // (We don't want OSD to draw an outdated tile in the canvas).
118 | if (self.filterIncrement !== currentIncrement) {
119 | return;
120 | }
121 | callback();
122 | };
123 | filtersProcessors[0](context, callbacks[0]);
124 | } else {
125 | for (var i = 0; i < filtersProcessors.length; i++) {
126 | filtersProcessors[i](context, function() {
127 | });
128 | }
129 | }
130 | }
131 |
132 | function tileDrawingHandler(event) {
133 | var tile = event.tile;
134 | var rendered = event.rendered;
135 | if (rendered._filterIncrement === self.filterIncrement) {
136 | return;
137 | }
138 | var processors = getFiltersProcessors(self, event.tiledImage);
139 | if (processors.length === 0) {
140 | if (rendered._originalImageData) {
141 | // Restore initial data.
142 | rendered.putImageData(rendered._originalImageData, 0, 0);
143 | delete rendered._originalImageData;
144 | }
145 | rendered._filterIncrement = self.filterIncrement;
146 | return;
147 | }
148 |
149 | if (rendered._originalImageData) {
150 | // The tile has been previously filtered (by another filter),
151 | // restore it first.
152 | rendered.putImageData(rendered._originalImageData, 0, 0);
153 | } else {
154 | rendered._originalImageData = rendered.getImageData(
155 | 0, 0, rendered.canvas.width, rendered.canvas.height);
156 | }
157 |
158 | if (tile._renderedContext) {
159 | if (tile._filterIncrement === self.filterIncrement) {
160 | var imgData = tile._renderedContext.getImageData(0, 0,
161 | tile._renderedContext.canvas.width,
162 | tile._renderedContext.canvas.height);
163 | rendered.putImageData(imgData, 0, 0);
164 | delete tile._renderedContext;
165 | delete tile._filterIncrement;
166 | rendered._filterIncrement = self.filterIncrement;
167 | return;
168 | }
169 | delete tile._renderedContext;
170 | delete tile._filterIncrement;
171 | }
172 | applyFilters(rendered, processors);
173 | rendered._filterIncrement = self.filterIncrement;
174 | }
175 | };
176 |
177 | function setOptions(instance, options) {
178 | options = options || {};
179 | var filters = options.filters;
180 | instance.filters = !filters ? [] :
181 | $.isArray(filters) ? filters : [filters];
182 | for (var i = 0; i < instance.filters.length; i++) {
183 | var filter = instance.filters[i];
184 | if (!filter.processors) {
185 | throw new Error('Filter processors must be specified.');
186 | }
187 | filter.processors = $.isArray(filter.processors) ?
188 | filter.processors : [filter.processors];
189 | }
190 | instance.filterIncrement++;
191 |
192 | if (options.loadMode === 'sync') {
193 | instance.viewer.forceRedraw();
194 | } else {
195 | var itemsToReset = [];
196 | for (var i = 0; i < instance.filters.length; i++) {
197 | var filter = instance.filters[i];
198 | if (!filter.items) {
199 | itemsToReset = getAllItems(instance.viewer.world);
200 | break;
201 | }
202 | if ($.isArray(filter.items)) {
203 | for (var j = 0; j < filter.items.length; j++) {
204 | addItemToReset(filter.items[j], itemsToReset);
205 | }
206 | } else {
207 | addItemToReset(filter.items, itemsToReset);
208 | }
209 | }
210 | for (var i = 0; i < itemsToReset.length; i++) {
211 | itemsToReset[i].reset();
212 | }
213 | }
214 | }
215 |
216 | function addItemToReset(item, itemsToReset) {
217 | if (itemsToReset.indexOf(item) >= 0) {
218 | throw new Error('An item can not have filters ' +
219 | 'assigned multiple times.');
220 | }
221 | itemsToReset.push(item);
222 | }
223 |
224 | function getAllItems(world) {
225 | var result = [];
226 | for (var i = 0; i < world.getItemCount(); i++) {
227 | result.push(world.getItemAt(i));
228 | }
229 | return result;
230 | }
231 |
232 | function getFiltersProcessors(instance, item) {
233 | if (instance.filters.length === 0) {
234 | return [];
235 | }
236 |
237 | var globalProcessors = null;
238 | for (var i = 0; i < instance.filters.length; i++) {
239 | var filter = instance.filters[i];
240 | if (!filter.items) {
241 | globalProcessors = filter.processors;
242 | } else if (filter.items === item ||
243 | $.isArray(filter.items) && filter.items.indexOf(item) >= 0) {
244 | return filter.processors;
245 | }
246 | }
247 | return globalProcessors ? globalProcessors : [];
248 | }
249 |
250 | $.Filters = {
251 | THRESHOLDING: function(threshold) {
252 | if (threshold < 0 || threshold > 255) {
253 | throw new Error('Threshold must be between 0 and 255.');
254 | }
255 | return function(context, callback) {
256 | var imgData = context.getImageData(
257 | 0, 0, context.canvas.width, context.canvas.height);
258 | var pixels = imgData.data;
259 | for (var i = 0; i < pixels.length; i += 4) {
260 | var r = pixels[i];
261 | var g = pixels[i + 1];
262 | var b = pixels[i + 2];
263 | var v = (r + g + b) / 3;
264 | pixels[i] = pixels[i + 1] = pixels[i + 2] =
265 | v < threshold ? 0 : 255;
266 | }
267 | context.putImageData(imgData, 0, 0);
268 | callback();
269 | };
270 | },
271 | BRIGHTNESS: function(adjustment) {
272 | if (adjustment < -255 || adjustment > 255) {
273 | throw new Error(
274 | 'Brightness adjustment must be between -255 and 255.');
275 | }
276 | var precomputedBrightness = [];
277 | for (var i = 0; i < 256; i++) {
278 | precomputedBrightness[i] = i + adjustment;
279 | }
280 | return function(context, callback) {
281 | var imgData = context.getImageData(
282 | 0, 0, context.canvas.width, context.canvas.height);
283 | var pixels = imgData.data;
284 | for (var i = 0; i < pixels.length; i += 4) {
285 | pixels[i] = precomputedBrightness[pixels[i]];
286 | pixels[i + 1] = precomputedBrightness[pixels[i + 1]];
287 | pixels[i + 2] = precomputedBrightness[pixels[i + 2]];
288 | }
289 | context.putImageData(imgData, 0, 0);
290 | callback();
291 | };
292 | },
293 | CONTRAST: function(adjustment) {
294 | if (adjustment < 0) {
295 | throw new Error('Contrast adjustment must be positive.');
296 | }
297 | var precomputedContrast = [];
298 | for (var i = 0; i < 256; i++) {
299 | precomputedContrast[i] = i * adjustment;
300 | }
301 | return function(context, callback) {
302 | var imgData = context.getImageData(
303 | 0, 0, context.canvas.width, context.canvas.height);
304 | var pixels = imgData.data;
305 | for (var i = 0; i < pixels.length; i += 4) {
306 | pixels[i] = precomputedContrast[pixels[i]];
307 | pixels[i + 1] = precomputedContrast[pixels[i + 1]];
308 | pixels[i + 2] = precomputedContrast[pixels[i + 2]];
309 | }
310 | context.putImageData(imgData, 0, 0);
311 | callback();
312 | };
313 | },
314 | GAMMA: function(adjustment) {
315 | if (adjustment < 0) {
316 | throw new Error('Gamma adjustment must be positive.');
317 | }
318 | var precomputedGamma = [];
319 | for (var i = 0; i < 256; i++) {
320 | precomputedGamma[i] = Math.pow(i / 255, adjustment) * 255;
321 | }
322 | return function(context, callback) {
323 | var imgData = context.getImageData(
324 | 0, 0, context.canvas.width, context.canvas.height);
325 | var pixels = imgData.data;
326 | for (var i = 0; i < pixels.length; i += 4) {
327 | pixels[i] = precomputedGamma[pixels[i]];
328 | pixels[i + 1] = precomputedGamma[pixels[i + 1]];
329 | pixels[i + 2] = precomputedGamma[pixels[i + 2]];
330 | }
331 | context.putImageData(imgData, 0, 0);
332 | callback();
333 | };
334 | },
335 | GREYSCALE: function() {
336 | return function(context, callback) {
337 | var imgData = context.getImageData(
338 | 0, 0, context.canvas.width, context.canvas.height);
339 | var pixels = imgData.data;
340 | for (var i = 0; i < pixels.length; i += 4) {
341 | var val = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
342 | pixels[i] = val;
343 | pixels[i + 1] = val;
344 | pixels[i + 2] = val;
345 | }
346 | context.putImageData(imgData, 0, 0);
347 | callback();
348 | };
349 | },
350 | INVERT: function() {
351 | var precomputedInvert = [];
352 | for (var i = 0; i < 256; i++) {
353 | precomputedInvert[i] = 255 - i;
354 | }
355 | return function(context, callback) {
356 | var imgData = context.getImageData(
357 | 0, 0, context.canvas.width, context.canvas.height);
358 | var pixels = imgData.data;
359 | for (var i = 0; i < pixels.length; i += 4) {
360 | pixels[i] = precomputedInvert[pixels[i]];
361 | pixels[i + 1] = precomputedInvert[pixels[i + 1]];
362 | pixels[i + 2] = precomputedInvert[pixels[i + 2]];
363 | }
364 | context.putImageData(imgData, 0, 0);
365 | callback();
366 | };
367 | },
368 | MORPHOLOGICAL_OPERATION: function(kernelSize, comparator) {
369 | if (kernelSize % 2 === 0) {
370 | throw new Error('The kernel size must be an odd number.');
371 | }
372 | var kernelHalfSize = Math.floor(kernelSize / 2);
373 |
374 | if (!comparator) {
375 | throw new Error('A comparator must be defined.');
376 | }
377 |
378 | return function(context, callback) {
379 | var width = context.canvas.width;
380 | var height = context.canvas.height;
381 | var imgData = context.getImageData(0, 0, width, height);
382 | var originalPixels = context.getImageData(0, 0, width, height)
383 | .data;
384 | var offset;
385 |
386 | for (var y = 0; y < height; y++) {
387 | for (var x = 0; x < width; x++) {
388 | offset = (y * width + x) * 4;
389 | var r = originalPixels[offset];
390 | var g = originalPixels[offset + 1];
391 | var b = originalPixels[offset + 2];
392 | for (var j = 0; j < kernelSize; j++) {
393 | for (var i = 0; i < kernelSize; i++) {
394 | var pixelX = x + i - kernelHalfSize;
395 | var pixelY = y + j - kernelHalfSize;
396 | if (pixelX >= 0 && pixelX < width &&
397 | pixelY >= 0 && pixelY < height) {
398 | offset = (pixelY * width + pixelX) * 4;
399 | r = comparator(originalPixels[offset], r);
400 | g = comparator(
401 | originalPixels[offset + 1], g);
402 | b = comparator(
403 | originalPixels[offset + 2], b);
404 | }
405 | }
406 | }
407 | imgData.data[offset] = r;
408 | imgData.data[offset + 1] = g;
409 | imgData.data[offset + 2] = b;
410 | }
411 | }
412 | context.putImageData(imgData, 0, 0);
413 | callback();
414 | };
415 | },
416 | CONVOLUTION: function(kernel) {
417 | if (!$.isArray(kernel)) {
418 | throw new Error('The kernel must be an array.');
419 | }
420 | var kernelSize = Math.sqrt(kernel.length);
421 | if ((kernelSize + 1) % 2 !== 0) {
422 | throw new Error('The kernel must be a square matrix with odd' +
423 | 'width and height.');
424 | }
425 | var kernelHalfSize = (kernelSize - 1) / 2;
426 |
427 | return function(context, callback) {
428 | var width = context.canvas.width;
429 | var height = context.canvas.height;
430 | var imgData = context.getImageData(0, 0, width, height);
431 | var originalPixels = context.getImageData(0, 0, width, height)
432 | .data;
433 | var offset;
434 |
435 | for (var y = 0; y < height; y++) {
436 | for (var x = 0; x < width; x++) {
437 | var r = 0;
438 | var g = 0;
439 | var b = 0;
440 | for (var j = 0; j < kernelSize; j++) {
441 | for (var i = 0; i < kernelSize; i++) {
442 | var pixelX = x + i - kernelHalfSize;
443 | var pixelY = y + j - kernelHalfSize;
444 | if (pixelX >= 0 && pixelX < width &&
445 | pixelY >= 0 && pixelY < height) {
446 | offset = (pixelY * width + pixelX) * 4;
447 | var weight = kernel[j * kernelSize + i];
448 | r += originalPixels[offset] * weight;
449 | g += originalPixels[offset + 1] * weight;
450 | b += originalPixels[offset + 2] * weight;
451 | }
452 | }
453 | }
454 | offset = (y * width + x) * 4;
455 | imgData.data[offset] = r;
456 | imgData.data[offset + 1] = g;
457 | imgData.data[offset + 2] = b;
458 | }
459 | }
460 | context.putImageData(imgData, 0, 0);
461 | callback();
462 | };
463 | },
464 | COLORMAP: function(cmap, ctr) {
465 | var resampledCmap = cmap.slice(0);
466 | var diff = 255 - ctr;
467 | for(var i = 0; i < 256; i++) {
468 | var position = 0;
469 | if(i > ctr) {
470 | position = Math.min((i - ctr) / diff * 128 + 128,255) | 0;
471 | }else{
472 | position = Math.max(0, i / (ctr / 128)) | 0;
473 | }
474 | resampledCmap[i] = cmap[position];
475 | }
476 | return function(context, callback) {
477 | var imgData = context.getImageData(
478 | 0, 0, context.canvas.width, context.canvas.height);
479 | var pxl = imgData.data;
480 | for (var i = 0; i < pxl.length; i += 4) {
481 | var v = (pxl[i] + pxl[i + 1] + pxl[i + 2]) / 3 | 0;
482 | var c = resampledCmap[v];
483 | pxl[i] = c[0];
484 | pxl[i + 1] = c[1];
485 | pxl[i + 2] = c[2];
486 | }
487 | context.putImageData(imgData, 0, 0);
488 | callback();
489 | };
490 | }
491 | };
492 |
493 | }());
494 |
--------------------------------------------------------------------------------
/demo/demo.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This software was developed at the National Institute of Standards and
3 | * Technology by employees of the Federal Government in the course of
4 | * their official duties. Pursuant to title 17 Section 105 of the United
5 | * States Code this software is not subject to copyright protection and is
6 | * in the public domain. This software is an experimental system. NIST assumes
7 | * no responsibility whatsoever for its use by other parties, and makes no
8 | * guarantees, expressed or implied, about its quality, reliability, or
9 | * any other characteristic. We would appreciate acknowledgement if the
10 | * software is used.
11 | */
12 |
13 | /**
14 | *
15 | * @author Antoine Vandecreme
16 | */
17 |
18 | require('file-loader?name=[name].[ext]!./index.html');
19 | require('style-loader?name=[name].[ext]!./style.css');
20 |
21 |
22 | var $ = require('jquery');
23 | require('webpack-jquery-ui');
24 | require('webpack-jquery-ui/css');
25 | var Spinner = require('./spinner');
26 | var SpinnerSlider = require('./spinner-slider');
27 |
28 | var OpenSeadragon = require('openseadragon');
29 | require('../openseadragon-filtering');
30 | var viewer = new OpenSeadragon({
31 | id: 'openseadragon',
32 | prefixUrl: 'images/',
33 | tileSources: '//openseadragon.github.io/example-images/highsmith/highsmith.dzi',
34 | crossOriginPolicy: 'Anonymous'
35 | });
36 |
37 | // Prevent Caman from caching the canvas because without this:
38 | // 1. We have a memory leak
39 | // 2. Non-caman filters in between 2 camans filters get ignored.
40 | var caman = Caman;
41 | caman.Store.put = function() {};
42 |
43 | // List of filters with their templates.
44 | var availableFilters = [
45 | {
46 | name: 'Invert',
47 | generate: function() {
48 | return {
49 | html: '',
50 | getParams: function() {
51 | return '';
52 | },
53 | getFilter: function() {
54 | /*eslint new-cap: 0*/
55 | return OpenSeadragon.Filters.INVERT();
56 | },
57 | sync: true
58 | };
59 | }
60 | }, {
61 | name: 'Colormap',
62 | generate: function(updateCallback) {
63 | var cmaps = {
64 | aCm: [ [0,0,0], [0,4,0], [0,8,0], [0,12,0], [0,16,0], [0,20,0], [0,24,0], [0,28,0], [0,32,0], [0,36,0], [0,40,0], [0,44,0], [0,48,0], [0,52,0], [0,56,0], [0,60,0], [0,64,0], [0,68,0], [0,72,0], [0,76,0], [0,80,0], [0,85,0], [0,89,0], [0,93,0], [0,97,0], [0,101,0], [0,105,0], [0,109,0], [0,113,0], [0,117,0], [0,121,0], [0,125,0], [0,129,2], [0,133,5], [0,137,7], [0,141,10], [0,145,13], [0,149,15], [0,153,18], [0,157,21], [0,161,23], [0,165,26], [0,170,29], [0,174,31], [0,178,34], [0,182,37], [0,186,39], [0,190,42], [0,194,45], [0,198,47], [0,202,50], [0,206,53], [0,210,55], [0,214,58], [0,218,61], [0,222,63], [0,226,66], [0,230,69], [0,234,71], [0,238,74], [0,242,77], [0,246,79], [0,250,82], [0,255,85], [3,251,87], [7,247,90], [11,243,92], [15,239,95], [19,235,98], [23,231,100], [27,227,103], [31,223,106], [35,219,108], [39,215,111], [43,211,114], [47,207,116], [51,203,119], [55,199,122], [59,195,124], [63,191,127], [67,187,130], [71,183,132], [75,179,135], [79,175,138], [83,171,140], [87,167,143], [91,163,146], [95,159,148], [99,155,151], [103,151,154], [107,147,156], [111,143,159], [115,139,162], [119,135,164], [123,131,167], [127,127,170], [131,123,172], [135,119,175], [139,115,177], [143,111,180], [147,107,183], [151,103,185], [155,99,188], [159,95,191], [163,91,193], [167,87,196], [171,83,199], [175,79,201], [179,75,204], [183,71,207], [187,67,209], [191,63,212], [195,59,215], [199,55,217], [203,51,220], [207,47,223], [211,43,225], [215,39,228], [219,35,231], [223,31,233], [227,27,236], [231,23,239], [235,19,241], [239,15,244], [243,11,247], [247,7,249], [251,3,252], [255,0,255], [255,0,251], [255,0,247], [255,0,244], [255,0,240], [255,0,237], [255,0,233], [255,0,230], [255,0,226], [255,0,223], [255,0,219], [255,0,216], [255,0,212], [255,0,208], [255,0,205], [255,0,201], [255,0,198], [255,0,194], [255,0,191], [255,0,187], [255,0,184], [255,0,180], [255,0,177], [255,0,173], [255,0,170], [255,0,166], [255,0,162], [255,0,159], [255,0,155], [255,0,152], [255,0,148], [255,0,145], [255,0,141], [255,0,138], [255,0,134], [255,0,131], [255,0,127], [255,0,123], [255,0,119], [255,0,115], [255,0,112], [255,0,108], [255,0,104], [255,0,100], [255,0,96], [255,0,92], [255,0,88], [255,0,85], [255,0,81], [255,0,77], [255,0,73], [255,0,69], [255,0,65], [255,0,61], [255,0,57], [255,0,54], [255,0,50], [255,0,46], [255,0,42], [255,0,38], [255,0,34], [255,0,30], [255,0,27], [255,0,23], [255,0,19], [255,0,15], [255,0,11], [255,0,7], [255,0,3], [255,0,0], [255,4,0], [255,8,0], [255,12,0], [255,17,0], [255,21,0], [255,25,0], [255,30,0], [255,34,0], [255,38,0], [255,43,0], [255,47,0], [255,51,0], [255,56,0], [255,60,0], [255,64,0], [255,69,0], [255,73,0], [255,77,0], [255,82,0], [255,86,0], [255,90,0], [255,95,0], [255,99,0], [255,103,0], [255,108,0], [255,112,0], [255,116,0], [255,121,0], [255,125,0], [255,129,0], [255,133,0], [255,138,0], [255,142,0], [255,146,0], [255,151,0], [255,155,0], [255,159,0], [255,164,0], [255,168,0], [255,172,0], [255,177,0], [255,181,0], [255,185,0], [255,190,0], [255,194,0], [255,198,0], [255,203,0], [255,207,0], [255,211,0], [255,216,0], [255,220,0], [255,224,0], [255,229,0], [255,233,0], [255,237,0], [255,242,0], [255,246,0], [255,250,0], [255,255,0]],
65 | bCm: [ [0,0,0], [0,0,4], [0,0,8], [0,0,12], [0,0,16], [0,0,20], [0,0,24], [0,0,28], [0,0,32], [0,0,36], [0,0,40], [0,0,44], [0,0,48], [0,0,52], [0,0,56], [0,0,60], [0,0,64], [0,0,68], [0,0,72], [0,0,76], [0,0,80], [0,0,85], [0,0,89], [0,0,93], [0,0,97], [0,0,101], [0,0,105], [0,0,109], [0,0,113], [0,0,117], [0,0,121], [0,0,125], [0,0,129], [0,0,133], [0,0,137], [0,0,141], [0,0,145], [0,0,149], [0,0,153], [0,0,157], [0,0,161], [0,0,165], [0,0,170], [0,0,174], [0,0,178], [0,0,182], [0,0,186], [0,0,190], [0,0,194], [0,0,198], [0,0,202], [0,0,206], [0,0,210], [0,0,214], [0,0,218], [0,0,222], [0,0,226], [0,0,230], [0,0,234], [0,0,238], [0,0,242], [0,0,246], [0,0,250], [0,0,255], [3,0,251], [7,0,247], [11,0,243], [15,0,239], [19,0,235], [23,0,231], [27,0,227], [31,0,223], [35,0,219], [39,0,215], [43,0,211], [47,0,207], [51,0,203], [55,0,199], [59,0,195], [63,0,191], [67,0,187], [71,0,183], [75,0,179], [79,0,175], [83,0,171], [87,0,167], [91,0,163], [95,0,159], [99,0,155], [103,0,151], [107,0,147], [111,0,143], [115,0,139], [119,0,135], [123,0,131], [127,0,127], [131,0,123], [135,0,119], [139,0,115], [143,0,111], [147,0,107], [151,0,103], [155,0,99], [159,0,95], [163,0,91], [167,0,87], [171,0,83], [175,0,79], [179,0,75], [183,0,71], [187,0,67], [191,0,63], [195,0,59], [199,0,55], [203,0,51], [207,0,47], [211,0,43], [215,0,39], [219,0,35], [223,0,31], [227,0,27], [231,0,23], [235,0,19], [239,0,15], [243,0,11], [247,0,7], [251,0,3], [255,0,0], [255,3,0], [255,7,0], [255,11,0], [255,15,0], [255,19,0], [255,23,0], [255,27,0], [255,31,0], [255,35,0], [255,39,0], [255,43,0], [255,47,0], [255,51,0], [255,55,0], [255,59,0], [255,63,0], [255,67,0], [255,71,0], [255,75,0], [255,79,0], [255,83,0], [255,87,0], [255,91,0], [255,95,0], [255,99,0], [255,103,0], [255,107,0], [255,111,0], [255,115,0], [255,119,0], [255,123,0], [255,127,0], [255,131,0], [255,135,0], [255,139,0], [255,143,0], [255,147,0], [255,151,0], [255,155,0], [255,159,0], [255,163,0], [255,167,0], [255,171,0], [255,175,0], [255,179,0], [255,183,0], [255,187,0], [255,191,0], [255,195,0], [255,199,0], [255,203,0], [255,207,0], [255,211,0], [255,215,0], [255,219,0], [255,223,0], [255,227,0], [255,231,0], [255,235,0], [255,239,0], [255,243,0], [255,247,0], [255,251,0], [255,255,0], [255,255,3], [255,255,7], [255,255,11], [255,255,15], [255,255,19], [255,255,23], [255,255,27], [255,255,31], [255,255,35], [255,255,39], [255,255,43], [255,255,47], [255,255,51], [255,255,55], [255,255,59], [255,255,63], [255,255,67], [255,255,71], [255,255,75], [255,255,79], [255,255,83], [255,255,87], [255,255,91], [255,255,95], [255,255,99], [255,255,103], [255,255,107], [255,255,111], [255,255,115], [255,255,119], [255,255,123], [255,255,127], [255,255,131], [255,255,135], [255,255,139], [255,255,143], [255,255,147], [255,255,151], [255,255,155], [255,255,159], [255,255,163], [255,255,167], [255,255,171], [255,255,175], [255,255,179], [255,255,183], [255,255,187], [255,255,191], [255,255,195], [255,255,199], [255,255,203], [255,255,207], [255,255,211], [255,255,215], [255,255,219], [255,255,223], [255,255,227], [255,255,231], [255,255,235], [255,255,239], [255,255,243], [255,255,247], [255,255,251], [255,255,255]],
66 | bbCm: [ [0,0,0], [2,0,0], [4,0,0], [6,0,0], [8,0,0], [10,0,0], [12,0,0], [14,0,0], [16,0,0], [18,0,0], [20,0,0], [22,0,0], [24,0,0], [26,0,0], [28,0,0], [30,0,0], [32,0,0], [34,0,0], [36,0,0], [38,0,0], [40,0,0], [42,0,0], [44,0,0], [46,0,0], [48,0,0], [50,0,0], [52,0,0], [54,0,0], [56,0,0], [58,0,0], [60,0,0], [62,0,0], [64,0,0], [66,0,0], [68,0,0], [70,0,0], [72,0,0], [74,0,0], [76,0,0], [78,0,0], [80,0,0], [82,0,0], [84,0,0], [86,0,0], [88,0,0], [90,0,0], [92,0,0], [94,0,0], [96,0,0], [98,0,0], [100,0,0], [102,0,0], [104,0,0], [106,0,0], [108,0,0], [110,0,0], [112,0,0], [114,0,0], [116,0,0], [118,0,0], [120,0,0], [122,0,0], [124,0,0], [126,0,0], [128,1,0], [130,3,0], [132,5,0], [134,7,0], [136,9,0], [138,11,0], [140,13,0], [142,15,0], [144,17,0], [146,19,0], [148,21,0], [150,23,0], [152,25,0], [154,27,0], [156,29,0], [158,31,0], [160,33,0], [162,35,0], [164,37,0], [166,39,0], [168,41,0], [170,43,0], [172,45,0], [174,47,0], [176,49,0], [178,51,0], [180,53,0], [182,55,0], [184,57,0], [186,59,0], [188,61,0], [190,63,0], [192,65,0], [194,67,0], [196,69,0], [198,71,0], [200,73,0], [202,75,0], [204,77,0], [206,79,0], [208,81,0], [210,83,0], [212,85,0], [214,87,0], [216,89,0], [218,91,0], [220,93,0], [222,95,0], [224,97,0], [226,99,0], [228,101,0], [230,103,0], [232,105,0], [234,107,0], [236,109,0], [238,111,0], [240,113,0], [242,115,0], [244,117,0], [246,119,0], [248,121,0], [250,123,0], [252,125,0], [255,127,0], [255,129,1], [255,131,3], [255,133,5], [255,135,7], [255,137,9], [255,139,11], [255,141,13], [255,143,15], [255,145,17], [255,147,19], [255,149,21], [255,151,23], [255,153,25], [255,155,27], [255,157,29], [255,159,31], [255,161,33], [255,163,35], [255,165,37], [255,167,39], [255,169,41], [255,171,43], [255,173,45], [255,175,47], [255,177,49], [255,179,51], [255,181,53], [255,183,55], [255,185,57], [255,187,59], [255,189,61], [255,191,63], [255,193,65], [255,195,67], [255,197,69], [255,199,71], [255,201,73], [255,203,75], [255,205,77], [255,207,79], [255,209,81], [255,211,83], [255,213,85], [255,215,87], [255,217,89], [255,219,91], [255,221,93], [255,223,95], [255,225,97], [255,227,99], [255,229,101], [255,231,103], [255,233,105], [255,235,107], [255,237,109], [255,239,111], [255,241,113], [255,243,115], [255,245,117], [255,247,119], [255,249,121], [255,251,123], [255,253,125], [255,255,127], [255,255,129], [255,255,131], [255,255,133], [255,255,135], [255,255,137], [255,255,139], [255,255,141], [255,255,143], [255,255,145], [255,255,147], [255,255,149], [255,255,151], [255,255,153], [255,255,155], [255,255,157], [255,255,159], [255,255,161], [255,255,163], [255,255,165], [255,255,167], [255,255,169], [255,255,171], [255,255,173], [255,255,175], [255,255,177], [255,255,179], [255,255,181], [255,255,183], [255,255,185], [255,255,187], [255,255,189], [255,255,191], [255,255,193], [255,255,195], [255,255,197], [255,255,199], [255,255,201], [255,255,203], [255,255,205], [255,255,207], [255,255,209], [255,255,211], [255,255,213], [255,255,215], [255,255,217], [255,255,219], [255,255,221], [255,255,223], [255,255,225], [255,255,227], [255,255,229], [255,255,231], [255,255,233], [255,255,235], [255,255,237], [255,255,239], [255,255,241], [255,255,243], [255,255,245], [255,255,247], [255,255,249], [255,255,251], [255,255,253], [255,255,255]],
67 | blueCm: [ [0,0,0], [0,0,1], [0,0,2], [0,0,3], [0,0,4], [0,0,5], [0,0,6], [0,0,7], [0,0,8], [0,0,9], [0,0,10], [0,0,11], [0,0,12], [0,0,13], [0,0,14], [0,0,15], [0,0,16], [0,0,17], [0,0,18], [0,0,19], [0,0,20], [0,0,21], [0,0,22], [0,0,23], [0,0,24], [0,0,25], [0,0,26], [0,0,27], [0,0,28], [0,0,29], [0,0,30], [0,0,31], [0,0,32], [0,0,33], [0,0,34], [0,0,35], [0,0,36], [0,0,37], [0,0,38], [0,0,39], [0,0,40], [0,0,41], [0,0,42], [0,0,43], [0,0,44], [0,0,45], [0,0,46], [0,0,47], [0,0,48], [0,0,49], [0,0,50], [0,0,51], [0,0,52], [0,0,53], [0,0,54], [0,0,55], [0,0,56], [0,0,57], [0,0,58], [0,0,59], [0,0,60], [0,0,61], [0,0,62], [0,0,63], [0,0,64], [0,0,65], [0,0,66], [0,0,67], [0,0,68], [0,0,69], [0,0,70], [0,0,71], [0,0,72], [0,0,73], [0,0,74], [0,0,75], [0,0,76], [0,0,77], [0,0,78], [0,0,79], [0,0,80], [0,0,81], [0,0,82], [0,0,83], [0,0,84], [0,0,85], [0,0,86], [0,0,87], [0,0,88], [0,0,89], [0,0,90], [0,0,91], [0,0,92], [0,0,93], [0,0,94], [0,0,95], [0,0,96], [0,0,97], [0,0,98], [0,0,99], [0,0,100], [0,0,101], [0,0,102], [0,0,103], [0,0,104], [0,0,105], [0,0,106], [0,0,107], [0,0,108], [0,0,109], [0,0,110], [0,0,111], [0,0,112], [0,0,113], [0,0,114], [0,0,115], [0,0,116], [0,0,117], [0,0,118], [0,0,119], [0,0,120], [0,0,121], [0,0,122], [0,0,123], [0,0,124], [0,0,125], [0,0,126], [0,0,127], [0,0,128], [0,0,129], [0,0,130], [0,0,131], [0,0,132], [0,0,133], [0,0,134], [0,0,135], [0,0,136], [0,0,137], [0,0,138], [0,0,139], [0,0,140], [0,0,141], [0,0,142], [0,0,143], [0,0,144], [0,0,145], [0,0,146], [0,0,147], [0,0,148], [0,0,149], [0,0,150], [0,0,151], [0,0,152], [0,0,153], [0,0,154], [0,0,155], [0,0,156], [0,0,157], [0,0,158], [0,0,159], [0,0,160], [0,0,161], [0,0,162], [0,0,163], [0,0,164], [0,0,165], [0,0,166], [0,0,167], [0,0,168], [0,0,169], [0,0,170], [0,0,171], [0,0,172], [0,0,173], [0,0,174], [0,0,175], [0,0,176], [0,0,177], [0,0,178], [0,0,179], [0,0,180], [0,0,181], [0,0,182], [0,0,183], [0,0,184], [0,0,185], [0,0,186], [0,0,187], [0,0,188], [0,0,189], [0,0,190], [0,0,191], [0,0,192], [0,0,193], [0,0,194], [0,0,195], [0,0,196], [0,0,197], [0,0,198], [0,0,199], [0,0,200], [0,0,201], [0,0,202], [0,0,203], [0,0,204], [0,0,205], [0,0,206], [0,0,207], [0,0,208], [0,0,209], [0,0,210], [0,0,211], [0,0,212], [0,0,213], [0,0,214], [0,0,215], [0,0,216], [0,0,217], [0,0,218], [0,0,219], [0,0,220], [0,0,221], [0,0,222], [0,0,223], [0,0,224], [0,0,225], [0,0,226], [0,0,227], [0,0,228], [0,0,229], [0,0,230], [0,0,231], [0,0,232], [0,0,233], [0,0,234], [0,0,235], [0,0,236], [0,0,237], [0,0,238], [0,0,239], [0,0,240], [0,0,241], [0,0,242], [0,0,243], [0,0,244], [0,0,245], [0,0,246], [0,0,247], [0,0,248], [0,0,249], [0,0,250], [0,0,251], [0,0,252], [0,0,253], [0,0,254], [0,0,255]],
68 | coolCm: [ [0,0,0], [0,0,1], [0,0,3], [0,0,5], [0,0,7], [0,0,9], [0,0,11], [0,0,13], [0,0,15], [0,0,17], [0,0,18], [0,0,20], [0,0,22], [0,0,24], [0,0,26], [0,0,28], [0,0,30], [0,0,32], [0,0,34], [0,0,35], [0,0,37], [0,0,39], [0,0,41], [0,0,43], [0,0,45], [0,0,47], [0,0,49], [0,0,51], [0,0,52], [0,0,54], [0,0,56], [0,0,58], [0,0,60], [0,0,62], [0,0,64], [0,0,66], [0,0,68], [0,0,69], [0,0,71], [0,0,73], [0,0,75], [0,0,77], [0,0,79], [0,0,81], [0,0,83], [0,0,85], [0,0,86], [0,0,88], [0,0,90], [0,0,92], [0,0,94], [0,0,96], [0,0,98], [0,0,100], [0,0,102], [0,0,103], [0,0,105], [0,1,107], [0,2,109], [0,4,111], [0,5,113], [0,6,115], [0,8,117], [0,9,119], [0,10,120], [0,12,122], [0,13,124], [0,14,126], [0,16,128], [0,17,130], [0,18,132], [0,20,134], [0,21,136], [0,23,137], [0,24,139], [0,25,141], [0,27,143], [0,28,145], [1,29,147], [1,31,149], [1,32,151], [1,33,153], [1,35,154], [2,36,156], [2,37,158], [2,39,160], [2,40,162], [2,42,164], [3,43,166], [3,44,168], [3,46,170], [3,47,171], [4,48,173], [4,50,175], [4,51,177], [4,52,179], [4,54,181], [5,55,183], [5,56,185], [5,58,187], [5,59,188], [5,61,190], [6,62,192], [6,63,194], [6,65,196], [6,66,198], [7,67,200], [7,69,202], [7,70,204], [7,71,205], [7,73,207], [8,74,209], [8,75,211], [8,77,213], [8,78,215], [8,80,217], [9,81,219], [9,82,221], [9,84,222], [9,85,224], [9,86,226], [10,88,228], [10,89,230], [10,90,232], [10,92,234], [11,93,236], [11,94,238], [11,96,239], [11,97,241], [11,99,243], [12,100,245], [12,101,247], [12,103,249], [12,104,251], [12,105,253], [13,107,255], [13,108,255], [13,109,255], [13,111,255], [14,112,255], [14,113,255], [14,115,255], [14,116,255], [14,118,255], [15,119,255], [15,120,255], [15,122,255], [15,123,255], [15,124,255], [16,126,255], [16,127,255], [16,128,255], [16,130,255], [17,131,255], [17,132,255], [17,134,255], [17,135,255], [17,136,255], [18,138,255], [18,139,255], [18,141,255], [18,142,255], [18,143,255], [19,145,255], [19,146,255], [19,147,255], [19,149,255], [19,150,255], [20,151,255], [20,153,255], [20,154,255], [20,155,255], [21,157,255], [21,158,255], [21,160,255], [21,161,255], [21,162,255], [22,164,255], [22,165,255], [22,166,255], [22,168,255], [22,169,255], [23,170,255], [23,172,255], [23,173,255], [23,174,255], [24,176,255], [24,177,255], [24,179,255], [24,180,255], [24,181,255], [25,183,255], [25,184,255], [25,185,255], [29,187,255], [32,188,255], [36,189,255], [40,191,255], [44,192,255], [47,193,255], [51,195,255], [55,196,255], [58,198,255], [62,199,255], [66,200,255], [69,202,255], [73,203,255], [77,204,255], [81,206,255], [84,207,255], [88,208,255], [92,210,255], [95,211,255], [99,212,255], [103,214,255], [106,215,255], [110,217,255], [114,218,255], [118,219,255], [121,221,255], [125,222,255], [129,223,255], [132,225,255], [136,226,255], [140,227,255], [143,229,255], [147,230,255], [151,231,255], [155,233,255], [158,234,255], [162,236,255], [166,237,255], [169,238,255], [173,240,255], [177,241,255], [180,242,255], [184,244,255], [188,245,255], [192,246,255], [195,248,255], [199,249,255], [203,250,255], [206,252,255], [210,253,255], [214,255,255], [217,255,255], [221,255,255], [225,255,255], [229,255,255], [232,255,255], [236,255,255], [240,255,255], [243,255,255], [247,255,255], [251,255,255], [255,255,255]],
69 | cubehelix0Cm: [ [0,0,0], [2,1,2], [5,2,5], [5,2,5], [6,2,6], [7,2,7], [10,3,10], [12,5,12], [13,5,14], [14,5,16], [15,5,17], [16,6,20], [17,7,22], [18,8,24], [19,9,26], [20,10,28], [21,11,30], [22,12,33], [22,13,34], [22,14,36], [22,15,38], [24,16,40], [25,17,43], [25,18,45], [25,19,46], [25,20,48], [25,22,50], [25,23,51], [25,25,53], [25,26,54], [25,28,56], [25,28,57], [25,29,59], [25,30,61], [25,33,62], [25,35,63], [25,36,65], [25,37,67], [25,38,68], [25,40,70], [25,43,71], [24,45,72], [23,46,73], [22,48,73], [22,49,75], [22,51,76], [22,52,76], [22,54,76], [22,56,76], [22,57,77], [22,59,78], [22,61,79], [21,63,79], [20,66,79], [20,67,79], [20,68,79], [20,68,79], [20,71,79], [20,73,79], [20,75,78], [20,77,77], [20,79,76], [20,80,76], [20,81,76], [21,83,75], [22,85,74], [22,86,73], [22,89,72], [22,91,71], [23,92,71], [24,93,71], [25,94,71], [26,96,70], [28,99,68], [28,100,68], [29,101,67], [30,102,66], [31,102,65], [32,103,64], [33,104,63], [35,105,62], [38,107,61], [39,107,60], [39,108,59], [40,109,58], [43,110,57], [45,112,56], [47,113,55], [49,113,54], [51,114,53], [54,116,52], [58,117,51], [60,117,50], [62,117,49], [63,117,48], [66,118,48], [68,119,48], [71,119,48], [73,119,48], [76,119,48], [79,120,47], [81,121,46], [84,122,45], [87,122,45], [91,122,45], [94,122,46], [96,122,47], [99,122,48], [103,122,48], [107,122,48], [109,122,49], [112,122,50], [114,122,51], [118,122,52], [122,122,53], [124,122,54], [127,122,55], [130,122,56], [133,122,57], [137,122,58], [140,122,60], [142,122,62], [145,122,63], [149,122,66], [153,122,68], [155,121,70], [158,120,72], [160,119,73], [162,119,75], [164,119,77], [165,119,79], [169,119,81], [173,119,84], [175,119,86], [176,119,89], [178,119,91], [181,119,95], [183,119,99], [186,120,102], [188,121,104], [191,122,107], [192,122,110], [193,122,114], [195,122,117], [197,122,119], [198,122,122], [200,122,126], [201,122,130], [202,123,132], [203,124,135], [204,124,137], [204,125,141], [205,126,144], [206,127,147], [207,127,151], [209,127,155], [209,128,158], [210,129,160], [211,130,163], [211,131,167], [211,132,170], [211,133,173], [211,134,175], [211,135,178], [211,136,182], [211,137,186], [211,138,188], [211,139,191], [211,140,193], [211,142,196], [211,145,198], [210,146,201], [209,147,204], [209,147,206], [209,150,209], [209,153,211], [208,153,213], [207,154,215], [206,155,216], [206,157,218], [206,158,220], [206,160,221], [205,163,224], [204,165,226], [203,167,228], [202,169,230], [201,170,232], [201,172,233], [201,173,234], [200,175,235], [199,176,236], [198,178,237], [197,181,238], [196,183,239], [196,185,239], [196,187,239], [196,188,239], [195,191,240], [193,193,242], [193,194,242], [193,195,242], [193,196,242], [193,198,242], [193,199,242], [193,201,242], [193,204,242], [193,206,242], [193,208,242], [193,209,242], [193,211,242], [193,212,242], [193,214,242], [194,215,242], [195,217,242], [196,219,242], [196,220,242], [196,221,242], [197,223,241], [198,225,240], [198,226,239], [200,228,239], [201,229,239], [202,230,239], [203,231,239], [204,232,239], [205,233,239], [206,234,239], [207,235,239], [208,236,239], [209,237,239], [210,238,239], [212,238,239], [214,239,239], [215,240,239], [216,242,239], [218,243,239], [220,243,239], [221,244,239], [224,246,239], [226,247,239], [228,247,240], [230,247,241], [232,247,242], [234,248,242], [237,249,242], [238,249,243], [240,249,243], [242,249,244], [243,251,246], [244,252,247], [246,252,249], [248,252,250], [249,252,252], [251,253,253], [253,254,254], [255,255,255]],
70 | cubehelix1Cm: [ [0,0,0], [2,0,2], [5,0,5], [6,0,7], [8,0,10], [10,0,12], [12,1,15], [15,2,17], [17,2,20], [18,2,22], [20,2,25], [21,2,29], [22,2,33], [23,3,35], [24,4,38], [25,5,40], [25,6,44], [25,7,48], [26,8,51], [27,9,53], [28,10,56], [28,11,59], [28,12,63], [27,14,66], [26,16,68], [25,17,71], [25,18,73], [25,19,74], [25,20,76], [24,22,80], [22,25,84], [22,27,85], [21,28,87], [20,30,89], [19,33,91], [17,35,94], [16,37,95], [14,39,96], [12,40,96], [11,43,99], [10,45,102], [8,47,102], [6,49,103], [5,51,104], [2,54,104], [0,58,104], [0,60,104], [0,62,104], [0,63,104], [0,66,104], [0,68,104], [0,71,104], [0,73,104], [0,76,104], [0,79,103], [0,81,102], [0,84,102], [0,86,99], [0,89,96], [0,91,96], [0,94,95], [0,96,94], [0,99,91], [0,102,89], [0,103,86], [0,105,84], [0,107,81], [0,109,79], [0,112,76], [0,113,73], [0,115,71], [0,117,68], [0,119,65], [0,122,61], [0,124,58], [0,125,56], [0,127,53], [0,128,51], [0,129,48], [0,130,45], [0,132,42], [0,135,38], [0,136,35], [0,136,33], [0,137,30], [3,138,26], [7,140,22], [10,140,21], [12,140,19], [15,140,17], [19,141,14], [22,142,10], [26,142,8], [29,142,6], [33,142,5], [38,142,2], [43,142,0], [46,142,0], [50,142,0], [53,142,0], [57,141,0], [62,141,0], [66,140,0], [72,140,0], [79,140,0], [83,139,0], [87,138,0], [91,137,0], [98,136,0], [104,135,0], [108,134,0], [113,133,0], [117,132,0], [123,131,0], [130,130,0], [134,129,0], [138,128,0], [142,127,0], [149,126,0], [155,124,0], [159,123,0], [164,121,1], [168,119,2], [174,118,6], [181,117,10], [185,116,12], [189,115,15], [193,114,17], [197,113,21], [200,113,24], [204,112,28], [209,110,33], [214,109,38], [217,108,41], [221,107,45], [224,107,48], [228,105,54], [232,104,61], [234,103,64], [237,102,68], [239,102,71], [243,102,77], [247,102,84], [249,101,89], [250,100,94], [252,99,99], [253,99,105], [255,99,112], [255,99,117], [255,99,122], [255,99,127], [255,99,131], [255,99,136], [255,99,140], [255,100,147], [255,102,155], [255,102,159], [255,102,164], [255,102,168], [255,103,174], [255,104,181], [255,105,185], [255,106,189], [255,107,193], [255,108,200], [255,109,206], [255,111,210], [255,113,215], [255,114,219], [253,117,224], [252,119,229], [250,120,232], [249,121,236], [247,122,239], [244,124,244], [242,127,249], [240,130,251], [238,132,253], [237,135,255], [234,136,255], [232,138,255], [229,140,255], [226,142,255], [224,145,255], [222,147,255], [221,150,255], [219,153,255], [215,156,255], [211,160,255], [209,162,255], [208,164,255], [206,165,255], [204,169,255], [201,173,255], [199,175,255], [198,178,255], [196,181,255], [193,183,255], [191,186,255], [189,188,255], [187,191,255], [186,193,255], [185,195,255], [184,197,255], [183,198,255], [182,202,255], [181,206,255], [180,208,255], [179,209,255], [178,211,255], [177,214,255], [175,216,255], [175,218,255], [175,220,255], [175,221,255], [175,224,255], [175,226,255], [176,228,255], [177,230,255], [178,232,255], [179,234,255], [181,237,255], [181,238,255], [182,238,255], [183,239,255], [184,240,252], [186,242,249], [187,243,249], [189,243,248], [191,244,247], [192,245,246], [194,246,245], [196,247,244], [198,248,243], [201,249,242], [203,250,242], [204,251,242], [206,252,242], [210,252,240], [214,252,239], [216,252,240], [219,252,241], [221,252,242], [224,253,242], [226,255,242], [229,255,243], [232,255,243], [234,255,244], [238,255,246], [242,255,247], [243,255,248], [245,255,249], [247,255,249], [249,255,251], [252,255,253], [255,255,255]],
71 | greenCm: [ [0,0,0], [0,1,0], [0,2,0], [0,3,0], [0,4,0], [0,5,0], [0,6,0], [0,7,0], [0,8,0], [0,9,0], [0,10,0], [0,11,0], [0,12,0], [0,13,0], [0,14,0], [0,15,0], [0,16,0], [0,17,0], [0,18,0], [0,19,0], [0,20,0], [0,21,0], [0,22,0], [0,23,0], [0,24,0], [0,25,0], [0,26,0], [0,27,0], [0,28,0], [0,29,0], [0,30,0], [0,31,0], [0,32,0], [0,33,0], [0,34,0], [0,35,0], [0,36,0], [0,37,0], [0,38,0], [0,39,0], [0,40,0], [0,41,0], [0,42,0], [0,43,0], [0,44,0], [0,45,0], [0,46,0], [0,47,0], [0,48,0], [0,49,0], [0,50,0], [0,51,0], [0,52,0], [0,53,0], [0,54,0], [0,55,0], [0,56,0], [0,57,0], [0,58,0], [0,59,0], [0,60,0], [0,61,0], [0,62,0], [0,63,0], [0,64,0], [0,65,0], [0,66,0], [0,67,0], [0,68,0], [0,69,0], [0,70,0], [0,71,0], [0,72,0], [0,73,0], [0,74,0], [0,75,0], [0,76,0], [0,77,0], [0,78,0], [0,79,0], [0,80,0], [0,81,0], [0,82,0], [0,83,0], [0,84,0], [0,85,0], [0,86,0], [0,87,0], [0,88,0], [0,89,0], [0,90,0], [0,91,0], [0,92,0], [0,93,0], [0,94,0], [0,95,0], [0,96,0], [0,97,0], [0,98,0], [0,99,0], [0,100,0], [0,101,0], [0,102,0], [0,103,0], [0,104,0], [0,105,0], [0,106,0], [0,107,0], [0,108,0], [0,109,0], [0,110,0], [0,111,0], [0,112,0], [0,113,0], [0,114,0], [0,115,0], [0,116,0], [0,117,0], [0,118,0], [0,119,0], [0,120,0], [0,121,0], [0,122,0], [0,123,0], [0,124,0], [0,125,0], [0,126,0], [0,127,0], [0,128,0], [0,129,0], [0,130,0], [0,131,0], [0,132,0], [0,133,0], [0,134,0], [0,135,0], [0,136,0], [0,137,0], [0,138,0], [0,139,0], [0,140,0], [0,141,0], [0,142,0], [0,143,0], [0,144,0], [0,145,0], [0,146,0], [0,147,0], [0,148,0], [0,149,0], [0,150,0], [0,151,0], [0,152,0], [0,153,0], [0,154,0], [0,155,0], [0,156,0], [0,157,0], [0,158,0], [0,159,0], [0,160,0], [0,161,0], [0,162,0], [0,163,0], [0,164,0], [0,165,0], [0,166,0], [0,167,0], [0,168,0], [0,169,0], [0,170,0], [0,171,0], [0,172,0], [0,173,0], [0,174,0], [0,175,0], [0,176,0], [0,177,0], [0,178,0], [0,179,0], [0,180,0], [0,181,0], [0,182,0], [0,183,0], [0,184,0], [0,185,0], [0,186,0], [0,187,0], [0,188,0], [0,189,0], [0,190,0], [0,191,0], [0,192,0], [0,193,0], [0,194,0], [0,195,0], [0,196,0], [0,197,0], [0,198,0], [0,199,0], [0,200,0], [0,201,0], [0,202,0], [0,203,0], [0,204,0], [0,205,0], [0,206,0], [0,207,0], [0,208,0], [0,209,0], [0,210,0], [0,211,0], [0,212,0], [0,213,0], [0,214,0], [0,215,0], [0,216,0], [0,217,0], [0,218,0], [0,219,0], [0,220,0], [0,221,0], [0,222,0], [0,223,0], [0,224,0], [0,225,0], [0,226,0], [0,227,0], [0,228,0], [0,229,0], [0,230,0], [0,231,0], [0,232,0], [0,233,0], [0,234,0], [0,235,0], [0,236,0], [0,237,0], [0,238,0], [0,239,0], [0,240,0], [0,241,0], [0,242,0], [0,243,0], [0,244,0], [0,245,0], [0,246,0], [0,247,0], [0,248,0], [0,249,0], [0,250,0], [0,251,0], [0,252,0], [0,253,0], [0,254,0], [0,255,0]],
72 | greyCm: [ [0,0,0], [1,1,1], [2,2,2], [3,3,3], [4,4,4], [5,5,5], [6,6,6], [7,7,7], [8,8,8], [9,9,9], [10,10,10], [11,11,11], [12,12,12], [13,13,13], [14,14,14], [15,15,15], [16,16,16], [17,17,17], [18,18,18], [19,19,19], [20,20,20], [21,21,21], [22,22,22], [23,23,23], [24,24,24], [25,25,25], [26,26,26], [27,27,27], [28,28,28], [29,29,29], [30,30,30], [31,31,31], [32,32,32], [33,33,33], [34,34,34], [35,35,35], [36,36,36], [37,37,37], [38,38,38], [39,39,39], [40,40,40], [41,41,41], [42,42,42], [43,43,43], [44,44,44], [45,45,45], [46,46,46], [47,47,47], [48,48,48], [49,49,49], [50,50,50], [51,51,51], [52,52,52], [53,53,53], [54,54,54], [55,55,55], [56,56,56], [57,57,57], [58,58,58], [59,59,59], [60,60,60], [61,61,61], [62,62,62], [63,63,63], [64,64,64], [65,65,65], [66,66,66], [67,67,67], [68,68,68], [69,69,69], [70,70,70], [71,71,71], [72,72,72], [73,73,73], [74,74,74], [75,75,75], [76,76,76], [77,77,77], [78,78,78], [79,79,79], [80,80,80], [81,81,81], [82,82,82], [83,83,83], [84,84,84], [85,85,85], [86,86,86], [87,87,87], [88,88,88], [89,89,89], [90,90,90], [91,91,91], [92,92,92], [93,93,93], [94,94,94], [95,95,95], [96,96,96], [97,97,97], [98,98,98], [99,99,99], [100,100,100], [101,101,101], [102,102,102], [103,103,103], [104,104,104], [105,105,105], [106,106,106], [107,107,107], [108,108,108], [109,109,109], [110,110,110], [111,111,111], [112,112,112], [113,113,113], [114,114,114], [115,115,115], [116,116,116], [117,117,117], [118,118,118], [119,119,119], [120,120,120], [121,121,121], [122,122,122], [123,123,123], [124,124,124], [125,125,125], [126,126,126], [127,127,127], [128,128,128], [129,129,129], [130,130,130], [131,131,131], [132,132,132], [133,133,133], [134,134,134], [135,135,135], [136,136,136], [137,137,137], [138,138,138], [139,139,139], [140,140,140], [141,141,141], [142,142,142], [143,143,143], [144,144,144], [145,145,145], [146,146,146], [147,147,147], [148,148,148], [149,149,149], [150,150,150], [151,151,151], [152,152,152], [153,153,153], [154,154,154], [155,155,155], [156,156,156], [157,157,157], [158,158,158], [159,159,159], [160,160,160], [161,161,161], [162,162,162], [163,163,163], [164,164,164], [165,165,165], [166,166,166], [167,167,167], [168,168,168], [169,169,169], [170,170,170], [171,171,171], [172,172,172], [173,173,173], [174,174,174], [175,175,175], [176,176,176], [177,177,177], [178,178,178], [179,179,179], [180,180,180], [181,181,181], [182,182,182], [183,183,183], [184,184,184], [185,185,185], [186,186,186], [187,187,187], [188,188,188], [189,189,189], [190,190,190], [191,191,191], [192,192,192], [193,193,193], [194,194,194], [195,195,195], [196,196,196], [197,197,197], [198,198,198], [199,199,199], [200,200,200], [201,201,201], [202,202,202], [203,203,203], [204,204,204], [205,205,205], [206,206,206], [207,207,207], [208,208,208], [209,209,209], [210,210,210], [211,211,211], [212,212,212], [213,213,213], [214,214,214], [215,215,215], [216,216,216], [217,217,217], [218,218,218], [219,219,219], [220,220,220], [221,221,221], [222,222,222], [223,223,223], [224,224,224], [225,225,225], [226,226,226], [227,227,227], [228,228,228], [229,229,229], [230,230,230], [231,231,231], [232,232,232], [233,233,233], [234,234,234], [235,235,235], [236,236,236], [237,237,237], [238,238,238], [239,239,239], [240,240,240], [241,241,241], [242,242,242], [243,243,243], [244,244,244], [245,245,245], [246,246,246], [247,247,247], [248,248,248], [249,249,249], [250,250,250], [251,251,251], [252,252,252], [253,253,253], [254,254,254], [255,255,255]],
73 | heCm: [ [0,0,0], [42,0,10], [85,0,21], [127,0,31], [127,0,47], [127,0,63], [127,0,79], [127,0,95], [127,0,102], [127,0,109], [127,0,116], [127,0,123], [127,0,131], [127,0,138], [127,0,145], [127,0,152], [127,0,159], [127,8,157], [127,17,155], [127,25,153], [127,34,151], [127,42,149], [127,51,147], [127,59,145], [127,68,143], [127,76,141], [127,85,139], [127,93,136], [127,102,134], [127,110,132], [127,119,130], [127,127,128], [127,129,126], [127,131,124], [127,133,122], [127,135,120], [127,137,118], [127,139,116], [127,141,114], [127,143,112], [127,145,110], [127,147,108], [127,149,106], [127,151,104], [127,153,102], [127,155,100], [127,157,98], [127,159,96], [127,161,94], [127,163,92], [127,165,90], [127,167,88], [127,169,86], [127,171,84], [127,173,82], [127,175,80], [127,177,77], [127,179,75], [127,181,73], [127,183,71], [127,185,69], [127,187,67], [127,189,65], [127,191,63], [128,191,64], [129,191,65], [130,191,66], [131,192,67], [132,192,68], [133,192,69], [134,192,70], [135,193,71], [136,193,72], [137,193,73], [138,193,74], [139,194,75], [140,194,76], [141,194,77], [142,194,78], [143,195,79], [144,195,80], [145,195,81], [146,195,82], [147,196,83], [148,196,84], [149,196,85], [150,196,86], [151,196,87], [152,197,88], [153,197,89], [154,197,90], [155,197,91], [156,198,92], [157,198,93], [158,198,94], [159,198,95], [160,199,96], [161,199,97], [162,199,98], [163,199,99], [164,200,100], [165,200,101], [166,200,102], [167,200,103], [168,201,104], [169,201,105], [170,201,106], [171,201,107], [172,202,108], [173,202,109], [174,202,110], [175,202,111], [176,202,112], [177,203,113], [178,203,114], [179,203,115], [180,203,116], [181,204,117], [182,204,118], [183,204,119], [184,204,120], [185,205,121], [186,205,122], [187,205,123], [188,205,124], [189,206,125], [190,206,126], [191,206,127], [191,206,128], [192,207,129], [192,207,130], [193,208,131], [193,208,132], [194,208,133], [194,209,134], [195,209,135], [195,209,136], [196,210,137], [196,210,138], [197,211,139], [197,211,140], [198,211,141], [198,212,142], [199,212,143], [199,212,144], [200,213,145], [200,213,146], [201,214,147], [201,214,148], [202,214,149], [202,215,150], [203,215,151], [203,216,152], [204,216,153], [204,216,154], [205,217,155], [205,217,156], [206,217,157], [206,218,158], [207,218,159], [207,219,160], [208,219,161], [208,219,162], [209,220,163], [209,220,164], [210,220,165], [210,221,166], [211,221,167], [211,222,168], [212,222,169], [212,222,170], [213,223,171], [213,223,172], [214,223,173], [214,224,174], [215,224,175], [215,225,176], [216,225,177], [216,225,178], [217,226,179], [217,226,180], [218,226,181], [218,227,182], [219,227,183], [219,228,184], [220,228,185], [220,228,186], [221,229,187], [221,229,188], [222,230,189], [222,230,190], [223,230,191], [223,231,192], [224,231,193], [224,231,194], [225,232,195], [225,232,196], [226,233,197], [226,233,198], [227,233,199], [227,234,200], [228,234,201], [228,234,202], [229,235,203], [229,235,204], [230,236,205], [230,236,206], [231,236,207], [231,237,208], [232,237,209], [232,237,210], [233,238,211], [233,238,212], [234,239,213], [234,239,214], [235,239,215], [235,240,216], [236,240,217], [236,240,218], [237,241,219], [237,241,220], [238,242,221], [238,242,222], [239,242,223], [239,243,224], [240,243,225], [240,244,226], [241,244,227], [241,244,228], [242,245,229], [242,245,230], [243,245,231], [243,246,232], [244,246,233], [244,247,234], [245,247,235], [245,247,236], [246,248,237], [246,248,238], [247,248,239], [247,249,240], [248,249,241], [248,250,242], [249,250,243], [249,250,244], [250,251,245], [250,251,246], [251,251,247], [251,252,248], [252,252,249], [252,253,250], [253,253,251], [253,253,252], [254,254,253], [254,254,254], [255,255,255]],
74 | heatCm: [ [0,0,0], [2,1,0], [5,2,0], [8,3,0], [11,4,0], [14,5,0], [17,6,0], [20,7,0], [23,8,0], [26,9,0], [29,10,0], [32,11,0], [35,12,0], [38,13,0], [41,14,0], [44,15,0], [47,16,0], [50,17,0], [53,18,0], [56,19,0], [59,20,0], [62,21,0], [65,22,0], [68,23,0], [71,24,0], [74,25,0], [77,26,0], [80,27,0], [83,28,0], [85,29,0], [88,30,0], [91,31,0], [94,32,0], [97,33,0], [100,34,0], [103,35,0], [106,36,0], [109,37,0], [112,38,0], [115,39,0], [118,40,0], [121,41,0], [124,42,0], [127,43,0], [130,44,0], [133,45,0], [136,46,0], [139,47,0], [142,48,0], [145,49,0], [148,50,0], [151,51,0], [154,52,0], [157,53,0], [160,54,0], [163,55,0], [166,56,0], [169,57,0], [171,58,0], [174,59,0], [177,60,0], [180,61,0], [183,62,0], [186,63,0], [189,64,0], [192,65,0], [195,66,0], [198,67,0], [201,68,0], [204,69,0], [207,70,0], [210,71,0], [213,72,0], [216,73,0], [219,74,0], [222,75,0], [225,76,0], [228,77,0], [231,78,0], [234,79,0], [237,80,0], [240,81,0], [243,82,0], [246,83,0], [249,84,0], [252,85,0], [255,86,0], [255,87,0], [255,88,0], [255,89,0], [255,90,0], [255,91,0], [255,92,0], [255,93,0], [255,94,0], [255,95,0], [255,96,0], [255,97,0], [255,98,0], [255,99,0], [255,100,0], [255,101,0], [255,102,0], [255,103,0], [255,104,0], [255,105,0], [255,106,0], [255,107,0], [255,108,0], [255,109,0], [255,110,0], [255,111,0], [255,112,0], [255,113,0], [255,114,0], [255,115,0], [255,116,0], [255,117,0], [255,118,0], [255,119,0], [255,120,0], [255,121,0], [255,122,0], [255,123,0], [255,124,0], [255,125,0], [255,126,0], [255,127,0], [255,128,0], [255,129,0], [255,130,0], [255,131,0], [255,132,0], [255,133,0], [255,134,0], [255,135,0], [255,136,0], [255,137,0], [255,138,0], [255,139,0], [255,140,0], [255,141,0], [255,142,0], [255,143,0], [255,144,0], [255,145,0], [255,146,0], [255,147,0], [255,148,0], [255,149,0], [255,150,0], [255,151,0], [255,152,0], [255,153,0], [255,154,0], [255,155,0], [255,156,0], [255,157,0], [255,158,0], [255,159,0], [255,160,0], [255,161,0], [255,162,0], [255,163,0], [255,164,0], [255,165,0], [255,166,3], [255,167,6], [255,168,9], [255,169,12], [255,170,15], [255,171,18], [255,172,21], [255,173,24], [255,174,27], [255,175,30], [255,176,33], [255,177,36], [255,178,39], [255,179,42], [255,180,45], [255,181,48], [255,182,51], [255,183,54], [255,184,57], [255,185,60], [255,186,63], [255,187,66], [255,188,69], [255,189,72], [255,190,75], [255,191,78], [255,192,81], [255,193,85], [255,194,88], [255,195,91], [255,196,94], [255,197,97], [255,198,100], [255,199,103], [255,200,106], [255,201,109], [255,202,112], [255,203,115], [255,204,118], [255,205,121], [255,206,124], [255,207,127], [255,208,130], [255,209,133], [255,210,136], [255,211,139], [255,212,142], [255,213,145], [255,214,148], [255,215,151], [255,216,154], [255,217,157], [255,218,160], [255,219,163], [255,220,166], [255,221,170], [255,222,173], [255,223,176], [255,224,179], [255,225,182], [255,226,185], [255,227,188], [255,228,191], [255,229,194], [255,230,197], [255,231,200], [255,232,203], [255,233,206], [255,234,209], [255,235,212], [255,236,215], [255,237,218], [255,238,221], [255,239,224], [255,240,227], [255,241,230], [255,242,233], [255,243,236], [255,244,239], [255,245,242], [255,246,245], [255,247,248], [255,248,251], [255,249,255], [255,250,255], [255,251,255], [255,252,255], [255,253,255], [255,254,255], [255,255,255]],
75 | rainbowCm: [ [255,0,255], [250,0,255], [245,0,255], [240,0,255], [235,0,255], [230,0,255], [225,0,255], [220,0,255], [215,0,255], [210,0,255], [205,0,255], [200,0,255], [195,0,255], [190,0,255], [185,0,255], [180,0,255], [175,0,255], [170,0,255], [165,0,255], [160,0,255], [155,0,255], [150,0,255], [145,0,255], [140,0,255], [135,0,255], [130,0,255], [125,0,255], [120,0,255], [115,0,255], [110,0,255], [105,0,255], [100,0,255], [95,0,255], [90,0,255], [85,0,255], [80,0,255], [75,0,255], [70,0,255], [65,0,255], [60,0,255], [55,0,255], [50,0,255], [45,0,255], [40,0,255], [35,0,255], [30,0,255], [25,0,255], [20,0,255], [15,0,255], [10,0,255], [5,0,255], [0,0,255], [0,5,255], [0,10,255], [0,15,255], [0,20,255], [0,25,255], [0,30,255], [0,35,255], [0,40,255], [0,45,255], [0,50,255], [0,55,255], [0,60,255], [0,65,255], [0,70,255], [0,75,255], [0,80,255], [0,85,255], [0,90,255], [0,95,255], [0,100,255], [0,105,255], [0,110,255], [0,115,255], [0,120,255], [0,125,255], [0,130,255], [0,135,255], [0,140,255], [0,145,255], [0,150,255], [0,155,255], [0,160,255], [0,165,255], [0,170,255], [0,175,255], [0,180,255], [0,185,255], [0,190,255], [0,195,255], [0,200,255], [0,205,255], [0,210,255], [0,215,255], [0,220,255], [0,225,255], [0,230,255], [0,235,255], [0,240,255], [0,245,255], [0,250,255], [0,255,255], [0,255,250], [0,255,245], [0,255,240], [0,255,235], [0,255,230], [0,255,225], [0,255,220], [0,255,215], [0,255,210], [0,255,205], [0,255,200], [0,255,195], [0,255,190], [0,255,185], [0,255,180], [0,255,175], [0,255,170], [0,255,165], [0,255,160], [0,255,155], [0,255,150], [0,255,145], [0,255,140], [0,255,135], [0,255,130], [0,255,125], [0,255,120], [0,255,115], [0,255,110], [0,255,105], [0,255,100], [0,255,95], [0,255,90], [0,255,85], [0,255,80], [0,255,75], [0,255,70], [0,255,65], [0,255,60], [0,255,55], [0,255,50], [0,255,45], [0,255,40], [0,255,35], [0,255,30], [0,255,25], [0,255,20], [0,255,15], [0,255,10], [0,255,5], [0,255,0], [5,255,0], [10,255,0], [15,255,0], [20,255,0], [25,255,0], [30,255,0], [35,255,0], [40,255,0], [45,255,0], [50,255,0], [55,255,0], [60,255,0], [65,255,0], [70,255,0], [75,255,0], [80,255,0], [85,255,0], [90,255,0], [95,255,0], [100,255,0], [105,255,0], [110,255,0], [115,255,0], [120,255,0], [125,255,0], [130,255,0], [135,255,0], [140,255,0], [145,255,0], [150,255,0], [155,255,0], [160,255,0], [165,255,0], [170,255,0], [175,255,0], [180,255,0], [185,255,0], [190,255,0], [195,255,0], [200,255,0], [205,255,0], [210,255,0], [215,255,0], [220,255,0], [225,255,0], [230,255,0], [235,255,0], [240,255,0], [245,255,0], [250,255,0], [255,255,0], [255,250,0], [255,245,0], [255,240,0], [255,235,0], [255,230,0], [255,225,0], [255,220,0], [255,215,0], [255,210,0], [255,205,0], [255,200,0], [255,195,0], [255,190,0], [255,185,0], [255,180,0], [255,175,0], [255,170,0], [255,165,0], [255,160,0], [255,155,0], [255,150,0], [255,145,0], [255,140,0], [255,135,0], [255,130,0], [255,125,0], [255,120,0], [255,115,0], [255,110,0], [255,105,0], [255,100,0], [255,95,0], [255,90,0], [255,85,0], [255,80,0], [255,75,0], [255,70,0], [255,65,0], [255,60,0], [255,55,0], [255,50,0], [255,45,0], [255,40,0], [255,35,0], [255,30,0], [255,25,0], [255,20,0], [255,15,0], [255,10,0], [255,5,0], [255,0,0]],
76 | redCm: [ [0,0,0], [1,0,0], [2,0,0], [3,0,0], [4,0,0], [5,0,0], [6,0,0], [7,0,0], [8,0,0], [9,0,0], [10,0,0], [11,0,0], [12,0,0], [13,0,0], [14,0,0], [15,0,0], [16,0,0], [17,0,0], [18,0,0], [19,0,0], [20,0,0], [21,0,0], [22,0,0], [23,0,0], [24,0,0], [25,0,0], [26,0,0], [27,0,0], [28,0,0], [29,0,0], [30,0,0], [31,0,0], [32,0,0], [33,0,0], [34,0,0], [35,0,0], [36,0,0], [37,0,0], [38,0,0], [39,0,0], [40,0,0], [41,0,0], [42,0,0], [43,0,0], [44,0,0], [45,0,0], [46,0,0], [47,0,0], [48,0,0], [49,0,0], [50,0,0], [51,0,0], [52,0,0], [53,0,0], [54,0,0], [55,0,0], [56,0,0], [57,0,0], [58,0,0], [59,0,0], [60,0,0], [61,0,0], [62,0,0], [63,0,0], [64,0,0], [65,0,0], [66,0,0], [67,0,0], [68,0,0], [69,0,0], [70,0,0], [71,0,0], [72,0,0], [73,0,0], [74,0,0], [75,0,0], [76,0,0], [77,0,0], [78,0,0], [79,0,0], [80,0,0], [81,0,0], [82,0,0], [83,0,0], [84,0,0], [85,0,0], [86,0,0], [87,0,0], [88,0,0], [89,0,0], [90,0,0], [91,0,0], [92,0,0], [93,0,0], [94,0,0], [95,0,0], [96,0,0], [97,0,0], [98,0,0], [99,0,0], [100,0,0], [101,0,0], [102,0,0], [103,0,0], [104,0,0], [105,0,0], [106,0,0], [107,0,0], [108,0,0], [109,0,0], [110,0,0], [111,0,0], [112,0,0], [113,0,0], [114,0,0], [115,0,0], [116,0,0], [117,0,0], [118,0,0], [119,0,0], [120,0,0], [121,0,0], [122,0,0], [123,0,0], [124,0,0], [125,0,0], [126,0,0], [127,0,0], [128,0,0], [129,0,0], [130,0,0], [131,0,0], [132,0,0], [133,0,0], [134,0,0], [135,0,0], [136,0,0], [137,0,0], [138,0,0], [139,0,0], [140,0,0], [141,0,0], [142,0,0], [143,0,0], [144,0,0], [145,0,0], [146,0,0], [147,0,0], [148,0,0], [149,0,0], [150,0,0], [151,0,0], [152,0,0], [153,0,0], [154,0,0], [155,0,0], [156,0,0], [157,0,0], [158,0,0], [159,0,0], [160,0,0], [161,0,0], [162,0,0], [163,0,0], [164,0,0], [165,0,0], [166,0,0], [167,0,0], [168,0,0], [169,0,0], [170,0,0], [171,0,0], [172,0,0], [173,0,0], [174,0,0], [175,0,0], [176,0,0], [177,0,0], [178,0,0], [179,0,0], [180,0,0], [181,0,0], [182,0,0], [183,0,0], [184,0,0], [185,0,0], [186,0,0], [187,0,0], [188,0,0], [189,0,0], [190,0,0], [191,0,0], [192,0,0], [193,0,0], [194,0,0], [195,0,0], [196,0,0], [197,0,0], [198,0,0], [199,0,0], [200,0,0], [201,0,0], [202,0,0], [203,0,0], [204,0,0], [205,0,0], [206,0,0], [207,0,0], [208,0,0], [209,0,0], [210,0,0], [211,0,0], [212,0,0], [213,0,0], [214,0,0], [215,0,0], [216,0,0], [217,0,0], [218,0,0], [219,0,0], [220,0,0], [221,0,0], [222,0,0], [223,0,0], [224,0,0], [225,0,0], [226,0,0], [227,0,0], [228,0,0], [229,0,0], [230,0,0], [231,0,0], [232,0,0], [233,0,0], [234,0,0], [235,0,0], [236,0,0], [237,0,0], [238,0,0], [239,0,0], [240,0,0], [241,0,0], [242,0,0], [243,0,0], [244,0,0], [245,0,0], [246,0,0], [247,0,0], [248,0,0], [249,0,0], [250,0,0], [251,0,0], [252,0,0], [253,0,0], [254,0,0], [255,0,0]],
77 | standardCm: [ [0,0,0], [0,0,3], [1,1,6], [2,2,9], [3,3,12], [4,4,15], [5,5,18], [6,6,21], [7,7,24], [8,8,27], [9,9,30], [10,10,33], [10,10,36], [11,11,39], [12,12,42], [13,13,45], [14,14,48], [15,15,51], [16,16,54], [17,17,57], [18,18,60], [19,19,63], [20,20,66], [20,20,69], [21,21,72], [22,22,75], [23,23,78], [24,24,81], [25,25,85], [26,26,88], [27,27,91], [28,28,94], [29,29,97], [30,30,100], [30,30,103], [31,31,106], [32,32,109], [33,33,112], [34,34,115], [35,35,118], [36,36,121], [37,37,124], [38,38,127], [39,39,130], [40,40,133], [40,40,136], [41,41,139], [42,42,142], [43,43,145], [44,44,148], [45,45,151], [46,46,154], [47,47,157], [48,48,160], [49,49,163], [50,50,166], [51,51,170], [51,51,173], [52,52,176], [53,53,179], [54,54,182], [55,55,185], [56,56,188], [57,57,191], [58,58,194], [59,59,197], [60,60,200], [61,61,203], [61,61,206], [62,62,209], [63,63,212], [64,64,215], [65,65,218], [66,66,221], [67,67,224], [68,68,227], [69,69,230], [70,70,233], [71,71,236], [71,71,239], [72,72,242], [73,73,245], [74,74,248], [75,75,251], [76,76,255], [0,78,0], [1,80,1], [2,82,2], [3,84,3], [4,87,4], [5,89,5], [6,91,6], [7,93,7], [8,95,8], [9,97,9], [9,99,9], [10,101,10], [11,103,11], [12,105,12], [13,108,13], [14,110,14], [15,112,15], [16,114,16], [17,116,17], [18,118,18], [18,120,18], [19,122,19], [20,124,20], [21,126,21], [22,129,22], [23,131,23], [24,133,24], [25,135,25], [26,137,26], [27,139,27], [27,141,27], [28,143,28], [29,145,29], [30,147,30], [31,150,31], [32,152,32], [33,154,33], [34,156,34], [35,158,35], [36,160,36], [36,162,36], [37,164,37], [38,166,38], [39,168,39], [40,171,40], [41,173,41], [42,175,42], [43,177,43], [44,179,44], [45,181,45], [45,183,45], [46,185,46], [47,187,47], [48,189,48], [49,192,49], [50,194,50], [51,196,51], [52,198,52], [53,200,53], [54,202,54], [54,204,54], [55,206,55], [56,208,56], [57,210,57], [58,213,58], [59,215,59], [60,217,60], [61,219,61], [62,221,62], [63,223,63], [63,225,63], [64,227,64], [65,229,65], [66,231,66], [67,234,67], [68,236,68], [69,238,69], [70,240,70], [71,242,71], [72,244,72], [72,246,72], [73,248,73], [74,250,74], [75,252,75], [76,255,76], [78,0,0], [80,1,1], [82,2,2], [84,3,3], [86,4,4], [88,5,5], [91,6,6], [93,7,7], [95,8,8], [97,8,8], [99,9,9], [101,10,10], [103,11,11], [105,12,12], [107,13,13], [109,14,14], [111,15,15], [113,16,16], [115,16,16], [118,17,17], [120,18,18], [122,19,19], [124,20,20], [126,21,21], [128,22,22], [130,23,23], [132,24,24], [134,24,24], [136,25,25], [138,26,26], [140,27,27], [142,28,28], [144,29,29], [147,30,30], [149,31,31], [151,32,32], [153,32,32], [155,33,33], [157,34,34], [159,35,35], [161,36,36], [163,37,37], [165,38,38], [167,39,39], [169,40,40], [171,40,40], [174,41,41], [176,42,42], [178,43,43], [180,44,44], [182,45,45], [184,46,46], [186,47,47], [188,48,48], [190,48,48], [192,49,49], [194,50,50], [196,51,51], [198,52,52], [201,53,53], [203,54,54], [205,55,55], [207,56,56], [209,56,56], [211,57,57], [213,58,58], [215,59,59], [217,60,60], [219,61,61], [221,62,62], [223,63,63], [225,64,64], [228,64,64], [230,65,65], [232,66,66], [234,67,67], [236,68,68], [238,69,69], [240,70,70], [242,71,71], [244,72,72], [246,72,72], [248,73,73], [250,74,74], [252,75,75], [255,76,76]]
78 | };
79 | var cmapOptions = '';
80 | Object.keys(cmaps).forEach(function(c) {
81 | cmapOptions += '';
82 | });
83 | var $html = $('' +
84 | ' Colormap:
' +
86 | ' Center: ' +
87 | '
');
88 | var cmapUpdate = function() {
89 | var val = $('#cmapSelect').val();
90 | $('#cmapSelect').change(function() {
91 | updateCallback(val);
92 | });
93 | return cmaps[val];
94 | };
95 | var spinnerSlider = new SpinnerSlider({
96 | $element: $html.find('#cmapCenter'),
97 | init: 128,
98 | min: 1,
99 | sliderMax: 254,
100 | step: 1,
101 | updateCallback: updateCallback
102 | });
103 | return {
104 | html: $html,
105 | getParams: function() {
106 | return spinnerSlider.getValue();
107 | },
108 | getFilter: function() {
109 | /*eslint new-cap: 0*/
110 | return OpenSeadragon.Filters.COLORMAP(cmapUpdate(), spinnerSlider.getValue());
111 | },
112 | sync: true
113 | };
114 | }
115 | }, {
116 | name: 'Colorize',
117 | help: 'The adjustment range (strength) is from 0 to 100.' +
118 | 'The higher the value, the closer the colors in the ' +
119 | 'image shift towards the given adjustment color.' +
120 | 'Color values are between 0 to 255',
121 | generate: function(updateCallback) {
122 | var redSpinnerId = 'redSpinner-' + idIncrement;
123 | var greenSpinnerId = 'greenSpinner-' + idIncrement;
124 | var blueSpinnerId = 'blueSpinner-' + idIncrement;
125 | var strengthSpinnerId = 'strengthSpinner-' + idIncrement;
126 | /*eslint max-len: 0*/
127 | var $html = $('' +
128 | '
' +
129 | '
' +
130 | ' Red: ' +
131 | '
' +
132 | '
' +
133 | ' Green: ' +
134 | '
' +
135 | '
' +
136 | ' Blue: ' +
137 | '
' +
138 | '
' +
139 | ' Strength: ' +
140 | '
' +
141 | '
' +
142 | '
');
143 | var redSpinner = new Spinner({
144 | $element: $html.find('#' + redSpinnerId),
145 | init: 100,
146 | min: 0,
147 | max: 255,
148 | step: 1,
149 | updateCallback: updateCallback
150 | });
151 | var greenSpinner = new Spinner({
152 | $element: $html.find('#' + greenSpinnerId),
153 | init: 20,
154 | min: 0,
155 | max: 255,
156 | step: 1,
157 | updateCallback: updateCallback
158 | });
159 | var blueSpinner = new Spinner({
160 | $element: $html.find('#' + blueSpinnerId),
161 | init: 20,
162 | min: 0,
163 | max: 255,
164 | step: 1,
165 | updateCallback: updateCallback
166 | });
167 | var strengthSpinner = new Spinner({
168 | $element: $html.find('#' + strengthSpinnerId),
169 | init: 50,
170 | min: 0,
171 | max: 100,
172 | step: 1,
173 | updateCallback: updateCallback
174 | });
175 | return {
176 | html: $html,
177 | getParams: function() {
178 | var red = redSpinner.getValue();
179 | var green = greenSpinner.getValue();
180 | var blue = blueSpinner.getValue();
181 | var strength = strengthSpinner.getValue();
182 | return 'R: ' + red + ' G: ' + green + ' B: ' + blue +
183 | ' S: ' + strength;
184 | },
185 | getFilter: function() {
186 | var red = redSpinner.getValue();
187 | var green = greenSpinner.getValue();
188 | var blue = blueSpinner.getValue();
189 | var strength = strengthSpinner.getValue();
190 | return function(context, callback) {
191 | caman(context.canvas, function() {
192 | this.colorize(red, green, blue, strength);
193 | this.render(callback);
194 | });
195 | };
196 | }
197 | };
198 | }
199 | }, {
200 | name: 'Contrast',
201 | help: 'Range is from 0 to infinity, although sane values are from 0 ' +
202 | 'to 4 or 5. Values between 0 and 1 will lessen the contrast ' +
203 | 'while values greater than 1 will increase it.',
204 | generate: function(updateCallback) {
205 | var $html = $('');
206 | var spinnerSlider = new SpinnerSlider({
207 | $element: $html,
208 | init: 1.3,
209 | min: 0,
210 | sliderMax: 4,
211 | step: 0.1,
212 | updateCallback: updateCallback
213 | });
214 | return {
215 | html: $html,
216 | getParams: function() {
217 | return spinnerSlider.getValue();
218 | },
219 | getFilter: function() {
220 | return OpenSeadragon.Filters.CONTRAST(
221 | spinnerSlider.getValue());
222 | },
223 | sync: true
224 | };
225 | }
226 | }, {
227 | name: 'Exposure',
228 | help: 'Range is -100 to 100. Values < 0 will decrease ' +
229 | 'exposure while values > 0 will increase exposure',
230 | generate: function(updateCallback) {
231 | var $html = $('');
232 | var spinnerSlider = new SpinnerSlider({
233 | $element: $html,
234 | init: 10,
235 | min: -100,
236 | max: 100,
237 | step: 1,
238 | updateCallback: updateCallback
239 | });
240 | return {
241 | html: $html,
242 | getParams: function() {
243 | return spinnerSlider.getValue();
244 | },
245 | getFilter: function() {
246 | var value = spinnerSlider.getValue();
247 | return function(context, callback) {
248 | caman(context.canvas, function() {
249 | this.exposure(value);
250 | this.render(callback); // don't forget to call the callback.
251 | });
252 | };
253 | }
254 | };
255 | }
256 | }, {
257 | name: 'Gamma',
258 | help: 'Range is from 0 to infinity, although sane values ' +
259 | 'are from 0 to 4 or 5. Values between 0 and 1 will ' +
260 | 'lessen the contrast while values greater than 1 will increase it.',
261 | generate: function(updateCallback) {
262 | var $html = $('');
263 | var spinnerSlider = new SpinnerSlider({
264 | $element: $html,
265 | init: 0.5,
266 | min: 0,
267 | sliderMax: 5,
268 | step: 0.1,
269 | updateCallback: updateCallback
270 | });
271 | return {
272 | html: $html,
273 | getParams: function() {
274 | return spinnerSlider.getValue();
275 | },
276 | getFilter: function() {
277 | var value = spinnerSlider.getValue();
278 | return OpenSeadragon.Filters.GAMMA(value);
279 | }
280 | };
281 | }
282 | }, {
283 | name: 'Hue',
284 | help: 'hue value is between 0 to 100 representing the ' +
285 | 'percentage of Hue shift in the 0 to 360 range',
286 | generate: function(updateCallback) {
287 | var $html = $('');
288 | var spinnerSlider = new SpinnerSlider({
289 | $element: $html,
290 | init: 20,
291 | min: 0,
292 | max: 100,
293 | step: 1,
294 | updateCallback: updateCallback
295 | });
296 | return {
297 | html: $html,
298 | getParams: function() {
299 | return spinnerSlider.getValue();
300 | },
301 | getFilter: function() {
302 | var value = spinnerSlider.getValue();
303 | return function(context, callback) {
304 | caman(context.canvas, function() {
305 | this.hue(value);
306 | this.render(callback); // don't forget to call the callback.
307 | });
308 | };
309 | }
310 | };
311 | }
312 | }, {
313 | name: 'Saturation',
314 | help: 'saturation value has to be between -100 and 100',
315 | generate: function(updateCallback) {
316 | var $html = $('');
317 | var spinnerSlider = new SpinnerSlider({
318 | $element: $html,
319 | init: 50,
320 | min: -100,
321 | max: 100,
322 | step: 1,
323 | updateCallback: updateCallback
324 | });
325 | return {
326 | html: $html,
327 | getParams: function() {
328 | return spinnerSlider.getValue();
329 | },
330 | getFilter: function() {
331 | var value = spinnerSlider.getValue();
332 | return function(context, callback) {
333 | caman(context.canvas, function() {
334 | this.saturation(value);
335 | this.render(callback); // don't forget to call the callback.
336 | });
337 | };
338 | }
339 | };
340 | }
341 | }, {
342 | name: 'Vibrance',
343 | help: 'vibrance value has to be between -100 and 100',
344 | generate: function(updateCallback) {
345 | var $html = $('');
346 | var spinnerSlider = new SpinnerSlider({
347 | $element: $html,
348 | init: 50,
349 | min: -100,
350 | max: 100,
351 | step: 1,
352 | updateCallback: updateCallback
353 | });
354 | return {
355 | html: $html,
356 | getParams: function() {
357 | return spinnerSlider.getValue();
358 | },
359 | getFilter: function() {
360 | var value = spinnerSlider.getValue();
361 | return function(context, callback) {
362 | caman(context.canvas, function() {
363 | this.vibrance(value);
364 | this.render(callback); // don't forget to call the callback.
365 | });
366 | };
367 | }
368 | };
369 | }
370 | }, {
371 | name: 'Sepia',
372 | help: 'sepia value has to be between 0 and 100',
373 | generate: function(updateCallback) {
374 | var $html = $('');
375 | var spinnerSlider = new SpinnerSlider({
376 | $element: $html,
377 | init: 50,
378 | min: 0,
379 | max: 100,
380 | step: 1,
381 | updateCallback: updateCallback
382 | });
383 | return {
384 | html: $html,
385 | getParams: function() {
386 | return spinnerSlider.getValue();
387 | },
388 | getFilter: function() {
389 | var value = spinnerSlider.getValue();
390 | return function(context, callback) {
391 | caman(context.canvas, function() {
392 | this.sepia(value);
393 | this.render(callback); // don't forget to call the callback.
394 | });
395 | };
396 | }
397 | };
398 | }
399 | }, {
400 | name: 'Noise',
401 | help: 'Noise cannot be smaller than 0',
402 | generate: function(updateCallback) {
403 | var $html = $('');
404 | var spinnerSlider = new SpinnerSlider({
405 | $element: $html,
406 | init: 50,
407 | min: 0,
408 | step: 1,
409 | updateCallback: updateCallback
410 | });
411 | return {
412 | html: $html,
413 | getParams: function() {
414 | return spinnerSlider.getValue();
415 | },
416 | getFilter: function() {
417 | var value = spinnerSlider.getValue();
418 | return function(context, callback) {
419 | caman(context.canvas, function() {
420 | this.noise(value);
421 | this.render(callback); // don't forget to call the callback.
422 | });
423 | };
424 | }
425 | };
426 | }
427 | }, {
428 | name: 'Greyscale',
429 | generate: function() {
430 | return {
431 | html: '',
432 | getParams: function() {
433 | return '';
434 | },
435 | getFilter: function() {
436 | return OpenSeadragon.Filters.GREYSCALE();
437 | },
438 | sync: true
439 | };
440 | }
441 | }, {
442 | name: 'Sobel Edge',
443 | generate: function() {
444 | return {
445 | html: '',
446 | getParams: function() {
447 | return '';
448 | },
449 | getFilter: function() {
450 | return function(context, callback) {
451 | var imgData = context.getImageData(
452 | 0, 0, context.canvas.width, context.canvas.height);
453 | var pixels = imgData.data;
454 | var originalPixels = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data;
455 | var oneRowOffset = context.canvas.width * 4;
456 | var onePixelOffset = 4;
457 | var Gy, Gx;
458 | var idx = 0;
459 | for (var i = 1; i < context.canvas.height - 1; i += 1) {
460 | idx = oneRowOffset * i + 4;
461 | for (var j = 1; j < context.canvas.width - 1; j += 1) {
462 | Gy = originalPixels[idx - onePixelOffset + oneRowOffset] + 2 * originalPixels[idx + oneRowOffset] + originalPixels[idx + onePixelOffset + oneRowOffset];
463 | Gy = Gy - (originalPixels[idx - onePixelOffset - oneRowOffset] + 2 * originalPixels[idx - oneRowOffset] + originalPixels[idx + onePixelOffset - oneRowOffset]);
464 | Gx = originalPixels[idx + onePixelOffset - oneRowOffset] + 2 * originalPixels[idx + onePixelOffset] + originalPixels[idx + onePixelOffset + oneRowOffset];
465 | Gx = Gx - (originalPixels[idx - onePixelOffset - oneRowOffset] + 2 * originalPixels[idx - onePixelOffset] + originalPixels[idx - onePixelOffset + oneRowOffset]);
466 | pixels[idx] = Math.sqrt(Gx * Gx + Gy * Gy); // 0.5*Math.abs(Gx) + 0.5*Math.abs(Gy);//100*Math.atan(Gy,Gx);
467 | pixels[idx + 1] = 0;
468 | pixels[idx + 2] = 0;
469 | idx += 4;
470 | }
471 | }
472 | context.putImageData(imgData, 0, 0);
473 | callback();
474 | };
475 | }
476 | };
477 | }
478 | }, {
479 | name: 'Brightness',
480 | help: 'Brightness must be between -255 (darker) and 255 (brighter).',
481 | generate: function(updateCallback) {
482 | var $html = $('');
483 | var spinnerSlider = new SpinnerSlider({
484 | $element: $html,
485 | init: 50,
486 | min: -255,
487 | max: 255,
488 | step: 1,
489 | updateCallback: updateCallback
490 | });
491 | return {
492 | html: $html,
493 | getParams: function() {
494 | return spinnerSlider.getValue();
495 | },
496 | getFilter: function() {
497 | return OpenSeadragon.Filters.BRIGHTNESS(
498 | spinnerSlider.getValue());
499 | },
500 | sync: true
501 | };
502 | }
503 | }, {
504 | name: 'Erosion',
505 | help: 'The erosion kernel size must be an odd number.',
506 | generate: function(updateCallback) {
507 | var $html = $('');
508 | var spinner = new Spinner({
509 | $element: $html,
510 | init: 3,
511 | min: 3,
512 | step: 2,
513 | updateCallback: updateCallback
514 | });
515 | return {
516 | html: $html,
517 | getParams: function() {
518 | return spinner.getValue();
519 | },
520 | getFilter: function() {
521 | return OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(
522 | spinner.getValue(), Math.min);
523 | }
524 | };
525 | }
526 | }, {
527 | name: 'Dilation',
528 | help: 'The dilation kernel size must be an odd number.',
529 | generate: function(updateCallback) {
530 | var $html = $('');
531 | var spinner = new Spinner({
532 | $element: $html,
533 | init: 3,
534 | min: 3,
535 | step: 2,
536 | updateCallback: updateCallback
537 | });
538 | return {
539 | html: $html,
540 | getParams: function() {
541 | return spinner.getValue();
542 | },
543 | getFilter: function() {
544 | return OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(
545 | spinner.getValue(), Math.max);
546 | }
547 | };
548 | }
549 | }, {
550 | name: 'Thresholding',
551 | help: 'The threshold must be between 0 and 255.',
552 | generate: function(updateCallback) {
553 | var $html = $('');
554 | var spinnerSlider = new SpinnerSlider({
555 | $element: $html,
556 | init: 127,
557 | min: 0,
558 | max: 255,
559 | step: 1,
560 | updateCallback: updateCallback
561 | });
562 | return {
563 | html: $html,
564 | getParams: function() {
565 | return spinnerSlider.getValue();
566 | },
567 | getFilter: function() {
568 | return OpenSeadragon.Filters.THRESHOLDING(
569 | spinnerSlider.getValue());
570 | },
571 | sync: true
572 | };
573 | }
574 | }];
575 | availableFilters.sort(function(f1, f2) {
576 | return f1.name.localeCompare(f2.name);
577 | });
578 |
579 | var idIncrement = 0;
580 | var hashTable = {};
581 |
582 | availableFilters.forEach(function(filter) {
583 | var $li = $('');
584 | var $plus = $('
');
585 | $li.append($plus);
586 | $li.append(filter.name);
587 | $li.appendTo($('#available'));
588 | $plus.click(function() {
589 | var id = 'selected_' + idIncrement++;
590 | var generatedFilter = filter.generate(updateFilters);
591 | hashTable[id] = {
592 | name: filter.name,
593 | generatedFilter: generatedFilter
594 | };
595 | var $li = $('');
596 | var $minus = $('');
597 | $li.find('.wdzt-row-layout').append($minus);
598 | $li.find('.wdzt-row-layout').append('' + filter.name + '
');
599 | if (filter.help) {
600 | var $help = $('');
602 | $help.tooltip();
603 | $li.find('.wdzt-row-layout').append($help);
604 | }
605 | $li.find('.wdzt-row-layout').append(
606 | $('')
607 | .append(generatedFilter.html));
608 | $minus.click(function() {
609 | delete hashTable[id];
610 | $li.remove();
611 | updateFilters();
612 | });
613 | $li.appendTo($('#selected'));
614 | updateFilters();
615 | });
616 | });
617 |
618 | $('#selected').sortable({
619 | containment: 'parent',
620 | axis: 'y',
621 | tolerance: 'pointer',
622 | update: updateFilters
623 | });
624 |
625 | function updateFilters() {
626 | var filters = [];
627 | var sync = true;
628 | $('#selected li').each(function() {
629 | var id = this.id;
630 | var filter = hashTable[id];
631 | filters.push(filter.generatedFilter.getFilter());
632 | sync &= filter.generatedFilter.sync;
633 | });
634 | viewer.setFilterOptions({
635 | filters: {
636 | processors: filters
637 | },
638 | loadMode: sync ? 'sync' : 'async'
639 | });
640 | }
641 |
642 |
--------------------------------------------------------------------------------