Zpracování ikon
Do diskusí na těchto stránkách jsem doplnil možnost zadat adresu vlastních webových stránek. To umožňuje lépe identifikovat autora příspěvku. Pro zpestření vzhledu diskuse a pro rychlejší orientaci v ní se navíc zobrazuje u autora ikona jeho webu. Není to gravatar (které nemám moc rád), ale je to soubor favicon.ico
z kořenového adresáře autorova webu (nenahlíží se tedy do stránky na hodnotu <link>
značky shortcut icon
).
Se zobrazením ikon je problém v tom, že prohlížeče (přinejmenším IE6) je sice dokáží zobrazit u adresy stránky nebo v oblíbených položkách, ale nedokáží to přímo v kódu stránky. Proto je nutné ikony převést do nějakého běžného formátu. K tomu můžeme použít buď existující knihovny nebo se podívat, jak formát vypadá a napsat si zpracování sami.
Já jsem zvolil druhý případ a vytvořil následující dvě funkce. Funkce imagecreatefromico
vezme název souboru s ikonou a požadované rozměry vráceného obrázku a pokusí se v souboru s ikonami najít ikonu daného rozměru s maximálním možným počtem barev. Když se jí to nepovede, tak vezme největší dostupnou ikonu, kterou na požadované rozměry následně převzorkuje.
<?php /** Načtení obrázku z ikony, pokud není k dispozici požadovaný rozměr ikony, tak se na něj převede největší ikona * @param string název souboru * @param int požadovaná šířka ikony * @param int požadovaná výška ikony * @return resource plnobarevný obrázek s ikonou požadovaných rozměrů nebo false v případě chyby * @copyright Jakub Vrána, https://php.vrana.cz/ */ function imagecreatefromico($filename, $width, $height) { $file = file_get_contents($filename); if (!$file) { return false; } $data = unpack("vcount", substr($file, 4, 2)); $read = array(); for ($i=0; $i < $data["count"]; $i++) { $icon = unpack("Cwidth/Cheight/Ccolors/Creserved/vplanes/vbitcount/Vlength/Voffset", substr($file, 6+$i*16, 16)); if (!$icon || !($icon2 = unpack("vibitcount/Vcompression", substr($file, $icon["offset"] + 14, 6)))) { return false; } if (!$read || ($read["width"] == $width && $read["height"] == $height ? $icon["width"] == $width && $icon["height"] == $height && $icon2["ibitcount"] > $read["ibitcount"] : $icon["width"] >= $read["width"] && $icon["height"] >= $read["height"] && $icon2["ibitcount"] >= $read["ibitcount"] )) { $read = $icon + $icon2; } } $im = icon_read(substr($file, $read["offset"] + 40, $read["length"]), $read["width"], $read["height"], $read["ibitcount"], $read["compression"]); if ($read["width"] == $width && $read["height"] == $height) { return $im; } $im2 = imagecreatetruecolor($width, $height); imagecopyresampled($im2, $im, 0, 0, 0, 0, $width, $height, imagesx($im), imagesy($im)); return $im2; } ?>
Pro pohodlné získávání hodnot ze souboru se používá funkce unpack, pro vlastní načtení dat potom uživatelská funkce icon_read
:
<?php /** Načtení obrazových dat ikony * @param string obrazová data * @param int šířka obrázku * @param int výška obrázku * @param int počet bitů na pixel * @param int komprese dat - ignoruje se * @return resource plnobarevný obrázek s ikonou * @copyright Jakub Vrána, https://php.vrana.cz/ */ function icon_read($file, $width, $height, $bitcount, $compression = 0) { $im = imagecreatetruecolor($width, $height); imagefilledrectangle($im, 0, 0, $width-1, $height-1, imagecolorallocate($im, 255, 255, 255)); // mohla by se používat transparentnost if ($bitcount <= 8) { $colors = array(); for ($i=0; $i < pow(2, $bitcount); $i++) { $color = unpack("Cblue/Cgreen/Cred", substr($file, 4*$i, 3)); $colors[] = imagecolorallocate($im, $color["red"], $color["green"], $color["blue"]); } $file = substr($file, 4*pow(2, $bitcount)); } for ($y=0; $y < $height; $y++) { for ($x=0; $x < $width; $x++) { $offset = 32*$y*ceil($width*$bitcount/32) + $x*$bitcount; $trans = ord($file[4*$height*ceil($width*$bitcount/32) + 4*$y*ceil($width/32) + floor($x/8)]) & (1 << (7 - $x % 8)); if ($bitcount <= 8) { $byte = ord($file[floor($offset/8)]); $color = $colors[$byte >> (8 - $bitcount - $offset % 8) & (pow(2, $bitcount) - 1)]; } elseif ($bitcount == 16) { $colors = unpack("nbgr", substr($file, $offset/8, 2)); $color = imagecolorallocate($im, round(255/31 * ($colors["bgr"] & 31)), round(255/63 * (($colors["bgr"] >> 5) & 63)), round(255/31 * ($colors["bgr"] >> 11))); } elseif ($bitcount == 32) { $colors = unpack("Cblue/Cgreen/Cred/Calpha", substr($file, $offset/8, 4)); $color = imagecolorallocate($im, 255 - $colors["alpha"] + round($colors["red"] * $colors["alpha"] / 255), 255 - $colors["alpha"] + round($colors["green"] * $colors["alpha"] / 255), 255 - $colors["alpha"] + round($colors["blue"] * $colors["alpha"] / 255)); } else { $colors = unpack("Cblue/Cgreen/Cred", substr($file, $offset/8, 3)); $color = imagecolorallocate($im, $colors["red"], $colors["green"], $colors["blue"]); } if (!$trans) { // $background[0] ^ $colors["red"] prohlížeče nepoužívají imagesetpixel($im, $x, $height - $y - 1, $color); } } } return $im; } ?>
Funkce převezme vlastní obrazová data a informace o nich a pomocí funkce imagesetpixel je umístní do obrázku. Pro získání informací o transparentnosti a indexech barev u paletových obrázků se používají bitové operace.
Pro průhledné body by se dala používat průhledná barva, ta je ale u plnobarevných PNG obrázků problematická. Navíc se obrázek může převzorkovat, při čemž se informace o průhlednosti ztratí. Proto se pro pozadí použije jednoduše bílá barva.
Obrázek vrácený funkcí imagecreatefromico
můžeme třeba funkcí imagepng uložit do souboru nebo poslat prohlížeči.
Na adrese http://www.google.com/s2/favicons?domain=
běží webová služba pro získávání ikon ve formátu PNG.
Implementace na PHP triky
Implementace na těchto stránkách vypadá tak, že po přidání diskusního příspěvku obsahujícího URL se stáhne ikona a uloží se do cache. Pokud ikona neexistuje, uloží se do cache prázdný soubor. Pokud už soubor v cache uložen je, tak se ikona znovu nestahuje. Při vypisování diskusních příspěvků se odkazy na autorovy stránky (na rozdíl od odkazů přímo v příspěvku) vypisují s atributem rel="nofollow"
, aby nemátly vyhledávače.
Na závěr přináším seznam deseti nejaktivnějších diskutérů na těchto stránkách:
Autor | Počet příspěvků |
---|---|
Jakub Vrána | 609 |
dgx | 167 |
spaze | 108 |
llook | 64 |
Jakub Podhorský | 51 |
Lukas | 50 |
Leo | 42 |
Michal | 31 |
@ss@ssIn | 31 |
Llaik | 28 |
Celkem | 3258 |
U nejaktivnějších snadno identifikovatelných autorů jsem URL zpětně doplnil. Pokud jste v minulosti na těchto stránkách aktivně diskutovali a máte zájem o totéž, zanechte komentář u tohoto článku.
Diskuse
MiSHAK:
HH pěkné jak je to s licencí?

Jakub Vrána
:
Stejně jako s ostatními skripty na těchto stránkách, je to uvedeno v patičce: "Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení", http://creativecommons.org/licenses/by/2.5/.


arieslink:
Have been looking for it for so long!
Nice!

Michal:
Skoro to vypadá, že si v kometářích povídáš sám pro sebe :)
Fruiko:
Já jen zkouším, jak to zobrazí ikonku... :-)
A jinak proč ne GRAVATARy? Ikonku by měli i ti, co nemají web.

Jakub Vrána
:
Takhle mají ikonu i ti, co nemají e-mail :-). Teď vážně - Gravatary jsou vázané na e-mail a ten já nerad někde uvádím.


JOKER:
Pěknééé, moc pěknééé.

Arcao:
Pekne...

Arcao:
Hm, ale moji ikonku to moc nesezralo

Jakub Vrána
:
Díky za upozornění, u 32-bitových ikon se nepočítalo s alfa-kanálem.


TPY:
zajimava myslenka :-)

Agarwaen:
Dobry napad! Uz jsem videl kdeco, ale tohle mne skutecne zaujalo! :)

Mazlo:
Test ikonky :-) Jinak fakt super nápad.
Jakub Vrána
:
http://www.mazlo.org/favicon.ico není ikona, ale BMP obrázek.


Vojtěch Semecký:
Zkoušel jsem kdysi uvedené ICO knihovny (class.ico.php a ImageCreateFromICO) a obě na určitých ikonách kolabovali. Tady koukám, že je ten samý problém (viz ikona uživatele Arcao). Nevíte v čem by mohl být problém?

Vojtěch Semecký:
U uživatele Mazlo to taky vrací jen černý čtvereček. U mého Kratce.cz to sice ikonu načte, ale z nějakých důvodů je rozsypaná :-( Schválně, co to udělá se SrovnaniCen.cz.

Jakub Vrána
:
Rozsypanost už jsem opravil, špatně se zpracovávaly dvoubarevné ikony rozměru 16x16. Problém byl v tom, že řádky v bitmapách musí být zarovnané na 4 bajty. V obrazových datech a transparentnosti se s tím počítalo, ve výpočtu začátku transparentních dat ne.


Vojtěch Semecký:
Děkuji za rychlou reakci. Tím je to asi dotaženo k dokonalosti. Gratuluji!

Vojtěch Semecký:
Tak SrovnáníCen.cz má ikonu v pohodě. To nechápu, vyráběl jsem je stejným způsobem.

Petr Vaclavek:
Super napad! Ja uz o tom premyslel drive, prece jen krom gravataru, ktery je vazany na email, existuji avatary a jine xxxtary, ktere jsou propojene s webem, ale bohuzel nejsou moc rozsirene. Pouzit favikonu me nikdy nenapadlo. Ted uz mi chybi ke spokojenosti jen plugin pro Textpattern, ktery by to umel pekne zpracovat :)

Jakub Podhorský:
pěknej nápad...tohle se mi hodně líbíani jsem netušil že jsem takovej "diskutér" :)
Dan:
Test ikonky

Honza:
Hm a co moje výtečné png ikonky?

Kevujin:
Tak já to teda taky zkusím .)

p360t:
No, keď sme pri tom skúšaní... Inak, aký je asi najvhodnejší formát faviconky? Ja používam staré 16*16 ICO, ale ako tak pozerám, niekto sa s tým viac hrá. Je lepšia jednoduchosť a krátky kód alebo grafická lahôdka s veľkým dátovým objemom?

TZ:
taky testík

Jasper:
test

Pavel:
Gratuluji, zajimavé zpestření
Pavel:
tak jsem ji dal do rootu, snad to napodruhe projde
elfineer:
Taky jen zkousim ikonku :). Ale bezva napad.

D1ce:
Zírám, co kousek kódu to skvost, sice to nemohu objektivně posoudit, jelikož dokonale nerozumím kódu, ale stejně mě to ohromilo. Kdybych se o něco podobného pokoušel sám, asi by vznikla třída na více než 1000 řádky a fungovala by jen pokud by vítr vál z té správné strany(jestli vůbec).

Skaven:
Tak ono to opravdu funguje! Opravdu pěkné...

Salko:
Tak isto iba skúška
Jakub Vrána
:
Jak jsem psal - ikona se bere z rootu.


mark:
možná by to přeci jen chtělo trochu zobecnit, a alespoň na tu metainformaci v HTML se jednou za čas podívat ...
Jakub Vrána
:
Do rootu na favicon.ico se kouká kde kdo (jestli tam tento soubor nemáte, tak se schválně někdy podívejte do errorlogu), takže je stejně lepší mít ikonu právě tam. Proto jsem se parsováním HTML nezabýval.


balud:
Test a gratulace k zajímavému nápadu... ostatně Gravatar se nějak těřkopádně probouzí ze zimního spánku.... tak třeba tudy vede cesta. Zas ne každej má vlastní web... no

Pilda:
Super nápad. Ani bych nečekal, že tak zdánlivě jednoduchá věc potřebuje tak dlouhý script :)

Pilda:
<citace>(na rozdíl od odkazů přímo v příspěvku) vypisují s atributem rel="nofollow", aby nemátly vyhledávače.</citace>Jak to mysliš, "aby nemátly"?
Jakub Vrána
:
Vyhledávače si o odkazech myslí (alespoň teoreticky), že vedou na relevantní stránky. Relevance u odkazu na autora příspěvku je jen velmi volná, proto o tom vyhledávač takto informuji.


3rojka:
Já chi taky to zkusit.

3rojka:
No tak nějak mi to nefunguje.

3rojka:
No tak že by to bylo tím BMP formátem

3rojka:
No tak pořád nic?

3rojka:
No tak teda nevím, sorry za vygenerování tolika bez účelných komenářů.

Jakub Vrána
:
Jakmile se ikona jednou načte (byť neplatná nebo neexistující), tak se uloží do cache a příště se už nenačítá. V cache jsem ji ručně obnovil.


test:
test ikony

Jirka:
Není mi jasné, co za proměnnou musím vypsat, abych dostal ikonu? Zkoušel jsem:<pre><img src="<?php echo $file; ?>" width="<?php echo $width; ?>" height="<?php echo $height; ?>" /></pre>
ale tohle je na moje php schopnosti přespříliš složité :-(
Jakub Vrána
:
Načtenou ikonu lze buď uložit do souboru - třeba funkcí imagepng, nebo ji lze téže funkcí poslat do prohlížeče a tento skript zavolat:
<img src="ikona.php?url=...">
ikona.php:
<?php
header("Content-Type: image/png");
imagepng(imagecreatefromico($_GET["url"], 16, 16));
?>


Marty:
test ikonky :-)

Jakub Vrána
:
Ikona http://www.martinvseticka.eu/favicon.ico není ve formátu ICO, ale ve formátu BMP.


Marty:
Opraveno, snad je to uz spravne.

Mark V:
Sorry, I don't really understand/write your language... Understanding PHP is enough for the article thou ;). Great code, cheers! I own you a beer for easing my multi-hour pain in the ass trying to figure out the very same thing.

Mark V:
In case anyone's interested...
http://printf.ru/wiki/Favicon::fetch
Here's the PHP code needed to fetch the favicon from a given site.
Parses (well, kindof) <meta> section, and uses /favicon.ico as a fallback. Usage is as easy as
header('Content-Type: image/png');
$image = Favicon::fetch('php.vrana.cz');
if ($image) imagepng($image);
Same attribution license as the original code.

xsuchy09:
Prosím o opravu ikony (smazání neplatné v cache) :) díky

Jakub Vrána
:
Obnoveno.


mutty:
._
|\
\
je tam?
Jakub Vrána
:
Není, protože soubor favicon.ico na serveru neexistuje.


Diskuse je zrušena z důvodu spamu.

