1 /*
2  *  PTfeather.c
3  *
4  *  Many of the routines are based on the program PTStitcher by Helmut
5  *  Dersch.
6  *
7  *  Copyright Helmut Dersch and Daniel M. German
8  *
9  *  Nov 2006
10  *
11  *  This program is free software; you can redistribute it and/or
12  *  modify it under the terms of the GNU General Public
13  *  License as published by the Free Software Foundation; either
14  *  version 2 of the License, or (at your option) any later version.
15  *
16  *  This software is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *  General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this software; see the file COPYING.  If not, a copy
23  *  can be downloaded from http://www.gnu.org/licenses/gpl.html, or
24  *  obtained by writing to the Free Software Foundation, Inc.,
25  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  *
28  *  Author: Daniel M German dmgerman at uvic doooot ca
29  *
30  */
31 
32 
33 #include "filter.h"
34 
35 #include "pttiff.h"
36 #include "file.h"
37 #include "PTcommon.h"
38 #include "ptstitch.h"
39 #include "metadata.h"
40 #include "ptfeather.h"
41 
42 #include <assert.h>
43 #include <float.h>
44 
panoFeatherSnowPixel8Bit(unsigned char * pixel,int featherSize,unsigned int index)45 static void panoFeatherSnowPixel8Bit(unsigned char *pixel, int featherSize, unsigned int index)
46 {
47     int newPixel = 0;
48     int randomComponent = 0;
49     unsigned int level;
50 
51     //    printf("Value %d %d\n", *pixel, index);
52 
53     // This operation could potentially overflow
54     level = (index * 255)/ featherSize;
55 
56     // TODO: check this expression. It needs to be evaluated in the order specified by the
57     // parenthesis
58 
59     //Make sure we do the arithmetic in long long to avoid overflows
60 
61     randomComponent = ((rand() - RAND_MAX/2) * (0xfeLL /featherSize)) / RAND_MAX;
62 
63     // we need to split the following expression to guarantee it is computed as integer, not unsigned char
64 
65     newPixel = *pixel;
66     newPixel = newPixel-  level  + randomComponent;
67 
68     //    printf("Value %d newvalue %d Contribution %d Random %d\n", *pixel, newPixel, level, randomComponent);
69 
70     if ( newPixel < 0 )
71 	// we can't make it zero. We rely on value 1 to know where the actual edge of an image is
72 	*pixel = 0;
73     else if (newPixel > 0xff)
74 	*pixel = 0xff;
75     else
76 	*pixel = newPixel;
77 }
78 
panoFeatherSnowPixel16Bit(unsigned char * pixel,int featherSize,int index)79 static void panoFeatherSnowPixel16Bit(unsigned char *pixel, int featherSize, int index)
80 {
81     int newPixel = 0;
82     int randomComponent = 0;
83     unsigned long long int level;
84 
85     uint16_t *pixel16;
86 
87 
88     level = (index * 0xffff)/ featherSize;
89 
90     pixel16  = (uint16_t *) pixel;
91 
92     // Make sure we do the arithmetic in long long to avoid overflows
93     randomComponent = ((rand() - RAND_MAX/2) * (0xfe00LL /featherSize)) / RAND_MAX;
94 
95     newPixel = (int)(*pixel16  -  level  + randomComponent);
96 
97     //    printf("Value %d newvalue %d Contribution %d Random %d\n", *pixel16, newPixel, level, randomComponent);
98 
99     if ( newPixel <= 0 )
100       // we can't make it zero. We rely on value 1 to know where the actual edge of an image is
101       *pixel16 = 0;
102     else if (newPixel > 0xffff)
103       *pixel16 = 0xffff;
104     else {
105       *pixel16 = newPixel;
106     }
107 }
108 
panoFeatherSnowPixel(unsigned char * pixel,int featherSize,int index,int bytesPerSample)109 static void panoFeatherSnowPixel(unsigned char *pixel, int featherSize, int index, int bytesPerSample)
110 {
111     if (bytesPerSample == 1)
112 	panoFeatherSnowPixel8Bit(pixel, featherSize, index);
113     else if (bytesPerSample == 2)
114 	panoFeatherSnowPixel16Bit(pixel, featherSize, index);
115     else
116 	assert(0);
117 }
118 
119 
panoFeatherSnowingHorizontalLeft(int column,int featherSize,unsigned char * ptrData,Image * image)120 static void panoFeatherSnowingHorizontalLeft(int column, int featherSize, unsigned char *ptrData, Image *image)
121 {
122     int index;
123     int currentColumn;
124 
125     unsigned char *ptrPixel;
126     unsigned int pixel;
127     int bytesPerPixel = panoImageBytesPerPixel(image);
128     int bytesPerSample = panoImageBytesPerSample(image);
129 
130     // ptrData points to the beginning of the line
131 
132     // We start to the right, because the current column is the empty one
133     for (currentColumn = column+1, index = featherSize;  currentColumn <  column + featherSize+1; currentColumn++, index-- ) {
134 
135 
136         // only operate within the image
137         // and IF the mask is not zero
138         // We do not want to "feather" outside the boundary
139         if (currentColumn < 0 || currentColumn >= panoImageWidth(image))
140             continue;
141 
142         ptrPixel = ptrData + currentColumn * bytesPerPixel;
143 	pixel = panoStitchPixelChannelGet(ptrPixel, bytesPerSample, 0);
144 
145 
146 	if (pixel == 0) {// stop when we find the edge
147 	    //	    printf("Breaking %d\n", currentColumn);
148 	    break;
149 	}
150 	panoFeatherSnowPixel(ptrPixel, featherSize, index, bytesPerSample);
151 
152     } ///for
153 
154     //    printf("End\n");
155 }
156 
panoFeatherSnowingHorizontalRight(int column,int featherSize,unsigned char * ptrData,Image * image)157 static void panoFeatherSnowingHorizontalRight(int column, int featherSize, unsigned char *ptrData, Image *image)
158 {
159     int index;
160     int currentColumn;
161 
162     unsigned int pixel;
163     unsigned char *ptrPixel;
164     int bytesPerPixel = panoImageBytesPerPixel(image);
165     int bytesPerSample = panoImageBytesPerSample(image);
166 
167     // ptrData points to the beginning of the line
168 
169     //    panoFeatherSnowingAreaVerticalFind(ptrData, bytesPerLine, gradient, column, &leftLines, &rightLines);
170 
171     // determine where we start snowing to the left
172 
173     index = 1;
174 
175     for (currentColumn = column, index = featherSize;  currentColumn >  column - featherSize; currentColumn--, index-- ) {
176 
177 
178         // only operate within the image
179         // and IF the mask is not zero
180         // We do not want to "feather" outside the boundary
181         if (currentColumn < 0 || currentColumn >= panoImageWidth(image))
182             continue;
183 
184         ptrPixel = ptrData + currentColumn * bytesPerPixel;
185 	pixel = panoStitchPixelChannelGet(ptrPixel, bytesPerSample, 0);
186 
187 	if (pixel == 0) {// stop when we find the edge
188 	    //	    printf("Breaking %d\n", currentColumn);
189 	    break;
190 	}
191 	panoFeatherSnowPixel(ptrPixel, featherSize, index, bytesPerSample);
192 
193     } ///for (currentColumn = column - gradient/2;      currentColumn <= column; currentColumn++, index++ ) {
194 
195     //    printf("End\n");
196 }
197 
panoFeatherSnowingVerticalBottom(int row,int featherSize,unsigned char * ptrData,Image * image)198 static void panoFeatherSnowingVerticalBottom(int row, int featherSize, unsigned char *ptrData, Image *image)
199 {
200     int index;
201     int currentRow;
202     int pixel;
203     unsigned char *ptrPixel;
204 
205     int bytesPerLine = panoImageBytesPerLine(image);
206     int bytesPerSample = panoImageBytesPerSample(image);
207 
208     for (currentRow = row, index=featherSize;  currentRow > row - featherSize; currentRow--, index-- ) {
209 
210         // only operate within the image
211         // and IF the mask is not zero
212         // We do not want to "feather" outside the boundary
213         if (currentRow < 0 || currentRow >= panoImageHeight(image)) {
214             continue;
215         }
216 
217         ptrPixel = ptrData + currentRow * bytesPerLine;
218 
219 	pixel = panoStitchPixelChannelGet(ptrPixel, bytesPerSample, 0);
220 
221 	if (pixel == 0) {// stop when we find the edge
222 	    //	    printf("Breaking %d\n", currentRow);
223 	    break;
224 	}
225 	//	printf("Doing...\n");
226 
227 	panoFeatherSnowPixel(ptrPixel, featherSize, index, bytesPerSample);
228 
229     } ///for (currentRow = row - gradient/2;      currentRow <= row; currentRow++, index++ ) {
230 
231 
232 }
233 
panoFeatherSnowingVerticalTop(int row,int featherSize,unsigned char * ptrData,Image * image)234 static void panoFeatherSnowingVerticalTop(int row, int featherSize, unsigned char *ptrData, Image *image)
235 {
236     int index;
237     int currentRow;
238     int pixel;
239     unsigned char *ptrPixel;
240 
241     int bytesPerLine = panoImageBytesPerLine(image);
242     int bytesPerSample = panoImageBytesPerSample(image);
243 
244     for (currentRow = row+1, index=featherSize;  currentRow < row + featherSize+1; currentRow++, index-- ) {
245 
246         // only operate within the image
247         // and IF the mask is not zero
248         // We do not want to "feather" outside the boundary
249 
250         if (currentRow < 0 || currentRow >= panoImageHeight(image)) {
251             continue;
252         }
253 
254         ptrPixel = ptrData + currentRow * bytesPerLine;
255 
256 	pixel = panoStitchPixelChannelGet(ptrPixel, bytesPerSample, 0);
257 
258 	if (pixel == 0) {// stop when we find the edge
259 	    //	    printf("Breaking %d\n", currentRow);
260 	    break;
261 	}
262 	panoFeatherSnowPixel(ptrPixel, featherSize, index, bytesPerSample);
263     } ///for (currentRow = row - gradient/2;      currentRow <= row; currentRow++, index++ ) {
264 
265 }
266 
267 
268 
panoFeatherMaskReplace(Image * image,unsigned int from,unsigned int to)269 void panoFeatherMaskReplace(Image* image, unsigned int from, unsigned int to)
270 {
271 
272     // Replace a given value in the first channel with the desired value
273 
274     int row;
275     int column;
276     uint16_t *pixel16;
277 
278     int bitsPerSample = panoImageBitsPerSample(image);
279 
280     int bytesPerPixel = panoImageBytesPerPixel(image);
281 
282     int bytesPerLine = panoImageBytesPerLine(image);
283 
284     int imageHeight = panoImageHeight(image);
285 
286     int imageWidth = panoImageWidth(image);
287 
288     unsigned char *pixel = panoImageData(image);
289 
290 
291     for (row = 0; row < imageHeight; row ++) {
292 
293 	pixel = panoImageData(image) + row * bytesPerLine;
294 
295         for (column = 0; column < imageWidth; column ++, pixel += bytesPerPixel) {
296 	    if (bitsPerSample == 8) {
297 		if ( *pixel == from ) {
298 		    *pixel = to;
299 		}
300 	    }
301 	    else if (bitsPerSample == 16) {
302 		pixel16  = (uint16_t *) pixel;
303 		if (*pixel16 == from) {
304 		    *pixel16 = to;
305 		}
306 	    } else {
307 		assert(0);
308 	    }
309         } // for column
310 
311     } // for row
312 
313 }
314 
panoFeatherChannelSave(unsigned char * channelBuffer,Image * image,int channel)315 void panoFeatherChannelSave(unsigned char *channelBuffer, Image *image, int channel)
316 {
317   // Copy a given channel to a preallocated buffer area
318   int i, j,k;
319   int bytesPerChannel;
320   unsigned char *imageData;
321   int bytesPerPixel;
322 
323   bytesPerChannel = panoImageBytesPerSample(image);
324   imageData = panoImageData(image);
325   bytesPerPixel = panoImageBytesPerPixel(image);
326 
327   for (i=0;i<panoImageWidth(image);i++)
328     for (j=0;j<panoImageHeight(image);j++) {
329       for (k=0;k<bytesPerChannel;k++) {
330             *(channelBuffer+k) = *(imageData + bytesPerChannel * channel + k);
331       }
332       channelBuffer += bytesPerChannel;
333       imageData += bytesPerPixel;
334     }
335 }
336 
panoFeatherChannelMerge(unsigned char * channelBuffer,Image * image,int channel)337 void panoFeatherChannelMerge(unsigned char *channelBuffer, Image *image, int channel)
338 {
339   // We merge two alpha channels using the  "multiply" operation.
340 
341   // Copy a given channel to a preallocated buffer area
342   int i, j;
343   int bytesPerChannel;
344   unsigned char *imageData;
345   int bytesPerPixel;
346   unsigned int a, b;
347   unsigned long long int la, lb;
348 
349   // FIrst test the rest of the logic before we do this
350 
351   bytesPerChannel = panoImageBytesPerSample(image);
352   imageData = panoImageData(image);
353   bytesPerPixel = panoImageBytesPerPixel(image);
354 
355   for (i=0;i<panoImageWidth(image);i++)
356   for (j=0;j<panoImageHeight(image);j++) {
357     if (bytesPerChannel == 1) {
358       a = *(imageData);
359       b = *(channelBuffer);
360 
361       if (a < b)
362           *(imageData) = a;
363       else
364         *(imageData) = b;
365     } else if (bytesPerChannel == 2) {
366       la = *((uint16_t*)imageData);
367       lb = *((uint16_t*)channelBuffer);
368       if (la < lb)
369         *((uint16_t*)imageData) = (uint16_t)(la);
370       else
371         *((uint16_t*)imageData) = (uint16_t)(lb);
372     } else {
373         assert(0);
374     }
375     channelBuffer += bytesPerChannel;
376     imageData += bytesPerPixel;
377   }
378 }
379 
panoFeatherChannelSwap(unsigned char * channelBuffer,Image * image,int channel)380 void panoFeatherChannelSwap(unsigned char *channelBuffer, Image *image, int channel)
381 {
382     // Swaps the data from a given channel
383     int i, j,k;
384     int bytesPerChannel;
385     unsigned char temp;
386     unsigned char *imageData;
387     int bytesPerPixel;
388 
389     bytesPerChannel = panoImageBytesPerSample(image);
390     imageData = panoImageData(image);
391     bytesPerPixel = panoImageBytesPerPixel(image);
392     //    printf("Bytes per channel %d\n", bytesPerChannel);
393     for (i=0;i<panoImageWidth(image);i++)
394 	for (j=0;j<panoImageHeight(image);j++) {
395 	    for (k=0;k<bytesPerChannel;k++) {
396 		temp = *(channelBuffer+k);
397 		*(channelBuffer+k) = *(imageData + bytesPerChannel * channel + k);
398 		*(imageData + bytesPerChannel * channel + k) = temp;
399 	    }
400 	    channelBuffer += bytesPerChannel;
401 	    imageData += bytesPerPixel;
402 	}
403 }
404 
405 
406 
407 
panoFeatherImage(Image * image,int featherSize)408 static int panoFeatherImage(Image * image, int featherSize)
409 {
410 
411     int ratio;
412     int difference;
413     unsigned char *pixelPtr;
414     unsigned char *ptrData;
415     unsigned char *savedAlphaChannel;
416     int column;
417     int row;
418     int gradient;
419 
420     int bytesPerPixel;
421     int bytesPerLine;
422     int imageWidth;
423     int imageHeight;
424     int imageIsCropped;
425     int imageLeftOffset;
426     int imageFullWidth;
427     int imageFullHeight;
428     int bitsPerSample;
429     int bytesPerSample;
430     unsigned char *imageData;
431 
432     if (featherSize == 0)
433 	return 1;
434 
435 
436     // Use local variables so we don't have to make function calls for each
437     // iteration
438     bitsPerSample = panoImageBitsPerSample(image);
439     bytesPerSample = bitsPerSample /8;
440     bytesPerPixel = panoImageBytesPerPixel(image);
441     bytesPerLine  = panoImageBytesPerLine(image);
442     imageHeight = panoImageHeight(image);
443     imageWidth = panoImageWidth(image);
444     imageIsCropped = panoImageIsCropped(image);
445     imageData = panoImageData(image);
446     imageFullWidth = panoImageFullWidth(image);
447     imageFullHeight = panoImageFullHeight(image);
448 
449     imageLeftOffset  = panoImageOffsetX(image);
450 
451     // This is sort of a hack. We replace 0's in the mask with 1's
452     // we have to "undo" it at the end
453 
454     //    panoFeatherMaskReplace(image, 0, 1);
455 
456     ratio = 0xfe00 / featherSize;
457 
458     // Horizontal first
459 
460     assert(bitsPerSample == 8 ||
461 	   bitsPerSample == 16);
462 
463     // This algorithm is not perfect. It does not deal very well with images that have very wavy edges.
464     // For that reason I feather in one direction, then in the other, and then I combine the feathers
465     // This means we need to allocate space for an extra channel
466 
467     savedAlphaChannel = calloc(bytesPerLine * imageHeight, 1);
468     if (savedAlphaChannel == NULL) {
469 	return 0;
470     }
471     panoFeatherChannelSave(savedAlphaChannel, image, 0);
472     ptrData = imageData;
473 
474     for ( row = 0; row < imageHeight; row++, ptrData += bytesPerLine) {
475 	int widthToProcess;
476 
477 	pixelPtr = ptrData;
478 
479 	// The following code deals with images that are cropped. We should feather edges only
480 	// if they are not the absolute edge of an image.
481 
482 	// by default we start in column zero
483 	column = 0;
484 	widthToProcess = imageWidth;
485 	if (imageIsCropped) {
486 	    // we need to deal with edges that are not "real" edges (as in the uncropped image
487 
488 	    if ( imageLeftOffset > 0) {
489 		// we have a mask to the left... so we start in column "-1"
490 		column = -1;
491 	    }
492 
493 	    if (imageLeftOffset + widthToProcess < imageFullWidth) {
494 		// then "add" one pixel to the right */
495 		widthToProcess ++;
496 	    }
497 	}
498 
499 
500 	for (/*empty, see initialization above */; column < widthToProcess -1;
501 						 column ++, pixelPtr+=bytesPerPixel) {
502 
503 	    // Values of mask in this pixel and next
504 	    int thisPixel;
505 	    int nextPixel;
506 
507 
508 	    if (column < 0) {
509 		// this is the imaginary pixel to the left of the edge that should be feathered
510 		thisPixel = 1;
511 	    } else  {
512 		thisPixel = panoStitchPixelChannelGet(pixelPtr, bytesPerSample, 0);
513 	    }
514 
515 	    if (column >= imageWidth -1) {
516 		// this is the imaginary pixel to the right of the edge that should be feathered
517 		nextPixel = 1;
518 	    } else {
519 		nextPixel = panoStitchPixelChannelGet(pixelPtr + bytesPerPixel, bytesPerSample, 0);
520 	    }
521 
522 	    difference = thisPixel - nextPixel;
523 
524 	    // This operation needs to be done here, otherwise 0x100/ratio will underflow
525 	    if (bitsPerSample == 8) {
526 		gradient = (abs(difference) * 0x100LL) / ratio;
527 	    }
528 	    else if (bitsPerSample == 16) {
529 		gradient = abs(difference) / ratio;
530 	    } else
531 		assert(0);
532 
533 	    if (nextPixel == 0 && thisPixel != 0) {
534 		// Moving from the mask... proceed if there is not
535 
536 		if ( gradient > 1 ) { //
537 		    panoFeatherSnowingHorizontalRight(column, featherSize, ptrData, image);
538 		}
539 	    }
540 
541 	    if (thisPixel == 0  && nextPixel != 0) {
542 		if ( gradient > 1 ) { //
543 		    panoFeatherSnowingHorizontalLeft(column, featherSize, ptrData, image);
544 		} //
545 
546 	    } //
547 
548 	} // for column...
549 
550     } // for row
551 
552     // We need to do the same in the orthogonal direction
553     // Sometimes I wished I had  iterators over an image...
554 
555     panoFeatherChannelSwap(savedAlphaChannel, image, 0);
556 
557     ptrData = imageData;
558 
559     for (column = 0; column < image->width; column ++, ptrData+=bytesPerPixel) {
560 	int heightToProcess;
561 
562 	// The following code deals with images that are cropped. We should feather edges only
563 	// if they are not the absolute edge of an image.
564 
565 	// by default we start in column zero
566 	row = 0;
567 	heightToProcess = imageHeight;
568 
569 	if (imageIsCropped) {
570 	    // we need to deal with edges that are not "real" edges (as in the uncropped image
571 	    int imageTopOffset;
572 
573 	    imageTopOffset  = panoImageOffsetY(image);
574 
575 	    if ( imageTopOffset > 0) {
576 		// we have a mask to the left... so we start in column "-1"
577 		row = -1;
578 	    }
579 
580 	    if (imageTopOffset + heightToProcess < imageFullHeight) {
581 		// then "add" one pixel to the right */
582 		heightToProcess ++;
583 	    }
584 	}
585 
586 	pixelPtr = ptrData;
587 	for (/*empty, see initialization above */; row < heightToProcess - 1;
588 						 row++, pixelPtr += bytesPerLine) {
589 	    int thisPixel;
590 	    int nextPixel;
591 
592 	    // get pixel in current row
593 	    // with pixel in the next row
594 
595 	    if (row < 0) {
596 		// this is the imaginary pixel to the left of the edge that should be feathered
597 		thisPixel = 1;
598 	    } else  {
599 		thisPixel = panoStitchPixelChannelGet(pixelPtr, bytesPerSample, 0);
600 	    }
601 
602 	    if (row >= imageHeight -1) {
603 		// this is the imaginary pixel to the right of the edge that should be feathered
604 		nextPixel = 1;
605 	    } else {
606 		nextPixel = panoStitchPixelChannelGet(pixelPtr + bytesPerLine, bytesPerSample, 0);
607 	    }
608 
609 	    difference = thisPixel - nextPixel;
610 
611 	    // This operation needs to be done here, otherwise 0x100/ratio will underflow
612 	    if (bitsPerSample == 8) {
613 		gradient = (abs(difference) * 0x100LL) / ratio;
614 	    }
615 	    else if (bitsPerSample == 16) {
616 		gradient = abs(difference) / ratio;
617 	    } else
618 		assert(0);
619 
620 	    if (gradient > 1) {
621 
622 		if (nextPixel == 0 && thisPixel != 0) {
623 		    // Moving from the mask... proceed if there is not
624 		    panoFeatherSnowingVerticalBottom(row, featherSize, ptrData, image);
625 		}
626 
627 		if (nextPixel != 0 && thisPixel == 0) {
628 		    panoFeatherSnowingVerticalTop(row, featherSize, ptrData, image);
629 		}
630 	    }
631 
632 	} // for column...
633 
634     } // for row
635 
636     // Average mask to avoid banding
637 
638 #if 0
639 
640     // THIS WAS AN ATTEMPT TO REMOVE BANDING, BUT IT IS TOO SIMPLE... IT WOULD REQUIRE A LARGER AREA,
641     // OR EVEN BETTER, A KERNEL BASED BLUR
642 
643     {
644 	int row, column;
645 	unsigned int above, below,left, right, thisPixel;
646 
647 	pixelPtr = imageData;
648 
649 	for ( row = 0; row < imageHeight; row++) {
650 	    for ( column = 0; column < imageWidth; column++, pixelPtr += bytesPerPixel) {
651 		// average pixel to its pixels around
652 		thisPixel = panoStitchPixelChannelGet(pixelPtr, bytesPerSample, 0);
653 
654 
655 		if (thisPixel == 0)
656 		    continue;
657 
658 		if ((bitsPerSample == 8 && thisPixel == 0xff) ||
659 		    (bitsPerSample == 16 && thisPixel == 0xffff)) {
660 		    continue;
661 		}
662 
663 		// average to its neighbors
664 
665 		above = below = left = right = thisPixel;
666 		if (row > 0)
667 		    above = panoStitchPixelChannelGet(pixelPtr - bytesPerLine, bytesPerSample, 0);
668 
669 		if (row < imageHeight)
670 		    below = panoStitchPixelChannelGet(pixelPtr + bytesPerLine, bytesPerSample, 0);
671 
672 		if (column > 0)
673 		    left = panoStitchPixelChannelGet(pixelPtr - bytesPerLine, bytesPerSample, 0);
674 
675 		if (column < imageWidth)
676 		    right = panoStitchPixelChannelGet(pixelPtr + bytesPerLine, bytesPerSample, 0);
677 
678 		thisPixel = (thisPixel + above + below + left + right)/ 5;
679 
680 		panoStitchPixelChannelSet(pixelPtr, bytesPerSample, 0, thisPixel);
681 
682 	    }
683 	}
684     }
685 #endif
686 
687     panoFeatherChannelMerge(savedAlphaChannel, image, 0);
688     free(savedAlphaChannel);
689 
690     return 1;
691 }
692 
693 
panoFeatherFile(fullPath * inputFile,fullPath * outputFile,int featherSize)694 int panoFeatherFile(fullPath * inputFile, fullPath * outputFile,
695 		     int featherSize)
696 {
697     Image image;
698     if (panoTiffRead(&image, inputFile->name) == 0) {
699         PrintError("Could not open TIFF-file [%s]", inputFile->name);
700         return 0;
701     }
702 
703     if (panoImageBitsPerSample(&image) == 8 ||
704 	panoImageBitsPerSample(&image) == 16) {
705         panoFeatherImage(&image, featherSize);
706     }
707     else {
708         fprintf(stderr,
709                 "Apply feather not supported for this image type (%d bitsPerPixel)\n",
710                 (int) image.bitsPerPixel);
711         exit(1);
712     }
713 
714     if (panoTiffWrite(&image, outputFile->name) == 0) {
715         PrintError("Could not write TIFF-file [%s]", outputFile->name);
716         return 0;
717     }
718 
719     panoImageDispose(&image);
720 
721     return 1;
722 
723 }
724 
725