Author Topic: How to load and save a picture faster when using this program?  (Read 1221 times)

0 Members and 1 Guest are viewing this topic.

Offline ascii

  • Newbie
  • Posts: 1
How to load and save a picture faster when using this program?
« on: December 07, 2021, 12:58:43 am »
So, below I have this little program, which is a kind of a graphic war. Or screensaver. First, it needs me to create a R:\Gwar.bmp 1920x1080 bitmap with 8 bit color depth (256 colors). But loading and saving the picture (when hitting Esc), is very slow. How can I speed that up?


CLS: DEFINT A-Z: DEFSNG N: DEFSTR F, S: DIM r(255), g(255), b(255)
v = 1920: w = 1080: SCREEN _NEWIMAGE(v, w, 256)
f = COMMAND$: IF f = "" THEN f = "r:\Gwar.bmp"
IF INSTR(f, "/") GOTO help ELSE IF INSTR(f, ".") = 0 THEN f = f + ".bmp"
IF NOT _FILEEXISTS(f) GOTO err1
OPEN f FOR RANDOM AS 1 LEN = 1: FIELD 1, 1 AS s: N = -1 'Read palette (55-1078)
FOR a = 55 TO 1075 STEP 4: N = N + 1: GET 1, a
r(N) = INT(ASC(s) / 4): GET 1, a + 1: g(N) = INT(ASC(s) / 4): GET 1, a + 2: b(N) = INT(ASC(s) / 4): NEXT
FOR a = 0 TO 255: PALETTE a, b(a) + 256 * g(a) + 65536 * r(a): NEXT: N = 1079 + v * w

'DRAW pic:
FOR a = 0 TO w - 1: N = N - v: FOR b = 0 TO v - 1: GET 1, N + b: PSET (b, a), ASC(s): NEXT: NEXT

WHILE INKEY$ <> CHR$(27)
  x = RND * (v - 1): y = RND * (w - 1): c = POINT(x, y) 'find a pixel and it's color. Next = war algorithm
  d = 3: PSET (x, y), c
  PSET (x, y - d), c
  PSET (x + d, y), c
  PSET (x, y + d), c
  PSET (x - d, y), c
  r1 = RND * 20 + 1: CIRCLE (x, y), r1, c
WEND

'SAVE pic:
save: N = 1079 + v * w: FOR a = 0 TO w - 1: N = N - v: FOR b = 0 TO v - 1: LSET s = CHR$(POINT(b, a)): PUT 1, N + b: NEXT

NEXT: PLAY "l16c": CLOSE: CLS: SYSTEM
err1: CLOSE: SCREEN 0: PRINT "r:\Gwar.bmp not found.": SLEEP: SYSTEM
help: CLOSE: SCREEN 0: PRINT "Graphwar x*y*256 bmp. Esc=Save. Command$ = filename.": SLEEP: SYSTEM
« Last Edit: December 07, 2021, 01:06:09 am by ascii »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: How to load and save a picture faster when using this program?
« Reply #1 on: December 07, 2021, 08:40:16 am »
Welcome @ascii

I can't read that yellow text that starts your post but I assume you are looking to load images and save images from your title.

For loading an image check out
https://www.qb64.org/wiki/Images

general overview and
https://www.qb64.org/wiki/LOADIMAGE

particularly.


For SaveImage I recommend looking under Utilities Board
https://www.qb64.org/forum/index.php?topic=1651.msg108777#msg108777

for Steve McNeill's SaveImage routines which will link you to
https://www.qb64.org/forum/index.php?topic=2701.msg138667#msg138667

his most recent update of that code.

« Last Edit: December 07, 2021, 10:42:55 am by bplus »

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
Re: How to load and save a picture faster when using this program?
« Reply #2 on: December 07, 2021, 09:55:19 am »
Welcome @ascii
I can't read that yellow text that starts your post but I assume you are looking to load images nd save images from your title.

B+,
guess u are on the light SMF theme, me as well, simply mark the text as if you would do to copy it, it will become readable.

As most users (and definitly new users) are on the dark theme, we must probably live with bad readable text. We, still at the light theme, are a minority.
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: How to load and save a picture faster when using this program?
« Reply #3 on: December 07, 2021, 10:01:35 am »
@RhoSigma Oh! thanks for tip, works perfect!

Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
Re: How to load and save a picture faster when using this program?
« Reply #4 on: December 07, 2021, 10:36:20 am »
So, below I have this little program, which is a kind of a graphic war. Or screensaver. First, it needs me to create a R:\Gwar.bmp 1920x1080 bitmap with 8 bit color depth (256 colors). But loading and saving the picture (when hitting Esc), is very slow. How can I speed that up?


CLS: DEFINT A-Z: DEFSNG N: DEFSTR F, S: DIM r(255), g(255), b(255)
v = 1920: w = 1080: SCREEN _NEWIMAGE(v, w, 256)
f = COMMAND$: IF f = "" THEN f = "r:\Gwar.bmp"
IF INSTR(f, "/") GOTO help ELSE IF INSTR(f, ".") = 0 THEN f = f + ".bmp"
IF NOT _FILEEXISTS(f) GOTO err1
OPEN f FOR RANDOM AS 1 LEN = 1: FIELD 1, 1 AS s: N = -1 'Read palette (55-1078)
FOR a = 55 TO 1075 STEP 4: N = N + 1: GET 1, a
r(N) = INT(ASC(s) / 4): GET 1, a + 1: g(N) = INT(ASC(s) / 4): GET 1, a + 2: b(N) = INT(ASC(s) / 4): NEXT
FOR a = 0 TO 255: PALETTE a, b(a) + 256 * g(a) + 65536 * r(a): NEXT: N = 1079 + v * w

'DRAW pic:
FOR a = 0 TO w - 1: N = N - v: FOR b = 0 TO v - 1: GET 1, N + b: PSET (b, a), ASC(s): NEXT: NEXT

WHILE INKEY$ <> CHR$(27)
  x = RND * (v - 1): y = RND * (w - 1): c = POINT(x, y) 'find a pixel and it's color. Next = war algorithm
  d = 3: PSET (x, y), c
  PSET (x, y - d), c
  PSET (x + d, y), c
  PSET (x, y + d), c
  PSET (x - d, y), c
  r1 = RND * 20 + 1: CIRCLE (x, y), r1, c
WEND

'SAVE pic:
save: N = 1079 + v * w: FOR a = 0 TO w - 1: N = N - v: FOR b = 0 TO v - 1: LSET s = CHR$(POINT(b, a)): PUT 1, N + b: NEXT

NEXT: PLAY "l16c": CLOSE: CLS: SYSTEM
err1: CLOSE: SCREEN 0: PRINT "r:\Gwar.bmp not found.": SLEEP: SYSTEM
help: CLOSE: SCREEN 0: PRINT "Graphwar x*y*256 bmp. Esc=Save. Command$ = filename.": SLEEP: SYSTEM

There are many things you can do to speed it up. I'll take it gradually. To read or change the palette of an 8-bit image, we have the command and function _PALETTECOLOR. Use _MEMPUT instead of PSET, use _MEMGET instead of POINT. To save and load an image. Use the _LOADIMAGE function (bmp, jpg, png, gif) to load and use the SAVEIMAGE function (jpg, bmp, png, gif formats) to save it. SaveImage you can download from @SMcNeill signature.

Another thing - do you need to save those images immediately, or can you first generate a hundred images and then save them at once? They can be stored in memory (see the commands _DEST, _SOURCE, _NEWIMAGE, _FREEIMAGE) because any immediate storage on the hard disk is always slow (maybe it will be fast with the SSD disk or if the program runs in RAMDISK). With QB64 you have many options including direct memory access.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: How to load and save a picture faster when using this program?
« Reply #5 on: December 07, 2021, 10:38:16 am »
@ascii

I have taken Steve's SaveImage download .zip altered to add your code with mods to fit my screen and draw concentric circles at random places until escape and then saves the picture into a .bmp format after you press escape from the circle drawing loop.

It's all in a zip, the exe for Windows but always best compile source .bas yourself.

Do you know about BI's and BM's as applies to QB64? because Steve's code uses them for SaveImage work.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: How to load and save a picture faster when using this program?
« Reply #6 on: December 07, 2021, 10:38:36 am »
For something simple like this, just do a quick screen grab.

DIM M AS _MEM: M = _MEMIMAGE(0)
Screengrab$ = SPACE$(M.SIZE)
_MEMGET M, M.OFFSET, Screengrab$

The above will:
Dim a mem block.  Point it to your screen's image.
Set a string that size.
Get the whole screen at once into that string.


Then to save to disk all at once:
OPEN "my_file.bin" FOR BINARY AS #1
PUT #1, , Screengrab$
CLOSE

Open the file, put the data in one chunk, close the file.



Loading is the same, in reverse:
OPEN "my_file.bin" FOR BINARY AS #1
GET #1, , Screengrab$
CLOSE


And putting the data back to screen is also done all in one command:
_MEMPUT M, M.OFFSET, Screengrab$



You'll be hard pressed to find any method faster than that.
« Last Edit: December 07, 2021, 11:59:14 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: How to load and save a picture faster when using this program?
« Reply #7 on: December 07, 2021, 10:43:32 am »
@ascii

I have taken Steve's SaveImage download .zip altered to add your code with mods to fit my screen and draw concentric circles at random places until escape and then saves the picture into a .bmp format after you press escape from the circle drawing loop.

It's all in a zip, the exe for Windows but always best compile source .bas yourself.

Do you know about BI's and BM's as applies to QB64? because Steve's code uses them for SaveImage work.

There's a problem with your approach @bplus:  Loading the image.

QB64 only _LOADIMAGEs 32-bit images.  The program is running and saving in 256-color mode.   You'll need to convert/dither down to 256 colors after loadimage loads your saved file in the wrong format, taking an unnecessary speed hit.

I'd just do a whole screen grab, save, and restore, as I posted above, for a 256 color screen.  (Unless I needed the images for an external program.)
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.
Re: How to load and save a picture faster when using this program?
« Reply #8 on: December 07, 2021, 10:53:26 am »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: How to load and save a picture faster when using this program?
« Reply #9 on: December 07, 2021, 11:09:37 am »
Yeah Steve yours works great! if you don't have the bin file ready yet or working from one of your own

after you fix your Typo!! ;-)) (in 3rd line in post above)

Code: QB64: [Select]
  1.  
  2. Scn& = _NewImage(800, 600, 32)
  3. Screen Scn&
  4. Circle (400, 300), 200
  5. Print "Save this endangered circle!   zzz..."
  6.  
  7. Dim M As _MEM: M = _MemImage(Scn&)
  8.  
  9. Screengrab$ = Space$(M.SIZE)
  10.  
  11. _MemGet M, M.OFFSET, Screengrab$
  12.  
  13. 'The above will:
  14. 'Dim a mem block.  Point it to your screen's image.
  15. 'Set a string that size.
  16. 'Get the whole screen at once into that string.
  17.  
  18.  
  19. 'Then to save to disk all at once:
  20. Open "Test SaveLoad Image.bin" For Binary As #1
  21. Put #1, , Screengrab$
  22.  
  23. 'Open the file, put the data in one chunk, close the file.
  24.  
  25. Print "Press any to see circle again... "
  26.  
  27. 'Loading is the same, in reverse:
  28. Open "Test SaveLoad Image.bin" For Binary As #1
  29. Get #1, , Screengrab$
  30.  
  31.  
  32. 'And putting the data back to screen is also done all in one command:
  33. _MemPut M, M.OFFSET, Screengrab$
  34.  
  35.  

Is this faster than loadImage? It seems way more convenient for saving images!
« Last Edit: December 07, 2021, 11:21:37 am by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: How to load and save a picture faster when using this program?
« Reply #10 on: December 07, 2021, 11:57:51 am »
I haven't tried bplus's demo yet, but here's how I'd normally do this type of thing:

Code: QB64: [Select]
  1. Cls: DefInt A-Z: DefSng N: DefStr F, S: Dim r(255), g(255), b(255)
  2. v = 1920: w = 1080: Screen _NewImage(v, w, 256)
  3. Const DataSize = 1920 * 1080
  4. Dim m As _MEM: m = _MemImage(0) 'make a mem block, point it at the screen
  5. Dim ImageData As String * DataSize 'set a string large enough to hold the screen
  6.  
  7. Open "My Movie.movie" For Output As #1: Close #1 'make certain our file is blank starting out
  8. Open "My Movie.movie" For Binary As #1 'open a file to get and put data to
  9.  
  10.  
  11. 'let's draw to the screen
  12. 'heck, let's save while we're doing it.
  13.  
  14. t# = Timer + 5 'I'm going to draw and save for 5 seconds uninterrupted.
  15.     CircleFill Rnd * _Width, Rnd * _Height, Rnd * 500, _RGB(Rnd * 255, Rnd * 255, Rnd * 255)
  16.     _Display
  17.     _Limit 300 ' Let's run with an insane 300 fps for a limit here.
  18.     _MemGet m, m.OFFSET, ImageData
  19.     compress$ = _Deflate$(ImageData): l = Len(compress$)
  20.     full$ = full$ + MKL$(l) + compress$
  21.     count = count + 1
  22. Put #1, 1, full$ 'put our whole movie to disk in one pass.
  23.  
  24.  
  25. Sleep 'pause so the user can see the screen
  26.  
  27. Cls 'blank the screen just to start with a blank canvas
  28. 'go back to the start of the file for image loading
  29.  
  30. Open "My Movie.movie" For Binary As #1 'open a file to get and put data to
  31.  
  32. t# = Timer
  33.     Get #1, , l
  34.     If EOF(1) Then Exit Do
  35.     compress$ = Space$(l) 'a string the size of the compressed data
  36.     Get #1, , compress$
  37.     ImageData = _Inflate$(compress$)
  38.     _MemPut m, m.OFFSET, ImageData
  39.     _Limit 300
  40.     _Display
  41.     recount = recount + 1
  42. t1# = Timer - t#
  43.  
  44. Print "In 5 seconds we drew, grabbed, compressed, and saved"; count; " images."
  45. Print Using "In #.## seconds, we loaded, uncompressed, and redrew those same #### images."; t1#, recount
  46.  
  47. Kill "My Movie.movie" 'clean up the drive of our "movie" after this demo is over.
  48.  
  49.  
  50.  
  51.  
  52. Sub CircleFill (CX As Integer, CY As Integer, R As Integer, C As _Unsigned Long)
  53.     ' CX = center x coordinate
  54.     ' CY = center y coordinate
  55.     '  R = radius
  56.     '  C = fill color
  57.     Dim Radius As Integer, RadiusError As Integer
  58.     Dim X As Integer, Y As Integer
  59.     Radius = Abs(R)
  60.     RadiusError = -Radius
  61.     X = Radius
  62.     Y = 0
  63.     If Radius = 0 Then PSet (CX, CY), C: Exit Sub
  64.     Line (CX - X, CY)-(CX + X, CY), C, BF
  65.     While X > Y
  66.         RadiusError = RadiusError + Y * 2 + 1
  67.         If RadiusError >= 0 Then
  68.             If X <> Y + 1 Then
  69.                 Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  70.                 Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  71.             End If
  72.             X = X - 1
  73.             RadiusError = RadiusError - X * 2
  74.         End If
  75.         Y = Y + 1
  76.         Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  77.         Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  78.     Wend
  79.  

Now, on my PC this is set to run at a limit of 300 FPS. 

SPOILER ALERT -- We don't actually get those speeds from it!

What we're doing here is drawing random circles of various sizes, colors, and positions, on a large 1920x1080 screen. Then we're grabbing that screen, compressing it so it doesn't use so much disk space, and then saving it to the disk.

I'm letting this run for 5 seconds to create a movie for me, and the statistics I get on my laptop are:

198 screens grabbed and compressed, and then wrote to disk in a 5MB file after running for 5 seconds.

Loading and displaying of those same 198 screens only takes about 1.8 seconds on my laptop.

This gives us a savetime of about 0.025 seconds per screen, with a loadtime of about half that amount -- all while compressing the image to reduce disk usage as much as possible.

I'd call that an acceptable speed for my general usage needs.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!