1 : /******************************************************************************
2 : * $Id: pnmdataset.cpp 16865 2009-04-27 12:49:49Z chaitanya $
3 : *
4 : * Project: PNM Driver
5 : * Purpose: Portable anymap file format imlementation
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2001, 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 "rawdataset.h"
31 : #include "cpl_string.h"
32 : #include <ctype.h>
33 :
34 : CPL_CVSID("$Id: pnmdataset.cpp 16865 2009-04-27 12:49:49Z chaitanya $");
35 :
36 : CPL_C_START
37 : void GDALRegister_PNM(void);
38 : CPL_C_END
39 :
40 : /************************************************************************/
41 : /* ==================================================================== */
42 : /* PNMDataset */
43 : /* ==================================================================== */
44 : /************************************************************************/
45 :
46 : class PNMDataset : public RawDataset
47 : {
48 : FILE *fpImage; // image data file.
49 :
50 : int bGeoTransformValid;
51 : double adfGeoTransform[6];
52 :
53 : public:
54 : PNMDataset();
55 : ~PNMDataset();
56 :
57 : virtual CPLErr GetGeoTransform( double * );
58 :
59 : static int Identify( GDALOpenInfo * );
60 : static GDALDataset *Open( GDALOpenInfo * );
61 : static GDALDataset *Create( const char * pszFilename,
62 : int nXSize, int nYSize, int nBands,
63 : GDALDataType eType, char ** papszOptions );
64 : };
65 :
66 : /************************************************************************/
67 : /* PNMDataset() */
68 : /************************************************************************/
69 :
70 40 : PNMDataset::PNMDataset()
71 : {
72 40 : fpImage = NULL;
73 40 : bGeoTransformValid = FALSE;
74 40 : adfGeoTransform[0] = 0.0;
75 40 : adfGeoTransform[1] = 1.0;
76 40 : adfGeoTransform[2] = 0.0;
77 40 : adfGeoTransform[3] = 0.0;
78 40 : adfGeoTransform[4] = 0.0;
79 40 : adfGeoTransform[5] = 1.0;
80 40 : }
81 :
82 : /************************************************************************/
83 : /* ~PNMDataset() */
84 : /************************************************************************/
85 :
86 80 : PNMDataset::~PNMDataset()
87 :
88 : {
89 40 : FlushCache();
90 40 : if( fpImage != NULL )
91 40 : VSIFCloseL( fpImage );
92 80 : }
93 :
94 : /************************************************************************/
95 : /* GetGeoTransform() */
96 : /************************************************************************/
97 :
98 4 : CPLErr PNMDataset::GetGeoTransform( double * padfTransform )
99 :
100 : {
101 :
102 4 : if( bGeoTransformValid )
103 : {
104 0 : memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
105 0 : return CE_None;
106 : }
107 : else
108 4 : return CE_Failure;
109 : }
110 :
111 : /************************************************************************/
112 : /* Identify() */
113 : /************************************************************************/
114 :
115 8776 : int PNMDataset::Identify( GDALOpenInfo * poOpenInfo )
116 :
117 : {
118 : /* -------------------------------------------------------------------- */
119 : /* Verify that this is a _raw_ ppm or pgm file. Note, we don't */
120 : /* support ascii files, or pbm (1bit) files. */
121 : /* -------------------------------------------------------------------- */
122 8776 : if( poOpenInfo->nHeaderBytes < 10 || poOpenInfo->fp == NULL )
123 8426 : return FALSE;
124 :
125 490 : if( poOpenInfo->pabyHeader[0] != 'P' ||
126 45 : (poOpenInfo->pabyHeader[2] != ' ' && // XXX: Magick number
127 45 : poOpenInfo->pabyHeader[2] != '\t' && // may be followed
128 45 : poOpenInfo->pabyHeader[2] != '\n' && // any of the blank
129 5 : poOpenInfo->pabyHeader[2] != '\r') ) // characters
130 310 : return FALSE;
131 :
132 57 : if( poOpenInfo->pabyHeader[1] != '5'
133 17 : && poOpenInfo->pabyHeader[1] != '6' )
134 0 : return FALSE;
135 :
136 40 : return TRUE;
137 : }
138 :
139 : /************************************************************************/
140 : /* Open() */
141 : /************************************************************************/
142 :
143 1049 : GDALDataset *PNMDataset::Open( GDALOpenInfo * poOpenInfo )
144 :
145 : {
146 : /* -------------------------------------------------------------------- */
147 : /* Verify that this is a _raw_ ppm or pgm file. Note, we don't */
148 : /* support ascii files, or pbm (1bit) files. */
149 : /* -------------------------------------------------------------------- */
150 1049 : if( !Identify( poOpenInfo ) )
151 1009 : return NULL;
152 :
153 : /* -------------------------------------------------------------------- */
154 : /* Parse out the tokens from the header. */
155 : /* -------------------------------------------------------------------- */
156 40 : const char *pszSrc = (const char *) poOpenInfo->pabyHeader;
157 : char szToken[512];
158 40 : int iIn, iToken = 0, nWidth =-1, nHeight=-1, nMaxValue=-1;
159 : unsigned int iOut;
160 :
161 40 : iIn = 2;
162 200 : while( iIn < poOpenInfo->nHeaderBytes && iToken < 3 )
163 : {
164 120 : iOut = 0;
165 120 : szToken[0] = '\0';
166 595 : while( iOut < sizeof(szToken) && iIn < poOpenInfo->nHeaderBytes )
167 : {
168 475 : if( pszSrc[iIn] == '#' )
169 : {
170 0 : while( pszSrc[iIn] != 10 && pszSrc[iIn] != 13
171 : && iIn < poOpenInfo->nHeaderBytes - 1 )
172 0 : iIn++;
173 : }
174 :
175 475 : if( iOut != 0 && isspace((unsigned char)pszSrc[iIn]) )
176 : {
177 120 : szToken[iOut] = '\0';
178 :
179 120 : if( iToken == 0 )
180 40 : nWidth = atoi(szToken);
181 80 : else if( iToken == 1 )
182 40 : nHeight = atoi(szToken);
183 40 : else if( iToken == 2 )
184 40 : nMaxValue = atoi(szToken);
185 :
186 120 : iToken++;
187 120 : iIn++;
188 120 : break;
189 : }
190 :
191 355 : else if( !isspace((unsigned char)pszSrc[iIn]) )
192 : {
193 315 : szToken[iOut++] = pszSrc[iIn];
194 : }
195 :
196 355 : iIn++;
197 : }
198 : }
199 :
200 : CPLDebug( "PNM", "PNM header contains: width=%d, height=%d, maxval=%d",
201 40 : nWidth, nHeight, nMaxValue );
202 :
203 40 : if( iToken != 3 || nWidth < 1 || nHeight < 1 || nMaxValue < 1 )
204 0 : return NULL;
205 :
206 : /* -------------------------------------------------------------------- */
207 : /* Create a corresponding GDALDataset. */
208 : /* -------------------------------------------------------------------- */
209 : PNMDataset *poDS;
210 :
211 40 : poDS = new PNMDataset();
212 :
213 : /* -------------------------------------------------------------------- */
214 : /* Capture some information from the file that is of interest. */
215 : /* -------------------------------------------------------------------- */
216 40 : poDS->nRasterXSize = nWidth;
217 40 : poDS->nRasterYSize = nHeight;
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* Assume ownership of the file handled from the GDALOpenInfo. */
221 : /* -------------------------------------------------------------------- */
222 40 : VSIFClose( poOpenInfo->fp );
223 40 : poOpenInfo->fp = NULL;
224 :
225 40 : if( poOpenInfo->eAccess == GA_Update )
226 16 : poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" );
227 : else
228 24 : poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
229 :
230 40 : if( poDS->fpImage == NULL )
231 : {
232 : CPLError( CE_Failure, CPLE_OpenFailed,
233 : "Failed to re-open %s within PNM driver.\n",
234 0 : poOpenInfo->pszFilename );
235 0 : return NULL;
236 : }
237 :
238 40 : poDS->eAccess = poOpenInfo->eAccess;
239 :
240 : /* -------------------------------------------------------------------- */
241 : /* Create band information objects. */
242 : /* -------------------------------------------------------------------- */
243 40 : int bMSBFirst = TRUE, iPixelSize;
244 : GDALDataType eDataType;
245 :
246 : #ifdef CPL_LSB
247 40 : bMSBFirst = FALSE;
248 : #endif
249 :
250 40 : if ( nMaxValue < 256 )
251 31 : eDataType = GDT_Byte;
252 : else
253 9 : eDataType = GDT_UInt16;
254 :
255 40 : iPixelSize = GDALGetDataTypeSize( eDataType ) / 8;
256 :
257 40 : if( poOpenInfo->pabyHeader[1] == '5' )
258 : {
259 23 : if (nWidth > INT_MAX / iPixelSize)
260 : {
261 : CPLError( CE_Failure, CPLE_AppDefined,
262 0 : "Int overflow occured.");
263 0 : delete poDS;
264 0 : return NULL;
265 : }
266 : poDS->SetBand(
267 : 1, new RawRasterBand( poDS, 1, poDS->fpImage, iIn, iPixelSize,
268 23 : nWidth*iPixelSize, eDataType, bMSBFirst, TRUE ));
269 23 : poDS->GetRasterBand(1)->SetColorInterpretation( GCI_GrayIndex );
270 : }
271 : else
272 : {
273 17 : if (nWidth > INT_MAX / (3 * iPixelSize))
274 : {
275 : CPLError( CE_Failure, CPLE_AppDefined,
276 0 : "Int overflow occured.");
277 0 : delete poDS;
278 0 : return NULL;
279 : }
280 : poDS->SetBand(
281 : 1, new RawRasterBand( poDS, 1, poDS->fpImage, iIn, 3*iPixelSize,
282 17 : nWidth*3*iPixelSize, eDataType, bMSBFirst, TRUE ));
283 : poDS->SetBand(
284 : 2, new RawRasterBand( poDS, 2, poDS->fpImage, iIn+iPixelSize,
285 : 3*iPixelSize, nWidth*3*iPixelSize,
286 34 : eDataType, bMSBFirst, TRUE ));
287 : poDS->SetBand(
288 : 3, new RawRasterBand( poDS, 3, poDS->fpImage, iIn+2*iPixelSize,
289 : 3*iPixelSize, nWidth*3*iPixelSize,
290 34 : eDataType, bMSBFirst, TRUE ));
291 :
292 17 : poDS->GetRasterBand(1)->SetColorInterpretation( GCI_RedBand );
293 17 : poDS->GetRasterBand(2)->SetColorInterpretation( GCI_GreenBand );
294 17 : poDS->GetRasterBand(3)->SetColorInterpretation( GCI_BlueBand );
295 : }
296 :
297 : /* -------------------------------------------------------------------- */
298 : /* Check for world file. */
299 : /* -------------------------------------------------------------------- */
300 : poDS->bGeoTransformValid =
301 : GDALReadWorldFile( poOpenInfo->pszFilename, ".wld",
302 40 : poDS->adfGeoTransform );
303 :
304 : /* -------------------------------------------------------------------- */
305 : /* Initialize any PAM information. */
306 : /* -------------------------------------------------------------------- */
307 40 : poDS->SetDescription( poOpenInfo->pszFilename );
308 40 : poDS->TryLoadXML();
309 :
310 : /* -------------------------------------------------------------------- */
311 : /* Check for overviews. */
312 : /* -------------------------------------------------------------------- */
313 40 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
314 :
315 40 : return( poDS );
316 : }
317 :
318 : /************************************************************************/
319 : /* Create() */
320 : /************************************************************************/
321 :
322 40 : GDALDataset *PNMDataset::Create( const char * pszFilename,
323 : int nXSize, int nYSize, int nBands,
324 : GDALDataType eType,
325 : char ** papszOptions )
326 :
327 : {
328 : /* -------------------------------------------------------------------- */
329 : /* Verify input options. */
330 : /* -------------------------------------------------------------------- */
331 40 : if( eType != GDT_Byte && eType != GDT_UInt16 )
332 : {
333 : CPLError( CE_Failure, CPLE_AppDefined,
334 : "Attempt to create PNM dataset with an illegal\n"
335 : "data type (%s), only Byte and UInt16 supported.\n",
336 18 : GDALGetDataTypeName(eType) );
337 :
338 18 : return NULL;
339 : }
340 :
341 22 : if( nBands != 1 && nBands != 3 )
342 : {
343 : CPLError( CE_Failure, CPLE_AppDefined,
344 : "Attempt to create PNM dataset with an illegal number\n"
345 : "of bands (%d). Must be 1 (greyscale) or 3 (RGB).\n",
346 7 : nBands );
347 :
348 7 : return NULL;
349 : }
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Try to create the file. */
353 : /* -------------------------------------------------------------------- */
354 : FILE *fp;
355 :
356 15 : fp = VSIFOpenL( pszFilename, "wb" );
357 :
358 15 : if( fp == NULL )
359 : {
360 : CPLError( CE_Failure, CPLE_OpenFailed,
361 : "Attempt to create file `%s' failed.\n",
362 0 : pszFilename );
363 0 : return NULL;
364 : }
365 :
366 : /* -------------------------------------------------------------------- */
367 : /* Write out the header. */
368 : /* -------------------------------------------------------------------- */
369 : char szHeader[500];
370 15 : const char *pszMaxValue = NULL;
371 15 : int nMaxValue = 0;
372 :
373 15 : pszMaxValue = CSLFetchNameValue( papszOptions, "MAXVAL" );
374 15 : if ( pszMaxValue )
375 : {
376 0 : nMaxValue = atoi( pszMaxValue );
377 0 : if ( eType == GDT_Byte && (nMaxValue > 255 || nMaxValue < 0) )
378 0 : nMaxValue = 255;
379 0 : else if ( nMaxValue > 65535 || nMaxValue < 0 )
380 0 : nMaxValue = 65535;
381 : }
382 : else
383 : {
384 15 : if ( eType == GDT_Byte )
385 11 : nMaxValue = 255;
386 : else
387 4 : nMaxValue = 65535;
388 : }
389 :
390 :
391 15 : memset( szHeader, 0, sizeof(szHeader) );
392 :
393 15 : if( nBands == 3 )
394 6 : sprintf( szHeader, "P6\n%d %d\n%d\n", nXSize, nYSize, nMaxValue );
395 : else
396 9 : sprintf( szHeader, "P5\n%d %d\n%d\n", nXSize, nYSize, nMaxValue );
397 :
398 15 : VSIFWriteL( (void *) szHeader, strlen(szHeader) + 2, 1, fp );
399 15 : VSIFCloseL( fp );
400 :
401 15 : return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
402 : }
403 :
404 : /************************************************************************/
405 : /* GDALRegister_PNM() */
406 : /************************************************************************/
407 :
408 338 : void GDALRegister_PNM()
409 :
410 : {
411 : GDALDriver *poDriver;
412 :
413 338 : if( GDALGetDriverByName( "PNM" ) == NULL )
414 : {
415 336 : poDriver = new GDALDriver();
416 :
417 336 : poDriver->SetDescription( "PNM" );
418 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
419 336 : "Portable Pixmap Format (netpbm)" );
420 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
421 336 : "frmt_various.html#PNM" );
422 336 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pnm" );
423 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE,
424 336 : "image/x-portable-anymap" );
425 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
426 336 : "Byte UInt16" );
427 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
428 : "<CreationOptionList>"
429 : " <Option name='MAXVAL' type='unsigned int' description='Maximum color value'/>"
430 336 : "</CreationOptionList>" );
431 :
432 336 : poDriver->pfnOpen = PNMDataset::Open;
433 336 : poDriver->pfnCreate = PNMDataset::Create;
434 336 : poDriver->pfnIdentify = PNMDataset::Identify;
435 :
436 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
437 : }
438 338 : }
|