12. vaje: BRANJE SLIK FORMATA PNG (25. 05. - 01. 06.)

Zapis slik v formatu PNG

Napišite program za branje datotek formata Portable Network Graphics ali PNG (več o tem formatu najdete na Wikipediji ali v specifikacijah konzorcija W3C). Program naj prebere podatke o velikosti slike, ki je shranjena v tem formatu.

Kratek opis formata:

Vse datoteke v formatu PNG imajo točno določeno strukturo. Na začetku datoteke je podpis (signature), ki zajema prvih 8 bajtov z vrednostmi 89 50 4E 47 0D 0A 1A 0A šestnajstiško. Podpis določa, da je to datoteka PNG (mimogrede, bajti 50 4E 47 so ASCII kode kratice PNG) ter da preostanek datoteke vsebuje eno PNG sliko, sestavljeno iz zaporedja kosov podatkov (chunks). Slednji se vedno začnejo s kosom IHDR ter zaključijo s kosom IEND.

Vsak kos podatkov (chunk) ima točno določeno obliko. Sestavljajo ga štiri polja, kot prikazuje slika:

dolžina tip (chunk type) podatki (chunk data) CRC
4 bajti 4 bajti dolžina bajtov 4 bajti

 

 

 

Prvo polje (prvi 4 bajti) določajo dolžino polja s podatki. Če je tu zapisana vrednost 0 (torej bajti 00 00 00 00), to pomeni, da polja s podatki nimamo.

Dolžini vedno sledi tip kosa, ki ga tudi določajo 4 bajti. Poznamo 18 različnih tipov, od katerih so 4 obvezni (imenujejo se kritični (critical) in datoteka jih mora vsebovati), ostalih 14 pa je opcijskih (imenujejo se pomožni (ancillary) in določajo dodaten opis slike, metapodatke). Obvezni kosi so naslednjih tipov: IHDR (glava slike, vedno je to prvi kos, vsebuje med drugim podatke o velikosti slike), PLTE (vsebuje paleto, seznam barv), IDAT (vsebuje sliko, ki je lahko razdeljena tudi na več IDAT kosov) ter IEND (označuje konec slike).

Podatki v kosu IHDR vsebujejo tudi širino (width) in višino (height) slike v pikslih. Širino določajo prvi 4 bajti podatkov, višino pa naslednji 4 bajti.

Primer zapisa začetka datoteke test.png (v hex):

89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52
00 00 03 20 00 00 02 58 08 06 00 00 00 9a 76 82
70 00 00 00 04 67 41 4d 41 00 00 b1 8e 7c fb 51
93 00 00 00 20 63 48 52 4d 00 00 87 0f 00 00 8c
0f 00 00 fd 52 00 00 81 40 00 00 7d 79 00 00 e9
8b 00 00 3c e5 00 00 19 cc 73 3c 85 77 00 00 0a
2f 69 43 43 50 49 43 43 20 50 72 6f 66 69 6c 65
...
d4 fc 3c 79 f2 e4 f0 ff 01 41 cc dc 96 c7 76 c4
4c 00 00 00 00 49 45 4e 44 ae 42 60 82

Z rdečo je označenih prvih 8 bajtov, to je podpis, ki ima vedno vnaprej določeno vsebino. Sledi kos IHDR (naslednjih 25 bajtov, označeni z zeleno, rumeno, modro in vijolično). Zelen del je velikost, kar pomeni, da bomo imeli 0d (to je 13) bajtov podatkov (modri na sliki). Sledi rumen tip kosa IHDR (IHDR chunk določajo bajti 49 48 44 52), modri podatki (med njimi širina slike 00 00 03 20 oz. 800 desetiško ter višina slike 00 00 02 58 oz. 600 desetiško, na sliki so podčrtani) ter vijolična koda CRC (Cyclic Redundancy Check, za preverjanje morebitne pokvarjenosti podatkov).
Temu sledijo naslednji kosi (na sliki je prvi obarvan svetlo sivo, naslednji temno sivo), ki jih je poljubno veliko. Na koncu datoteke imamo še oranžno obarvan kos IEND, pri katerem je dolžina podatkov 0 (glej prve 4 bajte kosa), tip kosa pa določajo bajti 49 45 4e 44.

Še enkrat povzetek podpisa in vseh štirih tipov obveznih kosov:

SIGNATURE 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
IHDR 0x49 0x48 0x44 0x52
PLTE 0x50 0x4C 0x54 0x45
IDAT 0x49 0x44 0x41 0x54
IEND 0x49 0x45 0x4E 0x44


Naloga:

a) Napišite program, ki bo znal prebrati podatke o velikosti slike iz datoteke formata PNG. Pri tem morate pravilno prebrati podpis slike (da je to res format PNG) in kos IHDR. V kosu IHDR se skrivajo tudi podatki o velikosti slike: prvi 4 bajti podatkov določajo širino slike, naslednji 4 bajti pa višino slike. Program naj izpiše velikost slike (širino in višino) v pikslih (kot vrednost int).
Primer izpisa za sliko test.png je naslednji:

Datoteka test.png je slika tipa PNG, velikosti 800 x 600.

b) Program dopolnite tako, da mu kot argumente lahko podamo več imen datotek in/ali direktorijev, nato pa program izpiše podatke o vseh datotekah s končnico .png, ki se nahajajo med argumenti (ne pozabite pogledati tudi v poddirektorije). Izpis naj bo urejen po naraščajoči velikosti slik.
Primer izpisa za argumenta test.png images (argumenti.zip):

Datoteka test-false.png je slika tipa ???, velikosti 0 x 0.
Datoteka img.png je slika tipa PNG, velikosti 100 x 75.
Datoteka IMG_0148.png je slika tipa PNG, velikosti 400 x 300.
Datoteka test.png je slika tipa PNG, velikosti 800 x 600.
Datoteka test1.png je slika tipa PNG, velikosti 800 x 600.
Datoteka IMG_0121.png je slika tipa PNG, velikosti 1400 x 1050.
Datoteka IMG_0216.png je slika tipa PNG, velikosti 1824 x 1368.

Nasvet za urejen izpis: Za hranjenje podatkov o eni sliki definirajte nov razred, za vse slike pa uporabite seznam. Pred izpisom seznam uredite s pomočjo metode Collections.sort(). Urejenost naj določa velikost slike (širina * višina).

c) V programu poskrbite tudi za ustrezno obravnavo izjem. Definirajte tudi novo izjemo (nov razred, izpeljan iz razreda Exception), ki se sproži, kadar odprta datoteka ni formata PNG (ob napačnem podpisu (signature) datoteke ali ob manjkajočem kosu IHDR).