the browser-facing portion of osu!
at master 4.2 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6namespace App\Libraries; 7 8use App\Exceptions\ImageProcessorException; 9 10class ImageProcessor 11{ 12 public $errors = []; 13 14 public $hardMaxDim = [5000, 5000]; 15 public $hardMaxFileSize = 10000000; 16 public $allowedTypes = [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG]; 17 18 public $inputDim = null; 19 public $inputFileSize = null; 20 public $inputPath = null; 21 public $targetDim = null; 22 public $targetFileSize = null; 23 24 public function __construct($inputPath, $targetDim, $targetFileSize) 25 { 26 $this->inputPath = $inputPath; 27 $this->targetDim = $targetDim; 28 $this->targetFileSize = $targetFileSize; 29 30 $this->parseInput(); 31 } 32 33 public function basicCheck() 34 { 35 if ($this->inputFileSize > $this->hardMaxFileSize) { 36 throw new ImageProcessorException(osu_trans('users.show.edit.cover.upload.too_large')); 37 } 38 39 if ($this->inputDim === null || !in_array($this->inputDim[2], $this->allowedTypes, true)) { 40 throw new ImageProcessorException(osu_trans('users.show.edit.cover.upload.unsupported_format')); 41 } 42 43 if ($this->inputDim[0] > $this->hardMaxDim[0] || $this->inputDim[1] > $this->hardMaxDim[1]) { 44 throw new ImageProcessorException(osu_trans('users.show.edit.cover.upload.too_large')); 45 } 46 } 47 48 public function ext() 49 { 50 return image_type_to_extension($this->inputDim[2], false); 51 } 52 53 public function parseInput() 54 { 55 $this->inputDim = read_image_properties($this->inputPath); 56 $this->inputFileSize = filesize($this->inputPath); 57 } 58 59 public function purgeExif() 60 { 61 if ($this->inputDim[2] !== IMAGETYPE_JPEG) { 62 return; 63 } 64 65 exec('jhead -autorot -purejpg -q '.escapeshellarg($this->inputPath)); 66 $this->parseInput(); 67 } 68 69 public function process() 70 { 71 $this->basicCheck(); 72 73 $this->purgeExif(); 74 75 $inputImage = open_image($this->inputPath, $this->inputDim); 76 77 if ($inputImage === null) { 78 throw new ImageProcessorException(osu_trans('users.show.edit.cover.upload.broken_file')); 79 } 80 81 if ( 82 $this->inputDim[0] <= $this->targetDim[0] && 83 $this->inputDim[1] <= $this->targetDim[1] 84 ) { 85 if ($this->inputFileSize < $this->targetFileSize) { 86 return; 87 } 88 89 $image = $inputImage; 90 } else { 91 $start = [0, 0]; 92 $inDim = [$this->inputDim[0], $this->inputDim[1]]; 93 $outDim = [$this->targetDim[0], $this->targetDim[1]]; 94 95 // figure out how to crop. 96 if ($this->inputDim[0] / $this->inputDim[1] >= $this->targetDim[0] / $this->targetDim[1]) { 97 $inDim[0] = $this->targetDim[0] / $this->targetDim[1] * $this->inputDim[1]; 98 $start[0] = ($this->inputDim[0] - $inDim[0]) / 2; 99 } else { 100 $inDim[1] = $this->targetDim[1] / $this->targetDim[0] * $this->inputDim[0]; 101 $start[1] = ($this->inputDim[1] - $inDim[1]) / 2; 102 } 103 104 // don't scale if input image is smaller. 105 if ($inDim[0] < $outDim[0] || $inDim[1] < $outDim[1]) { 106 $outDim = $inDim; 107 } 108 109 $image = imagecreatetruecolor($outDim[0], $outDim[1]); 110 imagesavealpha($image, true); 111 imagefill($image, 0, 0, imagecolorallocatealpha($image, 0, 0, 0, 127)); 112 imagecopyresampled($image, $inputImage, 0, 0, $start[0], $start[1], $outDim[0], $outDim[1], $inDim[0], $inDim[1]); 113 } 114 115 $toJpeg = true; 116 117 if ($this->inputDim[2] === IMAGETYPE_PNG || $this->inputDim[2] === IMAGETYPE_GIF) { 118 imagepng($image, $this->inputPath); 119 120 $this->parseInput(); 121 $toJpeg = $this->inputFileSize > $this->targetFileSize; 122 } 123 124 if ($toJpeg) { 125 imagejpeg($image, $this->inputPath, 90); 126 } 127 128 $this->parseInput(); 129 } 130}