MDL-14589 user icons now stored in file storage pool
[moodle.git] / lib / gdlib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * gdlib.php - Collection of routines in Moodle related to
20  * processing images using GD
21  *
22  * @package moodlecore
23  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
24  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 /**
28  *
29  * long description
30  * @global object
31  * @param object $dst_img
32  * @param object $src_img
33  * @param int $dst_x
34  * @param int $dst_y
35  * @param int $src_x
36  * @param int $src_y
37  * @param int $dst_w
38  * @param int $dst_h
39  * @param int $src_w
40  * @param int $src_h
41  * @return bool
42  * @todo Finish documenting this function
43  */
44 function ImageCopyBicubic ($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) {
46     global $CFG;
48     if (function_exists('ImageCopyResampled') and $CFG->gdversion >= 2) {
49        return ImageCopyResampled($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y,
50                                  $dst_w, $dst_h, $src_w, $src_h);
51     }
53     $totalcolors = imagecolorstotal($src_img);
54     for ($i=0; $i<$totalcolors; $i++) {
55         if ($colors = ImageColorsForIndex($src_img, $i)) {
56             ImageColorAllocate($dst_img, $colors['red'], $colors['green'], $colors['blue']);
57         }
58     }
60     $scaleX = ($src_w - 1) / $dst_w;
61     $scaleY = ($src_h - 1) / $dst_h;
63     $scaleX2 = $scaleX / 2.0;
64     $scaleY2 = $scaleY / 2.0;
66     for ($j = 0; $j < $dst_h; $j++) {
67         $sY = $j * $scaleY;
69         for ($i = 0; $i < $dst_w; $i++) {
70             $sX = $i * $scaleX;
72             $c1 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX,(int)$sY+$scaleY2));
73             $c2 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX,(int)$sY));
74             $c3 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX+$scaleX2,(int)$sY+$scaleY2));
75             $c4 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX+$scaleX2,(int)$sY));
77             $red = (int) (($c1['red'] + $c2['red'] + $c3['red'] + $c4['red']) / 4);
78             $green = (int) (($c1['green'] + $c2['green'] + $c3['green'] + $c4['green']) / 4);
79             $blue = (int) (($c1['blue'] + $c2['blue'] + $c3['blue'] + $c4['blue']) / 4);
81             $color = ImageColorClosest ($dst_img, $red, $green, $blue);
82             ImageSetPixel ($dst_img, $i + $dst_x, $j + $dst_y, $color);
83         }
84     }
85 }
87 /**
88  * Delete profile images associated with user or group
89  *
90  * @global object
91  * @param int $id user or group id
92  * @param string $dir type of entity - 'groups' or 'users'
93  * @return boolean success
94  */
95 function delete_profile_image($id, $dir='users') {
96     global $CFG;
98 //TODO: deprecate
100     require_once $CFG->libdir.'/filelib.php';
101     $location = $CFG->dataroot .'/'. $dir .'/'. $id;
103     if (file_exists($location)) {
104         return fulldelete($location);
105     }
107     return true;
110 /**
111  * Given an upload manager with the right settings, this function performs a virus scan, and then scales and crops
112  * it and saves it in the right place to be a "user" or "group" image.
113  *
114  * @global object
115  * @param int $id user or group id
116  * @param string $dir type of entity - groups, user, ...
117  * @return string $destination (profile image destination path) or false on error
118  */
119 function create_profile_image_destination($id, $dir='user') {
120     global $CFG;
122 //TODO: deprecate
124     umask(0000);
126     if (!file_exists($CFG->dataroot .'/'. $dir)) {
127         if (!mkdir($CFG->dataroot .'/'. $dir, $CFG->directorypermissions)) {
128             return false;
129         }
130     }
132     if ($dir == 'user') {
133         $destination = make_user_directory($id, true);
134     } else {
135         $destination = "$CFG->dataroot/$dir/$id";
136     }
138     if (!file_exists($destination)) {
139         if (!make_upload_directory(str_replace($CFG->dataroot . '/', '', $destination))) {
140             return false;
141         }
142     }
143     return $destination;
146 /**
147  * Stores optimised icon images in icon file area
148  *
149  * @param $context
150  * @param component
151  * @param $itemid
152  * @param $originalfile
153  * @return success
154  */
155 function process_new_icon($context, $component, $filearea, $itemid, $originalfile) {
156     global $CFG;
158     if (empty($CFG->gdversion)) {
159         return false;
160     }
162     if (!is_file($originalfile)) {
163         return false;
164     }
166     $imageinfo = GetImageSize($originalfile);
168     if (empty($imageinfo)) {
169         return false;
170     }
172     $image->width  = $imageinfo[0];
173     $image->height = $imageinfo[1];
174     $image->type   = $imageinfo[2];
176     switch ($image->type) {
177         case IMAGETYPE_GIF:
178             if (function_exists('ImageCreateFromGIF')) {
179                 $im = ImageCreateFromGIF($originalfile);
180             } else {
181                 debugging('GIF not supported on this server');
182                 return false;
183             }
184             break;
185         case IMAGETYPE_JPEG:
186             if (function_exists('ImageCreateFromJPEG')) {
187                 $im = ImageCreateFromJPEG($originalfile);
188             } else {
189                 debugging('JPEG not supported on this server');
190                 return false;
191             }
192             break;
193         case IMAGETYPE_PNG:
194             if (function_exists('ImageCreateFromPNG')) {
195                 $im = ImageCreateFromPNG($originalfile);
196             } else {
197                 debugging('PNG not supported on this server');
198                 return false;
199             }
200             break;
201         default:
202             return false;
203     }
206     if (function_exists('ImageCreateTrueColor') and $CFG->gdversion >= 2) {
207         $im1 = ImageCreateTrueColor(100,100);
208         $im2 = ImageCreateTrueColor(35,35);
209     } else {
210         $im1 = ImageCreate(100,100);
211         $im2 = ImageCreate(35,35);
212     }
214     $cx = $image->width / 2;
215     $cy = $image->height / 2;
217     if ($image->width < $image->height) {
218         $half = floor($image->width / 2.0);
219     } else {
220         $half = floor($image->height / 2.0);
221     }
223     ImageCopyBicubic($im1, $im, 0, 0, $cx-$half, $cy-$half, 100, 100, $half*2, $half*2);
224     ImageCopyBicubic($im2, $im, 0, 0, $cx-$half, $cy-$half, 35, 35, $half*2, $half*2);
226     if (!function_exists('ImageJpeg')) {
227         debugging('Jpeg not supported on this server, please fix server configuration');
228         return false;
229     }
231     $fs = get_file_storage();
233     $icon = array('contextid'=>$context->id, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>'/');
235     ob_start();
236     if (!ImageJpeg($im1, NULL, 90)) {
237         // keep old icons
238         ob_end_clean();
239         return false;
240     }
241     $data = ob_get_clean();
242     ImageDestroy($im1);
243     $icon['filename'] = 'f1.jpg';
244     $fs->delete_area_files($context->id, $component, $filearea, $itemid);
245     $fs->create_file_from_string($icon, $data);
247     ob_start();
248     if (!ImageJpeg($im2, NULL, 95)) {
249         ob_end_clean();
250         $fs->delete_area_files($context->id, $component, $filearea, $itemid);
251         return false;
252     }
253     $data = ob_get_clean();
254     ImageDestroy($im2);
255     $icon['filename'] = 'f2.jpg';
256     $fs->create_file_from_string($icon, $data);
258     return true;
261 /**
262  * Given an upload manager with the right settings, this function performs a virus scan, and then scales and crops
263  * it and saves it in the right place to be a "user" or "group" image.
264  *
265  * @param int $id user or group id
266  * @param object $userform with imagefile upload field
267  * @param string $dir type of entity - groups, user, ...
268  * @return boolean success
269  */
270 function save_profile_image($id, $userform, $dir='user') {
272 //TODO: deprecate
274     $destination = create_profile_image_destination($id, $dir);
275     if ($destination === false) {
276         return false;
277     }
279     $filename = $userform->get_new_filename('imagefile');
280     $pathname = $destination.'/'.$filename;
282     if (!$userform->save_file('imagefile', $pathname, true)) {
283         return false;
284     }
286     return process_profile_image($pathname, $destination);
289 /**
290  * Given a path to an image file this function scales and crops it and saves it in
291  * the right place to be a "user" or "group" image.
292  *
293  * @global object
294  * @param string $originalfile the path of the original image file
295  * @param string $destination the final destination directory of the profile image
296  * @return boolean
297  */
298 function process_profile_image($originalfile, $destination) {
299     global $CFG, $OUTPUT;
301 //TODO: deprecate
303     if(!(is_file($originalfile) && is_dir($destination))) {
304         return false;
305     }
307     if (empty($CFG->gdversion)) {
308         return false;
309     }
311     $imageinfo = GetImageSize($originalfile);
313     if (empty($imageinfo)) {
314         if (file_exists($originalfile)) {
315             unlink($originalfile);
316         }
317         return false;
318     }
320     $image->width  = $imageinfo[0];
321     $image->height = $imageinfo[1];
322     $image->type   = $imageinfo[2];
324     switch ($image->type) {
325         case IMAGETYPE_GIF:
326             if (function_exists('ImageCreateFromGIF')) {
327                 $im = ImageCreateFromGIF($originalfile);
328             } else {
329                 notice('GIF not supported on this server');
330                 unlink($originalfile);
331                 return false;
332             }
333             break;
334         case IMAGETYPE_JPEG:
335             if (function_exists('ImageCreateFromJPEG')) {
336                 $im = ImageCreateFromJPEG($originalfile);
337             } else {
338                 notice('JPEG not supported on this server');
339                 unlink($originalfile);
340                 return false;
341             }
342             break;
343         case IMAGETYPE_PNG:
344             if (function_exists('ImageCreateFromPNG')) {
345                 $im = ImageCreateFromPNG($originalfile);
346             } else {
347                 notice('PNG not supported on this server');
348                 unlink($originalfile);
349                 return false;
350             }
351             break;
352         default:
353             unlink($originalfile);
354             return false;
355     }
357     unlink($originalfile);
359     if (function_exists('ImageCreateTrueColor') and $CFG->gdversion >= 2) {
360         $im1 = ImageCreateTrueColor(100,100);
361         $im2 = ImageCreateTrueColor(35,35);
362     } else {
363         $im1 = ImageCreate(100,100);
364         $im2 = ImageCreate(35,35);
365     }
367     $cx = $image->width / 2;
368     $cy = $image->height / 2;
370     if ($image->width < $image->height) {
371         $half = floor($image->width / 2.0);
372     } else {
373         $half = floor($image->height / 2.0);
374     }
376     ImageCopyBicubic($im1, $im, 0, 0, $cx-$half, $cy-$half, 100, 100, $half*2, $half*2);
377     ImageCopyBicubic($im2, $im, 0, 0, $cx-$half, $cy-$half, 35, 35, $half*2, $half*2);
379     if (function_exists('ImageJpeg')) {
380         @touch($destination .'/f1.jpg');  // Helps in Safe mode
381         @touch($destination .'/f2.jpg');  // Helps in Safe mode
382         if (ImageJpeg($im1, $destination .'/f1.jpg', 90) and
383             ImageJpeg($im2, $destination .'/f2.jpg', 95) ) {
384             @chmod($destination .'/f1.jpg', 0666);
385             @chmod($destination .'/f2.jpg', 0666);
386             return 1;
387         }
388     } else {
389         echo $OUTPUT->notification('PHP has not been configured to support JPEG images.  Please correct this.');
390     }
391     return 0;
394 /**
395  * Given a user id this function scales and crops the user images to remove
396  * the one pixel black border.
397  *
398  * @global object
399  * @param int $id
400  * @param string $dir
401  * @return boolean
402  */
403 function upgrade_profile_image($id, $dir='users') {
404     global $CFG, $OUTPUT;
406 //TODO: deprecate
408     $im = ImageCreateFromJPEG($CFG->dataroot .'/'. $dir .'/'. $id .'/f1.jpg');
410     if (function_exists('ImageCreateTrueColor') and $CFG->gdversion >= 2) {
411         $im1 = ImageCreateTrueColor(100,100);
412         $im2 = ImageCreateTrueColor(35,35);
413     } else {
414         $im1 = ImageCreate(100,100);
415         $im2 = ImageCreate(35,35);
416     }
418     if (function_exists('ImageCopyResampled') and $CFG->gdversion >= 2) {
419         ImageCopyBicubic($im1, $im, 0, 0, 2, 2, 100, 100, 96, 96);
420     } else {
421         imagecopy($im1, $im, 0, 0, 0, 0, 100, 100);
422                 $c = ImageColorsForIndex($im1,ImageColorAt($im1,2,2));
423                 $color = ImageColorClosest ($im1, $c['red'], $c['green'], $c['blue']);
424                 ImageSetPixel ($im1, 0, 0, $color);
425                 $c = ImageColorsForIndex($im1,ImageColorAt($im1,2,97));
426                 $color = ImageColorClosest ($im1, $c['red'], $c['green'], $c['blue']);
427                 ImageSetPixel ($im1, 0, 99, $color);
428                 $c = ImageColorsForIndex($im1,ImageColorAt($im1,97,2));
429                 $color = ImageColorClosest ($im1, $c['red'], $c['green'], $c['blue']);
430                 ImageSetPixel ($im1, 99, 0, $color);
431                 $c = ImageColorsForIndex($im1,ImageColorAt($im1,97,97));
432                 $color = ImageColorClosest ($im1, $c['red'], $c['green'], $c['blue']);
433                 ImageSetPixel ($im1, 99, 99, $color);
434         for ($x = 1; $x < 99; $x++) {
435                 $c1 = ImageColorsForIndex($im1,ImageColorAt($im,$x,1));
436                 $color = ImageColorClosest ($im, $c1['red'], $c1['green'], $c1['blue']);
437                 ImageSetPixel ($im1, $x, 0, $color);
438                 $c2 = ImageColorsForIndex($im1,ImageColorAt($im1,$x,98));
439                 $color = ImageColorClosest ($im, $c2['red'], $c2['green'], $c2['blue']);
440                 ImageSetPixel ($im1, $x, 99, $color);
441         }
442         for ($y = 1; $y < 99; $y++) {
443                 $c3 = ImageColorsForIndex($im1,ImageColorAt($im, 1, $y));
444                 $color = ImageColorClosest ($im, $c3['red'], $c3['green'], $c3['blue']);
445                 ImageSetPixel ($im1, 0, $y, $color);
446                 $c4 = ImageColorsForIndex($im1,ImageColorAt($im1, 98, $y));
447                 $color = ImageColorClosest ($im, $c4['red'], $c4['green'], $c4['blue']);
448                 ImageSetPixel ($im1, 99, $y, $color);
449         }
450     }
451     ImageCopyBicubic($im2, $im, 0, 0, 2, 2, 35, 35, 96, 96);
453     if (function_exists('ImageJpeg')) {
454         if (ImageJpeg($im1, $CFG->dataroot .'/'. $dir .'/'. $id .'/f1.jpg', 90) and
455             ImageJpeg($im2, $CFG->dataroot .'/'. $dir .'/'. $id .'/f2.jpg', 95) ) {
456             @chmod($CFG->dataroot .'/'. $dir .'/'. $id .'/f1.jpg', 0666);
457             @chmod($CFG->dataroot .'/'. $dir .'/'. $id .'/f2.jpg', 0666);
458             return 1;
459         }
460     } else {
461         echo $OUTPUT->notification('PHP has not been configured to support JPEG images.  Please correct this.');
462     }
463     return 0;