static void * DLL_CALLCONV Open(FreeImageIO *io, fi_handle handle, BOOL read) { WebPMux *mux = NULL; int copy_data = 1; // 1 : copy data into the mux, 0 : keep a link to local data if(read) { // create the MUX object from the input stream WebPData bitstream; // read the input file and put it in memory if(!ReadFileToWebPData(io, handle, &bitstream)) { return NULL; } // create the MUX object mux = WebPMuxCreate(&bitstream, copy_data); // no longer needed since copy_data == 1 free((void*)bitstream.bytes); if(mux == NULL) { FreeImage_OutputMessageProc(s_format_id, "Failed to create mux object from file"); return NULL; } } else { // creates an empty mux object mux = WebPMuxNew(); if(mux == NULL) { FreeImage_OutputMessageProc(s_format_id, "Failed to create empty mux object"); return NULL; } } return mux; }
static int CreateMux(const char* const filename, WebPMux** mux) { WebPData bitstream; assert(mux != NULL); if (!ReadFileToWebPData(filename, &bitstream)) return 0; *mux = WebPMuxCreate(&bitstream, 1); free((void*)bitstream.bytes); if (*mux != NULL) return 1; fprintf(stderr, "Failed to create mux object from file %s.\n", filename); return 0; }
uint8_t* webpDelXMP(const uint8_t* data, size_t data_size, size_t* new_data_size) { WebPData image = {data, data_size}; WebPData output_data = {NULL, 0}; WebPMux* mux = WebPMuxCreate(&image, 0); if(WebPMuxDeleteChunk(mux, "XMP ") == WEBP_MUX_OK) { WebPMuxAssemble(mux, &output_data); } WebPMuxDelete(mux); *new_data_size = output_data.size; return (uint8_t*)(output_data.bytes); }
uint8_t* webpSetXMP( const uint8_t* data, size_t data_size, const char* metadata, size_t metadata_size, size_t* new_data_size ) { WebPData image = {data, data_size}; WebPData profile = {metadata, metadata_size}; WebPData output_data = {NULL, 0}; WebPMux* mux = WebPMuxCreate(&image, 0); if(WebPMuxSetChunk(mux, "XMP ", &profile, 0) == WEBP_MUX_OK) { WebPMuxAssemble(mux, &output_data); } WebPMuxDelete(mux); *new_data_size = output_data.size; return (uint8_t*)(output_data.bytes); }
// Outputs image data given a bitstream. The bitstream can either be a // single-image WebP file or raw VP8/VP8L data. // Also outputs 'is_lossless' to be true if the given bitstream is lossless. static WebPMuxError GetImageData(const WebPData* const bitstream, WebPData* const image, WebPData* const alpha, int* const is_lossless) { WebPDataInit(alpha); // Default: no alpha. if (bitstream->size < TAG_SIZE || memcmp(bitstream->bytes, "RIFF", TAG_SIZE)) { // It is NOT webp file data. Return input data as is. *image = *bitstream; } else { // It is webp file data. Extract image data from it. const WebPMuxImage* wpi; WebPMux* const mux = WebPMuxCreate(bitstream, 0); if (mux == NULL) return WEBP_MUX_BAD_DATA; wpi = mux->images_; assert(wpi != NULL && wpi->img_ != NULL); *image = wpi->img_->data_; if (wpi->alpha_ != NULL) { *alpha = wpi->alpha_->data_; } WebPMuxDelete(mux); } *is_lossless = VP8LCheckSignature(image->bytes, image->size); return WEBP_MUX_OK; }
static int read_header( Read *read, VipsImage *out ) { vips_image_init_fields( out, read->width, read->height, read->config.input.has_alpha ? 4 : 3, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); #ifdef HAVE_LIBWEBPMUX { WebPData bitstream; WebPMux *mux; int i; /* We have to parse the whole file again to get the metadata out. * * Don't make parse failure an error. We don't want to refuse to read * any pixels because of some malformed metadata. */ bitstream.bytes = read->data; bitstream.size = read->length; if( !(mux = WebPMuxCreate( &bitstream, 0 )) ) { vips_warn( "webp", "%s", _( "unable to read image metadata" ) ); return( 0 ); } for( i = 0; i < vips__n_webp_names; i++ ) { const char *vips = vips__webp_names[i].vips; const char *webp = vips__webp_names[i].webp; WebPData data; if( WebPMuxGetChunk( mux, webp, &data ) == WEBP_MUX_OK ) { void *blob; if( !(blob = vips_malloc( NULL, data.size )) ) { WebPMuxDelete( mux ); return( -1 ); } memcpy( blob, data.bytes, data.size ); vips_image_set_blob( out, vips, (VipsCallbackFn) vips_free, blob, data.size ); } } WebPMuxDelete( mux ); /* We may have read some exif ... parse into the header. */ if( vips__exif_parse( out ) ) return( -1 ); } #endif /*HAVE_LIBWEBPMUX*/ return( 0 ); }
PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) { PyBytesObject *webp_string; uint8_t *webp; Py_ssize_t size; PyObject *ret, *bytes, *pymode, *icc_profile = Py_None, *exif = Py_None; WebPDecoderConfig config; VP8StatusCode vp8_status_code = VP8_STATUS_OK; char* mode = "RGB"; if (!PyArg_ParseTuple(args, "S", &webp_string)) { Py_RETURN_NONE; } if (!WebPInitDecoderConfig(&config)) { Py_RETURN_NONE; } PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); vp8_status_code = WebPGetFeatures(webp, size, &config.input); if (vp8_status_code == VP8_STATUS_OK) { // If we don't set it, we don't get alpha. // Initialized to MODE_RGB if (config.input.has_alpha) { config.output.colorspace = MODE_RGBA; mode = "RGBA"; } #ifndef HAVE_WEBPMUX vp8_status_code = WebPDecode(webp, size, &config); #else { int copy_data = 0; WebPData data = { webp, size }; WebPMuxFrameInfo image; WebPData icc_profile_data = {0}; WebPData exif_data = {0}; WebPMux* mux = WebPMuxCreate(&data, copy_data); WebPMuxGetFrame(mux, 1, &image); webp = (uint8_t*)image.bitstream.bytes; size = image.bitstream.size; vp8_status_code = WebPDecode(webp, size, &config); WebPMuxGetChunk(mux, "ICCP", &icc_profile_data); if (icc_profile_data.size > 0) { icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size); } WebPMuxGetChunk(mux, "EXIF", &exif_data); if (exif_data.size > 0) { exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size); } WebPMuxDelete(mux); } #endif } if (vp8_status_code != VP8_STATUS_OK) { WebPFreeDecBuffer(&config.output); Py_RETURN_NONE; } if (config.output.colorspace < MODE_YUV) { bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba, config.output.u.RGBA.size); } else { // Skipping YUV for now. Need Test Images. // UNDONE -- unclear if we'll ever get here if we set mode_rgb* bytes = PyBytes_FromStringAndSize((char *)config.output.u.YUVA.y, config.output.u.YUVA.y_size); } #if PY_VERSION_HEX >= 0x03000000 pymode = PyUnicode_FromString(mode); #else pymode = PyString_FromString(mode); #endif ret = Py_BuildValue("SiiSSS", bytes, config.output.width, config.output.height, pymode, icc_profile, exif); WebPFreeDecBuffer(&config.output); return ret; }
gboolean load_image(const gchar *filename, gint32 *image_ID, GError **error) { gboolean status = FALSE; gchar *indata = NULL; gsize indatalen; gint width; gint height; WebPMux *mux = NULL; WebPData wp_data; uint32_t flags; uint8_t *outdata = NULL; #ifdef GIMP_2_9 /* Initialize GEGL */ gegl_init(NULL, NULL); #endif do { /* Attempt to read the file contents from disk */ if (g_file_get_contents(filename, &indata, &indatalen, error) == FALSE) { break; } /* Validate WebP data, grabbing the width and height */ if (!WebPGetInfo(indata, indatalen, &width, &height)) { break; } /* Create a WebPMux from the contents of the file */ wp_data.bytes = (uint8_t*)indata; wp_data.size = indatalen; mux = WebPMuxCreate(&wp_data, 1); if (mux == NULL) { break; } /* Retrieve the features present */ if (WebPMuxGetFeatures(mux, &flags) != WEBP_MUX_OK) { break; } /* TODO: decode the image in "chunks" or "tiles" */ /* TODO: check if an alpha channel is present */ /* Create the new image and associated layer */ *image_ID = gimp_image_new(width, height, GIMP_RGB); #ifdef WEBP_0_5 if (flags & ANIMATION_FLAG) { int frames, i; /* Retrieve the number of frames */ WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &frames); /* Loop over each of the frames */ for (i = 0; i < frames; ++i) { WebPMuxFrameInfo frame = {0}; /* Retrieve the data for the frame */ if (WebPMuxGetFrame(mux, i, &frame) != WEBP_MUX_OK) { goto error; } /* Decode the frame */ outdata = WebPDecodeRGBA(frame.bitstream.bytes, frame.bitstream.size, &width, &height); /* Free the compressed data */ WebPDataClear(&frame.bitstream); if (!outdata) { goto error; } /* Create a layer for the frame */ char name[255]; snprintf(name, 255, "Frame %d", (i + 1)); if (create_layer(*image_ID, outdata, 0, (gchar*)name, width, height, frame.x_offset, frame.y_offset) == FALSE) { goto error; } } /* If all is well, jump *over* the error label - otherwise leave the loop and begin cleaning things up */ goto success; error: break; success: ; } else { #endif /* Attempt to decode the data as a WebP image */ outdata = WebPDecodeRGBA(indata, indatalen, &width, &height); if (!outdata) { break; } /* Create a single layer */ status = create_layer(*image_ID, outdata, 0, "Background", width, height, 0, 0); #ifdef WEBP_0_5 } #ifdef GIMP_2_9 /* Load a color profile if one was provided */ if (flags & ICCP_FLAG) { WebPData icc_profile; GimpColorProfile *profile; /* Load the ICC profile from the file */ WebPMuxGetChunk(mux, "ICCP", &icc_profile); /* Have Gimp load the color profile */ profile = gimp_color_profile_new_from_icc_profile( icc_profile.bytes, icc_profile.size, NULL); if (profile) { gimp_image_set_color_profile(image_ID, profile); g_object_unref(profile); } } #endif #endif /* Set the filename for the image */ gimp_image_set_filename(*image_ID, filename); } while(0); /* Delete the mux object */ if (mux) { WebPMuxDelete(mux); } /* Free the data read from disk */ if (indata) { g_free(indata); } return status; }
PyObject* _anim_encoder_assemble(PyObject* self, PyObject* args) { uint8_t* icc_bytes; uint8_t* exif_bytes; uint8_t* xmp_bytes; Py_ssize_t icc_size; Py_ssize_t exif_size; Py_ssize_t xmp_size; WebPData webp_data; WebPAnimEncoderObject* encp = (WebPAnimEncoderObject*)self; WebPAnimEncoder* enc = encp->enc; WebPMux* mux = NULL; PyObject* ret = NULL; if (!PyArg_ParseTuple(args, "s#s#s#", &icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) { return NULL; } // Init the output buffer WebPDataInit(&webp_data); // Assemble everything into the output buffer if (!WebPAnimEncoderAssemble(enc, &webp_data)) { PyErr_SetString(PyExc_RuntimeError, WebPAnimEncoderGetError(enc)); return NULL; } // Re-mux to add metadata as needed if (icc_size > 0 || exif_size > 0 || xmp_size > 0) { WebPMuxError err = WEBP_MUX_OK; int i_icc_size = (int)icc_size; int i_exif_size = (int)exif_size; int i_xmp_size = (int)xmp_size; WebPData icc_profile = { icc_bytes, i_icc_size }; WebPData exif = { exif_bytes, i_exif_size }; WebPData xmp = { xmp_bytes, i_xmp_size }; mux = WebPMuxCreate(&webp_data, 1); if (mux == NULL) { PyErr_SetString(PyExc_RuntimeError, "could not re-mux to add metadata"); return NULL; } WebPDataClear(&webp_data); // Add ICCP chunk if (i_icc_size > 0) { err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, 1); if (err != WEBP_MUX_OK) { return HandleMuxError(err, "ICCP"); } } // Add EXIF chunk if (i_exif_size > 0) { err = WebPMuxSetChunk(mux, "EXIF", &exif, 1); if (err != WEBP_MUX_OK) { return HandleMuxError(err, "EXIF"); } } // Add XMP chunk if (i_xmp_size > 0) { err = WebPMuxSetChunk(mux, "XMP ", &xmp, 1); if (err != WEBP_MUX_OK) { return HandleMuxError(err, "XMP"); } } err = WebPMuxAssemble(mux, &webp_data); if (err != WEBP_MUX_OK) { return HandleMuxError(err, NULL); } } // Convert to Python bytes ret = PyBytes_FromStringAndSize((char*)webp_data.bytes, webp_data.size); WebPDataClear(&webp_data); // If we had to re-mux, we should free it now that we're done with it if (mux != NULL) { WebPMuxDelete(mux); } return ret; }
/* Save an animation to disk */ gboolean save_animation(gint32 nLayers, gint32 *allLayers, FILE *outfile, WebPSaveParams *params, GError **error) { gboolean status = FALSE; gboolean innerStatus = TRUE; WebPAnimEncoderOptions enc_options; WebPAnimEncoder *enc = NULL; int frame_timestamp = 0; WebPData webp_data = {0}; WebPMux *mux; WebPMuxAnimParams anim_params = {0}; /* Prepare for encoding an animation */ WebPAnimEncoderOptionsInit(&enc_options); do { int i; gint32 drawable_ID = allLayers[0]; /* Create the encoder */ enc = WebPAnimEncoderNew(gimp_drawable_width(drawable_ID), gimp_drawable_height(drawable_ID), &enc_options); /* Encode each layer */ for (i = 0; i < nLayers; i++) { if ((innerStatus = save_layer(allLayers[i], NULL, NULL, TRUE, enc, frame_timestamp, params, error)) == FALSE) { break; } } /* Check to make sure each layer was encoded correctly */ if (innerStatus == FALSE) { break; } /* Add NULL frame */ WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL); /* Initialize the WebP image structure */ WebPDataInit(&webp_data); /* Write the animation to the image */ if (!WebPAnimEncoderAssemble(enc, &webp_data)) { g_set_error(error, G_FILE_ERROR, 0, "Encoding error: '%s'", WebPAnimEncoderGetError(enc)); break; } /* Create a Mux */ mux = WebPMuxCreate(&webp_data, 1); /* Set animation parameters */ anim_params.loop_count = params->loop == TRUE ? 0 : 1; WebPMuxSetAnimationParams(mux, &anim_params); /* Assemble the image */ WebPMuxAssemble(mux, &webp_data); /* Write to disk */ if (fwrite(webp_data.bytes, webp_data.size, 1, outfile) != 1) { break; } /* Everything succeeded */ status = TRUE; } while(0); /* Free image data */ WebPDataClear(&webp_data); /* Free the animation encoder */ if (enc) { WebPAnimEncoderDelete(enc); } return status; }