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