1: <?php
2:
3: /**
4: ##DOC-SIGNATURE##
5:
6: This file is part of WideImage.
7:
8: WideImage is free software; you can redistribute it and/or modify
9: it under the terms of the GNU Lesser General Public License as published by
10: the Free Software Foundation; either version 2.1 of the License, or
11: (at your option) any later version.
12:
13: WideImage is distributed in the hope that it will be useful,
14: but WITHOUT ANY WARRANTY; without even the implied warranty of
15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: GNU Lesser General Public License for more details.
17:
18: You should have received a copy of the GNU Lesser General Public License
19: along with WideImage; if not, write to the Free Software
20: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21:
22: * @package WideImage
23: **/
24:
25: namespace WideImage;
26:
27: use WideImage\MapperFactory;
28: use WideImage\Exception\Exception;
29: use WideImage\Exception\UnsupportedFormatException;
30: use WideImage\Exception\InvalidImageSourceException;
31: use WideImage\Exception\InvalidImageHandleException;
32:
33: /**
34: * The gateway class for loading images and core library functions
35: *
36: * @package WideImage
37: */
38: class WideImage
39: {
40: const SIDE_TOP_LEFT = 1;
41: const SIDE_TOP = 2;
42: const SIDE_TOP_RIGHT = 4;
43: const SIDE_RIGHT = 8;
44: const SIDE_BOTTOM_RIGHT = 16;
45: const SIDE_BOTTOM = 32;
46: const SIDE_BOTTOM_LEFT = 64;
47: const SIDE_LEFT = 128;
48: const SIDE_ALL = 255;
49:
50: /**
51: * @var string Path to the library base directory
52: */
53: protected static $path = null;
54:
55: /**
56: * Returns the library version
57: * @return string The library version
58: */
59: public static function version()
60: {
61: return '##DEV##';
62: }
63:
64: /**
65: * Returns the path to the library
66: * @return string
67: */
68: public static function path()
69: {
70: if (static::$path === null) {
71: static::$path = __DIR__ . DIRECTORY_SEPARATOR;
72: }
73:
74: return static::$path;
75: }
76:
77: /**
78: * Checks whether the gd library is loaded, and throws an exception otherwise
79: */
80: public static function checkGD()
81: {
82: if (!extension_loaded('gd')) {
83: throw new Exception("WideImage requires the GD extension, but it's apparently not loaded.");
84: }
85: }
86:
87: /**
88: * Registers a custom mapper for image loading and saving
89: *
90: * Example:
91: * <code>
92: * \WideImage\WideImage::registerCustomMapper('\\WideImage\\Mapper\\TGA', 'image/tga', 'tga');
93: * </code>
94: *
95: * @param string $mapper_class_name
96: * @param string $mime_type
97: * @param string $extension
98: */
99: public static function registerCustomMapper($mapper_class_name, $mime_type, $extension)
100: {
101: MapperFactory::registerMapper($mapper_class_name, $mime_type, strtoupper($extension));
102: }
103:
104: /**
105: * Loads an image from a file, URL, HTML input file field, binary string, or a valid image handle.
106: * The image format is auto-detected.
107: *
108: * Currently supported formats: PNG, GIF, JPG, BMP, TGA, GD, GD2.
109: *
110: * This function analyzes the input and decides whether to use WideImage::loadFromHandle(),
111: * WideImage::loadFromFile(), WideImage::loadFromUpload() or WideImage::loadFromString(),
112: * all of which you can also call directly to spare WideImage some guessing.
113: *
114: * Arrays are supported for upload fields; it returns an array of loaded images.
115: * To load only a single image from an array field, use WideImage::loadFromUpload('img', $i),
116: * where $i is the index of the image you want to load.
117: *
118: * <code>
119: * $img = WideImage::load('http://url/image.png'); // image URL
120: * $img = WideImage::load('/path/to/image.png'); // local file path
121: * $img = WideImage::load('img'); // upload field name
122: * $img = WideImage::load(imagecreatetruecolor(10, 10)); // a GD resource
123: * $img = WideImage::load($image_data); // binary string containing image data
124: * </code>
125: *
126: * @param mixed $source File name, url, HTML file input field name, binary string, or a GD image resource
127: * @return \WideImage\Image|\WideImage\PaletteImage|\WideImage\TrueColorImage
128: */
129: public static function load($source)
130: {
131: $predictedSourceType = '';
132:
133: if ($source == '') {
134: $predictedSourceType = 'String';
135: }
136:
137: // Creating image via a valid resource
138: if (!$predictedSourceType && static::isValidImageHandle($source)) {
139: $predictedSourceType = 'Handle';
140: }
141:
142: // Check for binary string
143: if (!$predictedSourceType) {
144: // search first $binLength bytes (at a maximum) for ord<32 characters (binary image data)
145: $binLength = 64;
146: $sourceLength = strlen($source);
147: $maxlen = ($sourceLength > $binLength) ? $binLength : $sourceLength;
148:
149: for ($i = 0; $i < $maxlen; $i++) {
150: if (ord($source[$i]) < 32) {
151: $predictedSourceType = 'String';
152: break;
153: }
154: }
155: }
156:
157: // Uploaded image (array uploads not supported)
158: if (isset($_FILES[$source]) && isset($_FILES[$source]['tmp_name'])) {
159: $predictedSourceType = 'Upload';
160: }
161:
162: // Otherwise, must be a file or an URL
163: if (!$predictedSourceType) {
164: $predictedSourceType = 'File';
165: }
166:
167: return call_user_func(array(__CLASS__, 'loadFrom' . $predictedSourceType), $source);
168: }
169:
170: /**
171: * Create and load an image from a file or URL. The image format is auto-detected.
172: *
173: * @param string $uri File or url
174: * @return \WideImage\Image|\WideImage\PaletteImage|\WideImage\TrueColorImage
175: */
176: public static function loadFromFile($uri)
177: {
178: $data = file_get_contents($uri);
179: $handle = @imagecreatefromstring($data);
180:
181: if (!static::isValidImageHandle($handle)) {
182: try {
183: // try to find a mapper first
184: $mapper = MapperFactory::selectMapper($uri);
185:
186: if ($mapper) {
187: $handle = $mapper->load($uri);
188: }
189: } catch (UnsupportedFormatException $e) {
190: // mapper not found
191: }
192:
193: // try all custom mappers
194: if (!static::isValidImageHandle($handle)) {
195: $custom_mappers = MapperFactory::getCustomMappers();
196:
197: foreach ($custom_mappers as $mime_type => $mapper_class) {
198: $mapper = MapperFactory::selectMapper(null, $mime_type);
199: $handle = $mapper->loadFromString($data);
200:
201: if (static::isValidImageHandle($handle)) {
202: break;
203: }
204: }
205: }
206: }
207:
208: if (!static::isValidImageHandle($handle)) {
209: throw new InvalidImageSourceException("File '{$uri}' appears to be an invalid image source.");
210: }
211:
212: return static::loadFromHandle($handle);
213: }
214:
215: /**
216: * Create and load an image from a string. Format is auto-detected.
217: *
218: * @param string $string Binary data, i.e. from BLOB field in the database
219: * @return \WideImage\Image|\WideImage\PaletteImage|\WideImage\TrueColorImage
220: */
221: public static function loadFromString($string)
222: {
223: if (strlen($string) < 128) {
224: throw new InvalidImageSourceException("String doesn't contain image data.");
225: }
226:
227: $handle = @imagecreatefromstring($string);
228:
229: if (!static::isValidImageHandle($handle)) {
230: $custom_mappers = MapperFactory::getCustomMappers();
231:
232: foreach ($custom_mappers as $mime_type => $mapper_class) {
233: $mapper = MapperFactory::selectMapper(null, $mime_type);
234: $handle = $mapper->loadFromString($string);
235:
236: if (static::isValidImageHandle($handle)) {
237: break;
238: }
239: }
240: }
241:
242: if (!static::isValidImageHandle($handle)) {
243: throw new InvalidImageSourceException("String doesn't contain valid image data.");
244: }
245:
246: return static::loadFromHandle($handle);
247: }
248:
249: /**
250: * Create and load an image from an image handle.
251: *
252: * <b>Note:</b> the resulting image object takes ownership of the passed
253: * handle. When the newly-created image object is destroyed, the handle is
254: * destroyed too, so it's not a valid image handle anymore. In order to
255: * preserve the handle for use after object destruction, you have to call
256: * \WideImage\Image::releaseHandle() on the created image instance prior to its
257: * destruction.
258: *
259: * <code>
260: * $handle = imagecreatefrompng('file.png');
261: * $image = WideImage::loadFromHandle($handle);
262: * </code>
263: *
264: * @param resource $handle A valid GD image resource
265: * @return \WideImage\Image|\WideImage\PaletteImage|\WideImage\TrueColorImage
266: */
267: public static function loadFromHandle($handle)
268: {
269: if (!static::isValidImageHandle($handle)) {
270: throw new InvalidImageSourceException("Handle is not a valid GD image resource.");
271: }
272:
273: if (imageistruecolor($handle)) {
274: return new TrueColorImage($handle);
275: }
276:
277: return new PaletteImage($handle);
278: }
279:
280: /**
281: * This method loads a file from the $_FILES array. The image format is auto-detected.
282: *
283: * You only have to pass the field name as the parameter. For array fields, this function will
284: * return an array of image objects, unless you specify the $index parameter, which will
285: * load the desired image.
286: *
287: * @param $field_name Name of the key in $_FILES array
288: * @param int $index The index of the file to load (if the input field is an array)
289: * @return \WideImage\Image The loaded image
290: */
291: public static function loadFromUpload($field_name, $index = null)
292: {
293: if (!array_key_exists($field_name, $_FILES)) {
294: throw new InvalidImageSourceException("Upload field '{$field_name}' doesn't exist.");
295: }
296:
297: if (is_array($_FILES[$field_name]['tmp_name'])) {
298: if (isset($_FILES[$field_name]['tmp_name'][$index])) {
299: $filename = $_FILES[$field_name]['tmp_name'][$index];
300: } else {
301: $result = array();
302:
303: foreach ($_FILES[$field_name]['tmp_name'] as $idx => $tmp_name) {
304: $result[$idx] = static::loadFromFile($tmp_name);
305: }
306:
307: return $result;
308: }
309: } else {
310: $filename = $_FILES[$field_name]['tmp_name'];
311: }
312:
313: if (!file_exists($filename)) {
314: throw new InvalidImageSourceException("Uploaded file doesn't exist.");
315: }
316:
317: return static::loadFromFile($filename);
318: }
319:
320: /**
321: * Factory method for creating a palette image
322: *
323: * @param int $width
324: * @param int $height
325: * @return \WideImage\PaletteImage
326: */
327: public static function createPaletteImage($width, $height)
328: {
329: return PaletteImage::create($width, $height);
330: }
331:
332: /**
333: * Factory method for creating a true-color image
334: *
335: * @param int $width
336: * @param int $height
337: * @return \WideImage\TrueColorImage
338: */
339: public static function createTrueColorImage($width, $height)
340: {
341: return TrueColorImage::create($width, $height);
342: }
343:
344: /**
345: * Check whether the given handle is a valid GD resource
346: *
347: * @param mixed $handle The variable to check
348: * @return bool
349: */
350: public static function isValidImageHandle($handle)
351: {
352: return ($handle instanceof \GdImage || (is_resource($handle) && get_resource_type($handle) == 'gd'));
353: }
354:
355: /**
356: * Throws exception if the handle isn't a valid GD resource
357: *
358: * @param mixed $handle The variable to check
359: */
360: public static function assertValidImageHandle($handle)
361: {
362: if (!static::isValidImageHandle($handle)) {
363: throw new InvalidImageHandleException("{$handle} is not a valid image handle.");
364: }
365: }
366: }
367:
368: WideImage::checkGD();
369:
370: WideImage::registerCustomMapper('\\WideImage\\Mapper\\BMP', 'image/bmp', 'bmp');
371: WideImage::registerCustomMapper('\\WideImage\\Mapper\\TGA', 'image/tga', 'tga');
372: