1 : /******************************************************************************
2 : * $Id: nearblack.cpp 25582 2013-01-29 21:13:43Z rouault $
3 : *
4 : * Project: GDAL Utilities
5 : * Purpose: Convert nearly black or nearly white border to exact black/white.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : * ****************************************************************************
9 : * Copyright (c) 2006, MapShots Inc (www.mapshots.com)
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gdal.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include <vector>
34 : #include "commonutils.h"
35 :
36 : CPL_CVSID("$Id: nearblack.cpp 25582 2013-01-29 21:13:43Z rouault $");
37 :
38 : typedef std::vector<int> Color;
39 : typedef std::vector< Color > Colors;
40 :
41 : static void ProcessLine( GByte *pabyLine, GByte *pabyMask, int iStart,
42 : int iEnd, int nSrcBands, int nDstBands, int nNearDist,
43 : int nMaxNonBlack, int bNearWhite, Colors *poColors,
44 : int *panLastLineCounts, int bDoHorizontalCheck,
45 : int bDoVerticalCheck, int bBottomUp);
46 :
47 : /************************************************************************/
48 : /* IsInt() */
49 : /************************************************************************/
50 :
51 6 : int IsInt( const char *pszArg )
52 : {
53 6 : if( pszArg[0] == '-' )
54 0 : pszArg++;
55 :
56 6 : if( *pszArg == '\0' )
57 0 : return FALSE;
58 :
59 24 : while( *pszArg != '\0' )
60 : {
61 12 : if( *pszArg < '0' || *pszArg > '9' )
62 0 : return FALSE;
63 12 : pszArg++;
64 : }
65 :
66 6 : return TRUE;
67 : }
68 :
69 : /************************************************************************/
70 : /* Usage() */
71 : /************************************************************************/
72 :
73 0 : void Usage(const char* pszErrorMsg = NULL)
74 : {
75 : printf( "nearblack [-of format] [-white | [-color c1,c2,c3...cn]*] [-near dist] [-nb non_black_pixels]\n"
76 0 : " [-setalpha] [-setmask] [-o outfile] [-q] [-co \"NAME=VALUE\"]* infile\n" );
77 :
78 0 : if( pszErrorMsg != NULL )
79 0 : fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg);
80 :
81 0 : exit( 1 );
82 : }
83 :
84 : /************************************************************************/
85 : /* main() */
86 : /************************************************************************/
87 :
88 : #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
89 : do { if (i + nExtraArg >= argc) \
90 : Usage(CPLSPrintf("%s option requires %d argument(s)", argv[i], nExtraArg)); } while(0)
91 :
92 8 : int main( int argc, char ** argv )
93 :
94 : {
95 : /* Check that we are running against at least GDAL 1.4 (probably older in fact !) */
96 : /* Note to developers : if we use newer API, please change the requirement */
97 8 : if (atoi(GDALVersionInfo("VERSION_NUM")) < 1400)
98 : {
99 : fprintf(stderr, "At least, GDAL >= 1.4.0 is required for this version of %s, "
100 0 : "which was compiled against GDAL %s\n", argv[0], GDAL_RELEASE_NAME);
101 0 : exit(1);
102 : }
103 :
104 : /* -------------------------------------------------------------------- */
105 : /* Generic arg processing. */
106 : /* -------------------------------------------------------------------- */
107 8 : GDALAllRegister();
108 8 : GDALSetCacheMax( 100000000 );
109 8 : argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
110 8 : if( argc < 1 )
111 0 : exit( -argc );
112 :
113 : /* -------------------------------------------------------------------- */
114 : /* Parse arguments. */
115 : /* -------------------------------------------------------------------- */
116 : int i;
117 8 : const char *pszOutFile = NULL;
118 8 : const char *pszInFile = NULL;
119 8 : int nMaxNonBlack = 2;
120 8 : int nNearDist = 15;
121 8 : int bNearWhite = FALSE;
122 8 : int bSetAlpha = FALSE;
123 8 : int bSetMask = FALSE;
124 8 : const char* pszDriverName = "HFA";
125 8 : int bFormatExplicitelySet = FALSE;
126 8 : char** papszCreationOptions = NULL;
127 8 : int bQuiet = FALSE;
128 :
129 8 : Colors oColors;
130 :
131 44 : for( i = 1; i < argc; i++ )
132 : {
133 37 : if( EQUAL(argv[i], "--utility_version") )
134 : {
135 : printf("%s was compiled against GDAL %s and is running against GDAL %s\n",
136 1 : argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
137 1 : return 0;
138 : }
139 36 : else if( EQUAL(argv[i], "--help") )
140 0 : Usage();
141 36 : else if( EQUAL(argv[i], "-o") )
142 : {
143 5 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
144 5 : pszOutFile = argv[++i];
145 : }
146 31 : else if( EQUAL(argv[i], "-of") )
147 : {
148 7 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
149 7 : pszDriverName = argv[++i];
150 7 : bFormatExplicitelySet = TRUE;
151 : }
152 24 : else if( EQUAL(argv[i], "-white") ) {
153 1 : bNearWhite = TRUE;
154 : }
155 :
156 : /***** -color c1,c2,c3...cn *****/
157 :
158 23 : else if( EQUAL(argv[i], "-color") ) {
159 2 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
160 2 : Color oColor;
161 :
162 : /***** tokenize the arg on , *****/
163 :
164 : char **papszTokens;
165 2 : papszTokens = CSLTokenizeString2( argv[++i], ",", 0 );
166 :
167 : /***** loop over the tokens *****/
168 :
169 : int iToken;
170 16 : for( iToken = 0; papszTokens && papszTokens[iToken]; iToken++ )
171 : {
172 :
173 : /***** ensure the token is an int and add it to the color *****/
174 :
175 6 : if ( IsInt( papszTokens[iToken] ) )
176 6 : oColor.push_back( atoi( papszTokens[iToken] ) );
177 : else {
178 : CPLError(CE_Failure, CPLE_AppDefined,
179 0 : "Colors must be valid integers." );
180 0 : CSLDestroy( papszTokens );
181 0 : exit(1);
182 : }
183 : }
184 :
185 2 : CSLDestroy( papszTokens );
186 :
187 : /***** check if the number of bands is consistant *****/
188 :
189 2 : if ( oColors.size() > 0 &&
190 : oColors.front().size() != oColor.size() )
191 : {
192 : CPLError(CE_Failure, CPLE_AppDefined,
193 0 : "ERROR: all -color args must have the same number of values.\n" );
194 0 : exit(1);
195 : }
196 :
197 : /***** add the color to the colors *****/
198 :
199 2 : oColors.push_back( oColor );
200 :
201 : }
202 :
203 21 : else if( EQUAL(argv[i], "-nb") )
204 : {
205 6 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
206 6 : nMaxNonBlack = atoi(argv[++i]);
207 : }
208 15 : else if( EQUAL(argv[i], "-near") )
209 : {
210 0 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
211 0 : nNearDist = atoi(argv[++i]);
212 : }
213 15 : else if( EQUAL(argv[i], "-setalpha") )
214 3 : bSetAlpha = TRUE;
215 12 : else if( EQUAL(argv[i], "-setmask") )
216 2 : bSetMask = TRUE;
217 11 : else if( EQUAL(argv[i], "-q") || EQUAL(argv[i], "-quiet") )
218 1 : bQuiet = TRUE;
219 9 : else if( EQUAL(argv[i], "-co") )
220 : {
221 2 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
222 2 : papszCreationOptions = CSLAddString(papszCreationOptions, argv[++i]);
223 : }
224 7 : else if( argv[i][0] == '-' )
225 0 : Usage(CPLSPrintf("Unkown option name '%s'", argv[i]));
226 7 : else if( pszInFile == NULL )
227 7 : pszInFile = argv[i];
228 : else
229 0 : Usage("Too many command options.");
230 : }
231 :
232 7 : if( pszInFile == NULL )
233 0 : Usage("No input file specified.");
234 :
235 7 : if( pszOutFile == NULL )
236 2 : pszOutFile = pszInFile;
237 :
238 : /* -------------------------------------------------------------------- */
239 : /* Open input file. */
240 : /* -------------------------------------------------------------------- */
241 7 : GDALDatasetH hInDS, hOutDS = NULL;
242 : int nXSize, nYSize, nBands;
243 :
244 7 : if( pszOutFile == pszInFile )
245 2 : hInDS = hOutDS = GDALOpen( pszInFile, GA_Update );
246 : else
247 5 : hInDS = GDALOpen( pszInFile, GA_ReadOnly );
248 :
249 7 : if( hInDS == NULL )
250 0 : exit( 1 );
251 :
252 7 : nXSize = GDALGetRasterXSize( hInDS );
253 7 : nYSize = GDALGetRasterYSize( hInDS );
254 7 : nBands = GDALGetRasterCount( hInDS );
255 7 : int nDstBands = nBands;
256 :
257 7 : if( hOutDS != NULL && papszCreationOptions != NULL)
258 : {
259 : CPLError(CE_Warning, CPLE_AppDefined,
260 0 : "Warning: creation options are ignored when writing to an existing file.");
261 : }
262 :
263 : /* -------------------------------------------------------------------- */
264 : /* Do we need to create output file? */
265 : /* -------------------------------------------------------------------- */
266 7 : if( hOutDS == NULL )
267 : {
268 5 : GDALDriverH hDriver = GDALGetDriverByName( pszDriverName );
269 5 : if (hDriver == NULL)
270 0 : exit(1);
271 :
272 5 : if (!bQuiet && !bFormatExplicitelySet)
273 0 : CheckExtensionConsistency(pszOutFile, pszDriverName);
274 :
275 5 : if (bSetAlpha)
276 : {
277 : /***** fixme there should be a way to preserve alpha band data not in the collar *****/
278 2 : if (nBands == 4)
279 0 : nBands --;
280 : else
281 2 : nDstBands ++;
282 : }
283 :
284 5 : if (bSetMask)
285 : {
286 1 : if (nBands == 4)
287 0 : nDstBands = nBands = 3;
288 : }
289 :
290 : hOutDS = GDALCreate( hDriver, pszOutFile,
291 : nXSize, nYSize, nDstBands, GDT_Byte,
292 5 : papszCreationOptions );
293 5 : if( hOutDS == NULL )
294 0 : exit( 1 );
295 :
296 : double adfGeoTransform[6];
297 :
298 5 : if( GDALGetGeoTransform( hInDS, adfGeoTransform ) == CE_None )
299 : {
300 4 : GDALSetGeoTransform( hOutDS, adfGeoTransform );
301 4 : GDALSetProjection( hOutDS, GDALGetProjectionRef( hInDS ) );
302 : }
303 : }
304 : else
305 : {
306 2 : if (bSetAlpha)
307 : {
308 1 : if (nBands != 4 &&
309 : (nBands < 2 ||
310 : GDALGetRasterColorInterpretation(GDALGetRasterBand(hOutDS, nBands)) != GCI_AlphaBand))
311 : {
312 : CPLError(CE_Failure, CPLE_AppDefined,
313 0 : "Last band is not an alpha band.");
314 0 : exit(1);
315 : }
316 :
317 1 : nBands --;
318 : }
319 :
320 2 : if (bSetMask)
321 : {
322 1 : if (nBands == 4)
323 0 : nDstBands = nBands = 3;
324 : }
325 : }
326 :
327 : /***** set a color if there are no colors set? *****/
328 :
329 7 : if ( oColors.size() == 0) {
330 6 : Color oColor;
331 :
332 : /***** loop over the bands to get the right number of values *****/
333 :
334 : int iBand;
335 24 : for (iBand = 0; iBand < nBands ; iBand++) {
336 :
337 : /***** black or white? *****/
338 :
339 18 : if (bNearWhite)
340 3 : oColor.push_back(255);
341 : else
342 15 : oColor.push_back(0);
343 : }
344 :
345 : /***** add the color to the colors *****/
346 :
347 6 : oColors.push_back(oColor);
348 :
349 : }
350 :
351 : /***** does the number of bands match the number of color values? *****/
352 :
353 7 : if ( (int)oColors.front().size() != nBands ) {
354 : CPLError( CE_Failure, CPLE_AppDefined,
355 0 : "-color args must have the same number of values as the non alpha input band count.\n" );
356 0 : exit(1);
357 : }
358 :
359 : /***** check the input and output datasets are the same size *****/
360 :
361 7 : if (GDALGetRasterXSize(hOutDS) != nXSize ||
362 : GDALGetRasterYSize(hOutDS) != nYSize)
363 : {
364 : CPLError(CE_Failure, CPLE_AppDefined,
365 : "The dimensions of the output dataset don't match "
366 0 : "the dimensions of the input dataset.");
367 0 : exit(1);
368 : }
369 :
370 :
371 : int iBand;
372 28 : for( iBand = 0; iBand < nBands; iBand++ )
373 : {
374 21 : GDALRasterBandH hBand = GDALGetRasterBand(hInDS, iBand+1);
375 21 : if (GDALGetRasterDataType(hBand) != GDT_Byte)
376 : {
377 : CPLError(CE_Warning, CPLE_AppDefined,
378 0 : "Band %d is not of type GDT_Byte. It can lead to unexpected results.", iBand+1);
379 : }
380 21 : if (GDALGetRasterColorTable(hBand) != NULL)
381 : {
382 : CPLError(CE_Warning, CPLE_AppDefined,
383 : "Band %d has a color table, which is ignored by nearblack. "
384 0 : "It can lead to unexpected results.", iBand+1);
385 : }
386 : }
387 :
388 7 : GDALRasterBandH hMaskBand = NULL;
389 :
390 7 : if (bSetMask) {
391 :
392 : /***** if there isn't already a mask band on the output file create one *****/
393 :
394 2 : if ( GMF_PER_DATASET != GDALGetMaskFlags( GDALGetRasterBand(hOutDS, 1) ) )
395 : {
396 :
397 1 : if ( CE_None != GDALCreateDatasetMaskBand(hOutDS, GMF_PER_DATASET) ) {
398 : CPLError(CE_Failure, CPLE_AppDefined,
399 0 : "Failed to create mask band on output DS");
400 0 : bSetMask = FALSE;
401 : }
402 : }
403 :
404 2 : if (bSetMask) {
405 2 : hMaskBand = GDALGetMaskBand(GDALGetRasterBand(hOutDS, 1));
406 : }
407 : }
408 :
409 : /* -------------------------------------------------------------------- */
410 : /* Allocate a line buffer. */
411 : /* -------------------------------------------------------------------- */
412 : GByte *pabyLine;
413 7 : GByte *pabyMask=NULL;
414 :
415 : int *panLastLineCounts;
416 :
417 7 : pabyLine = (GByte *) CPLMalloc(nXSize * nDstBands);
418 :
419 7 : if (bSetMask)
420 2 : pabyMask = (GByte *) CPLMalloc(nXSize);
421 :
422 7 : panLastLineCounts = (int *) CPLCalloc(sizeof(int),nXSize);
423 :
424 : /* -------------------------------------------------------------------- */
425 : /* Processing data one line at a time. */
426 : /* -------------------------------------------------------------------- */
427 : int iLine;
428 :
429 357 : for( iLine = 0; iLine < nYSize; iLine++ )
430 : {
431 : CPLErr eErr;
432 :
433 : eErr = GDALDatasetRasterIO( hInDS, GF_Read, 0, iLine, nXSize, 1,
434 : pabyLine, nXSize, 1, GDT_Byte,
435 350 : nBands, NULL, nDstBands, nXSize * nDstBands, 1 );
436 350 : if( eErr != CE_None )
437 0 : break;
438 :
439 350 : if (bSetAlpha)
440 : {
441 : int iCol;
442 7650 : for(iCol = 0; iCol < nXSize; iCol ++)
443 : {
444 7500 : pabyLine[iCol * nDstBands + nDstBands - 1] = 255;
445 : }
446 : }
447 :
448 350 : if (bSetMask)
449 : {
450 : int iCol;
451 5100 : for(iCol = 0; iCol < nXSize; iCol ++)
452 : {
453 5000 : pabyMask[iCol] = 255;
454 : }
455 : }
456 :
457 : ProcessLine( pabyLine, pabyMask, 0, nXSize-1, nBands, nDstBands,
458 : nNearDist, nMaxNonBlack, bNearWhite, &oColors,
459 : panLastLineCounts,
460 : TRUE, // bDoHorizontalCheck
461 : TRUE, // bDoVerticalCheck
462 : FALSE // bBottomUp
463 350 : );
464 : ProcessLine( pabyLine, pabyMask, nXSize-1, 0, nBands, nDstBands,
465 : nNearDist, nMaxNonBlack, bNearWhite, &oColors,
466 : panLastLineCounts,
467 : TRUE, // bDoHorizontalCheck
468 : FALSE, // bDoVerticalCheck
469 : FALSE // bBottomUp
470 350 : );
471 :
472 : eErr = GDALDatasetRasterIO( hOutDS, GF_Write, 0, iLine, nXSize, 1,
473 : pabyLine, nXSize, 1, GDT_Byte,
474 350 : nDstBands, NULL, nDstBands, nXSize * nDstBands, 1 );
475 :
476 350 : if( eErr != CE_None )
477 0 : break;
478 :
479 : /***** write out the mask band line *****/
480 :
481 350 : if (bSetMask) {
482 :
483 : eErr = GDALRasterIO ( hMaskBand, GF_Write, 0, iLine, nXSize, 1,
484 : pabyMask, nXSize, 1, GDT_Byte,
485 100 : 0, 0 );
486 :
487 100 : if( eErr != CE_None ) {
488 : CPLError(CE_Warning, CPLE_AppDefined,
489 0 : "ERROR writeing out line to mask band.");
490 0 : break;
491 : }
492 : }
493 :
494 350 : if (!bQuiet)
495 300 : GDALTermProgress( 0.5 * ((iLine+1) / (double) nYSize), NULL, NULL );
496 : }
497 :
498 : /* -------------------------------------------------------------------- */
499 : /* Now process from the bottom back up .*/
500 : /* -------------------------------------------------------------------- */
501 7 : memset( panLastLineCounts, 0, sizeof(int) * nXSize);
502 :
503 357 : for( iLine = nYSize-1; iLine >= 0; iLine-- )
504 : {
505 : CPLErr eErr;
506 :
507 : eErr = GDALDatasetRasterIO( hOutDS, GF_Read, 0, iLine, nXSize, 1,
508 : pabyLine, nXSize, 1, GDT_Byte,
509 350 : nDstBands, NULL, nDstBands, nXSize * nDstBands, 1 );
510 350 : if( eErr != CE_None )
511 0 : break;
512 :
513 : /***** read the mask band line back in *****/
514 :
515 350 : if (bSetMask) {
516 :
517 : eErr = GDALRasterIO ( hMaskBand, GF_Read, 0, iLine, nXSize, 1,
518 : pabyMask, nXSize, 1, GDT_Byte,
519 100 : 0, 0 );
520 :
521 :
522 100 : if( eErr != CE_None )
523 0 : break;
524 : }
525 :
526 :
527 : ProcessLine( pabyLine, pabyMask, 0, nXSize-1, nBands, nDstBands,
528 : nNearDist, nMaxNonBlack, bNearWhite, &oColors,
529 : panLastLineCounts,
530 : TRUE, // bDoHorizontalCheck
531 : TRUE, // bDoVerticalCheck
532 : TRUE // bBottomUp
533 350 : );
534 : ProcessLine( pabyLine, pabyMask, nXSize-1, 0, nBands, nDstBands,
535 : nNearDist, nMaxNonBlack, bNearWhite, &oColors,
536 : panLastLineCounts,
537 : TRUE, // bDoHorizontalCheck
538 : FALSE, // bDoVerticalCheck
539 : TRUE // bBottomUp
540 350 : );
541 :
542 : eErr = GDALDatasetRasterIO( hOutDS, GF_Write, 0, iLine, nXSize, 1,
543 : pabyLine, nXSize, 1, GDT_Byte,
544 350 : nDstBands, NULL, nDstBands, nXSize * nDstBands, 1 );
545 350 : if( eErr != CE_None )
546 0 : break;
547 :
548 : /***** write out the mask band line *****/
549 :
550 350 : if (bSetMask) {
551 :
552 : eErr = GDALRasterIO ( hMaskBand, GF_Write, 0, iLine, nXSize, 1,
553 : pabyMask, nXSize, 1, GDT_Byte,
554 100 : 0, 0 );
555 :
556 :
557 100 : if( eErr != CE_None )
558 0 : break;
559 : }
560 :
561 :
562 350 : if (!bQuiet)
563 : GDALTermProgress( 0.5 + 0.5 * (nYSize-iLine) / (double) nYSize,
564 300 : NULL, NULL );
565 : }
566 :
567 7 : CPLFree(pabyLine);
568 7 : if (bSetMask)
569 2 : CPLFree(pabyMask);
570 :
571 7 : CPLFree( panLastLineCounts );
572 :
573 7 : GDALClose( hOutDS );
574 7 : if( hInDS != hOutDS )
575 5 : GDALClose( hInDS );
576 7 : GDALDumpOpenDatasets( stderr );
577 7 : CSLDestroy( argv );
578 7 : CSLDestroy( papszCreationOptions );
579 7 : GDALDestroyDriverManager();
580 :
581 7 : return 0;
582 : }
583 :
584 : /************************************************************************/
585 : /* ProcessLine() */
586 : /* */
587 : /* Process a single scanline of image data. */
588 : /************************************************************************/
589 :
590 1400 : static void ProcessLine( GByte *pabyLine, GByte *pabyMask, int iStart,
591 : int iEnd, int nSrcBands, int nDstBands, int nNearDist,
592 : int nMaxNonBlack, int bNearWhite, Colors *poColors,
593 : int *panLastLineCounts, int bDoHorizontalCheck,
594 : int bDoVerticalCheck, int bBottomUp )
595 : {
596 : int iDir, i;
597 1400 : GByte nReplacevalue = 0;
598 1400 : if( bNearWhite )
599 200 : nReplacevalue = 255;
600 :
601 : /* -------------------------------------------------------------------- */
602 : /* Vertical checking. */
603 : /* -------------------------------------------------------------------- */
604 :
605 1400 : if( bDoVerticalCheck )
606 : {
607 700 : int nXSize = MAX(iStart+1,iEnd+1);
608 :
609 35700 : for( i = 0; i < nXSize; i++ )
610 : {
611 :
612 : // are we already terminated for this column?
613 :
614 35000 : if( panLastLineCounts[i] > nMaxNonBlack )
615 24487 : continue;
616 :
617 : /***** is the pixel valid data? ****/
618 :
619 10513 : int bIsNonBlack = FALSE;
620 :
621 : /***** loop over the colors *****/
622 :
623 : int iColor;
624 10513 : for (iColor = 0; iColor < (int)poColors->size(); iColor++) {
625 :
626 12061 : Color oColor = (*poColors)[iColor];
627 :
628 12061 : bIsNonBlack = FALSE;
629 :
630 : /***** loop over the bands *****/
631 :
632 : int iBand;
633 41931 : for( iBand = 0; iBand < nSrcBands; iBand++ )
634 : {
635 32002 : int nPix = pabyLine[i * nDstBands + iBand];
636 :
637 32002 : if( oColor[iBand] - nPix > nNearDist ||
638 : nPix > nNearDist + oColor[iBand] )
639 : {
640 2132 : bIsNonBlack = TRUE;
641 2132 : break;
642 : }
643 : }
644 :
645 12061 : if (bIsNonBlack == FALSE)
646 : break;
647 : }
648 :
649 10513 : if (bIsNonBlack) {
650 584 : panLastLineCounts[i]++;
651 :
652 584 : if( panLastLineCounts[i] > nMaxNonBlack )
653 552 : continue;
654 : }
655 : //else
656 : // panLastLineCounts[i] = 0; // not sure this even makes sense
657 :
658 : /***** replace the pixel values *****/
659 :
660 : int iBand;
661 39844 : for( iBand = 0; iBand < nSrcBands; iBand++ )
662 29883 : pabyLine[i * nDstBands + iBand] = nReplacevalue;
663 :
664 : /***** alpha *****/
665 :
666 9961 : if( nDstBands > nSrcBands )
667 2457 : pabyLine[i * nDstBands + nDstBands - 1] = 0;
668 :
669 : /***** mask *****/
670 :
671 9961 : if (pabyMask != NULL)
672 1968 : pabyMask[i] = 0;
673 : }
674 : }
675 :
676 : /* -------------------------------------------------------------------- */
677 : /* Horizontal Checking. */
678 : /* -------------------------------------------------------------------- */
679 :
680 1400 : if( bDoHorizontalCheck )
681 : {
682 1400 : int nNonBlackPixels = 0;
683 :
684 : /***** on a bottom up pass assume nMaxNonBlack is 0 *****/
685 :
686 1400 : if (bBottomUp)
687 700 : nMaxNonBlack = 0;
688 :
689 1400 : if( iStart < iEnd )
690 700 : iDir = 1;
691 : else
692 700 : iDir = -1;
693 1400 : int bDoTest = TRUE;
694 :
695 70000 : for( i = iStart; i != iEnd; i += iDir )
696 : {
697 :
698 : /***** not seen any valid data? *****/
699 :
700 68600 : if ( bDoTest ) {
701 :
702 : /***** is the pixel valid data? ****/
703 :
704 23001 : int bIsNonBlack = FALSE;
705 :
706 : /***** loop over the colors *****/
707 :
708 : int iColor;
709 23001 : for (iColor = 0; iColor < (int)poColors->size(); iColor++) {
710 :
711 23161 : Color oColor = (*poColors)[iColor];
712 :
713 23161 : bIsNonBlack = FALSE;
714 :
715 : /***** loop over the bands *****/
716 :
717 : int iBand;
718 88333 : for( iBand = 0; iBand < nSrcBands; iBand++ )
719 : {
720 66664 : int nPix = pabyLine[i * nDstBands + iBand];
721 :
722 66664 : if( oColor[iBand] - nPix > nNearDist ||
723 : nPix > nNearDist + oColor[iBand] )
724 : {
725 1492 : bIsNonBlack = TRUE;
726 1492 : break;
727 : }
728 : }
729 :
730 23161 : if (bIsNonBlack == FALSE)
731 : break;
732 : }
733 :
734 23001 : if (bIsNonBlack) {
735 :
736 : /***** use nNonBlackPixels in grey areas *****/
737 : /***** from the verical pass's grey areas ****/
738 :
739 1332 : if( panLastLineCounts[i] <= nMaxNonBlack )
740 0 : nNonBlackPixels = panLastLineCounts[i];
741 : else
742 1332 : nNonBlackPixels++;
743 : }
744 :
745 23001 : if( nNonBlackPixels > nMaxNonBlack ) {
746 1300 : bDoTest = FALSE;
747 1300 : continue;
748 : }
749 :
750 : /***** replace the pixel values *****/
751 :
752 : int iBand;
753 86804 : for( iBand = 0; iBand < nSrcBands; iBand++ )
754 65103 : pabyLine[i * nDstBands + iBand] = nReplacevalue;
755 :
756 : /***** alpha *****/
757 :
758 21701 : if( nDstBands > nSrcBands )
759 5586 : pabyLine[i * nDstBands + nDstBands - 1] = 0;
760 :
761 : /***** mask *****/
762 :
763 21701 : if (pabyMask != NULL)
764 4362 : pabyMask[i] = 0;
765 : }
766 :
767 : /***** seen valid data but test if the *****/
768 : /***** vertical pass saw any non valid data *****/
769 :
770 45599 : else if( panLastLineCounts[i] == 0 ) {
771 700 : bDoTest = TRUE;
772 700 : nNonBlackPixels = 0;
773 : }
774 : }
775 : }
776 :
777 1400 : }
778 :
|