Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Al_save_bitmap only saves a blank png

Credits go to Edgar Reynaldo for helping out!
This thread is locked; no one can reply to it. rss feed Print
Al_save_bitmap only saves a blank png
TheNextGuy
Member #15,702
August 2014

Hey guys, I'm trying to make a function that saves a screenshot to a folder. I have it creating the .png's correcting but all of them are blank.

I am able to draw to the screen and I have tried to flipping the screen before and after screenshotting but neither does anything.

Here is what my screen looks like and what the screenshots look like.
http://imgur.com/a/6LzYu

Here is the code that produces my screenshots. The actual al_save_bitmap call is near the bottom but I thought it good measure to include it all.

#SelectExpand
1int AllegroEngine::FlushScreenshot(const char *destination_path, std::string myWorldName,int myCurrentYear,int myCurrentMonth, int myCurrentDay) 2{ 3 ALLEGRO_PATH *path; 4 char *filename; 5 const char *path_cstr; 6 int char_index = 0; 7 8 //Testing the destination given 9 if(!destination_path) 10 path = al_get_standard_path(ALLEGRO_USER_DOCUMENTS_PATH); 11 else 12 path = al_create_path_for_directory(destination_path); 13 14 if(!path) 15 return -1;//Where we want it is bad 16 17 18 //Length of gamename : YYYY- MM - DD NULL terminator 19 int bytes = myWorldName.size() + 1 + 4 + 1 + 2 + 1 + 2 + 1; 20 if (!(filename = (char *)malloc(bytes))) 21 { 22 al_destroy_path(path); 23 return -4;//We don't have enough space? 24 } 25 26 //Creating the file name 27 std::string name = myWorldName + "-"+ std::to_string(myCurrentYear)+"-"+std::to_string(myCurrentMonth)+"-" + std::to_string(myCurrentDay); 28 29 for (int c = 0; c < name.length(); c++) 30 { 31 filename[char_index] = name[c]; 32 char_index++; 33 } 34 filename[char_index] = '\0'; 35 36 //Setting up file specificiations 37 al_set_path_filename(path, filename);//Where are we writing this 38 al_set_path_extension(path, ".png");//We are creating a png 39 path_cstr = al_path_cstr(path, ALLEGRO_NATIVE_PATH_SEP); 40 41 42 if (!al_filename_exists(path_cstr)) 43 { 44 //Saving the bitmap 45 std::cout<<"Screenshot saved at "<<path_cstr<<" named "<<filename<<"."<<endl; 46 al_save_bitmap(path_cstr,al_get_backbuffer(main_display)); 47 return 1; 48 } 49 else 50 { 51 //We've already printed out this file 52 delete(filename); 53 al_destroy_path(path); 54 } 55 return -6;//We never placed a file

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Don't save from the backbuffer. It's contents are usually lost when you flip the display. And you won't be referring to the same bitmap as the one being displayed anyway.

Try using a buffer bitmap or just redraw to a buffer when you want to save it.

Neil Roy
Member #2,229
April 2002
avatar

This is a function I came up with to do just that and it works beautifully. Saves the screenshot with a date and time extension so if you save multiple screenshots they will all be named differently, just need to pass it the name of the game "My_Game" and that will be used with the date and time appended...

a5_screenshot.c

#SelectExpand
1// Note: if you use physfs, than you need to call al_set_standard_file_interface(); 2// before this function and al_set_physfs_file_interface(); afterwards. 3 4#include "a5_screenshot.h" 5 6bool a5_screenshot(const char *gamename) 7{ 8 time_t rawtime; 9 struct tm *timeinfo; 10 char filename[80], timestr[80]; 11 bool saved; 12 ALLEGRO_STATE state; 13 14 al_store_state(&state, ALLEGRO_STATE_NEW_FILE_INTERFACE); 15 16 al_set_standard_file_interface(); 17 18 time(&rawtime); 19 timeinfo = localtime(&rawtime); 20 21 strftime(timestr, 80, "%Y%m%d_%H%M%S", timeinfo); 22 snprintf(filename, 80, "%s_%s.png", gamename, timestr); 23 24 saved = al_save_bitmap(filename, al_get_target_bitmap()); 25 26 al_restore_state(&state); 27 28 if(!saved) return false; 29 30 return true; 31}

a5_screenshot.h

#ifndef _a5_screenshot_h_
#define _a5_screenshot_h_

#include "allegro5/allegro.h"
#include <stdio.h>

bool a5_screenshot(const char *gamename);

#endif //_a5_screenshot_h_

---
“I love you too.” - last words of Wanda Roy

Thomas Fjellstrom
Member #476
June 2000
avatar

With that code, just make sure to call it before an al_flip_display if you're trying to save from the backbuffer. If not, then it doesn't really matter.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

TheNextGuy
Member #15,702
August 2014

Even when I say al_get_target_buffer it comes up blank. With both target and back buffer and whether or not the screen flipping comes before, after, or not all makes no difference.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

TheNextGuy
Member #15,702
August 2014

Yes I have. The solution NickHackr presented is deprecated but even when updated it does not work. It creates blank png's all the same.

I have tried setting it to al_get_target_bitmap() and moving and deleting the flipping to no avail.

I suspect it has to do with drawing improperly or something. I only ever draw primitives. For instance:

#SelectExpand
1void Game::DrawBlade(int x, int y, unsigned char r,unsigned char g,unsigned char b) 2{ 3 al_draw_line(x,y+1,x,y-4,al_map_rgb(0,0,0),1);//Shadow 4 al_draw_line(x,y-1,x,y-5,al_map_rgb(r,g,b),3);//Actual 5};

I'd like to note that my "engine" class has only static members. Static screen and static functions. However the thing that draws to it- the game class -is not static.

#SelectExpand
1void AllegroEngine::InitializeAllegro() 2{ 3 if (!al_init()) 4 { 5 std::cout<<"Al init failed."<<endl; 6 } 7 //allegro-5.0.10-monolith-md-debug.lib 8 9 //Anti Aliasing 10 al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST); 11 al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST); 12 13 //Initializing Addons 14 al_init_image_addon(); 15 al_init_font_addon(); 16 al_init_ttf_addon(); 17 al_install_keyboard(); 18 al_install_audio(); 19 al_init_acodec_addon(); 20 al_init_primitives_addon(); 21 22 al_reserve_samples(10); 23 24 std::cout<<endl<<"Allegro initialized."; 25 26 InitializeFonts(); 27}; 28 29void AllegroEngine::InitializeScreen(int myScreenWidth, int myScreenHeight) 30{ 31 screen_width = myScreenWidth; 32 screen_height = myScreenHeight; 33 34 //Creating screen 35 ALLEGRO_DISPLAY* display = al_create_display(myScreenWidth, myScreenHeight); 36 al_set_window_position(display,0,0); 37 al_set_window_title(display,"World Simulation"); 38 main_display = display; 39};

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

I can load and save a png just fine here. Even off the backbuffer.

Try this test code with the attached test.png and see what results it gives you on your computer.

#SelectExpand
1 2 3 4#include "allegro5/allegro.h" 5#include "allegro5/allegro_image.h" 6 7 8int main(int argc , char** argv) { 9 10 if (!al_init()) {return 1;} 11 al_init_image_addon(); 12 13 al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_OPENGL); 14 ALLEGRO_DISPLAY* display = al_create_display(200,200); 15 16 ALLEGRO_BITMAP* testbmp = al_load_bitmap("test.png"); 17 18 al_save_bitmap("testsave.png" , testbmp); 19 20 al_set_target_backbuffer(display); 21 al_clear_to_color(al_map_rgb(0,255,0)); 22 al_draw_bitmap(testbmp , 0 , 0 , 0); 23 24 al_save_bitmap("testbackbuffersave.png" , al_get_backbuffer(display)); 25 26 al_flip_display(); 27 28 al_rest(3.0); 29 30 return 0; 31}

It works for me on Windows Vista with MinGW 4.8.1 using either DirectX or OpenGL driver.

TheNextGuy
Member #15,702
August 2014

Edgar my man it works. I fiddled with it so much I don't quite know what the exact solution is.

Thanks a gorillian.

I've attached a working screenshot x)

Neil Roy
Member #2,229
April 2002
avatar

With that code, just make sure to call it before an al_flip_display if you're trying to save from the backbuffer. If not, then it doesn't really matter.

I used it all through my game. I added calls to it from my title screen, my menu screen, my main game screen etc... never had a problem.

But I will keep what you said in mind.

---
“I love you too.” - last words of Wanda Roy

Thomas Fjellstrom
Member #476
June 2000
avatar

NiteHackr said:

I used it all through my game. I added calls to it from my title screen, my menu screen, my main game screen etc... never had a problem.

The main issue is that there is no guarantee that the backbuffer after a flip has anything in it that makes sense. It could be what you drew to it the frame before last, 5 frames ago, or garbage. (this is why they say it's undefined or implementation defined).

Often I think the "implementation" is just to keep N frames around for page flipping, and hand you back a previous frame on a flip. so for the most part it looks fine, but may not give you back the exact frame you expected if you called it after a flip, but before drawing. (not that it often matters, but it might if you're trying to use the screenshots for debugging)

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Neil Roy
Member #2,229
April 2002
avatar

I may go over my code and make sure my screenshot code is after a draw, just to be safe. Perhaps detect the screenshot key, flag the next draw for a save, then add code in after the draw, but before the flip to respond on that. Hmmm...

---
“I love you too.” - last words of Wanda Roy

Go to: