r348: fix linefeed problem
[ctsim.git] / libctsim / imagefile.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **      Name:         imagefile.cpp
5 **      Purpose:      Imagefile classes
6 **      Programmer:   Kevin Rosenberg
7 **      Date Started: June 2000
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2000 Kevin Rosenberg
11 **
12 **  $Id: imagefile.cpp,v 1.33 2001/01/02 16:02:13 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #include "ct.h"
29
30 const int ImageFile::FORMAT_INVALID = -1;
31 const int ImageFile::FORMAT_PGM = 0;
32 const int ImageFile::FORMAT_PGMASCII = 1;
33 #ifdef HAVE_PNG
34 const int ImageFile::FORMAT_PNG = 2;
35 const int ImageFile::FORMAT_PNG16 = 3;
36 #endif
37
38 const char* ImageFile::s_aszFormatName[] = 
39 {
40   {"pgm"},
41   {"pgmascii"},
42 #ifdef HAVE_PNG
43   {"png"},
44   {"png16"},
45 #endif
46 };
47
48 const char* ImageFile::s_aszFormatTitle[] = 
49 {
50   {"PGM"},
51   {"PGM ASCII"},
52   {"PNG"},
53   {"PNG 16-bit"},
54 };
55
56 const int ImageFile::s_iFormatCount = sizeof(s_aszFormatName) / sizeof(const char*);
57
58
59
60 F32Image::F32Image (int nx, int ny, int dataType)
61 : Array2dFile (nx, ny, sizeof(kfloat32), Array2dFile::PIXEL_FLOAT32, dataType)
62 {
63 }
64
65 F32Image::F32Image (void)
66 : Array2dFile()
67 {
68   setPixelFormat (Array2dFile::PIXEL_FLOAT32);
69   setPixelSize (sizeof(kfloat32));
70   setDataType (Array2dFile::DATA_TYPE_REAL);
71 }
72
73 F64Image::F64Image (int nx, int ny, int dataType)
74 : Array2dFile (nx, ny, sizeof(kfloat64), Array2dFile::PIXEL_FLOAT64, dataType)
75 {
76 }
77
78 F64Image::F64Image (void)
79 : Array2dFile ()
80 {
81   setPixelFormat (PIXEL_FLOAT64);
82   setPixelSize (sizeof(kfloat64));
83   setDataType (Array2dFile::DATA_TYPE_REAL);
84 }
85
86 void 
87 ImageFile::filterResponse (const char* const domainName, double bw, const char* const filterName, double filt_param, double dInputScale, double dOutputScale)
88 {
89   ImageFileArray v = getArray();
90   SignalFilter filter (filterName, domainName, bw, filt_param);
91   
92   int iXCenter, iYCenter;
93   if (isEven (m_nx))
94     iXCenter = m_nx / 2;
95   else
96     iXCenter = (m_nx - 1) / 2;
97   if (isEven (m_ny))
98     iYCenter = m_ny / 2;
99   else
100     iYCenter = (m_ny - 1) / 2;
101   
102   for (unsigned int ix = 0; ix < m_nx; ix++)
103     for (unsigned int iy = 0; iy < m_ny; iy++) {
104       long lD2 = ((ix - iXCenter) * (ix - iXCenter)) + ((iy - iYCenter) * (iy - iYCenter));
105       double r = ::sqrt (static_cast<double>(lD2)) * dInputScale;
106       v[ix][iy] = filter.response (r) * dOutputScale;
107     }
108 }
109
110 int
111 ImageFile::display (void) const
112 {
113   double pmin, pmax;
114   
115   getMinMax (pmin, pmax);
116   
117   return (displayScaling (1, pmin, pmax));
118 }
119
120 int 
121 ImageFile::displayScaling (const int scale, const ImageFileValue pmin, const ImageFileValue pmax) const
122 {
123   int nx = m_nx;
124   int ny = m_ny;
125   ImageFileArrayConst v = getArray();
126   if (v == NULL || nx == 0 || ny == 0)
127     return 0;
128   
129 #if HAVE_G2_H
130   int* pPens = new int [nx * ny * scale * scale ];
131   
132   double view_scale = 255 / (pmax - pmin);
133   int id_X11 = g2_open_X11 (nx * scale, ny * scale);
134   int grayscale[256];
135   for (int i = 0; i < 256; i++) {
136     double cval = i / 255.;
137     grayscale[i] = g2_ink (id_X11, cval, cval, cval);
138   }
139   
140   for (int iy = ny - 1; iy >= 0; iy--) {
141     int iRowPos = ((ny - 1 - iy) * scale) * (nx * scale);
142     for (int ix = 0; ix < nx; ix++) {
143       int cval = static_cast<int>((v[ix][iy] - pmin) * view_scale);
144       if (cval < 0)  
145         cval = 0;
146       else if (cval > 255) 
147         cval = 255;
148       for (int sy = 0; sy < scale; sy++)
149         for (int sx = 0; sx < scale; sx++)
150           pPens[iRowPos+(sy * nx * scale)+(sx + (ix * scale))] = grayscale[cval];
151     }
152   }
153   
154   g2_image (id_X11, 0., 0., nx * scale, ny * scale, pPens);
155   
156   delete pPens;
157   return (id_X11);
158 #else
159   return 0;
160 #endif
161 }
162
163
164
165 // ImageFile::comparativeStatistics    Calculate comparative stats
166 //
167 // OUTPUT
168 //   d   Normalized root mean squared distance measure
169 //   r   Normalized mean absolute distance measure
170 //   e   Worst case distance measure
171 //
172 // REFERENCES
173 //  G.T. Herman, Image Reconstruction From Projections, 1980
174
175 bool
176 ImageFile::comparativeStatistics (const ImageFile& imComp, double& d, double& r, double& e) const
177 {
178   if (imComp.nx() != m_nx && imComp.ny() != m_ny) {
179     sys_error (ERR_WARNING, "Image sizes differ [ImageFile::comparativeStatistics]");
180     return false;
181   }
182   ImageFileArrayConst v = getArray();
183   if (v == NULL || m_nx == 0 || m_ny == 0)
184     return false;
185   
186   ImageFileArrayConst vComp = imComp.getArray();
187   
188   double myMean = 0.;
189   for (unsigned int ix = 0; ix < m_nx; ix++) {
190     for (unsigned int iy = 0; iy < m_ny; iy++) {
191       myMean += v[ix][iy];
192     }
193   }
194   myMean /= (m_nx * m_ny);
195   
196   double sqErrorSum = 0.;
197   double absErrorSum = 0.;
198   double sqDiffFromMeanSum = 0.;
199   double absValueSum = 0.;
200   for (unsigned int ix2 = 0; ix2 < m_nx; ix2++) {
201     for (unsigned int iy = 0; iy < m_ny; iy++) {
202       double diff = v[ix2][iy] - vComp[ix2][iy];
203       sqErrorSum += diff * diff;
204       absErrorSum += fabs(diff);
205       double diffFromMean = v[ix2][iy] - myMean;
206       sqDiffFromMeanSum += diffFromMean * diffFromMean;
207       absValueSum += fabs(v[ix2][iy]);
208     }
209   }
210   
211   d = ::sqrt (sqErrorSum / sqDiffFromMeanSum);
212   r = absErrorSum / absValueSum;
213   
214   int hx = m_nx / 2;
215   int hy = m_ny / 2;
216   double eMax = -1;
217   for (int ix3 = 0; ix3 < hx; ix3++) {
218     for (int iy = 0; iy < hy; iy++) {
219       double avgPixel = 0.25 * (v[2*ix3][2*iy] + v[2*ix3+1][2*iy] + v[2*ix3][2*iy+1] + v[2*ix3+1][2*iy+1]);
220       double avgPixelComp = 0.25 * (vComp[2*ix3][2*iy] + vComp[2*ix3+1][2*iy] + vComp[2*ix3][2*iy+1] + vComp[2*ix3+1][2*iy+1]);
221       double error = fabs (avgPixel - avgPixelComp);
222       if (error > eMax)
223         eMax = error;
224     }
225   }
226   
227   e = eMax;
228   
229   return true;
230 }
231
232
233 bool
234 ImageFile::printComparativeStatistics (const ImageFile& imComp, std::ostream& os) const
235 {
236   double d, r, e;
237   
238   if (comparativeStatistics (imComp, d, r, e)) {
239     os << "  Normalized root mean squared distance (d): " << d << std::endl;
240     os << "      Normalized mean absolute distance (r): " << r << std::endl;
241     os << "Worst case distance (2x2 pixel average) (e): " << e << std::endl;
242     return true;
243   }
244   return false;
245 }
246
247
248 void
249 ImageFile::printStatistics (std::ostream& os) const
250 {
251   double min, max, mean, mode, median, stddev;
252   
253   statistics (min, max, mean, mode, median, stddev);
254   if (isComplex())
255     os << "Real Component Statistics" << std::endl;
256   
257   os << "   min: " << min << std::endl;
258   os << "   max: " << max << std::endl;
259   os << "  mean: " << mean << std::endl;
260   os << "  mode: " << mode << std::endl;
261   os << "median: " << median << std::endl;
262   os << "stddev: " << stddev << std::endl;
263   
264   if (isComplex()) {
265     statistics (getImaginaryArray(), min, max, mean, mode, median, stddev);
266     os << std::endl << "Imaginary Component Statistics" << std::endl;
267     os << "   min: " << min << std::endl;
268     os << "   max: " << max << std::endl;
269     os << "  mean: " << mean << std::endl;
270     os << "  mode: " << mode << std::endl;
271     os << "median: " << median << std::endl;
272     os << "stddev: " << stddev << std::endl;
273   }
274 }
275
276
277 void
278 ImageFile::statistics (double& min, double& max, double& mean, double& mode, double& median, double& stddev) const
279 {
280   ImageFileArrayConst v = getArray();
281   statistics (v, min, max, mean, mode, median, stddev);
282 }
283
284
285 void
286 ImageFile::statistics (ImageFileArrayConst v, double& min, double& max, double& mean, double& mode, double& median, double& stddev) const
287 {
288   int nx = m_nx;
289   int ny = m_ny;
290   
291   if (v == NULL || nx == 0 || ny == 0)
292     return;
293   
294   std::vector<double> vecImage;
295   int iVec = 0;
296   vecImage.resize (nx * ny);
297   for (int ix = 0; ix < nx; ix++) {
298     for (int iy = 0; iy < ny; iy++)
299       vecImage[iVec++] = v[ix][iy];
300   }
301   
302   vectorNumericStatistics (vecImage, nx * ny, min, max, mean, mode, median, stddev);
303 }
304
305 void
306 ImageFile::getMinMax (double& min, double& max) const
307 {
308   int nx = m_nx;
309   int ny = m_ny;
310   ImageFileArrayConst v = getArray();
311   
312   if (v == NULL || nx == 0 || ny == 0)
313     return;
314   
315   min = v[0][0];
316   max = v[0][0];
317   for (int ix = 0; ix < nx; ix++) {
318     for (int iy = 0; iy < ny; iy++) {
319       if (v[ix][iy] > max)
320         max = v[ix][iy];
321       if (v[ix][iy] < min)
322         min = v[ix][iy];
323     }
324   }
325 }
326
327 bool
328 ImageFile::convertRealToComplex ()
329 {
330   if (dataType() != Array2dFile::DATA_TYPE_REAL)
331     return false;
332   
333   if (! reallocRealToComplex())
334     return false;
335   
336   ImageFileArray vImag = getImaginaryArray();
337   for (unsigned int ix = 0; ix < m_nx; ix++) {
338     ImageFileColumn vCol = vImag[ix];
339     for (unsigned int iy = 0; iy < m_ny; iy++)
340       *vCol++ = 0;
341   }
342   
343   return true;
344 }
345
346 bool
347 ImageFile::convertComplexToReal ()
348 {
349   if (dataType() != Array2dFile::DATA_TYPE_COMPLEX)
350     return false;
351   
352   ImageFileArray vReal = getArray();
353   ImageFileArray vImag = getImaginaryArray();
354   for (unsigned int ix = 0; ix < m_nx; ix++) {
355     ImageFileColumn vRealCol = vReal[ix];
356     ImageFileColumn vImagCol = vImag[ix];
357     for (unsigned int iy = 0; iy < m_ny; iy++) {
358       CTSimComplex c (*vRealCol, *vImagCol);
359       *vRealCol++ = std::abs (c);
360       vImagCol++;
361     }
362   }
363   
364   return reallocComplexToReal();
365 }
366
367 bool
368 ImageFile::subtractImages (const ImageFile& rRHS, ImageFile& result) const
369 {
370   if (m_nx != rRHS.nx() || m_ny != rRHS.ny() || m_nx != result.nx() || m_ny != result.ny()) {
371     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::subtractImage]");
372     return false;
373   }
374   
375   if (isComplex() || rRHS.isComplex() && ! result.isComplex())
376     result.convertRealToComplex();
377   
378   ImageFileArrayConst vLHS = getArray();
379   ImageFileArrayConst vLHSImag = getImaginaryArray();
380   ImageFileArrayConst vRHS = rRHS.getArray();
381   ImageFileArrayConst vRHSImag = rRHS.getImaginaryArray();
382   ImageFileArray vResult = result.getArray();
383   ImageFileArray vResultImag = result.getImaginaryArray();
384   
385   for (unsigned int ix = 0; ix < m_nx; ix++) {
386     for (unsigned int iy = 0; iy < m_ny; iy++) {
387       vResult[ix][iy] = vLHS[ix][iy] - vRHS[ix][iy];
388       if (result.isComplex()) {
389         vResultImag[ix][iy] = 0;
390         if (isComplex())
391           vResultImag[ix][iy] += vLHSImag[ix][iy];
392         if (rRHS.isComplex())
393           vResultImag[ix][iy] -= vRHSImag[ix][iy];
394       }
395     }
396   }
397   
398   return true;
399 }
400
401 bool
402 ImageFile::addImages (const ImageFile& rRHS, ImageFile& result) const
403 {
404   if (m_nx != rRHS.nx() || m_ny != rRHS.ny() || m_nx != result.nx() || m_ny != result.ny()) {
405     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::subtractImage]");
406     return false;
407   }
408   
409   if (isComplex() || rRHS.isComplex() && ! result.isComplex())
410     result.convertRealToComplex();
411   
412   ImageFileArrayConst vLHS = getArray();
413   ImageFileArrayConst vLHSImag = getImaginaryArray();
414   ImageFileArrayConst vRHS = rRHS.getArray();
415   ImageFileArrayConst vRHSImag = rRHS.getImaginaryArray();
416   ImageFileArray vResult = result.getArray();
417   ImageFileArray vResultImag = result.getImaginaryArray();
418   
419   for (unsigned int ix = 0; ix < m_nx; ix++) {
420     for (unsigned int iy = 0; iy < m_ny; iy++) {
421       vResult[ix][iy] = vLHS[ix][iy] + vRHS[ix][iy];
422       if (result.isComplex()) {
423         vResultImag[ix][iy] = 0;
424         if (isComplex())
425           vResultImag[ix][iy] += vLHSImag[ix][iy];
426         if (rRHS.isComplex())
427           vResultImag[ix][iy] += vRHSImag[ix][iy];
428       }
429     }
430   }
431   
432   return true;
433 }
434
435 bool
436 ImageFile::multiplyImages (const ImageFile& rRHS, ImageFile& result) const
437 {
438   if (m_nx != rRHS.nx() || m_ny != rRHS.ny() || m_nx != result.nx() || m_ny != result.ny()) {
439     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::subtractImage]");
440     return false;
441   }
442   
443   if (isComplex() || rRHS.isComplex() && ! result.isComplex())
444     result.convertRealToComplex();
445   
446   ImageFileArrayConst vLHS = getArray();
447   ImageFileArrayConst vLHSImag = getImaginaryArray();
448   ImageFileArrayConst vRHS = rRHS.getArray();
449   ImageFileArrayConst vRHSImag = rRHS.getImaginaryArray();
450   ImageFileArray vResult = result.getArray();
451   ImageFileArray vResultImag = result.getImaginaryArray();
452   
453   for (unsigned int ix = 0; ix < m_nx; ix++) {
454     for (unsigned int iy = 0; iy < m_ny; iy++) {
455       if (result.isComplex()) {
456         double dImag = 0;
457         if (isComplex())
458           dImag = vLHSImag[ix][iy];
459         std::complex<double> cLHS (vLHS[ix][iy], dImag);
460         dImag = 0;
461         if (rRHS.isComplex())
462           dImag = vRHSImag[ix][iy];
463         std::complex<double> cRHS (vRHS[ix][iy], dImag);
464         std::complex<double> cResult = cLHS * cRHS;
465         vResult[ix][iy] = cResult.real();
466         vResultImag[ix][iy] = cResult.imag();
467       } else
468         vResult[ix][iy] = vLHS[ix][iy] * vRHS[ix][iy];
469     }
470   }
471   
472   
473   return true;
474 }
475
476 bool
477 ImageFile::divideImages (const ImageFile& rRHS, ImageFile& result) const
478 {
479   if (m_nx != rRHS.nx() || m_ny != rRHS.ny() || m_nx != result.nx() || m_ny != result.ny()) {
480     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::subtractImage]");
481     return false;
482   }
483   
484   if (isComplex() || rRHS.isComplex() && ! result.isComplex())
485     result.convertRealToComplex();
486   
487   ImageFileArrayConst vLHS = getArray();
488   ImageFileArrayConst vLHSImag = getImaginaryArray();
489   ImageFileArrayConst vRHS = rRHS.getArray();
490   ImageFileArrayConst vRHSImag = rRHS.getImaginaryArray();
491   ImageFileArray vResult = result.getArray();
492   ImageFileArray vResultImag = result.getImaginaryArray();
493   
494   for (unsigned int ix = 0; ix < m_nx; ix++) {
495     for (unsigned int iy = 0; iy < m_ny; iy++) {
496       if (result.isComplex()) {
497         double dImag = 0;
498         if (isComplex())
499           dImag = vLHSImag[ix][iy];
500         std::complex<double> cLHS (vLHS[ix][iy], dImag);
501         dImag = 0;
502         if (rRHS.isComplex())
503           dImag = vRHSImag[ix][iy];
504         std::complex<double> cRHS (vRHS[ix][iy], dImag);
505         std::complex<double> cResult = cLHS / cRHS;
506         vResult[ix][iy] = cResult.real();
507         vResultImag[ix][iy] = cResult.imag();
508       } else {
509         if (vRHS != 0)
510           vResult[ix][iy] = vLHS[ix][iy] / vRHS[ix][iy];
511         else
512           vResult[ix][iy] = 0;
513       }
514     }
515   }
516   
517   return true;
518 }
519
520
521 bool
522 ImageFile::invertPixelValues (ImageFile& result) const
523 {
524   if (m_nx != result.nx() || m_ny != result.ny()) {
525     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
526     return false;
527   }
528   
529   if (isComplex() && ! result.isComplex())
530     result.convertRealToComplex();
531   
532   ImageFileArrayConst vLHS = getArray();
533   ImageFileArray vResult = result.getArray();
534   
535   for (unsigned int ix = 0; ix < m_nx; ix++) {
536     ImageFileColumnConst in = vLHS[ix];
537     ImageFileColumn out = vResult[ix];
538     for (unsigned int iy = 0; iy < m_ny; iy++)
539       *out++ = - *in++;
540   }
541   
542   return true;
543 }
544
545 bool
546 ImageFile::sqrt (ImageFile& result) const
547 {
548   if (m_nx != result.nx() || m_ny != result.ny()) {
549     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
550     return false;
551   }
552   
553   if (isComplex() && ! result.isComplex())
554     result.convertRealToComplex();
555   
556   bool bComplexOutput = result.isComplex();
557   ImageFileArrayConst vLHS = getArray();
558   if (! bComplexOutput)   // check if should convert to complex output
559     for (unsigned int ix = 0; ix < m_nx; ix++)
560       for (unsigned int iy = 0; iy < m_ny; iy++)
561         if (! bComplexOutput && vLHS[ix][iy] < 0) {
562           result.convertRealToComplex();
563           bComplexOutput = true;
564           break;
565         }
566         
567         ImageFileArrayConst vLHSImag = getImaginaryArray();
568         ImageFileArray vResult = result.getArray();
569         ImageFileArray vResultImag = result.getImaginaryArray();
570         
571         for (unsigned int ix = 0; ix < m_nx; ix++) {
572           for (unsigned int iy = 0; iy < m_ny; iy++) {
573             if (result.isComplex()) {
574               double dImag = 0;
575               if (isComplex())
576                 dImag = vLHSImag[ix][iy];
577               std::complex<double> cLHS (vLHS[ix][iy], dImag);
578               std::complex<double> cResult = std::sqrt(cLHS);
579               vResult[ix][iy] = cResult.real();
580               vResultImag[ix][iy] = cResult.imag();
581             } else
582               vResult[ix][iy] = ::sqrt (vLHS[ix][iy]);
583           }
584         }
585         
586         
587         return true;
588 }
589
590 bool
591 ImageFile::log (ImageFile& result) const
592 {
593   if (m_nx != result.nx() || m_ny != result.ny()) {
594     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
595     return false;
596   }
597   
598   if (isComplex() && ! result.isComplex())
599     result.convertRealToComplex();
600   
601   ImageFileArrayConst vLHS = getArray();
602   ImageFileArrayConst vLHSImag = getImaginaryArray();
603   ImageFileArray vResult = result.getArray();
604   ImageFileArray vResultImag = result.getImaginaryArray();
605   
606   for (unsigned int ix = 0; ix < m_nx; ix++) {
607     for (unsigned int iy = 0; iy < m_ny; iy++) {
608       if (result.isComplex()) {
609         double dImag = 0;
610         if (isComplex())
611           dImag = vLHSImag[ix][iy];
612         std::complex<double> cLHS (vLHS[ix][iy], dImag);
613         std::complex<double> cResult = std::log (cLHS);
614         vResult[ix][iy] = cResult.real();
615         vResultImag[ix][iy] = cResult.imag();
616       } else
617         vResult[ix][iy] = ::log (vLHS[ix][iy]);
618     }
619   }
620   
621   
622   return true;
623 }
624
625 bool
626 ImageFile::exp (ImageFile& result) const
627 {
628   if (m_nx != result.nx() || m_ny != result.ny()) {
629     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
630     return false;
631   }
632   
633   if (isComplex() && ! result.isComplex())
634     result.convertRealToComplex();
635   
636   ImageFileArrayConst vLHS = getArray();
637   ImageFileArrayConst vLHSImag = getImaginaryArray();
638   ImageFileArray vResult = result.getArray();
639   ImageFileArray vResultImag = result.getImaginaryArray();
640   
641   for (unsigned int ix = 0; ix < m_nx; ix++) {
642     for (unsigned int iy = 0; iy < m_ny; iy++) {
643       if (result.isComplex()) {
644         double dImag = 0;
645         if (isComplex())
646           dImag = vLHSImag[ix][iy];
647         std::complex<double> cLHS (vLHS[ix][iy], dImag);
648         std::complex<double> cResult = std::exp (cLHS);
649         vResult[ix][iy] = cResult.real();
650         vResultImag[ix][iy] = cResult.imag();
651       } else
652         vResult[ix][iy] = ::exp (vLHS[ix][iy]);
653     }
654   }
655   
656   
657   return true;
658 }
659
660 bool
661 ImageFile::scaleImage (ImageFile& result) const
662 {
663   unsigned int nx = m_nx;
664   unsigned int ny = m_ny;
665   unsigned int newNX = result.nx();
666   unsigned int newNY = result.ny();
667   
668   double dXScale = static_cast<double>(newNX) / static_cast<double>(nx);
669   double dYScale = static_cast<double>(newNY) / static_cast<double>(ny);
670   
671   if (isComplex() && ! result.isComplex())
672     result.convertRealToComplex();
673   
674   ImageFileArrayConst vReal = getArray();
675   ImageFileArrayConst vImag = getImaginaryArray();
676   ImageFileArray vResult = result.getArray();
677   ImageFileArray vResultImag = result.getImaginaryArray();
678   
679   for (unsigned int ix = 0; ix < newNX; ix++) {
680     for (unsigned int iy = 0; iy < newNY; iy++) {
681       double dXPos = ix / dXScale;
682       double dYPos = iy / dYScale;
683       unsigned int scaleNX = static_cast<unsigned int> (dXPos);
684       unsigned int scaleNY = static_cast<unsigned int> (dYPos);
685       double dXFrac = dXPos - scaleNX;
686       double dYFrac = dYPos - scaleNY;
687       if (scaleNX >= nx - 1 || scaleNY >= ny - 1) {
688         vResult[ix][iy] = vReal[scaleNX][scaleNY];
689         if (result.isComplex()) {
690           if (isComplex())
691             vResultImag[ix][iy] = vImag[scaleNX][scaleNY];
692           else
693             vResultImag[ix][iy] = 0;
694         }
695       } else {
696         vResult[ix][iy] = vReal[scaleNX][scaleNY] + 
697           dXFrac * (vReal[scaleNX+1][scaleNY] - vReal[scaleNX][scaleNY]) + 
698           dYFrac * (vReal[scaleNX][scaleNY+1] - vReal[scaleNX][scaleNY]);
699         if (result.isComplex()) {
700           if (isComplex())
701             vResultImag[ix][iy] = vImag[scaleNX][scaleNY] + 
702             dXFrac * (vImag[scaleNX+1][scaleNY] - vImag[scaleNX][scaleNY]) + 
703             dYFrac * (vImag[scaleNX][scaleNY+1] - vImag[scaleNX][scaleNY]);
704           else
705             vResultImag[ix][iy] = 0;
706         }
707       }
708     }
709   }
710   
711   return true;
712 }
713
714 #ifdef HAVE_FFTW
715 bool
716 ImageFile::fft (ImageFile& result) const
717 {
718   if (m_nx != result.nx() || m_ny != result.ny()) {
719     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
720     return false;
721   }
722   
723   if (result.dataType() == Array2dFile::DATA_TYPE_REAL) {
724     if (! result.convertRealToComplex ())
725       return false;
726   }
727   
728   fftw_complex* in = new fftw_complex [m_nx * m_ny];
729   
730   ImageFileArrayConst vReal = getArray();
731   ImageFileArrayConst vImag = getImaginaryArray();
732   
733   unsigned int ix, iy;
734   unsigned int iArray = 0;
735   for (ix = 0; ix < m_nx; ix++)
736     for (iy = 0; iy < m_ny; iy++) {
737       in[iArray].re = vReal[ix][iy];
738       if (isComplex())
739         in[iArray].im = vImag[ix][iy];
740       else
741         in[iArray].im = 0;
742       iArray++;
743     }
744     
745     fftwnd_plan plan = fftw2d_create_plan (m_nx, m_ny, FFTW_FORWARD, FFTW_IN_PLACE);
746     
747     fftwnd_one (plan, in, NULL);
748     
749     ImageFileArray vRealResult = result.getArray();
750     ImageFileArray vImagResult = result.getImaginaryArray();
751     iArray = 0;
752     unsigned int iScale = m_nx * m_ny;
753     for (ix = 0; ix < m_nx; ix++)
754       for (iy = 0; iy < m_ny; iy++) {
755         vRealResult[ix][iy] = in[iArray].re / iScale;
756         vImagResult[ix][iy] = in[iArray].im / iScale;
757         iArray++;
758       }
759       
760       fftwnd_destroy_plan (plan);
761       delete in;
762       
763       
764       Fourier::shuffleFourierToNaturalOrder (result);
765       
766       return true;
767 }
768
769
770 bool
771 ImageFile::ifft (ImageFile& result) const
772 {
773   if (m_nx != result.nx() || m_ny != result.ny()) {
774     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
775     return false;
776   }
777   
778   if (result.dataType() == Array2dFile::DATA_TYPE_REAL) {
779     if (! result.convertRealToComplex ())
780       return false;
781   }
782   
783   ImageFileArrayConst vReal = getArray();
784   ImageFileArrayConst vImag = getImaginaryArray();
785   ImageFileArray vRealResult = result.getArray();
786   ImageFileArray vImagResult = result.getImaginaryArray();
787   unsigned int ix, iy;
788   for (ix = 0; ix < m_nx; ix++)
789     for (iy = 0; iy < m_ny; iy++) {
790       vRealResult[ix][iy] = vReal[ix][iy];
791       if (isComplex()) 
792         vImagResult[ix][iy] = vImag[ix][iy];
793       else
794         vImagResult[ix][iy] = 0;
795     }
796     
797     Fourier::shuffleNaturalToFourierOrder (result);
798     
799     fftw_complex* in = new fftw_complex [m_nx * m_ny];
800     
801     unsigned int iArray = 0;
802     for (ix = 0; ix < m_nx; ix++)
803       for (iy = 0; iy < m_ny; iy++) {
804         in[iArray].re = vRealResult[ix][iy];
805         in[iArray].im = vImagResult[ix][iy];
806         iArray++;
807       }
808       
809       fftwnd_plan plan = fftw2d_create_plan (m_nx, m_ny, FFTW_BACKWARD, FFTW_IN_PLACE);
810       
811       fftwnd_one (plan, in, NULL);
812       
813       iArray = 0;
814       for (ix = 0; ix < m_nx; ix++)
815         for (iy = 0; iy < m_ny; iy++) {
816           vRealResult[ix][iy] = in[iArray].re;
817           vImagResult[ix][iy] = in[iArray].im;
818           iArray++;
819         }
820         
821         fftwnd_destroy_plan (plan);
822         
823         delete in;
824         
825         return true;
826 }
827 #endif // HAVE_FFTW
828
829
830
831 bool
832 ImageFile::fourier (ImageFile& result) const
833 {
834   if (m_nx != result.nx() || m_ny != result.ny()) {
835     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
836     return false;
837   }
838   
839   if (! result.isComplex())
840     if (! result.convertRealToComplex ())
841       return false;
842     
843     ImageFileArrayConst vLHS = getArray();
844     ImageFileArrayConst vLHSImag = getImaginaryArray();
845     ImageFileArray vRealResult = result.getArray();
846     ImageFileArray vImagResult = result.getImaginaryArray();
847     
848     unsigned int ix, iy;
849     
850     // alloc output matrix
851     CTSimComplex** complexOut = new CTSimComplex* [m_nx];
852     for (ix = 0; ix < m_nx; ix++)
853       complexOut[ix] = new CTSimComplex [m_ny];
854     
855     // fourier each x column
856     CTSimComplex* pY = new CTSimComplex [m_ny];
857     for (ix = 0; ix < m_nx; ix++) {
858       for (iy = 0; iy < m_ny; iy++) {
859         double dImag = 0;
860         if (isComplex())
861           dImag = vLHSImag[ix][iy];
862         pY[iy] = std::complex<double>(vLHS[ix][iy], dImag);
863       } 
864       ProcessSignal::finiteFourierTransform (pY, complexOut[ix], m_ny,  ProcessSignal::FORWARD);
865     }
866     delete [] pY;
867     
868     // fourier each y row
869     CTSimComplex* pX = new CTSimComplex [m_nx];
870     CTSimComplex* complexOutRow = new CTSimComplex [m_nx];
871     for (iy = 0; iy < m_ny; iy++) {
872       for (ix = 0; ix < m_nx; ix++)
873         pX[ix] = complexOut[ix][iy];
874       ProcessSignal::finiteFourierTransform (pX, complexOutRow, m_nx, ProcessSignal::FORWARD);
875       for (ix = 0; ix < m_nx; ix++)
876         complexOut[ix][iy] = complexOutRow[ix];
877     }
878     delete [] pX;
879     delete [] complexOutRow;
880     
881     for (ix = 0; ix < m_nx; ix++)
882       for (iy = 0; iy < m_ny; iy++) {
883         vRealResult[ix][iy] = complexOut[ix][iy].real();
884         vImagResult[ix][iy] = complexOut[ix][iy].imag();
885       }
886       
887       Fourier::shuffleFourierToNaturalOrder (result);
888       
889       // delete complexOut matrix
890       for (ix = 0; ix < m_nx; ix++)
891         delete [] complexOut[ix];
892       delete [] complexOut;
893       
894       return true;
895 }
896
897 bool
898 ImageFile::inverseFourier (ImageFile& result) const
899 {
900   if (m_nx != result.nx() || m_ny != result.ny()) {
901     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
902     return false;
903   }
904   
905   if (result.dataType() == Array2dFile::DATA_TYPE_REAL) {
906     if (! result.convertRealToComplex ())
907       return false;
908   }
909   
910   ImageFileArrayConst vLHSReal = getArray();
911   ImageFileArrayConst vLHSImag = getImaginaryArray();
912   ImageFileArray vRealResult = result.getArray();
913   ImageFileArray vImagResult = result.getImaginaryArray();
914   
915   unsigned int ix, iy;
916   // alloc 2d complex output matrix
917   CTSimComplex** complexOut = new CTSimComplex* [m_nx];
918   for (ix = 0; ix < m_nx; ix++)
919     complexOut[ix] = new CTSimComplex [m_ny];
920   
921   // put input image into result
922   for (ix = 0; ix < m_nx; ix++)
923     for (iy = 0; iy < m_ny; iy++) {
924       vRealResult[ix][iy] = vLHSReal[ix][iy];
925       if (isComplex())
926         vImagResult[ix][iy] = vLHSImag[ix][iy];
927       else
928         vImagResult[ix][iy] = 0;
929     }
930     
931     Fourier::shuffleNaturalToFourierOrder (result);
932     
933     // ifourier each x column
934     CTSimComplex* pCol = new CTSimComplex [m_ny];
935     for (ix = 0; ix < m_nx; ix++) {
936       for (iy = 0; iy < m_ny; iy++) {
937         pCol[iy] = std::complex<double> (vRealResult[ix][iy], vImagResult[ix][iy]);
938       }
939       ProcessSignal::finiteFourierTransform (pCol, complexOut[ix], m_ny,  ProcessSignal::BACKWARD);
940     }
941     delete [] pCol;
942     
943     // ifourier each y row
944     CTSimComplex* complexInRow = new CTSimComplex [m_nx];
945     CTSimComplex* complexOutRow = new CTSimComplex [m_nx];
946     for (iy = 0; iy < m_ny; iy++) {
947       for (ix = 0; ix < m_nx; ix++)
948         complexInRow[ix] = complexOut[ix][iy];
949       ProcessSignal::finiteFourierTransform (complexInRow, complexOutRow, m_nx, ProcessSignal::BACKWARD);
950       for (ix = 0; ix < m_nx; ix++)
951         complexOut[ix][iy] = complexOutRow[ix];
952     }
953     delete [] complexInRow;
954     delete [] complexOutRow;
955     
956     for (ix = 0; ix < m_nx; ix++)
957       for (iy = 0; iy < m_ny; iy++) {
958         vRealResult[ix][iy] = complexOut[ix][iy].real();
959         vImagResult[ix][iy] = complexOut[ix][iy].imag();
960       }
961       
962       // delete complexOut matrix
963       for (ix = 0; ix < m_nx; ix++)
964         delete [] complexOut[ix];
965       delete [] complexOut;
966       
967       return true;
968 }
969
970
971 bool
972 ImageFile::magnitude (ImageFile& result) const
973 {
974   if (m_nx != result.nx() || m_ny != result.ny()) {
975     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
976     return false;
977   }
978   
979   ImageFileArray vReal = getArray();
980   ImageFileArray vImag = getImaginaryArray();
981   ImageFileArray vRealResult = result.getArray();
982   
983   for (unsigned int ix = 0; ix < m_nx; ix++)
984     for (unsigned int iy = 0; iy < m_ny; iy++) {
985       if (isComplex()) 
986         vRealResult[ix][iy] = ::sqrt (vReal[ix][iy] * vReal[ix][iy] + vImag[ix][iy] * vImag[ix][iy]);
987       else
988         vRealResult[ix][iy] = vReal[ix][iy];
989     }
990     
991     if (result.isComplex())
992       result.convertComplexToReal();
993     
994     return true;
995 }
996
997 bool
998 ImageFile::phase (ImageFile& result) const
999 {
1000   if (m_nx != result.nx() || m_ny != result.ny()) {
1001     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
1002     return false;
1003   }
1004   
1005   ImageFileArray vReal = getArray();
1006   ImageFileArray vImag = getImaginaryArray();
1007   ImageFileArray vRealResult = result.getArray();
1008   
1009   for (unsigned int ix = 0; ix < m_nx; ix++)
1010     for (unsigned int iy = 0; iy < m_ny; iy++) {
1011       if (isComplex())
1012         vRealResult[ix][iy] = ::atan2 (vImag[ix][iy], vReal[ix][iy]);
1013       else
1014         vRealResult[ix][iy] = 0;
1015     }
1016     
1017     if (result.isComplex())
1018       result.convertComplexToReal();
1019     
1020     return true;
1021 }
1022
1023 bool
1024 ImageFile::square (ImageFile& result) const
1025 {
1026   if (m_nx != result.nx() || m_ny != result.ny()) {
1027     sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::invertPixelValues]");
1028     return false;
1029   }
1030   
1031   if (isComplex() && ! result.isComplex())
1032     result.convertRealToComplex();
1033   
1034   ImageFileArrayConst vLHS = getArray();
1035   ImageFileArrayConst vLHSImag = getImaginaryArray();
1036   ImageFileArray vResult = result.getArray();
1037   ImageFileArray vResultImag = result.getImaginaryArray();
1038   
1039   for (unsigned int ix = 0; ix < m_nx; ix++) {
1040     for (unsigned int iy = 0; iy < m_ny; iy++) {
1041       if (result.isComplex()) {
1042         double dImag = 0;
1043         if (isComplex())
1044           dImag = vLHSImag[ix][iy];
1045         std::complex<double> cLHS (vLHS[ix][iy], dImag);
1046         std::complex<double> cResult = cLHS * cLHS;
1047         vResult[ix][iy] = cResult.real();
1048         vResultImag[ix][iy] = cResult.imag();
1049       } else
1050         vResult[ix][iy] = vLHS[ix][iy] * vLHS[ix][iy];
1051     }
1052   }
1053   
1054   
1055   return true;
1056 }
1057
1058 int
1059 ImageFile::convertFormatNameToID (const char* const formatName)
1060 {
1061   int formatID = FORMAT_INVALID;
1062   
1063   for (int i = 0; i < s_iFormatCount; i++)
1064     if (strcasecmp (formatName, s_aszFormatName[i]) == 0) {
1065       formatID = i;
1066       break;
1067     }
1068     
1069     return (formatID);
1070 }
1071
1072 const char*
1073 ImageFile::convertFormatIDToName (int formatID)
1074 {
1075   static const char *formatName = "";
1076   
1077   if (formatID >= 0 && formatID < s_iFormatCount)
1078     return (s_aszFormatName[formatID]);
1079   
1080   return (formatName);
1081 }
1082
1083 const char*
1084 ImageFile::convertFormatIDToTitle (const int formatID)
1085 {
1086   static const char *formatTitle = "";
1087   
1088   if (formatID >= 0 && formatID < s_iFormatCount)
1089     return (s_aszFormatTitle[formatID]);
1090   
1091   return (formatTitle);
1092 }
1093
1094 bool
1095 ImageFile::exportImage (const char* const pszFormat, const char* const pszFilename, int nxcell, int nycell, double densmin, double densmax)
1096 {
1097   int iFormatID = convertFormatNameToID (pszFormat);
1098   if (iFormatID == FORMAT_INVALID) {
1099     sys_error (ERR_SEVERE, "Invalid format %s [ImageFile::exportImage]", pszFormat);
1100     return false;
1101   }
1102   
1103   if (iFormatID == FORMAT_PGM)
1104     return writeImagePGM (pszFilename, nxcell, nycell, densmin, densmax);
1105   else if (iFormatID == FORMAT_PGMASCII)
1106     return writeImagePGMASCII (pszFilename, nxcell, nycell, densmin, densmax);
1107   else if (iFormatID == FORMAT_PNG)
1108     return writeImagePNG (pszFilename, 8, nxcell, nycell, densmin, densmax);
1109   else if (iFormatID == FORMAT_PNG16)
1110     return writeImagePNG (pszFilename, 16, nxcell, nycell, densmin, densmax);
1111   
1112   sys_error (ERR_SEVERE, "Invalid format %s [ImageFile::exportImage]", pszFormat);
1113   return false;
1114 }
1115
1116 bool
1117 ImageFile::writeImagePGM (const char* const outfile, int nxcell, int nycell, double densmin, double densmax)
1118 {
1119   FILE *fp;
1120   int nx = m_nx;
1121   int ny = m_ny;
1122   ImageFileArray v = getArray();
1123   
1124   unsigned char* rowp = new unsigned char [nx * nxcell];
1125   
1126   if ((fp = fopen (outfile, "wb")) == NULL)
1127     return false;
1128   
1129   fprintf(fp, "P5\n");
1130   fprintf(fp, "%d %d\n", nx, ny);
1131   fprintf(fp, "255\n");
1132   
1133   for (int irow = ny - 1; irow >= 0; irow--) {
1134     for (int icol = 0; icol < nx; icol++) {
1135       int pos = icol * nxcell;
1136       double dens = (v[icol][irow] - densmin) / (densmax - densmin);
1137       dens = clamp (dens, 0., 1.);
1138       for (int p = pos; p < pos + nxcell; p++) {
1139         rowp[p] = static_cast<unsigned int> (dens * 255.);
1140       }
1141     }
1142     for (int ir = 0; ir < nycell; ir++) {
1143       for (int ic = 0; ic < nx * nxcell; ic++) 
1144         fputc( rowp[ic], fp );
1145     }
1146   }
1147   
1148   delete rowp;
1149   fclose(fp);
1150   
1151   return true;
1152 }
1153
1154 bool
1155 ImageFile::writeImagePGMASCII (const char* const outfile, int nxcell, int nycell, double densmin, double densmax)
1156 {
1157   FILE *fp;
1158   int nx = m_nx;
1159   int ny = m_ny;
1160   ImageFileArray v = getArray();
1161   
1162   unsigned char* rowp = new unsigned char [nx * nxcell];
1163   
1164   if ((fp = fopen (outfile, "wb")) == NULL)
1165     return false;
1166   
1167   fprintf(fp, "P2\n");
1168   fprintf(fp, "%d %d\n", nx, ny);
1169   fprintf(fp, "255\n");
1170   
1171   for (int irow = ny - 1; irow >= 0; irow--) {
1172     for (int icol = 0; icol < nx; icol++) {
1173       int pos = icol * nxcell;
1174       double dens = (v[icol][irow] - densmin) / (densmax - densmin);
1175       dens = clamp (dens, 0., 1.);
1176       for (int p = pos; p < pos + nxcell; p++) {
1177         rowp[p] = static_cast<unsigned int> (dens * 255.);
1178       }
1179     }
1180     for (int ir = 0; ir < nycell; ir++) {
1181       for (int ic = 0; ic < nx * nxcell; ic++) 
1182         fprintf(fp, "%d ", rowp[ic]);
1183       fprintf(fp, "\n");
1184     }
1185   }
1186   
1187   delete rowp;
1188   fclose(fp);
1189   
1190   return true;
1191 }
1192
1193
1194 #ifdef HAVE_PNG
1195 bool
1196 ImageFile::writeImagePNG (const char* const outfile, int bitdepth, int nxcell, int nycell, double densmin, double densmax)
1197 {
1198   double max_out_level = (1 << bitdepth) - 1;
1199   int nx = m_nx;
1200   int ny = m_ny;
1201   ImageFileArray v = getArray();
1202   
1203   unsigned char* rowp = new unsigned char [nx * nxcell * (bitdepth / 8)];
1204   
1205   FILE *fp = fopen (outfile, "wb");
1206   if (! fp)
1207     return false;
1208   
1209   png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1210   if (! png_ptr)
1211     return false;
1212   
1213   png_infop info_ptr = png_create_info_struct (png_ptr);
1214   if (! info_ptr) {
1215     png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
1216     fclose (fp);
1217     return false;
1218   }
1219   
1220   if (setjmp (png_ptr->jmpbuf)) {
1221     png_destroy_write_struct (&png_ptr, &info_ptr);
1222     fclose (fp);
1223     return false;
1224   }
1225   
1226   png_init_io(png_ptr, fp);
1227   
1228   png_set_IHDR (png_ptr, info_ptr, nx * nxcell, ny * nycell, bitdepth, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT);
1229   
1230   png_write_info(png_ptr, info_ptr);
1231   for (int irow = ny - 1; irow >= 0; irow--) {
1232     png_bytep row_pointer = rowp;
1233     
1234     for (int icol = 0; icol < nx; icol++) {
1235       int pos = icol * nxcell;
1236       double dens = (v[icol][irow] - densmin) / (densmax - densmin);
1237       dens = clamp (dens, 0., 1.);
1238       unsigned int outval = static_cast<unsigned int> (dens * max_out_level);
1239       
1240       for (int p = pos; p < pos + nxcell; p++) {
1241         if (bitdepth == 8)
1242           rowp[p] = outval;
1243         else {
1244           int rowpos = p * 2;
1245           rowp[rowpos+1] = (outval >> 8) & 0xFF;
1246           rowp[rowpos] = (outval & 0xFF);
1247         }
1248       }
1249     }
1250     for (int ir = 0; ir < nycell; ir++)
1251       png_write_rows (png_ptr, &row_pointer, 1);
1252   }
1253   
1254   png_write_end (png_ptr, info_ptr);
1255   png_destroy_write_struct (&png_ptr, &info_ptr);
1256   delete rowp;
1257   
1258   fclose(fp);
1259   
1260   return true;
1261 }
1262 #endif
1263
1264 #ifdef HAVE_GD
1265 #include "gd.h"
1266 static const int N_GRAYSCALE=256;
1267
1268 bool
1269 ImageFile::writeImageGIF (const char* const outfile, int nxcell, int nycell, double densmin, double densmax)
1270 {
1271   int gs_indices[N_GRAYSCALE];
1272   int nx = m_nx;
1273   int ny = m_ny;
1274   ImageFileArray v = getArray();
1275   
1276   unsigned char* rowp = new unsigned char [nx * nxcell];
1277   
1278   gdImagePtr gif = gdImageCreate(nx * nxcell, ny * nycell);
1279   for (int i = 0; i < N_GRAYSCALE; i++)
1280     gs_indices[i] = gdImageColorAllocate(gif, i, i, i);
1281   
1282   int lastrow = ny * nycell - 1;
1283   for (int irow = 0; irow < ny; irow++) {
1284     int rpos = irow * nycell;
1285     for (int ir = rpos; ir < rpos + nycell; ir++) {
1286       for (int icol = 0; icol < nx; icol++) {
1287         int cpos = icol * nxcell;
1288         double dens = (v[icol][irow] - densmin) / (densmax - densmin);
1289         dens = clamp(dens, 0., 1.);
1290         for (int ic = cpos; ic < cpos + nxcell; ic++) {
1291           rowp[ic] = (unsigned int) (dens * (double) (N_GRAYSCALE - 1));
1292           gdImageSetPixel(gif, ic, lastrow - ir, gs_indices[rowp[ic]]);
1293         }
1294       }
1295     }
1296   }
1297   
1298   FILE *out;
1299   if ((out = fopen (outfile,"w")) == NULL) {
1300     sys_error(ERR_FATAL, "Error opening output file %s for writing", outfile);
1301     return false;
1302   }
1303   gdImageGif(gif,out);
1304   fclose(out);
1305   gdImageDestroy(gif);
1306   
1307   delete rowp;
1308   
1309   return true;
1310 }
1311 #endif
1312