Advanced Thumbnail Creator


eng | rus 
TL (x1, y1) = (,)
BR (x2, y2) = (,)
Width =
Height =
Thumbnail width =
Thumbnail height =



1 Задача

Работая с новостными лентами или же просто с информацией, содержащей графические изображения, довольно часто возникает потребность генерации картинок-превью. На первый взгляд нет ничего проще чем создать уменьшенное изображение произвольного рисунка или фотографии. Однако оказывается и в этой области есть где развернуться.

2 Существующие решения

Есть несколько способов решения данной задачи:

  1. 1. Использование оригинального изображения в качестве превью путем его уменьшения свойствами тега <img>.
  2. 2. Уменьшение размеров изображения при помощи функций imagecopyresampled(), imagecopyresized() библиотеки GD.
  3. 3. Выделение центральной области изображения и ее последующее преобразование функциями библиотеки GD.
  4. 4. Создание превью при помощи графических приложений на локальной машине и последующая загрузка на сервер.

Очевидное преимущество первых трех методов - полная автоматизация процесса. Недостатки: в первом случае изображение загружается в оригинальном размере (рис. 2а), во втором - в превью не сохраняются оригинальные пропорции изображения (рис. 2б), в третьем - центральная часть изображения не всегда является "значащей" (рис. 2в).

Четвертый способ генерации превью дает необходимые результаты (рис. 2г), но добавляет лишние хлопоты при переключении между приложениями, возникает необходимость загрузки двух файлов вместо одного (в лучшем случае).

Figure 1 - Initial image

Рисунок 1 - Исходное изображение

Рисунок 2а - Результаты генерации превью а)
Рисунок 2б - Результаты генерации превью б)
Рисунок 2в - Результаты генерации превью в)
Рисунок 2г - Результаты генерации превью г)

Рисунок 2 - Результаты генерации превью (70x70)

3 Решение

Суть метода - автоматическое создание превью с помощью php-скрипта посредством ручного выделения "значащей" области.

4 Используемые технологии

PHP, Javascript, DOM, CSS, HTML

5 Кросс-браузерность

Internet Explorer 6.0,7.0; FireFox 1.0,2.0; Opera 9.0; Safari 2.0;

6 Входные и выходные параметры

Входные параметры:

  1. $_GET[filename] - строка, определяющая имя файла и путь к нему относительно корневого каталога на сервере. Переменная $_GET[filename] должна быть предварительно закодирована функцией urlencode();
  2. $_GET[thumbwidth] - предустановленная ширина превью изображения. Если этот параметр равен "0" или не определен, ширина превью определяется в зависимости от выбранной области.
  3. $_GET[thumbheight] - предустановленная высота превью изображения. Если этот параметр равен "0" или не определен, высота превью определяется в зависимости от выбранной области.

Выходные параметры:

  1. Превью изображения на странице браузера.
  2. Файл-превью типа JPEG, находящийся в том же каталоге что и исходное изображение.

7 Реализация

Разработанный программный комплекс состоит из двух файлов.

  1. thumbcreate.js - набор функций для работы с выделяемой областью и функции подготовки генерации изображения.
  2. thumbcreate.php - содержит функцию генерации превью, блок отображения рабочей области и форму ввода-вывода параметров превью.

7.1. Описание функций модуля thumbcreate.js.

//initialization
function init()
{
	document.getElementById("image").onclick=mouseHandler;
	document.getElementById("image").onmousemove=mouseHandler;
	document.getElementById('th_w').value=thumbWidth;
	document.getElementById('th_h').value=thumbHeight;
}

//mouse handler
function mouseHandler(mouseEvent)
{
	if (!mouseEvent) mouseEvent = window.event;
	if (mouseEvent.button == 2) return;
	var element = (mouseEvent.target)?mouseEvent.target:mouseEvent.srcElement;

//for a clique we begin to draw a selection rectangle
	if (mouseEvent.type=="click")
	{
	var x = mouseEvent.clientX - document.getElementById("image").offsetLeft;
	var at = mouseEvent.clientY - document.getElementById("image").offsetTop;
	pointSet(x,y);
	rectangleDraw('area');
	};

//draw the selection region during mouse motion
	if (mouseEvent.type=="mousemove")
	{
	.
	}
}

//setting coordinates for top left and right bottom corner
function pointSet(x,y)
{
	if (!ptype)
	{
	x1=x+document.body.scrollLeft;
	y1=y+document.body.scrollTop;
	rectangleHide('area');
	inputUpdate();
	}
	else
	{
	x2=x+document.body.scrollLeft;
	y2=y+document.body.scrollTop;
	pointCorrect();
	inputUpdate();
	}
	ptype = !ptype;
}

//correcting TL BR coordinates if they are switched
function pointCorrect(x1c,y1c,x2c,y2c)
{
	.
}

//rectangle drawing (x1,y1); (x2,y2)
function rectangleDraw(rectId)
{
	.
}

// rectangle drawing from input fields
function rectangleDrawInput(rectId)
{
.
}

//rectangle hiding
function rectangleHide(rectId)
{
	.
}

//input fields update functions
function inputUpdate()
{
.
}

function inputWidthUpdate()
{
	.
};

function inputHeightUpdate()
{
	.
};

function inputXYUpdate()
{
	.
};

//prepare for thumbnail generation
function generateImageThumb()
{
	var previewclass='preview';
	var previewimage='/preview.gif';	

	var links=document.getElementsByTagName('a');
	var prevlinks=new Array();
	var c=0;
	
	var previewTest = new RegExp("(^|\s)" + previewclass + "(\s|$)");

	for(I=0; I<links.length; I++)
	{
	if (previewTest.test(links[I].className))
	 prevlinks[c]=links[I]; c++; }
	}

	for(I=0; I<prevlinks.length; I++)
	{

	var newa=document.createElement('a');
	newa.style.textDecoration="none";

	var newbutton=document.createElement('input');
	newbutton.type="button";
	newbutton.value="Generate Thumbnail"

	newa.appendChild(newbutton);
	newa.href="#";

	var newbr=document.createElement('br');
	newa.appendChild(newbr);

	newa.onclick=function()
		{
	if(this.getElementsByTagName('img')[0])
	this.removeChild(this.getElementsByTagName('img')[0]);

	var newimg=document.createElement('img');
	newimg.style.border="0";
	newimg.vspace="10";
	this.appendChild(newimg);

	var rand=parseInt(1000*Math.random());
	newimg.src="?action=generate&r="+rand+"&x1="+document.getElementById('x1_inp').value+
	"&y1="+document.getElementById('y1_inp').value+"&x2="+document.getElementById('x2_inp').value+
	"&y2="+document.getElementById('y2_inp').value+"&w="+document.getElementById('th_w').value+
	"&h="+document.getElementById('th_h').value+"&fn="+document.getElementById('fileName').value;

	return false;
		}

	prevlinks[I].parentNode.insertBefore(newa,prevlinks[I].nextSibling);
	}

}	

//initialization
window.onload=function()
{
	init();
	generateImageThumb();
}

7.2. Описание функций модуля thumbcreate.php.

<?php
	if (!$_GET[thumbwidth]) $_GET[thumbwidth]=0;
	if (!$_GET[thumbheight]) $_GET[thumbheight]=0;

//thumbnail creation function
	function createthumb($new_w,$new_h,$x1,$y1,$x2,$y2,$fn)
	{
	$src_img=imagecreatefromjpeg($_GET['fn']);
	$dst_img=ImageCreateTrueColor($new_w,$new_h);
	imagecopyresampled($dst_img,$src_img,0,0,$x1,$y1,$new_w,$new_h,$x2-$x1,$y2-$y1); 

//output to browser
	imagejpeg($dst_img);
//output to file
	imagejpeg($dst_img,substr($_GET['fn'],0,-4)."_th.jpg"); 

	imagedestroy($dst_img); 
	imagedestroy($src_img); 
	}
	if ($_GET[action]=="generate")
	{
	header('Content-type:image/jpeg');
	createthumb($_GET['w'],$_GET['h'],$_GET['x1'],$_GET['y1'],$_GET['x2'],$_GET['y2'],$_GET['fn']);
	}
?>

//working area
<div id="image" style="cursor: crosshair; width:<?= $imgWidth ?>px; 
height:<?= $imgHeight ?>px; border-width:0; background-image: url('<?= $_GET[filename] ?>') ">
	<div id="area"></div>
</div>

//input-output form
<form action="" method="post">
<table width="<?=$imgWidth?>px">
	<tr>
	<td width="33%" align=center>
	TL (x1, y1) = (<input type="text" id="x1_inp" value="0" class="atc1" onChange="inputXYUpdate();">,
	<input type="text" id="y1_inp" value="0" class="atc1" onChange="inputXYUpdate();">)<br>
	BR (x2, y2) = (<input type="text" id="x2_inp" value="0" class="atc1" onChange="inputXYUpdate();">,
	<input type="text" id="y2_inp" value="0" class="atc1" onChange="inputXYUpdate();">)<br></td>
	<td width="33%" align=center>
	Width = 
	<input type="text" id="th_width" value="0" class="atc2" onChange="inputWidthUpdate();"><br>
	Height = 
	<input type="text" id="th_height" value="0" class="atc2" onChange="inputHeightUpdate();"><br></td>
	<td width="33%" align=center>
	Thumbnail width = <input type="text" id="th_w" value="" class="atc2"><br>
	Thumbnail height = <input type="text" id="th_h" value="" class="atc2"><br></td></tr>
	<tr><td colspan=3 align=center><BR><a href="/none.gif" class="preview"></a></td></tr>
</table><input type="hidden" value="<?= $fileInput ?>" id="fileName"></form>

8 Ссылки

  1. Image previews with DOM JavaScript - http://icant.co.uk/articles/imagepreview/
  2. Сложное масштабирование изображений в PHP - http://www.codenet.ru/webmast/php/Image-Resize-GD/

9 Исходный код

Advanced Thumbnail Creator: atc.zip
Библиотека thumbcreate.js: thumbcreate.js
Модуль thumbcreate.php: thumbcreate.php
Описание в формате PDF: atc.pdf

© Жупаненко Андрей
research@zhupanenko.com
http://research.zhupanenko.com/atc/

февраль, 2007


Комментарии

aj

05 / 03 / 2007

ну вот задал я значит параметры:
?thumbwidth=100&thumbheight=100

а область выделил кривую. 300x100, к примеру. тамбнейл получился искаженный. нехорошо.

вот я и говорю, что если эти параметры заданы - стоит рамку отрисовывать используя такое же соотношение сторон, как у thumbwidth/thumbheight.

кроме того: рамка по двум кликам - неудобно. надо по одному, типа как в фотошопе. так сейчас модно. плюс - возможность перетаскивать рамку.

еще неплохо было б возможность управлять качеством компрессии и фичу чтоб писать файлы на диск)) и возможность массовой обработки.

ну и функции в пхп - бее))
ооп рулит..

так что - сыровато, имхо:)

Андрей Жупаненко

05 / 03 / 2007

ну как по мне "бее" это создавать класс ради одной функции. задача массовой обработки не ставилась. при помощи данного инструмента думаю реализовать будет не сложно

Коля

21 / 03 / 2007

апдейтов парочку бы сделать и будет отличный инструмент.

Di$el

24 / 05 / 2007

долго искал такой скрипт. думал уже сам писать, но видимо не прийдется:) спасибо.

Имя:
E-mail:
Текст: