netpbm: extract ppm/pgm into distinct unit
This commit is contained in:
parent
fc279753eb
commit
c82f3aad2e
@ -136,6 +136,8 @@ UTIL_FILES = \
|
||||
net/socket.hpp \
|
||||
net/types.cpp \
|
||||
net/types.hpp \
|
||||
netpbm.cpp \
|
||||
netpbm.hpp \
|
||||
nocopy.hpp \
|
||||
noise.hpp \
|
||||
noise/basis.hpp \
|
||||
|
113
image.cpp
113
image.cpp
@ -14,16 +14,10 @@
|
||||
* Copyright 2011-2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
|
||||
#include "image.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
|
||||
#include "except.hpp"
|
||||
#include "io.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
using util::image::buffer;
|
||||
|
||||
|
||||
@ -358,111 +352,6 @@ buffer<T>::end (void) const
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// HACK: This does not support the full header structure with any robustness.
|
||||
// In particular it will abort when it sees a comment. If you want better
|
||||
// support use waif, or port its implementation.
|
||||
util::image::buffer<uint8_t>
|
||||
util::pgm::read (const boost::filesystem::path &path)
|
||||
{
|
||||
util::mapped_file raw (path);
|
||||
|
||||
std::ifstream cooked (path.string (), std::ios::binary);
|
||||
char magic[2];
|
||||
size_t width, height, scale;
|
||||
char space;
|
||||
|
||||
cooked >> magic[0] >> magic[1] >> width >> height >> scale >> space;
|
||||
|
||||
if (magic[0] != 'P' && magic[1] != '5')
|
||||
throw std::runtime_error ("invalid header magic");
|
||||
|
||||
if (width == 0 || height == 0 || scale == 0)
|
||||
throw std::runtime_error ("zero width, height, or scale");
|
||||
|
||||
size_t expected = width * height;
|
||||
size_t remain = raw.size () - cooked.tellg ();
|
||||
if (expected != remain)
|
||||
throw std::runtime_error ("expected data size mismatch");
|
||||
|
||||
util::image::buffer<uint8_t> out (width, height);
|
||||
|
||||
CHECK_EQ (out.w, out.s);
|
||||
std::copy (raw.begin () + cooked.tellg () - 1, raw.end (), out.begin ());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
write_netpbm (const uint8_t *restrict pixels,
|
||||
size_t components,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path,
|
||||
const char* MAGIC) {
|
||||
CHECK (pixels);
|
||||
CHECK_GT (components, 0);
|
||||
CHECK_GT (width, 0);
|
||||
CHECK_GE (stride, width);
|
||||
CHECK_GT (height, 0);
|
||||
|
||||
// Establish an output stream
|
||||
std::ofstream output (path.string ());
|
||||
if (!output.good ())
|
||||
throw util::output_error ("Unable to open output file");
|
||||
|
||||
// Write the PPM header.
|
||||
output << MAGIC << "\n"
|
||||
<< width << "\n"
|
||||
<< height << "\n"
|
||||
<< (size_t)std::numeric_limits<uint8_t>::max () << "\n";
|
||||
|
||||
// Write the data rows
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
for (size_t x = 0; x < width; ++x)
|
||||
for (size_t c = 0; c < components; ++c)
|
||||
output << pixels[y * stride + x * components + c];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
util::pgm::write (const util::image::buffer<uint8_t> &src,
|
||||
const boost::filesystem::path &path)
|
||||
{
|
||||
write (src.begin (), src.w, src.h, src.s, path);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
util::pgm::write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path) {
|
||||
// TODO: We should switch between P2 (ascii) and P5 (binary)
|
||||
static const char MAGIC[] = "P5";
|
||||
write_netpbm (pixels, 1, width, height, stride, path, MAGIC);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
util::ppm::write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path) {
|
||||
// TODO: We should switch between P3 (ascii) and P6 (binary)
|
||||
static const char MAGIC[] = "P6";
|
||||
write_netpbm (pixels, 3, width, height, stride, path, MAGIC);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template struct util::image::buffer<uint8_t>;
|
||||
template struct util::image::buffer<float>;
|
||||
|
25
image.hpp
25
image.hpp
@ -23,7 +23,6 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
|
||||
namespace util {
|
||||
@ -72,30 +71,6 @@ namespace util {
|
||||
std::unique_ptr<T[]> m_data;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Portable GrayMap: single component greyscale.
|
||||
struct pgm {
|
||||
static image::buffer<uint8_t> read (const boost::filesystem::path&);
|
||||
|
||||
static void write (const image::buffer<uint8_t> &src,
|
||||
const boost::filesystem::path &);
|
||||
|
||||
static void write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path);
|
||||
};
|
||||
|
||||
/// Portable PixMap: 3-component colour images.
|
||||
struct ppm {
|
||||
static void write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
126
netpbm.cpp
Normal file
126
netpbm.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2011-2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "netpbm.hpp"
|
||||
|
||||
#include "io.hpp"
|
||||
#include "except.hpp"
|
||||
#include <fstream>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// HACK: This does not support the full header structure with any robustness.
|
||||
// In particular it will abort when it sees a comment. If you want better
|
||||
// support use waif, or port its implementation.
|
||||
util::image::buffer<uint8_t>
|
||||
util::pgm::read (const boost::filesystem::path &path)
|
||||
{
|
||||
util::mapped_file raw (path);
|
||||
|
||||
std::ifstream cooked (path.string (), std::ios::binary);
|
||||
char magic[2];
|
||||
size_t width, height, scale;
|
||||
char space;
|
||||
|
||||
cooked >> magic[0] >> magic[1] >> width >> height >> scale >> space;
|
||||
|
||||
if (magic[0] != 'P' && magic[1] != '5')
|
||||
throw std::runtime_error ("invalid header magic");
|
||||
|
||||
if (width == 0 || height == 0 || scale == 0)
|
||||
throw std::runtime_error ("zero width, height, or scale");
|
||||
|
||||
size_t expected = width * height;
|
||||
size_t remain = raw.size () - cooked.tellg ();
|
||||
if (expected != remain)
|
||||
throw std::runtime_error ("expected data size mismatch");
|
||||
|
||||
util::image::buffer<uint8_t> out (width, height);
|
||||
|
||||
CHECK_EQ (out.w, out.s);
|
||||
std::copy (raw.begin () + cooked.tellg () - 1, raw.end (), out.begin ());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
write_netpbm (const uint8_t *restrict pixels,
|
||||
size_t components,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path,
|
||||
const char* MAGIC) {
|
||||
CHECK (pixels);
|
||||
CHECK_GT (components, 0);
|
||||
CHECK_GT (width, 0);
|
||||
CHECK_GE (stride, width);
|
||||
CHECK_GT (height, 0);
|
||||
|
||||
// Establish an output stream
|
||||
std::ofstream output (path.string ());
|
||||
if (!output.good ())
|
||||
throw util::output_error ("Unable to open output file");
|
||||
|
||||
// Write the PPM header.
|
||||
output << MAGIC << "\n"
|
||||
<< width << "\n"
|
||||
<< height << "\n"
|
||||
<< (size_t)std::numeric_limits<uint8_t>::max () << "\n";
|
||||
|
||||
// Write the data rows
|
||||
for (size_t y = 0; y < height; ++y) {
|
||||
for (size_t x = 0; x < width; ++x)
|
||||
for (size_t c = 0; c < components; ++c)
|
||||
output << pixels[y * stride + x * components + c];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
util::pgm::write (const util::image::buffer<uint8_t> &src,
|
||||
const boost::filesystem::path &path)
|
||||
{
|
||||
write (src.begin (), src.w, src.h, src.s, path);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
util::pgm::write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path) {
|
||||
// TODO: We should switch between P2 (ascii) and P5 (binary)
|
||||
static const char MAGIC[] = "P5";
|
||||
write_netpbm (pixels, 1, width, height, stride, path, MAGIC);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
util::ppm::write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path) {
|
||||
// TODO: We should switch between P3 (ascii) and P6 (binary)
|
||||
static const char MAGIC[] = "P6";
|
||||
write_netpbm (pixels, 3, width, height, stride, path, MAGIC);
|
||||
}
|
51
netpbm.hpp
Normal file
51
netpbm.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2011-2015 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_NETPBM_HPP
|
||||
#define __UTIL_NETPBM_HPP
|
||||
|
||||
#include "image.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace util {
|
||||
// Portable GrayMap: single component greyscale.
|
||||
struct pgm {
|
||||
static image::buffer<uint8_t> read (const boost::filesystem::path&);
|
||||
|
||||
static void write (const image::buffer<uint8_t> &src,
|
||||
const boost::filesystem::path &);
|
||||
|
||||
static void write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path);
|
||||
};
|
||||
|
||||
/// Portable PixMap: 3-component colour images.
|
||||
struct ppm {
|
||||
static void write (const uint8_t *restrict pixels,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t stride,
|
||||
const boost::filesystem::path &path);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -13,6 +13,7 @@
|
||||
#include "noise/turbulence.hpp"
|
||||
#include "extent.hpp"
|
||||
#include "colour.hpp"
|
||||
#include "netpbm.hpp"
|
||||
|
||||
template struct util::noise::fractal::fbm<float, util::noise::basis::perlin<float,util::lerp::cubic>>;
|
||||
template struct util::noise::fractal::hmf<float, util::noise::basis::value<float,util::lerp::cubic>>;
|
||||
@ -23,7 +24,7 @@ int
|
||||
main (void)
|
||||
{
|
||||
// setup the output buffer
|
||||
#if ENABLE_DEBUGGING
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
util::extent2u size {320, 240};
|
||||
#else
|
||||
util::extent2u size {1920, 1080};
|
||||
|
Loading…
Reference in New Issue
Block a user