BxDolTranscoderImage.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. /**
  10. * This class transcodes images on the fly.
  11. * Transcoded image is saved in the specified storage engine and next time ready image is served.
  12. *
  13. *
  14. * To add image transcoder object add record to 'sys_objects_transcoder' table:
  15. * - object - name of the transcoder object, in the format: vendor prefix, underscore, module prefix, underscore, image size name; for example: bx_images_thumb.
  16. * - storage_object - name of the storage object to store transcoded data, the specified storage object need to be created too, @see BxDolStorage.
  17. * - source_type - type of the source, where is source image is taken from, available options 'Storage' and 'Folder' for now.
  18. * - source_params - source_type params, each source_type can have own set of params, please read futher for more info about particular source_types, serialized array of params is stored here.
  19. * - private - how to store transcoded data:
  20. * - no - store transcoded data publicly.
  21. * - yes - store transcoded data privately.
  22. * - auto - detect automatically, not supported for 'Folder' source type.
  23. * - atime_tracking - track last access time to the transcoded data, allowed values 0 - disables or 1 - enabled.
  24. * - atime_pruning - prune transcoded images by last access time, if last access time of the image is older than atime_pruning seconds - it is deleted, it works when atime_tracking is enabled
  25. * - ts - unix timestamp of the last change of transcoder parameters, if transcoded image is older than this value - image is deleted and transcoded again.
  26. *
  27. *
  28. * Then you need to add image filters to 'sys_transcoder_filters' table:
  29. * - transcoder_object - name of the transcoded object to apply filter to.
  30. * - filter - filter name, please read futher for available filters.
  31. * - filter_params - serialized array of filter params, please read futher for particular filters params.
  32. * - order - if there are several filters for one object, they will be applied in this order.
  33. *
  34. *
  35. * 'Folder' source types:
  36. * This source type is some folder with original images for the transcoding, the identifier of the image (handler) is file name.
  37. * The params are the following:
  38. * - path - path to the folder with original images
  39. * This source type has some limitation:
  40. * - automatic detection of private files is not supported
  41. * - transcoded file is not automaticlaly deleted/renewed if original file is changed
  42. *
  43. *
  44. * 'Storage' source type:
  45. * The source of original files is Storage engine, the identifier of the image (handler) is file id.
  46. * The params are the following:
  47. * - object - name of the Storage object
  48. *
  49. *
  50. * Available filters:
  51. * - Resize - this filter resizes original image, the parameters are the following:
  52. * - w - width of resulted image.
  53. * - h - height of resulted image.
  54. * - square_resize - make resulted image square, even of original image is not square, 'w' and 'h' parameters must be the same.
  55. * - crop_resize - crop image to destination size with filling whole area of destination size.
  56. * - force_type - always change type of the image to the specified type: jpg, png, gif.
  57. * - Grayscale - make image grayscale, there is no parameters for this filter
  58. *
  59. *
  60. * Automatic deletetion of associated data is supported - in the case if original or transcoded file is deleted,
  61. * but you need to register alert handlers, just call registerHandlers () function to register handler (for example, during module installation)
  62. * and call unregisterHandlers () function to unregister handlers (for example, during module uninstallation)
  63. *
  64. *
  65. * Example of usage:
  66. * @code
  67. * $oTranscoder = BxDolTranscoderImage::getObjectInstance('bx_images_thumb'); // change images transcode object name to your own
  68. * $oTranscoder->registerHandlers(); // make sure to call it only once! before the first usage, no need to call it every time
  69. * $sTranscodedImageUrl = $oTranscoder->getFileUrl('my_dog.jpg'); // the name of file, in the case of 'Folder' storage type this is file name
  70. * echo 'My dog : <img src="' . $sUrl . '" />'; // transcoded(resized and/or grayscaled) image will be shown, according to the specified filters
  71. * @endcode
  72. *
  73. */
  74. class BxDolTranscoderImage extends BxDolTranscoder implements iBxDolFactoryObject
  75. {
  76. protected function __construct($aObject, $oStorage)
  77. {
  78. parent::__construct($aObject, $oStorage);
  79. $this->_oDb = new BxDolTranscoderImageQuery($aObject, false);
  80. $this->_sQueueTable = $this->_oDb->getQueueTable();
  81. }
  82. public static function getObjectAbstract()
  83. {
  84. if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '!Abstract']))
  85. return $GLOBALS['bxDolClasses'][__CLASS__ . '!Abstract'];
  86. $aObject = array('object' => 'abstract');
  87. $o = new BxDolTranscoderImage ($aObject, null);
  88. return ($GLOBALS['bxDolClasses'][__CLASS__ . '!Abstract'] = $o);
  89. }
  90. /**
  91. * check if transcoder suppors given file mime type
  92. */
  93. public function isMimeTypeSupported($sMimeType)
  94. {
  95. $sMimeType = strtolower($sMimeType);
  96. switch ($sMimeType) {
  97. case 'image/gif':
  98. case 'image/jpeg':
  99. case 'image/pjpeg':
  100. case 'image/png':
  101. case 'image/webp':
  102. return true;
  103. }
  104. return false;
  105. }
  106. public function isMimeTypeSvg($sMimeType)
  107. {
  108. return $this->_oStorage->isMimeTypeSvg($sMimeType);
  109. }
  110. /**
  111. * Get transcoded file url.
  112. * If transcoded file is ready then direct url to the file is returned.
  113. * 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.
  114. * @param $mixedHandler - file handler
  115. * @return file url, or false on error.
  116. */
  117. public function getFileUrl($mixedHandler)
  118. {
  119. if(($sFileUrl = $this->getOrigFileUrl($mixedHandler)) !== false) {
  120. $sFileMimeType = $this->_oStorage->getMimeTypeByFileName($sFileUrl);
  121. if($this->isMimeTypeSvg($sFileMimeType))
  122. return $sFileUrl;
  123. }
  124. return parent::getFileUrl($mixedHandler);
  125. }
  126. public function getFileMimeType($mixedHandler)
  127. {
  128. return $this->_oStorage->getMimeTypeByFileName($this->getFileUrl($mixedHandler));
  129. }
  130. /**
  131. * Get file url when file isn't transcoded yet
  132. */
  133. public function getFileUrlNotReady($mixedHandler)
  134. {
  135. return BX_DOL_URL_ROOT . 'image_transcoder.php?o=' . $this->_aObject['object'] . '&h=' . $mixedHandler . '&dpx=' . $this->getDevicePixelRatio() . '&t=' . time();
  136. }
  137. public function isFileReady ($mixedHandlerOrig, $isCheckOutdated = true)
  138. {
  139. if (isAdmin() && false !== $this->getFilterParams('ResizeVar')) { // only operators can apply new image size
  140. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandlerOrig);
  141. $aTranscodedFileData = $this->_oDb->getTranscodedFileData ($mixedHandler);
  142. $x = $this->getCustomResizeDimension ('x');
  143. $y = $this->getCustomResizeDimension ('y');
  144. // if new sizes are provided - delete old image, so new one will be created
  145. if (($x && (!isset($aTranscodedFileData['x']) || $x != $aTranscodedFileData['x'])) || ($y && (!isset($aTranscodedFileData['y']) || $y != $aTranscodedFileData['y']))) {
  146. if (!($iFileId = $this->_oDb->getFileIdByHandler($mixedHandler)))
  147. return false;
  148. if (!($aFile = $this->_oStorage->getFile($iFileId)))
  149. return false;
  150. if (!$this->_oStorage->deleteFile($aFile['id']))
  151. return false;
  152. }
  153. }
  154. return parent::isFileReady ($mixedHandlerOrig, $isCheckOutdated);
  155. }
  156. public function getSize ($sFile)
  157. {
  158. $oImage = BxDolImageResize::getInstance();
  159. $aSize = $oImage->getImageSize($sFile);
  160. return $aSize;
  161. }
  162. protected function getCustomResizeDimension ($sName)
  163. {
  164. $i = (int)bx_get($sName);
  165. if ($i > 2048) $i = 2048;
  166. if ($i && $i < 16) $i = 16;
  167. return $i;
  168. }
  169. public function transcode ($mixedHandler, $iProfileId = 0)
  170. {
  171. if (!($bRet = parent::transcode ($mixedHandler, $iProfileId)))
  172. return $bRet;
  173. $x = $this->getCustomResizeDimension ('x');
  174. $y = $this->getCustomResizeDimension ('y');
  175. if ($x || $y) {
  176. $mixedHandler = $this->processHandlerForRetinaDevice($mixedHandler);
  177. $this->_oDb->updateTranscodedFileData($mixedHandler, array('x' => $x, 'y' => $y));
  178. }
  179. return $bRet;
  180. }
  181. protected function applyFilter_Grayscale ($sFile, $aParams)
  182. {
  183. $o = BxDolImageResize::getInstance();
  184. $o->removeCropOptions ();
  185. if (IMAGE_ERROR_SUCCESS == $o->grayscale($sFile))
  186. return true;
  187. bx_log('sys_transcoder', "[{$this->_aObject['object']}] ERROR: applyFilter_Grayscale failed for file ({$sFile}): " . $o->getError(), BX_LOG_ERR);
  188. return false;
  189. }
  190. protected function applyFilter_ResizeVar ($sFile, $aParams)
  191. {
  192. $aParams['w'] = $this->getCustomResizeDimension ('x');
  193. $aParams['h'] = $this->getCustomResizeDimension ('y');
  194. if(!$aParams['w'])
  195. unset($aParams['w']);
  196. if(!$aParams['h'])
  197. unset($aParams['h']);
  198. if(!isset($aParams['w']) && !isset($aParams['h']))
  199. return true;
  200. return $this->applyFilter_Resize ($sFile, $aParams);
  201. }
  202. }
  203. /** @} */