Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Getting artefact when loading image

This thread is locked; no one can reply to it. rss feed Print
Getting artefact when loading image
Francis Langlais
Member #15,985
June 2015

I'm trying to load an image for my game. Each robot is a structure and I store the image in it. What happen when the image is drawn is sometimes it draw some artefact over the image and sometimes it doesn't and it's completely random. In the first image attached, only the blue robot doesn't have artefact and in the second one, the pink one doesn't have it either. Those 2 frame are from different run of the program and the 4 robots are the same.

I was just wondering if storing an ALLEGRO_BITMAP into a structure is a good idea or this could be the source of my problem?

PS: I don't know how to link image directly in the post so I attached it.

RPG Hacker
Member #12,492
January 2011
avatar

It's hard to help you without having the code that produces this output.

So if I had to guess from the screenshots, I'd say it looks like you're trying to draw bitmaps that weren't properly initialized. These pink-ish garbage sprites above the robots look like you were trying to draw bitmaps with random data in them.

Or, actually, looking at the garbage sprites... it also looks like it could be actual sprites that for some reason have the wrong dimensions, so that the pixels are at the wrong locations.

In any case, as I said, without any code it's really hard to help here.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

You could be drawing destroyed copies of bitmaps depending on your code. What do you do with your stored ALLEGRO_BITMAP*? Remember it's just a pointer but if you're destroying it in your destructor and you make copies of your struct then the original will be destroyed and you will be left with a dangling pointer.

Francis Langlais
Member #15,985
June 2015

Here's the code (it's scattered around my program) :

How I create the Robot:

void BENDERBOT(ROBOTCOLORS color) {
  RegisterRobot("Bender", color, &BenderActions, &PimpOutBender,
      "resources\\images\\basic_robot.bmp", -1, -1, -1);
}

#SelectExpand
1void RegisterRobot(char *robotName, ROBOTCOLORS color, 2 void (*robotActions)(int), void (*configureRobot)(void), 3 char *customImage, int x, int y, float heading) { 4 int i, robotNumber; 5 ROBOT newRobot; 6 7 if (theGame.state != GS_SETUP) 8 AbortOnError( 9 "Call made to RegisterRobot() when game is not in setup state." 10 "\nProgram will end."); 11 12 robotNumber = SizeLL(robotList); 13 if (robotNumber == MAX_ROBOTS) 14 AbortOnError("RegisterRobot() Attempted to add too many robots.\n" 15 "Program will end."); 16 17 newRobot.ActionsFunction = robotActions; 18 19 switch (color) { 20 case ROBOT_RED: 21 newRobot.color = al_map_rgb(255, 0, 0); 22 break; 23 case ROBOT_GREEN: 24 newRobot.color = al_map_rgb(0, 255, 0); 25 break; 26 case ROBOT_BLUE: 27 newRobot.color = al_map_rgb(0, 0, 255); 28 break; 29 case ROBOT_YELLOW: 30 newRobot.color = al_map_rgb(255, 255, 0); 31 break; 32 case ROBOT_PURPLE: 33 newRobot.color = al_map_rgb(255, 0, 240); 34 break; 35 case ROBOT_TURQUOISE: 36 newRobot.color = al_map_rgb(0, 255, 255); 37 break; 38 case ROBOT_WHITE: 39 default: 40 newRobot.color = al_map_rgb(255, 255, 255); 41 } 42 if (HasNullCharacter(robotName, MAX_NAME_LEN)) { 43 newRobot.name = malloc(sizeof(char) * strlen(robotName)); 44 strcpy(newRobot.name, robotName); 45 newRobot.number = robotNumber; 46 } else 47 AbortOnError("RegisterRobot() was passed a robot name exceeding " 48 "MAX_NAME_LEN.\nProgram will end."); 49 50 //Set starting speed values 51 newRobot.leftTreadSpeed = 0; 52 newRobot.rightTreadSpeed = 0; 53 newRobot.impulseHeading = 0; 54 newRobot.impulseSpeed = 0; 55 newRobot.turboTime = 0; 56 57 //Configure the sensors. 58 newRobot.bumped = BUMP_NONE; 59 for (i = 0; i < MAX_SENSORS; i++) { 60 newRobot.sensorArray[i].type = SENSOR_NONE; 61 newRobot.sensorArray[i].image = NULL; 62 } 63 64 //Configure the laser. 65 newRobot.weaponArray[LASER_PORT].type = WEAPON_LASER; 66 newRobot.weaponArray[LASER_PORT].maxAngle = LASER_MAX_ANGLE; 67 newRobot.weaponArray[LASER_PORT].minEnergy = MIN_LASER_ENERGY; 68 newRobot.weaponArray[LASER_PORT].maxEnergy = MAX_LASER_ENERGY; 69 newRobot.weaponArray[LASER_PORT].bonusEnergy = LASER_ENERGY_BONUS; 70 newRobot.weaponArray[LASER_PORT].splashRange = LASER_SPLASH_RANGE; 71 newRobot.weaponArray[LASER_PORT].splashDamage = LASER_SPLASH_DAMAGE; 72 newRobot.weaponArray[LASER_PORT].speed = LASER_SPEED; 73 newRobot.weaponArray[LASER_PORT].bumpValue = BUMP_LASER; 74 newRobot.weaponArray[LASER_PORT].firingSound = SND_LASER_FIRE; 75 newRobot.weaponArray[LASER_PORT].impactSound = SND_LASER_HIT; 76 77 //Configure the missiles. 78 newRobot.weaponArray[MISSILE_PORT].type = WEAPON_MISSILE; 79 newRobot.weaponArray[MISSILE_PORT].maxAngle = MISSILE_MAX_ANGLE; 80 newRobot.weaponArray[MISSILE_PORT].minEnergy = MIN_MISSILE_ENERGY; 81 newRobot.weaponArray[MISSILE_PORT].maxEnergy = MAX_MISSILE_ENERGY; 82 newRobot.weaponArray[MISSILE_PORT].bonusEnergy = MISSILE_ENERGY_BONUS; 83 newRobot.weaponArray[MISSILE_PORT].splashRange = MISSILE_SPLASH_RANGE; 84 newRobot.weaponArray[MISSILE_PORT].splashDamage = MISSILE_SPLASH_DAMAGE; 85 newRobot.weaponArray[MISSILE_PORT].speed = MISSILE_SPEED; 86 newRobot.weaponArray[MISSILE_PORT].bumpValue = BUMP_MISSILE; 87 newRobot.weaponArray[MISSILE_PORT].firingSound = SND_MISSILE_FIRE; 88 newRobot.weaponArray[MISSILE_PORT].impactSound = SND_MISSILE_HIT; 89 90 for (i = 0; i < MAX_WEAPONS; i++) { 91 newRobot.weaponArray[i].chargeRate = 0; 92 newRobot.weaponArray[i].chargeEnergy = 0; 93 } 94 95 //Set starting energy priorites in same order as SYSTEMS declaration. 96 for (i = 0; i < NUM_ENERGY_SYSTEMS; i++) 97 newRobot.energyPriorities[i] = (SYSTEM) i; 98 99 //Set initial energy values. Weapons were done in the weapons section. 100 newRobot.shields = START_SHIELD_ENERGY; 101 newRobot.shieldChargeRate = 0; 102 103 //Set structure-related values 104 newRobot.generatorStructure = MAX_GENERATOR_STRUCTURE; 105 106 //Robot has no damage to be applied at start. 107 newRobot.damageBank = 0; 108 109 //Load the robot's graphic, if required. There is no error checking here as 110 //if newRobot.graphic is NULL, the graphics routines will simply use the 111 //default robot graphic to render the robot. 112 newRobot.graphic = NULL; 113 if (customImage != NULL){ 114 newRobot.graphic = al_load_bitmap(customImage); 115 al_convert_mask_to_alpha(newRobot.graphic, al_map_rgb(255, 0, 255)); 116 } 117 118 //Create the robot's bitmap. 119 newRobot.shield = al_create_bitmap(SHIELD_BMP_SZ, SHIELD_BMP_SZ); 120 newRobot.image = al_create_bitmap(SHIELD_BMP_SZ, SHIELD_BMP_SZ); 121 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP); 122 if (newRobot.image == NULL || newRobot.shield == NULL) 123 AbortOnError("RegisterRobot() failed to create robot image.\n" 124 "Program will end."); 125 126 strcpy(newRobot.statusMessage, ""); //No status message at start. 127 128 newRobot.mailBox = ConsLL(); //Create the robot's mailbox. 129 130 InsLastLL(robotList, newRobot); //Put robot on end of the list. 131 curRobot = LastElmLL(robotList); //Record current robot for configuration. 132 133 //Set robot starting location. This is done only after insertion into the 134 //list because ChooseRandomLocation() requires the list to have robots in it. 135 if (x < 0 || y < 0) 136 ChooseRandomLocation(curRobot); 137 else { 138 curRobot->x = x; 139 curRobot->y = y; 140 } 141 if (heading < 0) 142 curRobot->heading = GetRandomNumber(360); 143 else 144 curRobot->heading = heading; 145 146 configureRobot(); //Call user's configuration function. 147}

The image is stored in newRobot.graphic at the 112-115th line. Also note that if, when I call the RegistewrRobot and customImage = NULL the program gives it the default image and it work fine but if I give it the path to the default image it gives me artefact.

Then this is when I draw the robot :

#SelectExpand
1void DrawRobotBitmaps(t_LL listOfRobots) { 2 float shieldCenterX, shieldCenterY; 3 float drawAngle, modifier; 4 ROBOT *robot; 5 ALLEGRO_BITMAP *robotGraphic; 6 ALLEGRO_BITMAP *temp; 7 8 ForeachLL_M(listOfRobots, robot) 9 { 10 temp = al_create_bitmap(SHIELD_BMP_SZ, SHIELD_BMP_SZ); 11 12 clearBitmap(temp); 13 clearBitmap(robot->image); 14 15 shieldCenterX = al_get_bitmap_width(temp) / 2; //All robot's shields are same size, 16 shieldCenterY = al_get_bitmap_height(temp) / 2; //so calculate center point here. 17 18 drawAngle = 90 - robot->heading; //Convert "math" angle to screen "heading" 19 if (drawAngle < 0) //angle as rotate_sprite is in compass 20 drawAngle = drawAngle + 360; //coords and make positive. 21 drawAngle = drawAngle * PI / 180; //Convert from degrees to radian. 22 23 modifier = robot->shields / MAX_SHIELD_ENERGY; 24 25 if (robot->graphic != NULL){ 26 robotGraphic = robot->graphic; 27 } else { 28 robotGraphic = robotImg; 29 } 30 31 al_set_target_bitmap(temp); 32 al_draw_bitmap(robotGraphic, 33 al_get_bitmap_width(robot->image) / 2 34 - al_get_bitmap_width(robotGraphic) / 2, 35 al_get_bitmap_height(robot->image) / 2 36 - al_get_bitmap_height(robotGraphic) / 2, 0); 37 al_set_target_bitmap(robot->image); 38 al_draw_rotated_bitmap(temp, shieldCenterX, shieldCenterY, 39 SHIELD_BMP_SZ / 2, SHIELD_BMP_SZ / 2, drawAngle, 0); 40 al_draw_tinted_bitmap(robot->shield, 41 al_map_rgba_f(1 * modifier, 1 * modifier, 1 * modifier, 42 modifier), 0, 0, 0); 43 } 44 45 al_destroy_bitmap(temp); 46}

And this is when I destroy the bitmap :

#SelectExpand
1void EndCompetition() { 2 int i; 3 ROBOT *tempRobot, *nextRobot; 4 WEAPON *tempWeapon, *nextWeapon; 5 6 //Move the dead robots back into the main list for deletion. 7 //deadRobotList should be empty after this loop has finished. 8 while (!IsEmptyLL(deadRobotList)) 9 LinkAftLL(LastElmLL(robotList), UnlinkLL(FirstElmLL(deadRobotList))); 10 11 //Now iterate through each robot, cleaning up allocated memory 12 //and, when all memory is cleaned up, remove each one from the list. 13 SafeForeachLL_M(robotList, tempRobot, nextRobot) 14 //Has to be "safe" as 15 { //list elements will be 16 nextRobot = NextElmLL(tempRobot); //deleted during the 17 for (i = 0; i < MAX_SENSORS; i++) //iteration. 18 if (tempRobot->sensorArray[i].image != NULL) 19 al_destroy_bitmap(tempRobot->sensorArray[i].image); //Free sensor images. 20 21 if (tempRobot->graphic != NULL) 22 al_destroy_bitmap(tempRobot->graphic); //Free custom images. 23 al_destroy_bitmap(tempRobot->image); //Free robot bitmaps. 24 free(tempRobot->name); //Free robot name strings. 25 26 DestLL(tempRobot->mailBox); //Remove all messages. 27 28 DelElmLL(tempRobot); //Delete the robot from the list. 29 } 30 31 //Now clean up the weapon list, deleting each weapon. 32 SafeForeachLL_M(weaponList, tempWeapon, nextWeapon) 33 { 34 nextWeapon = NextElmLL(tempWeapon); 35 if (tempWeapon->image != NULL) 36 al_destroy_bitmap(tempWeapon->image); //Free weapon images. 37 DelElmLL(tempWeapon); //Delete the weapon. 38 } 39 40 if (IsEmptyLL(robotList) && IsEmptyLL(deadRobotList) 41 && IsEmptyLL(weaponList)) { 42 DestLL(robotList); 43 DestLL(deadRobotList); 44 DestLL(weaponList); 45 } else { 46 DestLL(robotList); 47 DestLL(deadRobotList); 48 DestLL(weaponList); 49 AbortOnError( 50 "EndCompetition() discovered one of the main linked lists was " 51 "not empty before deletion.\n" 52 "Program will now end."); 53 } 54 55 //Delete the sounds. 56 for (i = 0; i < NUM_SOUNDS; i++) 57 if (theGame.sounds[i] != NULL) 58 al_destroy_sample(theGame.sounds[i]); 59 60 DeInitGraphics(); 61 62}

The bitmap is destroyed at line 21-22th line.

Those are the only action I take on the robot.graphic.

Finally here is the action order of my game loop :

#SelectExpand
1void Fight(ALLEGRO_DISPLAY *display) { 2 3 calcsUntilOrders++; 4 5 UpdateEnergySystems(robotList); //Do first so we know what 6 //systems are powered. 7 8 MoveRobots(robotList); 9 DrawRobotBitmaps(robotList); 10 CheckRobotCollisions(&theGame, robotList); 11 12 MoveWeapons(weaponList); 13 CheckWeaponCollisions(&theGame, robotList, weaponList); 14 15 //Now that collisions are 16 ApplyDamage(&theGame, robotList, deadRobotList); //done and weapons have 17 //hit, we apply damage. 18 19 DrawSensorBitmaps(robotList); //Need to draw BEFORE data is 20 //updated because bimaps are 21 //used in collision detection. 22 UpdateSensorData(robotList); 23 24 UpdateParticles(); 25 26 calcsCompleted++; 27 28 if (calcsUntilOrders == ORDER_FREQ) { 29 calcsUntilOrders = 0; 30 ForeachLL_M(robotList, curRobot) 31 curRobot->ActionsFunction(TURN_TIME); 32 } 33 34// if (theGame.useSounds) 35// PlaySounds(&theGame); 36 37}

@Edgar Reynaldo
I don't know because all the robot are managed the same way and the artefact are random. They don't appear on random frame they just show up randomly on a robot graphic . Plus I don't destroy the robot.graphic until the end of the game.

The fact that if I load the bitmap using a path in the RegisterRobot or using NULL to load the same bitmap doesn't give me the same result feel strange to me.

RPG Hacker
Member #12,492
January 2011
avatar

#SelectExpand
119newRobot.shield = al_create_bitmap(SHIELD_BMP_SZ, SHIELD_BMP_SZ); 120newRobot.image = al_create_bitmap(SHIELD_BMP_SZ, SHIELD_BMP_SZ); 121al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);

I see this is where you create "shield", but where do you fill its contents? If you just create a bitmap without filling it, it will only contain garbage.
Also is there a special reason to set the bitmap flags to "memory bitmaps" after creating these bitmaps? First of all, memory bitmaps should be avoided if possible, because they're slow. Secondly, if you were intending "shield" or "image" to be memory bitmaps, you should have called al_set_new_bitmap_flags before creating the bitmaps.

temp = al_create_bitmap(SHIELD_BMP_SZ, SHIELD_BMP_SZ);

Is there a reason for creating and destroying the bitmap in every draw call? Creating bitmaps can be slow and shouldn't be done every frame unless absolutely necessary. Since you're using constant sizes here, recreating the bitmap every frame isn't necessary. Just create the image onces with the robot and destroy it once the robot is destroyed.

Francis Langlais
Member #15,985
June 2015

Yeah I found out that the shield was doing the artefact and I will simply remove the pre-rendered shield. I did that because I had a FPS problem which had nothing to do with it.

The memory thing was because I wanted to draw my particles (which use al_put_pixel) on it but Edgar told me to lock/unlock my bitmap instead.

Yes I will fix this creating/destroying bitmap thing.

Thank you for your help!

Go to: