1 : /******************************************************************************
2 : * $Id: pnmdataset.cpp 21696 2011-02-12 20:35:50Z rouault $
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 21696 2011-02-12 20:35:50Z rouault $");
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 : VSILFILE *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 49 : PNMDataset::PNMDataset()
71 : {
72 49 : fpImage = NULL;
73 49 : bGeoTransformValid = FALSE;
74 49 : adfGeoTransform[0] = 0.0;
75 49 : adfGeoTransform[1] = 1.0;
76 49 : adfGeoTransform[2] = 0.0;
77 49 : adfGeoTransform[3] = 0.0;
78 49 : adfGeoTransform[4] = 0.0;
79 49 : adfGeoTransform[5] = 1.0;
80 49 : }
81 :
82 : /************************************************************************/
83 : /* ~PNMDataset() */
84 : /************************************************************************/
85 :
86 49 : PNMDataset::~PNMDataset()
87 :
88 : {
89 49 : FlushCache();
90 49 : if( fpImage != NULL )
91 49 : VSIFCloseL( fpImage );
92 49 : }
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 12389 : 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 12389 : if( poOpenInfo->nHeaderBytes < 10 )
123 11530 : return FALSE;
124 :
125 1034 : if( poOpenInfo->pabyHeader[0] != 'P' ||
126 56 : (poOpenInfo->pabyHeader[2] != ' ' && // XXX: Magick number
127 56 : poOpenInfo->pabyHeader[2] != '\t' && // may be followed
128 56 : poOpenInfo->pabyHeader[2] != '\n' && // any of the blank
129 7 : poOpenInfo->pabyHeader[2] != '\r') ) // characters
130 810 : return FALSE;
131 :
132 67 : if( poOpenInfo->pabyHeader[1] != '5'
133 18 : && poOpenInfo->pabyHeader[1] != '6' )
134 0 : return FALSE;
135 :
136 49 : return TRUE;
137 : }
138 :
139 : /************************************************************************/
140 : /* Open() */
141 : /************************************************************************/
142 :
143 2296 : 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 2296 : if( !Identify( poOpenInfo ) )
151 2247 : return NULL;
152 :
153 : /* -------------------------------------------------------------------- */
154 : /* Parse out the tokens from the header. */
155 : /* -------------------------------------------------------------------- */
156 49 : const char *pszSrc = (const char *) poOpenInfo->pabyHeader;
157 : char szToken[512];
158 49 : int iIn, iToken = 0, nWidth =-1, nHeight=-1, nMaxValue=-1;
159 : unsigned int iOut;
160 :
161 49 : iIn = 2;
162 245 : while( iIn < poOpenInfo->nHeaderBytes && iToken < 3 )
163 : {
164 147 : iOut = 0;
165 147 : szToken[0] = '\0';
166 737 : while( iOut < sizeof(szToken) && iIn < poOpenInfo->nHeaderBytes )
167 : {
168 590 : if( pszSrc[iIn] == '#' )
169 : {
170 0 : while( pszSrc[iIn] != 10 && pszSrc[iIn] != 13
171 : && iIn < poOpenInfo->nHeaderBytes - 1 )
172 0 : iIn++;
173 : }
174 :
175 590 : if( iOut != 0 && isspace((unsigned char)pszSrc[iIn]) )
176 : {
177 147 : szToken[iOut] = '\0';
178 :
179 147 : if( iToken == 0 )
180 49 : nWidth = atoi(szToken);
181 98 : else if( iToken == 1 )
182 49 : nHeight = atoi(szToken);
183 49 : else if( iToken == 2 )
184 49 : nMaxValue = atoi(szToken);
185 :
186 147 : iToken++;
187 147 : iIn++;
188 147 : break;
189 : }
190 :
191 443 : else if( !isspace((unsigned char)pszSrc[iIn]) )
192 : {
193 394 : szToken[iOut++] = pszSrc[iIn];
194 : }
195 :
196 443 : iIn++;
197 : }
198 : }
199 :
200 : CPLDebug( "PNM", "PNM header contains: width=%d, height=%d, maxval=%d",
201 49 : nWidth, nHeight, nMaxValue );
202 :
203 49 : 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 49 : poDS = new PNMDataset();
212 :
213 : /* -------------------------------------------------------------------- */
214 : /* Capture some information from the file that is of interest. */
215 : /* -------------------------------------------------------------------- */
216 49 : poDS->nRasterXSize = nWidth;
217 49 : poDS->nRasterYSize = nHeight;
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* Open file */
221 : /* -------------------------------------------------------------------- */
222 :
223 49 : if( poOpenInfo->eAccess == GA_Update )
224 17 : poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" );
225 : else
226 32 : poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
227 :
228 49 : if( poDS->fpImage == NULL )
229 : {
230 : CPLError( CE_Failure, CPLE_OpenFailed,
231 : "Failed to re-open %s within PNM driver.\n",
232 0 : poOpenInfo->pszFilename );
233 0 : return NULL;
234 : }
235 :
236 49 : poDS->eAccess = poOpenInfo->eAccess;
237 :
238 : /* -------------------------------------------------------------------- */
239 : /* Create band information objects. */
240 : /* -------------------------------------------------------------------- */
241 49 : int bMSBFirst = TRUE, iPixelSize;
242 : GDALDataType eDataType;
243 :
244 : #ifdef CPL_LSB
245 49 : bMSBFirst = FALSE;
246 : #endif
247 :
248 49 : if ( nMaxValue < 256 )
249 37 : eDataType = GDT_Byte;
250 : else
251 12 : eDataType = GDT_UInt16;
252 :
253 49 : iPixelSize = GDALGetDataTypeSize( eDataType ) / 8;
254 :
255 49 : if( poOpenInfo->pabyHeader[1] == '5' )
256 : {
257 31 : if (nWidth > INT_MAX / iPixelSize)
258 : {
259 : CPLError( CE_Failure, CPLE_AppDefined,
260 0 : "Int overflow occured.");
261 0 : delete poDS;
262 0 : return NULL;
263 : }
264 : poDS->SetBand(
265 : 1, new RawRasterBand( poDS, 1, poDS->fpImage, iIn, iPixelSize,
266 31 : nWidth*iPixelSize, eDataType, bMSBFirst, TRUE ));
267 31 : poDS->GetRasterBand(1)->SetColorInterpretation( GCI_GrayIndex );
268 : }
269 : else
270 : {
271 18 : if (nWidth > INT_MAX / (3 * iPixelSize))
272 : {
273 : CPLError( CE_Failure, CPLE_AppDefined,
274 0 : "Int overflow occured.");
275 0 : delete poDS;
276 0 : return NULL;
277 : }
278 : poDS->SetBand(
279 : 1, new RawRasterBand( poDS, 1, poDS->fpImage, iIn, 3*iPixelSize,
280 18 : nWidth*3*iPixelSize, eDataType, bMSBFirst, TRUE ));
281 : poDS->SetBand(
282 : 2, new RawRasterBand( poDS, 2, poDS->fpImage, iIn+iPixelSize,
283 : 3*iPixelSize, nWidth*3*iPixelSize,
284 36 : eDataType, bMSBFirst, TRUE ));
285 : poDS->SetBand(
286 : 3, new RawRasterBand( poDS, 3, poDS->fpImage, iIn+2*iPixelSize,
287 : 3*iPixelSize, nWidth*3*iPixelSize,
288 36 : eDataType, bMSBFirst, TRUE ));
289 :
290 18 : poDS->GetRasterBand(1)->SetColorInterpretation( GCI_RedBand );
291 18 : poDS->GetRasterBand(2)->SetColorInterpretation( GCI_GreenBand );
292 18 : poDS->GetRasterBand(3)->SetColorInterpretation( GCI_BlueBand );
293 : }
294 :
295 : /* -------------------------------------------------------------------- */
296 : /* Check for world file. */
297 : /* -------------------------------------------------------------------- */
298 : poDS->bGeoTransformValid =
299 : GDALReadWorldFile( poOpenInfo->pszFilename, ".wld",
300 49 : poDS->adfGeoTransform );
301 :
302 : /* -------------------------------------------------------------------- */
303 : /* Initialize any PAM information. */
304 : /* -------------------------------------------------------------------- */
305 49 : poDS->SetDescription( poOpenInfo->pszFilename );
306 49 : poDS->TryLoadXML();
307 :
308 : /* -------------------------------------------------------------------- */
309 : /* Check for overviews. */
310 : /* -------------------------------------------------------------------- */
311 49 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
312 :
313 49 : return( poDS );
314 : }
315 :
316 : /************************************************************************/
317 : /* Create() */
318 : /************************************************************************/
319 :
320 55 : GDALDataset *PNMDataset::Create( const char * pszFilename,
321 : int nXSize, int nYSize, int nBands,
322 : GDALDataType eType,
323 : char ** papszOptions )
324 :
325 : {
326 : /* -------------------------------------------------------------------- */
327 : /* Verify input options. */
328 : /* -------------------------------------------------------------------- */
329 55 : if( eType != GDT_Byte && eType != GDT_UInt16 )
330 : {
331 : CPLError( CE_Failure, CPLE_AppDefined,
332 : "Attempt to create PNM dataset with an illegal\n"
333 : "data type (%s), only Byte and UInt16 supported.\n",
334 27 : GDALGetDataTypeName(eType) );
335 :
336 27 : return NULL;
337 : }
338 :
339 28 : if( nBands != 1 && nBands != 3 )
340 : {
341 : CPLError( CE_Failure, CPLE_AppDefined,
342 : "Attempt to create PNM dataset with an illegal number\n"
343 : "of bands (%d). Must be 1 (greyscale) or 3 (RGB).\n",
344 7 : nBands );
345 :
346 7 : return NULL;
347 : }
348 :
349 : /* -------------------------------------------------------------------- */
350 : /* Try to create the file. */
351 : /* -------------------------------------------------------------------- */
352 : VSILFILE *fp;
353 :
354 21 : fp = VSIFOpenL( pszFilename, "wb" );
355 :
356 21 : if( fp == NULL )
357 : {
358 : CPLError( CE_Failure, CPLE_OpenFailed,
359 : "Attempt to create file `%s' failed.\n",
360 5 : pszFilename );
361 5 : return NULL;
362 : }
363 :
364 : /* -------------------------------------------------------------------- */
365 : /* Write out the header. */
366 : /* -------------------------------------------------------------------- */
367 : char szHeader[500];
368 16 : const char *pszMaxValue = NULL;
369 16 : int nMaxValue = 0;
370 :
371 16 : pszMaxValue = CSLFetchNameValue( papszOptions, "MAXVAL" );
372 16 : if ( pszMaxValue )
373 : {
374 0 : nMaxValue = atoi( pszMaxValue );
375 0 : if ( eType == GDT_Byte && (nMaxValue > 255 || nMaxValue < 0) )
376 0 : nMaxValue = 255;
377 0 : else if ( nMaxValue > 65535 || nMaxValue < 0 )
378 0 : nMaxValue = 65535;
379 : }
380 : else
381 : {
382 16 : if ( eType == GDT_Byte )
383 11 : nMaxValue = 255;
384 : else
385 5 : nMaxValue = 65535;
386 : }
387 :
388 :
389 16 : memset( szHeader, 0, sizeof(szHeader) );
390 :
391 16 : if( nBands == 3 )
392 5 : sprintf( szHeader, "P6\n%d %d\n%d\n", nXSize, nYSize, nMaxValue );
393 : else
394 11 : sprintf( szHeader, "P5\n%d %d\n%d\n", nXSize, nYSize, nMaxValue );
395 :
396 16 : VSIFWriteL( (void *) szHeader, strlen(szHeader) + 2, 1, fp );
397 16 : VSIFCloseL( fp );
398 :
399 16 : return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
400 : }
401 :
402 : /************************************************************************/
403 : /* GDALRegister_PNM() */
404 : /************************************************************************/
405 :
406 582 : void GDALRegister_PNM()
407 :
408 : {
409 : GDALDriver *poDriver;
410 :
411 582 : if( GDALGetDriverByName( "PNM" ) == NULL )
412 : {
413 561 : poDriver = new GDALDriver();
414 :
415 561 : poDriver->SetDescription( "PNM" );
416 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
417 561 : "Portable Pixmap Format (netpbm)" );
418 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
419 561 : "frmt_various.html#PNM" );
420 561 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pnm" );
421 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE,
422 561 : "image/x-portable-anymap" );
423 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
424 561 : "Byte UInt16" );
425 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
426 : "<CreationOptionList>"
427 : " <Option name='MAXVAL' type='unsigned int' description='Maximum color value'/>"
428 561 : "</CreationOptionList>" );
429 561 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
430 :
431 561 : poDriver->pfnOpen = PNMDataset::Open;
432 561 : poDriver->pfnCreate = PNMDataset::Create;
433 561 : poDriver->pfnIdentify = PNMDataset::Identify;
434 :
435 561 : GetGDALDriverManager()->RegisterDriver( poDriver );
436 : }
437 582 : }
|