[FEATURE] Enable double-resolution icons in SpriteGenerator 87/21887/13
authorSteffen Ritter <info@rs-websystems.de>
Thu, 4 Jul 2013 16:56:44 +0000 (18:56 +0200)
committerSteffen Ritter <info@rs-websystems.de>
Tue, 15 Oct 2013 14:40:11 +0000 (16:40 +0200)
In order to display high resolution icons to according displays
the SpriteManager must be enabled to support this.

This patch adds support for that. It generates a second sprite
with double sized icons and uses background-size CSS3 property.
As this sprite is a separate file it does not affect performance
on non high Resolution Displays. High Resolution displays directly
load the second file - which indeed is 4 times the size.

For generating the high resolution sprite all icons which are not
available as @x2-variant are scaled with gd-library functionality.

Change-Id: Id606f0c2900d57bb46869829d5f0939f29e9db08
Resolves: #49667
Documentation: #52853
Releases: 6.2
Reviewed-on: https://review.typo3.org/21887
Reviewed-by: Christian Kuhn
Reviewed-by: Markus Klein
Tested-by: Markus Klein
Tested-by: Christian Kuhn
Reviewed-by: Steffen Ritter
Tested-by: Steffen Ritter
NEWS.txt
typo3/sysext/backend/Classes/Sprite/SpriteGenerator.php

index 4a5d3ba..4c7faeb 100644 (file)
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -32,6 +32,10 @@ Changes and Improvements
 General
 -------------------------------------------------------------------------------
 
+* SpriteGenerator now supports high density sprites
+
+It is now possible to create high density sprites.
+
 -------------------------------------------------------------------------------
 Logging
 -------------------------------------------------------------------------------
index 922d3ab..85e6f54 100644 (file)
@@ -50,6 +50,17 @@ class SpriteGenerator {
 }
 ';
 
+       /**
+        * Template creating CSS for the high density spritefile
+        *
+        * @var string
+        */
+       protected $templateSpriteHighDensity =  '
+.backgroundsize .###NAMESPACE###-###SPRITENAME### {
+       background-image: url(\'###SPRITEURL###\') !important;
+       background-size:###BGWIDTH### ###BGHEIGHT###;
+}
+';
        /**
         * Template creating CSS for position and size of a single icon
         *
@@ -61,6 +72,11 @@ class SpriteGenerator {
 }
 ';
 
+       /**
+        * @var boolean
+        */
+       protected $enableHighDensitySprite = TRUE;
+
        /**
         * Most common icon-width in the sprite
         *
@@ -115,7 +131,7 @@ class SpriteGenerator {
         *
         * @var boolean
         */
-       protected $ommitSpriteNameInIconName = FALSE;
+       protected $omitSpriteNameInIconName = FALSE;
 
        /**
         * Namespace of css classes
@@ -172,7 +188,6 @@ class SpriteGenerator {
         * Initializes the configuration of the spritegenerator
         *
         * @param string $spriteName The name of the sprite to be generated
-        * @return void
         */
        public function __construct($spriteName) {
                $this->spriteName = $spriteName;
@@ -182,7 +197,7 @@ class SpriteGenerator {
         * Sets namespace of css code
         *
         * @param string $nameSpace
-        * @return \TYPO3\CMS\Backend\Sprite\SpriteGenerator An instance of $this, to enable chaining.
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
         */
        public function setNamespace($nameSpace) {
                $this->nameSpace = $nameSpace;
@@ -193,7 +208,7 @@ class SpriteGenerator {
         * Sets the spritename
         *
         * @param string $spriteName The name of the sprite to be generated
-        * @return \TYPO3\CMS\Backend\Sprite\SpriteGenerator An instance of $this, to enable chaining.
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
         */
        public function setSpriteName($spriteName) {
                $this->spriteName = $spriteName;
@@ -204,7 +219,7 @@ class SpriteGenerator {
         * Sets the sprite-graphics target-folder
         *
         * @param string $folder The target folder where the generated sprite is stored
-        * @return \TYPO3\CMS\Backend\Sprite\SpriteGenerator An instance of $this, to enable chaining.
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
         */
        public function setSpriteFolder($folder) {
                $this->spriteFolder = $folder;
@@ -215,21 +230,44 @@ class SpriteGenerator {
         * Sets the sprite-css target-folder
         *
         * @param string $folder the target folder where the generated CSS files are stored
-        * @return \TYPO3\CMS\Backend\Sprite\SpriteGenerator An instance of $this, to enable chaining.
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
         */
        public function setCSSFolder($folder) {
                $this->cssFolder = $folder;
                return $this;
        }
 
+       /**
+        * Enables/Disables HighDensitySprite Generation
+        *
+        * @param boolean $enable
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
+        */
+       public function setEnableHighDensitySprite($enable = TRUE) {
+               $this->enableHighDensitySprite = $enable;
+               return $this;
+       }
+
        /**
         * Setter do enable the exclusion of the sprites-name from iconnames
         *
         * @param boolean $value
-        * @return \TYPO3\CMS\Backend\Sprite\SpriteGenerator An instance of $this, to enable chaining.
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
+        * @deprecated since 6.2, will be removed two versions later - use setOmitSpriteNameInIconName() instead
         */
        public function setOmmitSpriteNameInIconName($value) {
-               $this->ommitSpriteNameInIconName = is_bool($value) ? $value : FALSE;
+               GeneralUtility::logDeprecatedFunction();
+               return $this->setOmitSpriteNameInIconName($value);
+       }
+
+       /**
+        * Setter do enable the exclusion of the sprites-name from iconnames
+        *
+        * @param boolean $value
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
+        */
+       public function setOmitSpriteNameInIconName($value) {
+               $this->omitSpriteNameInIconName = is_bool($value) ? $value : FALSE;
                return $this;
        }
 
@@ -237,7 +275,7 @@ class SpriteGenerator {
         * Setter to adjust how much space is between to icons in the sprite
         *
         * @param integer $value
-        * @return \TYPO3\CMS\Backend\Sprite\SpriteGenerator An instance of $this, to enable chaining.
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
         */
        public function setIconSpace($value) {
                $this->space = intval($value);
@@ -248,7 +286,7 @@ class SpriteGenerator {
         * Setter for timestamp inclusion: imageFiles will be included with ?timestamp
         *
         * @param boolean $value
-        * @return \TYPO3\CMS\Backend\Sprite\SpriteGenerator An instance of $this, to enable chaining.
+        * @return SpriteGenerator An instance of $this, to enable "chaining".
         */
        public function setIncludeTimestampInCSS($value) {
                $this->includeTimestampInCSS = is_bool($value) ? $value : TRUE;
@@ -281,13 +319,16 @@ class SpriteGenerator {
         * @return array
         */
        public function generateSpriteFromArray(array $files) {
-               if (!$this->ommitSpriteNameInIconName) {
+               if (!$this->omitSpriteNameInIconName) {
                        $this->spriteBases[] = $this->spriteName;
                }
                $this->buildFileInformationCache($files);
                // Calculate Icon Position in sprite
                $this->calculateSpritePositions();
                $this->generateGraphic();
+               if ($this->enableHighDensitySprite) {
+                       $this->generateHighDensityGraphic();
+               }
                $this->generateCSS();
                $iconNames = array_keys($this->iconsData);
                natsort($iconNames);
@@ -322,8 +363,22 @@ class SpriteGenerator {
                foreach ($this->spriteBases as $base) {
                        $markerArray['###SPRITENAME###'] = $base;
                        $cssData .= HtmlParser::substituteMarkerArray($this->templateSprite, $markerArray);
+
+                       if ($this->enableHighDensitySprite) {
+                               $highDensityMarkerArray = array_merge($markerArray, array(
+                                       '###BGWIDTH###' => $this->spriteWidth . 'px',
+                                       '###BGHEIGHT###' => $this->spriteHeight . 'px',
+                                       '###SPRITEURL###' => str_replace(
+                                               $this->spriteName . '.png',
+                                               $this->spriteName . '@x2.png',
+                                               $markerArray['###SPRITEURL###']
+                                       )
+                               ));
+                               $cssData .= HtmlParser::substituteMarkerArray($this->templateSpriteHighDensity, $highDensityMarkerArray);
+                       }
                }
-               foreach ($this->iconsData as $key => $data) {
+
+               foreach ($this->iconsData as $data) {
                        $temp = $data['iconNameParts'];
                        array_shift($temp);
                        $cssName = implode('-', $temp);
@@ -377,9 +432,8 @@ class SpriteGenerator {
         */
        protected function generateGraphic() {
                $tempSprite = GeneralUtility::tempnam($this->spriteName);
-               $filePath = array(
-                       'mainFile' => PATH_site . $this->spriteFolder . $this->spriteName . '.png'
-               );
+               $filePath = PATH_site . $this->spriteFolder . $this->spriteName . '.png';
+
                // Create black true color image with given size
                $newSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight);
                imagesavealpha($newSprite, TRUE);
@@ -393,22 +447,52 @@ class SpriteGenerator {
                        }
                }
                imagepng($newSprite, $tempSprite . '.png');
-               GeneralUtility::upload_copy_move($tempSprite . '.png', $filePath['mainFile']);
+               GeneralUtility::upload_copy_move($tempSprite . '.png', $filePath);
                GeneralUtility::unlink_tempfile($tempSprite . '.png');
        }
 
+       /**
+        * The actual sprite generator, renders the command for IM/GM and executes
+        *
+        * @return void
+        */
+       protected function generateHighDensityGraphic() {
+               $tempSprite = GeneralUtility::tempnam($this->spriteName . '@x2');
+               $filePath = PATH_site . $this->spriteFolder . $this->spriteName . '@x2.png';
+
+               // Create black true color image with given size
+               $newSprite = imagecreatetruecolor($this->spriteWidth * 2, $this->spriteHeight * 2);
+               imagesavealpha($newSprite, TRUE);
+               // Make it transparent
+               imagefill($newSprite, 0, 0, imagecolorallocatealpha($newSprite, 0, 255, 255, 127));
+               foreach ($this->iconsData as $icon) {
+                       $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
+                       if (function_exists($function)) {
+                               if ($icon['fileNameHighDensity'] !== FALSE) {
+                                       // copy HighDensity file
+                                       $currentIcon = $function($icon['fileNameHighDensity']);
+                                       imagecopy($newSprite, $currentIcon, $icon['left'] * 2, $icon['top'] * 2, 0, 0, $icon['width'] * 2, $icon['height'] * 2);
+                               } else {
+                                       // scale up normal file
+                                       $currentIcon = $function($icon['fileName']);
+                                       imagecopyresized($newSprite, $currentIcon, $icon['left'] * 2, $icon['top'] * 2, 0, 0, $icon['width'] * 2, $icon['height'] * 2, $icon['width'], $icon['height']);
+                               }
+                       }
+               }
+               imagepng($newSprite, $tempSprite . '.png');
+               GeneralUtility::upload_copy_move($tempSprite . '.png', $filePath);
+               GeneralUtility::unlink_tempfile($tempSprite . '.png');
+       }
        /**
         * Arranges icons in sprites,
-        * afterwards all icons have information about ther position in sprite
+        * afterwards all icons have information about the position in sprite
         */
        protected function calculateSpritePositions() {
-               $currentLeft = 0;
-               $currentTop = 0;
                // Calculate width of every icon-size-group
                $sizes = array();
                foreach ($this->iconSizes as $sizeTag => $count) {
                        $size = $this->explodeSizeTag($sizeTag);
-                       $rowWidth = ceil(sqrt($count)) * $size['width'];
+                       $rowWidth = (int)ceil(sqrt($count)) * $size['width'];
                        while (isset($sizes[$rowWidth])) {
                                $rowWidth++;
                        }
@@ -416,7 +500,8 @@ class SpriteGenerator {
                }
                // Reverse sorting: widest group to top
                krsort($sizes);
-               // Integerate all icons grouped by icons size into the sprite
+               $currentTop = 0;
+               // Integrate all icons grouped by icons size into the sprite
                foreach ($sizes as $sizeTag) {
                        $size = $this->explodeSizeTag($sizeTag);
                        $currentLeft = 0;
@@ -458,7 +543,7 @@ class SpriteGenerator {
         */
        protected function getFolder($directoryPath) {
                $subFolders = GeneralUtility::get_dirs(PATH_site . $directoryPath);
-               if (!$this->ommitSpriteNameInIconName) {
+               if (!$this->omitSpriteNameInIconName) {
                        $subFolders[] = '';
                }
                $resultArray = array();
@@ -471,7 +556,7 @@ class SpriteGenerator {
                                foreach ($icons as $icon) {
                                        $fileInfo = pathinfo($icon);
                                        $iconName = ($folder ? $folder . '-' : '') . $fileInfo['filename'];
-                                       if (!$this->ommitSpriteNameInIconName) {
+                                       if (!$this->omitSpriteNameInIconName) {
                                                $iconName = $this->spriteName . '-' . $iconName;
                                        }
                                        $resultArray[$iconName] = $directoryPath . $folder . '/' . $icon;
@@ -504,8 +589,15 @@ class SpriteGenerator {
                                'width' => $imageInfo[0],
                                'height' => $imageInfo[1],
                                'left' => 0,
-                               'top' => 0
+                               'top' => 0,
+                               'fileNameHighDensity' => FALSE
                        );
+                       if ($this->enableHighDensitySprite) {
+                               $highDensityFile = str_replace('.' . $fileInfo['extension'], '@x2.' . $fileInfo['extension'], $iconFile);
+                               if (@file_exists(PATH_site . $highDensityFile)) {
+                                       $this->iconsData[$iconName]['fileNameHighDensity'] = $highDensityFile;
+                               }
+                       }
                        $sizeTag = $imageInfo[0] . 'x' . $imageInfo[1];
                        if (isset($this->iconSizes[$sizeTag])) {
                                $this->iconSizes[$sizeTag] += 1;