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=JPEG, 3=PNG. 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 $pallet_size = imagecolorstotal($this->_SRC['image']); 365 366 // Is there a specific transparent colour? 367 if ($trans_idx >= 0 && ($trans_idx < $pallet_size)) { 368 $trans_color = imagecolorsforindex($this->_SRC['image'], $trans_idx); 369 $trans_idx = imagecolorallocate( 370 $this->_DST['image'], 371 $trans_color['red'], 372 $trans_color['green'], 373 $trans_color['blue'] 374 ); 375 imagefill($this->_DST['image'], 0, 0, $trans_idx); 376 imagecolortransparent($this->_DST['image'], $trans_idx); 377 } elseif ($this->_DST['type'] == 3) { 378 imagealphablending($this->_DST['image'], false); 379 $transparent = imagecolorallocatealpha($this->_DST['image'], 0, 0, 0, 127); 380 imagefill($this->_DST['image'], 0, 0, $transparent); 381 imagesavealpha($this->_DST['image'], true); 382 } 383 } 384 385 imagecopyresampled( 386 $this->_DST['image'], 387 $this->_SRC['image'], 388 0, 389 0, 390 $off_w, 391 $off_h, 392 $this->_DST['width'], 393 $this->_DST['height'], 394 $this->_SRC['width'], 395 $this->_SRC['height'] 396 ); 397 398 if ($this->sharpen === true) { 399 $this->_DST['image'] = UnsharpMask($this->_DST['image'], 80, .5, 3); 400 } 401 402 // Finally, the real dimensions. 403 $this->height = $this->_DST['height']; 404 $this->width = $this->_DST['width']; 405 406 // Add magnifying glass. 407 if ($this->hint === true) { 408 // Should we really add white bars? 409 if ($this->addgreytohint === true) { 410 $trans = imagecolorallocatealpha($this->_DST['image'], 255, 255, 255, 25); 411 imagefilledrectangle( 412 $this->_DST['image'], 413 0, 414 $this->_DST['height'] - 9, 415 $this->_DST['width'], 416 $this->_DST['height'], 417 $trans 418 ); 419 } 420 421 $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>"))); 422 423 imagealphablending($this->_DST['image'], true); 424 imagecopy($this->_DST['image'], $magnifier, $this->_DST['width'] - 15, $this->_DST['height'] - 14, 0, 0, 11, 11); 425 imagedestroy($magnifier); 426 } 427 428 if ($verbose) { 429 echo "... saving image ..."; 430 } 431 432 if ($this->_DST['type'] == 1) { 433 imagetruecolortopalette($this->_DST['image'], false, 256); 434 if (function_exists('imagegif')) { 435 imagegif($this->_DST['image'], $this->_DST['file']); 436 } else { 437 imagedestroy($this->_DST['image']); 438 imagedestroy($this->_SRC['image']); 439 440 return false; 441 } 442 } elseif ($this->_DST['type'] == 2) { 443 imagejpeg($this->_DST['image'], $this->_DST['file'], $this->quality); 444 } elseif ($this->_DST['type'] == 3) { 445 imagepng($this->_DST['image'], $this->_DST['file']); 446 } 447 448 if ($verbose) { 449 echo "... image successfully saved ..."; 450 } 451 452 imagedestroy($this->_DST['image']); 453 imagedestroy($this->_SRC['image']); 454 455 return true; 456 } 457 458 /** 459 * Return a reference to the the thumbnail image as a HTML a or img tag. 460 * 461 * @param bool $aslink Return an anchor tag to the source image 462 * @param bool $aspopup Open the link in new window 463 * @return string HTML markup 464 */ 465 466 public function asTag($aslink = true, $aspopup = false) 467 { 468 $imgtag = '<img src="'.$this->_DST['file'].'" '.$this->html.' width="'.$this->width.'" height="'.$this->height.'" />'; 469 470 if ($aslink === true) { 471 return '<a href="'.((empty($this->linkurl)) ? $this->_SRC['file'] : $this->linkurl).'" '. 472 (($aspopup === true) ? ' rel="noopener" target="_blank"' : '').'>'.$imgtag.'</a>'; 473 } 474 475 return $imgtag; 476 } 477 } 478 479 /** 480 * Wrapper for wet_thumb interfacing Textpattern. 481 * 482 * @package Image 483 */ 484 485 class txp_thumb extends wet_thumb 486 { 487 /** 488 * File extension. 489 * 490 * @var string 491 */ 492 493 public $m_ext; 494 495 /** 496 * Image ID. 497 * 498 * @var int 499 */ 500 501 public $m_id; 502 503 /** 504 * Constructor. 505 * 506 * @param int $id The Image id. 507 */ 508 509 public function __construct($id) 510 { 511 $id = assert_int($id); 512 $rs = safe_row("*", 'txp_image', "id = $id LIMIT 1"); 513 if ($rs) { 514 extract($rs); 515 $this->m_ext = $ext; 516 $this->m_id = $id; 517 } 518 parent::__construct(); 519 } 520 521 /** 522 * Creates a thumbnail image from a source image. 523 * 524 * @param string $dummy1 Isn't used. 525 * @param string $dummy2 Isn't used. 526 * @return bool TRUE on success 527 */ 528 529 public function write($dummy1 = '', $dummy2 = '') 530 { 531 if (!isset($this->m_ext)) { 532 return false; 533 } 534 535 if (parent::write( 536 IMPATH.$this->m_id.$this->m_ext, 537 IMPATH.$this->m_id.'t'.$this->m_ext 538 )) { 539 safe_update( 540 'txp_image', 541 "thumbnail = 1, 542 thumb_w = $this->width, 543 thumb_h = $this->height, 544 date = NOW()", 545 "id = ".$this->m_id 546 ); 547 548 chmod(IMPATH.$this->m_id.'t'.$this->m_ext, 0644); 549 550 return true; 551 } 552 553 return false; 554 } 555 556 /** 557 * Removes a thumbnail. 558 * 559 * @return bool TRUE on success 560 */ 561 562 public function delete() 563 { 564 if (!isset($this->m_ext)) { 565 return false; 566 } 567 568 if (unlink(IMPATH.$this->m_id.'t'.$this->m_ext)) { 569 safe_update('txp_image', "thumbnail = 0", "id = ".$this->m_id); 570 571 return true; 572 } 573 574 return false; 575 } 576 } 577 578 /** 579 * Unsharp mask. 580 * 581 * Unsharp mask algorithm by Torstein Hønsi 2003 (thoensi_at_netcom_dot_no) 582 * Christoph Erdmann: changed it a little, because I could not reproduce the 583 * darker blurred image, now it is up to 15% faster with same results 584 * 585 * @author Torstein Hønsi 586 * @author Christoph Erdmann 587 * @param resource $img Image as a resource 588 * @param int $amount Filter parameter 589 * @param int $radius Filter parameter 590 * @param int $threshold Filter parameter 591 * @return resource Sharpened image as a resource. 592 * 593 * 594 * This library is free software; you can redistribute it and/or modify it 595 * under the terms of the GNU Lesser General Public License as published by 596 * the Free Software Foundation; either version 2.1 of the License, or 597 * (at your option) any later version. 598 */ 599 600 function UnsharpMask($img, $amount, $radius, $threshold) 601 { 602 // Attempt to calibrate the parameters to Photoshop: 603 if ($amount > 500) { 604 $amount = 500; 605 } 606 607 $amount = $amount * 0.016; 608 609 if ($radius > 50) { 610 $radius = 50; 611 } 612 613 $radius = $radius * 2; 614 615 if ($threshold > 255) { 616 $threshold = 255; 617 } 618 619 $radius = abs(round($radius)); // Only integers make sense. 620 621 if ($radius == 0) { 622 return $img; 623 } 624 625 $w = imagesx($img); 626 $h = imagesy($img); 627 $imgCanvas = $img; 628 $imgCanvas2 = $img; 629 $imgBlur = imagecreatetruecolor($w, $h); 630 631 // Gaussian blur matrix: 632 // 1 2 1 633 // 2 4 2 634 // 1 2 1 635 // Move copies of the image around one pixel at the time and merge them 636 // with weight according to the matrix. The same matrix is simply 637 // repeated for higher radii. 638 639 for ($i = 0; $i < $radius; $i++) { 640 imagecopy($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1); // up left 641 imagecopymerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50); // down right 642 imagecopymerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left 643 imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25); // up right 644 imagecopymerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left 645 imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25); // right 646 imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20); // up 647 imagecopymerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667); // down 648 imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50); // center 649 } 650 651 $imgCanvas = $imgBlur; 652 653 // Calculate the difference between the blurred pixels and the original 654 // and set the pixels. 655 656 for ($x = 0; $x < $w; $x++) { 657 // Each row. 658 for ($y = 0; $y < $h; $y++) { 659 // Each pixel. 660 $rgbOrig = ImageColorAt($imgCanvas2, $x, $y); 661 $rOrig = (($rgbOrig >> 16) & 0xFF); 662 $gOrig = (($rgbOrig >> 8) & 0xFF); 663 $bOrig = ($rgbOrig & 0xFF); 664 $rgbBlur = ImageColorAt($imgCanvas, $x, $y); 665 $rBlur = (($rgbBlur >> 16) & 0xFF); 666 $gBlur = (($rgbBlur >> 8) & 0xFF); 667 $bBlur = ($rgbBlur & 0xFF); 668 669 // When the masked pixels differ less from the original than the 670 // threshold specifies, they are set to their original value. 671 672 $rNew = (abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig; 673 $gNew = (abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig; 674 $bNew = (abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig; 675 676 if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) { 677 $pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew); 678 ImageSetPixel($img, $x, $y, $pixCol); 679 } 680 } 681 } 682 683 return $img; 684 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title