diff --git a/Makefile.am b/Makefile.am index a4d6ae90..03d7a670 100644 --- a/Makefile.am +++ b/Makefile.am @@ -196,8 +196,6 @@ UTIL_FILES = \ net/socket.hpp \ net/types.cpp \ net/types.hpp \ - netpbm.cpp \ - netpbm.hpp \ nocopy.hpp \ noise.hpp \ noise.ipp \ diff --git a/netpbm.cpp b/netpbm.cpp deleted file mode 100644 index a80a0ce7..00000000 --- a/netpbm.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 - */ - -#include "netpbm.hpp" - -#include "io.hpp" -#include "except.hpp" -#include - - -/////////////////////////////////////////////////////////////////////////////// -// 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<1,uint8_t> -util::pgm::read (const boost::filesystem::path &path) -{ - util::mapped_file raw (path.string ().c_str ()); - - 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<1,uint8_t> out ({width, height}); - - CHECK (out.is_packed ()); - 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, - std::ostream &output, - const char* MAGIC) { - CHECK (pixels); - CHECK_GT (components, 0); - CHECK_GT (width, 0); - CHECK_GE (stride, width); - CHECK_GT (height, 0); - - // Write the PPM header. - output << MAGIC << "\n" - << width << "\n" - << height << "\n" - << (size_t)std::numeric_limits::max () << "\n"; - - // Write the data rows - for (size_t y = 0; y < height; ++y) - output.write (reinterpret_cast (pixels + y * stride), width * components); -} - - -//----------------------------------------------------------------------------- -void -util::pgm::write (const util::image::buffer<1,uint8_t> &src, - std::ostream &dst) -{ - write (src.begin (), src.extent ().w, src.extent ().h, src.stride ().y, dst); -} - - -//----------------------------------------------------------------------------- -void -util::pgm::write (const util::image::buffer<1,uint8_t> &src, - const boost::filesystem::path &path) -{ - std::ofstream dst (path.string ()); - write (src.begin (), src.extent ().w, src.extent ().h, src.stride ().y, path); -} - - -//----------------------------------------------------------------------------- -void -util::pgm::write (const uint8_t *restrict pixels, - size_t width, - size_t height, - size_t stride, - const boost::filesystem::path &path) -{ - std::ofstream dst (path.string ()); - write (pixels, width, height, stride, dst); -} - - -//----------------------------------------------------------------------------- -void -util::pgm::write (const uint8_t *restrict pixels, - size_t width, - size_t height, - size_t stride, - std::ostream &dst) -{ - // TODO: We should switch between P2 (ascii) and P5 (binary) - static const char MAGIC[] = "P5"; - write_netpbm (pixels, 1, width, height, stride, dst, MAGIC); -} - - -//----------------------------------------------------------------------------- -void -util::ppm::write (const uint8_t *restrict pixels, - size_t width, - size_t height, - size_t stride, - const boost::filesystem::path &path) -{ - std::ofstream dst (path.string ()); - write (pixels, width, height, stride, dst); -} - - -//----------------------------------------------------------------------------- -void -util::ppm::write (const uint8_t *restrict pixels, - size_t width, - size_t height, - size_t stride, - std::ostream &dst) -{ - // TODO: We should switch between P3 (ascii) and P6 (binary) - static const char MAGIC[] = "P6"; - write_netpbm (pixels, 3, width, height, stride, dst, MAGIC); -} diff --git a/netpbm.hpp b/netpbm.hpp deleted file mode 100644 index 36c38b9c..00000000 --- a/netpbm.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 - */ - -#ifndef __UTIL_NETPBM_HPP -#define __UTIL_NETPBM_HPP - -#include "./image/buffer.hpp" - -#include -#include -#include -#include - -namespace util { - // Portable GrayMap: single component greyscale. - struct pgm { - static image::buffer<1,uint8_t> read (const boost::filesystem::path&); - - static void write (const image::buffer<1,uint8_t> &src, - const boost::filesystem::path &dst); - static void write (const image::buffer<1,uint8_t> &src, - std::ostream &dst); - - static void write (const uint8_t *restrict pixels, - size_t width, - size_t height, - size_t stride, - std::ostream &dst); - 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); - static void write (const uint8_t *restrict pixels, - size_t width, - size_t height, - size_t stride, - std::ostream &dst); - }; -} - -#endif diff --git a/tools/noise.cpp b/tools/noise.cpp deleted file mode 100644 index 47ba8da5..00000000 --- a/tools/noise.cpp +++ /dev/null @@ -1,397 +0,0 @@ -#include "image/buffer.hpp" -#include "noise.hpp" - -#include "noise/fractal/fbm.hpp" -#include "noise/fractal/hetero.hpp" -#include "noise/fractal/hmf.hpp" -#include "noise/fractal/rmf.hpp" -#include "noise/fractal/runtime.hpp" -#include "noise/lerp.hpp" -#include "noise/basis/constant.hpp" -#include "noise/basis/gradient/exp.hpp" -#include "noise/basis/value.hpp" -#include "noise/basis/patch.hpp" -#include "noise/basis/perlin.hpp" -#include "noise/basis/worley.hpp" -#include "noise/turbulence.hpp" -#include "noise/basis/runtime.hpp" -#include "noise/midpoint.hpp" -#include "extent.hpp" -#include "colour.hpp" -#include "netpbm.hpp" -#include "types.hpp" -#include "cmdopt.hpp" -#include "hash.hpp" -#include "region.hpp" - - -constexpr size_t S = 2; - -/////////////////////////////////////////////////////////////////////////////// -template struct util::noise::fractal::rmf>; - -template struct util::noise::fractal::fbm< - util::noise::basis::perlin< - S,float,util::lerp::cubic,util::noise::basis::gradient::uniform - > ->; - -template struct util::noise::fractal::fbm< - util::noise::basis::perlin< - S,float,util::lerp::quintic,util::noise::basis::gradient::exp - > ->; - -template struct util::noise::fractal::hmf< - util::noise::basis::value< - S,float,util::lerp::cubic - > ->; - -template struct util::noise::fractal::hetero>; - - -/////////////////////////////////////////////////////////////////////////////// -enum basis_t { - VALUE, - PERLIN, - WORLEY, - PATCH, - EXPDIST -}; - - -//----------------------------------------------------------------------------- -enum fractal_t { - FBM, - HMF, - RMF, - HETERO, -}; - - -//----------------------------------------------------------------------------- -enum lerp_t { - LINEAR, - CUBIC, - QUINTIC, - COSINE, - TRUNC -}; - - -//----------------------------------------------------------------------------- -std::istream& -operator>> (std::istream &is, basis_t &b) -{ - std::string name; - is >> name; - - b = name == "value" ? VALUE : - name == "perlin" ? PERLIN : - name == "worley" ? WORLEY : - name == "patch" ? PATCH : - name == "expgrad" ? EXPDIST : - (is.setstate (std::istream::failbit), b); - - return is; -} - - -//----------------------------------------------------------------------------- -std::ostream& -operator<< (std::ostream &os, basis_t b) -{ - switch (b) { - case VALUE: os << "value"; return os; - case PERLIN: os << "perlin"; return os; - case WORLEY: os << "worley"; return os; - case PATCH: os << "patch"; return os; - case EXPDIST: os << "expgrad"; return os; - - default: - unreachable (); - } -} - - -//----------------------------------------------------------------------------- -std::istream& -operator>> (std::istream &is, fractal_t &f) -{ - std::string name; - is >> name; - - f = name == "fbm" ? FBM : - name == "hmf" ? HMF : - name == "rmf" ? RMF : - name == "hetero" ? HETERO : - (is.setstate (std::istream::failbit), f); - - return is; -} - - -//----------------------------------------------------------------------------- -std::ostream& -operator<< (std::ostream &os, fractal_t f) -{ - switch (f) { - case FBM: os << "fbm"; return os; - case HMF: os << "hmf"; return os; - case RMF: os << "rmf"; return os; - case HETERO: os << "hetero"; return os; - - default: - unreachable (); - }; -} - - -//----------------------------------------------------------------------------- -std::istream& -operator>> (std::istream &is, lerp_t &l) -{ - std::string name; - is >> name; - - l = name == "linear" ? LINEAR : - name == "cubic" ? CUBIC : - name == "quintic" ? QUINTIC : - name == "cosine" ? COSINE : - name == "trunc" ? TRUNC : - (is.setstate (std::istream::failbit), l); - - return is; -} - - -//----------------------------------------------------------------------------- -std::ostream& -operator<< (std::ostream &os, lerp_t &l) -{ - switch (l) { - case LINEAR: os << "linear"; return os; - case CUBIC: os << "cubic"; return os; - case QUINTIC: os << "quintic"; return os; - case COSINE: os << "cosine"; return os; - case TRUNC: os << "trunc"; return os; - - default: - unreachable (); - } -} - - -/////////////////////////////////////////////////////////////////////////////// -int -main (int argc, char **argv) -{ - // setup default variables -#ifdef ENABLE_DEBUGGING - util::extent2u res {320, 240}; -#else - util::extent2u res {1920, 1080}; -#endif - - srand (time (nullptr)); - uint64_t seed = time (nullptr); - - basis_t basis = PERLIN; - fractal_t fractal = FBM; - lerp_t lerp = QUINTIC; - unsigned octaves = 8; - float H = std::numeric_limits::quiet_NaN (); - float lacunarity = std::numeric_limits::quiet_NaN (); - float amplitude = std::numeric_limits::quiet_NaN (); - float gain = std::numeric_limits::quiet_NaN (); - float offset = std::numeric_limits::quiet_NaN (); - float scale = 1.f; - float turbulence = 0.f; - unsigned single = 0; - float width = 0; - - // fill variables from arguments - util::cmdopt::parser args; - args.add> ('w', "width", "output image width", res.w); - args.add> ('h', "height", "output image height", res.h); - args.add> ('s', "seed", "random seed", seed); - args.add> ('b', "basis", "primary basis function", basis); - args.add> ('f', "fractal", "primary fractal function", fractal); - args.add> ('l', "lerp", "interpolation algorithm", lerp); - args.add> ('o', "octaves", "total fractal iterations", octaves); - args.add> ('1', "single", "single octave", single); - args.add> ('H', "hurst", "Hurst exponent", H); - args.add> ('G', "gain", "octave gain", gain); - args.add> ('A', "amplitude", "base amplitude", amplitude); - args.add> ('L', "lacunarity", "frequency multiplier", lacunarity); - args.add> ('x', "scale", "frequency multiplier", scale); - args.add> ('O', "offset", "hetero offset", offset); - args.add> ('t', "turbulence", "turbulence scale", turbulence); - args.add> ('W', "patch-width", "patch blur width", width); - - args.scan (argc, argv); - -#if !defined(ENABLE_DEBUGGING) and !defined(PLATFORM_WIN32) - if (isatty (fileno (stdout))) { - std::cerr << "cowardly refusing to dump binary data to console\n"; - return EXIT_FAILURE; - } -#endif - - util::noise::turbulence< - util::noise::fractal::runtime< - util::noise::basis::runtime - >, - util::noise::fractal::fbm< - util::noise::basis::perlin< - S,float, - util::lerp::cubic - > - > - > t (seed, util::vectorf (turbulence)); - - auto &f = t.data; - - switch (fractal) { - using namespace util::noise; - - case FBM: f.reset>> (seed); break; - case HMF: f.reset>> (seed); break; - case RMF: f.reset>> (seed); break; - case HETERO: { - auto &child = f.reset>> (seed); - if (!std::isnan (offset)) - child.offset (offset); - break; - } - - default: - unreachable (); - } - - auto &b = f.basis (); - switch (basis) { - using namespace util::noise; - - case PERLIN: { - switch (lerp) { - case LINEAR: b.reset> (seed); break; - case CUBIC: b.reset> (seed); break; - case QUINTIC: b.reset> (seed); break; - case COSINE: b.reset> (seed); break; - case TRUNC: b.reset> (seed); break; - - default: - unreachable (); - } - break; - } - - case EXPDIST: { - switch (lerp) { - case LINEAR: b.reset< - basis::perlin< - S,float,util::lerp::linear,basis::gradient::exp - > - > (seed); break; - - case CUBIC: b.reset> (seed); break; - case QUINTIC: b.reset> (seed); break; - case COSINE: b.reset> (seed); break; - case TRUNC: b.reset> (seed); break; - - default: - unreachable (); - } - break; - } - - case VALUE: { - switch (lerp) { - case LINEAR: b.reset> (seed); break; - case CUBIC: b.reset> (seed); break; - case QUINTIC: b.reset> (seed); break; - case COSINE: b.reset> (seed); break; - case TRUNC: b.reset> (seed); break; - - default: - unreachable (); - } - break; - } - - case WORLEY: { - b.reset> (seed); - break; - } - - case PATCH: { - b.reset> (seed, width); - break; - } - - default: - unreachable (); - } - - t.seed (seed); - f.octaves (octaves); - f.frequency (scale / res.w); - if (!std::isnan (H)) f.H (H); - if (!std::isnan (lacunarity)) f.lacunarity (lacunarity); - if (!std::isnan (amplitude)) f.amplitude (amplitude); - if (!std::isnan (gain)) f.gain (gain); - - for (auto &p: t.perturb) - p.frequency (scale / res.w); - - util::image::buffer<1,float> img (res); - - // XXX: offset slightly to avoid origin artefacts in some basis functions - const auto OFFSET = util::vector2f { - (util::hash::mix ( seed) & 0xFFFF) / float (0xFFFF), - (util::hash::mix (util::hash::mix (seed)) & 0xFFFF) / float (0xFFFF) - } / f.frequency (); - - { - for (size_t y = 0; y < res.h; ++y) - for (size_t x = 0; x < res.w; ++x) { - util::point2f p (x, y); - img[{x, y}] = t ((p + OFFSET).redim ()); - } - } - - // working on the assumption that all octave images are based on summation, - // subtract the image with one less octave from our current image to leave - // us with the highest octave contribution only. this is hideously - // inefficient, but it's not an operation we care about in general. - if (single && f.octaves () != 1) { - auto oldoctaves = f.octaves (); - f.octaves (oldoctaves - 1); - auto prev = img.clone (); - - for (size_t y = 0; y < res.h; ++y) - for (size_t x = 0; x < res.w; ++x) { - util::point2f p (x, y); - prev[{x,y}] = t ((p + OFFSET).redim ()); - } - - CHECK_EQ (img.stride (), prev.stride ()); - for (size_t i = 0; i < img.size (); ++i) - img[i] -= prev[i]; - - f.octaves (oldoctaves); - } - - // rescale into the range [0, 1] - auto range = std::minmax_element (img.begin (), img.end ()); - auto inc = *range.first; - auto div = *range.second - *range.first; - - std::cerr << '[' << *range.first << ',' << *range.second << "]\n"; - std::transform (img.begin (), img.end (), img.begin (), [inc,div] (auto i) { return (i - inc) / div; }); - - // write the images to disk - util::pgm::write (img.cast (), std::cout); -}