Author Topic: BMP picture viewer  (Read 9078 times)

0 Members and 1 Guest are viewing this topic.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: BMP picture viewer
« Reply #30 on: February 05, 2021, 07:14:53 pm »
OK maybe all my pics were 1024 pixels wide and  . . . . . .another was 1072

1024 * 3 /4 = 768  = no padding
1072 * 3 /4  = 804 = no padding

imagine that
  update coming
« Last Edit: February 05, 2021, 07:18:40 pm by NOVARSEG »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: BMP picture viewer
« Reply #31 on: February 05, 2021, 07:18:09 pm »
Ya but I did not have to adjust for "padding" in my code . If there are padding bytes at the end of a scan line then the padding byte don't contain pixel data.  The file pointer (GET, point, . . .) is supposed to point to pixel data only

In a, say, 1024 * 768  24 bit BMP there are exactly 1024 * 3 bytes per scan line. That is what my code is saying.  If there was actually padding bytes at the end of a scan line then surely I would have found them.

As I mentioned, the padding is only there in images whose width is not evenly divisible by four.

Most full screen images have no padding: 320, 480, 640, 720, 1024 are all evenly divisible by 4.  Where you normally see the padding is when folks take a screenshot of a potion of the screen.  (Say from 0,0 to 638, 372 — that 638 isn’t evenly divisible by 4 and thus has padding at the end of the line.)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: BMP picture viewer
« Reply #32 on: February 05, 2021, 07:19:26 pm »
Steve 

code update coming

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: BMP picture viewer
« Reply #33 on: February 05, 2021, 07:28:04 pm »
You guys are using this thread like a chatroom. Not illegal, but you realize Discord would be much more efficient?

Steve, you're my only hope. Drag them in.
You're not done when it works, you're done when it's right.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: BMP picture viewer
« Reply #34 on: February 05, 2021, 07:52:47 pm »
You guys are using this thread like a chatroom. Not illegal, but you realize Discord would be much more efficient?

Steve, you're my only hope. Drag them in.

Somebody forgot definition of a forum.

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: BMP picture viewer
« Reply #35 on: February 05, 2021, 07:54:29 pm »
Yeah, I'm raising the question of *who*.
You're not done when it works, you're done when it's right.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: BMP picture viewer
« Reply #36 on: February 05, 2021, 08:21:12 pm »
Discord is no good for sharing larger snippets of code.  Forums are much better places for stuff like that.

Here's the complete version of what you guys want:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1024, 800, 32)
  2. handle = loadBMP24("non4.bmp")
  3. _PUTIMAGE , handle
  4.  
  5. FUNCTION loadBMP24& (Filename$)
  6.     DIM OF AS _UNSIGNED LONG
  7.     DIM a AS _MEM
  8.     DIM handle AS LONG
  9.     DIM dh AS LONG
  10.     DIM tt AS STRING * 4
  11.     DIM t AS STRING * 3
  12.  
  13.     IF RIGHT$(UCASE$(Filename$), 4) <> ".BMP" THEN EXIT FUNCTION
  14.     PRINT Filename$
  15.     OPEN Filename$ FOR BINARY AS #1
  16.     GET #1, 11, OF
  17.     GET #1, 15, L
  18.     GET #1, , W
  19.     GET #1, , H
  20.     GET #1, , I
  21.     GET #1, , I 'get I twice ???
  22.     IF I <> 24 THEN EXIT FUNCTION
  23.     handle = _NEWIMAGE(W, H, 32)
  24.     a = _MEMIMAGE(handle)
  25.     IF ((W * 3) MOD 4) <> 0 THEN '’need padding pixels
  26.         Padding$ = SPACE$((4 - ((W * 3) MOD 4)))
  27.     END IF
  28.     SEEK #1, OF + 1 'go to the offset where the data starts
  29.     FOR R = H - 1 TO 0 STEP -1 'from the bottom to top
  30.         FOR C = 0 TO W - 1 'left to right
  31.             GET #1, , t 'get the data sequentially
  32.             tt = t + CHR$(255)
  33.             _MEMPUT a, a.OFFSET + (W * R + C) * 4, tt
  34.         NEXT C
  35.         GET #1, , Padding$ 'Get the padding at the end of the line
  36.     NEXT R
  37.     CLOSE
  38.  
  39.  
  40.     'dh = _NEWIMAGE(W, H, 32)
  41.     '_PUTIMAGE (0, 0), handle, dh
  42.  
  43.     loadBMP24& = handle
  44.     _MEMFREE a
  45.  
  46.  

I posted the changes needed above, but didn't actually plug it into the whole thing until now.  Sorry.  Today's my mom's 81st birthday, and I was enjoying the evening with her and the rest of the family for a change.

One thing which I think is also important to change here:

Why copy the image over to another image of the exact same size, then return the handle to that image, without ever freeing the first image?  You guys have a rather  serious memory leak in your code here, and it's much simpler to just pass the handle on directly, without worrying about the middleman dh.  See the changes down at the last few lines in the routine, before you exit.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: BMP picture viewer
« Reply #37 on: February 05, 2021, 08:52:06 pm »
Happy 81 Steve's Mom!

There is a lot in this thread to go over.  So I converted one of my 1024 pixel wide pics to 1023 pixels wide and my code crashed. Sorry about that.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: BMP picture viewer
« Reply #38 on: February 05, 2021, 09:07:39 pm »
Quote
Why copy the image over to another image of the exact same size, then return the handle to that image, without ever freeing the first image?  You guys have a rather  serious memory leak in your code here, and it's much simpler to just pass the handle on directly, without worrying about the middleman dh.  See the changes down at the last few lines in the routine, before you exit.

Yeah that was me, just trying to get the thing to work as a Function returning a handle of a loaded image.

If this were a chat room I'd ask if that was a cobweb hanging off the bottom of the Starship model and I'd say congrats to Steve's mother and mention I took my 89 year old mother for COVID vaccination today and maybe mention nothing sticks in her memory so I have the same set of questions over and over... but if this were a chatroom you wouldn't have the image to check and say, "No, I think that is just a plain old hair."

I had that dang code working, went to clean up with the new Dim As _Unsigned Long all those variables into one line take out commented out code and when I was done, a blank screen.

So thanks Steve for writing up the revised sub because my cleanup refused to work right and it's even better without the extra handle and memory leaking code!

 

« Last Edit: February 05, 2021, 09:15:26 pm by bplus »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: BMP picture viewer
« Reply #39 on: February 06, 2021, 03:38:58 am »
Some new code
 3 more pictures and an EXE to download

Fixed the padding bug.



Code: QB64: [Select]
  1. _TITLE "BMP slide show.   Press ESC to exit"
  2.  
  3.  
  4.     FUNCTION load_dir& (s AS STRING)
  5.     FUNCTION has_next_entry& ()
  6.     SUB close_dir ()
  7.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  8.  
  9. DIM flags AS LONG
  10. DIM length AS LONG
  11. DIM F(100) AS STRING
  12. DIM Fcount AS INTEGER
  13. DIM tt AS STRING * 4
  14. DIM t AS STRING * 3
  15. DIM handle AS LONG
  16.  
  17.  
  18. 'To view BMP pictures in the root drive or a particular folder (example)
  19. 'IF load_dir("C:" + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  20. 'IF load_dir("C:\QB64" + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  21.  
  22. 'To view BMP pictures in the directory (folder) from where the EXE is run (current)
  23. IF load_dir("." + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  24.  
  25. 'To view BMP pictures in the parent directory (folder) from where the EXE is run (current)
  26. 'IF load_dir(".." + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  27.  
  28.  
  29.     length = has_next_entry
  30.     IF length = -1 THEN
  31.         '_FULLSCREEN
  32.         PRINT "NO more files to process"
  33.         EXIT DO
  34.     END IF
  35.  
  36.     IF length > -1 THEN
  37.         NAM = SPACE$(length)
  38.         get_next_entry NAM, flags, file_size
  39.  
  40.  
  41.         IF (flags XOR 2) = 0 AND UCASE$(RIGHT$(NAM, 3)) = "BMP" THEN
  42.             PRINT NAM + "                  "
  43.             Fcount = Fcount + 1
  44.             F(Fcount) = NAM
  45.         END IF
  46.  
  47.     END IF
  48.  
  49. Fmax = Fcount
  50.  
  51. close_dir
  52.  
  53. Fcount = 0
  54.  
  55.     N = 0
  56.  
  57.     Fcount = Fcount + 1
  58.     OPEN F(Fcount) FOR BINARY AS #1
  59.  
  60.     GET #1, 11, OF
  61.     'PRINT "Offset to picture data"; OF
  62.  
  63.     GET #1, 15, L
  64.     ' PRINT "Header size "; L
  65.  
  66.     GET #1, , W
  67.     ' PRINT "image width "; W
  68.  
  69.     GET #1, , H
  70.     'PRINT "image height "; H
  71.  
  72.     GET #1, , I
  73.  
  74.     GET #1, , I 'bits per pixel. Must = 24 for the program to work.
  75.  
  76.     IF I <> 24 THEN
  77.         _FULLSCREEN
  78.         CLS
  79.         PRINT "width "; W; "    Height "; H; "    Bits per pixel "; I
  80.         PRINT Fcount
  81.         PRINT F(Fcount); " is not a 24 bit per pixel BMP"
  82.         DO
  83.             IF INKEY$ <> "" THEN _FULLSCREEN OFF: CLS: EXIT DO
  84.         LOOP
  85.         CLOSE: GOTO LL1
  86.     END IF
  87.  
  88.     DIM a AS _MEM
  89.     handle = _NEWIMAGE(W, H, 32)
  90.     a = _MEMIMAGE(handle)
  91.  
  92.     pad = 4 - (W * 3) MOD 4
  93.  
  94.     IF pad = 4 THEN pad = 0 'pad = multiple of 4
  95.  
  96.     FOR R = H - 1 TO 0 STEP -1
  97.  
  98.         FOR C = 1 TO W * 3 STEP 3
  99.             GET #1, R * (W * 3 + pad) + C + OF, t
  100.             tt = t + CHR$(255)
  101.             _MEMPUT a, a.OFFSET + N, tt
  102.             N = N + 4
  103.         NEXT C
  104.  
  105.     NEXT R
  106.  
  107.     SCREEN _NEWIMAGE(W, H, 32)
  108.     _PUTIMAGE (0, 0), handle
  109.     _MEMFREE a
  110.  
  111.  
  112.     t1 = TIMER
  113.     DO
  114.         _LIMIT 100
  115.         t2 = TIMER
  116.         IF t2 - t1 > 2 THEN EXIT DO
  117.         i$ = INKEY$
  118.         IF i$ = CHR$(27) THEN END
  119.     LOOP
  120.     LL1:
  121.  
  122.     CLOSE
  123.  
  124.     IF Fcount = Fmax THEN Fcount = 0
  125.  
* BMPview.exe (Filesize: 1.65 MB, Downloads: 530)

* engine2.bmp (Filesize: 2.43 MB, Dimensions: 1024x831, Views: 338)

* parts.bmp (Filesize: 3.96 MB, Dimensions: 1440x960, Views: 376)

* piston.bmp (Filesize: 3.58 MB, Dimensions: 1370x913, Views: 348)
« Last Edit: February 07, 2021, 02:41:07 pm by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: BMP picture viewer
« Reply #40 on: February 06, 2021, 08:24:07 pm »
So I will explain why this works

Quote
pad = 4 - (W * 3) MOD 4
 
    IF pad = 4 THEN pad = 0    'pad = multiple of 4
 
    FOR R = H - 1 TO 0 STEP -1
 
        FOR C = 1 TO W * 3 STEP 3
            GET #1, R * (W * 3 + pad) + C + OF, t
            tt = t + CHR$(255)
            _MEMPUT a, a.OFFSET + N, tt
            N = N + 4
        NEXT C
 
    NEXT R

FOR R = H - 1 TO 0 STEP -1

if say H = 768 then R is from 767 to 0  or 768 rows.

row 0  final
row 1
row 2
. . .
row 767   start


 The key thing here is the final row value R = 0 so
when R = 0


0 * (W * 3 + pad) + C + OF  = C + OF = the correct offset into row 0

There are (W * 3 + pad ) bytes per row. so when R = 1 then

 (W * 3 + pad) + C + OF  = the correct offset into row 1

with (W * 3 + pad) being the bytes in the preceding row 0

when R = 2 then

2 * (W * 3 + pad) + C + OF  = the correct offset into row 2
with  2 * (W * 3 + pad) = the total bytes of row 0 + row 1

and so on.

in reality the code down counts but the offset will still work out the same

« Last Edit: February 07, 2021, 02:41:30 pm by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: BMP picture viewer
« Reply #41 on: February 07, 2021, 12:01:47 am »
Working on next update

from   https://stackoverflow.com/questions/9296059/read-pixel-value-in-bmp-file

Quote
Some bitmaps can be written with a negative height, so when you try to allocate your image data buffer, your code will crash with std::bad_alloc. Bitmaps with negative height means that the image data is stored top to bottom instead of the traditional bottom to top. Therefore, a slightly better version of the top level answer is (still not including portability for systems with different endianness and size of bytes):

« Last Edit: February 07, 2021, 12:16:55 am by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: BMP picture viewer
« Reply #42 on: February 07, 2021, 02:55:13 am »
Code update

Works with negative height BMPs too.

EXE attached.

Code: QB64: [Select]
  1. _TITLE "BMP slide show.   Press ESC to exit"
  2.  
  3.  
  4.     FUNCTION load_dir& (s AS STRING)
  5.     FUNCTION has_next_entry& ()
  6.     SUB close_dir ()
  7.     SUB get_next_entry (s AS STRING, flags AS LONG, file_size AS LONG)
  8.  
  9. DIM flags AS LONG
  10. DIM length AS LONG
  11. DIM F(100) AS STRING
  12. DIM Fcount AS INTEGER
  13. DIM tt AS STRING * 4
  14. DIM t AS STRING * 3
  15. DIM handle AS LONG
  16.  
  17.  
  18. 'To view BMP pictures in the root drive or a particular folder (example)
  19. 'IF load_dir("C:" + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  20. 'IF load_dir("C:\QB64" + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  21.  
  22. 'To view BMP pictures in the directory (folder) from where the EXE is run (current)
  23. IF load_dir("." + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  24.  
  25. 'To view BMP pictures in the parent directory (folder) from where the EXE is run (current)
  26. 'IF load_dir(".." + CHR$(0)) <> -1 THEN _FULLSCREEN: PRINT: PRINT "Path name does not exist": SLEEP: END
  27.  
  28.  
  29.     length = has_next_entry
  30.     IF length = -1 THEN
  31.         PRINT "No more files to process"
  32.         EXIT DO
  33.     END IF
  34.  
  35.     IF length > -1 THEN
  36.         NAM = SPACE$(length)
  37.         get_next_entry NAM, flags, file_size
  38.  
  39.         IF (flags XOR 2) = 0 AND UCASE$(RIGHT$(NAM, 3)) = "BMP" THEN
  40.             PRINT NAM + "                              "
  41.             Fcount = Fcount + 1
  42.             F(Fcount) = NAM
  43.         END IF
  44.  
  45.     END IF
  46.  
  47.  
  48. Fmax = Fcount
  49. close_dir
  50. Fcount = 0
  51.  
  52.     N = 0
  53.  
  54.     Fcount = Fcount + 1
  55.     OPEN F(Fcount) FOR BINARY AS #1
  56.  
  57.     GET #1, 11, OF
  58.     'PRINT "Offset to picture data"; OF
  59.  
  60.     GET #1, 15, L
  61.     'PRINT "Header size "; L
  62.  
  63.     GET #1, , W
  64.     'PRINT "image width "; W
  65.  
  66.     GET #1, , H
  67.     'PRINT "image height "; H
  68.  
  69.     GET #1, , I 'biPlanes. Specifies the number of color planes on the target device.
  70.  
  71.     GET #1, , I 'bits per pixel. Must = 24 for the program to work.
  72.  
  73.     IF I <> 24 THEN
  74.         CLS
  75.         PRINT "width "; W; "    Height "; H; "    Bits per pixel "; I
  76.         PRINT Fcount
  77.         PRINT F(Fcount); " is not a 24 bit per pixel BMP"
  78.         DO
  79.             IF INKEY$ <> "" THEN CLS: EXIT DO
  80.         LOOP
  81.         GOTO LL1
  82.     END IF
  83.  
  84.     DIM a AS _MEM
  85.     handle = _NEWIMAGE(W, ABS(H), 32)
  86.     a = _MEMIMAGE(handle)
  87.  
  88.     pad = 4 - (W * 3) MOD 4
  89.  
  90.     IF pad = 4 THEN pad = 0 'pad = multiple of 4
  91.  
  92.     IF H < 0 THEN
  93.  
  94.         FOR R = 0 TO ABS(H) - 1
  95.  
  96.             FOR C = 1 TO W * 3 STEP 3
  97.                 GET #1, R * (W * 3 + pad) + C + OF, t
  98.                 tt = t + CHR$(255)
  99.                 _MEMPUT a, a.OFFSET + N, tt
  100.                 N = N + 4
  101.             NEXT C
  102.  
  103.         NEXT R
  104.  
  105.     END IF
  106.  
  107.  
  108.     IF H > 0 THEN
  109.  
  110.         FOR R = H - 1 TO 0 STEP -1
  111.  
  112.             FOR C = 1 TO W * 3 STEP 3
  113.                 GET #1, R * (W * 3 + pad) + C + OF, t
  114.                 tt = t + CHR$(255)
  115.                 _MEMPUT a, a.OFFSET + N, tt
  116.                 N = N + 4
  117.             NEXT C
  118.  
  119.         NEXT R
  120.  
  121.     END IF
  122.  
  123.  
  124.     SCREEN _NEWIMAGE(W, (ABS(H)), 32)
  125.     _PUTIMAGE (0, 0), handle
  126.     _MEMFREE a
  127.  
  128.  
  129.     t1 = TIMER
  130.     DO
  131.         _LIMIT 100
  132.         t2 = TIMER
  133.         IF t2 - t1 > 2 THEN EXIT DO
  134.         i$ = INKEY$
  135.         IF i$ = CHR$(27) THEN END
  136.     LOOP
  137.     LL1:
  138.  
  139.     CLOSE
  140.  
  141.     IF Fcount = Fmax THEN Fcount = 0
  142.  
* BMPview2.exe (Filesize: 1.65 MB, Downloads: 144)
« Last Edit: February 07, 2021, 02:41:56 pm by NOVARSEG »

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
    • View Profile
Re: BMP picture viewer
« Reply #43 on: February 07, 2021, 06:47:13 am »
Nice, FYI this used to be my GOTO reference for whenever I needed to do anything with BMPs, explains the padding nonsense and some compression:
http://www.petesqbsite.com/sections/tutorials/zines/qbcm/12-bmps.html

Hey, Steve, are you fairly active on discord? Anyone know?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: BMP picture viewer
« Reply #44 on: February 07, 2021, 07:19:12 am »
Hey, Steve, are you fairly active on discord? Anyone know?

I go and come a lot.  The last week or so, I've been in fairly often, but for the next week or so, I probably won't be.  It's time for me to go get mom and take care of her again for a bit to give my sister a break.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!