PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/system/jch_optimize/cache/css-sprite-gen.inc.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 684 lines | 477 code | 69 blank | 138 comment | 112 complexity | 7e44d608dc875e67bc62e1d1589461a7 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, MIT, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * This is a slightly modified version of the original class bearing the same file and
  4. * class name. This is originally from the online css sprite generator found at
  5. * http://spritegen.website-performance.org/
  6. *
  7. * @copyright Copyright (C) 2007-2009, Project Fondue (Ed Eliot, Stuart Colville
  8. * & Cyril Doussin). All rights reserved.
  9. * @license Software License Agreement (BSD License)
  10. */
  11. class CssSpriteGen {
  12. protected $sImageLibrary;
  13. protected $aImageTypes = array();
  14. protected $aFormValues = array();
  15. protected $aFormErrors = array();
  16. protected $sZipFolder = '';
  17. protected $bTransparent;
  18. protected $sCss;
  19. protected $sTempSpriteName;
  20. protected $bValidImages;
  21. protected $aBackground = array();
  22. protected $aPosition = array();
  23. public function __construct($ImageLibrary, $aFormValues) {
  24. if ($ImageLibrary == 'imagick') {
  25. $this->sImageLibrary = 'imagick';
  26. // what image formats does the installed version of Imagick support
  27. // probably overkill to call as PNG, GIF, JPEG surely supported but done for completeness
  28. try {
  29. // Fixes #473915 as queryformats no longer works as a static method.
  30. $oImagick = new Imagick();
  31. $aImageFormats = $oImagick->queryFormats();
  32. } catch (ImagickException $e) {
  33. error_log($e->getMessage());
  34. }
  35. // store supported formats for populating drop downs etc later
  36. if (in_array('PNG', $aImageFormats)) {
  37. $this->aImageTypes[] = 'PNG';
  38. }
  39. if (in_array('GIF', $aImageFormats)) {
  40. $this->aImageTypes[] = 'GIF';
  41. }
  42. if (in_array('JPG', $aImageFormats)) {
  43. $this->aImageTypes[] = 'JPG';
  44. }
  45. } else {
  46. $this->sImageLibrary = 'gd';
  47. // get info about installed GD library to get image types (some versions of GD don't include GIF support)
  48. $oGD = gd_info();
  49. // store supported formats for populating drop downs etc later
  50. if (@$oGD['PNG Support']) {
  51. $this->aImageTypes[] = 'PNG';
  52. }
  53. if (@$oGD['GIF Create Support']) {
  54. $this->aImageTypes[] = 'GIF';
  55. }
  56. if (@$oGD['JPG Support']) {
  57. $this->aImageTypes[] = 'JPG';
  58. }
  59. }
  60. $this->aFormValues = $aFormValues;
  61. $this->aFormValues = array(
  62. 'path'=> '',
  63. 'sub' => '',
  64. 'file-regex' => '',
  65. 'wrap-columns' => $aFormValues['wrap-columns'],
  66. 'build-direction' => $aFormValues['build-direction'],
  67. 'use-transparency' => 'on',
  68. 'use-optipng' => '',
  69. 'vertical-offset' => 50,
  70. 'horizontal-offset' => 50,
  71. 'background' => '',
  72. 'image-output' => $aFormValues['image-output'],
  73. 'image-num-colours' => 'true-colour',
  74. 'image-quality' => 75,
  75. 'width-resize' => 100,
  76. 'height-resize' => 100,
  77. 'ignore-duplicates' => 'merge',
  78. 'class-prefix' => '',
  79. 'selector-prefix' => '',
  80. 'selector-suffix' => '',
  81. 'add-width-height-to-css' => 'off'
  82. );
  83. }
  84. public function GetImageTypes() {
  85. return $this->aImageTypes;
  86. }
  87. public function CreateSprite($aFilePaths) {
  88. // set up variable defaults used when calculating offsets etc
  89. $aFilesInfo = array();
  90. $aFilesMD5 = array();
  91. $bResize = false;
  92. if ($this->aFormValues['build-direction'] == 'horizontal') {
  93. $iRowCount = 1;
  94. $iTotalWidth = 0;
  95. $iTotalHeight = 0;
  96. $aMaxRowHeight = array();
  97. $iMaxVOffset = 0;
  98. } else {
  99. $iColumnCount = 1;
  100. $iTotalWidth = 0;
  101. $iTotalHeight = 0;
  102. $aMaxColumnWidth = array();
  103. $iMaxHOffset = 0;
  104. }
  105. $iMaxWidth = 0;
  106. $iMaxHeight = 0;
  107. $i = 0;
  108. $k = 0;
  109. $bValidImages = false;
  110. $sOutputFormat = strtolower($this->aFormValues['image-output']);
  111. // read the contents of the directory passed
  112. //$oDir = dir($sFolderMD5);
  113. /*******************************************/
  114. /* this section calculates all offsets etc */
  115. /*******************************************/
  116. // loop through directory (files will be processed in whatever the OS's default file ordering scheme is)
  117. // save to array so we can sort alphabetically
  118. /* $aFiles = array();
  119. while (false !== ($sFile = $oDir->read())) {
  120. $aFiles[] = $sFile;
  121. }
  122. sort($aFiles);*/
  123. foreach ($aFilePaths as $sFilePath) {
  124. $sFilePath = plgSystemJCH_Optimize::getFilepath($sFilePath);
  125. // do we want to scale down the source images
  126. // scaling up isn't supported as that would result in poorer quality images
  127. $bResize = ($this->aFormValues['width-resize'] != 100 && $this->aFormValues['height-resize'] != 100);
  128. // grab path information
  129. //$sFilePath = $sFolderMD5.$sFile;
  130. $aPathParts = pathinfo($sFilePath);
  131. $sFile = $aPathParts['basename'];
  132. // are we matching filenames against a regular expression
  133. // if so it's likely not all images from the ZIP file will end up in the generated sprite image
  134. if (!empty($this->aFormValues['file-regex'])) {
  135. // forward slashes should be escaped - it's likely not doing this might be a security risk also
  136. // one might be able to break out and change the modifiers (to for example run PHP code)
  137. $this->aFormValues['file-regex'] = str_replace('/', '\/', $this->aFormValues['file-regex']);
  138. // if the regular expression matches grab the first match and store for use as the class name
  139. if (preg_match('/^'.$this->aFormValues['file-regex'].'$/i', $sFile, $aMatches)) {
  140. $sFileClass = $aMatches[1];
  141. } else {
  142. $sFileClass = '';
  143. }
  144. } else { // not using regular expressions - set the class name to the base part of the filename (excluding extension)
  145. $sFileClass = $aPathParts['basename'];
  146. }
  147. // format the class name - it should only contain certain characters
  148. // this strips out any which aren't
  149. $sFileClass = $this->FormatClassName($sFileClass);
  150. $iImageType = @exif_imagetype($sFilePath);
  151. // if we've got an empty class name then the file wasn't valid and shouldn't be included in the sprite image
  152. // the file also isn't valid if its extension doesn't match one of the image formats supported by the tool
  153. if (
  154. !empty($sFileClass) &&
  155. isset($aPathParts['extension']) &&
  156. in_array(strtoupper($aPathParts['extension']), $this->aImageTypes) &&
  157. in_array($iImageType, array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG)) &&
  158. substr($sFile, 0, 1) != '.'
  159. ) {
  160. // grab the file extension
  161. $sExtension = $aPathParts['extension'];
  162. // get MD5 of file (this can be used to compare if a file's content is exactly the same as another's)
  163. $sFileMD5 = md5(file_get_contents($sFilePath));
  164. // check if this file's MD5 already exists in array of MD5s recorded so far
  165. // if so it's a duplicate of another file in the ZIP
  166. if (($sKey = array_search($sFileMD5, $aFilesMD5)) !== false) {
  167. // do we want to drop duplicate files and merge CSS rules
  168. // if so CSS will end up like .filename1, .filename2 { }
  169. if ($this->aFormValues['ignore-duplicates'] == 'merge') {
  170. if (isset($aFilesInfo[$sKey]['class'])) {
  171. $aFilesInfo[$sKey]['class'] = $aFilesInfo[$sKey]['class'].
  172. $this->aFormValues['selector-suffix'].', '.
  173. $this->aFormValues['selector-prefix'].'.'.
  174. $this->aFormValues['class-prefix'].$sFileClass;
  175. $this->aBackground[$k] = $sKey;
  176. $k++;
  177. continue;
  178. }
  179. }
  180. }else{
  181. $this->aBackground[$k] = $i;
  182. $k++;
  183. }
  184. // add MD5 to array to check future files against
  185. $aFilesMD5[$i] = $sFileMD5;
  186. // store generated class selector details
  187. $aFilesInfo[$i]['class'] = ".{$this->aFormValues['class-prefix']}$sFileClass";
  188. // store file path information and extension
  189. $aFilesInfo[$i]['path'] = $sFilePath;
  190. $aFilesInfo[$i]['ext'] = $sExtension;
  191. // get dimensions of image
  192. $aImageInfo = getimagesize($sFilePath);
  193. $iWidth = $aImageInfo[0];
  194. $iHeight = $aImageInfo[1];
  195. if ($this->aFormValues['build-direction'] == 'horizontal') {
  196. // get the current width of the sprite image - after images processed so far
  197. $iCurrentWidth = $iTotalWidth + $this->aFormValues['horizontal-offset'] + $iWidth;
  198. // store the maximum width reached so far
  199. // if we're on a new column current height might be less than the maximum
  200. if ($iMaxWidth < $iCurrentWidth) {
  201. $iMaxWidth = $iCurrentWidth;
  202. }
  203. } else {
  204. // get the current height of the sprite image - after images processed so far
  205. $iCurrentHeight = $iTotalHeight + $this->aFormValues['vertical-offset'] + $iHeight;
  206. // store the maximum height reached so far
  207. // if we're on a new column current height might be less than the maximum
  208. if ($iMaxHeight < $iCurrentHeight) {
  209. $iMaxHeight = $iCurrentHeight;
  210. }
  211. }
  212. // store the original width and height of the image
  213. // we'll need this later if the image is to be resized
  214. $aFilesInfo[$i]['original-width'] = $iWidth;
  215. $aFilesInfo[$i]['original-height'] = $iHeight;
  216. // store the width and height of the image
  217. // if we're resizing they'll be less than the original
  218. $aFilesInfo[$i]['width'] = $bResize ? round(($iWidth / 100) * $this->aFormValues['width-resize']) : $iWidth;
  219. $aFilesInfo[$i]['height'] = $bResize ? round(($iHeight / 100) * $this->aFormValues['height-resize']) : $iHeight;
  220. if ($this->aFormValues['build-direction'] == 'horizontal') {
  221. // opera (9.0 and below) has a bug which prevents it recognising offsets of less than -2042px
  222. // all subsequent values are treated as -2042px
  223. // if we've hit 2000 pixels and we care about this (as set in the interface) then wrap to a new row
  224. // increment row count and reset current height
  225. if (
  226. ($iTotalWidth + $this->aFormValues['horizontal-offset']) >= 2000 &&
  227. !empty($this->aFormValues['wrap-columns'])
  228. ) {
  229. $iRowCount++;
  230. $iTotalWidth = 0;
  231. }
  232. // if the current image is higher than any other in the current row then set the maximum height to that
  233. // it will be used to set the height of the current row
  234. if ($aFilesInfo[$i]['height'] > $iMaxHeight) {
  235. $iMaxHeight = $aFilesInfo[$i]['height'];
  236. }
  237. // keep track of the height of rows added so far
  238. $aMaxRowHeight[$iRowCount] = $iMaxHeight;
  239. // calculate the current maximum vertical offset so far
  240. $iMaxVOffset = $this->aFormValues['vertical-offset'] * ($iRowCount - 1);
  241. // get the x position of current image in overall sprite
  242. $aFilesInfo[$i]['x'] = $iTotalWidth;
  243. $iTotalWidth += ($aFilesInfo[$i]['width'] + $this->aFormValues['horizontal-offset']);
  244. // get the y position of current image in overall sprite
  245. if ($iRowCount == 1) {
  246. $aFilesInfo[$i]['y'] = 0;
  247. } else {
  248. $aFilesInfo[$i]['y'] = (
  249. $this->aFormValues['vertical-offset'] *
  250. ($iRowCount - 1) +
  251. (array_sum($aMaxRowHeight) - $aMaxRowHeight[$iRowCount])
  252. );
  253. }
  254. $aFilesInfo[$i]['currentCombinedWidth'] = $iTotalWidth;
  255. $aFilesInfo[$i]['rowNumber'] = $iRowCount;
  256. } else {
  257. if (
  258. // opera (9.0 and below) has a bug which prevents it recognising offsets of less than -2042px
  259. // all subsequent values are treated as -2042px
  260. // if we've hit 2000 pixels and we care about this (as set in the interface) then wrap to a new column
  261. // increment column count and reset current height
  262. ($iTotalHeight + $this->aFormValues['vertical-offset']) >= 2000 &&
  263. !empty($this->aFormValues['wrap-columns'])
  264. ) {
  265. $iColumnCount++;
  266. $iTotalHeight = 0;
  267. }
  268. // if the current image is wider than any other in the current column then set the maximum width to that
  269. // it will be used to set the width of the current column
  270. if ($aFilesInfo[$i]['width'] > $iMaxWidth) {
  271. $iMaxWidth = $aFilesInfo[$i]['width'];
  272. }
  273. // keep track of the width of columns added so far
  274. $aMaxColumnWidth[$iColumnCount] = $iMaxWidth;
  275. // calculate the current maximum horizontal offset so far
  276. $iMaxHOffset = $this->aFormValues['horizontal-offset'] * ($iColumnCount - 1);
  277. // get the y position of current image in overall sprite
  278. $aFilesInfo[$i]['y'] = $iTotalHeight;
  279. $iTotalHeight += ($aFilesInfo[$i]['height'] + $this->aFormValues['vertical-offset']);
  280. // get the x position of current image in overall sprite
  281. if ($iColumnCount == 1) {
  282. $aFilesInfo[$i]['x'] = 0;
  283. } else {
  284. $aFilesInfo[$i]['x'] = (
  285. $this->aFormValues['horizontal-offset'] *
  286. ($iColumnCount - 1) +
  287. (array_sum($aMaxColumnWidth) - $aMaxColumnWidth[$iColumnCount])
  288. );
  289. }
  290. $aFilesInfo[$i]['currentCombinedHeight'] = $iTotalHeight;
  291. $aFilesInfo[$i]['columnNumber'] = $iColumnCount;
  292. }
  293. $i++;
  294. }else{
  295. $this->aBackground[$k] = null;
  296. $k++;
  297. }
  298. if($i > 30){
  299. break;
  300. }
  301. }
  302. // close the dir handle
  303. //$oDir->close();
  304. /*******************************************/
  305. /* this section generates the sprite image */
  306. /* and CSS rules */
  307. /*******************************************/
  308. // if $i is greater than 1 then we managed to generate enough info to create a sprite
  309. if ($i > 1) {
  310. // if Imagick throws an exception we want the script to terminate cleanly so that
  311. // temporary files are cleaned up
  312. try {
  313. // get the sprite width and height
  314. if ($this->aFormValues['build-direction'] == 'horizontal') {
  315. $iSpriteWidth = $iMaxWidth - $this->aFormValues['horizontal-offset'];
  316. $iSpriteHeight = array_sum($aMaxRowHeight) + $iMaxVOffset;
  317. } else {
  318. $iSpriteHeight = $iMaxHeight - $this->aFormValues['vertical-offset'];
  319. $iSpriteWidth = array_sum($aMaxColumnWidth) + $iMaxHOffset;
  320. }
  321. // get background colour - remove # if added
  322. $sBgColour = str_replace('#', '', $this->aFormValues['background']);
  323. // convert 3 digit hex values to 6 digit equivalent
  324. if (strlen($sBgColour) == 3) {
  325. $sBgColour = substr($sBgColour, 0, 1).
  326. substr($sBgColour, 0, 1).
  327. substr($sBgColour, 1, 1).
  328. substr($sBgColour, 1, 1).
  329. substr($sBgColour, 2, 1).
  330. substr($sBgColour, 2, 1);
  331. }
  332. // should the image be transparent
  333. $this->bTransparent = (
  334. !empty($this->aFormValues['use-transparency']) &&
  335. in_array($this->aFormValues['image-output'], array('GIF', 'PNG'))
  336. );
  337. // if using Imagick library create new instance of library class
  338. if ($this->sImageLibrary == 'imagick') {
  339. $oSprite = new Imagick();
  340. // create a new image - set background according to transparency
  341. if (!empty($this->aFormValues['background'])) {
  342. $oSprite->newImage($iSpriteWidth, $iSpriteHeight, new ImagickPixel("#$sBgColour"), $sOutputFormat);
  343. } else {
  344. if ($this->bTransparent) {
  345. $oSprite->newImage($iSpriteWidth, $iSpriteHeight, new ImagickPixel('#000000'), $sOutputFormat);
  346. } else {
  347. $oSprite->newImage($iSpriteWidth, $iSpriteHeight, new ImagickPixel('#ffffff'), $sOutputFormat);
  348. }
  349. }
  350. } else { // using GD - do the same thing
  351. if ($this->bTransparent && !empty($this->aFormValues['background'])) {
  352. $oSprite = imagecreate($iSpriteWidth, $iSpriteHeight);
  353. } else {
  354. $oSprite = imagecreatetruecolor($iSpriteWidth, $iSpriteHeight);
  355. }
  356. }
  357. // check for transparency option
  358. if ($this->bTransparent) {
  359. if ($this->sImageLibrary == 'imagick') {
  360. // set background colour to transparent
  361. // if no background colour use black
  362. if (!empty($this->aFormValues['background'])) {
  363. $oSprite->paintTransparentImage(new ImagickPixel("#$sBgColour"), 0.0, 0);
  364. } else {
  365. $oSprite->paintTransparentImage(new ImagickPixel("#000000"), 0.0, 0);
  366. }
  367. } else { // using GD - do the same thing
  368. if (!empty($this->aFormValues['background'])) {
  369. $iBgColour = hexdec($sBgColour);
  370. $iBgColour = imagecolorallocate(
  371. $oSprite,
  372. 0xFF & ($iBgColour >> 0x10),
  373. 0xFF & ($iBgColour >> 0x8),
  374. 0xFF & $iBgColour
  375. );
  376. } else {
  377. $iBgColour = imagecolorallocate($oSprite, 0, 0, 0);
  378. }
  379. imagecolortransparent($oSprite, $iBgColour);
  380. }
  381. } else {
  382. // set background colour if not using transparency and using GD
  383. if ($this->sImageLibrary != 'imagick') {
  384. if (empty($sBgColour)) {
  385. $sBgColour = 'ffffff';
  386. }
  387. $iBgColour = hexdec($sBgColour);
  388. $iBgColour = imagecolorallocate(
  389. $oSprite, 0xFF & ($iBgColour >> 0x10),
  390. 0xFF & ($iBgColour >> 0x8),
  391. 0xFF & $iBgColour
  392. );
  393. imagefill($oSprite, 0, 0, $iBgColour);
  394. }
  395. }
  396. // initalise variable to store CSS rules
  397. $this->aCss = array();
  398. // loop through file info for valid images
  399. for ($i = 0; $i < count($aFilesInfo); $i++) {
  400. // create a new image object for current file
  401. if (!$oCurrentImage = $this->CreateImage($aFilesInfo[$i]['path'], $aFilesInfo[$i]['ext'])) {
  402. // if we've got here then a valid but corrupt image was found
  403. // at this stage we've already allocated space for the image so create
  404. // a blank one to fill the space instead
  405. // this should happen very rarely
  406. $oCurrentImage = new Imagick();
  407. $oCurrentImage->newImage(
  408. $aFilesInfo[$i]['original-width'],
  409. $aFilesInfo[$i]['original-height'],
  410. new ImagickPixel('#ffffff')
  411. );
  412. }
  413. // if resizing get image width and height and resample to new dimensions (percentage of original)
  414. // and copy to sprite image
  415. if ($bResize) {
  416. if ($this->sImageLibrary == 'imagick') {
  417. // resample image should work but doesn't seem to - using thumbnailImage instead
  418. // which achieves the same effect
  419. $oCurrentImage->thumbnailImage($aFilesInfo[$i]['width'], $aFilesInfo[$i]['height']);
  420. } else {
  421. imagecopyresampled(
  422. $oSprite,
  423. $oCurrentImage,
  424. $aFilesInfo[$i]['x'],
  425. $aFilesInfo[$i]['y'], 0, 0,
  426. $aFilesInfo[$i]['width'],
  427. $aFilesInfo[$i]['height'],
  428. $aFilesInfo[$i]['original-width'],
  429. $aFilesInfo['original-height']
  430. );
  431. }
  432. }
  433. // copy image to sprite
  434. if ($this->sImageLibrary == 'imagick') {
  435. $oSprite->compositeImage(
  436. $oCurrentImage,
  437. $oCurrentImage->getImageCompose(),
  438. $aFilesInfo[$i]['x'],
  439. $aFilesInfo[$i]['y']
  440. );
  441. } else {
  442. // if using GD and already resized the image will have been copied as part of the resize
  443. if (!$bResize) {
  444. imagecopy(
  445. $oSprite,
  446. $oCurrentImage,
  447. $aFilesInfo[$i]['x'],
  448. $aFilesInfo[$i]['y'],
  449. 0, 0,
  450. $aFilesInfo[$i]['width'],
  451. $aFilesInfo[$i]['height']
  452. );
  453. }
  454. }
  455. // get CSS x & y values
  456. $iX = $aFilesInfo[$i]['x'] != 0 ? '-'.$aFilesInfo[$i]['x'].'px' : '0';
  457. $iY = $aFilesInfo[$i]['y'] != 0 ? '-'.$aFilesInfo[$i]['y'].'px' : '0';
  458. $this->aPosition[$i] = $iX.' '.$iY;
  459. // create CSS rules and append to overall CSS rules
  460. $this->sCss .= "{$this->aFormValues['selector-prefix']}{$aFilesInfo[$i]['class']} {$this->aFormValues['selector-suffix']}{ background-position: $iX $iY; ";
  461. // If add widths and heights the sprite image width and height are added to the CSS
  462. if ($this->aFormValues['add-width-height-to-css'] == 'on'){
  463. $this->sCss .= "width: {$aFilesInfo[$i]['width']}px; height: {$aFilesInfo[$i]['height']}px;";
  464. }
  465. $this->sCss .= " } \n";
  466. // destroy object created for current image to save memory
  467. if ($this->sImageLibrary == 'imagick') {
  468. $oCurrentImage->destroy();
  469. } else {
  470. imagedestroy($oCurrentImage);
  471. }
  472. }
  473. jimport('joomla.filesytem.folder');
  474. $path = JPATH_ROOT.DS.'images'.DS.'jch-optimize';
  475. JFolder::create($path);
  476. // create a unqiue filename for sprite image
  477. $sSpriteMD5 = md5(implode($aFilesMD5).implode($this->aFormValues));
  478. $this->sTempSpriteName = $path.DS.'csg-'.$sSpriteMD5.".$sOutputFormat";
  479. // write image to file (deleted by cron script after a limited time period)
  480. if (!file_exists($this->sTempSpriteName)){
  481. $this->WriteImage($oSprite, $sOutputFormat, $this->sTempSpriteName);
  482. }
  483. // destroy object created for sprite image to save memory
  484. if ($this->sImageLibrary == 'imagick') {
  485. $oSprite->destroy();
  486. } else {
  487. imagedestroy($oSprite);
  488. }
  489. // set flag to indicate valid images created
  490. $this->bValidImages = true;
  491. } catch (ImagickException $e) {
  492. error_log($e->getMessage());
  493. }
  494. }
  495. }
  496. protected function FormatClassName($sClassName) {
  497. $aExtensions = array();
  498. foreach ($this->aImageTypes as $sType) {
  499. $aExtensions[] = ".$sType";
  500. }
  501. return preg_replace("/[^a-z0-9_-]+/i", '', str_ireplace($aExtensions, '', $sClassName));
  502. }
  503. protected function CreateImage($sFile, $sExtension) {
  504. if ($this->sImageLibrary == 'imagick') {
  505. try {
  506. // Imagick auto detects file extension when creating object from image
  507. $oImage = new Imagick();
  508. $oImage->readImage($sFile);
  509. return $oImage;
  510. } catch (ImagickException $e) {
  511. return false;
  512. }
  513. } else {
  514. // we need to tell GD what type of image it's creating an object from
  515. switch ($sExtension) {
  516. case 'jpg':
  517. case 'jpeg':
  518. return @imagecreatefromjpeg($sFile);
  519. case 'gif':
  520. return @imagecreatefromgif($sFile);
  521. case 'png':
  522. return @imagecreatefrompng($sFile);
  523. }
  524. }
  525. }
  526. protected function WriteImage($oImage, $sExtension, $sFilename) {
  527. if ($this->sImageLibrary == 'imagick') {
  528. try {
  529. // check if we want to resample image to lower number of colours (to reduce file size)
  530. if (in_array($sExtension, array('gif', 'png')) && $this->aFormValues['image-num-colours'] != 'true-colour') {
  531. $oImage->quantizeImage($this->aFormValues['image-num-colours'], Imagick::COLORSPACE_RGB, 0, false, false);
  532. }
  533. // if we're creating a JEPG set image quality - 0% - 100%
  534. if (in_array($sExtension, array('jpg', 'jpeg'))) {
  535. $oImage->setCompression(Imagick::COMPRESSION_JPEG);
  536. $oImage->SetCompressionQuality($this->aFormValues['image-quality']);
  537. }
  538. // write out image to file
  539. $oImage->writeImage($sFilename);
  540. } catch (ImagickException $e) {
  541. error_log($e->getMessage());
  542. }
  543. } else {
  544. // check if we want to resample image to lower number of colours (to reduce file size)
  545. if (in_array($sExtension, array('gif', 'png')) && $this->aFormValues['image-num-colours'] != 'true-colour') {
  546. imagetruecolortopalette($oImage, true, $this->aFormValues['image-num-colours']);
  547. }
  548. switch ($sExtension) {
  549. case 'jpg':
  550. case 'jpeg':
  551. // GD takes quality setting in main creation function
  552. imagejpeg($oImage, $sFilename, $this->aFormValues['image-quality']);
  553. break;
  554. case 'gif':
  555. // force colour palette to 256 colours if saving sprite image as GIF
  556. // this will happen anyway (as GIFs can't be more than 256 colours)
  557. // but the quality will be better if pre-forcing
  558. if (
  559. $this->bTransparent &&
  560. (
  561. $this->aFormValues['image-num-colours'] == -1 ||
  562. $this->aFormValues['image-num-colours'] > 256
  563. )
  564. ) {
  565. imagetruecolortopalette($oImage, true, 256);
  566. }
  567. imagegif($oImage, $sFilename);
  568. break;
  569. case 'png':
  570. imagepng($oImage, $sFilename);
  571. break;
  572. }
  573. }
  574. // if using a PNG and option selected further compress sprite image using OptiPNG
  575. // this can result in more than 50% saving in file size with little loss in quality
  576. if (
  577. $sExtension == 'png' &&
  578. !empty($this->aFormValues['use-optipng']) &&
  579. ConfigHelper::Get('/binaries/optipng')
  580. ) {
  581. // this probably won't work with PHP safe mode enabled
  582. // no real alternative - you'll have to enable to use
  583. shell_exec(ConfigHelper::Get('/binaries/optipng')." $sFilename");
  584. }
  585. }
  586. public function ValidImages() {
  587. return $this->bValidImages;
  588. }
  589. public function GetSpriteFilename() {
  590. $aFileParts = pathinfo($this->sTempSpriteName);
  591. return $aFileParts['basename'];
  592. }
  593. public function GetSpriteHash() {
  594. //return md5($this->GetSpriteFilename().ConfigHelper::Get('/checksum'));
  595. }
  596. public function GetCss() {
  597. return $this->aCss;
  598. }
  599. public function GetAllErrors() {
  600. return $this->aFormErrors;
  601. }
  602. public function GetZipFolder() {
  603. return $this->sZipFolder;
  604. }
  605. public function GetCssBackground(){
  606. $aCssBackground = array();
  607. foreach($this->aBackground as $background){
  608. $aCssBackground[] = @$this->aPosition[$background];
  609. }
  610. return $aCssBackground;
  611. }
  612. }
  613. ?>