diff --git a/README.md b/README.md index 38f63be..73c935e 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,14 @@ Interface for reading and writing [PCM](https://en.wikipedia.org/wiki/Pulse-code Read and write [WAV](https://en.wikipedia.org/wiki/WAV) files +### [pel.hh](pel.hh) + +Interface for reading and writing [pixel](https://en.wikipedia.org/wiki/Pixel) data + +### [netpbm.hh](netpbm.hh) + +Read and write [Netpbm](https://en.wikipedia.org/wiki/Netpbm) files + ### [spline.hh](spline.hh) * Algorithm for computing uniform and [natural cubic splines](https://en.wikipedia.org/wiki/Spline_(mathematics)#Algorithm_for_computing_natural_cubic_splines) diff --git a/netpbm.hh b/netpbm.hh new file mode 100644 index 0000000..7102a20 --- /dev/null +++ b/netpbm.hh @@ -0,0 +1,180 @@ +/* +Read and write Netpbm files + +Copyright 2020 Ahmet Inan +*/ + +#pragma once + +#include +#include "pel.hh" + +namespace DSP { + +template +class ReadPNM : public ReadPEL +{ + std::ifstream is; + int width_, height_, maxval_; + bool mono_; + + TYPE linear(int val) + { + int K0 = std::nearbyint(0.03928 * maxval_); + TYPE phi(12.92 * maxval_), a(0.055 * maxval_), gamma(2.4); + return val <= K0 ? val / phi : std::pow((val + a) / (maxval_ + a), gamma); + } + static bool space(int c) + { + return c == ' ' || c == '\n' || c == '\t'; + } + static bool newline(int c) + { + return c == '\n'; + } + static bool digit(int c) + { + return c >= '0' && c <= '9'; + } + static int value(int c) + { + return c - '0'; + } +public: + ReadPNM(const char *name) : is(name, std::ios::binary) + { + if (is.get() != 'P') + return; + int c = is.get(); + if (!digit(c)) + return; + int number = value(c); + if (number == 5) + mono_ = true; + else if (number == 6) + mono_ = false; + else + return; + c = is.get(); + if (!space(c)) + return; + int num[3]; + for (int i = 0; i < 3; ++i) { + if (newline(c)) + for (c = is.get(); c == '#'; c = is.get()) + while (is.good() && !newline(is.get())); + while (space(c)) + c = is.get(); + for (num[i] = 0; digit(c); c = is.get()) + num[i] = 10 * num[i] + value(c); + } + if (!space(c)) + return; + width_ = num[0]; + height_ = num[1]; + maxval_ = num[2]; + } + void read(TYPE *buf, int num, int stride = -1) + { + if (mono_) { + if (stride < 0) + stride = 1; + for (int i = 0; i < num; ++i) { + int G = is.get(); + buf[stride*i] = linear(G); + } + } else { + if (stride < 0) + stride = 3; + for (int i = 0; i < num; ++i) { + int R = is.get(); + int G = is.get(); + int B = is.get(); + buf[stride*i+0] = linear(R); + buf[stride*i+1] = linear(G); + buf[stride*i+2] = linear(B); + } + } + } + bool good() + { + return is.good(); + } + bool mono() + { + return mono_; + } + int width() + { + return width_; + } + int height() + { + return height_; + } +}; + +template +class WritePNM : public WritePEL +{ + std::ofstream os; + int width_, height_, maxval_; + bool mono_; + + int srgb(TYPE val) + { + TYPE K0(0.03928), a(0.055), phi(12.92), gamma(2.4); + val = std::min(std::max(val, TYPE(0)), TYPE(1)); + val = val <= K0 / phi ? val * phi : (1 + a) * std::pow(val, 1 / gamma) - a; + return std::nearbyint(maxval_ * val); + } +public: + WritePNM(const char *name, int width, int height, bool mono = false, int maxval = 255) : + os(name, std::ios::binary | std::ios::trunc), + width_(width), height_(height), maxval_(maxval), mono_(mono) + { + int number = mono ? 5 : 6; + os << 'P' << number << ' ' << width << ' ' << height << ' ' << maxval << '\n'; + } + void write(const TYPE *buf, int num, int stride = -1) + { + if (mono_) { + if (stride < 0) + stride = 1; + for (int i = 0; i < num; ++i) { + int G = srgb(buf[stride*i]); + os.put(G); + } + } else { + if (stride < 0) + stride = 3; + for (int i = 0; i < num; ++i) { + int R = srgb(buf[stride*i+0]); + int G = srgb(buf[stride*i+1]); + int B = srgb(buf[stride*i+2]); + os.put(R); + os.put(G); + os.put(B); + } + } + } + bool good() + { + return os.good(); + } + bool mono() + { + return mono_; + } + int width() + { + return width_; + } + int height() + { + return height_; + } +}; + +} + diff --git a/pel.hh b/pel.hh new file mode 100644 index 0000000..db30775 --- /dev/null +++ b/pel.hh @@ -0,0 +1,34 @@ +/* +Interface for reading and writing pixel data + +Copyright 2020 Ahmet Inan +*/ + +#pragma once + +namespace DSP { + +template +struct WritePEL +{ + virtual void write(const TYPE *, int, int = -1) = 0; + virtual bool good() = 0; + virtual bool mono() = 0; + virtual int width() = 0; + virtual int height() = 0; + virtual ~WritePEL() = default; +}; + +template +struct ReadPEL +{ + virtual void read(TYPE *, int, int = -1) = 0; + virtual bool good() = 0; + virtual bool mono() = 0; + virtual int width() = 0; + virtual int height() = 0; + virtual ~ReadPEL() = default; +}; + +} +