static int ReadYUV(FILE* in_file, WebPPicture* const pic) { const int use_argb = pic->use_argb; const int uv_width = (pic->width + 1) / 2; const int uv_height = (pic->height + 1) / 2; int y; int ok = 0; pic->use_argb = 0; if (!WebPPictureAlloc(pic)) return ok; for (y = 0; y < pic->height; ++y) { if (fread(pic->y + y * pic->y_stride, pic->width, 1, in_file) != 1) { goto End; } } for (y = 0; y < uv_height; ++y) { if (fread(pic->u + y * pic->uv_stride, uv_width, 1, in_file) != 1) goto End; } for (y = 0; y < uv_height; ++y) { if (fread(pic->v + y * pic->uv_stride, uv_width, 1, in_file) != 1) goto End; } ok = 1; if (use_argb) ok = WebPPictureYUVAToARGB(pic); End: return ok; }
static int EncodeLossless(const uint8_t* const data, int width, int height, int effort_level, // in [0..6] range VP8BitWriter* const bw, WebPAuxStats* const stats) { int ok = 0; WebPConfig config; WebPPicture picture; VP8LBitWriter tmp_bw; WebPPictureInit(&picture); picture.width = width; picture.height = height; picture.use_argb = 1; picture.stats = stats; if (!WebPPictureAlloc(&picture)) return 0; // Transfer the alpha values to the green channel. { int i, j; uint32_t* dst = picture.argb; const uint8_t* src = data; for (j = 0; j < picture.height; ++j) { for (i = 0; i < picture.width; ++i) { dst[i] = (src[i] << 8) | 0xff000000u; } src += width; dst += picture.argb_stride; } } WebPConfigInit(&config); config.lossless = 1; config.method = effort_level; // impact is very small // Set moderate default quality setting for alpha. Higher qualities (80 and // above) could be very slow. config.quality = 10.f + 15.f * effort_level; if (config.quality > 100.f) config.quality = 100.f; ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3); ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK); WebPPictureFree(&picture); if (ok) { const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw); const size_t data_size = VP8LBitWriterNumBytes(&tmp_bw); VP8BitWriterAppend(bw, data, data_size); } VP8LBitWriterDestroy(&tmp_bw); return ok && !bw->error_; }
int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) { int y, width, uv_width; if (pic == NULL || gray_data == NULL) return 0; pic->colorspace = WEBP_YUV420; if (!WebPPictureAlloc(pic)) return 0; width = pic->width; uv_width = (width + 1) >> 1; for (y = 0; y < pic->height; ++y) { memcpy(pic->y + y * pic->y_stride, gray_data, width); gray_data += width; // <- we could use some 'data_stride' here if needed if ((y & 1) == 0) { memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width); memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width); } }
static int EncodeLossless(const uint8_t* const data, int width, int height, int effort_level, // in [0..6] range VP8LBitWriter* const bw, WebPAuxStats* const stats) { int ok = 0; WebPConfig config; WebPPicture picture; WebPPictureInit(&picture); picture.width = width; picture.height = height; picture.use_argb = 1; picture.stats = stats; if (!WebPPictureAlloc(&picture)) return 0; // Transfer the alpha values to the green channel. { int i, j; uint32_t* dst = picture.argb; const uint8_t* src = data; for (j = 0; j < picture.height; ++j) { for (i = 0; i < picture.width; ++i) { dst[i] = src[i] << 8; // we leave A/R/B channels zero'd. } src += width; dst += picture.argb_stride; } } WebPConfigInit(&config); config.lossless = 1; config.method = effort_level; // impact is very small // Set a low default quality for encoding alpha. Ensure that Alpha quality at // lower methods (3 and below) is less than the threshold for triggering // costly 'BackwardReferencesTraceBackwards'. config.quality = 8.f * effort_level; assert(config.quality >= 0 && config.quality <= 100.f); ok = (VP8LEncodeStream(&config, &picture, bw) == VP8_ENC_OK); WebPPictureFree(&picture); ok = ok && !bw->error_; if (!ok) { VP8LBitWriterDestroy(bw); return 0; } return 1; }
static int EncodeLossless(const uint8_t* const data, int width, int height, int effort_level, // in [0..6] range VP8LBitWriter* const bw, WebPAuxStats* const stats) { int ok = 0; WebPConfig config; WebPPicture picture; WebPPictureInit(&picture); picture.width = width; picture.height = height; picture.use_argb = 1; picture.stats = stats; if (!WebPPictureAlloc(&picture)) return 0; // Transfer the alpha values to the green channel. WebPDispatchAlphaToGreen(data, width, picture.width, picture.height, picture.argb, picture.argb_stride); WebPConfigInit(&config); config.lossless = 1; // Enable exact, or it would alter RGB values of transparent alpha, which is // normally OK but not here since we are not encoding the input image but an // internal encoding-related image containing necessary exact information in // RGB channels. config.exact = 1; config.method = effort_level; // impact is very small // Set a low default quality for encoding alpha. Ensure that Alpha quality at // lower methods (3 and below) is less than the threshold for triggering // costly 'BackwardReferencesTraceBackwards'. config.quality = 8.f * effort_level; assert(config.quality >= 0 && config.quality <= 100.f); // TODO(urvang): Temporary fix to avoid generating images that trigger // a decoder bug related to alpha with color cache. // See: https://code.google.com/p/webp/issues/detail?id=239 // Need to re-enable this later. ok = (VP8LEncodeStream(&config, &picture, bw, 0 /*use_cache*/) == VP8_ENC_OK); WebPPictureFree(&picture); ok = ok && !bw->error_; if (!ok) { VP8LBitWriterWipeOut(bw); return 0; } return 1; }
void save_as_webp(T1& file, T2 const& image, WebPConfig const& config, bool alpha) { bool valid = WebPValidateConfig(&config); if (!valid) { throw std::runtime_error("Invalid configuration"); } WebPPicture pic; if (!WebPPictureInit(&pic)) { throw std::runtime_error("version mismatch"); } pic.width = image.width(); pic.height = image.height(); int ok = 0; #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 pic.use_argb = !!config.lossless; // lossless fast track if (pic.use_argb) { pic.colorspace = static_cast<WebPEncCSP>(pic.colorspace | WEBP_CSP_ALPHA_BIT); if (WebPPictureAlloc(&pic)) { ok = 1; const int width = pic.width; const int height = pic.height; for (int y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.getRow(y); for (int x = 0; x < width; ++x) { const unsigned rgba = row[x]; unsigned a = (rgba >> 24) & 0xff; unsigned r = rgba & 0xff; unsigned g = (rgba >> 8 ) & 0xff; unsigned b = (rgba >> 16) & 0xff; const uint32_t argb = (a << 24) | (r << 16) | (g << 8) | (b); pic.argb[x + y * pic.argb_stride] = argb; } } }
static int _WebPPictureImportCairoSurface (WebPPicture *const picture, cairo_surface_t *image) { int stride; guchar *src_row; uint32_t *dest_row; int y, x, temp; guchar r, g, b, a; if (_cairo_image_surface_get_has_alpha (image)) picture->colorspace |= WEBP_CSP_ALPHA_BIT; else picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; if (! WebPPictureAlloc (picture)) return 0; stride = cairo_image_surface_get_stride (image); src_row = _cairo_image_surface_flush_and_get_data (image); dest_row = picture->argb; for (y= 0; y < cairo_image_surface_get_height (image); y++) { guchar *pixel = src_row; for (x = 0; x < cairo_image_surface_get_width (image); x++) { CAIRO_GET_RGBA (pixel, r, g, b, a); dest_row[x] = ((a << 24) | (r << 16) | (g << 8) | b); pixel += 4; } src_row += stride; dest_row += picture->argb_stride; } return 1; }
GDALDataset * WEBPDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, int bStrict, char ** papszOptions, GDALProgressFunc pfnProgress, void * pProgressData ) { int nBands = poSrcDS->GetRasterCount(); int nXSize = poSrcDS->GetRasterXSize(); int nYSize = poSrcDS->GetRasterYSize(); /* -------------------------------------------------------------------- */ /* WEBP library initialization */ /* -------------------------------------------------------------------- */ WebPPicture sPicture; if (!WebPPictureInit(&sPicture)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureInit() failed"); return NULL; } /* -------------------------------------------------------------------- */ /* Some some rudimentary checks */ /* -------------------------------------------------------------------- */ if( nXSize > 16383 || nYSize > 16383 ) { CPLError( CE_Failure, CPLE_NotSupported, "WEBP maximum image dimensions are 16383 x 16383."); return NULL; } if( nBands != 3 #if WEBP_ENCODER_ABI_VERSION >= 0x0100 && nBands != 4 #endif ) { CPLError( CE_Failure, CPLE_NotSupported, "WEBP driver doesn't support %d bands. Must be 3 (RGB) " #if WEBP_ENCODER_ABI_VERSION >= 0x0100 "or 4 (RGBA) " #endif "bands.", nBands ); return NULL; } GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); if( eDT != GDT_Byte ) { CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, "WEBP driver doesn't support data type %s. " "Only eight bit byte bands supported.", GDALGetDataTypeName( poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); if (bStrict) return NULL; } /* -------------------------------------------------------------------- */ /* What options has the user selected? */ /* -------------------------------------------------------------------- */ float fQuality = 75.0f; const char* pszQUALITY = CSLFetchNameValue(papszOptions, "QUALITY"); if( pszQUALITY != NULL ) { fQuality = (float) CPLAtof(pszQUALITY); if( fQuality < 0.0f || fQuality > 100.0f ) { CPLError( CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.", "QUALITY", pszQUALITY); return NULL; } } WebPPreset nPreset = WEBP_PRESET_DEFAULT; const char* pszPRESET = CSLFetchNameValueDef(papszOptions, "PRESET", "DEFAULT"); if (EQUAL(pszPRESET, "DEFAULT")) nPreset = WEBP_PRESET_DEFAULT; else if (EQUAL(pszPRESET, "PICTURE")) nPreset = WEBP_PRESET_PICTURE; else if (EQUAL(pszPRESET, "PHOTO")) nPreset = WEBP_PRESET_PHOTO; else if (EQUAL(pszPRESET, "PICTURE")) nPreset = WEBP_PRESET_PICTURE; else if (EQUAL(pszPRESET, "DRAWING")) nPreset = WEBP_PRESET_DRAWING; else if (EQUAL(pszPRESET, "ICON")) nPreset = WEBP_PRESET_ICON; else if (EQUAL(pszPRESET, "TEXT")) nPreset = WEBP_PRESET_TEXT; else { CPLError( CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.", "PRESET", pszPRESET ); return NULL; } WebPConfig sConfig; if (!WebPConfigInitInternal(&sConfig, nPreset, fQuality, WEBP_ENCODER_ABI_VERSION)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPConfigInit() failed"); return NULL; } #define FETCH_AND_SET_OPTION_INT(name, fieldname, minval, maxval) \ { \ const char* pszVal = CSLFetchNameValue(papszOptions, name); \ if (pszVal != NULL) \ { \ sConfig.fieldname = atoi(pszVal); \ if (sConfig.fieldname < minval || sConfig.fieldname > maxval) \ { \ CPLError( CE_Failure, CPLE_IllegalArg, \ "%s=%s is not a legal value.", name, pszVal ); \ return NULL; \ } \ } \ } FETCH_AND_SET_OPTION_INT("TARGETSIZE", target_size, 0, INT_MAX); const char* pszPSNR = CSLFetchNameValue(papszOptions, "PSNR"); if (pszPSNR) { sConfig.target_PSNR = CPLAtof(pszPSNR); if (sConfig.target_PSNR < 0) { CPLError( CE_Failure, CPLE_IllegalArg, "PSNR=%s is not a legal value.", pszPSNR ); return NULL; } } FETCH_AND_SET_OPTION_INT("METHOD", method, 0, 6); FETCH_AND_SET_OPTION_INT("SEGMENTS", segments, 1, 4); FETCH_AND_SET_OPTION_INT("SNS_STRENGTH", sns_strength, 0, 100); FETCH_AND_SET_OPTION_INT("FILTER_STRENGTH", filter_strength, 0, 100); FETCH_AND_SET_OPTION_INT("FILTER_SHARPNESS", filter_sharpness, 0, 7); FETCH_AND_SET_OPTION_INT("FILTER_TYPE", filter_type, 0, 1); FETCH_AND_SET_OPTION_INT("AUTOFILTER", autofilter, 0, 1); FETCH_AND_SET_OPTION_INT("PASS", pass, 1, 10); FETCH_AND_SET_OPTION_INT("PREPROCESSING", preprocessing, 0, 1); FETCH_AND_SET_OPTION_INT("PARTITIONS", partitions, 0, 3); #if WEBP_ENCODER_ABI_VERSION >= 0x0002 FETCH_AND_SET_OPTION_INT("PARTITION_LIMIT", partition_limit, 0, 100); #endif #if WEBP_ENCODER_ABI_VERSION >= 0x0100 sConfig.lossless = CSLFetchBoolean(papszOptions, "LOSSLESS", FALSE); if (sConfig.lossless) sPicture.use_argb = 1; #endif if (!WebPValidateConfig(&sConfig)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPValidateConfig() failed"); return NULL; } /* -------------------------------------------------------------------- */ /* Allocate memory */ /* -------------------------------------------------------------------- */ GByte *pabyBuffer; pabyBuffer = (GByte *) VSIMalloc( nBands * nXSize * nYSize ); if (pabyBuffer == NULL) { return NULL; } /* -------------------------------------------------------------------- */ /* Create the dataset. */ /* -------------------------------------------------------------------- */ VSILFILE *fpImage; fpImage = VSIFOpenL( pszFilename, "wb" ); if( fpImage == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Unable to create WEBP file %s.\n", pszFilename ); VSIFree(pabyBuffer); return NULL; } WebPUserData sUserData; sUserData.fp = fpImage; sUserData.pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress; sUserData.pProgressData = pProgressData; /* -------------------------------------------------------------------- */ /* WEBP library settings */ /* -------------------------------------------------------------------- */ sPicture.width = nXSize; sPicture.height = nYSize; sPicture.writer = WEBPDatasetWriter; sPicture.custom_ptr = &sUserData; #if WEBP_ENCODER_ABI_VERSION >= 0x0100 sPicture.progress_hook = WEBPDatasetProgressHook; #endif if (!WebPPictureAlloc(&sPicture)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureAlloc() failed"); VSIFree(pabyBuffer); VSIFCloseL( fpImage ); return NULL; } /* -------------------------------------------------------------------- */ /* Acquire source imagery. */ /* -------------------------------------------------------------------- */ CPLErr eErr = CE_None; eErr = poSrcDS->RasterIO( GF_Read, 0, 0, nXSize, nYSize, pabyBuffer, nXSize, nYSize, GDT_Byte, nBands, NULL, nBands, nBands * nXSize, 1, NULL ); /* -------------------------------------------------------------------- */ /* Import and write to file */ /* -------------------------------------------------------------------- */ #if WEBP_ENCODER_ABI_VERSION >= 0x0100 if (eErr == CE_None && nBands == 4) { if (!WebPPictureImportRGBA(&sPicture, pabyBuffer, nBands * nXSize)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGBA() failed"); eErr = CE_Failure; } } else #endif if (eErr == CE_None && !WebPPictureImportRGB(&sPicture, pabyBuffer, nBands * nXSize)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGB() failed"); eErr = CE_Failure; } if (eErr == CE_None && !WebPEncode(&sConfig, &sPicture)) { const char* pszErrorMsg = NULL; #if WEBP_ENCODER_ABI_VERSION >= 0x0100 switch(sPicture.error_code) { case VP8_ENC_ERROR_OUT_OF_MEMORY: pszErrorMsg = "Out of memory"; break; case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY: pszErrorMsg = "Out of memory while flushing bits"; break; case VP8_ENC_ERROR_NULL_PARAMETER: pszErrorMsg = "A pointer parameter is NULL"; break; case VP8_ENC_ERROR_INVALID_CONFIGURATION: pszErrorMsg = "Configuration is invalid"; break; case VP8_ENC_ERROR_BAD_DIMENSION: pszErrorMsg = "Picture has invalid width/height"; break; case VP8_ENC_ERROR_PARTITION0_OVERFLOW: pszErrorMsg = "Partition is bigger than 512k. Try using less SEGMENTS, or increase PARTITION_LIMIT value"; break; case VP8_ENC_ERROR_PARTITION_OVERFLOW: pszErrorMsg = "Partition is bigger than 16M"; break; case VP8_ENC_ERROR_BAD_WRITE: pszErrorMsg = "Error while flusing bytes"; break; case VP8_ENC_ERROR_FILE_TOO_BIG: pszErrorMsg = "File is bigger than 4G"; break; case VP8_ENC_ERROR_USER_ABORT: pszErrorMsg = "User interrupted"; break; default: break; } #endif if (pszErrorMsg) CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed : %s", pszErrorMsg); else CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed"); eErr = CE_Failure; } /* -------------------------------------------------------------------- */ /* Cleanup and close. */ /* -------------------------------------------------------------------- */ CPLFree( pabyBuffer ); WebPPictureFree(&sPicture); VSIFCloseL( fpImage ); if( eErr != CE_None ) { VSIUnlink( pszFilename ); return NULL; } /* -------------------------------------------------------------------- */ /* Re-open dataset, and copy any auxiliary pam information. */ /* -------------------------------------------------------------------- */ GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); /* If outputing to stdout, we can't reopen it, so we'll return */ /* a fake dataset to make the caller happy */ CPLPushErrorHandler(CPLQuietErrorHandler); WEBPDataset *poDS = (WEBPDataset*) WEBPDataset::Open( &oOpenInfo ); CPLPopErrorHandler(); if( poDS ) { poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); return poDS; } return NULL; }
int ReadWebP(const uint8_t* const data, size_t data_size, WebPPicture* const pic, int keep_alpha, Metadata* const metadata) { int ok = 0; VP8StatusCode status = VP8_STATUS_OK; WebPDecoderConfig config; WebPDecBuffer* const output_buffer = &config.output; WebPBitstreamFeatures* const bitstream = &config.input; if (data == NULL || data_size == 0 || pic == NULL) return 0; // TODO(jzern): add Exif/XMP/ICC extraction. if (metadata != NULL) { fprintf(stderr, "Warning: metadata extraction from WebP is unsupported.\n"); } if (!WebPInitDecoderConfig(&config)) { fprintf(stderr, "Library version mismatch!\n"); return 0; } status = WebPGetFeatures(data, data_size, bitstream); if (status != VP8_STATUS_OK) { PrintWebPError("input data", status); return 0; } { const int has_alpha = keep_alpha && bitstream->has_alpha; if (pic->use_argb) { output_buffer->colorspace = has_alpha ? MODE_RGBA : MODE_RGB; } else { output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV; } status = DecodeWebP(data, data_size, 0, &config); if (status == VP8_STATUS_OK) { pic->width = output_buffer->width; pic->height = output_buffer->height; if (pic->use_argb) { const uint8_t* const rgba = output_buffer->u.RGBA.rgba; const int stride = output_buffer->u.RGBA.stride; ok = has_alpha ? WebPPictureImportRGBA(pic, rgba, stride) : WebPPictureImportRGB(pic, rgba, stride); } else { pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; ok = WebPPictureAlloc(pic); if (!ok) { status = VP8_STATUS_OUT_OF_MEMORY; } else { const WebPYUVABuffer* const yuva = &output_buffer->u.YUVA; const int uv_width = (pic->width + 1) >> 1; const int uv_height = (pic->height + 1) >> 1; ImgIoUtilCopyPlane(yuva->y, yuva->y_stride, pic->y, pic->y_stride, pic->width, pic->height); ImgIoUtilCopyPlane(yuva->u, yuva->u_stride, pic->u, pic->uv_stride, uv_width, uv_height); ImgIoUtilCopyPlane(yuva->v, yuva->v_stride, pic->v, pic->uv_stride, uv_width, uv_height); if (has_alpha) { ImgIoUtilCopyPlane(yuva->a, yuva->a_stride, pic->a, pic->a_stride, pic->width, pic->height); } } } } }
void save_as_webp(T1& file, float quality, int method, int lossless, int image_hint, bool alpha, T2 const& image) { WebPConfig config; if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality)) { throw std::runtime_error("version mismatch"); } // Add additional tuning if (method >= 0) config.method = method; #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 config.lossless = !!lossless; config.image_hint = static_cast<WebPImageHint>(image_hint); #else #ifdef _MSC_VER #pragma NOTE(compiling against webp that does not support lossless flag) #else #warning "compiling against webp that does not support lossless flag" #endif #endif bool valid = WebPValidateConfig(&config); if (!valid) { throw std::runtime_error("Invalid configuration"); } WebPPicture pic; if (!WebPPictureInit(&pic)) { throw std::runtime_error("version mismatch"); } pic.width = image.width(); pic.height = image.height(); int ok = 0; #if (WEBP_ENCODER_ABI_VERSION >> 8) >= 1 pic.use_argb = !!lossless; // lossless fast track if (pic.use_argb) { pic.colorspace = static_cast<WebPEncCSP>(pic.colorspace | WEBP_CSP_ALPHA_BIT); if (WebPPictureAlloc(&pic)) { ok = 1; const int width = pic.width; const int height = pic.height; for (int y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.getRow(y); for (int x = 0; x < width; ++x) { const unsigned rgba = row[x]; unsigned a = (rgba >> 24) & 0xff; unsigned r = rgba & 0xff; unsigned g = (rgba >> 8 ) & 0xff; unsigned b = (rgba >> 16) & 0xff; const uint32_t argb = (a << 24) | (r << 16) | (g << 8) | (b); pic.argb[x + y * pic.argb_stride] = argb; } } }
GDALDataset * WEBPDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, int bStrict, char ** papszOptions, GDALProgressFunc pfnProgress, void * pProgressData ) { int nBands = poSrcDS->GetRasterCount(); int nXSize = poSrcDS->GetRasterXSize(); int nYSize = poSrcDS->GetRasterYSize(); /* -------------------------------------------------------------------- */ /* WEBP library initialization */ /* -------------------------------------------------------------------- */ WebPPicture sPicture; if (!WebPPictureInit(&sPicture)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureInit() failed"); return NULL; } /* -------------------------------------------------------------------- */ /* Some some rudimentary checks */ /* -------------------------------------------------------------------- */ if( nXSize > 16383 || nYSize > 16383 ) { CPLError( CE_Failure, CPLE_NotSupported, "WEBP maximum image dimensions are 16383 x 16383."); return NULL; } if( nBands != 3 ) { CPLError( CE_Failure, CPLE_NotSupported, "WEBP driver doesn't support %d bands. Must be 3 (RGB) bands.", nBands ); return NULL; } GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); if( eDT != GDT_Byte ) { CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, "WEBP driver doesn't support data type %s. " "Only eight bit byte bands supported.", GDALGetDataTypeName( poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); if (bStrict) return NULL; } /* -------------------------------------------------------------------- */ /* What options has the user selected? */ /* -------------------------------------------------------------------- */ float fQuality = 75.0f; const char* pszQUALITY = CSLFetchNameValue(papszOptions, "QUALITY"); if( pszQUALITY != NULL ) { fQuality = (float) atof(pszQUALITY); if( fQuality < 0.0f || fQuality > 100.0f ) { CPLError( CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.", "QUALITY", pszQUALITY); return NULL; } } WebPPreset nPreset = WEBP_PRESET_DEFAULT; const char* pszPRESET = CSLFetchNameValueDef(papszOptions, "PRESET", "DEFAULT"); if (EQUAL(pszPRESET, "DEFAULT")) nPreset = WEBP_PRESET_DEFAULT; else if (EQUAL(pszPRESET, "PICTURE")) nPreset = WEBP_PRESET_PICTURE; else if (EQUAL(pszPRESET, "PHOTO")) nPreset = WEBP_PRESET_PHOTO; else if (EQUAL(pszPRESET, "PICTURE")) nPreset = WEBP_PRESET_PICTURE; else if (EQUAL(pszPRESET, "DRAWING")) nPreset = WEBP_PRESET_DRAWING; else if (EQUAL(pszPRESET, "ICON")) nPreset = WEBP_PRESET_ICON; else if (EQUAL(pszPRESET, "TEXT")) nPreset = WEBP_PRESET_TEXT; else { CPLError( CE_Failure, CPLE_IllegalArg, "%s=%s is not a legal value.", "PRESET", pszPRESET ); return NULL; } WebPConfig sConfig; if (!WebPConfigInitInternal(&sConfig, nPreset, fQuality, WEBP_ENCODER_ABI_VERSION)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPConfigInit() failed"); return NULL; } #define FETCH_AND_SET_OPTION_INT(name, fieldname, minval, maxval) \ { \ const char* pszVal = CSLFetchNameValue(papszOptions, name); \ if (pszVal != NULL) \ { \ sConfig.fieldname = atoi(pszVal); \ if (sConfig.fieldname < minval || sConfig.fieldname > maxval) \ { \ CPLError( CE_Failure, CPLE_IllegalArg, \ "%s=%s is not a legal value.", name, pszVal ); \ return NULL; \ } \ } \ } FETCH_AND_SET_OPTION_INT("TARGETSIZE", target_size, 0, INT_MAX); const char* pszPSNR = CSLFetchNameValue(papszOptions, "PSNR"); if (pszPSNR) { sConfig.target_PSNR = atof(pszPSNR); if (sConfig.target_PSNR < 0) { CPLError( CE_Failure, CPLE_IllegalArg, "PSNR=%s is not a legal value.", pszPSNR ); return NULL; } } FETCH_AND_SET_OPTION_INT("METHOD", method, 0, 6); FETCH_AND_SET_OPTION_INT("SEGMENTS", segments, 1, 4); FETCH_AND_SET_OPTION_INT("SNS_STRENGTH", sns_strength, 0, 100); FETCH_AND_SET_OPTION_INT("FILTER_STRENGTH", filter_strength, 0, 100); FETCH_AND_SET_OPTION_INT("FILTER_SHARPNESS", filter_sharpness, 0, 7); FETCH_AND_SET_OPTION_INT("FILTER_TYPE", filter_type, 0, 1); FETCH_AND_SET_OPTION_INT("AUTOFILTER", autofilter, 0, 1); FETCH_AND_SET_OPTION_INT("PASS", pass, 1, 10); FETCH_AND_SET_OPTION_INT("PREPROCESSING", preprocessing, 0, 1); FETCH_AND_SET_OPTION_INT("PARTITIONS", partitions, 0, 3); if (!WebPValidateConfig(&sConfig)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPValidateConfig() failed"); return NULL; } /* -------------------------------------------------------------------- */ /* Allocate memory */ /* -------------------------------------------------------------------- */ GByte *pabyBuffer; pabyBuffer = (GByte *) VSIMalloc( 3 * nXSize * nYSize ); if (pabyBuffer == NULL) { return NULL; } /* -------------------------------------------------------------------- */ /* Create the dataset. */ /* -------------------------------------------------------------------- */ VSILFILE *fpImage; fpImage = VSIFOpenL( pszFilename, "wb" ); if( fpImage == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Unable to create WEBP file %s.\n", pszFilename ); VSIFree(pabyBuffer); return NULL; } /* -------------------------------------------------------------------- */ /* WEBP library settings */ /* -------------------------------------------------------------------- */ sPicture.colorspace = 0; sPicture.width = nXSize; sPicture.height = nYSize; sPicture.writer = WEBPDatasetWriter; sPicture.custom_ptr = fpImage; if (!WebPPictureAlloc(&sPicture)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureAlloc() failed"); VSIFree(pabyBuffer); VSIFCloseL( fpImage ); return NULL; } /* -------------------------------------------------------------------- */ /* Acquire source imagery. */ /* -------------------------------------------------------------------- */ CPLErr eErr = CE_None; eErr = poSrcDS->RasterIO( GF_Read, 0, 0, nXSize, nYSize, pabyBuffer, nXSize, nYSize, GDT_Byte, 3, NULL, 3, 3 * nXSize, 1 ); /* -------------------------------------------------------------------- */ /* Import and write to file */ /* -------------------------------------------------------------------- */ if (eErr == CE_None && !WebPPictureImportRGB(&sPicture, pabyBuffer, 3 * nXSize)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGB() failed"); eErr = CE_Failure; } if (eErr == CE_None && !WebPEncode(&sConfig, &sPicture)) { CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed"); eErr = CE_Failure; } /* -------------------------------------------------------------------- */ /* Cleanup and close. */ /* -------------------------------------------------------------------- */ CPLFree( pabyBuffer ); WebPPictureFree(&sPicture); VSIFCloseL( fpImage ); if( eErr != CE_None ) { VSIUnlink( pszFilename ); return NULL; } /* -------------------------------------------------------------------- */ /* Re-open dataset, and copy any auxilary pam information. */ /* -------------------------------------------------------------------- */ GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); /* If outputing to stdout, we can't reopen it, so we'll return */ /* a fake dataset to make the caller happy */ CPLPushErrorHandler(CPLQuietErrorHandler); WEBPDataset *poDS = (WEBPDataset*) WEBPDataset::Open( &oOpenInfo ); CPLPopErrorHandler(); if( poDS ) { poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); return poDS; } return NULL; }
WebPAnimEncoder* WebPAnimEncoderNewInternal( int width, int height, const WebPAnimEncoderOptions* enc_options, int abi_version) { WebPAnimEncoder* enc; if (WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_MUX_ABI_VERSION)) { return NULL; } if (width <= 0 || height <= 0 || (width * (uint64_t)height) >= MAX_IMAGE_AREA) { return NULL; } enc = (WebPAnimEncoder*)WebPSafeCalloc(1, sizeof(*enc)); if (enc == NULL) return NULL; // sanity inits, so we can call WebPAnimEncoderDelete(): enc->encoded_frames_ = NULL; enc->mux_ = NULL; // Dimensions and options. *(int*)&enc->canvas_width_ = width; *(int*)&enc->canvas_height_ = height; if (enc_options != NULL) { *(WebPAnimEncoderOptions*)&enc->options_ = *enc_options; SanitizeEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); } else { DefaultEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); } // Canvas buffers. if (!WebPPictureInit(&enc->curr_canvas_copy_) || !WebPPictureInit(&enc->prev_canvas_) || !WebPPictureInit(&enc->prev_canvas_disposed_)) { return NULL; } enc->curr_canvas_copy_.width = width; enc->curr_canvas_copy_.height = height; enc->curr_canvas_copy_.use_argb = 1; if (!WebPPictureAlloc(&enc->curr_canvas_copy_) || !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_) || !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_disposed_)) { goto Err; } WebPUtilClearPic(&enc->prev_canvas_, NULL); enc->curr_canvas_copy_modified_ = 1; // Encoded frames. ResetCounters(enc); // Note: one extra storage is for the previous frame. enc->size_ = enc->options_.kmax - enc->options_.kmin + 1; // We need space for at least 2 frames. But when kmin, kmax are both zero, // enc->size_ will be 1. So we handle that special case below. if (enc->size_ < 2) enc->size_ = 2; enc->encoded_frames_ = (EncodedFrame*)WebPSafeCalloc(enc->size_, sizeof(*enc->encoded_frames_)); if (enc->encoded_frames_ == NULL) goto Err; enc->mux_ = WebPMuxNew(); if (enc->mux_ == NULL) goto Err; enc->count_since_key_frame_ = 0; enc->first_timestamp_ = 0; enc->prev_timestamp_ = 0; enc->prev_candidate_undecided_ = 0; enc->is_first_frame_ = 1; enc->got_null_frame_ = 0; return enc; // All OK. Err: WebPAnimEncoderDelete(enc); return NULL; }