mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 17:33:25 +00:00
276 lines
7.2 KiB
C
276 lines
7.2 KiB
C
![]() |
/* GIMP - The GNU Image Manipulation Program
|
||
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
|
*
|
||
|
* file-webp - WebP file format plug-in for the GIMP
|
||
|
* Copyright (C) 2015 Nathan Osman
|
||
|
* Copyright (C) 2016 Ben Touchette
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdint.h>
|
||
|
|
||
|
#include <gegl.h>
|
||
|
|
||
|
#include <libgimp/gimp.h>
|
||
|
#include <libgimp/gimpui.h>
|
||
|
|
||
|
#include <webp/decode.h>
|
||
|
#include <webp/demux.h>
|
||
|
#include <webp/mux.h>
|
||
|
|
||
|
#include "file-webp-load.h"
|
||
|
|
||
|
#include "libgimp/stdplugins-intl.h"
|
||
|
|
||
|
|
||
|
static void
|
||
|
create_layer (gint32 image_ID,
|
||
|
uint8_t *layer_data,
|
||
|
gint32 position,
|
||
|
gchar *name,
|
||
|
gint width,
|
||
|
gint height,
|
||
|
gint32 offsetx,
|
||
|
gint32 offsety)
|
||
|
{
|
||
|
gint32 layer_ID;
|
||
|
GeglBuffer *geglbuffer;
|
||
|
GeglRectangle extent;
|
||
|
|
||
|
layer_ID = gimp_layer_new (image_ID, name,
|
||
|
width, height,
|
||
|
GIMP_RGBA_IMAGE,
|
||
|
100,
|
||
|
GIMP_NORMAL_MODE);
|
||
|
|
||
|
gimp_image_insert_layer (image_ID, layer_ID, -1, position);
|
||
|
|
||
|
if (offsetx > 0 || offsety > 0)
|
||
|
{
|
||
|
gimp_layer_set_offsets (layer_ID, offsetx, offsety);
|
||
|
}
|
||
|
|
||
|
/* Retrieve the buffer for the layer */
|
||
|
geglbuffer = gimp_drawable_get_buffer (layer_ID);
|
||
|
|
||
|
/* Copy the image data to the region */
|
||
|
gegl_rectangle_set (&extent, 0, 0, width, height);
|
||
|
gegl_buffer_set (geglbuffer, &extent, 0, NULL, layer_data,
|
||
|
GEGL_AUTO_ROWSTRIDE);
|
||
|
|
||
|
/* Flush the drawable and detach */
|
||
|
gegl_buffer_flush (geglbuffer);
|
||
|
|
||
|
if (geglbuffer)
|
||
|
g_object_unref (geglbuffer);
|
||
|
}
|
||
|
|
||
|
gint32
|
||
|
load_image (const gchar *filename,
|
||
|
gboolean interactive,
|
||
|
GError **error)
|
||
|
{
|
||
|
uint8_t *indata = NULL;
|
||
|
uint8_t *outdata;
|
||
|
gsize indatalen;
|
||
|
gint width;
|
||
|
gint height;
|
||
|
gint32 image_ID;
|
||
|
GFile *file;
|
||
|
GimpMetadata *metadata;
|
||
|
WebPMux *mux;
|
||
|
WebPBitstreamFeatures features;
|
||
|
WebPData wp_data;
|
||
|
uint32_t flag;
|
||
|
int animation = FALSE;
|
||
|
int icc = FALSE;
|
||
|
int exif = FALSE;
|
||
|
int xmp = FALSE;
|
||
|
int frames = 0;
|
||
|
gchar *name;
|
||
|
|
||
|
/* Attempt to read the file contents from disk */
|
||
|
if (! g_file_get_contents (filename,
|
||
|
(gchar **) &indata,
|
||
|
&indatalen,
|
||
|
error))
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
g_printerr ("Loading WebP file %s\n", filename);
|
||
|
|
||
|
/* Validate WebP data */
|
||
|
if (! WebPGetInfo (indata, indatalen, &width, &height))
|
||
|
{
|
||
|
g_printerr ("Invalid WebP file\n");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
gegl_init (NULL, NULL);
|
||
|
|
||
|
wp_data.bytes = indata;
|
||
|
wp_data.size = indatalen;
|
||
|
|
||
|
mux = WebPMuxCreate (&wp_data, 1);
|
||
|
if (! mux)
|
||
|
return -1;
|
||
|
|
||
|
WebPMuxGetFeatures (mux, &flag);
|
||
|
if (flag == 0)
|
||
|
{
|
||
|
animation = FALSE;
|
||
|
icc = FALSE;
|
||
|
exif = FALSE;
|
||
|
xmp = FALSE;
|
||
|
frames = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (flag & ANIMATION_FLAG)
|
||
|
animation = TRUE;
|
||
|
|
||
|
if (flag & ICCP_FLAG)
|
||
|
icc = TRUE;
|
||
|
|
||
|
if (flag & EXIF_FLAG)
|
||
|
exif = TRUE;
|
||
|
|
||
|
if (flag & XMP_FLAG)
|
||
|
xmp = TRUE;
|
||
|
}
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
if (! animation)
|
||
|
{
|
||
|
/* Attempt to decode the data as a WebP image */
|
||
|
outdata = WebPDecodeRGBA (indata, indatalen, &width, &height);
|
||
|
|
||
|
/* Free the original compressed data */
|
||
|
g_free (indata);
|
||
|
|
||
|
/* Check to ensure the image data was loaded correctly */
|
||
|
if (! outdata)
|
||
|
return -1;
|
||
|
|
||
|
create_layer (image_ID, outdata, 0, _("Background"),
|
||
|
width, height, 0, 0);
|
||
|
|
||
|
/* Free the image data */
|
||
|
free (outdata);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const WebPChunkId id = WEBP_CHUNK_ANMF;
|
||
|
WebPMuxAnimParams params;
|
||
|
gint loop;
|
||
|
|
||
|
WebPMuxGetAnimationParams (mux, ¶ms);
|
||
|
WebPMuxNumChunks (mux, id, &frames);
|
||
|
|
||
|
/* Attempt to decode the data as a WebP animation image */
|
||
|
for (loop = 0; loop < frames; loop++)
|
||
|
{
|
||
|
WebPMuxFrameInfo thisframe;
|
||
|
gint i = loop;
|
||
|
|
||
|
if (WebPMuxGetFrame (mux, i, &thisframe) == WEBP_MUX_OK)
|
||
|
{
|
||
|
WebPGetFeatures (thisframe.bitstream.bytes,
|
||
|
thisframe.bitstream.size, &features);
|
||
|
|
||
|
outdata = WebPDecodeRGBA (thisframe.bitstream.bytes,
|
||
|
thisframe.bitstream.size,
|
||
|
&width, &height);
|
||
|
|
||
|
if (! outdata)
|
||
|
return -1;
|
||
|
|
||
|
name = g_strdup_printf (_("Frame %d"), loop + 1);
|
||
|
create_layer (image_ID, outdata, 0,
|
||
|
name, width, height,
|
||
|
thisframe.x_offset,
|
||
|
thisframe.y_offset);
|
||
|
g_free (name);
|
||
|
|
||
|
/* Free the image data */
|
||
|
free (outdata);
|
||
|
}
|
||
|
|
||
|
WebPDataClear (&thisframe.bitstream);
|
||
|
}
|
||
|
|
||
|
WebPDataClear (&wp_data);
|
||
|
}
|
||
|
|
||
|
if (icc)
|
||
|
{
|
||
|
WebPData icc_profile;
|
||
|
GimpColorProfile *profile;
|
||
|
|
||
|
WebPMuxGetChunk (mux, "ICCP", &icc_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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (exif || xmp)
|
||
|
{
|
||
|
if (exif)
|
||
|
{
|
||
|
WebPData exif;
|
||
|
|
||
|
WebPMuxGetChunk (mux, "EXIF", &exif);
|
||
|
}
|
||
|
|
||
|
if (xmp)
|
||
|
{
|
||
|
WebPData xmp;
|
||
|
|
||
|
WebPMuxGetChunk (mux, "XMP ", &xmp);
|
||
|
}
|
||
|
|
||
|
file = g_file_new_for_path (filename);
|
||
|
metadata = gimp_image_metadata_load_prepare (image_ID, "image/webp",
|
||
|
file, NULL);
|
||
|
if (metadata)
|
||
|
{
|
||
|
gimp_image_metadata_load_finish (image_ID, "image/webp",
|
||
|
metadata, GIMP_METADATA_LOAD_ALL,
|
||
|
interactive);
|
||
|
g_object_unref (metadata);
|
||
|
}
|
||
|
|
||
|
g_object_unref (file);
|
||
|
}
|
||
|
|
||
|
gimp_image_set_filename (image_ID, filename);
|
||
|
|
||
|
return image_ID;
|
||
|
}
|