Completed
Push — master ( 7bb5bf...9a4e50 )
by Richard
28:24 queued 22s
created

htdocs/image.php (1 issue)

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 (https://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             core
15
 * @since               2.5.7
16
 * @author              luciorota <[email protected]>, Joe Lencioni <[email protected]>
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
 */
63
define('MEMORY_TO_ALLOCATE', '100M');
64
define('DEFAULT_IMAGE_QUALITY', 90);
65
define('DEFAULT_BACKGROUND_COLOR', '000000');
66
define('ONLY_LOCAL_IMAGES', true);
67
define('ENABLE_IMAGEFILTER', true); // Set to false to avoid excessive server load
68
define('ENABLE_ROUNDCORNER', true); // Set to false to avoid excessive server load
69
define('ENABLE_IMAGEROTATE', true); // Set to false to avoid excessive server load
70
71
if (function_exists('set_magic_quotes_runtime')) {
72
    @set_magic_quotes_runtime(false); // will never get called on PHP 5.4+
73
}
74
if (function_exists('mb_http_output')) {
75
    mb_http_output('pass');
76
}
77
78
$xoopsOption['nocommon'] = true;
79
require_once __DIR__ . '/mainfile.php';
80
81
include_once __DIR__ . '/include/defines.php';
82
include_once __DIR__ . '/include/functions.php';
83
include_once __DIR__ . '/include/version.php';
84
include_once __DIR__ . '/kernel/object.php';
85
include_once __DIR__ . '/class/xoopsload.php';
86
include_once __DIR__ . '/class/preload.php';
87
include_once __DIR__ . '/class/module.textsanitizer.php';
88
include_once __DIR__ . '/class/database/databasefactory.php';
89
require_once __DIR__ . '/class/criteria.php';
90
XoopsLoad::load('xoopslogger');
91
$xoopsLogger = XoopsLogger::getInstance();
92
$xoopsLogger->startTime();
93
error_reporting(0);
94
95
/**
96
 * @param $etag
97
 * @param $lastModified
98
 * @return null
99
 */
100
function 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
 */
130
function 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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $destinationImage also could return the type GdImage which is incompatible with the documented return type resource.
Loading history...
192
}
193
194
/**
195
 * @param $orig
196
 * @param $final
197
 *
198
 * @return mixed
199
 */
200
function 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
 */
217
function 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
 */
230
function 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);
261
if (!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
351
xoops_load('XoopsCache');
352
$edited_image_filename = 'editedimage_' . md5($_SERVER['REQUEST_URI']) . '_' . $imageFilename;
353
$cached_image = XoopsCache::read($edited_image_filename);
354
if (!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.
371
if (!$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
390
if (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;
404
if (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;
426
if ($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
443
ini_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
448
imagealphablending($destination_image, false);
449
imagesavealpha($destination_image, true);
450
$transparent = imagecolorallocatealpha($destination_image, 255, 255, 255, 127);
451
imagefilledrectangle($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
454
switch ($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
481
imagecopyresampled($destination_image, $sourceImage, 0, 0, $offset_x, $offset_y, $tn_width, $tn_height, $imageWidth, $imageHeight);
482
483
// Set background color
484
if (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
546
if (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
564
if (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
593
if (ENABLE_IMAGEROTATE && !empty($angle)) {
594
    $destination_image = imagerotate($destination_image, $angle, $background, 0);
595
}
596
597
if ($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
612
ob_start();
613
$output_function($destination_image, null, $quality);
614
$imageData = ob_get_contents();
615
ob_end_clean();
616
// Update $image_created_time
617
$imageCreatedTime = time();
618
619
// Clean up the memory
620
imagedestroy($sourceImage);
621
imagedestroy($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;
629
XoopsCache::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);
637
doConditionalGet($etag, $last_modified_string);
638
639
header('HTTP/1.1 200 OK');
640
// if image is cacheable
641
if (!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
}
653
header("Content-type: {$imageMimetype}");
654
header("Content-disposition: filename={$imageFilename}");
655
header('Content-Length: ' . strlen($imageData));
656
echo $imageData;
657