PageSourceSearch

https://www.toolpic.com/app/editor/js/image.js

js toolpic.com collected 2026-10-02 10:53:43 UTC 33,906 bytes, 1,092 lines download raw bytes

1/* global MAIN, EVENTS, LAYER, POP, HELPER, TOOLS, DRAW, GUI, EDIT, FILE */
2/* global canvas_active, ImageFilters, WIDTH, HEIGHT, canvas_active, canvas_front */
3
4var IMAGE = new IMAGE_CLASS();
5
6/** 
7 * manages image actions
8 * 
9 * @author toolpic.com
10 */
11function IMAGE_CLASS() {
12
13	//information
14	this.image_information = function () {
15		var _this = this;
16		var pixels = WIDTH*HEIGHT;
17		pixels = HELPER.number_format(pixels, 0);
18		
19		POP.add({title: "Width:", value: WIDTH});
20		POP.add({title: "Height:", value: HEIGHT});
21		POP.add({title: "Pixels:", value: pixels});
22		POP.add({title: "Unique colors:", value: '...'});
23		
24		//show general data
25		for (var i in FILE.file_info.general){
26			POP.add({title: i + ":", value: FILE.file_info.general[i]});
27		}
28		
29		//show exif data
30		var n = 0;
31		for (var i in FILE.file_info.exif){
32			if(i == 'undefined')
33				continue;
34			if(n == 0)
35				POP.add({title: "==== EXIF ====", value: ''});
36			POP.add({title: i + ":", value: FILE.file_info.exif[i]});
37			n++;
38		}
39		
40		POP.show('Information', null, null, function(){
41			//calc colors
42			setTimeout(function(){
43				var colors = _this.unique_colors_count(canvas_active(true));
44				colors = HELPER.number_format(colors, 0);
45				document.getElementById('pop_data_uniquecolo').innerHTML = colors;
46			}, 10);
47		});
48	};
49
50	//size
51	this.image_size = function () {
52		POP.add({name: "width", title: "Width:", value: WIDTH, placeholder: WIDTH});
53		POP.add({name: "height", title: "Height:", value: HEIGHT, placeholder: HEIGHT});
54		POP.show('Attributes', [IMAGE, 'resize_custom']);
55	};
56
57	//trim
58	this.image_trim = function () {
59		EDIT.save_state();
60		this.trim();
61	};
62
63	//crop
64	this.image_crop = function () {
65		EDIT.save_state();
66		if (DRAW.select_data == false) {
67			POP.add({html: 'Select area first'});
68			POP.show('Error', '');
69		}
70		else {
71			for (var i in LAYER.layers) {
72				var layer = document.getElementById(LAYER.layers[i].name).getContext("2d");
73
74				var tmp = layer.getImageData(DRAW.select_data.x, DRAW.select_data.y, DRAW.select_data.w, DRAW.select_data.h);
75				layer.clearRect(0, 0, WIDTH, HEIGHT);
76				layer.putImageData(tmp, 0, 0);
77			}
78
79			//resize
80			EDIT.save_state();
81			WIDTH = DRAW.select_data.w;
82			HEIGHT = DRAW.select_data.h;
83			LAYER.set_canvas_size();
84
85			DRAW.select_data = false;
86			canvas_front.clearRect(0, 0, WIDTH, HEIGHT);
87		}
88	};
89
90	//resize
91	this.image_resize = function () {
92		this.resize_box();
93	};
94
95	//rotate left
96	this.image_rotate_left = function () {
97		EDIT.save_state();
98		this.rotate_resize_doc(270, WIDTH, HEIGHT);
99		this.rotate_layer({angle: 270}, canvas_active(), WIDTH, HEIGHT);
100	};
101
102	//rotate right
103	this.image_rotate_right = function () {
104		EDIT.save_state();
105		this.rotate_resize_doc(90, WIDTH, HEIGHT);
106		this.rotate_layer({angle: 90}, canvas_active(), WIDTH, HEIGHT);
107	};
108
109	//rotate
110	this.image_rotate = function () {
111		POP.add({name: "angle", title: "Enter angle (0-360):", value: 0, range: [0, 360]});
112		POP.add({name: "mode", title: "Area:", values: ['All', 'Visible']});
113		POP.show(
114			'Rotate', 
115			function (response) {
116				EDIT.save_state();
117				if(response.mode == 'All')
118					IMAGE.rotate_resize_doc(response.angle, WIDTH, HEIGHT);
119				IMAGE.rotate_layer(response, canvas_active(), WIDTH, HEIGHT);
120			},
121			function (response, canvas_preview, w, h) {
122				IMAGE.rotate_layer(response, canvas_preview, w, h);
123			}
124		);
125	};
126
127	//vertical flip
128	this.image_vflip = function () {
129		EDIT.save_state();
130		var tempCanvas = document.createElement("canvas");
131		var tempCtx = tempCanvas.getContext("2d");
132		tempCanvas.width = WIDTH;
133		tempCanvas.height = HEIGHT;
134		tempCtx.drawImage(canvas_active(true), 0, 0, WIDTH, HEIGHT);
135		//flip
136		canvas_active().clearRect(0, 0, WIDTH, HEIGHT);
137		canvas_active().save();
138		canvas_active().scale(1, -1);
139		canvas_active().drawImage(tempCanvas, 0, -HEIGHT);
140		canvas_active().restore();
141	};
142
143	//horizontal flip
144	this.image_hflip = function () {
145		EDIT.save_state();
146		var tempCanvas = document.createElement("canvas");
147		var tempCtx = tempCanvas.getContext("2d");
148		tempCanvas.width = WIDTH;
149		tempCanvas.height = HEIGHT;
150		tempCtx.drawImage(canvas_active(true), 0, 0, WIDTH, HEIGHT);
151		//flip
152		canvas_active().clearRect(0, 0, WIDTH, HEIGHT);
153		canvas_active().save();
154		canvas_active().scale(-1, 1);
155		canvas_active().drawImage(tempCanvas, -WIDTH, 0);
156		canvas_active().restore();
157	};
158
159	//color corrections
160	this.image_colors = function () {
161		POP.add({name: "param1", title: "Brightness:", value: "0", range: [-100, 100]});
162		POP.add({name: "param2", title: "Contrast:", value: "0", range: [-100, 100]});
163		POP.add({});
164		POP.add({name: "param_red", title: "Red channel:", value: "0", range: [-255, 255]});
165		POP.add({name: "param_green", title: "Green channel:", value: "0", range: [-255, 255]});
166		POP.add({name: "param_blue", title: "Blue channel:", value: "0", range: [-255, 255]});
167		POP.add({});
168		POP.add({name: "param_h", title: "Hue:", value: "0", range: [-180, 180]});
169		POP.add({name: "param_s", title: "Saturation:", value: "0", range: [-100, 100]});
170		POP.add({name: "param_l", title: "Luminance:", value: "0", range: [-100, 100]});
171
172		POP.show(
173			'Color corrections', 
174			function (user_response) {
175				EDIT.save_state();
176				var param1 = parseInt(user_response.param1);
177				var param2 = parseInt(user_response.param2);
178				var param_red = parseInt(user_response.param_red);
179				var param_green = parseInt(user_response.param_green);
180				var param_blue = parseInt(user_response.param_blue);
181				var param_h = parseInt(user_response.param_h);
182				var param_s = parseInt(user_response.param_s);
183				var param_l = parseInt(user_response.param_l);
184
185				var imageData = canvas_active().getImageData(0, 0, WIDTH, HEIGHT);
186				//Brightness/Contrast
187				var filtered = ImageFilters.BrightnessContrastPhotoshop(imageData, param1, param2);
188				//RGB corrections
189				var filtered = ImageFilters.ColorTransformFilter(filtered, 1, 1, 1, 1, param_red, param_green, param_blue, 1);
190				//hue/saturation/luminance
191				var filtered = ImageFilters.HSLAdjustment(filtered, param_h, param_s, param_l);
192				canvas_active().putImageData(filtered, 0, 0);
193				GUI.zoom();
194			},
195			function (user_response, canvas_preview, w, h) {
196				var param1 = parseInt(user_response.param1);
197				var param2 = parseInt(user_response.param2);
198				var param_red = parseInt(user_response.param_red);
199				var param_green = parseInt(user_response.param_green);
200				var param_blue = parseInt(user_response.param_blue);
201				var param_h = parseInt(user_response.param_h);
202				var param_s = parseInt(user_response.param_s);
203				var param_l = parseInt(user_response.param_l);
204
205				var imageData = canvas_preview.getImageData(0, 0, w, h);
206				//Brightness/Contrast
207				var filtered = ImageFilters.BrightnessContrastPhotoshop(imageData, param1, param2);	//add effect
208				//RGB corrections
209				var filtered = ImageFilters.ColorTransformFilter(filtered, 1, 1, 1, 1, param_red, param_green, param_blue, 1);
210				//hue/saturation/luminance
211				var filtered = ImageFilters.HSLAdjustment(filtered, param_h, param_s, param_l);
212				canvas_preview.putImageData(filtered, 0, 0);
213			}
214		);
215	};
216
217	//auto adjust colors
218	this.image_auto_adjust = function () {
219		EDIT.save_state();
220		this.auto_adjust(canvas_active(), WIDTH, HEIGHT);
221	};
222
223	//convert to grayscale
224	this.image_GrayScale = function () {
225		EDIT.save_state();
226		var imageData = canvas_active().getImageData(0, 0, WIDTH, HEIGHT);
227		var filtered = ImageFilters.GrayScale(imageData);	//add effect
228		canvas_active().putImageData(filtered, 0, 0);
229	};
230
231	//enchance colors
232	this.image_decrease_colors = function () {
233		POP.add({name: "param1", title: "Colors:", value: "10", range: [1, 256]});
234		POP.add({name: "param3", title: "Greyscale:", values: ["No", "Yes"], });
235		POP.show(
236			'Decrease colors', 
237			function (user_response) {
238				EDIT.save_state();
239				var param1 = parseInt(user_response.param1);
240				if (user_response.param3 == 'Yes')
241					param3 = true;
242				else
243					param3 = false;
244
245				IMAGE.decrease_colors(canvas_active(true), canvas_active(true), WIDTH, HEIGHT, param1, param3);
246				GUI.zoom();
247			},
248			function (user_response, canvas_preview, w, h) {
249				var param1 = parseInt(user_response.param1);
250				if (user_response.param3 == 'Yes')
251					param3 = true;
252				else
253					param3 = false;
254
255				IMAGE.decrease_colors(canvas_active(true), document.getElementById("pop_post"), w, h, param1, param3);
256			}
257		);
258	};
259
260	//negative
261	this.image_negative = function () {
262		EDIT.save_state();
263		if (DRAW.select_data == false)
264			var imageData = canvas_active().getImageData(0, 0, WIDTH, HEIGHT);
265		else
266			var imageData = canvas_active().getImageData(DRAW.select_data.x, DRAW.select_data.y, DRAW.select_data.w, DRAW.select_data.h);
267		var pixels = imageData.data;
268		for (var i = 0; i < pixels.length; i += 4) {
269			pixels[i] = 255 - pixels[i]; // red
270			pixels[i + 1] = 255 - pixels[i + 1]; // green
271			pixels[i + 2] = 255 - pixels[i + 2]; // blue
272		}
273		//save
274		if (DRAW.select_data == false)
275			canvas_active().putImageData(imageData, 0, 0);
276		else
277			canvas_active().putImageData(imageData, DRAW.select_data.x, DRAW.select_data.y);
278	};
279
280	//grid
281	this.image_grid = function () {
282		POP.add({name: "visible", title: "Visible:", value: "Yes", values: ["Yes", "No"]});
283		POP.add({name: "gap_x", title: "Horizontal gap:", value: GUI.grid_size[0]});
284		POP.add({name: "gap_y", title: "Vertical gap:", value: GUI.grid_size[1]});
285		POP.show(
286			'Grid', 
287			function (response) {
288				if (response.visible == "Yes") {
289					GUI.grid = true;
290					gap_x = response.gap_x;
291					gap_y = response.gap_y;
292					GUI.draw_grid(gap_x, gap_y);
293				}
294				else {
295					GUI.grid = false;
296					GUI.draw_grid();
297				}
298			}
299		);
300	};
301
302	//histogram
303	this.image_histogram = function () {
304		this.histogram();
305	};
306
307	this.resize_custom = function (user_response) {
308		EDIT.save_state();
309		EVENTS.autosize = false;
310		if (user_response.width != WIDTH || user_response.height != HEIGHT) {
311			WIDTH = user_response.width;
312			HEIGHT = user_response.height;
313			LAYER.set_canvas_size();
314		}
315	};
316
317	//prepare rotation - increase doc dimensions if needed
318	this.rotate_resize_doc = function (angle, w, h) {
319		var o = angle * Math.PI / 180;
320		var new_x = w * Math.abs(Math.cos(o)) + h * Math.abs(Math.sin(o));
321		var new_y = w * Math.abs(Math.sin(o)) + h * Math.abs(Math.cos(o));
322		new_x = Math.ceil(Math.round(new_x * 1000) / 1000);
323		new_y = Math.ceil(Math.round(new_y * 1000) / 1000);
324
325		if (WIDTH != new_x || HEIGHT != new_y) {
326			EDIT.save_state();
327			var dx = 0;
328			var dy = 0;
329			if (new_x > WIDTH) {
330				dx = Math.ceil(new_x - WIDTH) / 2;
331				WIDTH = new_x;
332			}
333			if (new_y > HEIGHT) {
334				dy = Math.ceil(new_y - HEIGHT) / 2;
335				HEIGHT = new_y;
336			}
337			LAYER.set_canvas_size();
338
339			var tmp = canvas_active().getImageData(0, 0, WIDTH, HEIGHT);
340			canvas_active().clearRect(0, 0, WIDTH, HEIGHT);
341			canvas_active().putImageData(tmp, dx, dy);
342		}
343	};
344
345	//rotate layer
346	this.rotate_layer = function (user_response, canvas, w, h) {
347		var TO_RADIANS = Math.PI / 180;
348		angle = user_response.angle;
349		mode = user_response.mode;
350		
351		var area_x = 0;
352		var area_y = 0;
353		var area_w = w;
354		var area_h = h;
355		
356		var dx = 0;
357		var dy = 0;
358		
359		if(mode == 'Visible'){
360			//rotate only visible part
361			
362			var trim_info = this.trim_info(canvas.canvas);
363			area_x = trim_info.left;
364			area_y = trim_info.top;
365			area_w = w - trim_info.left - trim_info.right;
366			area_h = h - trim_info.top - trim_info.bottom;
367			
368			//calc how much dimensions will increase
369			var o = angle * Math.PI / 180;
370			var new_x = area_w * Math.abs(Math.cos(o)) + area_h * Math.abs(Math.sin(o));
371			var new_y = area_w * Math.abs(Math.sin(o)) + area_h * Math.abs(Math.cos(o));
372			new_x = Math.ceil(Math.round(new_x * 1000) / 1000);
373			new_y = Math.ceil(Math.round(new_y * 1000) / 1000);
374			if(new_x > area_w || new_y > area_h){
375				if (new_x > area_w) {
376					dx = Math.ceil(new_x - area_w) / 2;
377					if(area_x > dx)
378						dx = 0;
379				}
380				if (new_y > area_h) {
381					dy = Math.ceil(new_y - area_h) / 2;
382					if(area_y > dy)
383						dy = 0;
384				}
385				if (w == WIDTH && h == HEIGHT){
386					var tmp = canvas.getImageData(0, 0, w, h);
387					canvas.clearRect(0, 0, w, h);
388					canvas.putImageData(tmp, dx, dy);
389				}
390			}
391			
392			//recalc
393			var trim_info = this.trim_info(canvas.canvas);
394			area_x = trim_info.left;
395			area_y = trim_info.top;
396			area_w = w - trim_info.left - trim_info.right;
397			area_h = h - trim_info.top - trim_info.bottom;
398		}
399		
400		var tempCanvas = document.createElement("canvas");
401		var tempCtx = tempCanvas.getContext("2d");
402		tempCanvas.width = area_w;
403		tempCanvas.height = area_h;
404		var imageData = canvas.getImageData(area_x, area_y, area_w, area_h);
405		tempCtx.putImageData(imageData, 0, 0);
406
407		//rotate
408		canvas.clearRect(area_x, area_y, area_w, area_h);
409		canvas.save();
410		canvas.translate(area_x + Math.round(area_w / 2), area_y + Math.round(area_h / 2));
411		canvas.rotate(angle * TO_RADIANS);
412		canvas.drawImage(tempCanvas, -Math.round(area_w / 2), -Math.round(area_h / 2));
413		canvas.restore();
414		if (w == WIDTH && h == HEIGHT){	
415			//if main canvas
416			GUI.zoom();
417		}
418	};
419
420	this.resize_box = function () {
421		POP.add({name: "width", title: "Width (pixels):", value: '', placeholder: WIDTH});
422		POP.add({name: "height", title: "Height (pixels):", value: '', placeholder: HEIGHT});
423		POP.add({name: "width_percent", title: "Width (%):", value: '', placeholder: 100});
424		POP.add({name: "height_percent", title: "Height (%):", value: '', placeholder: 100});
425		POP.add({name: "mode", title: "Mode:", values: ["Resample - Hermite", "Basic", "HQX"]});
426		POP.add({name: "preblur", title: "Pre-Blur:", values: ["Yes", "No"], value: "No"});
427		POP.add({name: "sharpen", title: "Sharpen:", values: ["Yes", "No"], value: "No"});
428		POP.show('Resize', [IMAGE, "resize_layer"]);
429	};
430
431	this.resize_layer = function (user_response) {
432		EDIT.save_state();
433		var width = parseInt(user_response.width);
434		var height = parseInt(user_response.height);
435		var width_100 = parseInt(user_response.width_percent);
436		var height_100 = parseInt(user_response.height_percent);
437		var preblur = user_response.preblur;
438		var sharpen = user_response.sharpen;
439		if (isNaN(width) && isNaN(height) && isNaN(width_100) && isNaN(height_100))
440			return false;
441		if (width == WIDTH && height == HEIGHT)
442			return false;
443
444		//if dimension with percent provided
445		if (isNaN(width) && isNaN(height)) {
446			if (isNaN(width_100) == false) {
447				width = Math.round(WIDTH * width_100 / 100);
448			}
449			if (isNaN(height_100) == false) {
450				height = Math.round(HEIGHT * height_100 / 100);
451			}
452		}
453
454		//if only 1 dimension was provided
455		if (isNaN(width) || isNaN(height)) {
456			var ratio = WIDTH/HEIGHT;
457			if (isNaN(width))
458				width = Math.round(height * ratio);
459			if (isNaN(height))
460				height = Math.round(width / ratio);
461		}
462
463		//anti-artifacting?
464		if (preblur == 'Yes') {
465			var ratio_w = WIDTH / width;
466			var ratio_h = HEIGHT / height;
467			var ratio_avg = Math.max(ratio_w, ratio_h);
468			var power = ratio_avg * 0.3;
469			if (power > 0.6) {
470				var imageData = canvas_active().getImageData(0, 0, WIDTH, HEIGHT);
471				var filtered = ImageFilters.GaussianBlur(imageData, power);	//add effect
472				canvas_active().putImageData(filtered, 0, 0);
473			}
474		}
475		
476		//validate
477		if (user_response.mode == "Resample - Hermite" && (width > WIDTH || height > HEIGHT)){
478			//scalling up - Hermite not supported
479			user_response.mode = "Resize";
480		}
481		if (user_response.mode == "HQX" && (width < WIDTH && height < HEIGHT)){
482			//scalling down - HQX not supported
483			user_response.mode = "Resample - Hermite";
484		}
485
486		var resize_type;		
487		if (user_response.mode == "Resample - Hermite") {
488			//Hermite resample - max quality
489			resize_type = 'Hermite';
490			
491			var HERMITE = new Hermite_class();
492			HERMITE.resample_single(canvas_active(true), width, height);
493			
494			if (GUI.last_menu != 'layer_resize') {
495				WIDTH = width;
496				HEIGHT = height;
497				if (WIDTH < 1)
498					WIDTH = 1;
499				if (HEIGHT < 1)
500					HEIGHT = 1;
501				LAYER.set_canvas_size();
502			}
503			GUI.zoom();
504		}
505		else if(user_response.mode == "HQX") {
506			//HQX - 2, 3, 4 scale only, but amazing quality if there are only few colors
507			resize_type = 'HQX';
508			
509			//find correct dimensions
510			var multiply = Math.max(width/WIDTH, height/HEIGHT);
511			multiply = Math.round(multiply);
512			if(multiply < 2)
513				multiply = 2;
514			if(multiply > 4)
515				multiply = 4;
516			
517			var image_data_resized = hqx(canvas_active(true), multiply);
518			
519			canvas_active().clearRect(0, 0, WIDTH, HEIGHT);
520			WIDTH = WIDTH * multiply;
521			HEIGHT = HEIGHT * multiply;
522			LAYER.set_canvas_size();
523			canvas_active().putImageData(image_data_resized, 0, 0 );
524			GUI.zoom();
525		}
526		else {
527			//simple resize - max speed
528			resize_type = 'Default';
529			tmp_data = document.createElement("canvas");
530			tmp_data.width = WIDTH;
531			tmp_data.height = HEIGHT;
532			tmp_data.getContext("2d").drawImage(canvas_active(true), 0, 0);
533
534			canvas_active().clearRect(0, 0, WIDTH, HEIGHT);
535			WIDTH = width;
536			HEIGHT = height;
537			LAYER.set_canvas_size();
538			canvas_active().drawImage(tmp_data, 0, 0, width, height);
539			GUI.zoom();
540		}
541		
542		//sharpen after?
543		if (sharpen == 'Yes') {
544			var imageData = canvas_active().getImageData(0, 0, WIDTH, HEIGHT);
545			var filtered = ImageFilters.Sharpen(imageData, 1);	//add effect
546			canvas_active().putImageData(filtered, 0, 0);
547		}
548	};
549
550	/**
551	 * get canvas painted are coords
552	 * 
553	 * @param {HtmlElement} canvas
554	 * @param {boolean} trim_white
555	 */
556	this.trim_info = function (canvas, trim_white) {
557		var top = 0;
558		var left = 0;
559		var bottom = 0;
560		var right = 0;
561		var img = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height);
562		var imgData = img.data;
563		var empty = false;
564		if(trim_white == undefined)
565			trim_white = true;
566		//check top
567		main1:
568			for (var y = 0; y < img.height; y++) {
569			for (var x = 0; x < img.width; x++) {
570				var k = ((y * (img.width * 4)) + (x * 4));
571				if (imgData[k + 3] == 0)
572					continue; //transparent 
573				if (trim_white == true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
574					continue; //white
575				break main1;
576			}
577			top++;
578		}
579		//check left
580		main2:
581			for (var x = 0; x < img.width; x++) {
582			for (var y = 0; y < img.height; y++) {
583				var k = ((y * (img.width * 4)) + (x * 4));
584				if (imgData[k + 3] == 0)
585					continue; //transparent 
586				if (trim_white == true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
587					continue; //white
588				break main2;
589			}
590			left++;
591		}
592		//check bottom
593		main3:
594			for (var y = img.height - 1; y >= 0; y--) {
595			for (var x = img.width - 1; x >= 0; x--) {
596				var k = ((y * (img.width * 4)) + (x * 4));
597				if (imgData[k + 3] == 0)
598					continue; //transparent 
599				if (trim_white == true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
600					continue; //white
601				break main3;
602			}
603			bottom++;
604		}
605		//check right
606		main4:
607			for (var x = img.width - 1; x >= 0; x--) {
608			for (var y = img.height - 1; y >= 0; y--) {
609				var k = ((y * (img.width * 4)) + (x * 4));
610				if (imgData[k + 3] == 0)
611					continue; //transparent 
612				if (trim_white == true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
613					continue; //white
614				break main4;
615			}
616			right++;
617		}
618		
619		if(top == canvas.height && left == canvas.width){
620			//canvas is empty
621			empty = true;
622		}
623		
624		return {
625			top: top,
626			left: left,
627			bottom: bottom,
628			right: right,
629			empty: empty,
630		};
631	};
632
633	this.trim = function (layer, no_resize, removeWhiteColor) {
634		var all_top = HEIGHT;
635		var all_left = WIDTH;
636		var all_bottom = HEIGHT;
637		var all_right = WIDTH;
638		
639		if(no_resize == undefined)
640			no_resize = false;
641		if(removeWhiteColor == undefined)
642			removeWhiteColor = false;
643		
644		for (var i in LAYER.layers) {
645			if (layer != undefined && LAYER.layers[i].name != layer)
646				continue;
647
648			var top = 0;
649			var left = 0;
650			var bottom = 0;
651			var right = 0;
652			var img = document.getElementById(LAYER.layers[i].name).getContext("2d").getImageData(0, 0, WIDTH, HEIGHT);
653			var imgData = img.data;
654			//check top
655			main1:
656				for (var y = 0; y < img.height; y++) {
657				for (var x = 0; x < img.width; x++) {
658					var k = ((y * (img.width * 4)) + (x * 4));
659					if (imgData[k + 3] == 0)
660						continue; //transparent
661					if (removeWhiteColor === true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
662						continue; //white
663					break main1;
664				}
665				top++;
666			}
667			//check left
668			main2:
669				for (var x = 0; x < img.width; x++) {
670				for (var y = 0; y < img.height; y++) {
671					var k = ((y * (img.width * 4)) + (x * 4));
672					if (imgData[k + 3] == 0)
673						continue; //transparent
674					if (removeWhiteColor === true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
675						continue; //white
676					break main2;
677				}
678				left++;
679			}
680			//check bottom
681			main3:
682				for (var y = img.height - 1; y >= 0; y--) {
683				for (var x = img.width - 1; x >= 0; x--) {
684					var k = ((y * (img.width * 4)) + (x * 4));
685					if (imgData[k + 3] == 0)
686						continue; //transparent
687					if (removeWhiteColor === true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
688						continue; //white
689					break main3;
690				}
691				bottom++;
692			}
693			//check right
694			main4:
695				for (var x = img.width - 1; x >= 0; x--) {
696				for (var y = img.height - 1; y >= 0; y--) {
697					var k = ((y * (img.width * 4)) + (x * 4));
698					if (imgData[k + 3] == 0)
699						continue; //transparent
700					if (removeWhiteColor === true && imgData[k] == 255 && imgData[k + 1] == 255 && imgData[k + 2] == 255)
701						continue; //white
702					break main4;
703				}
704				right++;
705			}
706			all_top = Math.min(all_top, top);
707			all_left = Math.min(all_left, left);
708			all_bottom = Math.min(all_bottom, bottom);
709			all_right = Math.min(all_right, right);
710		}
711		//move to top-left corner
712		for (var i in LAYER.layers) {
713			if (layer != undefined && LAYER.layers[i].name != layer)
714				continue;
715
716			tmp_data = document.getElementById(LAYER.layers[i].name).getContext("2d").getImageData(0, 0, WIDTH, HEIGHT);
717			document.getElementById(LAYER.layers[i].name).getContext("2d").clearRect(0, 0, WIDTH, HEIGHT);
718			document.getElementById(LAYER.layers[i].name).getContext("2d").putImageData(tmp_data, -all_left, -all_top);
719			var canvas_name = LAYER.layers[i].name;
720		}
721		
722		if(removeWhiteColor == false && (all_left + all_right + all_top + all_bottom == 0 || (all_left + all_top == 0 && no_resize == true )) ){	
723			//also trim white color
724			this.trim(layer, no_resize, true);
725			return;
726		}
727		
728		//resize
729		if (no_resize == false){
730			if (layer != undefined) {
731				var W = Math.round(WIDTH - all_left - all_right);
732				var H = Math.round(HEIGHT - all_top - all_bottom);
733
734				var imageData = document.getElementById(layer).getContext("2d").getImageData(0, 0, W, H);
735				document.getElementById(layer).width = W;
736				document.getElementById(layer).height = H;
737				document.getElementById(layer).getContext("2d").clearRect(0, 0, W, H);
738				document.getElementById(layer).getContext("2d").putImageData(imageData, 0, 0);
739
740				return {
741					top: all_top,
742					left: all_left,
743					bottom: all_bottom,
744					right: all_right
745				};
746			}
747			else {
748				WIDTH = WIDTH - all_left - all_right;
749				HEIGHT = HEIGHT - all_top - all_bottom;
750				if (WIDTH < 1)
751					WIDTH = 1;
752				if (HEIGHT < 1)
753					HEIGHT = 1;
754				LAYER.set_canvas_size();
755				LAYER.update_info_block();
756			}
757		}
758	};
759
760	this.decrease_colors = function (canvas_source, canvas_destination, W, H, colors, greyscale) {
761		var context = canvas_destination.getContext("2d");
762		var img = context.getImageData(0, 0, W, H);
763		var imgData = img.data;
764		var palette = [];
765
766		//collect top colors
767		var block_size = 10;
768		var ctx = canvas_front; //use temp canvas
769		ctx.clearRect(0, 0, W, H);
770		ctx.drawImage(canvas_source, 0, 0, Math.ceil(canvas_source.width / block_size), Math.ceil(canvas_source.height / block_size)); //simple resize
771		var img_p = ctx.getImageData(0, 0, Math.ceil(canvas_source.width / block_size), Math.ceil(canvas_source.height / block_size));
772		var imgData_p = img_p.data;
773		ctx.clearRect(0, 0, W, H);
774
775		for (var i = 0; i < imgData_p.length; i += 4) {
776			if (imgData_p[i + 3] == 0)
777				continue;	//transparent
778			var grey = Math.round(0.2126 * imgData_p[i] + 0.7152 * imgData_p[i + 1] + 0.0722 * imgData_p[i + 2]);
779			palette.push([imgData_p[i], imgData_p[i + 1], imgData_p[i + 2], grey]);
780		}
781
782		//calculate weights
783		var grey_palette = [];
784		for (var i = 0; i < 256; i++)
785			grey_palette[i] = 0;
786		for (var i = 0; i < palette.length; i++)
787			grey_palette[palette[i][3]]++;
788
789		//remove similar colors
790		for (var max = 10 * 3; max < 100 * 3; max = max + 10 * 3) {
791			if (palette.length <= colors)
792				break;
793			for (var i = 0; i < palette.length; i++) {
794				if (palette.length <= colors)
795					break;
796				var valid = true;
797				for (var j = 0; j < palette.length; j++) {
798					if (palette.length <= colors)
799						break;
800					if (i == j)
801						continue;
802					if (Math.abs(palette[i][0] - palette[j][0]) + Math.abs(palette[i][1] - palette[j][1]) + Math.abs(palette[i][2] - palette[j][2]) < max) {
803						if (grey_palette[palette[i][3]] > grey_palette[palette[j][3]]) {
804							//remove color
805							palette.splice(j, 1);
806							j--;
807						}
808						else {
809							valid = false;
810							break;
811						}
812					}
813				}
814				//remove color
815				if (valid == false) {
816					palette.splice(i, 1);
817					i--;
818				}
819			}
820		}
821		palette = palette.slice(0, colors);
822
823		//change
824		var p_n = palette.length;
825		for (var j = 0; j < H; j++) {
826			for (var i = 0; i < W; i++) {
827				var k = ((j * (W * 4)) + (i * 4));
828				if (imgData[k + 3] == 0)
829					continue;	//transparent
830				
831				//find closest color
832				var index1 = 0;
833				var min = 999999;
834				var diff1;
835				for (var m = 0; m < p_n; m++) {
836					var diff = Math.abs(palette[m][0] - imgData[k]) + Math.abs(palette[m][1] - imgData[k + 1]) + Math.abs(palette[m][2] - imgData[k + 2]);
837					if (diff < min) {
838						min = diff;
839						index1 = m;
840						diff1 = diff;
841					}
842				}
843
844				imgData[k] = palette[index1][0];
845				imgData[k + 1] = palette[index1][1];
846				imgData[k + 2] = palette[index1][2];
847
848				if (greyscale == true) {
849					var mid = Math.round(0.2126 * imgData[k] + 0.7152 * imgData[k + 1] + 0.0722 * imgData[k + 2]);
850					imgData[k] = mid;
851					imgData[k + 1] = mid;
852					imgData[k + 2] = mid;
853				}
854			}
855		}
856		canvas_destination.getContext("2d").putImageData(img, 0, 0);
857	};
858
859	//fixing white and black color balance
860	this.auto_adjust = function (context, W, H) {
861		//settings
862		var white = 240;	//white color min
863		var black = 30;		//black color max
864		var target_white = 1; 	//how much % white colors should take
865		var target_black = 0.5;	//how much % black colors should take
866		var modify = 1.1;	//color modify strength
867		var cycles_count = 10; //how much iteration to change colors
868
869		document.body.style.cursor = "wait";
870		var img = context.getImageData(0, 0, W, H);
871		var imgData = img.data;
872		var n = 0;	//pixels count without transparent
873
874		//make sure we have white
875		var n_valid = 0;
876		for (var i = 0; i < imgData.length; i += 4) {
877			if (imgData[i + 3] == 0)
878				continue;	//transparent
879			if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 > white)
880				n_valid++;
881			n++;
882		}
883		target = target_white;
884		var n_fix_white = 0;
885		var done = false;
886		for (var j = 0; j < cycles_count; j++) {
887			if (n_valid * 100 / n >= target)
888				done = true;
889			if (done == true)
890				break;
891			n_fix_white++;
892
893			//adjust
894			for (var i = 0; i < imgData.length; i += 4) {
895				if (imgData[i + 3] == 0)
896					continue;	//transparent
897				for (var c = 0; c < 3; c++) {
898					var x = i + c;
899					if (imgData[x] < 10)
900						continue;
901					//increase white
902					imgData[x] *= modify;
903					imgData[x] = Math.round(imgData[x]);
904					if (imgData[x] > 255)
905						imgData[x] = 255;
906				}
907			}
908
909			//recheck
910			n_valid = 0;
911			for (var i = 0; i < imgData.length; i += 4) {
912				if (imgData[i + 3] == 0)
913					continue;	//transparent
914				if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 > white)
915					n_valid++;
916			}
917		}
918
919		//make sure we have black
920		n_valid = 0;
921		for (var i = 0; i < imgData.length; i += 4) {
922			if (imgData[i + 3] == 0)
923				continue;	//transparent
924			if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 < black)
925				n_valid++;
926		}
927		target = target_black;
928		var n_fix_black = 0;
929		var done = false;
930		for (var j = 0; j < cycles_count; j++) {
931			if (n_valid * 100 / n >= target)
932				done = true;
933			if (done == true)
934				break;
935			n_fix_black++;
936
937			//adjust
938			for (var i = 0; i < imgData.length; i += 4) {
939				if (imgData[i + 3] == 0)
940					continue;	//transparent
941				for (var c = 0; c < 3; c++) {
942					var x = i + c;
943					if (imgData[x] > 240)
944						continue;
945					//increase black
946					imgData[x] -= (255 - imgData[x]) * modify - (255 - imgData[x]);
947					imgData[x] = Math.round(imgData[x]);
948				}
949			}
950
951			//recheck
952			n_valid = 0;
953			for (var i = 0; i < imgData.length; i += 4) {
954				if (imgData[i + 3] == 0)
955					continue;	//transparent
956				if ((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3 < black)
957					n_valid++;
958			}
959		}
960
961		//save	
962		context.putImageData(img, 0, 0);
963		document.body.style.cursor = "auto";
964		//log('Iterations: brighten='+n_fix_white+", darken="+n_fix_black);
965	};
966
967	this.histogram = function () {
968		POP.add({name: "param1", title: "Channel:", values: ["Gray", "Red", "Green", "Blue"], onchange: "IMAGE.histogram_onload()"});
969		POP.add({title: 'Histogram:', function: function () {
970				var html = '<canvas style="position:relative;" id="c_h" width="256" height="100"></canvas>';
971				return html;
972			}});
973		POP.add({title: "Total pixels:", value: ""});
974		POP.add({title: "Average:", value: ""});
975		POP.show(
976			'Histogram',
977			undefined,
978			undefined,
979			this.histogram_onload
980		);
981	};
982	
983	this.histogram_onload = function () {
984		var img = canvas_active().getImageData(0, 0, WIDTH, HEIGHT);
985		var imgData = img.data;
986		var channel_grey = document.getElementById("pop_data_param1_poptmp0");
987		var channel_r = document.getElementById("pop_data_param1_poptmp1");
988		var channel_g = document.getElementById("pop_data_param1_poptmp2");
989		var channel_b = document.getElementById("pop_data_param1_poptmp3");
990		
991		if (channel_grey.checked == true)
992			channel = 0;
993		else if (channel_r.checked == true)
994			channel = 1;
995		else if (channel_g.checked == true)
996			channel = 2;
997		else if (channel_b.checked == true)
998			channel = 3;
999
1000		var hist_data = [ [], [], [], [] ]; //grey, red, green, blue
1001		var total = imgData.length / 4;
1002		var sum = 0;
1003		var grey;
1004
1005		for (var i = 0; i < imgData.length; i += 4) {
1006			//collect grey
1007			grey = Math.round((imgData[i] + imgData[i + 1] + imgData[i + 2]) / 3);
1008			sum = sum + imgData[i] + imgData[i + 1] + imgData[i + 2];
1009			if(hist_data[0][grey] == undefined)
1010				hist_data[0][grey] = 1;
1011			else
1012				hist_data[0][grey]++;
1013			
1014			//collect colors
1015			for(var c = 0; c < 3; c++) {
1016				if(c+1 != channel)
1017					continue;
1018				if(hist_data[c+1][imgData[i+c]] == undefined)
1019					hist_data[c+1][imgData[i+c]] = 1;
1020				else
1021					hist_data[c+1][imgData[i+c]]++;
1022			}
1023		}
1024		
1025		var c = document.getElementById("c_h").getContext("2d");
1026		c.rect(0, 0, 256, 100);
1027		c.fillStyle = "#ffffff";
1028		c.fill();
1029		var opacity = 1;
1030		
1031		//draw histogram
1032		for(var h in hist_data) {
1033			for (var i = 0; i <= 255; i++) {
1034				if(h != channel)
1035					continue;
1036				if (hist_data[h][i] == 0)
1037					continue;
1038				c.beginPath();
1039				
1040				if(h == 0)
1041					c.strokeStyle = "rgba(64, 64, 64, "+opacity*2+")";
1042				else if(h == 1)
1043					c.strokeStyle = "rgba(255, 0, 0, "+opacity+")";
1044				else if(h == 2)
1045					c.strokeStyle = "rgba(0, 255, 0, "+opacity+")";
1046				else if(h == 3)
1047					c.strokeStyle = "rgba(0, 0, 255, "+opacity+")";
1048				
1049				c.lineWidth = 1;
1050				c.moveTo(i + 0.5, 100 + 0.5);
1051				c.lineTo(i + 0.5, 100 - Math.round(hist_data[h][i] * 255 * 100 / total / 6) + 0.5);
1052				c.stroke();
1053			}
1054		}
1055		
1056		document.getElementById("pop_data_totalpixel").innerHTML = HELPER.number_format(total, 0);
1057		if (total > 0)
1058			average = Math.round(sum * 10 / total / 3) / 10;
1059		else
1060			average = '-';
1061		document.getElementById("pop_data_average").innerHTML = average;
1062	};
1063	
1064	this.unique_colors_count = function (canvas) {
1065		var img = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height);
1066		var imgData = img.data;
1067		var colors = [];
1068		var n = 0;
1069		for (var i = 0; i < imgData.length; i += 4) {
1070			if (imgData[i + 3] == 0)
1071				continue;	//transparent
1072			var key = imgData[i] + "." + imgData[i + 1] + "." + imgData[i + 2];
1073			if (colors[key] == undefined) {
1074				colors[key] = 1;
1075				n++;
1076			}
1077		}
1078		return n;
1079	};
1080	this.zoom_in = function() {
1081		GUI.zoom(+1, true);
1082	};
1083	this.zoom_out = function() {
1084		GUI.zoom(-1, true);
1085	};
1086	this.zoom_original = function() {
1087		GUI.zoom(100, true);
1088	};
1089	this.zoom_auto = function() {
1090		GUI.zoom_auto();
1091	};
1092}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.