You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
16 KiB

7 years ago
  1. <?php
  2. function thumbnail($filename = '', $thumb_width = 0, $thumb_height = 0, $is_create = false, $is_crop = true, $crop_mode = 'center', $is_sharpen = false, $um_value = '80/0.5/3', $create_animate_thumb = false)
  3. {
  4. $source_file = FCPATH . $filename;
  5. // 원본 파일이 없다면
  6. if (is_file($source_file) === false) return '';
  7. // 썸네일 크기가 지정안되있따면
  8. if (empty($thumb_width) && empty($thumb_height)) return base_url($filename);
  9. // gif, jpg, png만 적용한다.
  10. $size = @getimagesize($source_file);
  11. if ($size[2] < 1 OR $size[2] > 3) return base_url($filename);
  12. // Cache 디렉토리 생성
  13. $uploadDir = DIR_UPLOAD . '/' . 'cache' . '/';
  14. make_dir($uploadDir, FALSE);
  15. $exp = explode('/', $filename);
  16. $filepos = count($exp) - 1;
  17. for ($k = 0; $k < $filepos; $k++) {
  18. if($exp[$k] == DIR_UPLOAD) continue;
  19. $uploadDir .= $exp[$k] . '/';
  20. if (is_dir($uploadDir) === false) {
  21. @mkdir($uploadDir, 0755);
  22. @chmod($uploadDir, 0755);
  23. $file = $uploadDir . 'index.php';
  24. $f = @fopen($file, 'w');
  25. @fwrite($f, '');
  26. @fclose($f);
  27. @chmod($file, 0644);
  28. }
  29. }
  30. $realfilename = $exp[$filepos];
  31. $target_path = $uploadDir;
  32. // 디렉토리가 존재하지 않거나 쓰기 권한이 없으면 썸네일 생성하지 않음
  33. if ( ! (is_dir(FCPATH . $target_path) && is_writable(FCPATH . $target_path))) return base_url( $filename );
  34. // Animated GIF는 썸네일 생성하지 않음
  35. if ($size[2] === 1) {
  36. if (is_animated_gif ($source_file) && $create_animate_thumb === false) {
  37. return base_url($filename);
  38. }
  39. }
  40. $ext = array(1 => 'gif', 2 => 'jpg', 3 => 'png');
  41. $thumb_filename = preg_replace("/\.[^\.]+$/i", '', $realfilename); // 확장자제거
  42. $thumb_file = $target_path . 'thumb-' . $thumb_filename . '_' . $thumb_width . 'x' . $thumb_height . '.' . $ext[$size[2]];
  43. $thumb_time = @filemtime(FCPATH . $thumb_file);
  44. $source_time = @filemtime( $source_file);
  45. if (file_exists(FCPATH . $thumb_file)) {
  46. if ($is_create === false && $source_time < $thumb_time) {
  47. return base_url($thumb_file);
  48. }
  49. }
  50. // 원본파일의 GD 이미지 생성
  51. $src = null;
  52. $degree = 0;
  53. if ($size[2] === 1) {
  54. $src = imagecreatefromgif ($source_file);
  55. $src_transparency = imagecolortransparent($src);
  56. } elseif ($size[2] === 2) {
  57. $src = imagecreatefromjpeg($source_file);
  58. if (function_exists('exif_read_data')) {
  59. // exif 정보를 기준으로 회전각도 구함
  60. $exif = @exif_read_data($source_file);
  61. if ( ! empty($exif['Orientation'])) {
  62. switch ($exif['Orientation']) {
  63. case 8:
  64. $degree = 90;
  65. break;
  66. case 3:
  67. $degree = 180;
  68. break;
  69. case 6:
  70. $degree = -90;
  71. break;
  72. }
  73. // 회전각도 있으면 이미지 회전
  74. if ($degree) {
  75. $src = imagerotate($src, $degree, 0);
  76. // 세로사진의 경우 가로, 세로 값 바꿈
  77. if ($degree === 90 || $degree === -90) {
  78. $tmp = $size;
  79. $size[0] = $tmp[1];
  80. $size[1] = $tmp[0];
  81. }
  82. }
  83. }
  84. }
  85. } elseif ($size[2] === 3) {
  86. $src = imagecreatefrompng($source_file);
  87. imagealphablending($src, true);
  88. } else {
  89. return;
  90. }
  91. if (empty($src)) {
  92. return;
  93. }
  94. $is_large = true;
  95. $keep_origin = false;
  96. // width, height 설정
  97. if ($thumb_width) {
  98. if (empty($thumb_height)) {
  99. $thumb_height = round(($thumb_width * $size[1]) / $size[0]);
  100. if ($thumb_width > $size[0]) {
  101. $keep_origin = true;
  102. }
  103. } else {
  104. if ($size[0] < $thumb_width || $size[1] < $thumb_height) {
  105. $is_large = false;
  106. }
  107. }
  108. } else {
  109. if ($thumb_height) {
  110. $thumb_width = round(($thumb_height * $size[0]) / $size[1]);
  111. }
  112. }
  113. $dst_x = 0;
  114. $dst_y = 0;
  115. $src_x = 0;
  116. $src_y = 0;
  117. $src_w = $size[0];
  118. $src_h = $size[1];
  119. $dst_w = $keep_origin ? $src_w : $thumb_width;
  120. $dst_h = $keep_origin ? $src_h : $thumb_height;
  121. $ratio = $dst_h / $dst_w;
  122. if ($is_large) {
  123. // 크롭처리
  124. if ($is_crop) {
  125. switch ($crop_mode) {
  126. case 'center':
  127. if ($size[1] / $size[0] >= $ratio) {
  128. $src_h = round($src_w * $ratio);
  129. $src_y = round(($size[1] - $src_h) / 2);
  130. } else {
  131. $src_w = round($size[1] / $ratio);
  132. $src_x = round(($size[0] - $src_w) / 2);
  133. }
  134. break;
  135. default:
  136. if ($size[1] / $size[0] >= $ratio) {
  137. $src_h = round($src_w * $ratio);
  138. } else {
  139. $src_w = round($size[1] / $ratio);
  140. }
  141. break;
  142. }
  143. }
  144. $dst = imagecreatetruecolor($dst_w, $dst_h);
  145. if ($size[2] === 3) {
  146. imagealphablending($dst, false);
  147. imagesavealpha($dst, true);
  148. } elseif ($size[2] === 1) {
  149. $palletsize = imagecolorstotal($src);
  150. if ($src_transparency >= 0 && $src_transparency < $palletsize) {
  151. $transparent_color = imagecolorsforindex($src, $src_transparency);
  152. $current_transparent = imagecolorallocate($dst, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
  153. imagefill($dst, 0, 0, $current_transparent);
  154. imagecolortransparent($dst, $current_transparent);
  155. }
  156. }
  157. } else {
  158. $dst = imagecreatetruecolor($dst_w, $dst_h);
  159. $bgcolor = imagecolorallocate($dst, 255, 255, 255); // 배경색
  160. if ($src_w < $dst_w) {
  161. if ($src_h >= $dst_h) {
  162. $dst_x = round(($dst_w - $src_w) / 2);
  163. $src_h = $dst_h;
  164. } else {
  165. $dst_x = round(($dst_w - $src_w) / 2);
  166. $dst_y = round(($dst_h - $src_h) / 2);
  167. $dst_w = $src_w;
  168. $dst_h = $src_h;
  169. }
  170. } else {
  171. if ($src_h < $dst_h) {
  172. $dst_y = round(($dst_h - $src_h) / 2);
  173. $dst_h = $src_h;
  174. $src_w = $dst_w;
  175. }
  176. }
  177. if ($size[2] === 3) {
  178. $bgcolor = imagecolorallocatealpha($dst, 0, 0, 0, 127);
  179. imagefill($dst, 0, 0, $bgcolor);
  180. imagealphablending($dst, false);
  181. imagesavealpha($dst, true);
  182. } elseif ($size[2] === 1) {
  183. $palletsize = imagecolorstotal($src);
  184. if ($src_transparency >= 0 && $src_transparency < $palletsize) {
  185. $transparent_color = imagecolorsforindex($src, $src_transparency);
  186. $current_transparent = imagecolorallocate($dst, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
  187. imagefill($dst, 0, 0, $current_transparent);
  188. imagecolortransparent($dst, $current_transparent);
  189. } else {
  190. imagefill($dst, 0, 0, $bgcolor);
  191. }
  192. } else {
  193. imagefill($dst, 0, 0, $bgcolor);
  194. }
  195. }
  196. imagecopyresampled($dst, $src, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
  197. // sharpen 적용
  198. if ($is_sharpen && $is_large) {
  199. $val = explode('/', $um_value);
  200. UnsharpMask($dst, $val[0], $val[1], $val[2]);
  201. }
  202. if ($size[2] === 1) {
  203. imagegif ($dst, $thumb_file);
  204. } elseif ($size[2] === 3) {
  205. $png_compress = 5;
  206. imagepng($dst, $thumb_file, $png_compress);
  207. } else {
  208. $jpg_quality = 90;
  209. imagejpeg($dst, $thumb_file, $jpg_quality);
  210. }
  211. chmod($thumb_file, 0644); // 추후 삭제를 위하여 파일모드 변경
  212. imagedestroy($src);
  213. imagedestroy($dst);
  214. return base_url($thumb_file);
  215. }
  216. function UnsharpMask($img, $amount, $radius, $threshold)
  217. {
  218. /*
  219. New:
  220. - In version 2.1 (February 26 2007) Tom Bishop has done some important speed enhancements.
  221. - From version 2 (July 17 2006) the script uses the imageconvolution function in PHP
  222. version >= 5.1, which improves the performance considerably.
  223. Unsharp masking is a traditional darkroom technique that has proven very suitable for
  224. digital imaging. The principle of unsharp masking is to create a blurred copy of the image
  225. and compare it to the underlying original. The difference in colour values
  226. between the two images is greatest for the pixels near sharp edges. When this
  227. difference is subtracted from the original image, the edges will be
  228. accentuated.
  229. The Amount parameter simply says how much of the effect you want. 100 is 'normal'.
  230. Radius is the radius of the blurring circle of the mask. 'Threshold' is the least
  231. difference in colour values that is allowed between the original and the mask. In practice
  232. this means that low-contrast areas of the picture are left unrendered whereas edges
  233. are treated normally. This is good for pictures of e.g. skin or blue skies.
  234. Any suggenstions for improvement of the algorithm, expecially regarding the speed
  235. and the roundoff errors in the Gaussian blur process, are welcome.
  236. */
  237. ////////////////////////////////////////////////////////////////////////////////////////////////
  238. ////
  239. //// Unsharp Mask for PHP - version 2.1.1
  240. ////
  241. //// Unsharp mask algorithm by Torstein Hønsi 2003-07.
  242. //// thoensi_at_netcom_dot_no.
  243. //// Please leave this notice.
  244. ////
  245. ///////////////////////////////////////////////////////////////////////////////////////////////
  246. // $img is an image that is already created within php using
  247. // imgcreatetruecolor. No url! $img must be a truecolor image.
  248. // Attempt to calibrate the parameters to Photoshop:
  249. if ($amount > 500) {
  250. $amount = 500;
  251. }
  252. $amount = $amount * 0.016;
  253. if ($radius > 50) {
  254. $radius = 50;
  255. }
  256. $radius = $radius * 2;
  257. if ($threshold > 255) {
  258. $threshold = 255;
  259. }
  260. $radius = abs(round($radius)); // Only integers make sense.
  261. if ($radius === 0) {
  262. return $img; imagedestroy($img);
  263. }
  264. $w = imagesx($img); $h = imagesy($img);
  265. $imgCanvas = imagecreatetruecolor($w, $h);
  266. $imgBlur = imagecreatetruecolor($w, $h);
  267. // Gaussian blur matrix:
  268. //
  269. // 1 2 1
  270. // 2 4 2
  271. // 1 2 1
  272. //
  273. //////////////////////////////////////////////////
  274. if (function_exists('imageconvolution')) { // PHP >= 5.1
  275. $matrix = array(
  276. array( 1, 2, 1 ),
  277. array( 2, 4, 2 ),
  278. array( 1, 2, 1 ),
  279. );
  280. $divisor = array_sum(array_map('array_sum', $matrix));
  281. $offset = 0;
  282. imagecopy ($imgBlur, $img, 0, 0, 0, 0, $w, $h);
  283. imageconvolution($imgBlur, $matrix, $divisor, $offset);
  284. } else {
  285. // Move copies of the image around one pixel at the time and merge them with weight
  286. // according to the matrix. The same matrix is simply repeated for higher radii.
  287. for ($i = 0; $i < $radius; $i++) {
  288. imagecopy ($imgBlur, $img, 0, 0, 1, 0, $w - 1, $h); // left
  289. imagecopymerge ($imgBlur, $img, 1, 0, 0, 0, $w, $h, 50); // right
  290. imagecopymerge ($imgBlur, $img, 0, 0, 0, 0, $w, $h, 50); // center
  291. imagecopy ($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h);
  292. imagecopymerge ($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 33.33333 ); // up
  293. imagecopymerge ($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 25); // down
  294. }
  295. }
  296. if ($threshold> 0) {
  297. // Calculate the difference between the blurred pixels and the original
  298. // and set the pixels
  299. for ($x = 0; $x < $w-1; $x++) { // each row
  300. for ($y = 0; $y < $h; $y++) { // each pixel
  301. $rgbOrig = ImageColorAt($img, $x, $y);
  302. $rOrig = (($rgbOrig >> 16) & 0xFF);
  303. $gOrig = (($rgbOrig >> 8) & 0xFF);
  304. $bOrig = ($rgbOrig & 0xFF);
  305. $rgbBlur = ImageColorAt($imgBlur, $x, $y);
  306. $rBlur = (($rgbBlur >> 16) & 0xFF);
  307. $gBlur = (($rgbBlur >> 8) & 0xFF);
  308. $bBlur = ($rgbBlur & 0xFF);
  309. // When the masked pixels differ less from the original
  310. // than the threshold specifies, they are set to their original value.
  311. $rNew = (abs($rOrig - $rBlur) >= $threshold)
  312. ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig))
  313. : $rOrig;
  314. $gNew = (abs($gOrig - $gBlur) >= $threshold)
  315. ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig))
  316. : $gOrig;
  317. $bNew = (abs($bOrig - $bBlur) >= $threshold)
  318. ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig))
  319. : $bOrig;
  320. if (($rOrig !== $rNew) || ($gOrig !== $gNew) || ($bOrig !== $bNew)) {
  321. $pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
  322. ImageSetPixel($img, $x, $y, $pixCol);
  323. }
  324. }
  325. }
  326. } else {
  327. for ($x = 0; $x < $w; $x++) { // each row
  328. for ($y = 0; $y < $h; $y++) { // each pixel
  329. $rgbOrig = ImageColorAt($img, $x, $y);
  330. $rOrig = (($rgbOrig >> 16) & 0xFF);
  331. $gOrig = (($rgbOrig >> 8) & 0xFF);
  332. $bOrig = ($rgbOrig & 0xFF);
  333. $rgbBlur = ImageColorAt($imgBlur, $x, $y);
  334. $rBlur = (($rgbBlur >> 16) & 0xFF);
  335. $gBlur = (($rgbBlur >> 8) & 0xFF);
  336. $bBlur = ($rgbBlur & 0xFF);
  337. $rNew = ($amount * ($rOrig - $rBlur)) + $rOrig;
  338. if ($rNew > 255) {
  339. $rNew= 255;
  340. } elseif ($rNew < 0) {
  341. $rNew= 0;
  342. }
  343. $gNew = ($amount * ($gOrig - $gBlur)) + $gOrig;
  344. if ($gNew > 255) {
  345. $gNew= 255;
  346. } elseif ($gNew < 0) {
  347. $gNew= 0;
  348. }
  349. $bNew = ($amount * ($bOrig - $bBlur)) + $bOrig;
  350. if ($bNew>255) {
  351. $bNew= 255;
  352. } elseif ($bNew < 0) {
  353. $bNew= 0;
  354. }
  355. $rgbNew = ($rNew << 16) + ($gNew <<8) + $bNew;
  356. ImageSetPixel($img, $x, $y, $rgbNew);
  357. }
  358. }
  359. }
  360. imagedestroy($imgCanvas);
  361. imagedestroy($imgBlur);
  362. return true;
  363. }
  364. function is_animated_gif ($filename)
  365. {
  366. if ( ! ($fh = @fopen($filename, 'rb'))) {
  367. return false;
  368. }
  369. $count = 0;
  370. // an animated gif contains multiple "frames", with each frame having a
  371. // header made up of:
  372. // * a static 4-byte sequence (\x00\x21\xF9\x04)
  373. // * 4 variable bytes
  374. // * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?)
  375. // We read through the file til we reach the end of the file, or we've found
  376. // at least 2 frame headers
  377. while ( ! feof($fh) && $count < 2) {
  378. $chunk = fread($fh, 1024 * 100); //read 100kb at a time
  379. $count += preg_match_all(
  380. '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s',
  381. $chunk,
  382. $matches
  383. );
  384. }
  385. fclose($fh);
  386. return $count > 1;
  387. }