Author Topic: PNG chunk information  (Read 6419 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
PNG chunk information
« on: September 09, 2018, 02:05:02 pm »
Since TerryRichie was asking how to get the background color from a PNG file, I thought I'd go ahead and take a few moments to write up a program to do that for us:

Code: QB64: [Select]
  1. DIM DataLength AS _UNSIGNED LONG
  2. DIM code AS STRING * 4
  3. f$ = "e1.png"
  4.  
  5.     GET #1, , DataLength
  6.     DataLength = ConvertUL(DataLength)
  7.     PRINT DataLength,
  8.     GET #1, , code
  9.     PRINT code
  10.     IF code = "bKGD" THEN 'we found the background color
  11.         junk$ = " "
  12.         GET #1, , red
  13.         GET #1, , junk$ 'PNG stores the data as 2 bytes, so we need to read an extra character to move file pointer
  14.         GET #1, , blue
  15.         GET #1, , junk$ 'same as before
  16.         GET #1, , green
  17.         GET #1, , junk$ 'same as before
  18.         PRINT "BACKGROUND: "; red, blue, green
  19.         junk$ = "    " 'the CRC check, which we don't really care about, but we need to advance the file pointer to the next chunk
  20.         GET #1, , junk$
  21.     ELSE
  22.         junk$ = SPACE$(DataLength + 4)
  23.         GET #1, , junk$
  24.     END IF
  25.     SLEEP
  26. LOOP UNTIL code$ = "IEND"
  27.  
  28. FUNCTION ConvertUL~& (x AS _UNSIGNED LONG)
  29.     ConvertUL = x \ 2 ^ 24 OR x * 2 ^ 24 OR (x AND &HFF0000) \ 2 ^ 8 OR (x AND &HFF00~&) * 2 ^ 8
  30.  

Test image is below, which has a yellow background set as found here: http://www.schaik.com/pngsuite/pngsuite_bck_png.html

* e1.png (Filesize: 3.37 KB, Dimensions: 32x32, Views: 670)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: PNG chunk information
« Reply #1 on: September 09, 2018, 02:25:19 pm »
My goodness! That's awesome. Thank you for taking the time to create this.

I've been reading about your personal struggles too. I'm thinking about you and hoping for the best Steve.
In order to understand recursion, one must first understand recursion.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: PNG chunk information
« Reply #2 on: September 09, 2018, 02:30:22 pm »
PNGs are actually quite easy to sort out; everything is stored in set chunks of data.

4 bytes for data length
4 bytes for data type
data of length given above
4 bytes for CRC check

Last chunk is always "IEND", so just DO.... LOOP and read each chunk as you're ready to deal with them.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: PNG chunk information
« Reply #3 on: September 09, 2018, 05:30:01 pm »
I'm not sure what is going on here. The image you supplied worked fine with the code, however whenever I try to bring it into my paint program the program freaks with it. It shows no image and complains there is "insufficient memory" when I try to do anything with it. Windows picture viewer shows it no problem though.

I then tried to use one of the PNGs I created with a clear background and the code fails to find it. I then downloaded a random picture from the Internet (dog.png below) and the code fails to find the background as well.

Is there something missing?
dog.png
* dog.png (Filesize: 55.38 KB, Dimensions: 259x500, Views: 449)
In order to understand recursion, one must first understand recursion.

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: PNG chunk information
« Reply #4 on: September 09, 2018, 05:40:52 pm »
TerryRitchie: Your file contains none bKGD flag, Steve´s file it contains.

maybe try read tRNS chunk. Its also about transparency, if is one color transparent.
« Last Edit: September 09, 2018, 05:58:22 pm by Petr »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: PNG chunk information
« Reply #5 on: September 09, 2018, 06:00:43 pm »
TerryRitchie: Your file contains none bKGD flag, Steve´s file it contains.

Petr's correct; your file has no background color saved.  "bKGD" is an optional chunk and you're not guaranteed that any file will have it.  If it's there, it's easy to detect and extract.  Hopefully Sprite Sheets you're using have it, otherwise you're going to have to manually find it.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Re: PNG chunk information
« Reply #6 on: September 09, 2018, 06:11:30 pm »
So there are different versions/types of PNG files out there I take it, sort of like the Gif87/Gif89a.

I don't want a user of the library having to deal with PNG versions to make sure he/she has the correct type. I'll use the crappy detection routine I have for now. I'll need to investigate that document about PNG files for more info on the subject.

Thanks for your help though Steve. I definitely want to eventually use a solution such as yours.
In order to understand recursion, one must first understand recursion.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: PNG chunk information
« Reply #7 on: September 09, 2018, 06:33:02 pm »
PNG's selling point is flexibility.  All they *have* to contain is the Header, Pixel Data, End of Data tag; anything else is considered optional.  Some hold text, like creation date/time, image creator, version, ect.  Others contain dithering, palettes, compression, background data, ect... 

What'd I do for a library like yours is to look for the background code first, and if available, go with it.  If it's not found, then I'd manually look for the background color (or just ask the user to supply it).    If it's there, you *know* it's right; if not, you have to make a "best guess" at the color.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: PNG chunk information
« Reply #8 on: September 10, 2018, 09:51:24 am »
Hello. I tried to write a program for a more detailed PNG statement. It seems that 15 color pallets from 256 in the picture with the dog has some transparency, pallet number 1 is fully transparent, it should be the color _RGBA32 (71, 112, 76, 0) - palette 1 and RGBA32 (74, 65 , 50, 3) - palette 8

Code: QB64: [Select]
  1. 'PNG reader  / read some (not all) information from PNG file. Source informations for this program are from http://www.fileformat.info/format/png/corion.htm
  2.  
  3. file$ = "dog.png"
  4.  
  5.  
  6. TYPE PNG_header
  7.     id AS _UNSIGNED _BYTE ' 89h
  8.     PNG AS STRING * 3 '     PNG
  9.     id1 AS _UNSIGNED _BYTE '13
  10.     id2 AS _UNSIGNED _BYTE '10
  11.     id3 AS _UNSIGNED _BYTE '26
  12.     id4 AS _UNSIGNED _BYTE '10
  13.  
  14. TYPE Block
  15.     Data_Size AS _UNSIGNED LONG
  16.     Chunk AS STRING * 4
  17.  
  18. TYPE IHDR
  19.     height AS _UNSIGNED LONG
  20.     bit_depth AS _UNSIGNED _BYTE
  21.     color_type AS _UNSIGNED _BYTE
  22.     compression_type AS _UNSIGNED _BYTE
  23.     filter_type AS _UNSIGNED _BYTE
  24.     interlacce_type AS _UNSIGNED _BYTE
  25.  
  26.  
  27.  
  28.  
  29.  
  30. DIM Header AS PNG_header, Block AS Block, IHDR AS IHDR
  31.  
  32. OPEN file$ FOR BINARY AS #1
  33. GET #1, , Header
  34.  
  35. PRINT "PNG id (muss be 89H): "; HEX$(Header.id) + LTRIM$("H")
  36. PRINT "PNG id (muss be PNG): "; Header.PNG$
  37. PRINT "Header indentity (muss be 13, 10, 26, 10): "; Header.id1; Header.id2; Header.id3; Header.id4
  38.  
  39.  
  40.  
  41.  
  42.  
  43.     SEEK #1, SEEK(1) - 7
  44.     GET #1, , Block
  45.  
  46.     SELECT CASE Block.Chunk
  47.         CASE "IHDR"
  48.             GET #1, , IHDR
  49.             start = 1
  50.             size = ConvertUL~&(Block.Data_Size)
  51.             PRINT STRING$(45, "-")
  52.             PRINT "Chunk: "; Block.Chunk
  53.             PRINT "Data area for this chunk: "; size ' OK
  54.             PRINT "Image width: "; ConvertUL~&(IHDR.width)
  55.             PRINT "Image height: "; ConvertUL~&(IHDR.height)
  56.             PRINT "Image bit depth: "; IHDR.bit_depth
  57.             PRINT "Image color type: "; IHDR.color_type
  58.             PRINT "Image compression type: "; IHDR.compression_type
  59.             PRINT "Image filter type: "; IHDR.filter_type
  60.             PRINT "Image interlacce type: "; IHDR.interlacce_type
  61.             PRINT STRING$(45, "-")
  62.             kontinue
  63.  
  64.         CASE "PLTE"
  65.             REDIM RED AS _UNSIGNED _BYTE, GREEN AS _UNSIGNED _BYTE, BLUE AS _UNSIGNED _BYTE
  66.  
  67.             size = ConvertUL~&(Block.Data_Size)
  68.             PRINT STRING$(45, "-")
  69.             PRINT "Chunk: "; Block.Chunk
  70.             PRINT "Data area for this chunk: "; size ' OK
  71.             PRINT "Color palette: "
  72.  
  73.             IF size MOD 3 <> 0 THEN BEEP ' error, muss be dividible by 3
  74.             FOR R = 1 TO size / 3
  75.                 GET #1, , RED: GET #1, , GREEN: GET #1, , BLUE
  76.                 PRINT "Palette color: "; R; "RED: "; RED; "GREEN: "; GREEN; "BLUE:"; BLUE
  77.                 IF R MOD 15 = 0 THEN kontinue
  78.             NEXT
  79.             PRINT STRING$(45, "-")
  80.             kontinue
  81.         CASE "IDAT"
  82.             PRINT "IDAT: Image data block found..."
  83.             PRINT STRING$(45, "-")
  84.             PRINT "Chunk: "; Block.Chunk
  85.             size = ConvertUL~&(Block.Data_Size)
  86.             PRINT "Data area for this chunk: "; size ' OK
  87.             PRINT STRING$(45, "-")
  88.  
  89.             kontinue
  90.         CASE "IEND"
  91.             PRINT "IEND: End of image found..."
  92.             kontinue
  93.  
  94.  
  95.         CASE "bKGD" 'this next chunks are optional, muss not be contained in file
  96.             PRINT STRING$(45, "-")
  97.             PRINT "Chunk: "; Block.Chunk
  98.             PRINT "Data area for this chunk: "; size ' OK
  99.             SELECT CASE IHDR.color_type
  100.                 CASE 3
  101.                     REDIM palete AS _UNSIGNED _BYTE 'return PALETTE color value used as background, this palette is returned in PLTE block
  102.                     GET #1, , palete
  103.                     PRINT "Image type 3, palette background: "; palete
  104.                 CASE 4, 0
  105.                     REDIM palete2 AS _UNSIGNED INTEGER
  106.                     GET #1, , palete2
  107.                     PRINT "Image type 4 or 0, palette background: "; palete2
  108.                 CASE 2, 6
  109.                     REDIM RedPal AS STRING * 2, GreenPal AS STRING * 2, BluePal AS STRING * 2
  110.                     GET #1, , RedPal
  111.                     GET #1, , GreenPal
  112.                     GET #1, , BluePal
  113.                     PRINT "Image type 2 or 6, backgroud palette: Red 0 to"; re(RedPal); " Green 0 to "; re(GreenPal); " Blue 0 to "; re(BluePal)
  114.             END SELECT
  115.             kontinue
  116.  
  117.         CASE "cHRM" 'chromizace... co to je....
  118.             PRINT STRING$(45, "-")
  119.             PRINT "Chunk: "; Block.Chunk
  120.             PRINT "Data area for this chunk: "; size ' OK
  121.             PRINT "chromatization chunk.... not writed"
  122.  
  123.         CASE "tRNS"
  124.             PRINT STRING$(45, "-")
  125.             REDIM ALFA AS _UNSIGNED _BYTE
  126.  
  127.             size = ConvertUL~&(Block.Data_Size)
  128.  
  129.             PRINT "Chunk: "; Block.Chunk
  130.             PRINT "Data area for this chunk: "; size ' OK
  131.             SELECT CASE IHDR.color_type
  132.                 CASE 3 '                                                                                your DOG.PNG is this case
  133.  
  134.                     FOR R = 1 TO size
  135.                         GET #1, , ALFA
  136.                         PRINT "ALFA setting for colors from palette: Palette color: "; R; "ALFA: "; ALFA
  137.                         IF R MOD 15 = 0 THEN kontinue
  138.                     NEXT
  139.                     PRINT STRING$(45, "-")
  140.                     '255 is full visible, 0 is full transparent, palette values not listed here are all with alpha 255.
  141.  
  142.                 CASE 0
  143.                     REDIM ALFA0 AS STRING * 2
  144.                     GET #1, , ALFA0
  145.                     PRINT "Image type 0, alfa: "; re(ALFA0)
  146.                 CASE 2
  147.                     REDIM alfaR AS STRING * 2, alfaG AS STRING * 2, alfaB AS STRING * 2
  148.                     GET #1, , alfaR
  149.                     GET #1, , alfaG
  150.                     GET #1, , alfaB
  151.                     PRINT "image type 2 alfa for RED is "; re(alfaR); "for GREEN is "; re(alfaG); "for BLUE is "; re(alfaB)
  152.             END SELECT
  153.  
  154.         CASE "tIME"
  155.             TYPE intdat
  156.                 year AS STRING * 2
  157.                 month AS _UNSIGNED _BYTE
  158.                 day AS _UNSIGNED _BYTE
  159.                 hour AS _UNSIGNED _BYTE
  160.                 min AS _UNSIGNED _BYTE
  161.                 sec AS _UNSIGNED _BYTE
  162.             END TYPE
  163.             REDIM intdat AS intdat
  164.             GET #1, , intdat
  165.  
  166.             PRINT STRING$(45, "-")
  167.             size = ConvertUL~&(Block.Data_Size)
  168.  
  169.             PRINT "Chunk: "; Block.Chunk
  170.             PRINT "Data area for this chunk: "; size ' OK
  171.             PRINT "Image last edit: "; re(intdat.year); "-"; intdat.month; "-"; intdat.month; "-"; intdat.day; "-"; intdat.hour; "-"; intdat.min; "-"; intdat.min; "-"; intdat.sec
  172.             PRINT STRING$(45, "-")
  173.             kontinue
  174.     END SELECT
  175.  
  176. FUNCTION ConvertUL~& (x AS _UNSIGNED LONG)
  177.     ConvertUL = x \ 2 ^ 24 OR x * 2 ^ 24 OR (x AND &HFF0000) \ 2 ^ 8 OR (x AND &HFF00~&) * 2 ^ 8
  178.  
  179. SUB kontinue
  180.     PRINT "Press any key...": SLEEP
  181.  
  182.  
  183. FUNCTION re (num AS STRING)
  184.     n$ = ""
  185.     FOR f = LEN(num) TO 1 STEP -1
  186.         n$ = n$ + MID$(num, f, 1)
  187.     NEXT f
  188.     IF LEN(num) = 4 THEN re = CVL(n$)
  189.     IF LEN(num) = 2 THEN re = CVI(n$)
  190.  
  191.  
output.jpg
* output.jpg (Filesize: 428.47 KB, Dimensions: 1680x1152, Views: 419)
« Last Edit: September 10, 2018, 10:20:14 am by Petr »