Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide better support for high resolution images in mosaico #371

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CRM/Mosaico/Form/MosaicoAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ class CRM_Mosaico_Form_MosaicoAdmin extends CRM_Admin_Form_Setting {
protected $_settings = [
'mosaico_layout' => 'Mosaico Preferences',
'mosaico_graphics' => 'Mosaico Preferences',
'mosaico_scale_factor1' => 'Mosaico Preferences',
'mosaico_scale_factor2' => 'Mosaico Preferences',
'mosaico_scale_width_limit1' => 'Mosaico Preferences',
'mosaico_scale_width_limit2' => 'Mosaico Preferences',
'mosaico_custom_templates_dir' => 'Mosaico Custom Templates Directory',
'mosaico_custom_templates_url' => 'Mosaico Custom Templates URL'
];
Expand Down
4 changes: 3 additions & 1 deletion CRM/Mosaico/Graphics/Imagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*
* @see https://github.com/voidlabs/mosaico/blob/master/backend/README.txt
*/
class CRM_Mosaico_Graphics_Imagick implements CRM_Mosaico_Graphics_Interface {
class CRM_Mosaico_Graphics_Imagick extends CRM_Mosaico_Graphics_Interface {

/**
* CRM_Mosaico_Graphics_Imagick constructor.
Expand Down Expand Up @@ -79,6 +79,7 @@ public function createResizedImage($srcFile, $destFile, $width, $height) {
$mobileMinWidth = $config['MOBILE_MIN_WIDTH'];

$image = new Imagick($srcFile);
$this->adjustResizeDimensions($image->getImageWidth(), $image->getImageHeight(), $width, $height);

$resize_width = $width;
$resize_height = $image->getImageHeight();
Expand Down Expand Up @@ -109,6 +110,7 @@ public function createCoveredImage($srcFile, $destFile, $width, $height) {
$image = new Imagick($srcFile);

$image_geometry = $image->getImageGeometry();
$this->adjustResizeDimensions($image_geometry["width"], $image_geometry["height"], $width, $height);

$width_ratio = $image_geometry["width"] / $width;
$height_ratio = $image_geometry["height"] / $height;
Expand Down
58 changes: 54 additions & 4 deletions CRM/Mosaico/Graphics/Interface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* probably be better. As long as it remains internal, we have some
* flexibility to clean it up.
*/
interface CRM_Mosaico_Graphics_Interface {
abstract class CRM_Mosaico_Graphics_Interface {

/**
* Generate a placeholder image.
Expand All @@ -19,7 +19,7 @@ interface CRM_Mosaico_Graphics_Interface {
* @param int $height
* @return mixed
*/
public function sendPlaceholder($width, $height);
abstract public function sendPlaceholder($width, $height);

/**
* Generate a scaled version of the image.
Expand All @@ -40,7 +40,7 @@ public function sendPlaceholder($width, $height);
* NOTE: NULL or 0 are interpreted "auto-scaled".
* @return mixed
*/
public function createResizedImage($src, $dest, $width, $height);
abstract public function createResizedImage($src, $dest, $width, $height);

/**
* Generate a "cover" version of the image.
Expand All @@ -61,6 +61,56 @@ public function createResizedImage($src, $dest, $width, $height);
* NOTE: NULL or 0 are interpreted "auto-scaled".
* @return mixed
*/
public function createCoveredImage($src, $dest, $width, $height);
abstract public function createCoveredImage($src, $dest, $width, $height);

/**
* Adjust resize dimensions in order to preserve the best possible resolution for the image.
*
* @param int $imgWidth
* Image width in pixels.
* @param int $imgHeight
* Image height in pixels.
* @param int|NULL $resizeWidth
* Resize width in pixels.
* @param int|NULL $resizeHeight
* Resize height in pixels.
* @return float|null
*/
public function adjustResizeDimensions($imgWidth, $imgHeight, &$resizeWidth, &$resizeHeight) {
$scaleFactor = NULL;
$scales[Civi::settings()->get('mosaico_scale_width_limit1')] = Civi::settings()->get('mosaico_scale_factor1');
$scales[Civi::settings()->get('mosaico_scale_width_limit2')] = Civi::settings()->get('mosaico_scale_factor2');
$scales = array_filter($scales);
ksort($scales, SORT_NUMERIC);
if (!empty($scales) && $resizeWidth) {
foreach ($scales as $width => $slevel) {
if ($resizeWidth <= $width) {
$scaleFactor = $slevel;
break;
}
}
}
if (empty($scaleFactor)) {
return NULL;
}
// If scale-factor make new width bigger than that of image itself, re-compute scale-factor to
// maximum possible.
if ($scaleFactor && $resizeWidth && $imgWidth && ($imgWidth < ($resizeWidth * $scaleFactor))) {
$possibleLevels[] = $imgWidth / $resizeWidth;
}
if ($scaleFactor && $resizeHeight && $imgHeight && ($imgHeight < ($resizeHeight * $scaleFactor))) {
$possibleLevels[] = $imgHeight / $resizeHeight;
}
if (!empty($possibleLevels)) {
$scaleFactor = max($possibleLevels);
}
if ($scaleFactor && $resizeWidth) {
$resizeWidth = round($resizeWidth * $scaleFactor);
}
if ($scaleFactor && $resizeHeight) {
$resizeHeight = round($resizeHeight * $scaleFactor);
}
return $scaleFactor;
}

}
4 changes: 3 additions & 1 deletion CRM/Mosaico/Graphics/Intervention.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @see https://github.com/voidlabs/mosaico/blob/master/backend/README.txt
* @see http://image.intervention.io/getting_started/introduction
*/
class CRM_Mosaico_Graphics_Intervention implements CRM_Mosaico_Graphics_Interface {
class CRM_Mosaico_Graphics_Intervention extends CRM_Mosaico_Graphics_Interface {

const FONT_PATH = 'packages/mosaico/dist/vendor/notoregular/NotoSans-Regular-webfont.ttf';

Expand Down Expand Up @@ -96,6 +96,7 @@ protected static function flattenPoints($points) {
public function createResizedImage($srcFile, $destFile, $width, $height) {
$config = CRM_Mosaico_Utils::getConfig();
$img = Image::make($srcFile);
$this->adjustResizeDimensions($img->width(), $img->height(), $width, $height);

if ($width && $height) {
$img->resize($width, $height);
Expand All @@ -114,6 +115,7 @@ public function createResizedImage($srcFile, $destFile, $width, $height) {

public function createCoveredImage($srcFile, $destFile, $width, $height) {
$img = Image::make($srcFile);
$this->adjustResizeDimensions($img->width(), $img->height(), $width, $height);

$ratios = [];
if ($width) {
Expand Down
31 changes: 30 additions & 1 deletion CRM/Mosaico/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,36 @@ public static function getGraphicsOptions() {
];
}

/**
* Get a list of image resize scale factors
*
* @return array
* Array (int $machineName => string $label).
*/
public static function getResizeScaleFactor() {
return [
'' => E::ts('None'),
3 => E::ts('3x'),
2 => E::ts('2x'),
];
}

/**
* Get a list of image resize scale width limits
*
* @return array
* Array (int $machineName => string $label).
*/
public static function getResizeScaleWidthLimit() {
return [
'' => E::ts('None'),
190 => E::ts('Upto 190 pixels (e.g 3 column blocks)'),
285 => E::ts('Upto 285 pixels (e.g 2 column blocks)'),
999 => E::ts('Upto 570 pixels (e.g 1 column blocks)'),
9999 => E::ts('All (other) sizes'),
];
}

/**
* Get the path to the Mosaico layout file.
*
Expand Down Expand Up @@ -178,7 +208,6 @@ public static function getConfig() {
return $mConfig;
}


/**
* handler for upload requests
*/
Expand Down
Binary file added docs/images/scaling-factor-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/scaling-factor-resolution-diff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions docs/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Setup

## Support for High Resolution Images

Images when uploaded in 2 columns (e.g 258 x 100) or 3 (e.g 166 x 90) column blocks when upscaled to render on mobile devices, ends up with stretched out and lower resolution image.

Introduced `Image resize scale factor` mosaico setting attempts to solve the problem by scaling uploaded images to 2x or 3x of their block size specially for 2 and 3 column layouts so upscale doesn't look distorted or low resolution. The solution also work for single column images.

![](images/scaling-factor-config.png)

Following example shows how a correctly configured scaling factor can improve the resolution of image rendered.

![](images/scaling-factor-resolution-diff.png)

__Scaling factor config example__:
3x => Upto 285 pixels (covers both 2 and 3 column block images)
2x => All other sizes (single column block images)

Which means when an image is uploaded in a block with 285 pixels (or less), image gets trimmed to 285 * 3 pixels instead of 285 pixels.
For any image larger than that, size is reduced to 2x size of the block.

__Note__: higher the scaling factor, higher the resolution but lower the compression.

Scaling is not tied to any particular type of image. Png format supports lossless compression and therefore compression appears less than jpg images which support lossy compression.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ markdown_extensions:

nav:
- About: index.md
- Setup: setup.md
- API: api.md
- Development: develop.md
- Testing: testing.md
Expand Down
84 changes: 84 additions & 0 deletions settings/Mosaico.setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,90 @@
'description' => NULL,
'help_text' => NULL,
],
'mosaico_scale_factor1' => [
'group_name' => 'Mosaico Preferences',
'group' => 'mosaico',
'name' => 'mosaico_scale_factor1',
'quick_form_type' => 'Select',
'type' => 'String',
'html_type' => 'select',
'html_attributes' => [
'class' => 'crm-select2',
],
'pseudoconstant' => [
'callback' => 'CRM_Mosaico_Utils::getResizeScaleFactor',
],
'default' => '',
'add' => '5.24',
'title' => 'Image resize scale factor',
'is_domain' => 1,
'is_contact' => 0,
'description' => NULL,
'help_text' => NULL,
],
'mosaico_scale_factor2' => [
'group_name' => 'Mosaico Preferences',
'group' => 'mosaico',
'name' => 'mosaico_scale_factor2',
'quick_form_type' => 'Select',
'type' => 'String',
'html_type' => 'select',
'html_attributes' => [
'class' => 'crm-select2',
],
'pseudoconstant' => [
'callback' => 'CRM_Mosaico_Utils::getResizeScaleFactor',
],
'default' => '',
'add' => '5.24',
'title' => 'Image resize scale factor',
'is_domain' => 1,
'is_contact' => 0,
'description' => NULL,
'help_text' => NULL,
],
'mosaico_scale_width_limit1' => [
'group_name' => 'Mosaico Preferences',
'group' => 'mosaico',
'name' => 'mosaico_scale_width_limit1',
'quick_form_type' => 'Select',
'type' => 'String',
'html_type' => 'select',
'html_attributes' => [
'class' => 'crm-select2',
],
'pseudoconstant' => [
'callback' => 'CRM_Mosaico_Utils::getResizeScaleWidthLimit',
],
'default' => '',
'add' => '5.24',
'title' => 'Image resize scale factor width limit',
'is_domain' => 1,
'is_contact' => 0,
'description' => NULL,
'help_text' => NULL,
],
'mosaico_scale_width_limit2' => [
'group_name' => 'Mosaico Preferences',
'group' => 'mosaico',
'name' => 'mosaico_scale_width_limit2',
'quick_form_type' => 'Select',
'type' => 'String',
'html_type' => 'select',
'html_attributes' => [
'class' => 'crm-select2',
],
'pseudoconstant' => [
'callback' => 'CRM_Mosaico_Utils::getResizeScaleWidthLimit',
],
'default' => '',
'add' => '5.24',
'title' => 'Image resize scale factor width limit',
'is_domain' => 1,
'is_contact' => 0,
'description' => NULL,
'help_text' => NULL,
],
'mosaico_custom_templates_dir' => [
'group_name' => 'Mosaico Preferences',
'group' => 'mosaico',
Expand Down
10 changes: 10 additions & 0 deletions templates/CRM/Mosaico/Form/MosaicoAdmin.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@
{$form.mosaico_custom_templates_url.html|crmAddClass:'huge40'}
</td>
</tr>
<tr class="crm-mosaico-form-block-mosaico_scale_factor">
<td class="label">
{$form.mosaico_scale_factor1.label}
</td>
<td>
{$form.mosaico_scale_factor1.html|crmAddClass:six} {ts}for resize of images with width{/ts} {$form.mosaico_scale_width_limit1.html|crmAddClass:huge}<br/>
{$form.mosaico_scale_factor2.html|crmAddClass:six} {ts}for resize of images with width{/ts} {$form.mosaico_scale_width_limit2.html|crmAddClass:huge}<br/>
<span class="description">{ts}When uploading images, the mosaico editor trims it down to very required size (in pixels). Use scale factor setting to keep some buffer (2x or 3x) so upscale doesn't look distorted or low resolution. Example:{/ts}<br/>{ts}3x => Upto 285 pixels (covers both 2 and 3 column block images){/ts}<br/>{ts}2x => All other sizes (single column block images){/ts}</span>
</td>
</tr>
</table>
<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
</div>
Expand Down
Loading