qa-util-image.php 5.8 KB
Newer Older
Gideon Greenspan committed
1 2 3 4 5 6 7
<?php

/*
	Question2Answer (c) Gideon Greenspan

	http://www.question2answer.org/

Scott Vivian committed
8

Gideon Greenspan committed
9 10 11 12 13 14 15 16 17
	File: qa-include/qa-util-image.php
	Version: See define()s at top of qa-include/qa-base.php
	Description: Some useful image-related functions (using GD)


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.
Scott Vivian committed
18

Gideon Greenspan committed
19 20 21 22 23 24 25
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/
Scott Vivian committed
26

Gideon Greenspan committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
	if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
		header('Location: ../');
		exit;
	}


	function qa_has_gd_image()
/*
	Return true if PHP has the GD extension installed and it appears to be usable
*/
	{
		return extension_loaded('gd') && function_exists('imagecreatefromstring') && function_exists('imagejpeg');
	}


	function qa_image_file_too_big($imagefile, $size=null)
/*
	Check if the image in $imagefile will be too big for PHP/GD to process given memory usage and limits
	Pass the width and height limit beyond which the image will require scaling in $size (if any)
	Returns false if the image will fit fine, otherwise a safe estimate of the factor the image should be sized by
*/
	{
		if (function_exists('memory_get_usage')) {
			$gotbytes=trim(@ini_get('memory_limit'));
Scott Vivian committed
51

Gideon Greenspan committed
52 53 54 55 56 57 58 59
			switch (strtolower(substr($gotbytes, -1))) {
				case 'g':
					$gotbytes*=1024;
				case 'm':
					$gotbytes*=1024;
				case 'k':
					$gotbytes*=1024;
			}
Scott Vivian committed
60

Gideon Greenspan committed
61 62
			if ($gotbytes>0) { // otherwise we clearly don't know our limit
				$gotbytes=($gotbytes-memory_get_usage())*0.9; // safety margin of 10%
Scott Vivian committed
63

Gideon Greenspan committed
64 65 66
				$needbytes=filesize($imagefile); // memory to store file contents

				$imagesize=@getimagesize($imagefile);
Scott Vivian committed
67

Gideon Greenspan committed
68 69 70
				if (is_array($imagesize)) { // if image can't be parsed, don't worry about anything else
					$width=$imagesize[0];
					$height=$imagesize[1];
Gideon Greenspan committed
71 72
					$bits=isset($imagesize['bits']) ? $imagesize['bits'] : 8; // these elements can be missing (PHP bug) so assume this as default
					$channels=isset($imagesize['channels']) ? $imagesize['channels'] : 3; // for more info: http://gynvael.coldwind.pl/?id=223
Scott Vivian committed
73

Gideon Greenspan committed
74
					$needbytes+=$width*$height*$bits*$channels/8*2; // memory to load original image
Scott Vivian committed
75

Gideon Greenspan committed
76 77 78
					if (isset($size) && qa_image_constrain($width, $height, $size)) // memory for constrained image
						$needbytes+=$width*$height*3*2; // *2 here and above based on empirical tests
				}
Scott Vivian committed
79

Gideon Greenspan committed
80 81 82 83
				if ($needbytes>$gotbytes)
					return sqrt($gotbytes/($needbytes*1.5)); // additional 50% safety margin since JPEG quality may change
			}
		}
Scott Vivian committed
84

Gideon Greenspan committed
85 86
		return false;
	}
Scott Vivian committed
87 88


Gideon Greenspan committed
89
	function qa_image_constrain_data($imagedata, &$width, &$height, $maxwidth, $maxheight=null)
Gideon Greenspan committed
90
/*
Gideon Greenspan committed
91 92 93
	Given $imagedata containing JPEG/GIF/PNG data, constrain it proportionally to fit in $maxwidth x $maxheight.
	Return the new image data (will always be a JPEG), and set the $width and $height variables.
	If $maxheight is omitted or set to null, assume it to be the same as $maxwidth.
Gideon Greenspan committed
94 95 96
*/
	{
		$inimage=@imagecreatefromstring($imagedata);
Scott Vivian committed
97

Gideon Greenspan committed
98
		if (is_resource($inimage)) {
Gideon Greenspan committed
99 100
			$width=imagesx($inimage);
			$height=imagesy($inimage);
Scott Vivian committed
101

Gideon Greenspan committed
102
			// always call qa_gd_image_resize(), even if the size is the same, to take care of possible PNG transparency
Gideon Greenspan committed
103
			qa_image_constrain($width, $height, $maxwidth, $maxheight);
Gideon Greenspan committed
104
			qa_gd_image_resize($inimage, $width, $height);
Gideon Greenspan committed
105
		}
Scott Vivian committed
106

Gideon Greenspan committed
107 108 109 110 111
		if (is_resource($inimage)) {
			$imagedata=qa_gd_image_jpeg($inimage);
			imagedestroy($inimage);
			return $imagedata;
		}
Scott Vivian committed
112 113

		return null;
Gideon Greenspan committed
114
	}
Scott Vivian committed
115 116


Gideon Greenspan committed
117
	function qa_image_constrain(&$width, &$height, $maxwidth, $maxheight=null)
Gideon Greenspan committed
118
/*
Gideon Greenspan committed
119 120 121
	Given and $width and $height, return true if those need to be contrained to fit in $maxwidth x $maxheight.
	If so, also set $width and $height to the new proportionally constrained values.
	If $maxheight is omitted or set to null, assume it to be the same as $maxwidth.
Gideon Greenspan committed
122 123
*/
	{
Gideon Greenspan committed
124 125
		if (!isset($maxheight))
			$maxheight=$maxwidth;
Scott Vivian committed
126

Gideon Greenspan committed
127 128
		if (($width>$maxwidth) || ($height>$maxheight)) {
			$multiplier=min($maxwidth/$width, $maxheight/$height);
Gideon Greenspan committed
129 130 131 132 133
			$width=floor($width*$multiplier);
			$height=floor($height*$multiplier);

			return true;
		}
Scott Vivian committed
134

Gideon Greenspan committed
135 136
		return false;
	}
Scott Vivian committed
137 138


Gideon Greenspan committed
139 140 141 142 143 144 145 146 147
	function qa_gd_image_resize(&$image, $width, $height)
/*
	Resize the GD $image to $width and $height, setting it to null if the resize failed
*/
	{
		$oldimage=$image;
		$image=null;

		$newimage=imagecreatetruecolor($width, $height);
Gideon Greenspan committed
148 149 150
		$white=imagecolorallocate($newimage, 255, 255, 255); // fill with white first in case we have a transparent PNG
		imagefill($newimage, 0, 0, $white);

Gideon Greenspan committed
151 152 153 154 155
		if (is_resource($newimage)) {
			if (imagecopyresampled($newimage, $oldimage, 0, 0, 0, 0, $width, $height, imagesx($oldimage), imagesy($oldimage)))
				$image=$newimage;
			else
				imagedestroy($newimage);
Scott Vivian committed
156
		}
Gideon Greenspan committed
157 158 159

		imagedestroy($oldimage);
	}
Scott Vivian committed
160 161


Gideon Greenspan committed
162 163 164 165 166 167 168 169 170
	function qa_gd_image_jpeg($image, $output=false)
/*
	Return the JPEG data for GD $image, also echoing it to browser if $output is true
*/
	{
		ob_start();
		imagejpeg($image, null, 90);
		return $output ? ob_get_flush() : ob_get_clean();
	}
Scott Vivian committed
171 172


Gideon Greenspan committed
173 174 175 176 177 178
	function qa_gd_image_formats()
/*
	Return an array of strings listing the image formats that are supported
*/
	{
		$imagetypebits=imagetypes();
Scott Vivian committed
179

Gideon Greenspan committed
180 181 182 183 184
		$bitstrings=array(
			IMG_GIF => 'GIF',
			IMG_JPG => 'JPG',
			IMG_PNG => 'PNG',
		);
Scott Vivian committed
185

Gideon Greenspan committed
186 187 188
		foreach (array_keys($bitstrings) as $bit)
			if (!($imagetypebits&$bit))
				unset($bitstrings[$bit]);
Scott Vivian committed
189

Gideon Greenspan committed
190 191 192 193 194 195 196
		return $bitstrings;
	}


/*
	Omit PHP closing tag to help avoid accidental output
*/