Skip to content

Commit

Permalink
Preserve aspect ratio of images
Browse files Browse the repository at this point in the history
When posible, change the image size so the aspect ratio is always
preserved. This is only posible when one or both dimensions have some
room to play. When both dimensions are not fixed, a naive algorithm
tries to satisfy all {min,max}-{width,height} and aspect ratio
constraints, but it may fail. In that case, the aspect ratio is not
preserved.

Fixes the HTML tests img-aspect-ratio and img-max-bounds.
  • Loading branch information
rodarima committed Aug 12, 2024
1 parent 8a422fe commit d2d2b33
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 19 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dillo-3.2.0 [Not released yet]
GNU extensions.
- Perform an emergency stop of the layout engine loop after 1000 iterations to
prevent a hang.
- Improve image resize logic to always try to preserve the aspect ratio.
Patches: Rodrigo Arias Mallo
+- Add primitive support for SVG using the nanosvg.h library.
Patches: dogma, Rodrigo Arias Mallo
Expand Down
184 changes: 167 additions & 17 deletions dw/image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Dillo Widget
*
* Copyright 2005-2007 Sebastian Geerken <[email protected]>
* Copyright 2024 Rodrigo Arias Mallo <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -23,6 +24,7 @@
#include "dlib/dlib.h"
#include "../lout/msg.h"
#include "../lout/misc.hh"
//#define DEBUG_LEVEL 1
#include "../lout/debug.hh"

namespace dw {
Expand Down Expand Up @@ -177,6 +179,8 @@ void Image::sizeRequestSimpl (core::Requisition *requisition)
{
DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");

/* First set the a naive size based on the image properties if given */

if (buffer) {
requisition->width = buffer->getRootWidth ();
requisition->ascent = buffer->getRootHeight ();
Expand All @@ -201,9 +205,15 @@ void Image::sizeRequestSimpl (core::Requisition *requisition)
requisition->ascent += boxOffsetY ();
requisition->descent += boxRestHeight ();

/* Then correct the size, so it fits within the {min,max}-{width,height}
* contraints */

correctRequisition (requisition, core::splitHeightPreserveDescent, true,
true);

/* Finally, ensure that we don't distort the image unless the height and
* width are fixed. */

if (buffer) {
// If one dimension is set, preserve the aspect ratio (without
// extraSpace/margin/border/padding). Notice that
Expand All @@ -214,24 +224,164 @@ void Image::sizeRequestSimpl (core::Requisition *requisition)
// TODO Check again possible overflows. (Aren't buffer
// dimensions limited to 2^15?)

bool widthSpecified = getStyle()->width != core::style::LENGTH_AUTO ||
getStyle()->minWidth != core::style::LENGTH_AUTO ||
getStyle()->maxWidth != core::style::LENGTH_AUTO;
bool heightSpecified = getStyle()->height != core::style::LENGTH_AUTO ||
getStyle()->minHeight != core::style::LENGTH_AUTO ||
getStyle()->maxHeight != core::style::LENGTH_AUTO;

if (!widthSpecified && heightSpecified)
requisition->width =
(requisition->ascent + requisition->descent - boxDiffHeight ())
* buffer->getRootWidth () / buffer->getRootHeight ()
+ boxDiffWidth ();
else if (widthSpecified && !heightSpecified) {
requisition->ascent = (requisition->width + boxDiffWidth ())
* buffer->getRootHeight () / buffer->getRootWidth ()
+ boxOffsetY ();
requisition->descent = boxRestHeight ();
bool wFixed = getStyle()->width != core::style::LENGTH_AUTO;
bool hFixed = getStyle()->height != core::style::LENGTH_AUTO;

/* Image dimensions */
int w = buffer->getRootWidth ();
int h = buffer->getRootHeight ();

/* Reference size in case we are the root */
int w_ref = -1, h_ref = -1;

/* TODO: What if we have a quasiParent? */
if (getParent() == NULL) {
w_ref = layout->getWidthViewport();
h_ref = layout->getHeightViewport();
}

/* FIXME: Cannot get private information from Layout to avoid the
* scrollbar width. */
//int w_ref = layout->getWidthViewport() - (layout->canvasHeightGreater ?
// layout->vScrollbarThickness : 0);

DEBUG_MSG(1, "initial size: w=%d, h=%d\n", w, h);

float ratio = (float) h / (float) w;

DEBUG_MSG(1, "wFixed=%d, hFixed=%d\n", wFixed, hFixed);

/* When the size is fixed, set w and h accordingly */
if (hFixed) {
int hh = calcHeight(getStyle()->height, true, h_ref, getParent(), true);
if (hh == -1)
hFixed = false;
else
h = hh;
}

if (wFixed) {
int ww = calcWidth(getStyle()->width, w_ref, getParent(), 0, true);
if (ww == -1)
wFixed = false;
else
w = ww;
}

DEBUG_MSG(1, "size after fixed correction: w=%d, h=%d\n", w, h);

/* Correct the image size to keep the aspect ratio, but only change the
* dimensions that are not fixed, if any. */

if (!wFixed && !hFixed) {
DEBUG_MSG(1, "case: both can change\n");
/* Both width and height are not set, so we can play with both dimensions. */
int minWidth = calcWidth(getStyle()->minWidth, w_ref, getParent(), 0, true);
int maxWidth = calcWidth(getStyle()->maxWidth, w_ref, getParent(), 0, true);
int minHeight = calcHeight(getStyle()->minHeight, true, h_ref, getParent(), true);
int maxHeight = calcHeight(getStyle()->maxHeight, true, h_ref, getParent(), true);

DEBUG_MSG(1, "minWidth = %d, maxWidth = %d\n", minWidth, maxWidth);
DEBUG_MSG(1, "minHeight= %d, maxHeight= %d\n", minHeight, maxHeight);

/* Fix broken width by expanding the maximum */
if (minWidth != -1 && maxWidth != -1 && minWidth > maxWidth)
maxWidth = minWidth;

/* Fix broken height by expanding the maximum */
if (minHeight != -1 && maxHeight != -1 && minHeight > maxHeight)
maxHeight = minHeight;

DEBUG_MSG(1, "corrected minWidth = %d, maxWidth = %d\n", minWidth, maxWidth);
DEBUG_MSG(1, "corrected minHeight= %d, maxHeight= %d\n", minHeight, maxHeight);

/* Repeat 2 times fixing the aspect ratio, two more without */
for (int i = 0; i < 4; i++) {
DEBUG_MSG(1, "iteration %d, w=%d, h=%d\n", i, w, h);
int old_w = w;

/* Constraint the width first */
if (maxWidth != -1 && w > maxWidth)
w = maxWidth;
else if (minWidth != -1 && w < minWidth)
w = minWidth;

/* Now we may have distorted the image, so try to fix the aspect ratio */
if (w != old_w && i < 2)
h = w * ratio;

int old_h = h;

/* Now constraint the height */
if (maxHeight != -1 && h > maxHeight)
h = maxHeight;
else if (minHeight != -1 && h < minHeight)
h = minHeight;

/* Now we may have distorted the image again, so fix w */
if (h != old_h && i < 2)
w = h / ratio;

DEBUG_MSG(1, "w=%d, h=%d\n", w, h);

/* All contraints meet */
if (old_h == h && old_w == w)
break;
}
} else if (wFixed && !hFixed) {
/* Only height can change */
DEBUG_MSG(1, "case: only heigh can change\n");
int minHeight = calcHeight(getStyle()->minHeight, true, h_ref, getParent(), true);
int maxHeight = calcHeight(getStyle()->maxHeight, true, h_ref, getParent(), true);

DEBUG_MSG(1, "minHeight= %d, maxHeight= %d\n", minHeight, maxHeight);

/* Fix broken height by expanding the maximum */
if (minHeight != -1 && maxHeight != -1 && minHeight > maxHeight)
maxHeight = minHeight;

DEBUG_MSG(1, "corrected minHeight= %d, maxHeight= %d\n", minHeight, maxHeight);

/* Try preserving the ratio */
h = w * ratio;

/* Now constraint the height */
if (maxHeight != -1 && h > maxHeight)
h = maxHeight;
else if (minHeight != -1 && h < minHeight)
h = minHeight;
} else if (!wFixed && hFixed) {
/* Only width can change */
DEBUG_MSG(1, "case: only width can change\n");
int minWidth = calcWidth(getStyle()->minWidth, w_ref, getParent(), 0, true);
int maxWidth = calcWidth(getStyle()->maxWidth, w_ref, getParent(), 0, true);

DEBUG_MSG(1, "minWidth = %d, maxWidth = %d\n", minWidth, maxWidth);

/* Fix broken width by expanding the maximum */
if (minWidth != -1 && maxWidth != -1 && minWidth > maxWidth)
maxWidth = minWidth;

DEBUG_MSG(1, "corrected minWidth = %d, maxWidth = %d\n", minWidth, maxWidth);

/* Try preserving the ratio */
w = h / ratio;
DEBUG_MSG(1, "by ratio, w=%d\n", w);

/* Now constraint the width */
if (maxWidth != -1 && w > maxWidth)
w = maxWidth;
else if (minWidth != -1 && w < minWidth)
w = minWidth;
} else {
/* Both dimensions are fixed, so nothing to do. */
}

DEBUG_MSG(1, "final: w=%d, h=%d\n", w, h);

requisition->width = w + boxDiffWidth ();
requisition->ascent = h + boxOffsetY ();
requisition->descent = boxRestHeight ();
}

DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
Expand Down
2 changes: 0 additions & 2 deletions test/html/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ TESTS = \
XFAIL_TESTS = \
render/div-100-percent-with-padding.html \
render/float-img-justify.html \
render/img-aspect-ratio.html \
render/img-max-bounds.html \
render/margin-auto.html \
render/max-width-html.html \
render/min-width-html.html \
Expand Down

0 comments on commit d2d2b33

Please sign in to comment.