Take snapshots of current screen

What is this post about

This post rises to explain how to take a snapshot of what LittlevGL is printing in the whole screen. In other words, press a pseudo Ptr Scr key.

I share my way of doing it (with holder functions/variables). I am sure that this way will not fit anyone code at a first glance, but I hope that you catch the drift, so you are able to adapt it for your needs.

Why would I want to take a screen snapshot

This is basically for two main reasons:

How to obtain a binary of the whole VDB

The first thing we want to achieve is to flush to a binary file the screen pixel values:

static FILE* pFileOut;
static BOOL_E bSnapshot;
/** Flush buffer.
 *
 * Flush buffer into a binary file.
 *
 * @note: data pixel should be formated to uint32_t RGBA. Imagemagick requirements.
 *
 * @param[in] pFileName   Output binary file name.
 *
 */
void DISP_IMPL_flushBuffer (const char_t* pFileName)
{
  pFileOut = fopen(pFileName, "w");

  if (pFileOut == NULL) {
    printf(("[Display] error: %s cannot be opened", pFileName));
    return;
  }

  bSnapshot = BOOL_TRUE;
  lv_obj_invalidate(lv_scr_act());
  lv_refr_now();                    /* Will call our disp_drv.disp_flush function */
  bSnapshot = BOOL_FALSE;

  fclose(pFileOut);
  printf(("[Display] data flushed to %s", pFileName));
}
void APP_DISP_lvgl_flush (const lv_color_t* pData, const DISPLAY_BOUNDS_T* const pPxBounds)
{
  uint32_t data;
  if (bSnapshot == BOOL_TRUE) {
    for (uint32_t i = 0; i < (pPxBounds->dimension.width * pPxBounds->dimension.height); i++) {
      data = DISP_IMPL_lvgl_formatPixel(pData[i]);  /* Function for converting LittlevGL pixel format to RGB888 */
      fwrite(&data,
             sizeof(uint32_t),
             1,
             pFileOut);
    }
  }
}
/** Convert lvgl RGB565 pixel to RGB888 pixel
 *
 * @note: convert potential gray colors to true gray color.
 *
 */
static uint32_t DISP_IMPL_lvgl_formatPixel (lv_color_t color){

  lv_color32_t data = (lv_color32_t) lv_color_to32(color);
  uint8_t aux;

  /* Make potential gray color true gray color */
  if (color.red == color.blue) {
    uint32_t dif = data.green - data.red;
    if (dif <= 8) {
      data.red += dif;
      data.blue += dif;
    }
  }

  /* BGR to RGB */
  aux = data.blue;
  data.blue = data.red;
  data.red = aux;

  return data.full;
}

How to convert binary files to images

Once our screen content has been flushed into a binary file, we lean on Imagemagick to convert every binary file within a directory to an image. This can be basically achieved using next bash commands (you should adapt it to your own requirements):

#!/bin/bash
# convert_bin_images.bash
  OUT_BIN_LIST="${IMAGES_DIR}/*.bin"
  for BIN_FILE in $OUT_BIN_LIST
  do
    convert -size 320X240 -depth 8 rgba:${BIN_FILE} ${BIN_FILE}.bmp
  done

How to compare and differentiate files (automatic tests)

One of the purposes of being able to take snapshots, is to pass automatic test by comparing current output with a reference. In order to achieve it automatically I share the bash basic commands (you should adapt it to your own requirements). Note that if binary files differ a new diff_ image is created in order to show where the difference is:

#!/bin/bash
#run_test.bash

for i in ${BIN_LIST[@]}
do
  diff -q ${IMAGES_DIR_OUT}/$i ${IMAGES_DIR_REF}/$i
  if [ $? != 0 ]; then
    $SCRIPT_DIR/convert_bin_images.bash ${IMAGES_DIR_REF}/$i
    compare ${IMAGES_DIR_OUT}/$i.bmp ${IMAGES_DIR_REF}/$i.bmp -compose src ${IMAGES_DIR_OUT}/diff_$i.png
    TEST_SUITE_OK=0
  fi
done

if [ $TEST_SUITE_OK -eq 0 ]; then
  exit 1
fi

Example snapshots and differences

Output snapshot

Output snapshot

Reference snapshot

Reference snapshot

Difference between above images

Difference between above images