Reverse bits in a b/w (.xbm) source code image?

The GIMP and bitmap program can be used to create simple .XBM files, b/w or 1 bit/pixel, 8 bit bytes.
Using e.g. GxEPD2 to display small b/w, 1 bit/pixel, .xbm 16x16 - 32x32 bitmaps on a GDEH0213B73 e-paper display calling drawImage() gave unexpected results, as I had forgotten that .xbm uses little endian and Arduino/Esp32 uses big endian.

Yes, there exist many utilities to convert images to source code suitable for big endian.
However, b/w .xbm being a simple ascii format, why not just reverse a hex coded byte like 0x01 to 0x80?
Maybe more inconvenient than having a dedicated drawImageXBM() function, but may save a few bytes when memory is tight.

This resulted in a small command line program with very little error checking to test the concept.

The program is written for use on Linux, but ought to compile on other platforms with little or no changes.
The source code contains a short usage description.

/*
 * reversebits.c
 * 
 * Convert an ascii file containing little endian to big endian hex bytes like 0x12, 0xA1.
 * Written to convert .xbm files containing byte values, no 16bit values, when compiled.
 * The conversion is done in place whenever a 0x prefix is found, assuming one hex value 0xXX.
 * Values like 0xX makes the result useless as the result always becomes 0xXX.
 * If the value converted becomes greater than 255, no conversion is done.
 * 
 * 
 * Also conversion of a header file or other source file is possible. 
 * Saves converting one image file at a time.
 * 
 * Using GIMP or bitmap results in little endian bit format.
 * And eg. ESP32 is big endian.
 * The ESP32 can do the reversing byte by byte, taking time and resources.
 * Better to modify the source before compiling.
 * 
 * The bit reversing is done using a one liner from:
 * http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
 * Look for: Reverse the bits in a byte with 7 operations (no 64-bit):
 * 
 * Simple compile (Linux)
 * cc xbmreversebits.c -o xbmreversebits
 * 
 * Usage example, reverse bits and copy file:
 *  cat ng16x16.xbm | ./xbmreversebits > nr16x16.xbm
 * 
 * No/little checking of the input is done!
 * 
 * Output file name must be different from input.
 * 
 * Assuming a file format like:
 * 
 * #define ng16x16_width 16
 * #define ng16x16_height 16                           
 * static unsigned char ng16x16_bits[] = {
 * 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xe0, 0x07,
 * 0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xf8, 0x1f, 0xfc, 0x3f, 0xfc, 0x3f,
 * 0x3e, 0x7c, 0x0e, 0x70, 0x06, 0x60, 0x00, 0x00 };
 * 
 * The result becomes:
 * 
 * #define ng16x16_width 16
 * #define ng16x16_height 16
 * static unsigned char ng16x16_bits[] = {
 * 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x07, 0xe0,
 * 0x0f, 0xf0, 0x0f, 0xf0, 0x1f, 0xf8, 0x1f, 0xf8, 0x3f, 0xfc, 0x3f, 0xfc,
 * 0x7c, 0x3e, 0x70, 0x0e, 0x60, 0x06, 0x00, 0x00 };
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char NL='\n';
int main(void)
{
  char line[512];
  int ch;
  size_t len = 0;
  size_t lineSize = 0;
  unsigned char baseBits, revBits, preserve;
  do {
    lineSize = 0;
    while(lineSize < sizeof(line)-1 ) {
      ch = getchar();
      line[lineSize++] = ch;
      if(ch == NL || ch == EOF) {
        line[lineSize++] = '\0';
        break;
      }
    }
    char *ps = line;
    //Check for ascii hex notation
    if((ps = strstr(line, "0x")) != NULL ) {
      //Assume comma separated values
      while( ps != NULL && (lineSize - (ps - line) > 4)) {
        //Convert hex string to a unsigned long value.
        unsigned long lx = strtol(ps, NULL, 16);
        //Ignore values exceding one byte
        if( lx < 256) {
          baseBits = (unsigned char) lx; 
          //Reverse the bit order in a byte
          revBits = ((baseBits * 0x0802LU & 0x22110LU) | (baseBits * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
          //Save the position where a NULL is placed after the snprintf() function
          preserve = *(ps + 4);
          snprintf(ps, 5, "0x%02x", revBits);
          *(ps + 4) = preserve;
        }
        //Find next hex notation or finish line
        if((ps = strstr(ps + 4, "0x")) == NULL )
          break;
      }
      printf("%s", line);
    } else {
      //No 0x value found, just copy to output
      printf("%s", line);
    } 
  } while(ch != EOF);
  return 0;
}

Even an Arduino Uno can do the conversion e.g. read from a SD card/memory and write to serial out.

Next is a simple Arduino sketch just to show the bit reversing of a source image coded as a hex string, the result appearing on the serial output.

/*
   BitmapRev.ino
   Simple test for reverse 0xXX in string.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static char line[] = 
"#define ng16x16_width 16\n\
#define ng16x16_height 16\n\
static const char ng16x16_bits[] = {\n\
0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xe0, 0x07,\n\
0xf0, 0x0f, 0xf0, 0x0f, 0xf8, 0x1f, 0xf8, 0x1f, 0xfc, 0x3f, 0xfc, 0x3f,\n\
0x3e, 0x7c, 0x0e, 0x70, 0x06, 0x60, 0x00, 0x00\n\
};\n";


void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println(line);
  Serial.flush();
  int lineSize = sizeof(line);
  unsigned char baseBits, revBits, preserve;
  char *ps = line;
  //Check for ascii hex notation
  if ((ps = strstr(line, "0x")) != NULL ) {
    //Assume comma separated values
    while ( ps != NULL && (lineSize - (ps - line) > 4)) {
      //Convert hex string to a unsigned long value.
      unsigned long lx = strtol(ps, NULL, 16);
      //Ignore values exceding one byte
      if ( lx < 256) {
        baseBits = (unsigned char) lx;
        //Reverse the bit order in a byte
        revBits = ((baseBits * 0x0802LU & 0x22110LU) | (baseBits * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
        //Save the position where a NULL is placed after the snprintf() function
        preserve = *(ps + 4);
        snprintf(ps, 5, "0x%02x", revBits);
        *(ps + 4) = preserve;
      }
      //Find next hex notation or finish line
      if ((ps = strstr(ps + 4, "0x")) == NULL )
        break;
    }
    Serial.println(line);
    Serial.flush();
  }
}

void loop() {
  delay(100);
}

Yes, you can write an Arduino sketch that writes a new array to the Serial Terminal.

Then copy-paste the Serial Terminal to a new Tab in your sketch.
e.g. convert an array from "xbm.h" and copy-paste to "bitmap.h"

Quite honestly, if GIMP only creates XBM format arrays, just use the appropriate XBM methods to display them.
e.g. Adafruit_GFX style libraries have drawXBitmap() for XBM in PROGMEM

  void drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w,
                   int16_t h, uint16_t color);

and U8g2lib can draw from both SRAM and PROGMEM

    void drawXBM(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap)
      { u8g2_DrawXBM(&u8g2, x, y, w, h, bitmap); }
    void drawXBMP(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap)
      { u8g2_DrawXBMP(&u8g2, x, y, w, h, bitmap); }

Personally, I find the bigendian format is more intuitive than the XBM format. But hey-ho, the PC tools do all the work so it does not really matter.

Incidentally, I have converted fonts, bitmaps, screen dumps, ... with the simple technique of Serial.print() to the Serial Terminal. Much easier than writing to SD card, reading SD card in PC, ...

David.