# Visual Basic > Visual Basic 6 and Earlier >  GdipSaveAddImage (GIF animation)

## baka

so I messed around with GifMakerByTheTrick and other sources to create a gif-maker that can be used kind of a "recorder"
so, first I create the bitmap/image. thats fast, no problem here.
after that I use GdipSaveImageToStream and GdipSaveAddImage to merge the pictures and lastly I save it.

now, even without any optimization (I skip all that) and use the minimal code to addimages its very slow.
takes, at least 1 second to add 1 picture, so if the .GIF file is around 10MB (when done) it would take several seconds to make. we are talking 10+ seconds

if I instead add images directly, it would make the game start to stuttering, since that second would freeze the game-loop.
the problem is not save the istream to HDD, thats fast, but the API GdipSaveAddImage

are there any other alternative to GdipSaveAddImage?

I mean, if I use GdipSaveImageToFile to just save 1 .gif file, its superfast.
so I wonder, why GdipSaveAddImage is so slow.

----------


## baka

ok, so using

GdipSaveImageToFile
GdipSaveAddImage

is faster ,
but I have problems using GdipSetPropertyItem to set the framerate of the GIF.
since I don't know how many frames the GIF will be I can't set the length when creating the file.

it seems no matter what kind of values I use it will not work.

will do a bit more testing for optimization and will see if I can manage.

----------


## wqweto

Btw, usually screen recorders grab frames off screen as quickly as possible usually to some uncompressed format (e.g. AVI container with uncompressed DIBs) and then shell *ffmpeg.exe* to convert this to a tightly compressed/optimized GIF

I know you are possibly targeting Win7 and that's why I'm not mentioning DirectX11 built-in Desktop Duplication which is like having a free screen mirror driver preinstalled on Win8 and above. Life is tough in the barren Win7 land :-))

cheers,
</wqw>

----------


## Schmidt

I'd encode to (M)JPG - since it offers the best compromise between buffer-size and "needed encoding-power".

e.g. if you have a Game-Surface Bitmap-Size of 1024x768, encoding that to a JPG:
- would take roughly 8-12msec
- and a size of roughly 150-250kByte in the resulting ByteArray

If you do that continuously, e.g. a Stream with 15 JPG-images per second, would result in:
- an additional CPU-Load of only about 15% (leaving enough breathing-space for your GameLoop)
- and a Stream-of-ByteArrays which sums up to about 3MB per second
- so, ... 10secs will "cost" you about 30MB, 30seconds about 100MB Memory 

You could of course also forego the In-Memory-buffering and save directly to disk
(3MB-per-second is a Write-rate, which even older "spinning disks" can manage without problem).

The disadvantage (if you don't want to fiddle around with AVI-Containers and -APIs) would be, 
that you will have to "invent your own MJPG-FileStream-Format" ...
Easiest done, with a small "fixed Header at the beginning of the File:
- HdrField1: TotalFrameCount
- HdrField2: FrameRatePerSecond
- HdrField3: "ByteOffset-To-JpgBufferSize-Table-at-the-end-of-the-file"

The JpgBufferSize-Table is just a simple "Long-Array" and the only thing which you "keep and prolong in memory"

Your encoded JpgBufs can then be simply appended at the end of the file (behind the 3 "Fixed-Header-Fields") -
"as they come in" (without any "separators" between them).

When the recording is finished, the last actions to take on the file would be:
- filling HdrFields 1 + 2 with the appropriate information for this recording
- then storing the current StreamPosition in HdrField3 
- followed by appending your InMemory-Long-Array with the JpgBuf-Sizes of each encoded Buffer
After that you can close the File.

HTH

Olaf

----------


## baka

yeah. I notice that the picture gets big fast. theres no compression going on here. and quality is not that great
hm. m(jpg) seems to be available to play using a media-player, such as mediaclassic. my firefox couldn't open it and neither window photo viewer.
the good with .gif is that is old and it can be played with most system, even if its just 256 colors. but if I can't make the .gif maker work I will look into mjpg.

right now Im thinking of:
- create the bitmap (fast) using a timer, like 5 pics/second, this when I press the "start button"
- when I click the "start button again" it will instead start to save pic by pic. at least during recording is fast, but during saving it can be a bit choppy.

I will do some testing tomorrow.

----------


## dilettante

Creating and Saving a Multiple-Frame Image




> Note
> 
> You cannot use SaveAdd to add frames to an animated gif file.

----------


## baka

yeah. Im using that method dilettante. its not that I can not make one, but its slow.
the trick is using GdipSaveAdd, in his "ISteam" method to complete the GIF and also apply speed.
following his code it works. but slow.

another thing that Im thinking about is this:
- create a new project, this project can receive a "hdc" command + format (If I want to use with to take screenshot in jpg/png and gif)
- this program will start recording the hdc. when the project is receiving another input from the main-program it will stop recording and it will save to hdd

will this help u think?
this is like using multithreading but instead of the same program I use an external one
this to avoid any stuttering, also I could apply some filter and even compressing since it will not matter anymore.

----------


## wqweto

Btw, an issue with (M)JPEG compression on temp screen grab is that it might introduce slight differences/jitter between frames which will later increase size of GIF delta compression.

----------


## baka

ok- I created the external program that will record and save in jpg/png and gif(animated)
and it works well. no freezing at all.

still the size of the .gif is big. theres online tools that can make it half the size
looking in gdi+ I can't find anything at all. just for jpeg

maybe its time to look into WIC and see if I can use that instead of GDI+

----------


## baka

very hard to find anything at all.
in the end I found a command-line tool called gifsicle that I can call from "shell" after the .gif is saved.
its not the best. from 10-40% reduction, but still better than nothing.

----------


## wqweto

Did you try to shell ffmpeg?

----------


## baka

no. but ffmpeg is something that need to be installed? gifsicle is just 1 exe that is 200kb in size no dependency or other files.
but I can do some testing and see the differences between the two in compression. will do that tomorrow.

----------


## Brenker

In the illustration below, the frames on the left side are the captured ones on screen.  The frames on the right side are what should be the actually saved ones in an Animated GIF/ PNG file, which may well be smaller in size, may start at an offset other than [0, 0], and may have a designated transparent color.

----------


## baka

yeah. it seems its what gifsicle is doing when using the optimization mode.
Im thinking that VB6 is slow here. we need all those low-level bitwise operators to analyze each frame for this purpose.
if I do the scanning myself in VB6 it will never be as fast as gifsicle that also has a 64bit version to that.
that is why I need an API that can do it for me. but GDI+ doesn't seem to have this optimization, unlikely WIC has it either. 
looking at c/c++ code online I find methods (bitwise) to optimize the gif or anything that is to change colors.
will need to stick with gifsicle until maybe Twinbasic is ready for my needs. also I dont want to spend too much time in this,
since the .gif recording is just something extra for my game, I need to focus on the game. my surely in the future this will be interesting to look at again in TB.

----------


## baka

so, I expanded the external-exe with more functionality.
now it can also play MP3. I notice the game runs a bit smoother, since theres no need to load new MP3 when the music is changing (that before could create a small freeze)
quite easy to control using sendmessage

----------


## Schmidt

> Btw, an issue with (M)JPEG compression on temp screen grab is that it might introduce slight differences/jitter between frames which will later increase size of GIF delta compression.


That's true - but what I meant (especially when the Game is TrueColor - and not a "mostly-static one"),
was that the "Sum of the JPG-Frames" is usually below a single ("GifSicled" or not) animated GIF.

A simple player for these JPG-Frames (no matter if they were accumulated in a singe File, or just sitting there in a Folder),
can be done in a few lines of code, with just a Timer-Control (set to the stored recording-frequency).

@baka
If you record and encode a certain "typical scene with typical dynamic" (e.g. 100 Frames long) writing both:
- 100 JPG single Frames 
- 100 GIF single Frames 
...and then run the 100GIFs through "GifSicle" with all "delta-Optimizations" -
what is larger - the resulting animated Single-GIF-File, or the Sum of the 100 JPG-Files in their temp-folder?

Olaf

----------


## baka

Schmidt, yeah I get what u mean.
the resulting GIF need to show "full-animation" and not (like the elephant) objects with transparent background moving.
so, even the gifsicle optimization the GIF is big. so if we record 30seconds:
takes around 9 seconds to "finish my part", after that it calls gifsicle that takes around the same time to be done. so 15-20 seconds to make the .gif + compression
and the resulting size is 54MB.
the animation is 10frames/sec so its 30x10=300 frames. so 300 GIF pictures changed into 256 colors with the dimension of 800x600
using the 2 other format in that area Im in the game and GDI+ with quality setting set to 80:
PNG: 450kb *300=135MB
JPG: 160KB *300=48MB
GIF: 180KB *300=54MB

and this is JPG without any optimization, just GDI+ quality 80 saving of 1 pic.

so I think with a bit lower quality we could reach 1 second/1MB, so 30 seconds into 30MB

----------


## The trick

With a static background you could greatly reduce the size in GIF. GDI+ doesn't have such option (as far as i remember) but it's quite simple format.

----------


## baka

its moving background, since its a screenshot from the game. everything is moving. 
but since its 10 pictures / second, Im not moving the camera all the time. sometimes Im on the same spot, killing a monster. that is why gifsicle can optimize it
so it all depends on where I am and what Im doing in the game that will affect the end-result after gifsicle optimization.

but maybe I will add MJPEG as well, as a saving solution. I mean, why not?
I just need to investigate the header and how to merge the JPG's into 1 MJPEG file.

----------


## -Franky-

Here is a simple example using WIC to assemble images into an animated GIF. http://www.activevb.de/cgi-bin/uploa...oad.pl?id=3711

----------


## baka

ok. I added the MJPEG and its working. but, theres no header and I can't set fps.
reading online, it says theres no Standardisation. 
its just jpg merged together and some media-players can "detect" its multi-jpg file and run it.
but since theres no header theres no way to tell the player to adjust its speed.
what I can read is that u need to convert it into a format that has a header where u can input that variable.
sure it works, but since I want it to be 10fps its running too fast.
if I use 25fps the jpg will get big. maybe even bigger than the .gif

edit:
also tried with AVI. using the avifil32.dll, but the size is big as well. 
at least here I can decide the framerate. but a 3 second AVI (10fps) takes 42MB
but the video is RGB24 so of course its big. need to see if I can compress is or use something else.

----------


## baka

ok Im reading about AVI OpenDML + MJPEG
if theres a way to combine the two it could allow to set framerate right?
now, the question is, how to do it.
just add AVI header and after that append jpeg one by one or theres more to it?
will take some time to investigate

----------


## wqweto

> or theres more to it?


There is more to it. AVI is container format, something called RIFF -- it's pretty simple to parse and produce manually but it's the A/V codec inside it which is hard.

cheers,
</wqw>

----------


## Brenker

If I see that we can arrive at a consensus on all the following points, I might be willing to point a way out for you to try.  

Point 1: * We should recognise the shortcoming of GDIplus to handle frame images on Load and Save*.

In posting #13, I presented what should be the correct frame images in a properly coded Animated GIF / PNG file.  Why do we sometimes have an Animated GIF file  which has frame images as shown in the left hand side of my posting #13?

The culprit at the root is the bad influence of GDIplus' GdipImageSelectActiveFrame,  because using it you can obtain only the "processed frame image".  This has the tendency of misleading the user to think that the "processed frame image" is the "actual frame image coded in the file".    Later on the user may just take the said processed frame images and call GDIplus' GdipSaveAddImage to form an Animated GIF file (It is pity that I've seen too many VB guys doing it this way).

Point 2: *We are not going to involve gadgets like gifsicle or ffmpeg*.

Point 3:  *We should abandon palette Optimization*.

On forming an animated GIF file, we do color optimization (to 256-color palette) as and when we progress for each "processed frame image".  We need to do color optimization because the "processed frame image" might have more than 256 colors.

The above practice will lead you to nowhere, as there is the "speed" constraint which you cannot surmount.

Point 4:  *We should aim at an Animated PNG file, not an Animated GIF file*.

(1) The Animated PNG file can be a simple practical solution (I'd seen live examples of them, and can vouch for their validity - their frame images are those per the right hand side in posting #13); and (2) If wanted, we can convert the resultant Animated PNG file to an Animated GIF file usng the free conversion websites (there are two good sites I know, but I prefer the one avoiding "duplicate palette entries").

----------


## Schmidt

> ok. I added the MJPEG and its working. but, theres no header and I can't set fps.


Then just add your own header at the end; somewhat similar to what I wrote in #4 -
and then write your own little "BakaMJPG_Player.exe" ...  :Wink: 
(using a timer-control, a H-slider and a Play/Stop-button + some 10-20 lines of code)

Olaf

----------


## baka

well. yeah. its easy to create a small mjpg player with framespeed at the end of the file as u say.
the only problem is that I want people to play it without the need of a special player.

but its not a bad idea.
and to make it even easier I can create a list.
end of file is frame-count + frame-speed, 2 integer.
knowing the frame-count I go back in the bytearray() to read each "size" of each jpg.
so I dont need to scan for start/end and after that load all jpeg. should go quite fast using WIC and D2D for playback.

will do that tomorrow.

----------


## wqweto

https://github.com/Ricardicus/mjpeg-avi

This guy is constructing the RIFF content from JPGs manually.

Might try translate this approach to VB6 i.e. instead of storing in custom temp file.

----------


## baka

I will look into it later. I have spent too much time into this "little-side-project"
but I will definitely look into this jpg to avi method and how he did it and translate into vb6 if possible.

----------


## reexre

A long time ago I had taken and modified some code (I don't remember whose it was) to create a screen recorder.
It captures a series of Jpg images from the screen which are then encoded in Video.
The code uses avifil32.dll and is quite spaghetti, but perhaps the WriteAVI sub may be of interest.
https://www.vbforums.com/showthread....creen-Recorder

----------


## reexre

duplicated...

----------


## baka

nice, thx. will check it out!

----------


## wqweto

https://trac.ffmpeg.org/wiki/Capture/Desktop

Btw, just found out that ffmpeg can record screen from GDI source or from Desktop Duplication (Win8+) and can use NVIDIA h/w accelerated encoding. Unfortunately all bells and whistles amount to a 80MB executable :-))

cheers,
</wqw>

----------

