Alex Trofimov 12 лет назад
Родитель
Сommit
e0ed1abf0b

+ 21 - 4
image_transcoder.php

@@ -1,10 +1,22 @@
 <?php
 /**
- * @package     Dolphin Core
- * @copyright   Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
- * @license     CC-BY - http://creativecommons.org/licenses/by/3.0/
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
  */
 
+if (isset($_GET['devicePixelRatio'])) {
+    $sDpr = $_GET['devicePixelRatio'];
+
+    if ('' . ceil(intval($sDpr)) !== $sDpr)
+        $sDpr = '1';
+
+    setcookie('devicePixelRatio', $sDpr, time()+60*60*24*365);
+    exit();
+}
+
 ob_start();
 
 require_once('./inc/header.inc.php');
@@ -23,13 +35,16 @@ if (!$oTranscoder) {
 
 ob_end_clean();
 
-$sImageUrl = '';
 if (!$oTranscoder->isImageReady($sHandler) && !$oTranscoder->transcode ($sHandler)) {
     bx_transcoder_error_occured();
     exit;
 }
 
 $sImageUrl = $oTranscoder->getImageUrl($sHandler);
+if (!$sImageUrl) {
+    bx_transcoder_error_occured();
+    exit;
+}
 
 header('HTTP/1.1 301 Moved Permanently');
 header('Location: ' . $sImageUrl);
@@ -44,3 +59,5 @@ function bx_transcoder_error_occured($sMethod = 'displayPageNotFound') {
     $oTemplate->$sMethod ();
 }
 
+/** @} */
+

+ 4 - 4
inc/classes/BxDolImageResize.php

@@ -197,7 +197,7 @@ class BxDolImageResize extends BxDol implements iBxDolSingleton {
         if (!imagefilter($src_im, IMG_FILTER_GRAYSCALE)) 
             return IMAGE_ERROR_GD_FILTER_ERROR;
 
-        $writeResult = $this->_writeImageGD ($src_im, $sDstImage);
+        $writeResult = $this->_writeImageGD ($src_im, $sDstImage, $size[2]);
 
         // free memory
         imagedestroy( $src_im );
@@ -352,7 +352,7 @@ class BxDolImageResize extends BxDol implements iBxDolSingleton {
         if ( !$convertResult )
             return IMAGE_ERROR_GD_RESIZE_ERROR;
 
-        $writeResult = $this->_writeImageGD ($dst_im, $sDstImage);
+        $writeResult = $this->_writeImageGD ($dst_im, $sDstImage, $size[2]);
 
         // free memory
         if ( $dst_im != $src_im )
@@ -532,7 +532,7 @@ class BxDolImageResize extends BxDol implements iBxDolSingleton {
         }
     }
 
-    function _writeImageGD ($src_im, $sDstImage) {
+    function _writeImageGD ($src_im, $sDstImage, $iSrcImageType) {
 
         $writeResult = false;
 
@@ -551,7 +551,7 @@ class BxDolImageResize extends BxDol implements iBxDolSingleton {
                 break;
 
             default:
-                switch ( $size[2] ) {
+                switch ($iSrcImageType) {
                     case IMAGE_TYPE_GIF:
                         $writeResult = imagegif( $src_im, $sDstImage );
                         break;

+ 50 - 12
inc/classes/BxDolImageTranscoder.php

@@ -81,11 +81,12 @@ bx_import('BxDolImageTranscoderQuery');
  * @endcode
  *
  */
-class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
-
+class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject 
+{
     protected $_aObject; ///< object properties
     protected $_oStorage; ///< storage object, transcoded images are stored here
     protected $_oDb; ///< database queries object
+    protected $_sRetinaSuffix = '@2x'; ///< handler suffix for retina image file
 
     /**
      * constructor
@@ -204,12 +205,20 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
      * Called automatically, upon original file deletetion.
      */
     public function onDeleteFileOrig($mixedHandler) {
-
+        // delete main file
         $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
         if (!$iFileId)
             return false;
 
-        return $this->_oStorage->deleteFile($iFileId);
+        if (!$this->_oStorage->deleteFile($iFileId)) 
+            return false;
+
+        // delete retina file
+        $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler . $this->_sRetinaSuffix);
+        if ($iFileId)
+            $this->_oStorage->deleteFile($iFileId);
+
+        return true;
     }
 
     /**
@@ -242,7 +251,7 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
     /**
      * Get transcoded image url. 
      * If transcoded image is ready then direct url to this image is returned. 
-     * If there is not transcoded data available special url is returned, upon opening this url - image is transcoed automatically and redirects to the ready transcoed image.
+     * If there is no transcoded data available, then special url is returned, upon opening this url image is transcoded automatically and redirects to the ready transcoed image.
      * @params $mixedHandler - image handler
      * @return image url, or false on error.
      */
@@ -250,6 +259,8 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
 
         if ($this->isImageReady($mixedHandler)) {
 
+            $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
+
             $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
             if (!$iFileId)
                 return false;
@@ -264,7 +275,8 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
             return $this->_oStorage->getFileUrlById($iFileId);
         }
 
-        return BX_DOL_URL_ROOT . 'image_transcoder.php?o=' . $this->_aObject['object'] . '&h=' . $mixedHandler . '&t=' . time();
+        // TODO: reconsider adding timestamp in the url
+        return BX_DOL_URL_ROOT . 'image_transcoder.php?o=' . $this->_aObject['object'] . '&h=' . $mixedHandler . '&dpx=' . $this->getDevicePixelRatio() . '&t=' . time();
     }
 
     /**
@@ -293,7 +305,7 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
         $sTmpFile = $this->$sMethodStoreFile($mixedHandler);
         if (!$sTmpFile) 
             return false;
-        
+
         // appply filters to tmp file
         $this->initFilters ();
         foreach ($this->_aObject['filters'] as $aParams) {
@@ -334,6 +346,8 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
 
         }
 
+        $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
+
         // store transcoded file in the storage
         $sMethodIsPrivate = 'isPrivate_' . $this->_aObject['source_type'];
         $isPrivate = $this->$sMethodIsPrivate($mixedHandler);
@@ -367,9 +381,20 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
         return $iCount;
     }
 
+    public function getDevicePixelRatio () {
+        if (isset($_COOKIE['devicePixelRatio']) && (!isset($this->_aObject['source_params']['disable_retina']) || !$this->_aObject['source_params']['disable_retina'])) {
+            $iDevicePixelRatio = intval($_COOKIE['devicePixelRatio']);
+            if ($iDevicePixelRatio >= 2)
+                return 2;
+        }
+        return 1;
+    }
+
     // ---------------------------
 
-    protected function isImageReady_Folder ($mixedHandler, $isCheckOutdated = true) {
+    protected function isImageReady_Folder ($mixedHandlerOrig, $isCheckOutdated = true) {
+
+        $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
 
         $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
         if (!$iFileId)
@@ -380,7 +405,7 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
             return false;
 
         if ($isCheckOutdated) { // warning, $isCheckOutdated is partially supported for Folder source type - file modification is not checked
-            if ($this->_aObject['ts'] > $aFile['modified'] || !$this->getFilePath_Folder($mixedHandler)) { // if we changed transcoder object params or original file is deleted
+            if ($this->_aObject['ts'] > $aFile['modified'] || !$this->getFilePath_Folder($mixedHandlerOrig)) { // if we changed transcoder object params or original file is deleted
                 // delete file, so it will be recreated next time
                 if ($this->_oStorage->deleteFile($aFile['id']))
                     return false;
@@ -390,7 +415,9 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
         return true;
     }
 
-    protected function isImageReady_Storage ($mixedHandler, $isCheckOutdated = true) {
+    protected function isImageReady_Storage ($mixedHandlerOrig, $isCheckOutdated = true) {
+
+        $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
 
         $iFileId = $this->_oDb->getFileIdByHandler($mixedHandler);
         if (!$iFileId)
@@ -403,7 +430,7 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
         if ($isCheckOutdated) {
             $oStorageOriginal = BxDolStorage::getObjectInstance($this->_aObject['source_params']['object']);
             if ($oStorageOriginal) {
-                $aFileOriginal = $oStorageOriginal->getFile($mixedHandler);
+                $aFileOriginal = $oStorageOriginal->getFile($mixedHandlerOrig);
                 if (!$aFileOriginal || $aFileOriginal['modified'] > $aFile['modified'] || $this->_aObject['ts'] > $aFile['modified']) { // if original file was changed OR we changed transcoder object params 
                     // delete file, so it will be recreated next time
                     if ($this->_oStorage->deleteFile($aFile['id']))
@@ -515,7 +542,7 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
         $o->removeCropOptions ();
 
         if (isset($aParams['w']) && isset($aParams['h']))
-            $o->setSize ($aParams['w'], $aParams['h']);
+            $o->setSize ($aParams['w'] * $this->getDevicePixelRatio(), $aParams['h'] * $this->getDevicePixelRatio());
 
         if (isset($aParams['crop_resize']) && $aParams['crop_resize']) {
             $o->setAutoCrop (true);
@@ -576,6 +603,17 @@ class BxDolImageTranscoder extends BxDol implements iBxDolFactoryObject {
         return $oCacheDb->removeAllByPrefix('db_');
     }
 
+    public function getDevicePixelRatioHandlerSuffix () {
+        $iDevicePixelRatio = $this->getDevicePixelRatio ();
+        if ($iDevicePixelRatio >= 2)
+            return $this->_sRetinaSuffix;
+        return '';
+    }
+
+    protected function processHandlerForRetinaDevice ($mixedHandler) {
+        return '' . $mixedHandler . $this->getDevicePixelRatioHandlerSuffix ();
+    }
+
     static protected function _registerHandlersArray ($mixed, $sFunc) {
         if (!is_array($mixed))
             $mixed = array($mixed);

+ 3 - 3
install/sql/system.sql

@@ -3180,9 +3180,9 @@ CREATE TABLE IF NOT EXISTS `sys_objects_transcoder_images` (
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
 INSERT INTO `sys_objects_transcoder_images` (`object`, `storage_object`, `source_type`, `source_params`, `private`, `atime_tracking`, `atime_pruning`, `ts`) VALUES 
-('sys_icon_apple', 'sys_images', 'Storage', 'a:1:{s:6:"object";s:10:"sys_images";}', 'no', '0', '0', '0'),
-('sys_icon_facebook', 'sys_images', 'Storage', 'a:1:{s:6:"object";s:10:"sys_images";}', 'no', '0', '0', '0'),
-('sys_icon_favicon', 'sys_images', 'Storage', 'a:1:{s:6:"object";s:10:"sys_images";}', 'no', '0', '0', '0'),
+('sys_icon_apple', 'sys_images', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0'),
+('sys_icon_facebook', 'sys_images', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0'),
+('sys_icon_favicon', 'sys_images', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0'),
 ('sys_cmts_images_preview', 'sys_cmts_images_preview', 'Storage', 'a:1:{s:6:"object";s:15:"sys_cmts_images";}', 'no', '1', '2592000', '0');
 
 

+ 11 - 0
template/_header.html

@@ -55,6 +55,17 @@
             $('#bx-toolbar').css({opacity: 0.9});
         });
 
+        (function(w) {
+            var dpr = (w.devicePixelRatio === undefined ? 1 : w.devicePixelRatio);
+            if (!!w.navigator.standalone) {
+                var r = new XMLHttpRequest();
+                r.open('GET', sUrlRoot + 'image_transcoder.php?devicePixelRatio=' + dpr, false);
+                r.send();
+            } else {
+                $.cookie('devicePixelRatio', dpr, {expires: 365, path: '/'});
+            }
+        })(window);
+
     </script>
     __extra_js__