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:

AutorPočet příspěvků
Jakub Vrána609
dgx167
spaze108
llook64
Jakub Podhorský51
Lukas50
Leo42
Michal31
@ss@ssIn31
Llaik28
Celkem3258

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.

Jakub Vrána, Seznámení s oblastí, 21.2.2007, on-line

Diskuse

ikona MiSHAK:

HH pěkné jak je to s licencí?
21.2.2007 02:02:31

ikona 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/.
21.2.2007 08:57:54

ikona arieslink:

Have been looking for it for so long!
Nice!
24.8.2009 22:30:57

Michal:

Skoro to vypadá, že si v kometářích povídáš sám pro sebe :)
21.2.2007 02:47:49

ikona Fruiko:

Já jen zkouším, jak to zobrazí ikonku... :-)

A jinak proč ne GRAVATARy? Ikonku by měli i ti, co nemají web.
21.2.2007 08:32:37

ikona 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.
21.2.2007 08:59:51

ikona JOKER:

Pěknééé, moc pěknééé.
21.2.2007 09:03:34

ikona Arcao:

Pekne...
21.2.2007 09:11:39

ikona Arcao:

Hm, ale moji ikonku to moc nesezralo
21.2.2007 09:12:09

ikona Jakub Vrána:

Díky za upozornění, u 32-bitových ikon se nepočítalo s alfa-kanálem.
21.2.2007 11:10:55

ikona TPY:

zajimava myslenka :-)
21.2.2007 09:44:56

ikona Agarwaen:

Dobry napad! Uz jsem videl kdeco, ale tohle mne skutecne zaujalo! :)
21.2.2007 10:54:01

Mazlo:

Test ikonky :-) Jinak fakt super nápad.
21.2.2007 11:05:28

ikona Jakub Vrána:

http://www.mazlo.org/favicon.ico není ikona, ale BMP obrázek.
21.2.2007 11:09:48

ikona 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?
21.2.2007 11:06:20

ikona 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.
21.2.2007 11:07:37

ikona 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.
21.2.2007 12:03:08

ikona Vojtěch Semecký:

Děkuji za rychlou reakci. Tím je to asi dotaženo k dokonalosti. Gratuluji!
21.2.2007 12:24:58

ikona Vojtěch Semecký:

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

ikona 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 :)
21.2.2007 11:16:42

Jakub Podhorský:

pěknej nápad...tohle se mi hodně líbí

ani jsem netušil že jsem takovej "diskutér" :)
21.2.2007 14:05:57

ikona Dan:

Test ikonky
21.2.2007 16:11:20

ikona Honza:

Hm a co moje výtečné png ikonky?
21.2.2007 18:34:43

ikona Kevujin:

Tak já to teda taky zkusím .)
21.2.2007 19:02:46

ikona 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?
21.2.2007 19:57:36

ikona TZ:

taky testík
21.2.2007 20:34:04

ikona Jasper:

test
28.2.2007 00:32:27

Pavel:

Gratuluji, zajimavé zpestření
21.2.2007 21:37:00

Pavel:

tak jsem ji dal do rootu, snad to napodruhe projde
22.2.2007 23:35:03

ikona elfineer:

Taky jen zkousim ikonku :). Ale bezva napad.
21.2.2007 21:56:11

ikona 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).
22.2.2007 00:12:59

ikona Skaven:

Tak ono to opravdu funguje! Opravdu pěkné...
22.2.2007 00:46:05

Salko:

Tak isto iba skúška
22.2.2007 20:20:00

ikona Jakub Vrána:

Jak jsem psal - ikona se bere z rootu.
22.2.2007 22:25:29

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 ...
25.2.2007 10:31:15

ikona 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.
25.2.2007 13:59:38

ikona 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
22.2.2007 20:26:26

ikona Pilda:

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

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"?
3.3.2007 01:22:45

ikona 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.
3.3.2007 01:34:34

ikona 3rojka:

Já chi taky to zkusit.
4.3.2007 12:01:44

ikona 3rojka:

No tak nějak mi to nefunguje.
4.3.2007 12:03:28

ikona 3rojka:

No tak že by to bylo tím BMP formátem
4.3.2007 12:06:32

ikona 3rojka:

No tak pořád nic?
4.3.2007 12:07:19

ikona 3rojka:

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

ikona 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.
5.3.2007 11:21:59

ikona test:

test ikony
7.5.2007 22:25:50

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é :-(
28.5.2007 02:17:00

ikona 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));
?>

28.5.2007 13:07:44

ikona Marty:

test ikonky :-)
27.7.2007 16:37:01

ikona Jakub Vrána:

Ikona http://www.martinvseticka.eu/favicon.ico není ve formátu ICO, ale ve formátu BMP.
27.7.2007 18:30:05

ikona Marty:

Opraveno, snad je to uz spravne.
27.7.2007 19:06:30

ikona 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.
26.11.2007 01:03:44

ikona 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.
28.11.2007 00:51:46

ikona xsuchy09:

Prosím o opravu ikony (smazání neplatné v cache) :) díky
10.3.2009 05:02:47

ikona Jakub Vrána:

Obnoveno.
10.3.2009 14:51:00

mutty:


._
|\
  \
   je tam?
11.4.2010 19:01:33

ikona Jakub Vrána:

Není, protože soubor favicon.ico na serveru neexistuje.
12.4.2010 15:05:05
avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.