1<?php 2/** 3 * XOOPS image access/edit 4 * 5 * You may not change or alter any portion of this comment or credits 6 * of supporting developers from this source code or any supporting source code 7 * which is considered copyrighted (c) material of the original comment or credit authors. 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 * 12 * @copyright (c) 2000-2016 XOOPS Project (www.xoops.org) 13 * @license GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html) 14 * @package core 15 * @since 2.5.7 16 * @author luciorota <lucio.rota@gmail.com>, Joe Lencioni <joe@shiftingpixel.com> 17 * 18 * Enhanced image access/edit 19 * This enhanced version is very useful in many cases, for example when you need a 20 * smallest version of an image. This script uses Xoops cache to minimize server load. 21 * 22 * 23 * Parameters need to be passed in through the URL's query string: 24 * @param int id Xoops image id; 25 * @param string url relative to XOOPS_ROOT_PATH, path of local image starting with "/" 26 * (e.g. /images/toast.jpg); 27 * @param string src relative to XOOPS_ROOT_PATH, path of local image starting with "/" 28 * @param int width (optional) maximum width of final image in pixels (e.g. 700); 29 * @param int height (optional) maximum height of final image in pixels (e.g. 700); 30 * @param string color (optional) background hex color for filling transparent PNGs (e.g. 900 or 16a942); 31 * @param string cropratio (optional) ratio of width to height to crop final image (e.g. 1:1 or 3:2); 32 * @param boolean nocache (optional) don't read image from the cache; 33 * @param boolean noservercache (optional) don't read image from the server cache; 34 * @param boolean nobrowsercache (optional) don't read image from the browser cache; 35 * @param int quality (optional, 0-100, default: 90) quality of output image; 36 * @param mixed filter (optional, imagefilter 2nd, 3rd, 4th, 5th arguments, more info on php.net 37 * manual) a filter or an array of filters; 38 * @param int radius (optional, 1, 2, 3 or 4 integer values, CW) round corner radius 39 * @param float angle (optional), rotation angle) 40 * 41 */ 42 43/* @example image.php 44 * Resizing a JPEG: 45 * <img src="/image.php?url=image-name.jpg&width=100&height=100" alt="Don't forget your alt text" /> 46 * Resizing and cropping a JPEG into a square: 47 * <img src="/image.php?url=image-name.jpg?width=100&height=100&cropratio=1:1" alt="Don't forget your alt text" /> 48 * Matting a PNG with #990000: 49 * <img src="/image.php?url=image-name.png?color=900&image=/path/to/image.png" alt="Don't forget your alt text" /> 50 * Apply a filter: 51 * <img src="/image.php?url=/path/to/image.png&filter=IMG_FILTER_COLORIZE,128,60,256" alt="Don't forget the alt text" /> 52 * Apply more filters (array) : 53 * <img src="/image.php?url=/path/to/image.png&filter[]=IMG_FILTER_GRAYSCALE&filter[]=IMG_FILTER_COLORIZE,128,60,256" /> 54 * Round the image corners: 55 * All corners with same radius: 56 * <img src="/image.php?url=/path/to/image.png&radius=20" alt="Don't forget your alt text" /> 57 * Left and right corners with different radius (20 for left corners and 40 for right corners) 58 * <img src="/image.php?url=/path/to/image.png&radius=20,40" alt="Don't forget your alt text" /> 59 * 4 corners, 4 radius, clockwise order 60 * <img src="/image.php?url=/path/to/image.png&radius=20,40,0,10" alt="Don't forget your alt text" /> 61 * 62 */ 63define('MEMORY_TO_ALLOCATE', '100M'); 64define('DEFAULT_IMAGE_QUALITY', 90); 65define('DEFAULT_BACKGROUND_COLOR', '000000'); 66define('ONLY_LOCAL_IMAGES', true); 67define('ENABLE_IMAGEFILTER', true); // Set to false to avoid excessive server load 68define('ENABLE_ROUNDCORNER', true); // Set to false to avoid excessive server load 69define('ENABLE_IMAGEROTATE', true); // Set to false to avoid excessive server load 70 71if (get_magic_quotes_runtime()) { 72 set_magic_quotes_runtime(false); // will never get called on PHP 5.4+ 73} 74if (function_exists('mb_http_output')) { 75 mb_http_output('pass'); 76} 77 78$xoopsOption['nocommon'] = true; 79require_once __DIR__ . '/mainfile.php'; 80 81include_once __DIR__ . '/include/defines.php'; 82include_once __DIR__ . '/include/functions.php'; 83include_once __DIR__ . '/include/version.php'; 84include_once __DIR__ . '/kernel/object.php'; 85include_once __DIR__ . '/class/xoopsload.php'; 86include_once __DIR__ . '/class/preload.php'; 87include_once __DIR__ . '/class/module.textsanitizer.php'; 88include_once __DIR__ . '/class/database/databasefactory.php'; 89require_once __DIR__ . '/class/criteria.php'; 90XoopsLoad::load('xoopslogger'); 91$xoopsLogger = XoopsLogger::getInstance(); 92$xoopsLogger->startTime(); 93error_reporting(0); 94 95/** 96 * @param $etag 97 * @param $lastModified 98 * @return null 99 */ 100function doConditionalGet($etag, $lastModified) 101{ 102 header("Last-Modified: $lastModified"); 103 header("ETag: \"{$etag}\""); 104 $ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false; 105 $ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) 106 ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false; 107 if (!$ifModifiedSince && !$ifNoneMatch) { 108 return null; 109 } 110 if ($ifNoneMatch && $ifNoneMatch != $etag && $ifNoneMatch != '"' . $etag . '"') { 111 return null; 112 } // etag is there but doesn't match 113 if ($ifModifiedSince && $ifModifiedSince != $lastModified) { 114 return null; 115 } // if-modified-since is there but doesn't match 116 // Nothing has changed since their last request - serve a 304 and exit 117 header('HTTP/1.1 304 Not Modified'); 118 exit(); 119} 120 121/** 122 * ref: http://www.tricksofit.com/2014/08/round-corners-on-image-using-php-and-gd-library 123 * 124 * @param resource $sourceImage GD Image resource 125 * @param int[] $radii array(top left, top right, bottom left, bottom right) of pixel radius 126 * for each corner. A 0 disables rounding on a corner. 127 * 128 * @return resource 129 */ 130function imageCreateCorners($sourceImage, $radii) 131{ 132 $q = 2; // quality - improve alpha blending by using larger (*$q) image size 133 134 // find a unique color 135 $tryCounter = 0; 136 do { 137 if (++$tryCounter > 255) { 138 $r = 2; 139 $g = 254; 140 $b = 0; 141 break; 142 } 143 $r = rand(0, 255); 144 $g = rand(0, 255); 145 $b = rand(0, 255); 146 } while (imagecolorexact($sourceImage, $r, $g, $b) < 0); 147 148 $imageWidth = imagesx($sourceImage); 149 $imageHeight = imagesy($sourceImage); 150 151 $workingWidth = $imageWidth * $q; 152 $workingHeight = $imageHeight * $q; 153 154 $workingImage= imagecreatetruecolor($workingWidth, $workingHeight); 155 $alphaColor = imagecolorallocatealpha($workingImage, $r, $g, $b, 127); 156 imagealphablending($workingImage, false); 157 imagesavealpha($workingImage, true); 158 imagefilledrectangle($workingImage, 0, 0, $workingWidth, $workingHeight, $alphaColor); 159 160 imagefill($workingImage, 0, 0, $alphaColor); 161 imagecopyresampled($workingImage, $sourceImage, 0, 0, 0, 0, $workingWidth, $workingHeight, $imageWidth, $imageHeight); 162 if (0 < ($radius = $radii[0] * $q)) { // left top 163 imagearc($workingImage, $radius - 1, $radius - 1, $radius * 2, $radius * 2, 180, 270, $alphaColor); 164 imagefilltoborder($workingImage, 0, 0, $alphaColor, $alphaColor); 165 } 166 if (0 < ($radius = $radii[1] * $q)) { // right top 167 imagearc($workingImage, $workingWidth - $radius, $radius - 1, $radius * 2, $radius * 2, 270, 0, $alphaColor); 168 imagefilltoborder($workingImage, $workingWidth - 1, 0, $alphaColor, $alphaColor); 169 } 170 if (0 < ($radius = $radii[2] * $q)) { // left bottom 171 imagearc($workingImage, $radius - 1, $workingHeight - $radius, $radius * 2, $radius * 2, 90, 180, $alphaColor); 172 imagefilltoborder($workingImage, 0, $workingHeight - 1, $alphaColor, $alphaColor); 173 } 174 if (0 < ($radius = $radii[3] * $q)) { // right bottom 175 imagearc($workingImage, $workingWidth - $radius, $workingHeight - $radius, $radius * 2, $radius * 2, 0, 90, $alphaColor); 176 imagefilltoborder($workingImage, $workingWidth - 1, $workingHeight - 1, $alphaColor, $alphaColor); 177 } 178 imagealphablending($workingImage, true); 179 imagecolortransparent($workingImage, $alphaColor); 180 181 // scale back down to original size 182 $destinationImage = imagecreatetruecolor($imageWidth, $imageHeight); 183 imagealphablending($destinationImage, false); 184 imagesavealpha($destinationImage, true); 185 imagefilledrectangle($destinationImage, 0, 0, $imageWidth, $imageHeight, $alphaColor); 186 imagecopyresampled($destinationImage, $workingImage, 0, 0, 0, 0, $imageWidth, $imageHeight, $workingWidth, $workingHeight); 187 188 // imagedestroy($sourceImage); 189 imagedestroy($workingImage); 190 191 return $destinationImage; 192} 193 194/** 195 * @param $orig 196 * @param $final 197 * 198 * @return mixed 199 */ 200function findSharp($orig, $final) 201{ 202 // Function from Ryan Rud (http://adryrun.com) 203 $final *= (750.0 / $orig); 204 $a = 52; 205 $b = -0.27810650887573124; 206 $c = .00047337278106508946; 207 $result = $a + $b * $final + $c * $final * $final; 208 209 return max(round($result), 0); 210} 211 212/** 213 * issue an error for bad request 214 * 215 * Many different issues end up here, so message is generic 404. This keeps us from leaking info by probing 216 */ 217function exit404BadReq() 218{ 219 header('HTTP/1.1 404 Not Found'); 220 exit(); 221} 222 223/** 224 * check local image url for possible issues 225 * 226 * @param string $imageUrl url to local image starting at site root with a '/' 227 * 228 * @return bool true if name is acceptable, exit if not 229 */ 230function imageFilenameCheck($imageUrl) 231{ 232 if ($imageUrl[0] !== '/') { // must start with slash 233 exit404BadReq(); 234 } 235 236 if ($imageUrl === '/') { // can't be empty 237 exit404BadReq(); 238 } 239 240 if (preg_match('/(\.\.|<|>|\:|[[:cntrl:]])/', $imageUrl)) { // no "..", "<", ">", ":" or controls 241 exit404BadReq(); 242 } 243 244 $fullPath = XOOPS_ROOT_PATH . $imageUrl; 245 if (strpos($fullPath, XOOPS_VAR_PATH) === 0) { // no access to data (shouldn't be in root, but...) 246 exit404BadReq(); 247 } 248 if (strpos($fullPath, XOOPS_PATH) === 0) { // no access to lib (shouldn't be in root, but...) 249 exit404BadReq(); 250 } 251 252 return true; 253} 254 255/* 256 * Get image 257 */ 258// Get id (Xoops image) or url or src (standard image) 259$imageId = isset($_GET['id']) ? (int)$_GET['id'] : false; 260$imageUrl = isset($_GET['url']) ? (string)$_GET['url'] : (isset($_GET['src']) ? (string)$_GET['src'] : false); 261if (!empty($imageId)) { 262 // If image is a Xoops image 263 /* @var XoopsImageHandler $imageHandler */ 264 $imageHandler = xoops_getHandler('image'); 265 $criteria = new CriteriaCompo(new Criteria('i.image_display', true)); 266 $criteria->add(new Criteria('i.image_id', $imageId)); 267 $images = $imageHandler->getObjects($criteria, false, true); 268 if (count($images) != 1) { 269 // No Xoops images or to many Xoops images 270 header('Content-type: image/gif'); 271 readfile(XOOPS_UPLOAD_PATH . '/blank.gif'); 272 exit(); 273 } 274 $image = $images[0]; 275 // Get image category 276 $imgcatId = $image->getVar('imgcat_id'); 277 $imgcatHandler = xoops_getHandler('imagecategory'); 278 if (!$imgcat = $imgcatHandler->get($imgcatId)) { 279 // No Image category 280 header('Content-type: image/gif'); 281 readfile(XOOPS_UPLOAD_PATH . '/blank.gif'); 282 exit(); 283 } 284 // Get image data 285 $imageFilename = $image->getVar('image_name'); // image filename 286 $imageMimetype = $image->getVar('image_mimetype'); 287 $imageCreatedTime = $image->getVar('image_created'); // image creation date 288 if ($imgcat->getVar('imgcat_storetype') === 'db') { 289 $imagePath = null; 290 $imageData = $image->getVar('image_body'); 291 } else { 292 $imagePath = XOOPS_UPLOAD_PATH . '/' . $image->getVar('image_name'); 293 $imageData = file_get_contents($imagePath); 294 } 295 $sourceImage = imagecreatefromstring($imageData); 296 $imageWidth = imagesx($sourceImage); 297 $imageHeight = imagesy($sourceImage); 298} elseif (!empty($imageUrl)) { 299 // If image is a standard image 300 if (ONLY_LOCAL_IMAGES) { 301 // Images must be local files, so for convenience we strip the domain if it's there 302 $imageUrl = str_replace(XOOPS_URL, '', $imageUrl); 303 304 // will exit on any unacceptable urls 305 imageFilenameCheck($imageUrl); 306 307 $imagePath = XOOPS_ROOT_PATH . $imageUrl; 308 if (!file_exists($imagePath)) { 309 exit404BadReq(); 310 } 311 } else { 312 if ($imageUrl{0} === '/') { 313 $imageUrl = substr($imageUrl, 0, 1); 314 } 315 $imagePath = $imageUrl; 316 } 317 // Get the size and MIME type of the requested image 318 $imageFilename = basename($imagePath); // image filename 319 $imagesize = getimagesize($imagePath); 320 $imageWidth = $imagesize[0]; 321 $imageHeight = $imagesize[1]; 322 $imageMimetype = $imagesize['mime']; 323 $imageCreatedTime = filemtime($imagePath); // image creation date 324 $imageData = file_get_contents($imagePath); 325 switch ($imageMimetype) { 326 case 'image/gif': 327 $sourceImage = imagecreatefromgif($imagePath); 328 break; 329 case 'image/png': 330 $sourceImage = imagecreatefrompng($imagePath); 331 break; 332 case 'image/jpeg': 333 $sourceImage = imagecreatefromjpeg($imagePath); 334 break; 335 default: 336 exit404BadReq(); 337 break; 338 } 339} else { 340 // No id, no url, no src parameters 341 header('Content-type: image/gif'); 342 readfile(XOOPS_ROOT_PATH . '/uploads/blank.gif'); 343 exit(); 344} 345 346/* 347 * Use Xoops cache 348 */ 349// Get image_data from the Xoops cache only if the edited image has been cached after the latest modification 350// of the original image 351xoops_load('XoopsCache'); 352$edited_image_filename = 'editedimage_' . md5($_SERVER['REQUEST_URI']) . '_' . $imageFilename; 353$cached_image = XoopsCache::read($edited_image_filename); 354if (!isset($_GET['nocache']) && !isset($_GET['noservercache']) && !empty($cached_image) 355 && ($cached_image['cached_time'] >= $imageCreatedTime)) { 356 header("Content-type: {$imageMimetype}"); 357 header('Content-Length: ' . strlen($cached_image['image_data'])); 358 echo $cached_image['image_data']; 359 exit(); 360} 361 362/* 363 * Get/check editing parameters 364 */ 365// width, height 366$max_width = isset($_GET['width']) ? (int)$_GET['width'] : false; 367$max_height = isset($_GET['height']) ? (int)$_GET['height'] : false; 368// If either a max width or max height are not specified, we default to something large so the unspecified 369// dimension isn't a constraint on our resized image. 370// If neither are specified but the color is, we aren't going to be resizing at all, just coloring. 371if (!$max_width && $max_height) { 372 $max_width = PHP_INT_MAX; 373} elseif ($max_width && !$max_height) { 374 $max_height = PHP_INT_MAX; 375} elseif (!$max_width && !$max_height) { 376 $max_width = $imageWidth; 377 $max_height = $imageHeight; 378} 379 380// color 381$color = isset($_GET['color']) ? preg_replace('/[^0-9a-fA-F]/', '', (string)$_GET['color']) : false; 382 383// filter, radius, angle 384$filter = isset($_GET['filter']) ? $_GET['filter'] : false; 385$radius = isset($_GET['radius']) ? (string)$_GET['radius'] : false; 386$angle = isset($_GET['angle']) ? (float)$_GET['angle'] : false; 387 388// If we don't have a width or height or color or filter or radius or rotate we simply output the original 389// image and exit 390if (empty($_GET['width']) && empty($_GET['height']) && empty($_GET['color']) && empty($_GET['filter']) 391 && empty($_GET['radius']) && empty($_GET['angle'])) { 392 $last_modified_string = gmdate('D, d M Y H:i:s', $imageCreatedTime) . ' GMT'; 393 $etag = md5($imageData); 394 doConditionalGet($etag, $last_modified_string); 395 header("Content-type: {$imageMimetype}"); 396 header('Content-Length: ' . strlen($imageData)); 397 echo $imageData; 398 exit(); 399} 400 401// cropratio 402$offset_x = 0; 403$offset_y = 0; 404if (isset($_GET['cropratio'])) { 405 $crop_ratio = explode(':', (string)$_GET['cropratio']); 406 if (count($crop_ratio) == 2) { 407 $ratio_computed = $imageWidth / $imageHeight; 408 $crop_radio_computed = (float)$crop_ratio[0] / (float)$crop_ratio[1]; 409 if ($ratio_computed < $crop_radio_computed) { 410 // Image is too tall so we will crop the top and bottom 411 $orig_height = $imageHeight; 412 $imageHeight = $imageWidth / $crop_radio_computed; 413 $offset_y = ($orig_height - $imageHeight) / 2; 414 } elseif ($ratio_computed > $crop_radio_computed) { 415 // Image is too wide so we will crop off the left and right sides 416 $orig_width = $imageWidth; 417 $imageWidth = $imageHeight * $crop_radio_computed; 418 $offset_x = ($orig_width - $imageWidth) / 2; 419 } 420 } 421} 422// Setting up the ratios needed for resizing. We will compare these below to determine how to resize the image 423// (based on height or based on width) 424$xRatio = $max_width / $imageWidth; 425$yRatio = $max_height / $imageHeight; 426if ($xRatio * $imageHeight < $max_height) { 427 // Resize the image based on width 428 $tn_height = ceil($xRatio * $imageHeight); 429 $tn_width = $max_width; 430} else { 431 // Resize the image based on height 432 $tn_width = ceil($yRatio * $imageWidth); 433 $tn_height = $max_height; 434} 435 436// quality 437$quality = isset($_GET['quality']) ? (int)$_GET['quality'] : DEFAULT_IMAGE_QUALITY; 438 439/* 440 * Start image editing 441 */ 442// We don't want to run out of memory 443ini_set('memory_limit', MEMORY_TO_ALLOCATE); 444 445// Set up a blank canvas for our resized image (destination) 446$destination_image = imagecreatetruecolor($tn_width, $tn_height); 447 448imagealphablending($destination_image, false); 449imagesavealpha($destination_image, true); 450$transparent = imagecolorallocatealpha($destination_image, 255, 255, 255, 127); 451imagefilledrectangle($destination_image, 0, 0, $tn_width, $tn_height, $transparent); 452 453// Set up the appropriate image handling functions based on the original image's mime type 454switch ($imageMimetype) { 455 case 'image/gif': 456 // We will be converting GIFs to PNGs to avoid transparency issues when resizing GIFs 457 // This is maybe not the ideal solution, but IE6 can suck it 458 $output_function = 'imagepng'; 459 $imageMimetype = 'image/png'; // We need to convert GIFs to PNGs 460 $do_sharpen = false; 461 $quality = round(10 - ($quality / 10)); // We are converting the GIF to a PNG and PNG needs a compression 462 // level of 0 (no compression) through 9 (max) 463 break; 464 case 'image/png': 465 case 'image/x-png': 466 $output_function = 'imagepng'; 467 $do_sharpen = false; 468 $quality = round(10 - ($quality / 10)); // PNG needs a compression level of 0 (no compression) through 9 469 break; 470 case 'image/jpeg': 471 case 'image/pjpeg': 472 $output_function = 'imagejpeg'; 473 $do_sharpen = true; 474 break; 475 default: 476 exit404BadReq(); 477 break; 478} 479 480// Resample the original image into the resized canvas we set up earlier 481imagecopyresampled($destination_image, $sourceImage, 0, 0, $offset_x, $offset_y, $tn_width, $tn_height, $imageWidth, $imageHeight); 482 483// Set background color 484if (in_array($imageMimetype, array('image/gif', 'image/png'))) { 485 if (!$color) { 486 // If this is a GIF or a PNG, we need to set up transparency 487 imagealphablending($destination_image, false); 488 imagesavealpha($destination_image, true); 489 $png_transparency = imagecolorallocatealpha($destination_image, 0, 0, 0, 127); 490 imagefill($destination_image, 0, 0, $png_transparency); 491 } else { 492 // Fill the background with the specified color for matting purposes 493 if ($color[0] === '#') { 494 $color = substr($color, 1); 495 } 496 $background = false; 497 if (strlen($color) == 6) { 498 $background = imagecolorallocate( 499 $destination_image, 500 intval($color[0] . $color[1], 16), 501 intval($color[2] . $color[3], 16), 502 intval($color[4] . $color[5], 16) 503 ); 504 } elseif (strlen($color) == 3) { 505 $background = imagecolorallocate( 506 $destination_image, 507 intval($color[0] . $color[0], 16), 508 intval($color[1] . $color[1], 16), 509 intval($color[2] . $color[2], 16) 510 ); 511 } 512 if ($background) { 513 imagefill($destination_image, 0, 0, $background); 514 } 515 } 516} else { 517 if (!$color) { 518 $color = DEFAULT_BACKGROUND_COLOR; 519 } 520 // Fill the background with the specified color for matting purposes 521 if ($color[0] === '#') { 522 $color = substr($color, 1); 523 } 524 $background = false; 525 if (strlen($color) == 6) { 526 $background = imagecolorallocate( 527 $destination_image, 528 intval($color[0] . $color[1], 16), 529 intval($color[2] . $color[3], 16), 530 intval($color[4] . $color[5], 16) 531 ); 532 } elseif (strlen($color) == 3) { 533 $background = imagecolorallocate( 534 $destination_image, 535 intval($color[0] . $color[0], 16), 536 intval($color[1] . $color[1], 16), 537 intval($color[2] . $color[2], 16) 538 ); 539 } 540 if ($background) { 541 imagefill($destination_image, 0, 0, $background); 542 } 543} 544 545// Imagefilter 546if (ENABLE_IMAGEFILTER && !empty($filter)) { 547 $filterSet = (array) $filter; 548 foreach ($filterSet as $currentFilter) { 549 $rawFilterArgs = explode(',', $currentFilter); 550 $filterConst = constant(array_shift($rawFilterArgs)); 551 if (null !== $filterConst) { // skip if unknown constant 552 $filterArgs = array(); 553 $filterArgs[] = $destination_image; 554 $filterArgs[] = $filterConst; 555 foreach ($rawFilterArgs as $tempValue) { 556 $filterArgs[] = trim($tempValue); 557 } 558 call_user_func_array('imagefilter', $filterArgs); 559 } 560 } 561} 562 563// Round corners 564if (ENABLE_ROUNDCORNER && !empty($radius)) { 565 $radii = explode(',', $radius); 566 switch (count($radii)) { 567 case 1: 568 $radii[3] = $radii[2] = $radii[1] = $radii[0]; 569 break; 570 case 2: 571 $radii[3] = $radii[0]; 572 $radii[2] = $radii[1]; 573 break; 574 case 3: 575 $radii[3] = $radii[0]; 576 break; 577 case 4: 578 // NOP 579 break; 580 } 581 582 $destination_image = imageCreateCorners($destination_image, $radii); 583 // we need png to support the alpha corners correctly 584 if ($imageMimetype === 'image/jpeg') { 585 $output_function = 'imagepng'; 586 $imageMimetype = 'image/png'; 587 $do_sharpen = false; 588 $quality = round(10 - ($quality / 10)); 589 } 590} 591 592// Imagerotate 593if (ENABLE_IMAGEROTATE && !empty($angle)) { 594 $destination_image = imagerotate($destination_image, $angle, $background, 0); 595} 596 597if ($do_sharpen) { 598 // Sharpen the image based on two things: 599 // (1) the difference between the original size and the final size 600 // (2) the final size 601 $sharpness = findSharp($imageWidth, $tn_width); 602 $sharpen_matrix = array( 603 array(-1, -2, -1), 604 array(-2, $sharpness + 12, -2), 605 array(-1, -2, -1)); 606 $divisor = $sharpness; 607 $offset = 0; 608 imageconvolution($destination_image, $sharpen_matrix, $divisor, $offset); 609} 610 611// Put the data of the resized image into a variable 612ob_start(); 613$output_function($destination_image, null, $quality); 614$imageData = ob_get_contents(); 615ob_end_clean(); 616// Update $image_created_time 617$imageCreatedTime = time(); 618 619// Clean up the memory 620imagedestroy($sourceImage); 621imagedestroy($destination_image); 622 623/* 624 * Write the just edited image into the Xoops cache 625 */ 626$cached_image['edited_image_filename'] = $edited_image_filename; 627$cached_image['image_data'] = $imageData; 628$cached_image['cached_time'] = $imageCreatedTime; 629XoopsCache::write($edited_image_filename, $cached_image); 630 631/* 632 * Send the edited image to the browser 633 */ 634// See if the browser already has the image 635$last_modified_string = gmdate('D, d M Y H:i:s', $imageCreatedTime) . ' GMT'; 636$etag = md5($imageData); 637doConditionalGet($etag, $last_modified_string); 638 639header('HTTP/1.1 200 OK'); 640// if image is cacheable 641if (!isset($_GET['nocache']) && !isset($_GET['nobrowsercache'])) { 642 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $imageCreatedTime) . 'GMT'); 643 header('Cache-control: max-age=31536000'); 644 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . 'GMT'); 645} else { 646 // "Kill" the browser cache 647 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // past date 648 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified 649 header('Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1 650 header('Cache-Control: post-check=0, pre-check=0', false); 651 header('Pragma: no-cache'); // HTTP/1.0 652} 653header("Content-type: {$imageMimetype}"); 654header("Content-disposition: filename={$imageFilename}"); 655header('Content-Length: ' . strlen($imageData)); 656echo $imageData; 657