1 : /******************************************************************************
2 : * $Id: webpdataset.cpp 25570 2013-01-27 00:22:37Z rouault $
3 : *
4 : * Project: GDAL WEBP Driver
5 : * Purpose: Implement GDAL WEBP Support based on libwebp
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2011, Even Rouault
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 :
33 : #include "webp/decode.h"
34 : #include "webp/encode.h"
35 :
36 : CPL_CVSID("$Id: webpdataset.cpp 25570 2013-01-27 00:22:37Z rouault $");
37 :
38 : CPL_C_START
39 : void GDALRegister_WEBP(void);
40 : CPL_C_END
41 :
42 : /************************************************************************/
43 : /* ==================================================================== */
44 : /* WEBPDataset */
45 : /* ==================================================================== */
46 : /************************************************************************/
47 :
48 : class WEBPRasterBand;
49 :
50 : class WEBPDataset : public GDALPamDataset
51 : {
52 : friend class WEBPRasterBand;
53 :
54 : VSILFILE* fpImage;
55 : GByte* pabyUncompressed;
56 : int bHasBeenUncompressed;
57 : CPLErr eUncompressErrRet;
58 : CPLErr Uncompress();
59 :
60 : int bHasReadXMPMetadata;
61 :
62 : public:
63 : WEBPDataset();
64 : ~WEBPDataset();
65 :
66 : virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
67 : void *, int, int, GDALDataType,
68 : int, int *, int, int, int );
69 :
70 : virtual char **GetMetadata( const char * pszDomain = "" );
71 :
72 : static GDALDataset *Open( GDALOpenInfo * );
73 : static int Identify( GDALOpenInfo * );
74 : static GDALDataset* CreateCopy( const char * pszFilename,
75 : GDALDataset *poSrcDS,
76 : int bStrict, char ** papszOptions,
77 : GDALProgressFunc pfnProgress,
78 : void * pProgressData );
79 : };
80 :
81 : /************************************************************************/
82 : /* ==================================================================== */
83 : /* WEBPRasterBand */
84 : /* ==================================================================== */
85 : /************************************************************************/
86 :
87 : class WEBPRasterBand : public GDALPamRasterBand
88 33 : {
89 : friend class WEBPDataset;
90 :
91 : public:
92 :
93 : WEBPRasterBand( WEBPDataset *, int );
94 :
95 : virtual CPLErr IReadBlock( int, int, void * );
96 : virtual GDALColorInterp GetColorInterpretation();
97 : };
98 :
99 : /************************************************************************/
100 : /* WEBPRasterBand() */
101 : /************************************************************************/
102 :
103 33 : WEBPRasterBand::WEBPRasterBand( WEBPDataset *poDS, int nBand )
104 :
105 : {
106 33 : this->poDS = poDS;
107 :
108 33 : eDataType = GDT_Byte;
109 :
110 33 : nBlockXSize = poDS->nRasterXSize;
111 33 : nBlockYSize = 1;
112 33 : }
113 :
114 : /************************************************************************/
115 : /* IReadBlock() */
116 : /************************************************************************/
117 :
118 750 : CPLErr WEBPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
119 : void * pImage )
120 :
121 : {
122 750 : WEBPDataset* poGDS = (WEBPDataset*) poDS;
123 :
124 750 : if( poGDS->Uncompress() != CE_None )
125 0 : return CE_Failure;
126 :
127 : int i;
128 : GByte* pabyUncompressed =
129 750 : &poGDS->pabyUncompressed[nBlockYOff * nRasterXSize * poGDS->nBands + nBand - 1];
130 105450 : for(i=0;i<nRasterXSize;i++)
131 104700 : ((GByte*)pImage)[i] = pabyUncompressed[poGDS->nBands * i];
132 :
133 750 : return CE_None;
134 : }
135 :
136 : /************************************************************************/
137 : /* GetColorInterpretation() */
138 : /************************************************************************/
139 :
140 18 : GDALColorInterp WEBPRasterBand::GetColorInterpretation()
141 :
142 : {
143 18 : if ( nBand == 1 )
144 5 : return GCI_RedBand;
145 :
146 13 : else if( nBand == 2 )
147 5 : return GCI_GreenBand;
148 :
149 8 : else if ( nBand == 3 )
150 5 : return GCI_BlueBand;
151 :
152 : else
153 3 : return GCI_AlphaBand;
154 : }
155 :
156 : /************************************************************************/
157 : /* ==================================================================== */
158 : /* WEBPDataset */
159 : /* ==================================================================== */
160 : /************************************************************************/
161 :
162 :
163 : /************************************************************************/
164 : /* WEBPDataset() */
165 : /************************************************************************/
166 :
167 10 : WEBPDataset::WEBPDataset()
168 :
169 : {
170 10 : fpImage = NULL;
171 10 : pabyUncompressed = NULL;
172 10 : bHasBeenUncompressed = FALSE;
173 10 : eUncompressErrRet = CE_None;
174 10 : bHasReadXMPMetadata = FALSE;
175 10 : }
176 :
177 : /************************************************************************/
178 : /* ~WEBPDataset() */
179 : /************************************************************************/
180 :
181 10 : WEBPDataset::~WEBPDataset()
182 :
183 : {
184 10 : FlushCache();
185 10 : if (fpImage)
186 10 : VSIFCloseL(fpImage);
187 10 : VSIFree(pabyUncompressed);
188 10 : }
189 :
190 : /************************************************************************/
191 : /* GetMetadata() */
192 : /************************************************************************/
193 :
194 5 : char **WEBPDataset::GetMetadata( const char * pszDomain )
195 : {
196 5 : if ((pszDomain != NULL && EQUAL(pszDomain, "xml:XMP")) && !bHasReadXMPMetadata)
197 : {
198 2 : bHasReadXMPMetadata = TRUE;
199 :
200 2 : VSIFSeekL(fpImage, 12, SEEK_SET);
201 :
202 2 : int bFirst = TRUE;
203 2 : while(TRUE)
204 : {
205 : char szHeader[5];
206 : GUInt32 nChunkSize;
207 :
208 4 : if (VSIFReadL(szHeader, 1, 4, fpImage) != 4 ||
209 : VSIFReadL(&nChunkSize, 1, 4, fpImage) != 4)
210 0 : break;
211 :
212 4 : szHeader[4] = '\0';
213 : CPL_LSBPTR32(&nChunkSize);
214 :
215 4 : if (bFirst)
216 : {
217 2 : if (strcmp(szHeader, "VP8X") != 0 || nChunkSize < 10)
218 1 : break;
219 :
220 : int nFlags;
221 1 : if (VSIFReadL(&nFlags, 1, 4, fpImage) != 4)
222 0 : break;
223 : CPL_LSBPTR32(&nFlags);
224 1 : if ((nFlags & 8) == 0)
225 0 : break;
226 :
227 1 : VSIFSeekL(fpImage, nChunkSize - 4, SEEK_CUR);
228 :
229 1 : bFirst = FALSE;
230 : }
231 2 : else if (strcmp(szHeader, "META") == 0)
232 : {
233 1 : if (nChunkSize > 1024 * 1024)
234 0 : break;
235 :
236 1 : char* pszXMP = (char*) VSIMalloc(nChunkSize + 1);
237 1 : if (pszXMP == NULL)
238 0 : break;
239 :
240 1 : if ((GUInt32)VSIFReadL(pszXMP, 1, nChunkSize, fpImage) != nChunkSize)
241 : {
242 0 : VSIFree(pszXMP);
243 0 : break;
244 : }
245 1 : pszXMP[nChunkSize] = '\0';
246 :
247 : /* Avoid setting the PAM dirty bit just for that */
248 1 : int nOldPamFlags = nPamFlags;
249 :
250 : char *apszMDList[2];
251 1 : apszMDList[0] = pszXMP;
252 1 : apszMDList[1] = NULL;
253 1 : SetMetadata(apszMDList, "xml:XMP");
254 :
255 1 : nPamFlags = nOldPamFlags;
256 :
257 1 : VSIFree(pszXMP);
258 1 : break;
259 : }
260 : else
261 1 : VSIFSeekL(fpImage, nChunkSize, SEEK_CUR);
262 : }
263 : }
264 :
265 5 : return GDALPamDataset::GetMetadata(pszDomain);
266 : }
267 :
268 : /************************************************************************/
269 : /* Uncompress() */
270 : /************************************************************************/
271 :
272 750 : CPLErr WEBPDataset::Uncompress()
273 : {
274 750 : if (bHasBeenUncompressed)
275 745 : return eUncompressErrRet;
276 5 : bHasBeenUncompressed = TRUE;
277 5 : eUncompressErrRet = CE_Failure;
278 :
279 5 : VSIFSeekL(fpImage, 0, SEEK_END);
280 5 : vsi_l_offset nSize = VSIFTellL(fpImage);
281 5 : if (nSize != (vsi_l_offset)(uint32_t)nSize)
282 0 : return CE_Failure;
283 5 : VSIFSeekL(fpImage, 0, SEEK_SET);
284 5 : uint8_t* pabyCompressed = (uint8_t*)VSIMalloc(nSize);
285 5 : if (pabyCompressed == NULL)
286 0 : return CE_Failure;
287 5 : VSIFReadL(pabyCompressed, 1, nSize, fpImage);
288 : uint8_t* pRet;
289 :
290 5 : if (nBands == 4)
291 : pRet = WebPDecodeRGBAInto(pabyCompressed, (uint32_t)nSize,
292 : (uint8_t*)pabyUncompressed, nRasterXSize * nRasterYSize * nBands,
293 2 : nRasterXSize * nBands);
294 : else
295 : pRet = WebPDecodeRGBInto(pabyCompressed, (uint32_t)nSize,
296 : (uint8_t*)pabyUncompressed, nRasterXSize * nRasterYSize * nBands,
297 3 : nRasterXSize * nBands);
298 5 : VSIFree(pabyCompressed);
299 5 : if (pRet == NULL)
300 : {
301 : CPLError(CE_Failure, CPLE_AppDefined,
302 0 : "WebPDecodeRGBInto() failed");
303 0 : return CE_Failure;
304 : }
305 5 : eUncompressErrRet = CE_None;
306 :
307 5 : return CE_None;
308 : }
309 :
310 : /************************************************************************/
311 : /* IRasterIO() */
312 : /************************************************************************/
313 :
314 0 : CPLErr WEBPDataset::IRasterIO( GDALRWFlag eRWFlag,
315 : int nXOff, int nYOff, int nXSize, int nYSize,
316 : void *pData, int nBufXSize, int nBufYSize,
317 : GDALDataType eBufType,
318 : int nBandCount, int *panBandMap,
319 : int nPixelSpace, int nLineSpace, int nBandSpace )
320 :
321 : {
322 0 : if((eRWFlag == GF_Read) &&
323 : (nBandCount == nBands) &&
324 : (nXOff == 0) && (nXOff == 0) &&
325 : (nXSize == nBufXSize) && (nXSize == nRasterXSize) &&
326 : (nYSize == nBufYSize) && (nYSize == nRasterYSize) &&
327 : (eBufType == GDT_Byte) &&
328 : (nPixelSpace == nBands) &&
329 : (nLineSpace == (nPixelSpace*nXSize)) &&
330 : (nBandSpace == 1) &&
331 : (pData != NULL) &&
332 : (panBandMap != NULL) &&
333 0 : (panBandMap[0] == 1) && (panBandMap[1] == 2) && (panBandMap[2] == 3) && (nBands == 3 || panBandMap[3] == 4))
334 : {
335 0 : Uncompress();
336 0 : memcpy(pData, pabyUncompressed, nBands * nXSize * nYSize);
337 0 : return CE_None;
338 : }
339 :
340 : return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
341 : pData, nBufXSize, nBufYSize, eBufType,
342 : nBandCount, panBandMap,
343 0 : nPixelSpace, nLineSpace, nBandSpace);
344 : }
345 :
346 : /************************************************************************/
347 : /* Identify() */
348 : /************************************************************************/
349 :
350 11444 : int WEBPDataset::Identify( GDALOpenInfo * poOpenInfo )
351 :
352 : {
353 11444 : GByte *pabyHeader = NULL;
354 11444 : int nHeaderBytes = poOpenInfo->nHeaderBytes;
355 :
356 11444 : pabyHeader = poOpenInfo->pabyHeader;
357 :
358 11444 : if( nHeaderBytes < 20 )
359 11251 : return FALSE;
360 :
361 : return memcmp(pabyHeader, "RIFF", 4) == 0 &&
362 : memcmp(pabyHeader + 8, "WEBP", 4) == 0 &&
363 : (memcmp(pabyHeader + 12, "VP8 ", 4) == 0 ||
364 : memcmp(pabyHeader + 12, "VP8L", 4) == 0 ||
365 193 : memcmp(pabyHeader + 12, "VP8X", 4) == 0);
366 : }
367 :
368 : /************************************************************************/
369 : /* Open() */
370 : /************************************************************************/
371 :
372 1342 : GDALDataset *WEBPDataset::Open( GDALOpenInfo * poOpenInfo )
373 :
374 : {
375 1342 : if( !Identify( poOpenInfo ) )
376 1332 : return NULL;
377 :
378 : int nWidth, nHeight;
379 10 : if (!WebPGetInfo((const uint8_t*)poOpenInfo->pabyHeader, (uint32_t)poOpenInfo->nHeaderBytes,
380 : &nWidth, &nHeight))
381 0 : return NULL;
382 :
383 10 : int nBands = 3;
384 :
385 : #if WEBP_DECODER_ABI_VERSION >= 0x0002
386 : WebPDecoderConfig config;
387 10 : if (!WebPInitDecoderConfig(&config))
388 0 : return NULL;
389 :
390 10 : int bOK = WebPGetFeatures(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes, &config.input) == VP8_STATUS_OK;
391 :
392 10 : if (config.input.has_alpha)
393 3 : nBands = 4;
394 :
395 10 : WebPFreeDecBuffer(&config.output);
396 :
397 10 : if (!bOK)
398 0 : return NULL;
399 :
400 : #endif
401 :
402 10 : if( poOpenInfo->eAccess == GA_Update )
403 : {
404 : CPLError( CE_Failure, CPLE_NotSupported,
405 : "The WEBP driver does not support update access to existing"
406 0 : " datasets.\n" );
407 0 : return NULL;
408 : }
409 :
410 : /* -------------------------------------------------------------------- */
411 : /* Open the file using the large file api. */
412 : /* -------------------------------------------------------------------- */
413 10 : VSILFILE* fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
414 :
415 10 : if( fpImage == NULL )
416 0 : return NULL;
417 :
418 10 : GByte* pabyUncompressed = (GByte*)VSIMalloc3(nWidth, nHeight, nBands);
419 10 : if (pabyUncompressed == NULL)
420 : {
421 0 : VSIFCloseL(fpImage);
422 0 : return NULL;
423 : }
424 :
425 : /* -------------------------------------------------------------------- */
426 : /* Create a corresponding GDALDataset. */
427 : /* -------------------------------------------------------------------- */
428 : WEBPDataset *poDS;
429 :
430 10 : poDS = new WEBPDataset();
431 10 : poDS->nRasterXSize = nWidth;
432 10 : poDS->nRasterYSize = nHeight;
433 10 : poDS->fpImage = fpImage;
434 10 : poDS->pabyUncompressed = pabyUncompressed;
435 :
436 : /* -------------------------------------------------------------------- */
437 : /* Create band information objects. */
438 : /* -------------------------------------------------------------------- */
439 86 : for( int iBand = 0; iBand < nBands; iBand++ )
440 33 : poDS->SetBand( iBand+1, new WEBPRasterBand( poDS, iBand+1 ) );
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* Initialize any PAM information. */
444 : /* -------------------------------------------------------------------- */
445 10 : poDS->SetDescription( poOpenInfo->pszFilename );
446 :
447 10 : poDS->TryLoadXML( poOpenInfo->papszSiblingFiles );
448 :
449 : /* -------------------------------------------------------------------- */
450 : /* Open overviews. */
451 : /* -------------------------------------------------------------------- */
452 10 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->papszSiblingFiles );
453 :
454 10 : return poDS;
455 : }
456 :
457 : /************************************************************************/
458 : /* WebPUserData */
459 : /************************************************************************/
460 :
461 : typedef struct
462 : {
463 : VSILFILE *fp;
464 : GDALProgressFunc pfnProgress;
465 : void *pProgressData;
466 : } WebPUserData;
467 :
468 : /************************************************************************/
469 : /* WEBPDatasetWriter() */
470 : /************************************************************************/
471 :
472 : static
473 31 : int WEBPDatasetWriter(const uint8_t* data, size_t data_size,
474 : const WebPPicture* const picture)
475 : {
476 31 : WebPUserData* pUserData = (WebPUserData*)picture->custom_ptr;
477 31 : return VSIFWriteL(data, 1, data_size, pUserData->fp) == data_size;
478 : }
479 :
480 : /************************************************************************/
481 : /* WEBPDatasetProgressHook() */
482 : /************************************************************************/
483 :
484 : static
485 54 : int WEBPDatasetProgressHook(int percent, const WebPPicture* const picture)
486 : {
487 54 : WebPUserData* pUserData = (WebPUserData*)picture->custom_ptr;
488 54 : return pUserData->pfnProgress( percent / 100.0, NULL, pUserData->pProgressData );
489 : }
490 :
491 : /************************************************************************/
492 : /* CreateCopy() */
493 : /************************************************************************/
494 :
495 : GDALDataset *
496 23 : WEBPDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
497 : int bStrict, char ** papszOptions,
498 : GDALProgressFunc pfnProgress, void * pProgressData )
499 :
500 : {
501 23 : int nBands = poSrcDS->GetRasterCount();
502 23 : int nXSize = poSrcDS->GetRasterXSize();
503 23 : int nYSize = poSrcDS->GetRasterYSize();
504 :
505 : /* -------------------------------------------------------------------- */
506 : /* WEBP library initialization */
507 : /* -------------------------------------------------------------------- */
508 :
509 : WebPPicture sPicture;
510 23 : if (!WebPPictureInit(&sPicture))
511 : {
512 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureInit() failed");
513 0 : return NULL;
514 : }
515 :
516 : /* -------------------------------------------------------------------- */
517 : /* Some some rudimentary checks */
518 : /* -------------------------------------------------------------------- */
519 :
520 23 : if( nXSize > 16383 || nYSize > 16383 )
521 : {
522 : CPLError( CE_Failure, CPLE_NotSupported,
523 0 : "WEBP maximum image dimensions are 16383 x 16383.");
524 :
525 0 : return NULL;
526 : }
527 :
528 23 : if( nBands != 3
529 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
530 : && nBands != 4
531 : #endif
532 : )
533 : {
534 : CPLError( CE_Failure, CPLE_NotSupported,
535 : "WEBP driver doesn't support %d bands. Must be 3 (RGB) "
536 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
537 : "or 4 (RGBA) "
538 : #endif
539 : "bands.",
540 14 : nBands );
541 :
542 14 : return NULL;
543 : }
544 :
545 9 : GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
546 :
547 9 : if( eDT != GDT_Byte )
548 : {
549 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
550 : "WEBP driver doesn't support data type %s. "
551 : "Only eight bit byte bands supported.",
552 : GDALGetDataTypeName(
553 0 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
554 :
555 0 : if (bStrict)
556 0 : return NULL;
557 : }
558 :
559 : /* -------------------------------------------------------------------- */
560 : /* What options has the user selected? */
561 : /* -------------------------------------------------------------------- */
562 9 : float fQuality = 75.0f;
563 9 : const char* pszQUALITY = CSLFetchNameValue(papszOptions, "QUALITY");
564 9 : if( pszQUALITY != NULL )
565 : {
566 1 : fQuality = (float) atof(pszQUALITY);
567 1 : if( fQuality < 0.0f || fQuality > 100.0f )
568 : {
569 : CPLError( CE_Failure, CPLE_IllegalArg,
570 0 : "%s=%s is not a legal value.", "QUALITY", pszQUALITY);
571 0 : return NULL;
572 : }
573 : }
574 :
575 9 : WebPPreset nPreset = WEBP_PRESET_DEFAULT;
576 9 : const char* pszPRESET = CSLFetchNameValueDef(papszOptions, "PRESET", "DEFAULT");
577 9 : if (EQUAL(pszPRESET, "DEFAULT"))
578 9 : nPreset = WEBP_PRESET_DEFAULT;
579 0 : else if (EQUAL(pszPRESET, "PICTURE"))
580 0 : nPreset = WEBP_PRESET_PICTURE;
581 0 : else if (EQUAL(pszPRESET, "PHOTO"))
582 0 : nPreset = WEBP_PRESET_PHOTO;
583 0 : else if (EQUAL(pszPRESET, "PICTURE"))
584 0 : nPreset = WEBP_PRESET_PICTURE;
585 0 : else if (EQUAL(pszPRESET, "DRAWING"))
586 0 : nPreset = WEBP_PRESET_DRAWING;
587 0 : else if (EQUAL(pszPRESET, "ICON"))
588 0 : nPreset = WEBP_PRESET_ICON;
589 0 : else if (EQUAL(pszPRESET, "TEXT"))
590 0 : nPreset = WEBP_PRESET_TEXT;
591 : else
592 : {
593 : CPLError( CE_Failure, CPLE_IllegalArg,
594 0 : "%s=%s is not a legal value.", "PRESET", pszPRESET );
595 0 : return NULL;
596 : }
597 :
598 : WebPConfig sConfig;
599 9 : if (!WebPConfigInitInternal(&sConfig, nPreset, fQuality, WEBP_ENCODER_ABI_VERSION))
600 : {
601 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPConfigInit() failed");
602 0 : return NULL;
603 : }
604 :
605 :
606 : #define FETCH_AND_SET_OPTION_INT(name, fieldname, minval, maxval) \
607 : { \
608 : const char* pszVal = CSLFetchNameValue(papszOptions, name); \
609 : if (pszVal != NULL) \
610 : { \
611 : sConfig.fieldname = atoi(pszVal); \
612 : if (sConfig.fieldname < minval || sConfig.fieldname > maxval) \
613 : { \
614 : CPLError( CE_Failure, CPLE_IllegalArg, \
615 : "%s=%s is not a legal value.", name, pszVal ); \
616 : return NULL; \
617 : } \
618 : } \
619 : }
620 :
621 9 : FETCH_AND_SET_OPTION_INT("TARGETSIZE", target_size, 0, INT_MAX);
622 :
623 9 : const char* pszPSNR = CSLFetchNameValue(papszOptions, "PSNR");
624 9 : if (pszPSNR)
625 : {
626 0 : sConfig.target_PSNR = atof(pszPSNR);
627 0 : if (sConfig.target_PSNR < 0)
628 : {
629 : CPLError( CE_Failure, CPLE_IllegalArg,
630 0 : "PSNR=%s is not a legal value.", pszPSNR );
631 0 : return NULL;
632 : }
633 : }
634 :
635 9 : FETCH_AND_SET_OPTION_INT("METHOD", method, 0, 6);
636 9 : FETCH_AND_SET_OPTION_INT("SEGMENTS", segments, 1, 4);
637 9 : FETCH_AND_SET_OPTION_INT("SNS_STRENGTH", sns_strength, 0, 100);
638 9 : FETCH_AND_SET_OPTION_INT("FILTER_STRENGTH", filter_strength, 0, 100);
639 9 : FETCH_AND_SET_OPTION_INT("FILTER_SHARPNESS", filter_sharpness, 0, 7);
640 9 : FETCH_AND_SET_OPTION_INT("FILTER_TYPE", filter_type, 0, 1);
641 9 : FETCH_AND_SET_OPTION_INT("AUTOFILTER", autofilter, 0, 1);
642 9 : FETCH_AND_SET_OPTION_INT("PASS", pass, 1, 10);
643 9 : FETCH_AND_SET_OPTION_INT("PREPROCESSING", preprocessing, 0, 1);
644 9 : FETCH_AND_SET_OPTION_INT("PARTITIONS", partitions, 0, 3);
645 : #if WEBP_ENCODER_ABI_VERSION >= 0x0002
646 9 : FETCH_AND_SET_OPTION_INT("PARTITION_LIMIT", partition_limit, 0, 100);
647 : #endif
648 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
649 9 : sConfig.lossless = CSLFetchBoolean(papszOptions, "LOSSLESS", FALSE);
650 9 : if (sConfig.lossless)
651 1 : sPicture.use_argb = 1;
652 : #endif
653 :
654 9 : if (!WebPValidateConfig(&sConfig))
655 : {
656 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPValidateConfig() failed");
657 0 : return NULL;
658 : }
659 :
660 : /* -------------------------------------------------------------------- */
661 : /* Allocate memory */
662 : /* -------------------------------------------------------------------- */
663 : GByte *pabyBuffer;
664 :
665 9 : pabyBuffer = (GByte *) VSIMalloc( nBands * nXSize * nYSize );
666 9 : if (pabyBuffer == NULL)
667 : {
668 0 : return NULL;
669 : }
670 :
671 : /* -------------------------------------------------------------------- */
672 : /* Create the dataset. */
673 : /* -------------------------------------------------------------------- */
674 : VSILFILE *fpImage;
675 :
676 9 : fpImage = VSIFOpenL( pszFilename, "wb" );
677 9 : if( fpImage == NULL )
678 : {
679 : CPLError( CE_Failure, CPLE_OpenFailed,
680 : "Unable to create WEBP file %s.\n",
681 4 : pszFilename );
682 4 : VSIFree(pabyBuffer);
683 4 : return NULL;
684 : }
685 :
686 : WebPUserData sUserData;
687 5 : sUserData.fp = fpImage;
688 5 : sUserData.pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
689 5 : sUserData.pProgressData = pProgressData;
690 :
691 : /* -------------------------------------------------------------------- */
692 : /* WEBP library settings */
693 : /* -------------------------------------------------------------------- */
694 :
695 5 : sPicture.width = nXSize;
696 5 : sPicture.height = nYSize;
697 5 : sPicture.writer = WEBPDatasetWriter;
698 5 : sPicture.custom_ptr = &sUserData;
699 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
700 5 : sPicture.progress_hook = WEBPDatasetProgressHook;
701 : #endif
702 5 : if (!WebPPictureAlloc(&sPicture))
703 : {
704 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureAlloc() failed");
705 0 : VSIFree(pabyBuffer);
706 0 : VSIFCloseL( fpImage );
707 0 : return NULL;
708 : }
709 :
710 : /* -------------------------------------------------------------------- */
711 : /* Acquire source imagery. */
712 : /* -------------------------------------------------------------------- */
713 5 : CPLErr eErr = CE_None;
714 :
715 : eErr = poSrcDS->RasterIO( GF_Read, 0, 0, nXSize, nYSize,
716 : pabyBuffer, nXSize, nYSize, GDT_Byte,
717 : nBands, NULL,
718 5 : nBands, nBands * nXSize, 1 );
719 :
720 : /* -------------------------------------------------------------------- */
721 : /* Import and write to file */
722 : /* -------------------------------------------------------------------- */
723 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
724 8 : if (eErr == CE_None && nBands == 4)
725 : {
726 3 : if (!WebPPictureImportRGBA(&sPicture, pabyBuffer, nBands * nXSize))
727 : {
728 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGBA() failed");
729 0 : eErr = CE_Failure;
730 : }
731 : }
732 : else
733 : #endif
734 2 : if (eErr == CE_None &&
735 : !WebPPictureImportRGB(&sPicture, pabyBuffer, nBands * nXSize))
736 : {
737 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGB() failed");
738 0 : eErr = CE_Failure;
739 : }
740 :
741 5 : if (eErr == CE_None && !WebPEncode(&sConfig, &sPicture))
742 : {
743 0 : const char* pszErrorMsg = NULL;
744 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
745 0 : switch(sPicture.error_code)
746 : {
747 0 : case VP8_ENC_ERROR_OUT_OF_MEMORY: pszErrorMsg = "Out of memory"; break;
748 0 : case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY: pszErrorMsg = "Out of memory while flushing bits"; break;
749 0 : case VP8_ENC_ERROR_NULL_PARAMETER: pszErrorMsg = "A pointer parameter is NULL"; break;
750 0 : case VP8_ENC_ERROR_INVALID_CONFIGURATION: pszErrorMsg = "Configuration is invalid"; break;
751 0 : case VP8_ENC_ERROR_BAD_DIMENSION: pszErrorMsg = "Picture has invalid width/height"; break;
752 0 : case VP8_ENC_ERROR_PARTITION0_OVERFLOW: pszErrorMsg = "Partition is bigger than 512k. Try using less SEGMENTS, or increase PARTITION_LIMIT value"; break;
753 0 : case VP8_ENC_ERROR_PARTITION_OVERFLOW: pszErrorMsg = "Partition is bigger than 16M"; break;
754 0 : case VP8_ENC_ERROR_BAD_WRITE: pszErrorMsg = "Error while flusing bytes"; break;
755 0 : case VP8_ENC_ERROR_FILE_TOO_BIG: pszErrorMsg = "File is bigger than 4G"; break;
756 0 : case VP8_ENC_ERROR_USER_ABORT: pszErrorMsg = "User interrupted"; break;
757 : default: break;
758 : }
759 : #endif
760 0 : if (pszErrorMsg)
761 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed : %s", pszErrorMsg);
762 : else
763 0 : CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed");
764 0 : eErr = CE_Failure;
765 : }
766 :
767 : /* -------------------------------------------------------------------- */
768 : /* Cleanup and close. */
769 : /* -------------------------------------------------------------------- */
770 5 : CPLFree( pabyBuffer );
771 :
772 5 : WebPPictureFree(&sPicture);
773 :
774 5 : VSIFCloseL( fpImage );
775 :
776 5 : if( eErr != CE_None )
777 : {
778 0 : VSIUnlink( pszFilename );
779 0 : return NULL;
780 : }
781 :
782 : /* -------------------------------------------------------------------- */
783 : /* Re-open dataset, and copy any auxilary pam information. */
784 : /* -------------------------------------------------------------------- */
785 5 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
786 :
787 : /* If outputing to stdout, we can't reopen it, so we'll return */
788 : /* a fake dataset to make the caller happy */
789 5 : CPLPushErrorHandler(CPLQuietErrorHandler);
790 5 : WEBPDataset *poDS = (WEBPDataset*) WEBPDataset::Open( &oOpenInfo );
791 5 : CPLPopErrorHandler();
792 5 : if( poDS )
793 : {
794 5 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
795 5 : return poDS;
796 : }
797 :
798 0 : return NULL;
799 : }
800 :
801 : /************************************************************************/
802 : /* GDALRegister_WEBP() */
803 : /************************************************************************/
804 :
805 610 : void GDALRegister_WEBP()
806 :
807 : {
808 : GDALDriver *poDriver;
809 :
810 610 : if( GDALGetDriverByName( "WEBP" ) == NULL )
811 : {
812 588 : poDriver = new GDALDriver();
813 :
814 588 : poDriver->SetDescription( "WEBP" );
815 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
816 588 : "WEBP" );
817 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
818 588 : "frmt_webp.html" );
819 588 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "webp" );
820 588 : poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/webp" );
821 :
822 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
823 588 : "Byte" );
824 :
825 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
826 : "<CreationOptionList>\n"
827 : " <Option name='QUALITY' type='float' description='good=100, bad=0' default='75'/>\n"
828 : #if WEBP_ENCODER_ABI_VERSION >= 0x0100
829 : " <Option name='LOSSLESS' type='boolean' description='Whether lossless compression should be used' default='FALSE'/>\n"
830 : #endif
831 : " <Option name='PRESET' type='string-select' description='kind of image' default='DEFAULT'>\n"
832 : " <Value>DEFAULT</Value>\n"
833 : " <Value>PICTURE</Value>\n"
834 : " <Value>PHOTO</Value>\n"
835 : " <Value>DRAWING</Value>\n"
836 : " <Value>ICON</Value>\n"
837 : " <Value>TEXT</Value>\n"
838 : " </Option>\n"
839 : " <Option name='TARGETSIZE' type='int' description='if non-zero, desired target size in bytes. Has precedence over QUALITY'/>\n"
840 : " <Option name='PSNR' type='float' description='if non-zero, minimal distortion to to achieve. Has precedence over TARGETSIZE'/>\n"
841 : " <Option name='METHOD' type='int' description='quality/speed trade-off. fast=0, slower-better=6' default='4'/>\n"
842 : " <Option name='SEGMENTS' type='int' description='maximum number of segments [1-4]' default='4'/>\n"
843 : " <Option name='SNS_STRENGTH' type='int' description='Spatial Noise Shaping. off=0, maximum=100' default='50'/>\n"
844 : " <Option name='FILTER_STRENGTH' type='int' description='Filter strength. off=0, strongest=100' default='20'/>\n"
845 : " <Option name='FILTER_SHARPNESS' type='int' description='Filter sharpness. off=0, least sharp=7' default='0'/>\n"
846 : " <Option name='FILTER_TYPE' type='int' description='Filtering type. simple=0, strong=1' default='0'/>\n"
847 : " <Option name='AUTOFILTER' type='int' description=\"Auto adjust filter's strength. off=0, on=1\" default='0'/>\n"
848 : " <Option name='PASS' type='int' description='Number of entropy analysis passes [1-10]' default='1'/>\n"
849 : " <Option name='PREPROCESSING' type='int' description='Preprocessing filter. none=0, segment-smooth=1' default='0'/>\n"
850 : " <Option name='PARTITIONS' type='int' description='log2(number of token partitions) in [0..3]' default='0'/>\n"
851 : #if WEBP_ENCODER_ABI_VERSION >= 0x0002
852 : " <Option name='PARTITION_LIMIT' type='int' description='quality degradation allowed to fit the 512k limit on prediction modes coding (0=no degradation, 100=full)' default='0'/>\n"
853 : #endif
854 588 : "</CreationOptionList>\n" );
855 :
856 588 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
857 :
858 588 : poDriver->pfnIdentify = WEBPDataset::Identify;
859 588 : poDriver->pfnOpen = WEBPDataset::Open;
860 588 : poDriver->pfnCreateCopy = WEBPDataset::CreateCopy;
861 :
862 588 : GetGDALDriverManager()->RegisterDriver( poDriver );
863 : }
864 610 : }
|