向下缩放png字体

时间:2019-06-28 14:46:17

标签: c++ c opengl fonts scaling

是否有一种方法可以在启动时以最高质量缩小在opengl中为png图像的字体?我尝试过gluScaleImage,但是有很多人工制品。是否有使用lanczos的东西?我不想编写一个着色器或任何可以扩展运行时的东西。

1 个答案:

答案 0 :(得分:4)

这是基于我几十年前从德语c't Magazin复制而来的一种算法,至今仍不时将其用于类似OP描述的类似问题。

PARVW_ER

如果我做对了,这会实现bilinear interpolation


尽管我打算这样做,但我不敢称之为Minimal Complete Verifiable Example

完整的示例应用程序:

简化的PERNR_ER

SELECT PARVW INTO PARVW_ER FROM VBPA WHERE VBELN = VBPA~VBELN. AND PARVW = 'ER'. ENDSELECT. SELECT PERNR INTO PERNR_ER FROM VBPA WHERE VBELN = VBPA~VBELN. AND PARVW = 'ER'. ENDSELECT.

TYPES: begin of TY_TABLE,
    PARVW LIKE VBPA-PARVW,
    PERNR LIKE VBPA-PERNR,
    END OF TY_TABLE.

DATA: WA_TABLE TYPE TY_TABLE,
      IT_TABLE  TYPE TABLE OF TY_TABLE.

SELECT PARVW PERNR
    APPENDING CORRESPONDING FIELDS OF TABLE IT_TABLE
    FROM VBPA
    WHERE VBELN = VBPA~VBELN.

LOOP AT IT_TABLE INTO WA_TABLE.
  IF WA_TABLE-PARVW = 'ER'.
    PARVW_ER = WA_TABLE-PARVW.
    PERNR_ER = WA_TABLE-PERNR.
  ENDIF.
ENDLOOP.

bool scaleDown( const Image &imgSrc, Image &imgDst, int w, int h, int align) { const int wSrc = imgSrc.w(), hSrc = imgSrc.h(); assert(w > 0 && w <= wSrc && h > 0 && h <= hSrc); // compute scaling factors const double sx = (double)wSrc / (double)w; const double sy = (double)hSrc / (double)h; const double sxy = sx * sy; // prepare destination image imgDst.resize(w, h, (w * 3 + align - 1) / align * align); // cache some data const uint8 *const dataSrc = imgSrc.data(); const int bPRSrc = imgSrc.bPR(); // perform scaling for (int y = 0; y < h; ++y) { const double yStart = sy * y; const double yEnd = std::min(sy * (y + 1), (double)hSrc); const int yStartInt = (int)yStart; const int yEndInt = (int)yEnd - (yEndInt == yEnd); const double tFrm = 1 + yStartInt - yStart, bFrm = yEnd - yEndInt; for (int x = 0; x < w; ++x) { const double xStart = sx * x; const double xEnd = std::min(sx * (x + 1), (double)wSrc); const int xStartInt = (int)xStart; const int xEndInt = (int)xEnd - (xEndInt == xEnd); double lFrm = 1 + xStartInt - xStart, rFrm = xEnd - xEndInt; double pixel[3] = { 0.0, 0.0, 0.0 }; // values of target pixel for (int i = yStartInt; i <= yEndInt; ++i) { int jData = i * bPRSrc + xStartInt * 3; for (int j = xStartInt; j <= xEndInt; ++j) { double pixelAdd[3]; for (int k = 0; k < 3; ++k) { pixelAdd[k] = (double)dataSrc[jData++] / sxy; } if (j == xStartInt) { for (int k = 0; k < 3; ++k) pixelAdd[k] *= lFrm; } else if (j == xEndInt) { for (int k = 0; k < 3; ++k) pixelAdd[k] *= rFrm; } if (i == yStartInt) { for (int k = 0; k < 3; ++k) pixelAdd[k] *= tFrm; } else if (i == yEndInt) { for (int k = 0; k < 3; ++k) pixelAdd[k] *= bFrm; } for (int k = 0; k < 3; ++k) pixel[k] += pixelAdd[k]; } } imgDst.setPixel(x, y, (uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]); } } // done return true; }

class Image

图像缩放

image.h

#ifndef IMAGE_H
#define IMAGE_H

#include <vector>

// convenience type for bytes
typedef unsigned char uint8;

// image helper class
class Image {
  private: // variables:
    int _w, _h; // image size
    size_t _bPR; // bytes per row
    std::vector<uint8> _data; // image data

  public: // methods:

    // constructor.
    Image(): _w(0), _h(0), _bPR(0) { }
    // destructor.
    ~Image() = default;
    // copy constructor.
    Image(const Image&) = delete; // = default; would work as well.
    // copy assignment.
    Image& operator=(const Image&) = delete; // = default; would work as well.

    // returns width of image.
    int w() const { return _w; }
    // returns height of image.
    int h() const { return _h; }
    // returns bytes per row.
    size_t bPR() const { return _bPR; }
    // returns pointer to image data.
    const uint8* data(
      int y = 0) // row number
    const {
      return &_data[y * _bPR];
    }
    // returns data size (in bytes).
    size_t size() const { return _data.size(); }

    // clears image.
    void clear();

    // resizes image.
    uint8* resize( // returns allocated buffer
      int w, // image width
      int h, // image height
      int bPR); // bytes per row

    // returns pixel.
    int getPixel(
      int x, // column
      int y) // row
    const;
    // sets pixel.
    void setPixel(
      int x, // column
      int y, // row
      uint8 r, uint8 g, uint8 b);
    // sets pixel.
    void setPixel(
      int x, // column
      int y, // row
      int value) // RGB value
    {
      setPixel(x, y, value & 0xff, value >> 8 & 0xff, value >> 16 & 0xff);
    }
};

// helper functions:

inline uint8 getR(int value) { return value & 0xff; }

inline uint8 getG(int value) { return value >> 8 & 0xff; }

inline uint8 getB(int value) { return value >> 16 & 0xff; }

#endif // IMAGE_H

image.cc

#include <cassert>

#include "image.h"

// clears image.
void Image::clear()
{
  _data.clear(); _w = _h = _bPR = 0;
}

// allocates image data.
uint8* Image::resize( // returns allocated buffer
  int w, // image width
  int h, // image height
  int bPR) // bits per row
{
  assert(w >= 0 && 3 * w <= bPR);
  assert(h >= 0);
  _w = w; _h = h; _bPR = bPR;
  const size_t size = h * bPR;
  _data.resize(size);
  return _data.data();
}

// returns pixel.
int Image::getPixel(
  int x, // column
  int y) // row
const {
  assert(x >= 0 && x < _w);
  assert(y >= 0 && y < _h);
  const size_t offs = y * _bPR + 3 * x;
  return _data[offs + 0]
    | _data[offs + 1] << 8
    | _data[offs + 2] << 16;
}

// sets pixel.
void Image::setPixel(
  int x, // column
  int y, // row
  uint8 r, uint8 g, uint8 b) // R, G, B values
{
  assert(x >= 0 && x < _w);
  assert(y >= 0 && y < _h);
  const size_t offs = y * _bPR + 3 * x;
  _data[offs + 0] = r;
  _data[offs + 1] = g;
  _data[offs + 2] = b;
}

PPM文件IO

imageScale.h

#ifndef IMAGE_SCALE_H
#define IMAGE_SCALE_H

#include "image.h"

/* scales an image to a certain width and height.
 *
 * Note:
 * imgSrc and imgDst may not be identical.
 */
bool scaleTo( // returns true if successful
  const Image &imgSrc, // source image
  Image &imgDst, // destination image
  int w, int h, // destination width and height
  int align = 4); // row alignment

/* scales an image about a certain horizontal/vertical scaling factor.
 *
 * Note:
 * imgSrc and imgDst may not be identical.
 */
inline bool scaleXY( // returns true if successful
  const Image &imgSrc, // source image
  Image &imgDst, // destination image
  double sX, // horizontal scaling factor (must be > 0 but not too large)
  double sY, // vertical scaling factor (must be > 0 but not too large)
  int align = 4) // row alignment
{
  return sX > 0.0 && sY > 0.0
   ? scaleTo(imgSrc, imgDst,
     (int)(sX * imgSrc.w()), (int)(sY * imgSrc.h()), align)
    : false;
}

/* scales an image about a certain scaling factor.
 *
 * Note:
 * imgSrc and imgDst may not be identical.
 */
inline bool scale( // returns true if successful
  const Image &imgSrc, // source image
  Image &imgDst, // destination image
  double s, // scaling factor (must be > 0 but not too large)
  int align = 4) // row alignment
{
  return scaleXY(imgSrc, imgDst, s, s, align);
}

#endif // IMAGE_SCALE_H

imageScale.cc

#include <cassert>
#include <algorithm>

#include "imageScale.h"

namespace {

template <typename VALUE>
VALUE clip(VALUE value, VALUE min, VALUE max)
{
  return value < min ? min : value > max ? max : value;
}

bool scaleDown(
  const Image &imgSrc,
  Image &imgDst,
  int w, int h,
  int align)
{
  const int wSrc = imgSrc.w(), hSrc = imgSrc.h();
  assert(w > 0 && w <= wSrc && h > 0 && h <= hSrc);
  // compute scaling factors
  const double sx = (double)wSrc / (double)w;
  const double sy = (double)hSrc / (double)h;
  const double sxy = sx * sy;
  // prepare destination image
  imgDst.resize(w, h, (w * 3 + align - 1) / align * align);
  // cache some data
  const uint8 *const dataSrc = imgSrc.data();
  const int bPRSrc = imgSrc.bPR();
  // perform scaling
  for (int y = 0; y < h; ++y) {
    const double yStart = sy * y;
    const double yEnd = std::min(sy * (y + 1), (double)hSrc);
    const int yStartInt = (int)yStart;
    const int yEndInt = (int)yEnd - (yEndInt == yEnd);
    const double tFrm = 1 + yStartInt - yStart, bFrm = yEnd - yEndInt;
    for (int x = 0; x < w; ++x) {
      const double xStart = sx * x;
      const double xEnd = std::min(sx * (x + 1), (double)wSrc);
      const int xStartInt = (int)xStart;
      const int xEndInt = (int)xEnd - (xEndInt == xEnd);
      double lFrm = 1 + xStartInt - xStart, rFrm = xEnd - xEndInt;
      double pixel[3] = { 0.0, 0.0, 0.0 }; // values of target pixel
      for (int i = yStartInt; i <= yEndInt; ++i) {
        int jData = i * bPRSrc + xStartInt * 3;
        for (int j = xStartInt; j <= xEndInt; ++j) {
          double pixelAdd[3];
          for (int k = 0; k < 3; ++k) {
            pixelAdd[k] = (double)dataSrc[jData++] / sxy;
          }
          if (j == xStartInt) {
            for (int k = 0; k < 3; ++k) pixelAdd[k] *= lFrm;
          } else if (j == xEndInt) {
            for (int k = 0; k < 3; ++k) pixelAdd[k] *= rFrm;
          }
          if (i == yStartInt) {
            for (int k = 0; k < 3; ++k) pixelAdd[k] *= tFrm;
          } else if (i == yEndInt) {
            for (int k = 0; k < 3; ++k) pixelAdd[k] *= bFrm;
          }
          for (int k = 0; k < 3; ++k) pixel[k] += pixelAdd[k];
        }
      }
      imgDst.setPixel(x, y,
        (uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]);
    }
  }
  // done
  return true;
}

bool scaleUp(
  const Image &imgSrc,
  Image &imgDst,
  int w, int h,
  int align)
{
  const int wSrc = imgSrc.w(), hSrc = imgSrc.h();
  assert(w && w >= wSrc && h && h >= hSrc);
  // compute scaling factors
  const double sx = (double)wSrc / (double)w;
  const double sy = (double)hSrc / (double)h;
  // prepare destination image
  imgDst.resize(w, h, (w * 3 + align - 1) / align * align);
  // cache some data
  const uint8 *const dataSrc = imgSrc.data();
  const int bPRSrc = imgSrc.bPR();
  // perform scaling
  for (int y = 0; y < h; ++y) {
    const double yStart = sy * y;
    const double yEnd = std::min(sy * (y + 1), (double)hSrc - 1);
    const int yStartInt = (int)yStart;
    const int yEndInt = (int)yEnd;
    if (yStartInt < yEndInt) {
      const double bFract = clip((double)((yEnd - yEndInt) / sy), 0.0, 1.0);
      const double tFract = 1.0 - bFract;
      for (int x = 0; x < w; ++x) {
        const double xStart = sx * x;
        const double xEnd = std::min(sx * (x + 1), (double)wSrc - 1);
        const int xStartInt = (int)xStart, xEndInt = (int)xEnd;
        double pixel[4];
        if (xStartInt < xEndInt) {
          const double rFract
            = clip((double)((xEnd - xEndInt) / sx), 0.0, 1.0);
          const double lFract = 1.0 - rFract;
          int jData = yStartInt * bPRSrc + xStartInt * 3;
          for (int k = 0; k < 3; ++k) {
            pixel[k] = tFract * lFract * dataSrc[jData++];
          }
          for (int k = 0; k < 3; ++k) {
            pixel[k] += tFract * rFract * dataSrc[jData++];
          }
          jData = yEndInt * bPRSrc + xStartInt * 3;
          for (int k = 0; k < 3; ++k) {
            pixel[k] += bFract * lFract *dataSrc[jData++];
          }
          for (int k = 0; k < 3; ++k) {
            pixel[k] += bFract * rFract *dataSrc[jData++];
          }
        } else {
          int jData = yStartInt * bPRSrc + xStartInt * 3;
          for (int k = 0; k < 3; ++k) {
        pixel[k] = tFract * dataSrc[jData++];
      }
          jData = yEndInt * bPRSrc + xStartInt * 3;
          for (int k = 0; k < 3; ++k) {
        pixel[k] += bFract * dataSrc[jData++];
      }
        }
        imgDst.setPixel(x, y,
          (uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]);
      }
    } else {
      for (int x = 0; x < w; ++x) {
        const double xStart = sx * x;
        const double xEnd = std::min(sx * (x + 1), (double)wSrc - 1);
        const int xStartInt = (int)xStart, xEndInt = (int)xEnd;
        double pixel[3];
        if (xStartInt < xEndInt) {
          const double rFract
            = clip((double)((xEnd - xEndInt) / sx), 0.0, 1.0);
          const double lFract = 1.0 - rFract;
          int jData = yStartInt * bPRSrc + xStartInt * 3;
          for (int k = 0; k < 3; ++k) {
        pixel[k] = lFract * dataSrc[jData++];
      }
          for (int k = 0; k < 3; ++k) {
        pixel[k] += rFract * dataSrc[jData++];
      }
        } else {
          int jData = yStartInt * bPRSrc + xStartInt * 3;
          for (int k = 0; k < 3; ++k) pixel[k] = dataSrc[jData++];
        }
        imgDst.setPixel(x, y,
          (uint8)pixel[0], (uint8)pixel[1], (uint8)pixel[2]);
      }
    }
  }
  // done
  return true;
}

} // namespace

bool scaleTo(const Image &imgSrc, Image &imgDst, int w, int h, int align)
{
  Image imgTmp;
  return w <= 0 || h <= 0 ? false
    : w >= imgSrc.w() && h >= imgSrc.h()
    ? scaleUp(imgSrc, imgDst, w, h, align)
    : w <= imgSrc.w() && h <= imgSrc.h()
    ? scaleDown(imgSrc, imgDst, w, h, align)
    : w >= imgSrc.w()
    ? scaleUp(imgSrc, imgTmp, w, imgSrc.h(), 1)
      && scaleDown(imgTmp, imgDst, w, h, align)
    : scaleDown(imgSrc, imgTmp, w, imgSrc.h(), 1)
      && scaleUp(imgTmp, imgDst, w, h, align);
}

主要应用

imagePPM.h

#ifndef IMAGE_PPM_H
#define IMAGE_PPM_H

#include <iostream>

#include "image.h"

// reads a binary PPM file.
bool readPPM( // returns true if successful
  std::istream &in, // input stream (must be opened with std::ios::binary)
  Image &img, // image to read into
  int align = 4); // row alignment

// writes binary PPM file.
bool writePPM( // returns true if successful
  std::ostream &out, // output stream (must be opened with std::ios::binary)
  const Image &img); // image to write from

#endif // IMAGE_PPM_H

测试

编译于cygwin64

imagePPM.cc

用于测试的示例图像#include <sstream> #include <string> #include "imagePPM.h" // reads a binary PPM file. bool readPPM( // returns true if successful std::istream &in, // input stream (must be opened with std::ios::binary) Image &img, // image to read into int align) // row alignment { // parse header std::string buffer; if (!getline(in, buffer)) return false; if (buffer != "P6") { std::cerr << "Wrong header! 'P6' expected.\n"; return false; } int w = 0, h = 0, t = 0; for (int i = 0; i < 3;) { if (!getline(in, buffer)) return false; if (buffer.empty()) continue; // skip empty lines if (buffer[0] == '#') continue; // skip comments std::istringstream str(buffer); switch (i) { case 0: if (!(str >> w)) continue; ++i; case 1: if (!(str >> h)) continue; ++i; case 2: if (!(str >> t)) continue; ++i; } } if (t != 255) { std::cerr << "Unsupported format! t = 255 expected.\n"; return false; } // allocate image buffer uint8 *data = img.resize(w, h, (w * 3 + align - 1) / align * align); // read data for (int i = 0; i < h; ++i) { if (!in.read((char*)data, 3 * img.w())) return false; data += img.bPR(); } // done return true; } // writes binary PPM file. bool writePPM( // returns true if successful std::ostream &out, // output stream (must be opened with std::ios::binary) const Image &img) // image to write from { // write header if (!(out << "P6\n" << img.w() << ' ' << img.h() << " 255\n")) return false; // write image data for (size_t y = 0; y < img.h(); ++y) { const uint8 *const data = img.data(y); if (!out.write((const char*)data, 3 * img.w())) return false; } // done return true; } –在GIMP中转换为PPM:

test.ppm (converted back to PNG for proper display in browser)

使用示例图像进行测试:

scaleRGBImg.cc

这就是结果:

#include <iostream> #include <fstream> #include <string> #include "image.h" #include "imagePPM.h" #include "imageScale.h" int main(int argc, char **argv) { // read command line arguments if (argc <= 3) { std::cerr << "Missing arguments!\n"; std::cout << "Usage:\n" << " scaleRGBImg IN_FILE SCALE OUT_FILE\n"; return 1; } const std::string inFile = argv[1]; char *end; const double s = std::strtod(argv[2], &end); if (end == argv[2] || *end != '\0') { std::cerr << "Invalid scale factor '" << argv[2] << "'!\n"; return 1; } if (s <= 0.0) { std::cerr << "Invalid scale factor " << s << "!\n"; return 1; } const std::string outFile = argv[3]; // read image Image imgSrc; { std::ifstream fIn(inFile.c_str(), std::ios::binary); if (!readPPM(fIn, imgSrc)) { std::cerr << "Reading '" << inFile << "' failed!\n"; return 1; } } // scale image Image imgDst; if (!scale(imgSrc, imgDst, s)) { std::cerr << "Scaling failed!\n"; return 1; } // write image { std::ofstream fOut(outFile.c_str(), std::ios::binary); if (!writePPM(fOut, imgDst) || (fOut.close(), !fOut.good())) { std::cerr << "Writing '" << outFile << "' failed!\n"; return 1; } } // done return 0; }

test.0.8.ppm (converted back to PNG for proper display in browser)

$ g++ -std=c++11 -o scaleRGBImg scaleRGBImg.cc image.cc imagePPM.cc imageScale.cc $

test.0.6.ppm (converted back to PNG for proper display in browser)

test.ppm

test.0.4.ppm (converted back to PNG for proper display in browser)

$ for I in 0.8 0.6 0.4 0.2 ; do echo ./scaleRGBImg test.ppm $I test.$I.ppm ; done ./scaleRGBImg test.ppm 0.8 test.0.8.ppm ./scaleRGBImg test.ppm 0.6 test.0.6.ppm ./scaleRGBImg test.ppm 0.4 test.0.4.ppm ./scaleRGBImg test.ppm 0.2 test.0.2.ppm $ for I in 0.8 0.6 0.4 0.2 ; do ./scaleRGBImg test.ppm $I test.$I.ppm ; done $

test.0.2.ppm (converted back to PNG for proper display in browser)