1 : /******************************************************************************
2 : * $Id: xpmdataset.cpp 18239 2009-12-10 15:48:43Z warmerdam $
3 : *
4 : * Project: XPM Driver
5 : * Purpose: Implement GDAL XPM Support
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2002, Frank Warmerdam
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_pam.h"
31 : #include "cpl_string.h"
32 : #include "memdataset.h"
33 : #include "gdal_frmts.h"
34 :
35 :
36 : CPL_CVSID("$Id: xpmdataset.cpp 18239 2009-12-10 15:48:43Z warmerdam $");
37 :
38 : static unsigned char *ParseXPM( const char *pszInput,
39 : int *pnXSize, int *pnYSize,
40 : GDALColorTable **ppoRetTable );
41 :
42 :
43 : /************************************************************************/
44 : /* ==================================================================== */
45 : /* XPMDataset */
46 : /* ==================================================================== */
47 : /************************************************************************/
48 :
49 : class XPMDataset : public GDALPamDataset
50 : {
51 : public:
52 : XPMDataset();
53 : ~XPMDataset();
54 :
55 : static GDALDataset *Open( GDALOpenInfo * );
56 : };
57 :
58 : /************************************************************************/
59 : /* XPMDataset() */
60 : /************************************************************************/
61 :
62 2 : XPMDataset::XPMDataset()
63 :
64 : {
65 2 : }
66 :
67 : /************************************************************************/
68 : /* ~XPMDataset() */
69 : /************************************************************************/
70 :
71 4 : XPMDataset::~XPMDataset()
72 :
73 : {
74 2 : FlushCache();
75 4 : }
76 :
77 : /************************************************************************/
78 : /* Open() */
79 : /************************************************************************/
80 :
81 9624 : GDALDataset *XPMDataset::Open( GDALOpenInfo * poOpenInfo )
82 :
83 : {
84 : /* -------------------------------------------------------------------- */
85 : /* First we check to see if the file has the expected header */
86 : /* bytes. For now we expect the XPM file to start with a line */
87 : /* containing the letters XPM, and to have "static" in the */
88 : /* header. */
89 : /* -------------------------------------------------------------------- */
90 9624 : if( poOpenInfo->nHeaderBytes < 32
91 : || strstr((const char *) poOpenInfo->pabyHeader,"XPM") == NULL
92 : || strstr((const char *) poOpenInfo->pabyHeader,"static") == NULL )
93 9622 : return NULL;
94 :
95 2 : if( poOpenInfo->eAccess == GA_Update )
96 : {
97 : CPLError( CE_Failure, CPLE_NotSupported,
98 : "The XPM driver does not support update access to existing"
99 0 : " files." );
100 0 : return NULL;
101 : }
102 :
103 2 : if (poOpenInfo->fp == NULL)
104 0 : return NULL;
105 :
106 : /* -------------------------------------------------------------------- */
107 : /* Read the whole file into a memory strings. */
108 : /* -------------------------------------------------------------------- */
109 : unsigned int nFileSize;
110 : char *pszFileContents;
111 :
112 2 : VSIFSeek( poOpenInfo->fp, 0, SEEK_END );
113 2 : nFileSize = VSIFTell( poOpenInfo->fp );
114 :
115 2 : pszFileContents = (char *) VSIMalloc(nFileSize+1);
116 2 : if( pszFileContents == NULL )
117 : {
118 : CPLError( CE_Failure, CPLE_OutOfMemory,
119 : "Insufficient memory for loading XPM file %s into memory.",
120 0 : poOpenInfo->pszFilename );
121 0 : return NULL;
122 : }
123 2 : pszFileContents[nFileSize] = '\0';
124 :
125 2 : VSIFSeek( poOpenInfo->fp, 0, SEEK_SET );
126 :
127 2 : if( VSIFRead( pszFileContents, 1, nFileSize, poOpenInfo->fp ) != nFileSize)
128 : {
129 0 : CPLFree( pszFileContents );
130 : CPLError( CE_Failure, CPLE_FileIO,
131 : "Failed to read all %d bytes from file %s.",
132 0 : nFileSize, poOpenInfo->pszFilename );
133 0 : return NULL;
134 : }
135 :
136 : /* -------------------------------------------------------------------- */
137 : /* Convert into a binary image. */
138 : /* -------------------------------------------------------------------- */
139 : GByte *pabyImage;
140 : int nXSize, nYSize;
141 2 : GDALColorTable *poCT = NULL;
142 :
143 2 : CPLErrorReset();
144 :
145 2 : pabyImage = ParseXPM( pszFileContents, &nXSize, &nYSize, &poCT );
146 2 : CPLFree( pszFileContents );
147 :
148 2 : if( pabyImage == NULL )
149 : {
150 0 : return NULL;
151 : }
152 :
153 : /* -------------------------------------------------------------------- */
154 : /* Create a corresponding GDALDataset. */
155 : /* -------------------------------------------------------------------- */
156 : XPMDataset *poDS;
157 :
158 2 : poDS = new XPMDataset();
159 :
160 : /* -------------------------------------------------------------------- */
161 : /* Capture some information from the file that is of interest. */
162 : /* -------------------------------------------------------------------- */
163 2 : poDS->nRasterXSize = nXSize;
164 2 : poDS->nRasterYSize = nYSize;
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Create band information objects. */
168 : /* -------------------------------------------------------------------- */
169 : MEMRasterBand *poBand;
170 :
171 : poBand = new MEMRasterBand( poDS, 1, pabyImage, GDT_Byte, 1, nXSize,
172 4 : TRUE );
173 2 : poBand->SetColorTable( poCT );
174 2 : poDS->SetBand( 1, poBand );
175 :
176 4 : delete poCT;
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Initialize any PAM information. */
180 : /* -------------------------------------------------------------------- */
181 2 : poDS->SetDescription( poOpenInfo->pszFilename );
182 2 : poDS->TryLoadXML();
183 :
184 : /* -------------------------------------------------------------------- */
185 : /* Support overviews. */
186 : /* -------------------------------------------------------------------- */
187 2 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
188 :
189 2 : return poDS;
190 : }
191 :
192 : /************************************************************************/
193 : /* XPMCreateCopy() */
194 : /************************************************************************/
195 :
196 : static GDALDataset *
197 16 : XPMCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
198 : int bStrict, char ** papszOptions,
199 : GDALProgressFunc pfnProgress, void * pProgressData )
200 :
201 : {
202 16 : int nBands = poSrcDS->GetRasterCount();
203 16 : int nXSize = poSrcDS->GetRasterXSize();
204 16 : int nYSize = poSrcDS->GetRasterYSize();
205 : GDALColorTable *poCT;
206 :
207 : /* -------------------------------------------------------------------- */
208 : /* Some some rudimentary checks */
209 : /* -------------------------------------------------------------------- */
210 16 : if( nBands != 1 )
211 : {
212 : CPLError( CE_Failure, CPLE_NotSupported,
213 5 : "XPM driver only supports one band images.\n" );
214 :
215 5 : return NULL;
216 : }
217 :
218 11 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
219 : && bStrict )
220 : {
221 : CPLError( CE_Failure, CPLE_NotSupported,
222 : "XPM driver doesn't support data type %s. "
223 : "Only eight bit bands supported.\n",
224 : GDALGetDataTypeName(
225 10 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
226 :
227 10 : return NULL;
228 : }
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* If there is no colortable on the source image, create a */
232 : /* greyscale one with 64 levels of grey. */
233 : /* -------------------------------------------------------------------- */
234 1 : GDALRasterBand *poBand = poSrcDS->GetRasterBand(1);
235 : int i;
236 1 : GDALColorTable oGreyTable;
237 :
238 1 : poCT = poBand->GetColorTable();
239 1 : if( poCT == NULL )
240 : {
241 1 : poCT = &oGreyTable;
242 :
243 257 : for( i = 0; i < 256; i++ )
244 : {
245 : GDALColorEntry sColor;
246 :
247 256 : sColor.c1 = (short) i;
248 256 : sColor.c2 = (short) i;
249 256 : sColor.c3 = (short) i;
250 256 : sColor.c4 = 255;
251 :
252 256 : poCT->SetColorEntry( i, &sColor );
253 : }
254 : }
255 :
256 : /* -------------------------------------------------------------------- */
257 : /* Build list of active colors, and the mapping from pixels to */
258 : /* our active colormap. */
259 : /* -------------------------------------------------------------------- */
260 1 : const char *pszColorCodes = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-+=[]|:;,.<>?/";
261 :
262 : int anPixelMapping[256];
263 : GDALColorEntry asPixelColor[256];
264 1 : int nActiveColors = MIN(poCT->GetColorEntryCount(),256);
265 :
266 : // Setup initial colortable and pixel value mapping.
267 1 : memset( anPixelMapping+0, 0, sizeof(int) * 256 );
268 257 : for( i = 0; i < nActiveColors; i++ )
269 : {
270 256 : poCT->GetColorEntryAsRGB( i, asPixelColor + i );
271 256 : anPixelMapping[i] = i;
272 : }
273 :
274 : /* ==================================================================== */
275 : /* Iterate merging colors until we are under our limit (about 85). */
276 : /* ==================================================================== */
277 171 : while( nActiveColors > (int) strlen(pszColorCodes) )
278 : {
279 169 : int nClosestDistance = 768;
280 169 : int iClose1 = -1, iClose2 = -1;
281 : int iColor1, iColor2;
282 :
283 : // Find the closest pair of colors.
284 7225 : for( iColor1 = 0; iColor1 < nActiveColors; iColor1++ )
285 : {
286 840480 : for( iColor2 = iColor1+1; iColor2 < nActiveColors; iColor2++ )
287 : {
288 : int nDistance;
289 :
290 833255 : if( asPixelColor[iColor1].c4 < 128
291 : && asPixelColor[iColor2].c4 < 128 )
292 0 : nDistance = 0;
293 : else
294 : nDistance =
295 : ABS(asPixelColor[iColor1].c1-asPixelColor[iColor2].c1)
296 : + ABS(asPixelColor[iColor1].c2-asPixelColor[iColor2].c2)
297 833255 : + ABS(asPixelColor[iColor1].c3-asPixelColor[iColor2].c3);
298 :
299 833255 : if( nDistance < nClosestDistance )
300 : {
301 699 : nClosestDistance = nDistance;
302 699 : iClose1 = iColor1;
303 699 : iClose2 = iColor2;
304 : }
305 : }
306 :
307 7225 : if( nClosestDistance < 8 )
308 169 : break;
309 : }
310 :
311 : // This should never happen!
312 169 : if( iClose1 == -1 )
313 0 : break;
314 :
315 : // Merge two selected colors - shift icolor2 into icolor1 and
316 : // move the last active color into icolor2's slot.
317 43433 : for( i = 0; i < 256; i++ )
318 : {
319 43264 : if( anPixelMapping[i] == iClose2 )
320 169 : anPixelMapping[i] = iClose1;
321 43095 : else if( anPixelMapping[i] == nActiveColors-1 )
322 113 : anPixelMapping[i] = iClose2;
323 : }
324 :
325 169 : asPixelColor[iClose2] = asPixelColor[nActiveColors-1];
326 169 : nActiveColors--;
327 : }
328 :
329 : /* ==================================================================== */
330 : /* Write the output image. */
331 : /* ==================================================================== */
332 : FILE *fpPBM;
333 :
334 1 : fpPBM = VSIFOpen( pszFilename, "wt+" );
335 1 : if( fpPBM == NULL )
336 : {
337 : CPLError( CE_Failure, CPLE_OpenFailed,
338 : "Unable to create file `%s'.",
339 0 : pszFilename );
340 :
341 0 : return NULL;
342 : }
343 :
344 : /* -------------------------------------------------------------------- */
345 : /* Write the header lines. */
346 : /* -------------------------------------------------------------------- */
347 1 : fprintf( fpPBM, "/* XPM */\n" );
348 : fprintf( fpPBM, "static char *%s[] = {\n",
349 1 : CPLGetBasename( pszFilename ) );
350 1 : fprintf( fpPBM, "/* width height num_colors chars_per_pixel */\n" );
351 : fprintf( fpPBM, "\" %3d %3d %3d 1\",\n",
352 1 : nXSize, nYSize, nActiveColors );
353 1 : fprintf( fpPBM, "/* colors */\n" );
354 :
355 : /* -------------------------------------------------------------------- */
356 : /* Write the color table. */
357 : /* -------------------------------------------------------------------- */
358 88 : for( i = 0; i < nActiveColors; i++ )
359 : {
360 87 : if( asPixelColor[i].c4 < 128 )
361 0 : fprintf( fpPBM, "\"%c c None\",\n", pszColorCodes[i] );
362 : else
363 : fprintf( fpPBM,
364 : "\"%c c #%02x%02x%02x\",\n",
365 87 : pszColorCodes[i],
366 : asPixelColor[i].c1,
367 : asPixelColor[i].c2,
368 174 : asPixelColor[i].c3 );
369 : }
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* Dump image. */
373 : /* -------------------------------------------------------------------- */
374 : int iLine;
375 : GByte *pabyScanline;
376 :
377 1 : pabyScanline = (GByte *) CPLMalloc( nXSize );
378 11 : for( iLine = 0; iLine < nYSize; iLine++ )
379 : {
380 : poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
381 10 : (void *) pabyScanline, nXSize, 1, GDT_Byte, 0, 0 );
382 :
383 10 : fputc( '"', fpPBM );
384 110 : for( int iPixel = 0; iPixel < nXSize; iPixel++ )
385 100 : fputc( pszColorCodes[anPixelMapping[pabyScanline[iPixel]]],
386 100 : fpPBM);
387 10 : fprintf( fpPBM, "\",\n" );
388 : }
389 :
390 1 : CPLFree( pabyScanline );
391 :
392 : /* -------------------------------------------------------------------- */
393 : /* cleanup */
394 : /* -------------------------------------------------------------------- */
395 1 : fprintf( fpPBM, "};\n" );
396 1 : VSIFClose( fpPBM );
397 :
398 : /* -------------------------------------------------------------------- */
399 : /* Re-open dataset, and copy any auxilary pam information. */
400 : /* -------------------------------------------------------------------- */
401 : GDALPamDataset *poDS = (GDALPamDataset *)
402 1 : GDALOpen( pszFilename, GA_ReadOnly );
403 :
404 1 : if( poDS )
405 1 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
406 :
407 1 : return poDS;
408 : }
409 :
410 : /************************************************************************/
411 : /* GDALRegister_XPM() */
412 : /************************************************************************/
413 :
414 338 : void GDALRegister_XPM()
415 :
416 : {
417 : GDALDriver *poDriver;
418 :
419 338 : if( GDALGetDriverByName( "XPM" ) == NULL )
420 : {
421 336 : poDriver = new GDALDriver();
422 :
423 336 : poDriver->SetDescription( "XPM" );
424 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
425 336 : "X11 PixMap Format" );
426 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
427 336 : "frmt_various.html#XPM" );
428 336 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xpm" );
429 336 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/x-xpixmap" );
430 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
431 336 : "Byte" );
432 :
433 336 : poDriver->pfnOpen = XPMDataset::Open;
434 336 : poDriver->pfnCreateCopy = XPMCreateCopy;
435 :
436 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
437 : }
438 338 : }
439 :
440 : /************************************************************************/
441 : /* ParseXPM() */
442 : /************************************************************************/
443 :
444 : static unsigned char *
445 2 : ParseXPM( const char *pszInput, int *pnXSize, int *pnYSize,
446 : GDALColorTable **ppoRetTable )
447 :
448 : {
449 : /* ==================================================================== */
450 : /* Parse input into an array of strings from within the first C */
451 : /* initializer (list os comma separated strings in braces). */
452 : /* ==================================================================== */
453 2 : char **papszXPMList = NULL;
454 2 : const char *pszNext = pszInput;
455 : int i;
456 :
457 : // Skip till after open brace.
458 66 : while( *pszNext != '\0' && *pszNext != '{' )
459 62 : pszNext++;
460 :
461 2 : if( *pszNext == '\0' )
462 0 : return NULL;
463 :
464 2 : pszNext++;
465 :
466 : // Read lines till close brace.
467 :
468 2116 : while( *pszNext != '\0' && *pszNext != '}' )
469 : {
470 : // skip whole comment.
471 2112 : if( EQUALN(pszNext,"/*",2) )
472 : {
473 4 : pszNext += 2;
474 106 : while( *pszNext != '\0' && !EQUALN(pszNext,"*/",2) )
475 98 : pszNext++;
476 : }
477 :
478 : // reading string constants
479 2108 : else if( *pszNext == '"' )
480 : {
481 : char *pszLine;
482 :
483 698 : pszNext++;
484 698 : i = 0;
485 :
486 265620 : while( pszNext[i] != '\0' && pszNext[i] != '"' )
487 264224 : i++;
488 :
489 698 : if( pszNext[i] == '\0' )
490 : {
491 0 : CSLDestroy( papszXPMList );
492 0 : return NULL;
493 : }
494 :
495 698 : pszLine = (char *) CPLMalloc(i+1);
496 698 : strncpy( pszLine, pszNext, i );
497 698 : pszLine[i] = '\0';
498 :
499 698 : papszXPMList = CSLAddString( papszXPMList, pszLine );
500 698 : CPLFree( pszLine );
501 698 : pszNext = pszNext + i + 1;
502 : }
503 :
504 : // just ignore everything else (whitespace, commas, newlines, etc).
505 : else
506 1410 : pszNext++;
507 : }
508 :
509 2 : if( CSLCount(papszXPMList) < 3 || *pszNext != '}' )
510 : {
511 0 : CSLDestroy( papszXPMList );
512 0 : return NULL;
513 : }
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* Get the image information. */
517 : /* -------------------------------------------------------------------- */
518 : int nColorCount, nCharsPerPixel;
519 :
520 2 : if( sscanf( papszXPMList[0], "%d %d %d %d",
521 : pnXSize, pnYSize, &nColorCount, &nCharsPerPixel ) != 4 )
522 : {
523 : CPLError( CE_Failure, CPLE_AppDefined,
524 : "Image definition (%s) not well formed.",
525 0 : papszXPMList[0] );
526 0 : CSLDestroy( papszXPMList );
527 0 : return NULL;
528 : }
529 :
530 2 : if( nCharsPerPixel != 1 )
531 : {
532 : CPLError( CE_Failure, CPLE_AppDefined,
533 0 : "Only one character per pixel XPM images supported by GDAL at this time." );
534 0 : CSLDestroy( papszXPMList );
535 0 : return NULL;
536 : }
537 :
538 : /* -------------------------------------------------------------------- */
539 : /* Parse out colors. */
540 : /* -------------------------------------------------------------------- */
541 : int iColor;
542 : int anCharLookup[256];
543 2 : GDALColorTable oCTable;
544 :
545 514 : for( i = 0; i < 256; i++ )
546 512 : anCharLookup[i] = -1;
547 :
548 176 : for( iColor = 0; iColor < nColorCount; iColor++ )
549 : {
550 174 : char **papszTokens = CSLTokenizeString( papszXPMList[iColor+1]+1 );
551 : GDALColorEntry sColor;
552 : int nRed, nGreen, nBlue;
553 :
554 174 : if( CSLCount(papszTokens) != 2 || !EQUAL(papszTokens[0],"c") )
555 : {
556 : CPLError( CE_Failure, CPLE_AppDefined,
557 : "Ill formed color definition (%s) in XPM header.",
558 0 : papszXPMList[iColor+1] );
559 0 : CSLDestroy( papszXPMList );
560 0 : CSLDestroy( papszTokens );
561 0 : return NULL;
562 : }
563 :
564 174 : anCharLookup[(int)papszXPMList[iColor+1][0]] = iColor;
565 :
566 174 : if( EQUAL(papszTokens[1],"None") )
567 : {
568 0 : sColor.c1 = 0;
569 0 : sColor.c2 = 0;
570 0 : sColor.c3 = 0;
571 0 : sColor.c4 = 0;
572 : }
573 174 : else if( sscanf( papszTokens[1], "#%02x%02x%02x",
574 : &nRed, &nGreen, &nBlue ) != 3 )
575 : {
576 : CPLError( CE_Failure, CPLE_AppDefined,
577 : "Ill formed color definition (%s) in XPM header.",
578 0 : papszXPMList[iColor+1] );
579 0 : CSLDestroy( papszXPMList );
580 0 : CSLDestroy( papszTokens );
581 0 : return NULL;
582 : }
583 : else
584 : {
585 174 : sColor.c1 = (short) nRed;
586 174 : sColor.c2 = (short) nGreen;
587 174 : sColor.c3 = (short) nBlue;
588 174 : sColor.c4 = 255;
589 : }
590 :
591 174 : oCTable.SetColorEntry( iColor, &sColor );
592 :
593 174 : CSLDestroy( papszTokens );
594 : }
595 :
596 : /* -------------------------------------------------------------------- */
597 : /* Prepare image buffer. */
598 : /* -------------------------------------------------------------------- */
599 : GByte *pabyImage;
600 :
601 2 : pabyImage = (GByte *) VSIMalloc2(*pnXSize, *pnYSize);
602 2 : if( pabyImage == NULL )
603 : {
604 : CPLError( CE_Failure, CPLE_OutOfMemory,
605 : "Insufficient memory for %dx%d XPM image buffer.",
606 0 : *pnXSize, *pnYSize );
607 0 : CSLDestroy( papszXPMList );
608 0 : return NULL;
609 : }
610 :
611 2 : memset( pabyImage, 0, *pnXSize * *pnYSize );
612 :
613 : /* -------------------------------------------------------------------- */
614 : /* Parse image. */
615 : /* -------------------------------------------------------------------- */
616 524 : for( int iLine = 0; iLine < *pnYSize; iLine++ )
617 : {
618 522 : const char *pszInLine = papszXPMList[iLine + nColorCount + 1];
619 :
620 522 : if( pszInLine == NULL )
621 : {
622 0 : CPLFree( pabyImage );
623 0 : CSLDestroy( papszXPMList );
624 : CPLError( CE_Failure, CPLE_AppDefined,
625 0 : "Insufficient imagery lines in XPM image." );
626 0 : return NULL;
627 : }
628 :
629 525532 : for( int iPixel = 0;
630 262766 : pszInLine[iPixel] != '\0' && iPixel < *pnXSize;
631 : iPixel++ )
632 : {
633 262244 : int nPixelValue = anCharLookup[(int)pszInLine[iPixel]];
634 262244 : if( nPixelValue != -1 )
635 262244 : pabyImage[iLine * *pnXSize + iPixel] = (GByte) nPixelValue;
636 : }
637 : }
638 :
639 2 : CSLDestroy( papszXPMList );
640 :
641 2 : *ppoRetTable = oCTable.Clone();
642 :
643 2 : return pabyImage;
644 : }
|