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