resize obrazkov prilis neproporcionalnych rozmerov

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Bladerik
Člen | 1
+
0
-

Zdravim mam problem pri uploade obrazkov s prilis neproporcionalnymi rozmermi, pri uploade obrazku ktory chcem resiznut a ma sirku napr. 5 px a vysku napr. 5000 px mi vyskoci vynimka
‚Image width and height must be greater than zero‘

ono to vyzera tak ze ked resizujem ten obrazok tak nette dopocita sirku mensiu ako 1 pixel, zatial to riesim odchytavanim exceptionov ale skor by som prijal nieco taketo

<?php
if($obrZmenseny->getWidth() < 1){
   $obrZmenseny->resize(1, 60, Image::ENLARGE | Image::STRETCH);
} else if ($obrZmenseny->getHeight() < 1){
   $obrZmenseny->resize(80, 1, Image::ENLARGE | Image::STRETCH);
}
?>

ale kedze sa mi vynimka vyhodi na riadku ktory je pred touto podmienkou tak sa to k tomu ani nedostane, konkretne vynimka vyskoci tu na druhom riadku

<?php
if($obrZmenseny->getHeight() > 60){
	$obrZmenseny->resize(80, 60);
} else {
	$obrZmenseny->resize(80, 60, NImage::ENLARGE);
}
?>

ono je malo pravdepodobne ze mi niekto bude uploadovat obrazok s takymito rozmermi ale to co programujem by som chcel mat co najuniverzalnejsie :)
este raz opakujem ze pri inych obrazkoch ktore nemaju takyto velky percentualny rozdiel v sirke a vyske mi vsetko funguje bezproblemovo.

pouzivam Nette Framework 2.0-dev (revision 9e52d77 released on 2010–12–21)
dakujem za rady :)

Šaman
Člen | 2666
+
0
-

Tohle asi hoď do sekce Hlášení chyb. Jak k tomu došlo je zřejmé.
Vypočítaná hodnota se zaokrouhlí pomocí fce round(), takže pokud se vypočítá méně jak půlpixel, tak se zaokrouhlí na nulu..

Takže tohle bude muset opravit přímo David.

Editoval Šaman (27. 12. 2010 11:16)

kravčo
Člen | 721
+
0
-

Šaman napsal(a):

Takže tohle bude muset opravit přímo David.

Takže stačí poslať patch na github.

Šaman
Člen | 2666
+
0
-

kravčo napsal(a):

Takže stačí poslať patch na github.

A to můžem i my, prostí neGuru? :)

Bohužel nemám právě teď čas zkoušet GIT (v práci máme SVN) tak prosím někoho pro koho to bude jen pár kliků, jestli by mohl poslat tento patch pod čarou:

Původní Image.php

<?php
342:             $newWidth = round($srcWidth * $scale);
343:             $newHeight = round($srcHeight * $scale);
?>

Upravený Image.php

<?php
342:             $newWidth = max(round($srcWidth * $scale), 1);
343:             $newHeight = max(round($srcHeight * $scale), 1);
?>

//Edit: Tak to co jsem poslal by opravilo JEN problém s proporciální změnou, ale obecně u všech resizů by bylo lepší upravit až poslední řádek funkce calculateSize() v Image.php

<?php
346:         return array((int) $newWidth, (int) $newHeight);

?>

Upravený Image.php

<?php
346:         return array( max( (int) $newWidth, 1), max( (int) $newHeight, 1) );
?>

Ale to už mi zase připadá na úkor čitelnosti..

Editoval Šaman (28. 12. 2010 11:46)

Bernard Williams
Člen | 207
+
0
-

Nazdárek,

nebylo by jednodušší použít ceil (zaokrouhlení nahoru) místo konstrukce max?

Bernard

Šaman
Člen | 2666
+
0
-

To by byl už zásah který něco mění, chtěl jsem patch který opravuje JEN případný chybný stav a navíc na jediném řádku. (Profesionální deformace z doby, kdy jsem upravoval 10 let staré, nezdokumentované prográmky v Pascalu které řídily výrobní stroje ⇒ žádná oprava není dost malá, aby nenasekala další chyby.)

Samozřejmě zaokrouhlení nahoru by problém řešilo taky, jen by bylo více změn.

Editoval Šaman (28. 12. 2010 11:52)

jtousek
Člen | 951
+
0
-

Šaman: Ten tvůj fix ale znamená že když někdo zadá $image->resize(0, 0) tak se z toho udělá 1×1, nebo se mýlím?

ceil +1

Šaman
Člen | 2666
+
0
-

Jj, to znamená. Ale taky to znamená, že mu to nevyhodí výjimku a resizne mu to na nejmenší možný rozměr.

Ceil změní chování už existující funkce (i když minimálně), proto se mi moc nelíbí. Max se bude chovat naprosto stejně jako doposud, dokud jeden rozměr nebude nulový (nebo nedejbože záporný, i když to by nastat nikdy nemělo).

Nechám to na tom, kdo bude patch posílat. Je ale vidět, že i na takové drobnosti může být více názorů, proto jsem předpokládal že právo hrabat se v repozitáři mají jen vybraní jedinci.

kravčo
Člen | 721
+
0
-

Šaman napsal(a):

kravčo napsal(a):

Takže stačí poslať patch na github.

A to můžem i my, prostí neGuru? :)

Áno, a neverili by ste, je to veľmi jednoduché. Právo poslať patch má totiž každý. Na patch totiž nepotrebujete prístup do repozitára Nette, posielaný patch je návrhom na zmenu a autor sa môže rozhodnúť či zmenu prijme. (rovnako aj ostatní ľudia s write prístupom do repozitára). Github oplýva nápovedami, takže ani prostí kvázi-neGuruovia, ktorí píšu Nette aplikácie a pracujú s SVN s ním nebudú mať najmenší problém. Stačí len chcieť…

(pozn.: autor príspevku sa za gurua nepovažuje)


Fascinuje ma, ako sa pri nájdení chybky v nette rozthrne vrece s príspevkami typu áno toto určite treba opraviť a (môj obľúbený) +1, zväčša bez riešenia, alebo bez argumentov. Namiesto toho sa zväčša stačí na chvíľu zamyslieť, napísať opravu, k nej jeden-dva testy a poslať pull-request… Alebo diskutovať a navrhnúť riešenie a argumenty preň.


K problému.

Uvažujem na čo je dobrý náhľad obrázku 300×1px (z pôvodného 9000×10px) keď na ňom hovno uvidím. Určite viac platný je náhľad 300×10px (výrez), na ktorom už niečo vidno bude. Mám za to, že vyhodenie takejto výnimky je trochu blbé, ale osobne mi príde náhľad 300×1px blbší. Riešením by mohla byť iná výnimkav metóde Image::resize():

if ($newWidth === 0 || $newHeight === 0) {
    throw new \InvalidStateException('Image cannot be resized without stretching, width-to-height ratio is out of range.');
}

Bladerik napsal(a):

workaround

<?php

use Nette\Image;

//...

$img = Image::fromFile('more-than-wide.jpg'); // 5000x5px

list($w, $h) = Image::calculateSize(
    $img->width, $img->height, 80, 80, Image::FIT
);
$w = max(1, $w);
$h = max(1, $w);
$img->resize($w, $h, Image::STRETCH);

$img->send();
Šaman
Člen | 2666
+
0
-

kravčo napsal(a):

Mám za to, že vyhodenie takejto výnimky je trochu blbé, ale osobne mi príde náhľad 300×1px blbší.

Průšvih nastane, když máš například možnost uploadovat obrázky k nějakému příspěvku a automaticky generuješ náhledy zmenšením. A pak nějaký chalan uploadne panorama 9000×10px a ty mu vyhodíš pětistovku.. To je podle mě nejhorší.

<blbost>Jestli chceš předjímat co bude uživateli užitečné, tak budeme muset hlasovat jaký rozměr je minimální a tu InvalidStateException('Image cannot be resized without stretching..) budeme vyhazovat pokud některý rozměr klesne pod ten rozměr (navrhuji 5px).</blbost>

Takže jde o to, jak patchnout funkci resize, aby při jakémkoliv platném vstupu neházela výjimku, což teď dělá. Ano, lepší řešení než automatický resize naslepo je výřez. Ve svém projektu jsem to použil taky tak. Ale to není problém, který řešíme.

<?php
// tvorba náhledu z většího obrázku výřezem na střed
$thumbnailImage = clone $image;

$width = $thumbnailImage->width;
$height = $thumbnailImage->height;

if ( $width/MyImage::WIDTH_THUMBNAIL > $height/MyImage::HEIGHT_THUMBNAIL ) // kdyz je obrazek pomerne delsi nez vyssi
{ // tak upravime vysku a potom orizneme na spravnou sirku
	$thumbnailImage->resize(NULL, MyImage::HEIGHT_THUMBNAIL);
}
else
{ // jinak upravime sirku a potom orizneme na spravnou vysku
	$thumbnailImage->resize(MyImage::WIDTH_THUMBNAIL, NULL);
}

$thumbnailImage->crop( ( $thumbnailImage->width / 2 - MyImage::WIDTH_THUMBNAIL / 2 ), ( $thumbnailImage->height / 2 - MyImage::HEIGHT_THUMBNAIL / 2 ), MyImage::WIDTH_THUMBNAIL, MyImage::HEIGHT_THUMBNAIL);
?>

Editoval Šaman (28. 12. 2010 19:59)

kravčo
Člen | 721
+
0
-

Príklad, keď expert uploadne „panorámu“ 9000×10px je asi taký pravdepodobný ako že ti uploadne chybný typ súboru a výsledok je rovnaký – vyhodená výnimka.


Upísal som sa, chcel som vyhadzovať Nette\ImageException a predpokladal som použitie:

try {
    $img->resize(80, 80);

} catch (Nette\ImageException $e) {
    list($w, $h) = Image::calculateSize(
        $img->width, $img->height, 80, 80, Image::FIT
    );
    $w = max(1, $w);
    $h = max(1, $w);
    $img->resize($w, $h, Image::STRETCH);
}

Čo nie je veľmi použiteľné v praxi, keďže je to BC break, obaľovanie každého resize() do takéhoto try-catch bloku je tiež nafigu a metóda by to mohla riešiť za nás… Teda štandardne vygenerovať blbý náhľad s tým, že okrajové podmienky si môže programátor predtým ošetriť sám a upraviť volanie resize() podľa potrieb.

Šaman
Člen | 2666
+
0
-

Tak jsem si založil účet na GitHUBu a pullnul jsem tu opravu pomocí fce max.
Akorát k tomu nejsou žádné automatické testy a k ručnímu otestování se dostanu až po Silvestru. (Dneska hned po práci odjíždím do hor. Tak pokud mě nezasype lavina, sypejte mi popel na hlavu až o víkendu.)

//Edit: Tak odzkoušeno alespoň použitím v aplikaci. A musím říct, že z obrázku 800×2px se kloudný náhled nevytvoří ani výřezem na střed, dokonce ani když se napřed zvětší a pak ořeže. Prostě kde nic není, ani čert nebere.. Ale obejde se to bez výjimky.

Editoval Šaman (29. 12. 2010 10:53)