mirror of
https://github.com/aicodix/dsp.git
synced 2026-04-27 14:30:36 +00:00
180 lines
3.2 KiB
C++
180 lines
3.2 KiB
C++
/*
|
|
Read and write Netpbm files
|
|
|
|
Copyright 2020 Ahmet Inan <inan@aicodix.de>
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <fstream>
|
|
#include "pel.hh"
|
|
|
|
namespace DSP {
|
|
|
|
template <typename TYPE>
|
|
class ReadPNM : public ReadPEL<TYPE>
|
|
{
|
|
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 <typename TYPE>
|
|
class WritePNM : public WritePEL<TYPE>
|
|
{
|
|
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_;
|
|
}
|
|
};
|
|
|
|
}
|
|
|