File: PTcommon.c

package info (click to toggle)
libpano13 2.9.12.dfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 8,616 kB
  • ctags: 3,403
  • sloc: ansic: 31,169; sh: 8,383; cpp: 1,714; makefile: 236; perl: 202
file content (1807 lines) | stat: -rw-r--r-- 61,089 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
/*
 *  PTcommon.c
 *
 *  Many of the routines are based on the program PTStitcher by Helmut
 *  Dersch.
 * 
 *  Copyright Helmut Dersch and Daniel M. German
 *  
 *  Dec 2005
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This software is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public
 *  License along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 *  Author: Daniel M German dmgerman at uvic doooot ca
 * 
 */

#include "filter.h"
#include "PTcommon.h"
#include "ColourBrightness.h"

#include "pttiff.h"
#include "file.h"
#include "PTcommon.h"
#include "ptstitch.h" 

#include <assert.h>
#include <float.h>

//#include <stdio.h>
//#include <stdlib.h>
//#include <sys/types.h>
//#include <dirent.h>
//#include <unistd.h>
//#include <stdint.h>
//#include <math.h>

#ifndef _MSC_VER
#include <unistd.h>
#else
#define isnan _isnan
#include "tools/compat_win32/getopt.h"
#endif


int panoFlattenTIFF(fullPath * fullPathImages, int counterImageFiles,
                    fullPath * outputFileName, int removeOriginals);


//declare functions
void getCropInformationFromTiff(TIFF * tif, CropInfo * c);
void getROI(TrformStr * TrPtr, aPrefs * aP, PTRect * ROIRect);

int ptQuietFlag = 0;

void InsertFileName(fullPath * fp, char *fname)
{
    char *c = strrchr((char *) (fp->name), PATH_SEP);
    if (c != NULL)
        c++;
    else
        c = fp->name;
    strcpy(c, fname);
}


int panoPSDCreate(fullPath * fullPathImages, int numberImages,
                  fullPath * outputFileName, pano_flattening_parms *flatteningParms)
{
    Image *ptrImage;
    int i;
    stBuf stitchInfo;
    fullPath tempFile;
    char tempString[128];
    Image image;

    assert(numberImages > 0);
    assert(fullPathImages != NULL);
    assert(outputFileName != NULL);

    if (ptQuietFlag == 0) {
        Progress(_initProgress, "Converting TIFF to PSD");
        sprintf(tempString, "%d", 100 / numberImages);
        Progress(_setProgress, tempString);
    }

    // Process background of PSD
    SetImageDefaults(&image);

    if (panoTiffRead(&image, fullPathImages[0].name) == 0) {

      PrintError("Could not read TIFF image No 0 %s", fullPathImages[0].name);
        if (ptQuietFlag == 0)
            Progress(_disposeProgress, tempString);

        return -1;
    }


    if (!(image.bitsPerPixel == 64 || image.bitsPerPixel == 32)) {
        PrintError("Image type not supported (%d bits per pixel)\n",
                   image.bitsPerPixel);
        return -1;
    }

    if (numberImages > 1 && image.bitsPerPixel != 32) {
        if (image.bitsPerPixel == 64) {
            PrintError
                ("Panotools is not able to save 16bit PSD images. Downsampling to 8 bit");
            TwoToOneByte(&image);       //we need to downsample to 8 bit if we are provided 16 bit images
        }
    }

    if (numberImages == 1) {
	if (writePSD(&image, outputFileName) != 0) {
	    PrintError("Could not write PSD-file");
	    if (ptQuietFlag != 0)
		Progress(_disposeProgress, tempString);
	    return -1;
	}
	return 0;
    }

    //////////////////////////////////////////////////////////////////////

    //Write out the first image as the base layer in the PSD file

    if (writePSDwithLayer(&image, outputFileName) != 0) {
        PrintError("Could not write PSD-file");
        if (ptQuietFlag != 0)
            Progress(_disposeProgress, tempString);
        return -1;
    }
    panoImageDispose(&image);

    ptrImage = &image;

    //Now iterate over all other images and add them as layers to the PSD file
    for (i = 1; i < numberImages; i++) {

        if (ptQuietFlag == 0) {
            sprintf(tempString, "%d", i * 100 / numberImages);
            if (Progress(_setProgress, tempString) == 0) {
                remove(outputFileName->name);
                return -1;
            }
        }

        if (panoTiffRead(ptrImage, fullPathImages[i].name) == 0) {

            PrintError("Could not read TIFF image No &d", i);
            if (ptQuietFlag == 0)
                Progress(_disposeProgress, tempString);
            return -1;
        }

        // We can't process 16 bit TIFFs. We have to downsample to 8 bit if necessary
        if (image.bitsPerPixel == 64)
            TwoToOneByte(ptrImage);

        // Create a new file with the result PSD, then delete the current one

        strcpy(tempFile.name, outputFileName->name);

        if (panoFileMakeTemp(&tempFile) == 0) {
            PrintError("Could not make Tempfile");
            return -1;

        }

        stitchInfo.seam = 1;
        stitchInfo.feather = 0;
        if (flatteningParms->stacked) 
          stitchInfo.psdOpacity = (unsigned char) (255.0/ (i + 1));
        else
          stitchInfo.psdOpacity = 255;
	stitchInfo.psdBlendingMode = flatteningParms->psdBlendingMode;

        if (addLayerToFile(ptrImage, outputFileName, &tempFile, &stitchInfo)
            != 0) {
            PrintError("Could not write Panorama File");
            return -1;
        }

        remove(outputFileName->name);
        rename(tempFile.name, outputFileName->name);

	panoImageDispose(ptrImage);
    }

    if (!ptQuietFlag) {
        Progress(_setProgress, "100");
        Progress(_disposeProgress, tempString);
    }

    return 0;
}



#ifdef __Win__
//void InsertFileName( fullPath *fp, char *fname ){
// char *c = strrchr((char*)(fp->name), PATH_SEP);
// if(c != NULL) c++;
// else c = fp->name;
// strcpy( c, fname );
//}   
#endif


static void ARGtoRGBAImage(Image * im)
{
    int right;
    int left;
    int bottom;
    int top;
    int width;
    int i;


    if (im->selection.bottom == 0 && im->selection.right == 0) {

        top = 0;
        left = 0;
        bottom = im->height;
        right = im->width;


    }
    else {

        top = im->selection.top;
        bottom = im->selection.bottom;
        left = im->selection.left;
        right = im->selection.right;
    }

    width = right - left;

    //fprintf(stderr, "\nWidth %10d Top: %10d bottom %10d Right %10d Left %10d-------", width, top, bottom, right, left);

    assert(width >= 0);
    assert(bottom >= top);

    for (i = 0; i < bottom - top; i++) {

        ARGBtoRGBA(*(im->data) + i * im->bytesPerLine, width,
                   im->bitsPerPixel);

    }                           // for 

}



void Clear_Area_Outside_Selected_Region(Image * image)
{
    // This function clears (i.e. sets to zero) the area outside the 
    // selection region 

    int right;
    int left;
    int bottom;
    int top;
    //  int width;
    //  int var24;
    int bytesPerPixel;          // 32
    unsigned char *dataPtr;
    unsigned char *pixelPtr;

    int currentRow;
    int currentColumn;

    // Only works for 8/16 bit per channel (32/64 bits per pixel) images
    assert(image->bitsPerPixel == 32 || image->bitsPerPixel == 64);

    top = image->selection.top;
    bottom = image->selection.bottom;
    left = image->selection.left;
    right = image->selection.right;

    if (bottom == 0)
        bottom = image->height;

    if (right == 0)
        right = image->width;
    

    if (image->bitsPerPixel == 32) {
        bytesPerPixel = 4;
    }
    else if (image->bitsPerPixel == 64) {
        bytesPerPixel = 8;
    }
    else {
	PrintError("Invalid bits per pixel in image %d", image->bitsPerPixel);
        exit(1);
    }


    if (image->format == _fisheye_circ) {


	// TODO
	// This routine works only in fisheyes in portrait mode
	// it probably fails in landscape mode
	
	int horCenter, verCenter;
	int horRadious;
	int horRadious2;

	horCenter = (left + right) / 2;
	verCenter = (top + bottom) / 2;
	
	// Compute the horizontal width divided by 2, 
	// let us call it horizontal radios

	horRadious = (right - left) / 2;
	// Square it, so we don't have to compute this 
	// every time
	horRadious2 = horRadious * horRadious;
	
	dataPtr = *(image->data);
	
	// Scan the image from top to bottom
	for (currentRow = 0; currentRow < image->height; currentRow++) {
	    int verDistance;
	    int verDistance2;

	    // The algorith it simple. Find the distance of each from from the 
	    // center of the image. If the point is farther than horRadious
	    // then set mask to zero
	    
	    currentColumn = 0;
	    pixelPtr = dataPtr;
	    
	    // Compute the square of the vertical distance to this row from center
	    verDistance = (currentRow - verCenter);
	    verDistance2 = verDistance * verDistance;
	    
	    for (currentColumn = 0; currentColumn < image->width; currentColumn ++) {
		int horDistance;
		int horDistance2;

		// Compute square of distance of this point to center 
		// the old Pythagoras way
		// distance^2 = horDistance^2  + verDistance^2
		
		horDistance = (currentColumn - horCenter);
		horDistance2 = horDistance * horDistance;
		
		if (horDistance2 + verDistance2 > horRadious2) {

		    // Point falls outside the circle defined its horizontal maximum distance

		    // Set mask to zero
		    if (bytesPerPixel == 4)
			*pixelPtr = 0;     
		    else if (bytesPerPixel == 8) {
			*pixelPtr = 0;     
			*(pixelPtr+1) = 0;     
		    }
			
		}
		pixelPtr +=  bytesPerPixel;

	    } // for column
	    dataPtr += image->bytesPerLine;
	}  // for row
	return;
    }



    // Clear the area at above the image
    dataPtr = *(image->data);

    for (currentRow = 0; currentRow < top; currentRow++) {
        pixelPtr = dataPtr;

        for (currentColumn = 0; currentColumn < image->width; currentColumn++) {
            assert(sizeof(int) == bytesPerPixel);
            memset(pixelPtr, 0, bytesPerPixel);
            pixelPtr += bytesPerPixel;
        }

        dataPtr += image->bytesPerLine;
    }

    // Clear area below the picture
    dataPtr = bottom * image->bytesPerLine + *(image->data);

    for (currentRow = bottom; currentRow < image->height; currentRow++) {
        pixelPtr = dataPtr;
        for (currentColumn = 0; currentColumn < image->width; currentColumn++) {
            memset(pixelPtr, 0, bytesPerPixel);
            pixelPtr += bytesPerPixel;
        }

        dataPtr += image->bytesPerLine;

    }                           //  for (    ;  %currentColumn < image->width ; currentColumn++,pixelPtr += bytesPerPixel) {


    /* Clear the area to the left of the picture */

    dataPtr = *(image->data);
    for (currentRow = 0; currentRow < image->height; currentRow++) {

        pixelPtr = dataPtr;
        for (currentColumn = 0; currentColumn < left; currentColumn++) {
            memset(pixelPtr, 0, bytesPerPixel);
            pixelPtr += bytesPerPixel;
        }

        dataPtr += image->bytesPerLine;
    }

    /* Clear the area to the right of the picture */

    dataPtr = *(image->data);

    for (currentRow = 0; currentRow < image->height; currentRow++) {

        pixelPtr = dataPtr + bytesPerPixel * right;

        for (currentColumn = right; currentColumn < image->width;
             currentColumn++) {

            memset(pixelPtr, 0, bytesPerPixel);

            pixelPtr += bytesPerPixel;

        }

        dataPtr += image->bytesPerLine;

    }

    return;

}


/**
 * This function computes the minimal rectangle needed to encompass
 * the region of the output image (TrPtr->dest) that will be populated with 
 * data from the input image (TrPtr->src) using the options specified
 * in aP.  The ROIRect is populated with the left/right/top/bottom values
 * that define this ROI within the output image
 */
void getROI(TrformStr * TrPtr, aPrefs * aP, PTRect * ROIRect)
{
    struct MakeParams mpinv;
    fDesc invstack[15], finvD;
    int color = 0;

    int x, y, x_jump;
    double x_d, y_d;            // Cartesian Coordinates of point in source (i.e. input) image
    double Dx, Dy;              // Coordinates of corresponding point in destination (i.e. output) image

    double w2 = (double) TrPtr->dest->width / 2.0 - 0.5;        //half destination image width
    double h2 = (double) TrPtr->dest->height / 2.0 - 0.5;       //half destination image height
    double sw2 = (double) TrPtr->src->width / 2.0 - 0.5;        //half source image width
    double sh2 = (double) TrPtr->src->height / 2.0 - 0.5;       //half source image height

    //Set initial values for ROI to be adjusted during this function
    ROIRect->left = TrPtr->dest->width - 1;
    ROIRect->right = 0;
    ROIRect->top = TrPtr->dest->height - 1;
    ROIRect->bottom = 0;

    //The "forward" transform (although not used here) allows us to map pixel
    //coordinates in the output image to their location in the source image.
    //SetMakeParams( stack, &mp, &(aP->im) , &(aP->pano), color );
    //fD.func = execute_stack; fD.param = stack;

    //The "inverse" transform allows us to map pixel coordinates in each source image
    //to their location in the output image.
    SetInvMakeParams(invstack, &mpinv, &(aP->im), &(aP->pano), color);
    finvD.func = execute_stack_new;
    finvD.param = invstack;

    //iterate over edges of input image and compute left/right/top/bottom-most coordinate
    //in output image
    //For equirectangular output projection covering 360/180, iterating over the 
    //edges of each input image isn't sufficient to determine ROI because an 
    //an interior point in an input image can be at the edge of ROI.  More research 
    //needed here, but for now include some representative interior points as well.
    for (y = 0; y <= TrPtr->src->height; y += 1) {
        
                x_jump = (y==0 || y==TrPtr->src->height || abs(y - TrPtr->src->height/2)<=5) ? 1 : TrPtr->src->width/2; 
                
                for (x = 0; x <= TrPtr->src->width; x += x_jump) {
                        //convert source coordinates to cartesian coordinates (i.e. origin at center of image)
                        x_d = (double) x - sw2 ;
                        y_d = (double) y - sh2 ;
                        
                        //Map the source image cartesian coordinate to the destination image cartesian coordinate
                        finvD.func( x_d, y_d, &Dx, &Dy, finvD.param);
                        
                        //Convert destination cartesian coordinate back to destination "screen" coordinates (i.e. origin at top left of image)
                        Dx += w2;
                        Dy += h2;
                        
                        //printf("  IN: %d,%d -> OUT: %f, %f   (%d, %d)\n", x, y, Dx, Dy, (int)Dx, (int)Dy);
                        
                        //Expand ROI if necessary
                        //I've observed that in some cases, the mapping function returns
                        //a value of "-1.#IND00".  This is not a number, and probably indicates
                        //a divide by zero error somewhere in the mapping function.  This should
                        //be solved, but, for now, discard this value and keep going
                        if (!isnan(Dx)) {
                                if ((int)Dx < ROIRect->left) ROIRect->left = (int)Dx;
                                if ((int)Dx > ROIRect->right) ROIRect->right = (int)Dx;
                        }
                        if (!isnan(Dy)){                
                                if ((int)Dy < ROIRect->top) ROIRect->top = (int)Dy;
                                if ((int)Dy > ROIRect->bottom) ROIRect->bottom = (int)Dy;
                        }
                }
        }
        
        //Reduce ROI if it extends beyond boundaries of final panorama region
        if (ROIRect->left    < 0) ROIRect->left =0;
        if (ROIRect->top     < 0) ROIRect->top  =0;
        if (ROIRect->right   > (TrPtr->dest->width-1))  ROIRect->right    = TrPtr->dest->width-1;  
        if (ROIRect->bottom  > (TrPtr->dest->height-1)) ROIRect->bottom   = TrPtr->dest->height-1;
        
        //printf("ROI: %d,%d - %d, %d\n", ROIRect->left, ROIRect->top, ROIRect->right, ROIRect->bottom);
}


// NO LONGER NEEDED

/**
 * Populates the CropInfo struct with data about cropping of 
 * the TIFF file specified by filename
 */
void getCropInformation(char *filename, CropInfo * c)
{

    TIFF *tif = TIFFOpen(filename, "r");
    if (tif == NULL) {
        PrintError("getCropInformation: Could not open TIFF file");
    }
    else {
        getCropInformationFromTiff(tif, c);
        TIFFClose(tif);
    }

}


#if 0 

void setFullSizeImageParameters(pt_tiff_parms * imageParameters,
                                CropInfo * crop_info)
{
    // Update the imageParameters so that the dimensions reflect the
    // the size of the full-sized output image, (recorded in the crop_info struct)
    imageParameters->imageLength = crop_info->full_height;
    imageParameters->imageWidth = crop_info->full_width;
    imageParameters->bytesPerLine =
        imageParameters->imageWidth * (imageParameters->bitsPerPixel / 8);
}

#endif


int panoCreatePanorama(fullPath ptrImageFileNames[], int counterImageFiles,
                       fullPath * panoFileName, fullPath * scriptFileName)
{

    Image *currentImagePtr;
    aPrefs *prefs;
    int var01;
    int var00;
    int colourCorrection;
    int panoProjection = 0;

    int lines;
    fullPath *fullPathImages;
    int loopCounter;
    char var40[8];
    char *tempString;           // It looks like a char *temp;          
    char outputFileName[512];
#if 0
    VRPanoOptions defaultVRPanoOptions;
#endif
    char tmpStr[64];            // string
    fullPath currentFullPath;
    fullPath panoName;          // according to documention: QTVR, PNG, PICT, TIFF, etc plus options...*/
    fullPath tempScriptFile;
    char output_file_format[256];
    Image resultPanorama;       //Output Image
    Image image1;               //Input Image

    FILE *regFile;
    char *regScript;
    unsigned int regLen;
    unsigned int regWritten;

    int feather = 0;


    pano_Tiff *tiffFile;             //Output file...will be written during this function
    TrformStr transform;        //structure holds pointers to input and output images and misc other info

    int ebx;

    int croppedTIFFIntermediate = 1;
    int croppedWidth = 0, croppedHeight = 0;
    PTRect ROIRect;
    unsigned int outputScanlineNumber = 0;

    pano_ImageMetadata metadata;

    /* Variables */
    colourCorrection = 0;       // can have values of 1 2 or 3
    var00 = 0;
    var01 = 0;

    //Copy script line for line into a new temporary file
    memcpy(&tempScriptFile, scriptFileName, sizeof(fullPath));
    if (panoFileMakeTemp(&tempScriptFile) == 0) {
        PrintError("Unable to create temporary file");
        goto mainError;
    }

    panoTiffSetErrorHandler();

    if ((regFile = fopen(tempScriptFile.name, "w")) == NULL) {
        PrintError("Could not open temporary Scriptfile");
        goto mainError;
    }

    if ((regScript = LoadScript(scriptFileName)) == 0) {
        PrintError("Could not load ScriptFile");
        goto mainError;
    }

    regLen = strlen(regScript);

    // Write script to temp file
    regWritten = fwrite(regScript, 1, regLen, regFile);

    // Make sure script was written completely
    if (regWritten != strlen(regScript)) {
        PrintError("Could not write temporary script");
        goto mainError;
    }
    
    fclose(regFile);

    //Initialize members to zero
    SetImageDefaults(&image1);
    SetImageDefaults(&resultPanorama);

    //transform structure holds input and output images, and some miscellaneous other information
    transform.src = &image1;    // Input image
    transform.dest = &resultPanorama;   // Output image
    transform.mode = 8;         // How to run transformation
    transform.success = 1;      // 1 success 0 failure

    //Allocate space to hold fully qualified names of input images
    if ((fullPathImages = malloc(counterImageFiles * 512)) == NULL) {
        PrintError("Not enough memory");
        goto mainError;
    }

    // This is the main processing loop...it iterates over each input image
    // and maps the pixels in these input images into the output image(s)
    for (loopCounter = 0; loopCounter < counterImageFiles; loopCounter++) {

	// TODO 
	
	// the original PTstitcher logic  is strange

	// It processes a lot of data more than once. This part should really be done once for all images


        currentImagePtr = &image1;

        // Read the next adjust line (contains yaw, pitch, roll and other information)
        // for one input image from the script file
        if ((prefs = readAdjustLine(&tempScriptFile)) == 0) {
            PrintError("Could not read Scriptfile");
            goto mainError;
        }

	//	printf("*********cut frame: should be zero for S and no crop %d\n",  prefs->im.cP.cutFrame);
	
        //New for PTMender...PTMender uses "cropped" TIFFs as its intermediate file 
        //format for all processing.  In contrast, PTStitcher used full-size TIFF
        //images for all intermediate processing.  PTMender can still write "uncropped" 
        //final as needed.
        //
        //To the end user, PTMender appears to behave similarly to PTStitcher for 
        //TIFF_m and TIFF_mask formats, outputting a one "full-size" TIFF for each 
        //layer.  However, the internal processing is done on cropped TIFFs which
        //speeds things up considerably.
        //
        //An important improvement over PTStitcher is that the user can also explicitly 
        //requests cropped output for multi layer TIFF output by inlcluding 
        //"r:CROP" as part of the "p" line (e.g. n"TIFF_m r:CROP")
        //
        //Using cropped TIFF as the intermediate format significantly speeds up 
        //processing, with larger panos showing more dramatic increases in speed.  
        //It should also mean that the creation of the "flattened" formats will 
        //be significantly more memory-friendly, as the masking steps and PSD 
        //assembly steps won't need to load images the size of the output file 
        //into memory at once.  Unless the PTMender is fed extremely large input 
        //images, all memory constraints should now be a thing of the past (MRDL - May 2006).

        //croppedTIFFIntermediate determines if all intermediate processing is done
        //with cropped or full size TIFF.  There probably isn't much of a reason
        //to ever disable this feature, other than for testing/debugging purposes.


	// By default we do cropped TIFF,
	// but at this point we cannot properly calculate the ROI for fisheyes, so just
	// process them uncropped.

	if (prefs->im.format == _fisheye_circ) {
	    croppedTIFFIntermediate = 0;
	} 

        colourCorrection = prefs->sBuf.colcorrect;
        // This is a strange value:
        // colourCorrection == (i & 3) + (i+1)*4;
        // where i is the number of the reference image
        
        assert(colourCorrection >= 0
               && colourCorrection < (counterImageFiles + 1) * 4);
        if (prefs->pano.cP.radial != 0) {
            
            var00 = prefs->pano.cP.radial_params[0][2]; // what is this for, I have NO idea.
            var00++;

        }                       // begins 804a00e


        if (prefs->pano.cP.horizontal != 0) {

          // Colour correction in color only, not brightness

            var01 = prefs->pano.cP.horizontal_params[0];        // 0x75c //[3] 3 colours x horizontal shift value
            // at this point var01 contains the index to the image to use as anchor fo

            var01++;

        }

        // Projection format for final panorama
        panoProjection = prefs->pano.format;

        // Copy output pano name to panoName
        memcpy(&panoName, &prefs->pano.name, sizeof(fullPath));
        //memcpy(&global5640, &prefs->sBuf, sizeof(stBuf));
        
        //panoName.name contains the n"XXX" value from the script "p" lines (e.g. n"TIFF_m" or n"QTVR w400 h300 c1")
        tempString = panoName.name;
        --tempString;           /* nextWord does ++ before testing anything, this guarantess proper execution */
        nextWord(output_file_format, &tempString);
        
	if (loopCounter == 0 && strcmp(output_file_format, "TIFF_m") != 0) {
	    PrintError("No support for this ouput image format (%s). Output will be TIFF_m", output_file_format);
	}



        transform.interpolator = prefs->interpolator;
        transform.gamma = prefs->gamma;
        
        if (ptQuietFlag == 0) {
            sprintf(tmpStr, "Converting Image %d / %d", (loopCounter + 1),
                    counterImageFiles);
            Progress(_initProgress, tmpStr);
        }

        //Read input image into transform.src
        if (panoImageRead(currentImagePtr, &ptrImageFileNames[loopCounter]) == 0) {
            PrintError("Could not read input image [%s]", ptrImageFileNames[loopCounter].name);
            goto mainError;
        }

	// printf("Ended reading INPUT image\n");

        //This "masks" the input image so that some pixels are excluded from 
        //transformation routine during pixel remapping/interpolation 
	
        if (prefs->im.cP.cutFrame != 0) {       // remove frame? 0 - no; 1 - yes
	    // THIS CODE is executed in crop C type only, but not in S type
	    //	    printf("To crop image\n");
            if (CropImage(currentImagePtr, &(prefs->im.selection)) == 0) {
                prefs->im.selection.left = 0;
                prefs->im.selection.right = 0;
                prefs->im.selection.bottom = 0;
                prefs->im.selection.top = 0;
            }
        }
        //setup width/height of input image
        prefs->im.width = image1.width;
        prefs->im.height = image1.height;

        //Try to set reasonable values for output pano width and/or height if not 
        //specified as part of input (Do this only when processing first image in script)
        if (loopCounter == 0) {

            feather = prefs->sBuf.feather;
            if (prefs->pano.width == 0) {
                // if the pano did not set the width, then try to set it
                if (prefs->im.hfov != 0.0) {
                    prefs->pano.width =
                        prefs->im.width * prefs->pano.hfov / prefs->im.hfov;
                    prefs->pano.width /= 10;    // Round to multiple of 10
                    prefs->pano.width *= 10;
                }
            }

            if (prefs->pano.height == 0)
                prefs->pano.height = prefs->pano.width / 2;
            
            resultPanorama.height = prefs->pano.height;
            resultPanorama.width = prefs->pano.width;
            
            if (resultPanorama.height == 0 || resultPanorama.width == 0) {
                PrintError("Please set Panorama width/height");
                goto mainError;
            }
        }                       //End attempt at setting reasonable values for pano width/height
        
        
        //printf("to set metadata\n");
        
        //////////////////////////////////////////////////////////////////////
        // Set metadata for output file

        panoMetadataCopy(&metadata, &image1.metadata);
        
        // The size of the image will change, so we have to update all the
        // fields accordingly.
        panoMetadataResetSize(&metadata,
                              resultPanorama.width,
                              resultPanorama.height);
        
	metadata.imageNumber = loopCounter;
	metadata.imageTotalNumber = counterImageFiles;
        metadata.imageDescription = strdup(regScript);


        // Set output width/height for output file 
        if (croppedTIFFIntermediate) {
            getROI(&transform, prefs, &ROIRect);
            //Dimensions determine size of TIFF file
            croppedWidth = (ROIRect.right - ROIRect.left) + 1;
            croppedHeight = (ROIRect.bottom - ROIRect.top) + 1;
            
            panoMetadataSetAsCropped(&metadata, 
                                     croppedWidth, croppedHeight,
                                     ROIRect.left, ROIRect.top);
        }
        
        panoMetadataSetCompression(&metadata, prefs->pano.name);

        //The resultPanorama.selection determines which region of the output image
        //is iterated over during the main pixel-remapping processing logic.  Much
        //of the image will be empty (black space) for any given input image.  However,
        //if cropped output is selected, then only the region of interest (ROI) into
        //which this input image will be mapped is processed...this significantly
        //speeds up processing
        if (croppedTIFFIntermediate) {
            resultPanorama.selection.left = ROIRect.left;
            resultPanorama.selection.right = ROIRect.right + 1; // the right edge is actually the pixel NOT in the pano
            resultPanorama.selection.top = ROIRect.top;
        }
        else {
            resultPanorama.selection.left = 0;
            resultPanorama.selection.right = resultPanorama.width;
            resultPanorama.selection.top = 0;
        }

        resultPanorama.bitsPerPixel = image1.bitsPerPixel;
        resultPanorama.bytesPerLine = metadata.bytesPerLine;

        panoMetadataCopy(&resultPanorama.metadata, &metadata);
        
        panoMetadataFree(&metadata);
        
        //////End of set metadata


        ///  CREATE OUTPUT FILE

        // Copy the current output file name to he fullPathImages[loopCounter]
        memcpy(&fullPathImages[loopCounter], &panoFileName, sizeof(fullPath));

        // Create temporary file where output data wil be written
        if (panoFileMakeTemp(&fullPathImages[loopCounter]) == 0) {
            PrintError("Could not make Tempfile");
            goto mainError;
        }

        // Populate currentFullPath.name with output file name
        GetFullPath(&fullPathImages[loopCounter], currentFullPath.name);

        // Open up output file for writing...data will be written in TIFF format

        if ((tiffFile = panoTiffCreate(currentFullPath.name, 
                       &resultPanorama.metadata)) == 0) {
            PrintError("Could not open %s for writing", currentFullPath.name);
            goto mainError;
        }

        if (ptQuietFlag == 0) {
            if (Progress(_setProgress, "5") == 0) {
                panoTiffClose(tiffFile);
                remove(fullPathImages[loopCounter].name);
                return (-1);
            }
        }

        //The output image is generated a few lines at a time to make efficient use
        //of limited memory...compute a reasonable number of lines to process (must
        //be at least 1, but no more than output height)
        lines = 500000 / resultPanorama.bytesPerLine;

        if (lines == 0)
            lines = 1;

        //Don't process more lines than are available
        if (lines >
            (croppedTIFFIntermediate ? croppedHeight : resultPanorama.height))
            lines =
                (croppedTIFFIntermediate ? croppedHeight : resultPanorama.
                 height);

        if ((resultPanorama.data =
             (unsigned char **) mymalloc(lines *
                                         resultPanorama.bytesPerLine)) ==
            NULL) {
            PrintError("Not enough memory for output panorama buffer");
            exit(1);
        }
        //NB resultPanorama.selection.bottom is actually one pixel beyond last row with data.
        resultPanorama.selection.bottom =
            resultPanorama.selection.top + lines;

        //    printf("bits per pixel %d\n", resultPanorama.bitsPerPixel);
        //    printf("cropped %d\n", croppedTIFFIntermediate);

        if (resultPanorama.bitsPerPixel != image1.bitsPerPixel) {
            PrintError
                ("All source images must have the same number of bits per pixel.");
            exit(1);
        }

        //Copy all position related data (yaw, pitch, roll, etc) for input image to currentImagePtr
        CopyPosition(currentImagePtr, &(prefs->im));

        //image1.selection determines how much of the input image to be 
        //included during main pixel remapping logic
        image1.selection.top = prefs->im.selection.top;
        image1.selection.bottom = prefs->im.selection.bottom;
        image1.selection.left = prefs->im.selection.left;
        image1.selection.right = prefs->im.selection.right;

	/*
	printf("****** Image selection hfov %f, %d %d %d %d \n", image1.hfov, image1.selection.top,
	       image1.selection.bottom,
	       image1.selection.left,
	       image1.selection.right);
	*/

        CopyPosition(&resultPanorama, &(prefs->pano));

        //Set image data outside selection region to zeros

        Clear_Area_Outside_Selected_Region(currentImagePtr);

        //pano.width and height must be equal to the full canvas size (not the 
        //size of the cropped output image...if selected) in order for the pixel 
        //remapping logic to work correctly.
        prefs->pano.width = resultPanorama.width;
        prefs->pano.height = resultPanorama.height;

        //Iterate over the output image multiple lines at a time, remapping pixels
        //from the input image into the output image, and writing data to an
        //output TIFF file.  Finish iterating when we reach the bottom of the 
        //output image (or, in the case of a cropped file, the bottom of the 
        //output ROI).
        outputScanlineNumber = 0;
        while (resultPanorama.selection.top <
               (croppedTIFFIntermediate ? ROIRect.bottom +
                1 : resultPanorama.height)) {

            // Call the main pixel remapping routine...all the interpolation happens here

	    /*
	    printf("Prefs: %f\n", prefs->pano.hfov);
	    printf("Prefs im: hvof %f, yaw %f pitch %f, roll %f\n", prefs->im.hfov, prefs->im.yaw, prefs->im.pitch, prefs->im.roll);
	    printf("Prefs pano: hvof %f, vfov %f pitch %f, roll %f\n", prefs->pano.hfov, prefs->pano.yaw, prefs->pano.pitch, prefs->pano.roll);
	    printf("Prefs Interpolator %d:\n", prefs->interpolator);
	    printf("Prefs Gamma %d:\n", prefs->gamma);
	    */

            MakePano(&transform, prefs);

            if (transform.success == 0) {       // Error 
                PrintError("Error converting image");
                goto mainError;
            }

            //Reverse byte order before writing out to TIFF file
            ARGtoRGBAImage(&resultPanorama);

            //Write calculated data rows to TIFF file one row (aka "scanline") at a time
            for (ebx = 0;
                 ebx <
                 resultPanorama.selection.bottom -
                 resultPanorama.selection.top; ebx++) {
                if (TIFFWriteScanline(tiffFile->tiff, 
                      *resultPanorama.data + (resultPanorama.bytesPerLine * ebx),
                      outputScanlineNumber, 1) != 1) {
                    PrintError("Unable to write to TIFF file\n");
                    return -1;
                }

                outputScanlineNumber++;
            }

            if (ptQuietFlag == 0) {

                //Update progress bar
                if (croppedTIFFIntermediate)
                    sprintf(tmpStr, "%d",
                            (int) ((resultPanorama.selection.bottom -
                                    ROIRect.top) * 100 / croppedHeight));
                else
                    sprintf(tmpStr, "%d",
                            (int) (resultPanorama.selection.bottom * 100 /
                                   resultPanorama.height));

                if (Progress(_setProgress, tmpStr) == 0) {
                    // Cancelled by the user
                    panoTiffClose(tiffFile);
                    remove(tempScriptFile.name);
                    remove(fullPathImages[loopCounter].name);
                    return (-1);
                }
            }

            //specify the next batch of rows to be processed 
            resultPanorama.selection.top = resultPanorama.selection.bottom;
            resultPanorama.selection.bottom =
                resultPanorama.selection.top + lines;

            //Be careful at boundary...end of image
            if (resultPanorama.selection.bottom >
                (croppedTIFFIntermediate ? ROIRect.bottom +
                 1 : resultPanorama.height))
                resultPanorama.selection.bottom =
                    (croppedTIFFIntermediate ? ROIRect.bottom +
                     1 : resultPanorama.height);
        }

        panoTiffClose(tiffFile);
	
#ifdef UNCROP_FISHEYES
	if (croppedTIFFIntermediate == 0) {
	    // We can't process (yet) all files in cropped mode
	    // To quite the roar from the masses let them think we
	    // do. I wonder how long it will take for them to notice. Placebo effect?
	    pano_cropping_parms croppingParms;
	    bzero(&croppingParms, sizeof(croppingParms));
	    
	    if (panoTiffCrop(currentFullPath.name, currentFullPath.name, &croppingParms) == 0) {
		PrintError("Unable to write output file %s", currentFullPath.name);
		remove(tempScriptFile.name);
		return (-1);
	    }
	}
#endif

        //////////////////////////////////////////////////////////////////////
	panoImageDispose(&image1);

        // The memory for td and ts was allocated in morpher.c with malloc 
        // (not myMalloc), so we need to use free (not myFree)
        if (prefs->td != NULL) {
            free((void **) prefs->td);
        }

        if (prefs->ts != NULL) {
            free((void **) prefs->ts);
        }
        free(prefs);

	panoImageDispose(&resultPanorama);
        
    }                           //End of main image processing loop
    
    if (!ptQuietFlag)
        Progress(_disposeProgress, "");

    // This is the end of the pixel remapping for all input images.
    // At this point we should have a collection of TIFF files containing
    // the warped input images.  For TIFF_m format this is all we need.  For
    // other formats, we may need to do extra work (feathering, flattening, etc.)

    //----------------------------------------------------------------------

    remove(tempScriptFile.name);

    panoImageDispose(&resultPanorama);
    panoImageDispose(&image1);

#if 0 
    // NO LONGER SUPPORTED IN THIS FUNCTION. IT SHOULD BE REMOVED IN THE FUTURE

    // These functions are to correct and/or brightness.  They are not required for 
    // panoramas that do not need any brightness adjustments.  Moreover, Dersch
    // was not fully satisfied with the quality of results obtained from
    // using these functions, and knew that they could be significantly
    // improved.  In general, I think it best to avoid using these features, 
    // and doing any color/brightness adjustments manually either before
    // or after stitching.  While these functions work OK for some images, some 
    // of the time, they can produce some obviously wrong results in some
    // circumstances...perhaps an area for future improvement, but probably not 
    // as important a feature (now that we have multi-resolution splining 
    // software like Enblend) as when Desrch first added these (MRDL).

    if (var00 != 0) {
        ColourBrightness(fullPathImages, fullPathImages, counterImageFiles,
                         var00 - 1, 1, 0);
    }

    if (var01 != 0) {           //
      //      fprintf(stderr, "This type of correction... 1\n");
        ColourBrightness(fullPathImages, fullPathImages, counterImageFiles,
                         var01 - 1, 2, 0);
    }                           // 

    if (colourCorrection != 0) {
      //      fprintf(stderr, "This type of correction... 2\n");
        ColourBrightness(fullPathImages, fullPathImages, counterImageFiles,
                         (colourCorrection / 4) - 1, 0, 0);
    }

    SetVRPanoOptionsDefaults(&defaultVRPanoOptions);

    /* Soo, at this point we have skipped the first word of the panorama:
       # n"QTVR w400 h300 c1"           additional viewer options in a quoted string together with format
       #              the following options are recognized:
       #                  w(width) and h(height) of viewer window (only QTVR on Macs)
       #                  c(codec: 0-JPEG, 1-Cinepak, 2-Sorenson) (only QTVR on Macs)
       #                  q(codec quality):
       #                     0-high,1-normal,2-low    QTVR on Macs
       #                     0-100(highest)           on other jpeg-formats (PAN, IVR, IVR_java, VRML)
       #                  g  progressive jpeg (0-no, 1-yes) (PAN, IVR, IVR_java, VRML)
       #                     Optimized JPEG (0-on(default), 2-disabled), (3-progressive with optimized disabled)
       #                  p  initial pan angle ( QTVR on Macs, VRML, IVR)
       #                  v  field of view (QTVR, VRML, IVR)
       #                  Many more options can be set by editing the viewer scripts
     */
    //int getVRPanoOptions( VRPanoOptions *v, char *line )

    getVRPanoOptions(&defaultVRPanoOptions, tempString);
#endif

    // We have to add "masks" to the images before finishing...

    if (ptQuietFlag == 0)
        Progress(_initProgress, "Writing Output Images");
    
    for (loopCounter = 0; loopCounter < counterImageFiles; loopCounter++) {
        
        if (ptQuietFlag == 0) {
            sprintf(tmpStr, "%d",
                    (100 * loopCounter) / counterImageFiles);
            if (Progress(_setProgress, tmpStr) == 0) {
                return (1);
            }
        }
        
        strcpy(outputFileName, panoFileName->name);
        sprintf(var40, "%04d", loopCounter);
        strcat(outputFileName, var40);
        panoReplaceExt(outputFileName, ".tif");
        // remove output file, if it exists. 
        if (panoSingleFileExists(outputFileName)) {
            remove(outputFileName);
        }
        rename(fullPathImages[loopCounter].name, outputFileName);

        
    }
    free(fullPathImages);


    if (ptQuietFlag == 0) {
	Progress(_setProgress, "100%");
	Progress(_disposeProgress, "");
    }


    free(regScript);
    return (0);

    // FUNCTION ENDS HERE

  mainError:
    free(regScript);

    return (-1);
}


#if 0

    // THIS_CODE_IS_NO_LONGER_SUPPORTED. IT SHOULD BE REMOVED IN THE FUTURE

    if (counterImageFiles > 1) {

      // There is no point in adding stitching masks for just one image 
      //printf("Creating seams******************\n");

        if (panoStitchReplaceMasks(fullPathImages, fullPathImages, counterImageFiles,
                                   feather) != 0) {
            PrintError("Could not create stitching masks");
            goto mainError;
        }
    }



    /************ OUTPUT FORMATS: Multiple TIFF ***************/
    // TIFF_m and TIFF_mask...just rename the intermediate files 
    // that we've already computed with numbers (e.g. img0000.tif, img0001.tif, etc.) 
    // and we are finished processing.
    if (strcmp(output_file_format, "TIFF_m") == 0
        || strcmp(output_file_format, "TIFF_mask") == 0) {


            if ((croppedTIFFIntermediate != 0 && croppedTIFFOutput != 0) ||
                (croppedTIFFIntermediate == 0 && croppedTIFFOutput == 0)) {
                // if intermediate and output formats are the same, then just rename and quit
                rename(fullPathImages[loopCounter].name, outputFileName);
            }
            else if (croppedTIFFIntermediate != 0 && croppedTIFFOutput == 0) {
                // if cropped intermediate, but we want uncropped output, then uncrop
                if (!panoUnCropTiff
                    (fullPathImages[loopCounter].name, outputFileName)) {
                    return (1);
                }
                remove(fullPathImages[loopCounter].name);
            }
            else {
                // only other option is to use uncropped files as intermediate, and want 
                // cropped as output.  This is (a) a waste of time and (b) not supported.
                // Show error, but be nice and rename existing images anyway
                PrintError
                    ("Cropped output files cannot be created from uncropped intermediate files\n\nWriting uncropped output: %s",
                     outputFileName);
                rename(fullPathImages[loopCounter].name, outputFileName);
            }


        }                       // end of for loop
        free(fullPathImages);

        if (ptQuietFlag == 0) {
            Progress(_setProgress, "100%");
            Progress(_disposeProgress, "");
        }
        return (0);
    }

    //printf("To start creating the output files\n");
    
  /************ OUTPUT FORMATS: Layered PSD ***************/
    // Layered PSD is less simple...we need to assemble the existing
    // intermediate files into a layered photoshop document
    if (strcmp(output_file_format, "PSD_nomask") == 0
        || strcmp(output_file_format, "PSD_mask") == 0
        ) {
        panoReplaceExt(panoFileName->name, ".psd");
        
        if (panoCreatePSD(fullPathImages, counterImageFiles, panoFileName, 0) != 0) {
            PrintError("Error creating PSD file");
            return (-1);
        }

        for (loopCounter = 0; loopCounter < counterImageFiles; loopCounter++) {
            remove(fullPathImages[loopCounter].name);
        }

        free(fullPathImages);
        return (0);
    }


  /************ OUTPUT FORMATS: Flattened files ***************/
    // All other formats require us to "flatten" the intermediate layers into
    // one final document...general approach is to flatten to a single TIFF file, 
    // and then convert this to the desired output file format (e.g. JPEG, PNG, etc.)
    if (counterImageFiles > 1) 
    {

    if (!panoFlattenTIFF
        (fullPathImages, counterImageFiles, &fullPathImages[0], TRUE)) 
        {
        PrintError("Error while flattening TIFF-image");
        goto mainError;
    }

    }
    panoReplaceExt(panoFileName->name, ".tif");
    rename(fullPathImages[0].name, panoFileName->name);

    free(fullPathImages);

    //Desired output format is TIFF...no further conversion needed
    if (strcmp(output_file_format, "TIFF") == 0
        || strcmp(output_file_format, "TIF") == 0)
        return (0);


    //Read back in again so we can convert to final desired format
    if (panoImageRead(&resultPanorama, panoFileName) == 0) {
        PrintError("Could not read result image %s", panoFileName->name);
        goto mainError;
    }

    remove(panoFileName->name);

    if (strcmp(output_file_format, "QTVR") == 0)
        return Create_QTVR(&resultPanorama, panoFileName);

    if (strcmp(output_file_format, "IVR_java") == 0) {
        if (panoProjection == 1)
            return Unknown03(&resultPanorama, panoFileName);
        else
            return Unknown02(&resultPanorama, panoFileName);
    }

    if (strcmp(output_file_format, "VRML") == 0)
        return Unknown05(&resultPanorama, panoFileName);

    if (strncmp(output_file_format, "IVR", 3) == 0) {   // compare first 3 characters of it // end at 804ae10
        if (panoProjection == 1)
            return Unknown01(&resultPanorama, panoFileName);
        else
            return Create_LP_ivr(&resultPanorama, panoFileName);
    }

    if (strcmp(output_file_format, "PAN") == 0) {       // 
        return Unknown04(&resultPanorama, panoFileName);
    }                           // 804ae10

    if (strcmp(output_file_format, "JPEG") == 0
        || strcmp(output_file_format, "JPG") == 0) {
        if (!ptQuietFlag) {
            char temp[100];
            
            sprintf(temp, "Creating JPEG (quality %d jpegProgressive %d)\n",
                    defaultVRPanoOptions.cquality,
                    defaultVRPanoOptions.progressive);
                    
            Progress(_initProgress, temp);
        }
        panoReplaceExt(panoFileName->name, ".jpg");
        return writeJPEG(&resultPanorama, panoFileName,
                         defaultVRPanoOptions.cquality,
                         defaultVRPanoOptions.progressive);
    }


    if (strcmp(output_file_format, "PSD") == 0) {
        panoReplaceExt(panoFileName->name, ".psd");
        return (writePSD(&resultPanorama, panoFileName));

    }

    if (strcmp(output_file_format, "PNG") == 0) {
        panoReplaceExt(panoFileName->name, ".PNG");
        return (writePNG(&resultPanorama, panoFileName));
    }

    PrintError("Panorama output format not supported: %s",
               output_file_format);

#endif



/*
 * Because this function can be called with a directory name with a period
 * inside it (e.g. "c:\dir\another.dir\filewithoutextension") then we need to 
 * make sure that the . happens after the last \ otherwise we'd truncate 
 * the directory name rather than replacing the extension
 */
void panoReplaceExt(char *filename, char *extension)
{
    char *dot_pos = strrchr(filename, '.');
    char *path_sep_win = strrchr(filename, '\\');
    char *path_sep_unix = strrchr(filename, '/');
        char *path_sep = (path_sep_unix == NULL ? path_sep_win : path_sep_unix );

    if (dot_pos != NULL && (path_sep == NULL || dot_pos>path_sep)) {
        strcpy(dot_pos, extension);
    }
    else {
        strcat(filename, extension);
    }
    return;
}





int panoFlattenTIFF(fullPath * fullPathImages, int counterImageFiles,
                    fullPath * outputFileName, int removeOriginals)
{

    pano_Tiff **tiffFileHandles;
    pano_Tiff *outputFile;

    unsigned char **imageDataBuffers;
    unsigned char *resultBuffer;


    fullPath tmpFullPath;
    char tmpFilename[512];


    pano_CropInfo *cropInfo;
    unsigned int linesPerPass;
    pano_ImageMetadata *outputMetadata;

    unsigned int i;
    unsigned int offsetBeforeThisPass = 0;
    int linesLeft = 0;
    unsigned int linesToRead;
    int rowInPass;
    int inputImageRowIndex;
    int outputImageRowIndex;
    unsigned char *pixelPtr;

    //Open up all intermediate TIFF files at once

    assert(fullPathImages != NULL);
    assert(counterImageFiles > 1);
    assert(outputFileName != NULL);

    tiffFileHandles = calloc(counterImageFiles, sizeof(pano_Tiff));

    if (tiffFileHandles == NULL) {
        PrintError("Not enough memory");
        return 0;
    }

    for (i = 0; (int) i < counterImageFiles; i++) {

        if (GetFullPath(&fullPathImages[i], tmpFilename) != 0) {
            PrintError("Could not get filename");
            return 0;
        }

        if ((tiffFileHandles[i] = panoTiffOpen(tmpFilename)) == NULL) {
            PrintError("Could not open TIFF-Layer %d", i);
            return 0;
        }

    }

//////////////////////////////////////////////////////////////////////


    //modify "tmpFullPath" to contain the name of a new, empty temp file
    if (panoFileMakeTemp(&tmpFullPath) == 0) {
        PrintError("Could not make Tempfile");
        return 0;
    }

    //copy the name of this new tmpFullPath into a string (tmpFilename)
    if (GetFullPath(&tmpFullPath, tmpFilename) != 0) {
        PrintError("Could not get filename");
        return 0;
    }

    // Because the 0th intermediate TIFF file might be a "cropped" file, we 
    // need to update the imageParameters so that the dimensions reflect the
    // the size of the full-sized output image, rather than one of the 
    // (potentially) cropped intermediate files

    if ((outputFile =
         panoTiffCreateUnCropped(tmpFilename,
                                 &tiffFileHandles[0]->metadata)) == 0) {
        PrintError("Could not create TIFF file");
        return 0;
    }

    // Calculate number of lines to read at a time so that we are reading 
    // approximately 500 KB at a time from each input file.  This could be 
    // memory intensive if we have an awful lot of images and not much memory, 
    // but probably not a big problem for 99.9% of cases on 99.9% of machines.
    linesPerPass = 500000 / outputFile->metadata.bytesPerLine;

    if (linesPerPass == 0)
        linesPerPass = 1;

    outputMetadata = &outputFile->metadata;

    // We dont need to read more lines that the size of the file
    if (outputMetadata->imageHeight < linesPerPass) {
        linesPerPass = outputMetadata->imageHeight;
        if (linesPerPass == 0) {
            PrintError
                ("Invalid image length in TIFF file. It might be corrupted");
            return -1;
        }
    }

    // Create as many image data buffers as we have input files.  Note that the 
    // input buffers are as wide as the final output image, which may be more
    // than we technically need if the input images are cropped...it makes the 
    // code simpler, however.
    imageDataBuffers = calloc(counterImageFiles, sizeof(unsigned char *));

    for (i = 0; (int) i < counterImageFiles; i++) {
        imageDataBuffers[i] =
            calloc(linesPerPass * outputMetadata->bytesPerLine, 1);
        if (imageDataBuffers[i] == NULL) {
            PrintError("Not enough memory to allocate input buffers");
            return -1;
        }
    }

    //we need one buffer to store output result
    resultBuffer = calloc(linesPerPass * outputMetadata->bytesPerLine, 1);

    if (resultBuffer == NULL) {
        PrintError("Not enough memory to allocate output buffer");
        return -1;
    }

    offsetBeforeThisPass = 0;

    if (ptQuietFlag == 0) {
        Progress(_initProgress, "Flattening Image");
    }

    //  printf("To do %d lines\n", outputMetadata->imageHeight);

    linesLeft = outputMetadata->imageHeight;

    // Main flattening loop...iterate over input files, read some data from each, 
    // combine into output buffer, write to file
    while (linesLeft > 0) {

        linesToRead = (linesLeft > (int)linesPerPass) ? linesPerPass : linesLeft;

        // iterate over each input file
        for (i = 0; (int) i < counterImageFiles; i++) {
            cropInfo = &(tiffFileHandles[i]->metadata.cropInfo);

            // Get a few lines of data from this input file one row at a time
            for (rowInPass = 0; rowInPass < (int) linesToRead; rowInPass++) {

                //figure out which row to read/write from input/output images
                outputImageRowIndex = offsetBeforeThisPass + rowInPass;
                inputImageRowIndex = outputImageRowIndex - cropInfo->yOffset;

                //point to first byte on this row of the input buffer
                pixelPtr =
                    imageDataBuffers[i] +
                    (outputMetadata->bytesPerLine * rowInPass);

                //clear out any old data, and fill with empty space (zeros)
                memset(pixelPtr, 0, outputMetadata->bytesPerLine);

                // Only try to read data if we are reading from a row that exists in the 
                // input image        
                if (inputImageRowIndex >= 0
                    && inputImageRowIndex < cropInfo->croppedHeight) {
                    if (TIFFReadScanline
                        (tiffFileHandles[i]->tiff,
                         pixelPtr +
                         (cropInfo->xOffset * outputMetadata->bytesPerPixel),
                         inputImageRowIndex, 0) != 1) {
                        PrintError("Error reading tiff file\n");
                        return 0;
                    }
                }
            }
        }

        //    printf("Passing offsetAfterThisPass [%d] of [%d] linesPerPass  %d \n",offsetAfterThisPass, outputMetadata->imageHeight, linesPerPass);

        if (ptQuietFlag == 0) {
            sprintf(tmpFilename, "%d",
                    (offsetBeforeThisPass +
                     linesToRead) * 100 / outputMetadata->imageHeight);
            if (Progress(_setProgress, tmpFilename) == 0)
                return 0;
        }

        // FlattenImageSection
        panoStitchBlendLayers(imageDataBuffers, counterImageFiles, resultBuffer,
                              linesToRead, outputMetadata->imageWidth,
                              outputMetadata->bitsPerPixel,
                              outputMetadata->bytesPerLine);

        for (i = 0; i < linesToRead; i++) {
            if (TIFFWriteScanline
                (outputFile->tiff,
                 resultBuffer + outputMetadata->bytesPerLine * i,
                 offsetBeforeThisPass + i, 0) != 1) {
                PrintError("Unable to write TIFF to file\n");
                return 0;
            }
        }

        offsetBeforeThisPass += linesToRead;
        linesLeft -= linesToRead;

    }

    if (!ptQuietFlag)
        Progress(_disposeProgress, "Done flattening.");

    //  printf("Lines read %d from %d\n", offsetBeforeThisPass,outputMetadata->imageHeight);

    for (i = 0; (int) i < counterImageFiles; i++) {
        free(imageDataBuffers[i]);
        panoTiffClose(tiffFileHandles[i]);
    }

    panoTiffClose(outputFile);

    if (removeOriginals) {
        for (i = 0; (int) i < counterImageFiles; i++) {
            remove(fullPathImages[i].name);
        }
    }

    rename(tmpFullPath.name, outputFileName->name);

    free(tiffFileHandles);

    free(imageDataBuffers);
    free(resultBuffer);

    return 1;

}




// the functionality of PTcrop and PTuncrop is essentially identical, except for
// the function that they call

int panoCroppingMain(int argc,char *argv[], int operation, char *version, char *usage, char *defaultPrefix)
{
    char opt;
    int ptForceProcessing = 0;
    int filesCount;
    int retVal;
    FILE *testFile;
    pano_cropping_parms croppingParms;
    char outputPrefix[MAX_PATH_LENGTH];
    int ptDeleteSources = 0;
    fullPath *ptrInputFiles;
    fullPath *ptrOutputFiles;
    int base;
    int i;
  
    // Set defaults
    strcpy(outputPrefix, defaultPrefix);
    bzero(&croppingParms, sizeof(croppingParms));
    
    printf(version);
    
    //Need enough space for a message to be returned if something goes wrong
  
    while ((opt = getopt(argc, argv, "p:fqhx")) != -1) {

        // o overwrite
        // h       -> help
        // q       -> quiet?
    
        switch(opt) {  // fhoqs        f: 102 h:104  111 113 115  o:f:hsq
	case 'p':
	    if (strlen(optarg) < MAX_PATH_LENGTH) {
		strcpy(outputPrefix, optarg);
	    } else {
		PrintError("Illegal length for output prefix");
		return -1;
	    }
	    break;
        case 'f':
            ptForceProcessing = 1;
            break;
	case 'x':
	    ptDeleteSources = 1;
            break;
        case 'q':
            ptQuietFlag = 1;
            break;
        case 'h':
            printf(usage);
            exit(0);
        default:
            break;
        }
    }
    filesCount = argc - optind;

    if (filesCount < 1) {
        PrintError("No files specified in the command line");
        printf(usage);
        exit(0);
    }
    // Allocate memory for filenames
    if ((ptrInputFiles = calloc(filesCount, sizeof(fullPath))) == NULL || 
        (ptrOutputFiles = calloc(filesCount, sizeof(fullPath))) == NULL)        {
        PrintError("Not enough memory");
        return -1;
    }

    // GET input file names
    base = optind;
    for (; optind < argc; optind++) {
        char *currentParm;

        currentParm = argv[optind];

        if (StringtoFullPath(&ptrInputFiles[optind-base], currentParm) !=0) { // success
            PrintError("Syntax error: Not a valid pathname");
            return(-1);
        }
    }
        // Generate output file names
    if (panoFileOutputNamesCreate(ptrOutputFiles, filesCount, outputPrefix) == 0) {
	return -1;
    }

    if (!ptForceProcessing) {
	char *temp;
	if ((temp = panoFileExists(ptrOutputFiles, filesCount)) != NULL) {
	    PrintError("Output filename exists %s. Use -f to overwrite", temp);
	    return -1;
	}
    }
    if (! ptQuietFlag) printf("Cropping %d files\n", filesCount);
  
    for (i=0; i< filesCount; i++) {


	if (!ptQuietFlag) {
	    PrintError("Processing %d reading %s creating %s", i, ptrInputFiles[i].name, ptrOutputFiles[i].name);
	}
	croppingParms.forceProcessing = ptForceProcessing;
	switch (operation) {
	case PANO_CROPPING_CROP:
	    retVal = panoTiffCrop(ptrInputFiles[i].name, ptrOutputFiles[i].name, &croppingParms);
	    break;
	case PANO_CROPPING_UNCROP:
	    retVal = panoTiffUnCrop(ptrInputFiles[i].name, ptrOutputFiles[i].name, &croppingParms);
	    break;
	default:
	    PrintError("Illegal operation in panoCroppingMain. Programming error");
	    exit(0);
	}


	if (! retVal ) {
	    PrintError("Error cropping file %s", ptrInputFiles[i].name);
	    return -1;
	}
    }
    if (ptDeleteSources) {
	panoFileDeleteMultiple(ptrInputFiles, filesCount);
    }
    if (ptrInputFiles != NULL)
	free(ptrInputFiles);
    if (ptrOutputFiles != NULL)
	free(ptrOutputFiles);

    return 0;
}