S
S
Snewer2016-08-04 16:52:22
PHP
Snewer, 2016-08-04 16:52:22

How to choose image sizes to get a rectangle?

Hello!
There is an array with image data:

[
    [
        'url' => 'ссылка на изображение',
        'width' => 'ширина изображения',
        'height' => 'высота изображения',
        ...
    ]
]

It is necessary to assemble a rectangle from these images (a proportional reduction in size is allowed). That is, we need approximately the same output of images as it is implemented in VKontakte.
Are there any algorithms? Ideas?
Thank you!

Answer the question

In order to leave comments, you need to log in

3 answer(s)
X
xmoonlight, 2016-08-04
@Snewer

1. Divide your future rectangle into a 50x50px grid of areas, for example.
2. Determine the aspect ratio of each image and fit it into the grid segments (determine through the multiplicity).
3. After receiving the "figures" - you need to place them one after the other.
4. The easiest way to fill with a maximum area is 6 blocks high and brickwork: there is a gap - we are looking for a suitable block in the height of the block, we did not find a suitable block - we put another instead of the previous one and look again.

L
Lander, 2016-08-04
@usdglander

Here, most likely, cropping can be dispensed with only in exceptional cases! The easiest way is to bring all images to the same aspect ratio format and then line them up. For example take for l = 100 points. Go through all images, convert all lengths or heights to n * l format. Bring the other side to m * l with a pruning and line up from here. Well, this is the general plan of action. In general, this task is similar to the "package task". Google it, maybe some ideas will come to mind.

6
65536, 2016-08-04
@65536

sit and purposefully think about this topic, try to do it, then it will turn out sooner or later))
you can make it look dumb, ignore the proportions of the pictures, make everything the same squares or rectangles. for example, there are 6 pictures, one is drawn large (possible in real proportions), under it / on the side we draw 5 identical ones, in which the width (if under / above is large) or height (if on the side) is equal to the size of the large picture divided by 5. if 7 pictures , then one large one and under it 2 rows of 3, again with such dimensions to fill the desired width / height. it is not difficult to make such an algorithm, it is arithmetic.
and in the squares already enter the pictures so that they occupy them entirely (crop)
so I can warm up my inscriber

class Wrapper
{
    private $sourceWidth;
    private $sourceHeight;

    public function sourceSize($width, $height)
    {
        $this->sourceWidth = $width;
        $this->sourceHeight = $height;

        return $this;
    }

    private $targetWidth;
    private $targetHeight;

    public function targetSize($width = false, $height = false)
    {
        $this->targetWidth = $width;
        $this->targetHeight = $height;

        return $this;
    }

    private $preventUpsize = false;

    public function preventUpsize($value = true)
    {
        $this->preventUpsize = $value;

        return $this;
    }

    private $ratio;
    private $left;
    private $top;
    private $width;
    private $height;

    /**
     * растянуть/сжать так, чтобы source полностью поместилось в target
     */
    public function fit()
    {
        $sourceRatio = $this->sourceWidth / $this->sourceHeight;

        if (!$this->targetWidth || !$this->targetHeight) {
            if ($this->targetWidth) {
                $this->targetHeight = $this->targetWidth / $sourceRatio;
            } elseif ($this->targetHeight) {
                $this->targetWidth = $sourceRatio / $this->targetHeight;
            }

            $targetRatio = $sourceRatio;
        } else {
            $targetRatio = $this->targetWidth / $this->targetHeight;
        }

        if ($sourceRatio > $targetRatio) {
            $this->ratio = $this->targetWidth / $this->sourceWidth;
        } else {
            $this->ratio = $this->targetHeight / $this->sourceHeight;
        }

        $this->resize();
        $this->center();

        return $this->getOutput();
    }

    /**
     * растянуть/сжать так, чтобы source заняло все пространство target
     */
    public function fill()
    {
        $sourceRatio = $this->sourceWidth / $this->sourceHeight;

        if (!$this->targetWidth || !$this->targetHeight) {
            if ($this->targetWidth) {
                $this->targetHeight = $this->targetWidth / $sourceRatio;
            } elseif ($this->targetHeight) {
                $this->targetWidth = $sourceRatio / $this->targetHeight;
            }

            $targetRatio = $sourceRatio;
        } else {
            $targetRatio = $this->targetWidth / $this->targetHeight;
        }

        if ($sourceRatio < $targetRatio) {
            $this->ratio = $this->targetWidth / $this->sourceWidth;
        } else {
            $this->ratio = $this->targetHeight / $this->sourceHeight;
        }

        $this->resize();
        $this->center();

        return $this->getOutput();
    }

    private function resize()
    {
        if ($this->preventUpsize && $this->ratio > 1) {
            $this->ratio = 1;
        }

        $this->width = $this->sourceWidth * $this->ratio;
        $this->height = $this->sourceHeight * $this->ratio;
    }

    private function center()
    {
        $this->left = ($this->targetWidth - $this->width) / 2;
        $this->top = ($this->targetHeight - $this->height) / 2;
    }

    private function getOutput()
    {
        return [
            'left'   => $this->left,
            'top'    => $this->top,
            'width'  => $this->width,
            'height' => $this->height
        ];
    }
}

The dimensions of the picture and the rectangle to be filled with the picture must be known. That is, you first need to calculate the sizes of these rectangles, and then resize the pictures for them. More or less like this:
$wrapper = new Wrapper;

$wrapper->sourceSize($imageWidth, $imageHeight);
$wrapper->targetSize($containerWidth, $containerHeight);

$wrapData = $wrapper->fill();

$wrapData will contain the desired width and height and offset (centering)
This data can already be used anywhere, in html or js. I write directly in html styles (no one died):
<div class="image_wrapper" style="width: {WRAPPER_WIDTH}; height: {WRAPPER_HEIGHT};">
    <div style="left: {LEFT}px; top: {TOP}px">
        <img src="{SRC}" width="{WIDTH}" height="{HEIGHT}">
    </div>
</div>

WRAPPER_WIDTH - $containerWidth
WRAPPER_HEIGHT - $containerHeight
LEFT - $wrapData['left']
TOP - $wrapData['top']
WIDTH - $wrapData['width']
HEIGHT - $wrapData['height']
css needed
.image_wrapper {
    position: relative;
    overflow: hidden;

    > div {
        position: relative;
    }
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question