[Solved] how to create a graphics object using an Atom text editor


There are lot of graphics formats available with varying capabilities.

First distinction I would make is:

raster graphics vs. vector graphics

Raster graphics (storing the image pixel by pixel) are more often binary encoded as the amount of data is usally directly proportional to size of image. However some of them are textual encoded or can be textual as well as binary encoded.

Examples are:

Although these file formats are a little bit exotic, it is not difficult to find software which supports them. E.g. GIMP supports both out of the box (and even on Windows). Btw. they are that simple that it is not too complicated to write loader and writer by yourself.

A simple reader and writer for PPM (the color version of Portable anymap) can be found in my answer to SO: Convolution for Edge Detection in C.

Vector graphics (store graphics primitives which build the image) are more often textual encoded. As vector graphics can be “loss-less” scaled to any image size by simply applying a scaling factor to all coordinates, file size and destination image size are not directly related. Thus, vector graphics are the preferrable format for drawings especially if they are needed in multiple target resolutions.

For this, I would exclusively recommend:

which is (hopefully) the upcoming standard for scalable graphics in Web contents. Qt does provide (limited) support for SVG and thus, it is my preferred option for resolution independent icons.


A different (but maybe related) option is to embed graphics in source code. This can be done with rather any format if your image loader library provides image loading from memory (as well as from file). (All I know does this.)
Thus, the problem can be reduced to: How to embed a large chunk of (ASCII or binary) data as constant in C/C++ source code? which is IMHO trivial to solve.

I did this in my answer for SO: Paint a rect on qglwidget at specifit times.


Update:

As I noticed that the linked sample for PPM (as well as another for PBM) read actually the binary format, I implemented a sample application which demonstrates usage of ASCII PPM.

I believe that XPM is better suitable for the specific requirement to be editable in a text editor. Thus, I considered this in my sample also.

As the question doesn’t mention what specific internal image format is desired nor in what API it shall be usable, I choosed Qt which

  • is something I’m familiar with
  • provides a QImage which is used as destination for image import
  • needs only a few lines of code for visual output of result.

Source code test-QShowPPM-XPM.cc:

// standard C++ header:
#include <cassert>
#include <iostream>
#include <string>
#include <sstream>

// Qt header:
#include <QtWidgets>

// sample image in ASCII PPM format
// (taken from https://en.wikipedia.org/wiki/Netpbm_format)
const char ppmData[] =
"P3\n"
"3 2\n"
"255\n"
"255   0   0     0 255   0     0   0 255\n"
"255 255   0   255 255 255     0   0   0\n";

// sample image in XPM3 format
/* XPM */
const char *xpmData[] = {
  // w, h, nC, cPP
  "16 16 5 1",
  // colors
  "  c #ffffff",
  "# c #000000",
  "g c #ffff00",
  "r c #ff0000",
  "b c #0000ff",
  // pixels
  "       ##       ",
  "    ###gg###    ",
  "   #gggggggg#   ",
  "  #gggggggggg#  ",
  " #ggbbggggbbgg# ",
  " #ggbbggggbbgg# ",
  " #gggggggggggg# ",
  "#gggggggggggggg#",
  "#ggrrggggggrrgg#",
  " #ggrrrrrrrrgg# ",
  " #ggggrrrrgggg# ",
  " #gggggggggggg# ",
  "  #gggggggggg#  ",
  "   #gggggggg#   ",
  "    ###gg###    ",
  "       ##       "
};

// Simplified PPM ASCII Reader (no support of comments)

inline int clamp(int value, int min, int max)
{
  return value < min ? min : value > max ? max : value;
}

inline int scale(int value, int maxOld, int maxNew)
{
  return value * maxNew / maxOld;
}

QImage readPPM(std::istream &in)
{
  std::string header;
  std::getline(in, header);
  if (header != "P3") throw "ERROR! Not a PPM ASCII file.";
  int w = 0, h = 0, max = 255; // width, height, bits per component
  if (!(in >> w >> h >> max)) throw "ERROR! Premature end of file.";
  if (max <= 0 || max > 255) throw "ERROR! Invalid format.";
  QImage qImg(w, h, QImage::Format_RGB32);
  for (int y = 0; y < h; ++y) {
    for (int x = 0; x < w; ++x) {
      int r, g, b;
      if (!(in >> r >> g >> b)) throw "ERROR! Premature end of file.";
      qImg.setPixel(x, y,
          scale(clamp(r, 0, 255), max, 255) << 16
        | scale(clamp(g, 0, 255), max, 255) << 8
        | scale(clamp(b, 0, 255), max, 255));
    }
  }
  return qImg;
}

// Simplified XPM Reader (implements sub-set of XPM3)

char getChar(const char *&p)
{
  if (!*p) throw "ERROR! Premature end of file.";
  return *p++;
}

std::string getString(const char *&p)
{
  std::string str;
  while (*p && !isspace(*p)) str += *p++;
  return str;
}

void skipWS(const char *&p)
{
  while (*p && isspace(*p)) ++p;
}

QImage readXPM(const char **xpmData)
{
  int w = 0, h = 0; // width, height
  int nC = 0, cPP = 1; // number of colors, chars per pixel
  { std::istringstream in(*xpmData);
    if (!(in >> w >> h >> nC >> cPP)) throw "ERROR! Premature end of file.";
    ++xpmData;
  }
  std::map<std::string, std::string> colTbl;
  for (int i = nC; i--; ++xpmData) {
    const char *p = *xpmData;
    std::string chr;
    for (int j = cPP; j--;) chr += getChar(p);
    skipWS(p);
    if (getChar(p) != 'c') throw "ERROR! Format not supported.";
    skipWS(p);
    colTbl[chr] = getString(p);
  }
  QImage qImg(w, h, QImage::Format_RGB32);
  for (int y = 0; y < h; ++y, ++xpmData) {
    const char *p = *xpmData;
    for (int x = 0; x < w; ++x) {
      std::string pixel;
      for (int j = cPP; j--;) pixel += getChar(p);
      qImg.setPixelColor(x, y, QColor(colTbl[pixel].c_str()));
    }
  }
  return qImg;
}

// a customized QLabel to handle scaling
class LabelImage: public QLabel {

  private:
    QPixmap _qPixmap, _qPixmapScaled;

  public:
    LabelImage();
    LabelImage(const QPixmap &qPixmap): LabelImage()
    {
      setPixmap(qPixmap);
    }
    LabelImage(const QImage &qImg): LabelImage(QPixmap::fromImage(qImg))
    { }

    void setPixmap(const QPixmap &qPixmap) { setPixmap(qPixmap, size()); }

  protected:
    virtual void resizeEvent(QResizeEvent *pQEvent);

  private:
    void setPixmap(const QPixmap &qPixmap, const QSize &size);
};

// main function
int main(int argc, char **argv)
{
  qDebug() << QT_VERSION_STR;
  // main application
#undef qApp // undef macro qApp out of the way
  QApplication qApp(argc, argv);
  // setup GUI
  QMainWindow qWin;
  QGroupBox qBox;
  QGridLayout qGrid;
  LabelImage qLblImgPPM(readPPM(std::istringstream(ppmData)));
  qGrid.addWidget(&qLblImgPPM, 0, 0, Qt::AlignCenter);
  LabelImage qLblImgXPM(readXPM(xpmData));
  qGrid.addWidget(&qLblImgXPM, 1, 0, Qt::AlignCenter);
  qBox.setLayout(&qGrid);
  qWin.setCentralWidget(&qBox);
  qWin.show();
  // run application
  return qApp.exec();
}

// implementation of LabelImage

LabelImage::LabelImage(): QLabel()
{
  setFrameStyle(Raised | Box);
  setAlignment(Qt::AlignCenter);
  //setMinimumSize(QSize(1, 1)); // seems to be not necessary
  setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
}

void LabelImage::resizeEvent(QResizeEvent *pQEvent)
{
  QLabel::resizeEvent(pQEvent);
  setPixmap(_qPixmap, pQEvent->size());
}

void LabelImage::setPixmap(const QPixmap &qPixmap, const QSize &size)
{
  _qPixmap = qPixmap;
  _qPixmapScaled = _qPixmap.scaled(size, Qt::KeepAspectRatio);
  QLabel::setPixmap(_qPixmapScaled);
}

This has been compiled in VS2013 and tested in Windows 10 (64 bit):

Snapshot of testQShowPPM-XPM

2

solved how to create a graphics object using an Atom text editor