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 98 : PNMDataset::PNMDataset()
71 : {
72 98 : fpImage = NULL;
73 98 : bGeoTransformValid = FALSE;
74 98 : adfGeoTransform[0] = 0.0;
75 98 : adfGeoTransform[1] = 1.0;
76 98 : adfGeoTransform[2] = 0.0;
77 98 : adfGeoTransform[3] = 0.0;
78 98 : adfGeoTransform[4] = 0.0;
79 98 : adfGeoTransform[5] = 1.0;
80 98 : }
81 :
82 : /************************************************************************/
83 : /* ~PNMDataset() */
84 : /************************************************************************/
85 :
86 98 : PNMDataset::~PNMDataset()
87 :
88 : {
89 98 : FlushCache();
90 98 : if( fpImage != NULL )
91 98 : VSIFCloseL( fpImage );
92 98 : }
93 :
94 : /************************************************************************/
95 : /* GetGeoTransform() */
96 : /************************************************************************/
97 :
98 8 : CPLErr PNMDataset::GetGeoTransform( double * padfTransform )
99 :
100 : {
101 :
102 8 : if( bGeoTransformValid )
103 : {
104 0 : memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
105 0 : return CE_None;
106 : }
107 : else
108 8 : return CE_Failure;
109 : }
110 :
111 : /************************************************************************/
112 : /* Identify() */
113 : /************************************************************************/
114 :
115 23358 : 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 23358 : if( poOpenInfo->nHeaderBytes < 10 )
123 21708 : return FALSE;
124 :
125 1992 : if( poOpenInfo->pabyHeader[0] != 'P' ||
126 110 : (poOpenInfo->pabyHeader[2] != ' ' && // XXX: Magick number
127 110 : poOpenInfo->pabyHeader[2] != '\t' && // may be followed
128 110 : poOpenInfo->pabyHeader[2] != '\n' && // any of the blank
129 12 : poOpenInfo->pabyHeader[2] != '\r') ) // characters
130 1552 : return FALSE;
131 :
132 134 : if( poOpenInfo->pabyHeader[1] != '5'
133 36 : && poOpenInfo->pabyHeader[1] != '6' )
134 0 : return FALSE;
135 :
136 98 : return TRUE;
137 : }
138 :
139 : /************************************************************************/
140 : /* Open() */
141 : /************************************************************************/
142 :
143 4448 : 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 4448 : if( !Identify( poOpenInfo ) )
151 4350 : return NULL;
152 :
153 : /* -------------------------------------------------------------------- */
154 : /* Parse out the tokens from the header. */
155 : /* -------------------------------------------------------------------- */
156 98 : const char *pszSrc = (const char *) poOpenInfo->pabyHeader;
157 : char szToken[512];
158 98 : int iIn, iToken = 0, nWidth =-1, nHeight=-1, nMaxValue=-1;
159 : unsigned int iOut;
160 :
161 98 : iIn = 2;
162 490 : while( iIn < poOpenInfo->nHeaderBytes && iToken < 3 )
163 : {
164 294 : iOut = 0;
165 294 : szToken[0] = '\0';
166 1474 : while( iOut < sizeof(szToken) && iIn < poOpenInfo->nHeaderBytes )
167 : {
168 1180 : if( pszSrc[iIn] == '#' )
169 : {
170 0 : while( pszSrc[iIn] != 10 && pszSrc[iIn] != 13
171 : && iIn < poOpenInfo->nHeaderBytes - 1 )
172 0 : iIn++;
173 : }
174 :
175 1180 : if( iOut != 0 && isspace((unsigned char)pszSrc[iIn]) )
176 : {
177 294 : szToken[iOut] = '\0';
178 :
179 294 : if( iToken == 0 )
180 98 : nWidth = atoi(szToken);
181 196 : else if( iToken == 1 )
182 98 : nHeight = atoi(szToken);
183 98 : else if( iToken == 2 )
184 98 : nMaxValue = atoi(szToken);
185 :
186 294 : iToken++;
187 294 : iIn++;
188 294 : break;
189 : }
190 :
191 886 : else if( !isspace((unsigned char)pszSrc[iIn]) )
192 : {
193 788 : szToken[iOut++] = pszSrc[iIn];
194 : }
195 :
196 886 : iIn++;
197 : }
198 : }
199 :
200 : CPLDebug( "PNM", "PNM header contains: width=%d, height=%d, maxval=%d",
201 98 : nWidth, nHeight, nMaxValue );
202 :
203 98 : 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 98 : poDS = new PNMDataset();
212 :
213 : /* -------------------------------------------------------------------- */
214 : /* Capture some information from the file that is of interest. */
215 : /* -------------------------------------------------------------------- */
216 98 : poDS->nRasterXSize = nWidth;
217 98 : poDS->nRasterYSize = nHeight;
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* Open file */
221 : /* -------------------------------------------------------------------- */
222 :
223 98 : if( poOpenInfo->eAccess == GA_Update )
224 34 : poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" );
225 : else
226 64 : poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
227 :
228 98 : 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 98 : poDS->eAccess = poOpenInfo->eAccess;
237 :
238 : /* -------------------------------------------------------------------- */
239 : /* Create band information objects. */
240 : /* -------------------------------------------------------------------- */
241 98 : int bMSBFirst = TRUE, iPixelSize;
242 : GDALDataType eDataType;
243 :
244 : #ifdef CPL_LSB
245 98 : bMSBFirst = FALSE;
246 : #endif
247 :
248 98 : if ( nMaxValue < 256 )
249 74 : eDataType = GDT_Byte;
250 : else
251 24 : eDataType = GDT_UInt16;
252 :
253 98 : iPixelSize = GDALGetDataTypeSize( eDataType ) / 8;
254 :
255 98 : if( poOpenInfo->pabyHeader[1] == '5' )
256 : {
257 62 : 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 62 : nWidth*iPixelSize, eDataType, bMSBFirst, TRUE ));
267 62 : poDS->GetRasterBand(1)->SetColorInterpretation( GCI_GrayIndex );
268 : }
269 : else
270 : {
271 36 : 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 36 : 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 72 : eDataType, bMSBFirst, TRUE ));
285 : poDS->SetBand(
286 : 3, new RawRasterBand( poDS, 3, poDS->fpImage, iIn+2*iPixelSize,
287 : 3*iPixelSize, nWidth*3*iPixelSize,
288 72 : eDataType, bMSBFirst, TRUE ));
289 :
290 36 : poDS->GetRasterBand(1)->SetColorInterpretation( GCI_RedBand );
291 36 : poDS->GetRasterBand(2)->SetColorInterpretation( GCI_GreenBand );
292 36 : poDS->GetRasterBand(3)->SetColorInterpretation( GCI_BlueBand );
293 : }
294 :
295 : /* -------------------------------------------------------------------- */
296 : /* Check for world file. */
297 : /* -------------------------------------------------------------------- */
298 : poDS->bGeoTransformValid =
299 : GDALReadWorldFile( poOpenInfo->pszFilename, ".wld",
300 98 : poDS->adfGeoTransform );
301 :
302 : /* -------------------------------------------------------------------- */
303 : /* Initialize any PAM information. */
304 : /* -------------------------------------------------------------------- */
305 98 : poDS->SetDescription( poOpenInfo->pszFilename );
306 98 : poDS->TryLoadXML();
307 :
308 : /* -------------------------------------------------------------------- */
309 : /* Check for overviews. */
310 : /* -------------------------------------------------------------------- */
311 98 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
312 :
313 98 : return( poDS );
314 : }
315 :
316 : /************************************************************************/
317 : /* Create() */
318 : /************************************************************************/
319 :
320 104 : 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 104 : 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 54 : GDALGetDataTypeName(eType) );
335 :
336 54 : return NULL;
337 : }
338 :
339 50 : 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 14 : nBands );
345 :
346 14 : return NULL;
347 : }
348 :
349 : /* -------------------------------------------------------------------- */
350 : /* Try to create the file. */
351 : /* -------------------------------------------------------------------- */
352 : VSILFILE *fp;
353 :
354 36 : fp = VSIFOpenL( pszFilename, "wb" );
355 :
356 36 : if( fp == NULL )
357 : {
358 : CPLError( CE_Failure, CPLE_OpenFailed,
359 : "Attempt to create file `%s' failed.\n",
360 4 : pszFilename );
361 4 : return NULL;
362 : }
363 :
364 : /* -------------------------------------------------------------------- */
365 : /* Write out the header. */
366 : /* -------------------------------------------------------------------- */
367 : char szHeader[500];
368 32 : const char *pszMaxValue = NULL;
369 32 : int nMaxValue = 0;
370 :
371 32 : pszMaxValue = CSLFetchNameValue( papszOptions, "MAXVAL" );
372 32 : 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 32 : if ( eType == GDT_Byte )
383 22 : nMaxValue = 255;
384 : else
385 10 : nMaxValue = 65535;
386 : }
387 :
388 :
389 32 : memset( szHeader, 0, sizeof(szHeader) );
390 :
391 32 : if( nBands == 3 )
392 10 : sprintf( szHeader, "P6\n%d %d\n%d\n", nXSize, nYSize, nMaxValue );
393 : else
394 22 : sprintf( szHeader, "P5\n%d %d\n%d\n", nXSize, nYSize, nMaxValue );
395 :
396 32 : VSIFWriteL( (void *) szHeader, strlen(szHeader) + 2, 1, fp );
397 32 : VSIFCloseL( fp );
398 :
399 32 : return (GDALDataset *) GDALOpen( pszFilename, GA_Update );
400 : }
401 :
402 : /************************************************************************/
403 : /* GDALRegister_PNM() */
404 : /************************************************************************/
405 :
406 1135 : void GDALRegister_PNM()
407 :
408 : {
409 : GDALDriver *poDriver;
410 :
411 1135 : if( GDALGetDriverByName( "PNM" ) == NULL )
412 : {
413 1093 : poDriver = new GDALDriver();
414 :
415 1093 : poDriver->SetDescription( "PNM" );
416 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
417 1093 : "Portable Pixmap Format (netpbm)" );
418 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
419 1093 : "frmt_various.html#PNM" );
420 1093 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pnm" );
421 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE,
422 1093 : "image/x-portable-anymap" );
423 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
424 1093 : "Byte UInt16" );
425 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
426 : "<CreationOptionList>"
427 : " <Option name='MAXVAL' type='unsigned int' description='Maximum color value'/>"
428 1093 : "</CreationOptionList>" );
429 1093 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
430 :
431 1093 : poDriver->pfnOpen = PNMDataset::Open;
432 1093 : poDriver->pfnCreate = PNMDataset::Create;
433 1093 : poDriver->pfnIdentify = PNMDataset::Identify;
434 :
435 1093 : GetGDALDriverManager()->RegisterDriver( poDriver );
436 : }
437 1135 : }
|