1 /**************************************************************************/
2 /*  Copyright 2009 Tim Day                                                */
3 /*                                                                        */
4 /*  This file is part of Fracplanet                                       */
5 /*                                                                        */
6 /*  Fracplanet 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 /*  Fracplanet 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 Fracplanet.  If not, see <http://www.gnu.org/licenses/>.   */
18 /**************************************************************************/
19 
20 /*! \file
21   \brief Interface (and implementation) for templated Image class.
22 */
23 
24 #ifndef _image_h_
25 #define _image_h_
26 
27 #include "common.h"
28 #include "rgb.h"
29 
30 class Progress;
31 
32 //! Class to support specialisation for particular pixel formats
33 template <typename T> class PixelTraits
34 {
35  public:
36 
37   typedef T ComputeType;
38   typedef T ScalarType;
scalar(const T & v)39   static ScalarType scalar(const T& v) {return v;}
40 };
41 
42 template<> class PixelTraits<uchar>
43 {
44  public:
45 
46   typedef float ComputeType;
47   typedef uchar ScalarType;
scalar(const uchar & v)48   static ScalarType scalar(const uchar& v) {return v;}
49 };
50 
51 template<> class PixelTraits<ushort>
52 {
53  public:
54 
55   typedef float ComputeType;
56   typedef ushort ScalarType;
scalar(const ushort & v)57   static ScalarType scalar(const ushort& v) {return v;}
58 };
59 
60 template<> class PixelTraits<ByteRGBA>
61 {
62  public:
63 
64   typedef FloatRGBA ComputeType;
65   typedef float ScalarType;
scalar(const ByteRGBA & v)66   static ScalarType scalar(const ByteRGBA& v) {return (static_cast<float>(v.r)+static_cast<float>(v.g)+static_cast<float>(v.b))/3.0f;}
67 };
68 
69 //! Class for 2D raster images of a specified type.
70 /*! Assumes explicit instantiation.
71   NB The base type just refers to storage allocated elsewhere.
72   Used to be gratuitously implemented using boost multiarray type,
73   but that was too inefficient
74   This is still pretty bad byt gcc 4.1 seems to do a very nice job with -O2.
75  */
76 template <typename T> class Raster
77 {
78  public:
79 
80   typedef typename PixelTraits<T>::ComputeType ComputeType;
81   typedef typename PixelTraits<T>::ScalarType ScalarType;
82 
scalar(const T & v)83   static const ScalarType scalar(const T& v) {return PixelTraits<T>::scalar(v);}
84 
Raster(uint w,uint h,uint p,T * d)85   Raster(uint w,uint h,uint p,T* d)
86     :_width(w)
87     ,_height(h)
88     ,_pitch(p)
89     ,_data(d)
90     ,_row_end(row_range(h),p)
91     ,_const_row_end(row_range(h),p)
92     {}
93 
~Raster()94   virtual ~Raster()
95     {}
96 
width()97   uint width() const
98     {
99       return _width;
100     }
101 
height()102   uint height() const
103     {
104       return _height;
105     }
106 
pitch()107   uint pitch() const
108     {
109       return _pitch;
110     }
111 
contiguous()112   bool contiguous() const
113     {
114       return (_width==_pitch);
115     }
116 
contiguous_size()117   uint contiguous_size() const
118     {
119       assert(contiguous());
120       return _width*_height;
121     }
122 
contiguous_begin()123   T* contiguous_begin()
124     {
125       assert(contiguous());
126       return _data;
127     }
128 
contiguous_begin()129   const T* contiguous_begin() const
130     {
131       assert(contiguous());
132       return _data;
133     }
134 
contiguous_end()135   T* contiguous_end()
136     {
137       assert(contiguous());
138       return _data+contiguous_size();
139     }
140 
contiguous_end()141   const T* contiguous_end() const
142     {
143       assert(contiguous());
144       return _data+contiguous_size();
145     }
146 
row(uint r)147   T* row(uint r)
148     {
149       return _data+r*_pitch;
150     }
151 
row(uint r)152   const T* row(uint r) const
153     {
154       return _data+r*_pitch;
155     }
156 
row_range(uint r)157   boost::iterator_range<T*> row_range(uint r)
158     {
159       T*const it=row(r);
160       return boost::iterator_range<T*>(it,it+_width);
161     }
162 
row_range(uint r)163   boost::iterator_range<const T*> row_range(uint r) const
164     {
165       const T*const it=row(r);
166       return boost::iterator_range<const T*>(it,it+_width);
167     }
168 
169   class RowIterator : public std::iterator<std::forward_iterator_tag, boost::iterator_range<T*> >
170     {
171     public:
172 
RowIterator(const boost::iterator_range<T * > & row,uint p)173       RowIterator(const boost::iterator_range<T*>& row, uint p)
174 	:_row(row)
175 	,_pitch(p)
176 	{}
177 
~RowIterator()178       ~RowIterator()
179 	{}
180 
181       RowIterator& operator=(const RowIterator& it)
182 	{
183 	  _row=it._row;
184 	  assert(_pitch==it._pitch);
185 	  return (*this);
186 	}
187 
188       bool operator==(const RowIterator& it) const
189 	{
190 	  return _row.begin()==it._row.begin();
191 	}
192 
193       bool operator!=(const RowIterator& it) const
194 	{
195 	  return _row.begin()!=it._row.begin();
196 	}
197 
198       RowIterator& operator++()
199 	{
200 	  _row=boost::iterator_range<T*>
201 	    (
202 	     _row.begin()+_pitch,
203 	     _row.end()+_pitch
204 	     );
205 	  return (*this);
206 	}
207 
208       RowIterator operator++(int)
209 	{
210 	  RowIterator tmp(*this);
211 	  _row=boost::iterator_range<T*>
212 	    (
213 	     _row.begin()+_pitch,
214 	     _row.end()+_pitch
215 	     );
216 	  return tmp;
217 	}
218 
219       boost::iterator_range<T*>& operator*()
220 	{
221 	  return _row;
222 	}
223 
224       boost::iterator_range<T*>* operator->()
225 	{
226 	  return &_row;
227 	}
228 
229     private:
230 
231       boost::iterator_range<T*> _row;
232 
233       const uint _pitch;
234     };
235 
row_begin()236   RowIterator row_begin()
237     {
238       return RowIterator(row_range(0),_pitch);
239     }
240 
row_end()241   RowIterator row_end()
242     {
243       return _row_end;
244     }
245 
246   class ConstRowIterator : public std::iterator<std::forward_iterator_tag, boost::iterator_range<const T*> >
247     {
248     public:
249 
ConstRowIterator(const boost::iterator_range<const T * > & row,uint p)250       ConstRowIterator(const boost::iterator_range<const T*>& row, uint p)
251 	:_row(row)
252 	,_pitch(p)
253 	{}
254 
~ConstRowIterator()255       ~ConstRowIterator()
256 	{}
257 
258       ConstRowIterator& operator=(const ConstRowIterator& it)
259 	{
260 	  _row=it._row;
261 	  assert(_pitch==it._pitch);
262 	  return (*this);
263 	}
264 
265       bool operator==(const ConstRowIterator& it) const
266 	{
267 	  return _row.begin()==it._row.begin();
268 	}
269 
270       bool operator!=(const ConstRowIterator& it) const
271 	{
272 	  return _row.begin()!=it._row.begin();
273 	}
274 
275       ConstRowIterator& operator++()
276 	{
277 	  _row=boost::iterator_range<const T*>
278 	    (
279 	     _row.begin()+_pitch,
280 	     _row.end()+_pitch
281 	     );
282 	  return (*this);
283 	}
284 
285       ConstRowIterator operator++(int)
286 	{
287 	  ConstRowIterator tmp(*this);
288 	  _row=boost::iterator_range<const T*>
289 	    (
290 	     _row.begin()+_pitch,
291 	     _row.end()+_pitch
292 	     );
293 	  return tmp;
294 	}
295 
296       boost::iterator_range<const T*>& operator*()
297 	{
298 	  return _row;
299 	}
300 
301       boost::iterator_range<const T*>* operator->()
302 	{
303 	  return &_row;
304 	}
305 
306     private:
307 
308       boost::iterator_range<const T*> _row;
309 
310       const uint _pitch;
311     };
312 
row_begin()313   ConstRowIterator row_begin() const
314     {
315       return ConstRowIterator(row_range(0),_pitch);
316     }
317 
row_end()318   ConstRowIterator row_end() const
319     {
320       return _const_row_end;
321     }
322 
323   //! Clear the image to a constant value.
324   void fill(const T& v);
325 
326   const ScalarType maximum_scalar_pixel_value() const;
327 
328   //! Fill a line segment on the given half-open range [x0,x1), interpolating between the two given values.
329   void scan(uint y,float x0,const ComputeType& v0,float x1,const ComputeType& v1);
330 
331   //! Variant scan, interpolates between two values then process them through function before
332   template <typename V> void scan(uint y,float x0,const V& v0,float x1,const V& v1,const boost::function<ComputeType (const V&)>& fn);
333 
334   bool write_ppmfile(const std::string&,Progress*) const;
335   bool write_pgmfile(const std::string&,Progress*) const;
336 
337  private:
338 
339   const uint _width;
340 
341   const uint _height;
342 
343   const uint _pitch;
344 
345   T*const _data;
346 
347   const RowIterator _row_end;
348 
349   const ConstRowIterator _const_row_end;
350 };
351 
352 /*! Scan x0 to x1 into image.
353   Pixel centres are at 0.5 , so x0=0.75 goes to pixel 1.
354   Rightmost pixel is at width()-0.5.
355 */
scan(uint y,float x0,const ComputeType & v0,float x1,const ComputeType & v1)356 template <typename T> inline void Raster<T>::scan(uint y,float x0,const ComputeType& v0,float x1,const ComputeType& v1)
357 {
358   assert(x0<=x1);
359 
360   if (x1<0.5f || width()-0.5f<x0) return;  // Early out for spans off edges
361 
362   const int ix_min=static_cast<int>(std::max(0.0f        ,ceilf(x0-0.5f)));
363   const int ix_max=static_cast<int>(std::min(width()-0.5f,floorf(x1-0.5f)));
364 
365   const ComputeType kv((v1-v0)/(x1-x0));
366 
367   T*const row_ptr=row(y);
368   for (int ix=ix_min;ix<=ix_max;ix++)
369     {
370       const ComputeType v(v0+kv*(ix+0.5f-x0));
371       row_ptr[ix]=static_cast<T>(v);
372     }
373 }
374 
scan(uint y,float x0,const V & v0,float x1,const V & v1,const boost::function<ComputeType (const V &)> & fn)375 template <typename T> template <typename V> inline void Raster<T>::scan(uint y,float x0,const V& v0,float x1,const V& v1,const boost::function<ComputeType (const V&)>& fn)
376 {
377   assert(x0<=x1);
378 
379   if (x1<0.5f || width()-0.5f<x0) return;  // Early out for spans off edges
380 
381   const int ix_min=static_cast<int>(std::max(0.0f        ,ceilf(x0-0.5f)));
382   const int ix_max=static_cast<int>(std::min(width()-0.5f,floorf(x1-0.5f)));
383 
384   const V kv((v1-v0)/(x1-x0));
385 
386   T*const row_ptr=row(y);
387   for (int ix=ix_min;ix<=ix_max;ix++)
388     {
389       const ComputeType v(fn(v0+kv*(ix+0.5f-x0)));
390       row_ptr[ix]=static_cast<T>(v);
391     }
392 }
393 
394 template <typename T> class ImageStorage
395 {
396  public:
ImageStorage(int n)397   ImageStorage(int n)
398     :_storage(new T[n])
399     {}
~ImageStorage()400   ~ImageStorage()
401     {}
402  protected:
403   boost::scoped_array<T> _storage;
404 };
405 
406 template <typename T> class Image : private ImageStorage<T>, public Raster<T>, public boost::noncopyable
407 {
408  public:
Image(uint w,uint h)409   Image(uint w,uint h)
410     :ImageStorage<T>(w*h)
411     ,Raster<T>(w,h,w,ImageStorage<T>::_storage.get())
412     {}
~Image()413   ~Image()
414     {}
415 };
416 
417 #endif
418