1 /**************************************************************************/
2 /*  Copyright 2012 Tim Day                                                */
3 /*                                                                        */
4 /*  This file is part of Evolvotron                                       */
5 /*                                                                        */
6 /*  Evolvotron is free software: you can redistribute it and/or modify    */
7 /*  it under the terms of the GNU General Public License as published by  */
8 /*  the Free Software Foundation, either version 3 of the License, or     */
9 /*  (at your option) any later version.                                   */
10 /*                                                                        */
11 /*  Evolvotron is distributed in the hope that it will be useful,         */
12 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
13 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
14 /*  GNU General Public License for more details.                          */
15 /*                                                                        */
16 /*  You should have received a copy of the GNU General Public License     */
17 /*  along with Evolvotron.  If not, see <http://www.gnu.org/licenses/>.   */
18 /**************************************************************************/
19 
20 /*! \file
21   \brief Standalone renderer for evolvotron function files.
22 */
23 
24 #include "function_registry.h"
25 #include "mutatable_image.h"
26 #include "random.h"
27 
28 #include <boost/program_options.hpp>
29 
30 //! Application code
main(int argc,char * argv[])31 int main(int argc,char* argv[])
32 {
33   {
34     uint frames;
35     bool help;
36     bool jitter;
37     int multisample;
38     std::string output_filename;
39     std::string size;
40     bool verbose;
41 
42     boost::program_options::options_description options_desc("Options");
43     boost::program_options::positional_options_description pos_options_desc;
44     {
45       using namespace boost::program_options;
46       options_desc.add_options()
47 	("frames,f"     ,value<uint>(&frames)->default_value(1)    ,"Frames in an animation")
48 	("help,h"       ,bool_switch(&help)                        ,"Print command-line options help message and exit")
49 	("jitter,j"     ,bool_switch(&jitter)                      ,"Enable rendering jitter")
50 	("multisample,m",value<int>(&multisample)->default_value(1),"Multisampling grid (NxN)")
51 	("output,o"     ,value<std::string>(&output_filename)      ,"Output filename (.png or .ppm suffix).  (Or use first positional argument.)")
52 	("size,s"       ,value<std::string>(&size)->default_value("512x515"),"Generated image size")
53 	("verbose,v"    ,bool_switch(&verbose)                     ,"Log some details to stderr")
54 	;
55       pos_options_desc.add("output",1);
56     }
57 
58     boost::program_options::variables_map options;
59     boost::program_options::store
60       (
61        boost::program_options::command_line_parser(argc,argv)
62        .options(options_desc).positional(pos_options_desc).run()
63        ,options
64        );
65     boost::program_options::notify(options);
66 
67     if (help)
68       {
69 	std::cerr << options_desc;
70 	return 0;
71       }
72 
73     if (verbose)
74       std::clog.rdbuf(std::cerr.rdbuf());
75     else
76       std::clog.rdbuf(sink_ostream.rdbuf());
77 
78     //! \todo Could be done better maybe (set 'x' as input separator)
79     const std::string::size_type p=size.find("x");
80     if (p==std::string::npos || p==0 || p==size.size()-1)
81       {
82 	std::cerr << "--size option argument isn't in <width>x<height> format\n";
83 	return 1;
84       }
85     else
86       {
87 	size[p]=' ';
88       }
89     int width=512;
90     int height=512;
91     std::stringstream(size) >> width >> height;
92 
93     if (frames<1)
94       {
95 	std::cerr << "Must specify at least 1 frame (option: -f <frames>)\n";
96 	return 1;
97       }
98 
99     if (output_filename.empty())
100       {
101 	std::cerr << "Must specify an output filename\n";
102 	return 1;
103       }
104 
105     FunctionRegistry function_registry;
106 
107     std::string report;
108     const boost::shared_ptr<const MutatableImage> imagefn(MutatableImage::load_function(function_registry,std::cin,report));
109 
110     if (imagefn.get()==0)
111       {
112 	std::cerr << "evolvotron_render: Error: Function not loaded due to errors:\n" << report;
113 	return 1;
114       }
115     else if (!report.empty())
116       {
117 	std::cerr << "evolvotron_render: Warning: Function loaded with warnings:\n" << report;
118       }
119 
120     // Seed value pretty unimportant; only used for sample jitter.
121     Random01 r01(23);
122 
123     for (uint frame=0;frame<frames;frame++)
124       {
125 	std::vector<uint> image_data;
126 	image_data.reserve(width*height);
127 
128 	uint pixels=0;
129 	uint report=1;
130 	const uint reports=20;
131 	for (int row=0;row<height;row++)
132 	  for (int col=0;col<width;col++)
133 	    {
134 	      const XYZ v(imagefn->sampling_coordinate(col,row,frame,width,height,frames));
135 
136 	      const XYZ colour(imagefn->get_rgb(col,row,frame,width,height,frames,(jitter ? &r01 : 0),multisample));
137 
138 	      const uint col0=lrint(clamped(colour.x(),0.0,255.0));
139 	      const uint col1=lrint(clamped(colour.y(),0.0,255.0));
140 	      const uint col2=lrint(clamped(colour.z(),0.0,255.0));
141 
142 	      image_data.push_back(((col0<<16)|(col1<<8)|(col2)));
143 
144 	      pixels++;
145 	      if (pixels>=(report*width*height)/reports)
146 		{
147 		  std::clog << "[" << (100*report)/reports << "%]";
148 		  report++;
149 		}
150 	    }
151 	std::clog << "\n";
152 
153 	{
154 	  //! \todo If filename is "-", write PPM to stdout (QImage save only supports write-to-a-filenames though)
155 	  QString save_filename(QString::fromLocal8Bit(output_filename.c_str()));
156 
157 	  const char* save_format="PPM";
158 	  if (save_filename.toUpper().endsWith(".PPM"))
159 	    {
160 	      save_format="PPM";
161 	    }
162 	  else if (save_filename.toUpper().endsWith(".PNG"))
163 	    {
164 	      save_format="PNG";
165 	    }
166 	  else
167 	    {
168 	      std::cerr
169 		<< "evolvotron_render: Warning: Unrecognised file suffix.  File will be written in "
170 		<< save_format
171 		<< " format.\n";
172 	    }
173 
174 	  if (frames>1)
175 	    {
176 	      QString frame_component;
177 	      frame_component.sprintf(".f%06d",frame);
178 	      int insert_point=save_filename.lastIndexOf(QString("."));
179 	      if (insert_point==-1)
180 		{
181 		  save_filename.append(frame_component);
182 		}
183 	      else
184 		{
185 		  save_filename.insert(insert_point,frame_component);
186 		}
187 	    }
188 
189 	  QImage image
190 	    (
191 	     reinterpret_cast<uchar*>(&(image_data[0])),
192 	     width,
193 	     height,
194 	     QImage::Format_RGB32
195 	     );
196 
197 	  if (!image.save(save_filename,save_format))
198 	    {
199 	      std::cerr
200 		<< "evolvotron_render: Error: Couldn't save file "
201 		<< save_filename.toLocal8Bit().data()
202 		<< "\n";
203 	      return 1;
204 	    }
205 
206 	  std::clog
207 	    << "Wrote file "
208 	    << save_filename.toLocal8Bit().data()
209 	    << "\n";
210 	}
211       }
212   }
213 
214   return 0;
215 }
216