stefanhaustein / TerminalImageViewer

Small C++ program to display images in a (modern) terminal using RGB ANSI codes and unicode block graphics characters
Other
1.56k stars 111 forks source link

How i eliminated redundant colors #80

Closed clort81 closed 2 years ago

clort81 commented 4 years ago

Both rendering time and filesize can be reduced by not emitting colors for a character when they haven't changed from the previous character. Also space characters don't need a foreground color set.

void emit_image(const cimg_library::CImg<unsigned char> & image, int flags) {
    int oldbgr = -1, oldbgg = -1, oldbgb = -1;
    int oldfgr = -1, oldfgg = -1, oldfgb = -1;

    for (int y = 0; y <= image.height() - 8; y += 8) {
      for (int x = 0; x <= image.width() - 4; x += 4) {
        CharData charData = flags & FLAG_NOOPT
          ? getCharData(image, x, y, 0x2584, 0x0000ffff)
          : getCharData(image, x, y);

        // only emit bg color if it has changed
        if ( charData.bgColor[0] != oldbgr || charData.bgColor[1] != oldbgg || charData.bgColor[2] != oldbgb ) {
          emit_color(flags | FLAG_BG, charData.bgColor[0], charData.bgColor[1], charData.bgColor[2]);
        }

        // only emit fg color if it has changed and not a space (space has no fg color)
        if ( (charData.fgColor[0] != oldfgr || charData.fgColor[1] != oldfgg || charData.fgColor[2] != oldfgb) && (charData.codePoint !=  0x00a0)) {
          emit_color(flags | FLAG_FG, charData.fgColor[0], charData.fgColor[1], charData.fgColor[2]);
        }
        emitCodepoint(charData.codePoint);
        oldbgr = charData.bgColor[0];  oldbgg = charData.bgColor[1]; oldbgb = charData.bgColor[2];

        // don't change old foreground color if codePoint is a space (has no foreground color)
        if (charData.codePoint != 0x00a0) {
          oldfgr = charData.fgColor[0];  oldfgg = charData.fgColor[1]; oldfgb = charData.fgColor[2];
        }
      }
      std::cout << "\x1b[0m" << std::endl;

      // Clear out oldfg and oldbg so first character first char of next line gets colors set
      oldbgr = -1, oldbgg = -1, oldbgb = -1;
      oldfgr = -1, oldfgg = -1, oldfgb = -1;
    }
}

This saves me about 3-75% in filesize, depending on image:

57483 Jul  1 18:51 khadasgirl1.ans   // original tiv emit_image
25270 Jul  1 23:48 khadasgirl1b.ans // eliminated redundant color codes

This is just WorksForMe code, but a real programmer should understand what is intended.

aaronliu0130 commented 4 years ago

Currently on mobile and can't test this out. Understand the code but afraid of some feature issues/bugs. Can you please give me an example of an image, let's say, the github logo, ran through your edited script? Plus, if you'd want it, you can open it in a PR.

stefanhaustein commented 4 years ago

There are already checks whether fg/bg colors are different in emit_image:

      if (x == 0 || charData.bgColor != lastCharData.bgColor)
        emit_color(flags | FLAG_BG, charData.bgColor[0], charData.bgColor[1], charData.bgColor[2]);
      if (x == 0 || charData.fgColor != lastCharData.fgColor)
        emit_color(flags | FLAG_FG, charData.fgColor[0], charData.fgColor[1], charData.fgColor[2]);