Png animation


#1

hi,

I’d like to make a png animation. I’ve created a timer and I want it to draw each frame of my animation consecutively. The problem is that I need an instance of the class Graphics to draw an image, and I can find it only in my “paint()” method… Can somebody help me find a solution please? thanx a lot.

Leskimo


#2

damnit! I promess, next time I’ll think more about my issue before posting here… I think I found a solution, I just saw that I can create a instance of graphics from an image. The fact is that my app has an image on its background and I have access to this image. So normaly I should be able to draw something on my background… Gonna try it, sorry about this thread.

Leskimo


#3

Aghh… You’ve fundamentally misunderstood how it all works! Go and read carefully the comments for the Component::paint and Component::repaint methods!


#4

how to do this step by step in a easy and not optimized way:

  • define in your component the png frames as OwnedArray frames
  • keep also a int counter
  • initialize the counter to 0
  • load all the pngs in order and add() them to the frames array
  • then start the timer, and trigger a repaint() in timerCallback
  • then in you paint method, you only need to access the right png, blit it and then increment the counter, something like:

Image* currentFrame = frames.getUnchecked(counter++);
g.drawImageAt( 0,0, currentFrame->width()… blah blah)
// or the like, look into documentation which draw method better suit your needs

// then check for counter correctly
if (counter >= frames.size())
counter = 0;

that’s all, remember this is not a good way to do things, is better to have another image (treated as image buffer) in the component and then in the timer just blit the correct frame onto that image and check for frames bounds, and leave the paint method only blitting of the shared image buffer…


#5

I took a look at the paint and repaint method and finally understood how it works… I was for sure completly wrong. Thanx kraken for your “step by step” tutorial.

Leskimo


#6

Hey  kraken,

                      I followed your step by step tutorial. Below are the code snippets.

 

-define in your component the png frames as OwnedArray  frames 

OwnedArray<juce::Image> images;

- load all the pngs in order and add() them to the frames array- doing this in the constructor.

 for(int a= 0;a<8;a++)

    {

        juce::File f("/Users/nehamakhija/Downloads/sprite_individuals/a[a].png");

        image = ImageFileFormat::loadFrom(f);

        images.add(&image);

}

 

then start the timer, and trigger a repaint() in timerCallback

void timerCallback()

    {

        startTimer(100);

        repaint();

    }

 

 

 then in you paint method, you only need to access the right png, blit it and then increment the counter

 

       void paint (Graphics& g)

        {

            

    

             

       

            if (counter <= images.size())

            {

            

            juce::Image* currentFrame = images.getUnchecked(counter++);

            g.drawImageAt(*currentFrame,0.5,0.1);

             counter++;

            }

            else

                counter = 0;

}

 

when I run my program I get the following error

 

JUCE Assertion failure in juce_OwnedArray.h:157

 

where am I going wrong. 

 

Thanks

Neha

    

 

 

 

 


#7

Debug and watch the value of counter.

Rail


#8

Image objects are reference-counterd The bug may be you're getting the address of the variable 'image' (presumably a member variable?) and putting that in the OwnedArray.

juce::File f("/Users/nehamakhija/Downloads/sprite_individuals/a[a].png");
image = ImageFileFormat::loadFrom(f);
images.add(&image);

Since Image objects should be passed by value, you can probably just use an Array<> rather than OwnedArray<>. But if you do use OwnedArray<Image> then it would need to be something like this:

juce::File f ("/Users/nehamakhija/Downloads/sprite_individuals/a[a].png");
images.add (new Image (ImageFileFormat::loadFrom (f)));

If you just use Array<Image> then it's:

juce::File f ("/Users/nehamakhija/Downloads/sprite_individuals/a[a].png");
images.add (ImageFileFormat::loadFrom (f));

Your counter 'a' is another problem. If you're images are called a0.png, a1.png, a2.png, etc. Then the correct code is:

juce::File f (String ("/Users/nehamakhija/Downloads/sprite_individuals/a") + String (a) + String (".png"));

or

juce::File f (String::formatted ("/Users/nehamakhija/Downloads/sprite_individuals/a%d.png", a));

(You can put the square brackets back into the strings if they actually part of the filename, I wasn't sure.)

Hope this helps.

 


#9

furthermore think that paint is not only called by the timerCallback() but by any occasion when the windowing system thinks it should be repainted (e.g. was hidden before by another window, was resized, ...)

So the changing of counter should not depend on a call to paint(). This should rather be done in your timerCallback().

like:

void timerCallback()
{
    counter = counter++ % images.size();
    repaint();
}

so in paint() you do ONLY painting:


void paint (Graphics& g)
{
    juce::Image* currentFrame = images.getUnchecked(counter);
    g.drawImageAt(*currentFrame,0.5,0.1);
}

or shorter:


void paint (Graphics& g)
{
    g.drawImageAt (images[counter], 0.5, 0.1);
}

And your mathematics is also broken:

if (counter <= images.size())

if counter== images.size() your getUnchecked() is already out of bounds, because you count from 0.

 

And you don't have to start the timer in your callback, because it is an interval. Rather do startTimer() or startTimerHz() in the constructor or where ever you initialize things.

HTH and good luck


#10

Very helpful comments here!

I'd just add that you should never need to create an Image on the heap, always create them as copy-by-value objects. If you ever see yourself typing Image* or new Image, you're probably off-track.


#11

Thanks guys for all the help!!! It helped me a lot.....

 

 

Thanks

Neha


#12