Kundello

Untitled

Feb 26th, 2018
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.93 KB | None | 0 0
  1. <?php
  2. declare(strict_types = 1);
  3.  
  4. namespace App\Services\Character;
  5.  
  6. use App\Exceptions\RuntimeException;
  7. use App\Exceptions\User\Character\InvalidImageSizeException;
  8.  
  9. /**
  10. * Class Skin
  11. * Produces processing and cutting images of skin and cloak.
  12. *
  13. * @author D3lph1 <[email protected]>
  14. * @package App\Services\Character
  15. */
  16. class Skin
  17. {
  18. /**
  19. * @var null|resource
  20. */
  21. private $skin = null;
  22.  
  23. /**
  24. * @var null|resource
  25. */
  26. private $cloak = null;
  27.  
  28. /**
  29. * @var int Aspect ratio of the skin image.
  30. */
  31. private $skinRatio = 1;
  32.  
  33. /**
  34. * @var int Aspect ratio of the cloak image.
  35. */
  36. private $cloakRatio = 1;
  37.  
  38. /**
  39. * Skin constructor.
  40. *
  41. * @param string $player
  42. */
  43. public function __construct(string $player)
  44. {
  45. $skinPath = self::getSkinPath($player);
  46. if (!$skinPath) {
  47. $skinPath = $local = config('l-shop.profile.skins.default');
  48. }
  49. $cloakPath = self::getCloakPath($player);
  50.  
  51. $this->loadSkin($skinPath);
  52. if ($cloakPath) {
  53. $this->loadCloak($cloakPath);
  54. }
  55. }
  56.  
  57. public function __destructor()
  58. {
  59. if (!is_null($this->skin)) {
  60. imagedestroy($this->skin);
  61. }
  62. if (!is_null($this->cloak)) {
  63. imagedestroy($this->cloak);
  64. }
  65. }
  66.  
  67. /**
  68. * Load new skin.
  69. *
  70. * @param string $file Path or URL to the cloak.
  71. */
  72. public function loadSkin(string $file): void
  73. {
  74. if (!is_null($this->skin)) {
  75. imagedestroy($this->skin);
  76. }
  77.  
  78. if (($this->skin = imagecreatefrompng($file)) === false) {
  79. throw new RuntimeException("Unable to load skin");
  80. }
  81. if (!$this->validSkin()) {
  82. throw new InvalidImageSizeException("Invalid skin format");
  83. }
  84. }
  85.  
  86. /**
  87. * Load new cloak.
  88. *
  89. * @param string $file Path or URL to the cloak.
  90. */
  91. public function loadCloak(string $file): void
  92. {
  93. if (!is_null($this->cloak)) {
  94. imagedestroy($this->cloak);
  95. }
  96.  
  97. if (($this->cloak = imagecreatefrompng($file)) === false) {
  98. throw new RuntimeException("Unable to load cloak");
  99. }
  100.  
  101. if (!$this->validCloak()) {
  102. throw new InvalidImageSizeException("Invalid cloak format");
  103. }
  104. }
  105.  
  106. /**
  107. * Return image width.
  108. *
  109. * @param resource $image Image.
  110. *
  111. * @return int Image width in pixels.
  112. */
  113. private function width($image): int
  114. {
  115. if (!is_null($image)) {
  116. return imagesx($image);
  117. }
  118.  
  119. throw new RuntimeException("File not load");
  120. }
  121.  
  122. /**
  123. * Return image height.
  124. *
  125. * @param resource $image Image.
  126. *
  127. * @return int Image height in pixels.
  128. */
  129. private function height($image): int
  130. {
  131. if ($image != null) {
  132. return imagesy($image);
  133. }
  134.  
  135. throw new RuntimeException("File not load");
  136. }
  137.  
  138. /**
  139. * Checks whether the skin is valid.
  140. */
  141. protected function validSkin(): bool
  142. {
  143. $this->skinRatio = (int)($this->width($this->skin) / 64);
  144.  
  145. $validWidth = $this->width($this->skin) / $this->skinRatio === 64;
  146. $validHeight = $this->height($this->skin) / $this->skinRatio === 32;
  147.  
  148. return ($validWidth && $validHeight) ? true : false;
  149. }
  150.  
  151. /**
  152. * Checks whether the cloak is valid.
  153. */
  154. protected function validCloak(): bool
  155. {
  156. $this->cloakRatio = (int)($this->width($this->cloak) / 64);
  157.  
  158. $validWidth = 0;
  159. $validHeight = 0;
  160.  
  161. if ($this->cloakRatio != 0) {
  162. $validWidth = $this->width($this->cloak) / $this->cloakRatio === 64;
  163. $validHeight = $this->height($this->cloak) / $this->cloakRatio === 32;
  164. }
  165.  
  166. if (!($validWidth && $validHeight)) {
  167. $this->cloakRatio = (int)($this->width($this->cloak) / 17);
  168.  
  169. $validWidth = $this->width($this->cloak) / $this->cloakRatio === 22;
  170. $validHeight = $this->height($this->cloak) / $this->cloakRatio === 17;
  171. }
  172.  
  173. return ($validWidth && $validHeight) ? true : false;
  174. }
  175.  
  176. private function getBackground($width, $height, $ratio, $transparent = true, $r = 233, $g = 233, $b = 233)
  177. {
  178. $image = imagecreatetruecolor($width * $ratio, $height * $ratio);
  179. $background = imagecolorallocate($image, $r, $g, $b);
  180. imagefilledrectangle($image, 0, 0, $width * $ratio, $height * $ratio, $background);
  181.  
  182. if ($transparent) {
  183. imageColorTransparent($image, $background);
  184. }
  185.  
  186. return $image;
  187. }
  188.  
  189. /**
  190. * Builds the front of the head.
  191. */
  192. public function getFrontHead(): ReadySkin
  193. {
  194. $newImage = $this->getBackground(8, 8, $this->skinRatio);
  195.  
  196. // Face
  197. imagecopy($newImage, $this->skin, 0, 0, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio);
  198. // Area around the head
  199. $this->imageCopyAlpha($newImage, $this->skin, 0, 0, 40 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, imagecolorat($this->skin, 63 * $this->skinRatio, 0));
  200.  
  201. return new ReadySkin($newImage);
  202. }
  203.  
  204. /**
  205. * Builds the back of the head.
  206. */
  207. public function getBackHead(): ReadySkin
  208. {
  209. $newImage = $this->getBackground(8, 8, $this->skinRatio);
  210.  
  211. // Face
  212. imagecopy($newImage, $this->skin, 0, 0, 24 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio);
  213. // Area around the head
  214. $this->imageCopyAlpha($newImage, $this->skin, 0, 0, 56 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, imagecolorat($this->skin, 63 * $this->skinRatio, 0));
  215.  
  216. return new ReadySkin($newImage);
  217. }
  218.  
  219. /**
  220. * Builds the front of the skin.
  221. */
  222. public function getFrontSkin(): ReadySkin
  223. {
  224. $newImage = $this->getBackground(16, 32, $this->skinRatio);
  225.  
  226. // Face
  227. imagecopy($newImage, $this->skin, 4 * $this->skinRatio, 0, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio);
  228. // Area around the head
  229. $this->imageCopyAlpha($newImage, $this->skin, 4 * $this->skinRatio, 0, 40 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, imagecolorat($this->skin, 63 * $this->skinRatio, 0));
  230. // Body
  231. imagecopy($newImage, $this->skin, 4 * $this->skinRatio, 8 * $this->skinRatio, 20 * $this->skinRatio, 20 * $this->skinRatio, 8 * $this->skinRatio, 12 * $this->skinRatio);
  232. // Legs
  233. imagecopyresampled($newImage, $this->skin, 8 * $this->skinRatio, 20 * $this->skinRatio, (4 * $this->skinRatio + 4 * $this->skinRatio - 1), 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio, 0 - 4 * $this->skinRatio, 12 * $this->skinRatio);
  234. imagecopy($newImage, $this->skin, 4 * $this->skinRatio, 20 * $this->skinRatio, 4 * $this->skinRatio, 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio);
  235. // Arms
  236. imagecopyresampled($newImage, $this->skin, 12 * $this->skinRatio, 8 * $this->skinRatio, (44 * $this->skinRatio + 4 * $this->skinRatio - 1), 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio, 0 - 4 * $this->skinRatio, 12 * $this->skinRatio);
  237. imagecopy($newImage, $this->skin, 0, 8 * $this->skinRatio, 44 * $this->skinRatio, 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio);
  238.  
  239. return new ReadySkin($newImage);
  240. }
  241.  
  242. /**
  243. * Builds the back of the skin.
  244. */
  245. public function getBackSkin(): ReadySkin
  246. {
  247. $newImage = $this->getBackground(16, 32, $this->skinRatio);
  248.  
  249. // Face
  250. imagecopy($newImage, $this->skin, 4 * $this->skinRatio, 0, 24 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio);
  251. // Area around the head
  252. $this->imageCopyAlpha($newImage, $this->skin, 4 * $this->skinRatio, 0, 56 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, 8 * $this->skinRatio, imagecolorat($this->skin, 63 * $this->skinRatio, 0));
  253. // Body
  254. imagecopy($newImage, $this->skin, 4 * $this->skinRatio, 8 * $this->skinRatio, 32 * $this->skinRatio, 20 * $this->skinRatio, 8 * $this->skinRatio, 12 * $this->skinRatio);
  255. // Legs
  256. imagecopy($newImage, $this->skin, 8 * $this->skinRatio, 20 * $this->skinRatio, 12 * $this->skinRatio, 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio);
  257. imagecopyresampled($newImage, $this->skin, 4 * $this->skinRatio, 20 * $this->skinRatio, (12 * $this->skinRatio + 4 * $this->skinRatio - 1), 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio, 0 - 4 * $this->skinRatio, 12 * $this->skinRatio);
  258. // Arms
  259. imagecopy($newImage, $this->skin, 12 * $this->skinRatio, 8 * $this->skinRatio, 52 * $this->skinRatio, 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio);
  260. imagecopyresampled($newImage, $this->skin, 0 * $this->skinRatio, 8 * $this->skinRatio, (52 * $this->skinRatio + 4 * $this->skinRatio - 1), 20 * $this->skinRatio, 4 * $this->skinRatio, 12 * $this->skinRatio, 0 - 4 * $this->skinRatio, 12 * $this->skinRatio);
  261.  
  262. return new ReadySkin($newImage);
  263. }
  264.  
  265. /**
  266. * Builds the front of the cloak.
  267. */
  268. public function getFrontCloak(): ?ReadySkin
  269. {
  270. if (is_null($this->cloak)) {
  271. return null;
  272. }
  273.  
  274. $newImage = $this->getBackground(10, 16, $this->cloakRatio);
  275.  
  276. imagecopy($newImage, $this->cloak, 0, 0, 12 * $this->cloakRatio, 1 * $this->cloakRatio, 10 * $this->cloakRatio, 16 * $this->cloakRatio);
  277.  
  278. return new ReadySkin($newImage);
  279. }
  280.  
  281. /**
  282. * Builds the back of the cloak.
  283. */
  284. public function getBackCloak(): ?ReadySkin
  285. {
  286. if (is_null($this->cloak)) {
  287. return null;
  288. }
  289.  
  290. $newImage = $this->getBackground(10, 16, $this->cloakRatio);
  291.  
  292. imagecopy($newImage, $this->cloak, 0, 0, 1 * $this->cloakRatio, 1 * $this->cloakRatio, 10 * $this->cloakRatio, 16 * $this->cloakRatio);
  293.  
  294. return new ReadySkin($newImage);
  295. }
  296.  
  297.  
  298. private function imageCopyAlpha($dst, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h, $bg): void
  299. {
  300. for ($i = 0; $i < $w; $i++) {
  301. for ($j = 0; $j < $h; $j++) {
  302. $rgb = imagecolorat($src, $src_x + $i, $src_y + $j);
  303.  
  304. if (($rgb & 0xFFFFFF) === ($bg & 0xFFFFFF)) {
  305. $alpha = 127;
  306. } else {
  307. $colors = imagecolorsforindex($src, $rgb);
  308. $alpha = $colors["alpha"];
  309. }
  310. imagecopymerge($dst, $src, $dst_x + $i, $dst_y + $j, $src_x + $i, $src_y + $j, 1, 1, 100 - (($alpha / 127) * 100));
  311. }
  312. }
  313. }
  314.  
  315. /**
  316. * Return path to player skin.
  317. *
  318. * @param string $player Player username.
  319. *
  320. * @return bool|string Path to skin or null if skin does not exists.
  321. */
  322. public static function getSkinPath(string $player): ?string
  323. {
  324. $file = config('l-shop.profile.skins.path') . DIRECTORY_SEPARATOR . $player . '.png';
  325.  
  326. if (file_exists($file)) {
  327. return $file;
  328. }
  329.  
  330. return null;
  331. }
  332.  
  333. /**
  334. * Return path to player cloak.
  335. *
  336. * @param string $player Player username.
  337. *
  338. * @return null|string Path to cloak or null if cloak does not exists.
  339. */
  340. public static function getCloakPath(string $player): ?string
  341. {
  342. $file = config('l-shop.profile.cloaks.path') . DIRECTORY_SEPARATOR . $player . '.png';
  343.  
  344. if (file_exists($file)) {
  345. return $file;
  346. }
  347.  
  348. return null;
  349. }
  350. }
Add Comment
Please, Sign In to add comment