Textpattern | PHP Cross Reference | Content Management Systems |
Description: Tools for creating thumbnails.
1 <?php 2 3 /** 4 * Tools for creating thumbnails. 5 * 6 * @package Image 7 * @author C. Erdmann 8 * @link http://www.cerdmann.de/thumb 9 * @author Robert Wetzlmayr 10 * 11 * Refactored from function.thumb.php by C. Erdmann, which contained 12 * the following credit and licensing terms: 13 * 14 * Smarty plugin "Thumb" 15 * Purpose: creates cached thumbnails 16 * Home: http://www.cerdmann.com/thumb/ 17 * Copyright (C) 2005 Christoph Erdmann 18 * 19 * This library is free software; you can redistribute it and/or 20 * modify it under the terms of the GNU Lesser General Public License 21 * as published by the Free Software Foundation; either version 2.1 of 22 * the License, or (at your option) any later version. 23 * 24 * This library is distributed in the hope that it will be useful, but WITHOUT 25 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 26 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 27 * details. 28 * 29 * You should have received a copy of the GNU Lesser General Public License 30 * along with this library; if not, write to the Free Software Foundation, 31 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 32 * 33 * @author Christoph Erdmann (CE) <smarty@cerdmann.com> 34 * @link http://www.cerdmann.com 35 * 36 * @author Benjamin Fleckenstein (BF) 37 * @link http://www.benjaminfleckenstein.de 38 * 39 * @author Marcus Gueldenmeister (MG) 40 * @link http://www.gueldenmeister.de/marcus/ 41 * 42 * @author Andreas Bösch (AB) 43 */ 44 45 /** 46 * Output debug log. 47 * 48 * @global bool $verbose 49 */ 50 51 $verbose = false; 52 53 /** 54 * Creates thumbnails for larger images. 55 * 56 * @package Image 57 */ 58 59 class wet_thumb 60 { 61 /** 62 * The width of your thumbnail. The height (if not set) will be 63 * automatically calculated. 64 * 65 * @var int 66 */ 67 68 public $width; 69 70 /** 71 * The height of your thumbnail. The width (if not set) will be 72 * automatically calculated. 73 * 74 * @var int 75 */ 76 77 public $height; 78 79 /** 80 * Set the longest side of the image if width, height and shortside is 81 * not set. 82 */ 83 84 public $longside; 85 86 /** 87 * Set the shortest side of the image if width, height and longside is 88 * not set. 89 */ 90 91 public $shortside; 92 93 /** 94 * Set to 'false' if your source image is smaller than the calculated 95 * thumb and you do not want the image to get extrapolated. 96 */ 97 98 public $extrapolate; 99 100 /** 101 * Crops the image. 102 * 103 * If set to TRUE, image will be cropped in the center to destination width 104 * and height params, while keeping aspect ratio. Otherwise the image will 105 * get resized. 106 * 107 * @var bool 108 */ 109 110 public $crop; 111 112 /** 113 * Applies unsharpen mask. 114 * 115 * Set to FALSE if you don't want to use the Unsharp-Mask. 116 * Thumbnail creation will be faster, but quality is reduced. 117 * 118 * @var bool 119 */ 120 121 public $sharpen; 122 123 /** 124 * If set to FALSE the image will not have a lens icon. 125 * 126 * @var bool 127 */ 128 129 public $hint; 130 131 /** 132 * Set to FALSE to get no lightgrey bottom bar. 133 * 134 * @var bool 135 */ 136 137 public $addgreytohint; 138 139 /** 140 * JPEG image quality (0...100, defaults to 80). 141 * 142 * @var int 143 */ 144 145 public $quality; 146 147 /** 148 * Set to your target URL (a href="linkurl"). 149 * 150 * @var string 151 */ 152 153 public $linkurl; 154 155 /** 156 * Will be inserted in the image-tag. 157 * 158 * @var string 159 */ 160 161 public $html; 162 163 /** 164 * An array of accepted image formats. 165 * 166 * @var array 167 */ 168 169 public $types = array('', '.gif', '.jpg', '.png'); 170 171 /** 172 * Source. 173 * 174 * @var array 175 */ 176 177 public $_SRC; 178 179 /** 180 * Destination. 181 * 182 * @var array 183 */ 184 185 public $_DST; 186 187 /** 188 * Constructor. 189 */ 190 191 public function __construct() 192 { 193 $this->extrapolate = false; 194 $this->crop = true; 195 $this->sharpen = true; 196 $this->hint = true; 197 $this->addgreytohint = true; 198 $this->quality = 80; 199 $this->html = ' alt="" title="" '; 200 $this->link = true; 201 } 202 203 /** 204 * Writes a thumbnail file. 205 * 206 * @param string $infile Image file name. 207 * @param array $outfile Array of thumb file names (1...n) 208 * @return bool TRUE on success 209 */ 210 211 public function write($infile, $outfile) 212 { 213 global $verbose; 214 215 if ($verbose) { 216 echo "writing thumb nail..."; 217 } 218 219 // Get source image info. 220 $temp = getimagesize($infile); 221 222 $this->_SRC['file'] = $infile; 223 $this->_SRC['width'] = $temp[0]; 224 $this->_SRC['height'] = $temp[1]; 225 $this->_SRC['type'] = $temp[2]; // 1=GIF, 2=JPG, 3=PNG, SWF=4. 226 $this->_SRC['string'] = $temp[3]; 227 $this->_SRC['filename'] = basename($infile); 228 //$this->_SRC['modified'] = filemtime($infile); 229 230 // Check image orientation. 231 if ($this->_SRC['width'] >= $this->_SRC['height']) { 232 $this->_SRC['format'] = 'landscape'; 233 } else { 234 $this->_SRC['format'] = 'portrait'; 235 } 236 237 // Get destination image info. 238 if (is_numeric($this->width) and empty($this->height)) { 239 $this->_DST['width'] = $this->width; 240 $this->_DST['height'] = round($this->width/($this->_SRC['width']/$this->_SRC['height'])); 241 } elseif (is_numeric($this->height) and empty($this->width)) { 242 $this->_DST['height'] = $this->height; 243 $this->_DST['width'] = round($this->height/($this->_SRC['height']/$this->_SRC['width'])); 244 } elseif (is_numeric($this->width) and is_numeric($this->height)) { 245 $this->_DST['width'] = $this->width; 246 $this->_DST['height'] = $this->height; 247 } elseif (is_numeric($this->longside) and empty($this->shortside)) { 248 // Preserve aspect ratio based on provided height. 249 if ($this->_SRC['format'] == 'portrait') { 250 $this->_DST['height'] = $this->longside; 251 $this->_DST['width'] = round($this->longside/($this->_SRC['height']/$this->_SRC['width'])); 252 } else { 253 $this->_DST['width'] = $this->longside; 254 $this->_DST['height'] = round($this->longside/($this->_SRC['width']/$this->_SRC['height'])); 255 } 256 } elseif (is_numeric($this->shortside)) { 257 // Preserve aspect ratio based on provided width. 258 if ($this->_SRC['format'] == 'portrait') { 259 $this->_DST['width'] = $this->shortside; 260 $this->_DST['height'] = round($this->shortside/($this->_SRC['width']/$this->_SRC['height'])); 261 } else { 262 $this->_DST['height'] = $this->shortside; 263 $this->_DST['width'] = round($this->shortside/($this->_SRC['height']/$this->_SRC['width'])); 264 } 265 } else { 266 // Default dimensions. 267 $this->width = 100; 268 $this->_DST['width'] = $this->width; 269 $this->_DST['height'] = round($this->width/($this->_SRC['width']/$this->_SRC['height'])); 270 } 271 272 // Don't make the new image larger than the original image. 273 if ( 274 $this->extrapolate === false && 275 $this->_DST['height'] > $this->_SRC['height'] && 276 $this->_DST['width'] > $this->_SRC['width'] 277 ) { 278 $this->_DST['width'] = $this->_SRC['width']; 279 $this->_DST['height'] = $this->_SRC['height']; 280 } 281 282 $this->_DST['type'] = $this->_SRC['type']; 283 $this->_DST['file'] = $outfile; 284 285 // Make sure we have enough memory if the image is large. 286 if (max($this->_SRC['width'], $this->_SRC['height']) > 1024) { 287 $shorthand = array('K', 'M', 'G'); 288 $tens = array('000', '000000', '000000000'); // A good enough decimal approximation of K, M, and G. 289 290 // Do not *decrease* memory_limit. 291 list($ml, $extra) = str_ireplace($shorthand, $tens, array(ini_get('memory_limit'), EXTRA_MEMORY)); 292 293 if ($ml < $extra) { 294 ini_set('memory_limit', EXTRA_MEMORY); 295 } 296 } 297 298 // Read source image. 299 if ($this->_SRC['type'] == 1) { 300 $this->_SRC['image'] = imagecreatefromgif($this->_SRC['file']); 301 } elseif ($this->_SRC['type'] == 2) { 302 $this->_SRC['image'] = imagecreatefromjpeg($this->_SRC['file']); 303 } elseif ($this->_SRC['type'] == 3) { 304 $this->_SRC['image'] = imagecreatefrompng($this->_SRC['file']); 305 } 306 307 // Crop image. 308 $off_w = 0; 309 $off_h = 0; 310 311 if ($this->crop != false) { 312 if ($this->_SRC['height'] < $this->_SRC['width']) { 313 $ratio = (double) ($this->_SRC['height'] / $this->_DST['height']); 314 $cpyWidth = round($this->_DST['width'] * $ratio); 315 316 if ($cpyWidth > $this->_SRC['width']) { 317 $ratio = (double) ($this->_SRC['width'] / $this->_DST['width']); 318 $cpyWidth = $this->_SRC['width']; 319 $cpyHeight = round($this->_DST['height'] * $ratio); 320 $off_w = 0; 321 $off_h = round(($this->_SRC['height'] - $cpyHeight) / 2); 322 $this->_SRC['height'] = $cpyHeight; 323 } else { 324 $cpyHeight = $this->_SRC['height']; 325 $off_w = round(($this->_SRC['width'] - $cpyWidth) / 2); 326 $off_h = 0; 327 $this->_SRC['width'] = $cpyWidth; 328 } 329 } else { 330 $ratio = (double) ($this->_SRC['width'] / $this->_DST['width']); 331 $cpyHeight = round($this->_DST['height'] * $ratio); 332 333 if ($cpyHeight > $this->_SRC['height']) { 334 $ratio = (double) ($this->_SRC['height'] / $this->_DST['height']); 335 $cpyHeight = $this->_SRC['height']; 336 $cpyWidth = round($this->_DST['width'] * $ratio); 337 $off_w = round(($this->_SRC['width'] - $cpyWidth) / 2); 338 $off_h = 0; 339 $this->_SRC['width'] = $cpyWidth; 340 } else { 341 $cpyWidth = $this->_SRC['width']; 342 $off_w = 0; 343 $off_h = round(($this->_SRC['height'] - $cpyHeight) / 2); 344 $this->_SRC['height'] = $cpyHeight; 345 } 346 } 347 } 348 349 // Ensure non-zero height/width. 350 if (!$this->_DST['height']) { 351 $this->_DST['height'] = 1; 352 } 353 354 if (!$this->_DST['width']) { 355 $this->_DST['width'] = 1; 356 } 357 358 // Create DST. 359 $this->_DST['image'] = imagecreatetruecolor($this->_DST['width'], $this->_DST['height']); 360 361 // GIF or PNG destination, set the transparency up. 362 if ($this->_DST['type'] == 1 || $this->_DST['type'] == 3) { 363 $trans_idx = imagecolortransparent($this->_SRC['image']); 364 365 // Is there a specific transparent colour? 366 if ($trans_idx >= 0) { 367 $trans_color = imagecolorsforindex($this->_SRC['image'], $trans_idx); 368 $trans_idx = imagecolorallocate( 369 $this->_DST['image'], 370 $trans_color['red'], 371 $trans_color['green'], 372 $trans_color['blue'] 373 ); 374 imagefill($this->_DST['image'], 0, 0, $trans_idx); 375 imagecolortransparent($this->_DST['image'], $trans_idx); 376 } elseif ($this->_DST['type'] == 3) { 377 imagealphablending($this->_DST['image'], false); 378 $transparent = imagecolorallocatealpha($this->_DST['image'], 0, 0, 0, 127); 379 imagefill($this->_DST['image'], 0, 0, $transparent); 380 imagesavealpha($this->_DST['image'], true); 381 } 382 } 383 384 imagecopyresampled( 385 $this->_DST['image'], 386 $this->_SRC['image'], 387 0, 388 0, 389 $off_w, 390 $off_h, 391 $this->_DST['width'], 392 $this->_DST['height'], 393 $this->_SRC['width'], 394 $this->_SRC['height'] 395 ); 396 397 if ($this->sharpen === true) { 398 $this->_DST['image'] = UnsharpMask($this->_DST['image'], 80, .5, 3); 399 } 400 401 // Finally, the real dimensions. 402 $this->height = $this->_DST['height']; 403 $this->width = $this->_DST['width']; 404 405 // Add magnifying glass. 406 if ($this->hint === true) { 407 // Should we really add white bars? 408 if ($this->addgreytohint === true) { 409 $trans = imagecolorallocatealpha($this->_DST['image'], 255, 255, 255, 25); 410 imagefilledrectangle( 411 $this->_DST['image'], 412 0, 413 $this->_DST['height'] - 9, 414 $this->_DST['width'], 415 $this->_DST['height'], 416 $trans 417 ); 418 } 419 420 $magnifier = imagecreatefromstring(gzuncompress(base64_decode("eJzrDPBz5+WS4mJgYOD19HAJAtLcIMzBBiRXrilXA1IsxU6eIRxAUMOR0gHkcxZ4RBYD1QiBMOOlu3V/gIISJa4RJc5FqYklmfl5CiGZuakMBoZ6hkZ6RgYGJs77ex2BalRBaoLz00rKE4tSGXwTk4vyc1NTMhMV3DKLUsvzi7KLFXwjFEAa2svWnGdgYPTydHEMqZhTOsE++1CAyNHzm2NZjgau+dAmXlAwoatQmOld3t/NPxlLMvY7sovPzXHf7re05BPzjpQTMkZTPjm1HlHkv6clYWK43Zt16rcDjdZ/3j2cd7qD4/HHH3GaprFrw0QZDHicORXl2JsPsveVTDz//L3N+WpxJ5Hff+10Tjdd2/Vi17vea79Om5w9zzyne9GLnWGrN8atby/ayXPOsu2w4quvVtxNCVVz5nAf3nDpZckBCedpqSc28WTOWnT7rZNXZSlPvFybie9EFc6y3bIMCn3JAoJ+kyyfn9qWq+LZ9Las26Jv482cDRE6Ci0B6gVbo2oj9KabzD8vyMK4ZMqMs2kSvW4chz88SXNzmeGjtj1QZK9M3HHL8L7HITX3t19//VVY8CYDg9Kvy2vDXu+6mGGxNOiltMPsjn/t9eJr0ja/FOdi5TyQ9Lz3fOqstOr99/dnro2vZ1jy76D/vYivPsBoYPB09XNZ55TQBAAJjs5s</body>"))); 421 422 imagealphablending($this->_DST['image'], true); 423 imagecopy($this->_DST['image'], $magnifier, $this->_DST['width'] - 15, $this->_DST['height'] - 14, 0, 0, 11, 11); 424 imagedestroy($magnifier); 425 } 426 427 if ($verbose) { 428 echo "... saving image ..."; 429 } 430 431 if ($this->_DST['type'] == 1) { 432 imagetruecolortopalette($this->_DST['image'], false, 256); 433 if (function_exists('imagegif')) { 434 imagegif($this->_DST['image'], $this->_DST['file']); 435 } else { 436 imagedestroy($this->_DST['image']); 437 imagedestroy($this->_SRC['image']); 438 439 return false; 440 } 441 } elseif ($this->_DST['type'] == 2) { 442 imagejpeg($this->_DST['image'], $this->_DST['file'], $this->quality); 443 } elseif ($this->_DST['type'] == 3) { 444 imagepng($this->_DST['image'], $this->_DST['file']); 445 } 446 447 if ($verbose) { 448 echo "... image successfully saved ..."; 449 } 450 451 imagedestroy($this->_DST['image']); 452 imagedestroy($this->_SRC['image']); 453 454 return true; 455 } 456 457 /** 458 * Return a reference to the the thumbnail image as a HTML a or img tag. 459 * 460 * @param bool $aslink Return an anchor tag to the source image 461 * @param bool $aspopup Open the link in new window 462 * @return string HTML markup 463 */ 464 465 public function asTag($aslink = true, $aspopup = false) 466 { 467 $imgtag = '<img src="'.$this->_DST['file'].'" '.$this->html.' width="'.$this->width.'" height="'.$this->height.'" />'; 468 469 if ($aslink === true) { 470 return '<a href="'.((empty($this->linkurl)) ? $this->_SRC['file'] : $this->linkurl).'" '. 471 (($aspopup === true) ? 'target="_blank"' : '').'>'.$imgtag.'</a>'; 472 } 473 474 return $imgtag; 475 } 476 } 477 478 /** 479 * Wrapper for wet_thumb interfacing Textpattern. 480 * 481 * @package Image 482 */ 483 484 class txp_thumb extends wet_thumb 485 { 486 /** 487 * File extension. 488 * 489 * @var string 490 */ 491 492 public $m_ext; 493 494 /** 495 * Image ID. 496 * 497 * @var int 498 */ 499 500 public $m_id; 501 502 /** 503 * Constructor. 504 * 505 * @param int $id The Image id. 506 */ 507 508 public function __construct($id) 509 { 510 $id = assert_int($id); 511 $rs = safe_row("*", 'txp_image', "id = $id LIMIT 1"); 512 if ($rs) { 513 extract($rs); 514 $this->m_ext = $ext; 515 $this->m_id = $id; 516 } 517 parent::__construct(); 518 } 519 520 /** 521 * Creates a thumbnail image from a source image. 522 * 523 * @param string $dummy1 Isn't used. 524 * @param string $dummy2 Isn't used. 525 * @return bool TRUE on success 526 */ 527 528 public function write($dummy1 = '', $dummy2 = '') 529 { 530 if (!isset($this->m_ext)) { 531 return false; 532 } 533 534 if (parent::write( 535 IMPATH.$this->m_id.$this->m_ext, 536 IMPATH.$this->m_id.'t'.$this->m_ext 537 )) { 538 safe_update( 539 'txp_image', 540 "thumbnail = 1, 541 thumb_w = $this->width, 542 thumb_h = $this->height, 543 date = NOW()", 544 "id = ".$this->m_id 545 ); 546 547 chmod(IMPATH.$this->m_id.'t'.$this->m_ext, 0644); 548 549 return true; 550 } 551 552 return false; 553 } 554 555 /** 556 * Removes a thumbnail. 557 * 558 * @return bool TRUE on success 559 */ 560 561 public function delete() 562 { 563 if (!isset($this->m_ext)) { 564 return false; 565 } 566 567 if (unlink(IMPATH.$this->m_id.'t'.$this->m_ext)) { 568 safe_update('txp_image', "thumbnail = 0", "id = ".$this->m_id); 569 570 return true; 571 } 572 573 return false; 574 } 575 } 576 577 /** 578 * Unsharp mask. 579 * 580 * Unsharp mask algorithm by Torstein Hønsi 2003 (thoensi_at_netcom_dot_no) 581 * Christoph Erdmann: changed it a little, because I could not reproduce the 582 * darker blurred image, now it is up to 15% faster with same results 583 * 584 * @author Torstein Hønsi 585 * @author Christoph Erdmann 586 * @param resource $img Image as a resource 587 * @param int $amount Filter parameter 588 * @param int $radius Filter parameter 589 * @param int $threshold Filter parameter 590 * @return resource Sharpened image as a resource. 591 * 592 * 593 * This library is free software; you can redistribute it and/or modify it 594 * under the terms of the GNU Lesser General Public License as published by 595 * the Free Software Foundation; either version 2.1 of the License, or 596 * (at your option) any later version. 597 */ 598 599 function UnsharpMask($img, $amount, $radius, $threshold) 600 { 601 // Attempt to calibrate the parameters to Photoshop: 602 if ($amount > 500) { 603 $amount = 500; 604 } 605 606 $amount = $amount * 0.016; 607 608 if ($radius > 50) { 609 $radius = 50; 610 } 611 612 $radius = $radius * 2; 613 614 if ($threshold > 255) { 615 $threshold = 255; 616 } 617 618 $radius = abs(round($radius)); // Only integers make sense. 619 620 if ($radius == 0) { 621 return $img; 622 } 623 624 $w = imagesx($img); 625 $h = imagesy($img); 626 $imgCanvas = $img; 627 $imgCanvas2 = $img; 628 $imgBlur = imagecreatetruecolor($w, $h); 629 630 // Gaussian blur matrix: 631 // 1 2 1 632 // 2 4 2 633 // 1 2 1 634 // Move copies of the image around one pixel at the time and merge them 635 // with weight according to the matrix. The same matrix is simply 636 // repeated for higher radii. 637 638 for ($i = 0; $i < $radius; $i++) { 639 imagecopy($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1); // up left 640 imagecopymerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50); // down right 641 imagecopymerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left 642 imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25); // up right 643 imagecopymerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left 644 imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25); // right 645 imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20); // up 646 imagecopymerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667); // down 647 imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50); // center 648 } 649 650 $imgCanvas = $imgBlur; 651 652 // Calculate the difference between the blurred pixels and the original 653 // and set the pixels. 654 655 for ($x = 0; $x < $w; $x++) { 656 // Each row. 657 for ($y = 0; $y < $h; $y++) { 658 // Each pixel. 659 $rgbOrig = ImageColorAt($imgCanvas2, $x, $y); 660 $rOrig = (($rgbOrig >> 16) & 0xFF); 661 $gOrig = (($rgbOrig >> 8) & 0xFF); 662 $bOrig = ($rgbOrig & 0xFF); 663 $rgbBlur = ImageColorAt($imgCanvas, $x, $y); 664 $rBlur = (($rgbBlur >> 16) & 0xFF); 665 $gBlur = (($rgbBlur >> 8) & 0xFF); 666 $bBlur = ($rgbBlur & 0xFF); 667 668 // When the masked pixels differ less from the original than the 669 // threshold specifies, they are set to their original value. 670 671 $rNew = (abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig; 672 $gNew = (abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig; 673 $bNew = (abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig; 674 675 if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) { 676 $pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew); 677 ImageSetPixel($img, $x, $y, $pixCol); 678 } 679 } 680 } 681 682 return $img; 683 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title