PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Player-Avatar/classes/Char_Image.php

https://github.com/Billiam/Minecraft-Overviewer-Addons
PHP | 388 lines | 198 code | 48 blank | 142 comment | 21 complexity | 3211b0ac9bd0b459970818f5b2b6e1d4 MD5 | raw file
  1. <?php
  2. /**
  3. * Character image generator
  4. *
  5. * @author billiam
  6. * @version 0.1
  7. */
  8. class Char_Image {
  9. const LOC_BACK = 'back';
  10. const LOC_FRONT = 'front';
  11. const LOC_OUTSIDE = 'outside';
  12. const LOC_LEFT = 'left';
  13. const LOC_RIGHT = 'right';
  14. const LOC_TOP = 'top';
  15. const LOC_BOTTOM = 'bottom';
  16. const FORMAT_FACE = 'face';
  17. const FORMAT_FLAT = 'flat';
  18. public static $formats = array(
  19. self::FORMAT_FACE,
  20. self::FORMAT_FLAT,
  21. );
  22. /**
  23. * Coordinates of body parts in character PNG
  24. * (incomplete)
  25. *
  26. * @var array
  27. * Format:
  28. * <body part> => array (
  29. * <location> => array(x, y, width, height)
  30. * )
  31. */
  32. public static $coords = array(
  33. 'head' => array(
  34. self::LOC_RIGHT => array(0,8,8,8),
  35. self::LOC_FRONT => array(8,8,8,8),
  36. self::LOC_LEFT => array(16, 8,8,8),
  37. self::LOC_BACK => array(24,8,8,8),
  38. ),
  39. 'body' => array(
  40. self::LOC_FRONT => array(20,20,8,12),
  41. ),
  42. 'arms' => array(
  43. self::LOC_FRONT => array(44,20,4,12),
  44. ),
  45. 'legs' => array(
  46. self::LOC_FRONT => array(4,20,4,12),
  47. ),
  48. 'helmet' => array(
  49. self::LOC_FRONT => array(40,8,8,8),
  50. )
  51. );
  52. /**
  53. * @var resource
  54. */
  55. protected $_skin;
  56. /**
  57. * Current image scale
  58. * @var int
  59. */
  60. protected $_scale = 1;
  61. /**
  62. * Border color
  63. *
  64. * Formats:
  65. * #aaffcc, afc
  66. * @var string
  67. */
  68. protected $_borderColor;
  69. /**
  70. * Border width
  71. *
  72. * @var int
  73. */
  74. protected $_borderWidth = 0;
  75. /**
  76. * @var array
  77. */
  78. protected $_parts = array();
  79. /**
  80. * @var string
  81. */
  82. protected $_format = self::FORMAT_FACE;
  83. /**
  84. * Create image from input file
  85. *
  86. * @param string|resource $skin
  87. */
  88. public function __construct($skin)
  89. {
  90. if(is_string($skin)) {
  91. $this->_skin = imagecreatefromstring($skin);
  92. } elseif(is_resource($skin)) {
  93. $this->_skin = $skin;
  94. }
  95. }
  96. /**
  97. * Destroy skin image on destruct
  98. */
  99. public function __destruct()
  100. {
  101. imagedestroy($this->_skin);
  102. }
  103. /**
  104. * Add a border around output player image
  105. *
  106. * @param string $color
  107. * @param int $width
  108. * @return Char_Image
  109. */
  110. public function setBorder($color, $width)
  111. {
  112. $color = self::htmlToRgb($color);
  113. if ($color && is_numeric($width) && $width > 0 && $width < 100) {
  114. $width = (int)$width;
  115. $this->_borderWidth = $width;
  116. $this->_borderColor = $color;
  117. }
  118. return $this;
  119. }
  120. /**
  121. * Set format for displayed player avatars
  122. *
  123. * @see self::$formats
  124. * @param string $format
  125. * @return Char_Image
  126. */
  127. public function setFormat($format)
  128. {
  129. if (in_array($format, self::$formats)) {
  130. $this->_format = $format;
  131. }
  132. return $this;
  133. }
  134. /**
  135. * Set the image scale
  136. *
  137. * @param int $scale
  138. *
  139. * @return Char_Image
  140. */
  141. public function setScale($scale)
  142. {
  143. $this->_scale = (int)$scale;
  144. return $this;
  145. }
  146. /**
  147. * Get the image scale
  148. * @return int
  149. */
  150. public function getScale()
  151. {
  152. return $this->_scale;
  153. }
  154. /**
  155. * Does player skin include a helmet?
  156. *
  157. * @return bool
  158. */
  159. public function hasHelmet()
  160. {
  161. //check for transparent pixels in region outside hat area
  162. $index = imagecolorat($this->_skin, 25, 0);
  163. $alpha = ($index & 0x7F000000) >> 24;
  164. return $alpha == 127;
  165. }
  166. /**
  167. * Display player as flat, front view
  168. *
  169. * @param bool $fetch If true, image output will be returned as a string
  170. * @return void|string
  171. */
  172. public function _showFlat($fetch = false)
  173. {
  174. $face = self::$coords['head'][self::LOC_FRONT];
  175. $arms = self::$coords['arms'][self::LOC_FRONT];
  176. $body = self::$coords['body'][self::LOC_FRONT];
  177. $legs = self::$coords['legs'][self::LOC_FRONT];
  178. $helmet = self::$coords['helmet'][self::LOC_FRONT];
  179. $srcWidth = $arms[2] * 2 + $body[2];
  180. $srcHeight = $legs[3] + $body[3] + $face[3] + 1;
  181. $image = $this->_createImage($srcWidth, $srcHeight);
  182. //Place and scale image/body parts
  183. if ( $this->_borderWidth) {
  184. $color = imagecolorallocate($image, $this->_borderColor[0], $this->_borderColor[1], $this->_borderColor[2]);
  185. //border behind trunk
  186. imagefilledrectangle($image, $arms[2] * $this->_scale, $this->_scale, ($arms[2] + $body[2]) * $this->_scale + $this->_borderWidth * 2 - 1, ($face[3] + $body[3] + $legs[3] + 1) * $this->_scale + $this->_borderWidth * 2 - 1, $color);
  187. imagefilledrectangle($image, 0, ($face[3] + 1) * $this->_scale, ($arms[2] * 2 + $body[2]) * $this->_scale + $this->_borderWidth * 2, (1 + $face[3] + $arms[3]) * $this->_scale + $this->_borderWidth * 2 - 1, $color);
  188. }
  189. $this->_copyTo($image, $this->_skin, $face, $arms[2], 1);
  190. $this->_copyTo($image, $this->_skin, $arms, 0, $face[3] + 1);
  191. $this->_copyTo($image, $this->_skin, $arms, $arms[2] + $body[2], $face[3] + 1, true);
  192. $this->_copyTo($image, $this->_skin, $body, $arms[2], $face[3] + 1);
  193. $this->_copyTo($image, $this->_skin, $legs, $arms[2], $face[3] + $body[3] + 1);
  194. $this->_copyTo($image, $this->_skin, $legs, $arms[2] + $legs[2], $face[3] + $body[3] + 1, true);
  195. if ( $this->hasHelmet()) {
  196. $this->_copyTo($image, $this->_skin, $helmet, $arms[2] - 1, 0, false, 10/8);
  197. }
  198. return self::getPngString($image);
  199. }
  200. /**
  201. * Display player face only
  202. *
  203. * @return void
  204. */
  205. protected function _showFace()
  206. {
  207. $face = self::$coords['head'][self::LOC_FRONT];
  208. $helmet = self::$coords['helmet'][self::LOC_FRONT];
  209. $image = $this->_createImage($face[2] + 2, $face[3] + 2);
  210. //Place and scale image/body parts
  211. if ( $this->_borderWidth) {
  212. $color = imagecolorallocate($image, $this->_borderColor[0], $this->_borderColor[1], $this->_borderColor[2]);
  213. imagefilledrectangle($image, $this->_scale, $this->_scale, ($face[3] + 1) * $this->_scale + $this->_borderWidth * 2 - 1, ($face[3] + 1) * $this->_scale + $this->_borderWidth * 2 - 1, $color);
  214. }
  215. $this->_copyTo($image, $this->_skin, $face, 1, 1);
  216. if ( $this->hasHelmet()) {
  217. $this->_copyTo($image, $this->_skin, $helmet, 0, 0, false, 10/8);
  218. }
  219. return self::getPngString($image);
  220. }
  221. /**
  222. * Copy portion of skin image to destination output image
  223. *
  224. * @param resource $dest
  225. * @param resource $src
  226. * @param array $coords
  227. * @param int $destX
  228. * @param int $destY
  229. * @param bool $flip Flip image horizontally
  230. * @param int $partScale Body part-specific scale (ex: helmets)
  231. * @return void
  232. */
  233. protected function _copyTo($dest, $src, $coords, $destX, $destY, $flip=false, $partScale = 1)
  234. {
  235. $flipMult = 1;
  236. $widthOffset = 0;
  237. if ($flip) {
  238. $flipMult = -1;
  239. $widthOffset = $coords[2] -1;
  240. }
  241. imagecopyresampled(
  242. $dest, //destination
  243. $src, //source
  244. $destX * $this->_scale + $this->_borderWidth, //destination x
  245. $destY * $this->_scale + $this->_borderWidth, //destination y
  246. $coords[0] + $widthOffset, //source x
  247. $coords[1], //source y
  248. $coords[2] * $this->_scale * $partScale, //destination width
  249. $coords[3] * $this->_scale * $partScale, //destination height
  250. $coords[2] * $flipMult,//source width
  251. $coords[3] //source height
  252. );
  253. }
  254. /**
  255. * Get image string for an image resource
  256. *
  257. * @static
  258. * @param resource $image
  259. * @return void
  260. */
  261. public static function getPngString($image)
  262. {
  263. ob_start();
  264. imagepng($image, null, 9);
  265. $returnImage = ob_get_contents();
  266. ob_end_clean();
  267. return $returnImage;
  268. }
  269. /**
  270. * Output image to client
  271. *
  272. * @static
  273. * @param string|resource $image
  274. * @return void
  275. */
  276. public static function outputImage($image)
  277. {
  278. header('Content-type: image/png');
  279. if (is_resource($image)) {
  280. $image = self::getPngString($image);
  281. }
  282. header('Content-type: image/png');
  283. echo $image;
  284. }
  285. /**
  286. * Generate a player image resource
  287. *
  288. * @param int $width
  289. * @param int $height
  290. * @return resource
  291. */
  292. protected function _createImage($width, $height)
  293. {
  294. $image = imagecreatetruecolor($width * $this->_scale + $this->_borderWidth * 2, $height * $this->_scale + $this->_borderWidth * 2);
  295. imagesavealpha($image, true);
  296. $transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
  297. imagefill($image, 0, 0, $transparent);
  298. imagealphablending($image,true);
  299. return $image;
  300. }
  301. /**
  302. * @param bool $return
  303. * @return string|void
  304. */
  305. public function getImage()
  306. {
  307. if ( $this->_format == self::FORMAT_FACE) {
  308. $image = $this->_showFace();
  309. } else {
  310. $image = $this->_showFlat();
  311. }
  312. return $image;
  313. }
  314. /*public function showIsometric()
  315. {
  316. }*/
  317. /**
  318. * Convert html code to rgb array
  319. *
  320. * @static
  321. * @param $color
  322. * @return array|bool
  323. */
  324. public static function htmlToRgb($color)
  325. {
  326. if ($color[0] == '#')
  327. $color = substr($color, 1);
  328. if (strlen($color) == 6)
  329. list($r, $g, $b) = array($color[0].$color[1],
  330. $color[2].$color[3],
  331. $color[4].$color[5]);
  332. elseif (strlen($color) == 3)
  333. list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]);
  334. else
  335. return false;
  336. $r = hexdec($r); $g = hexdec($g); $b = hexdec($b);
  337. return array($r, $g, $b);
  338. }
  339. }