Determine if an image is solid
Nazerith

Hey guys. I'm working with a bitmap image from an old piece of software (2001-ish). The image is a tile sheet of tiles used in the game. I'm creating a map editor for the game, so I need to read and write the map files in the exact same format.

In the map files, tiles are numerically ordered starting from one, based on the order they appear in the sheet (tiles are 100x100 pixels). However, not all spots in the sheet contain images. In some cases, the spot is filled in with a single color (which the program uses as transparent on its tile). In cases where a tile is absent, that spot is skipped for the purposes of assigning numbers to the tiles.

So I need a way to do one of two things:

1) Test if a bitmap is a solid color.

or

2) Test if two bitmaps are equal.

I couldn't find a function for either in allegro, and I was curious if I missed it. Worse comes to worse, I'll write the function if it doesn't exist, but prefer not to do that if I can avoid it.

Or am I just missing a totally obvious solution? I kinda feel like I am.

Trent Gamblin

I'd be interested in a solution that doesn't have to compare every pixel (i.e., is faster than doing that). I don't know of one. Allegro does not have either function though.

Edgar Reynaldo
Nazerith said:

1) Test if a bitmap is a solid color.

2) Test if two bitmaps are equal.

You'll have to do both yourself. The first is easy, and the second is only slightly harder.

1)

int is_bitmap_solid_color(BITMAP* bmp , int color) {
   if (!bmp) {return 0;}
   for (int y = 0 ; y < bmp->h ; ++y) {
      for (int x = 0 ; x < bmp->w ; ++x) {
         if (color != getpixel(bmp , x , y)) {return 0;}
      }
   }
   return 1;
}

2)

int are_bitmaps_equal(BITMAP* b1 , BITMAP* b2) {
   if (!b1 || !b2) {return 0;}
   if (b1 == b2) {return 1;}
   if (bitmap_color_depth(b1) != bitmap_color_depth(b2)) {return 0;}
   if ((b1->w != b2->w) || (b1->h != b2->h)) {return 0;}
   for (int y = 0 ; y < b1->h ; ++y) {
      for (int x = 0 ; x < b1->w ; ++x) {
         if (getpixel(b1,x,y) != getpixel(b2,x,y)) {return 0;}
      }
   }
   return 1;
}

I just wrote them myself, since they only took a couple minutes.

Nazerith

Appreciate the effort Edgar. I knew how to write them, but was hoping for a more elegant solution than a per pixel comparison :-/. Guess we can't have everything in life can we?

[EDIT]

I just noticed you put getpixel. I assume you meant al_get_pixel.

Edgar Reynaldo

You didn't specify A4 or A5, so I just used A4.

Using A5 will be somewhat different :
1)

int is_bitmap_solid_color(ALLEGRO_BITMAP* bmp , ALLEGRO_COLOR color) {
   if (!bmp) {return 0;}
   al_lock_bitmap(bmp , al_get_bitmap_format(bmp) , ALLEGRO_LOCK_READONLY);
   for (int y = 0 ; y < al_get_bitmap_height(bmp) ; ++y) {
      for (int x = 0 ; x < al_get_bitmap_width(bmp) ; ++x) {
         if (color != al_get_pixel(bmp , x , y)) {
            al_unlock_bitmap(bmp);
            return 0;
         }
      }
   }
   al_unlock_bitmap(bmp);
   return 1;
}

2)

#SelectExpand
1int are_bitmaps_equal(ALLEGRO_BITMAP* b1 , ALLEGRO_BITMAP* b2) { 2 if (!b1 || !b2) {return 0;} 3 if (b1 == b2) {return 1;} 4 if (al_get_bitmap_format(b1) != al_get_bitmap_format(b2)) {return 0;} 5 if ((al_get_bitmap_width(b1) != al_get_bitmap_width(b2)) || 6 (al_get_bitmap_height(b1) != al_get_bitmap_height(b2))) {return 0;} 7 8 al_lock_bitmap(b1 , al_get_bitmap_format(b1) , ALLEGRO_LOCK_READONLY); 9 al_lock_bitmap(b2 , al_get_bitmap_format(b2) , ALLEGRO_LOCK_READONLY); 10 11 for (int y = 0 ; y < al_get_bitmap_height(b1) ; ++y) { 12 for (int x = 0 ; x < al_get_bitmap_width(b1) ; ++x) { 13 if (al_get_pixel(b1,x,y) != al_get_pixel(b2,x,y)) { 14 al_unlock_bitmap(b1); 15 al_unlock_bitmap(b2); 16 return 0; 17 } 18 } 19 } 20 al_unlock_bitmap(b1); 21 al_unlock_bitmap(b2); 22 return 1; 23}

A5 takes more code. :P

Nazerith

[EDIT]

Text removed because I am dumb.

Audric

With allegro 4 bitmaps, you could use memcmp() on each bmp->line[y] to speed up the comparison. It would also avoid the bit-shifting that occurs in al_get_pixel() to re-order the R G B components.

Tobias Dammers

I'd be interested in a solution that doesn't have to compare every pixel (i.e., is faster than doing that). I don't know of one. Allegro does not have either function though.

For checking whether two images are exactly identical, I can think of a few optimizations:
1. Use some heuristic to determine the areas where images are most likely to be different, and start comparison there (e.g., the center of an image is more likely to be distinctive than the edges)
2. Upon loading each bitmap, create a digest hash and keep it around. Only do the per-pixel comparison when the hash matches. If your hash is large enough and has sufficient entropy, you might even take the risk of a hash collision and skip the pixel test altogether.
3. Use a few quick checks to weed out obvious non-matches: differing sizes, color depths, file sizes, etc.

Audric

Looks like ML already posted some code for fast image comparison:
http://www.allegro.cc/forums/thread/606084/898790#target
In the linked post he compared only edges, ie. lines on top, bottom, left and right.
The critical code is:

  // Do once
  b1_lock = al_lock_bitmap(b1, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READONLY);
  b2_lock = al_lock_bitmap(b2, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READONLY);  
  b1_data = b1_lock->data;
  b2_data = b2_lock->data;
  // Do for each y line of bitmaps:
  if (memcmp(
      (uint8_t*)&b1_data[x1] + b1_lock->pitch * (y1+y), 
      (uint8_t*)&b2_data[x2] + b2_lock->pitch * (y2+y),
          width)) {...} // is different

(with x1,y1 the coordinates of the block in bitmap b1, and x2,y2 the coordinates in bitmap b2)

Edgar Reynaldo

You should use al_get_bitmap_format(bmp) as the 2nd parameter to pass to al_lock_bitmap though, otherwise format conversion would slow it down. And you should also use width*locked_region.pixel_size as the number of bytes to compare.

Thread #606488. Printed from Allegro.cc