Esempio n. 1
0
// Cleans up 'mux' by removing any unnecessary chunks.
static WebPMuxError MuxCleanup(WebPMux* const mux) {
  int num_frames;
  int num_anim_chunks;

  // If we have an image with a single frame, and its rectangle
  // covers the whole canvas, convert it to a non-animated image
  // (to avoid writing ANMF chunk unnecessarily).
  WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames);
  if (err != WEBP_MUX_OK) return err;
  if (num_frames == 1) {
    WebPMuxImage* frame = NULL;
    err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame);
    assert(err == WEBP_MUX_OK);  // We know that one frame does exist.
    assert(frame != NULL);
    if (frame->header_ != NULL &&
        ((mux->canvas_width_ == 0 && mux->canvas_height_ == 0) ||
         (frame->width_ == mux->canvas_width_ &&
          frame->height_ == mux->canvas_height_))) {
      assert(frame->header_->tag_ == kChunks[IDX_ANMF].tag);
      ChunkDelete(frame->header_);  // Removes ANMF chunk.
      frame->header_ = NULL;
      num_frames = 0;
    }
  }
  // Remove ANIM chunk if this is a non-animated image.
  err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks);
  if (err != WEBP_MUX_OK) return err;
  if (num_anim_chunks >= 1 && num_frames == 0) {
    err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
    if (err != WEBP_MUX_OK) return err;
  }
  return WEBP_MUX_OK;
}
Esempio n. 2
0
// Cleans up 'mux' by removing any unnecessary chunks.
static WebPMuxError MuxCleanup(WebPMux* const mux) {
  int num_frames;
  int num_fragments;
  int num_anim_chunks;

  // If we have an image with single fragment or frame, convert it to a
  // non-animated non-fragmented image (to avoid writing FRGM/ANMF chunk
  // unnecessarily).
  WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames);
  if (err != WEBP_MUX_OK) return err;
  err = WebPMuxNumChunks(mux, kChunks[IDX_FRGM].id, &num_fragments);
  if (err != WEBP_MUX_OK) return err;
  if (num_frames == 1 || num_fragments == 1) {
    WebPMuxImage* frame_frag;
    err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame_frag);
    assert(err == WEBP_MUX_OK);  // We know that one frame/fragment does exist.
    if (frame_frag->header_ != NULL) {
      assert(frame_frag->header_->tag_ == kChunks[IDX_ANMF].tag ||
             frame_frag->header_->tag_ == kChunks[IDX_FRGM].tag);
      ChunkDelete(frame_frag->header_);  // Removes ANMF/FRGM chunk.
      frame_frag->header_ = NULL;
    }
    num_frames = 0;
    num_fragments = 0;
  }
  // Remove ANIM chunk if this is a non-animated image.
  err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks);
  if (err != WEBP_MUX_OK) return err;
  if (num_anim_chunks >= 1 && num_frames == 0) {
    err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
    if (err != WEBP_MUX_OK) return err;
  }
  return WEBP_MUX_OK;
}
// Test basic constraints:
// retrieval, maximum number of chunks by index (use -1 to skip)
// and feature incompatibility (use NO_FLAG to skip).
// On success returns WEBP_MUX_OK and stores the chunk count in *num.
static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
                                  WebPFeatureFlags feature,
                                  uint32_t vp8x_flags,
                                  int max, int* num) {
  const WebPMuxError err =
      WebPMuxNumChunks(mux, kChunks[idx].id, num);
  if (err != WEBP_MUX_OK) return err;
  if (max > -1 && *num > max) return WEBP_MUX_INVALID_ARGUMENT;
  if (feature != NO_FLAG && IsNotCompatible(vp8x_flags & feature, *num)) {
    return WEBP_MUX_INVALID_ARGUMENT;
  }
  return WEBP_MUX_OK;
}
Esempio n. 4
0
static WebPMuxError DisplayInfo(const WebPMux* mux) {
  int width, height;
  uint32_t flag;

  WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
  assert(err == WEBP_MUX_OK);  // As WebPMuxCreate() was successful earlier.
  printf("Canvas size: %d x %d\n", width, height);

  err = WebPMuxGetFeatures(mux, &flag);
  if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
  RETURN_IF_ERROR("Failed to retrieve features\n");

  if (flag == 0) {
    fprintf(stderr, "No features present.\n");
    return err;
  }

  // Print the features present.
  printf("Features present:");
  if (flag & ANIMATION_FLAG) printf(" animation");
  if (flag & FRAGMENTS_FLAG) printf(" image fragments");
  if (flag & ICCP_FLAG)      printf(" ICC profile");
  if (flag & EXIF_FLAG)      printf(" EXIF metadata");
  if (flag & XMP_FLAG)       printf(" XMP metadata");
  if (flag & ALPHA_FLAG)     printf(" transparency");
  printf("\n");

  if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
    const int is_anim = !!(flag & ANIMATION_FLAG);
    const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
    const char* const type_str = is_anim ? "frame" : "fragment";
    int nFrames;

    if (is_anim) {
      WebPMuxAnimParams params;
      err = WebPMuxGetAnimationParams(mux, &params);
      assert(err == WEBP_MUX_OK);
      printf("Background color : 0x%.8X  Loop Count : %d\n",
             params.bgcolor, params.loop_count);
    }

    err = WebPMuxNumChunks(mux, id, &nFrames);
    assert(err == WEBP_MUX_OK);

    printf("Number of %ss: %d\n", type_str, nFrames);
    if (nFrames > 0) {
      int i;
      printf("No.: width height alpha x_offset y_offset ");
      if (is_anim) printf("duration   dispose blend ");
      printf("image_size\n");
      for (i = 1; i <= nFrames; i++) {
        WebPMuxFrameInfo frame;
        err = WebPMuxGetFrame(mux, i, &frame);
        if (err == WEBP_MUX_OK) {
          WebPBitstreamFeatures features;
          const VP8StatusCode status = WebPGetFeatures(
              frame.bitstream.bytes, frame.bitstream.size, &features);
          assert(status == VP8_STATUS_OK);  // Checked by WebPMuxCreate().
          (void)status;
          printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
                 features.height, features.has_alpha ? "yes" : "no",
                 frame.x_offset, frame.y_offset);
          if (is_anim) {
            const char* const dispose =
                (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
                                                                : "background";
            const char* const blend =
                (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
            printf("%8d %10s %5s ", frame.duration, dispose, blend);
          }
          printf("%10d\n", (int)frame.bitstream.size);
        }
        WebPDataClear(&frame.bitstream);
        RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
      }
    }
  }

  if (flag & ICCP_FLAG) {
    WebPData icc_profile;
    err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
    assert(err == WEBP_MUX_OK);
    printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
  }

  if (flag & EXIF_FLAG) {
    WebPData exif;
    err = WebPMuxGetChunk(mux, "EXIF", &exif);
    assert(err == WEBP_MUX_OK);
    printf("Size of the EXIF metadata: %d\n", (int)exif.size);
  }

  if (flag & XMP_FLAG) {
    WebPData xmp;
    err = WebPMuxGetChunk(mux, "XMP ", &xmp);
    assert(err == WEBP_MUX_OK);
    printf("Size of the XMP metadata: %d\n", (int)xmp.size);
  }

  if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
    WebPMuxFrameInfo image;
    err = WebPMuxGetFrame(mux, 1, &image);
    if (err == WEBP_MUX_OK) {
      printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
    }
    WebPDataClear(&image.bitstream);
    RETURN_IF_ERROR("Failed to retrieve the image\n");
  }

  return WEBP_MUX_OK;
}
static WebPMuxError DisplayInfo(const WebPMux* mux) {
  uint32_t flag;

  WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
#ifndef WEBP_EXPERIMENTAL_FEATURES
  if (flag & FRAGMENTS_FLAG) err = WEBP_MUX_INVALID_ARGUMENT;
#endif
  RETURN_IF_ERROR("Failed to retrieve features\n");

  if (flag == 0) {
    fprintf(stderr, "No features present.\n");
    return err;
  }

  // Print the features present.
  printf("Features present:");
  if (flag & ANIMATION_FLAG) printf(" animation");
  if (flag & FRAGMENTS_FLAG) printf(" image fragments");
  if (flag & ICCP_FLAG)      printf(" ICC profile");
  if (flag & EXIF_FLAG)      printf(" EXIF metadata");
  if (flag & XMP_FLAG)       printf(" XMP metadata");
  if (flag & ALPHA_FLAG)     printf(" transparency");
  printf("\n");

  if ((flag & ANIMATION_FLAG) || (flag & FRAGMENTS_FLAG)) {
    const int is_anim = !!(flag & ANIMATION_FLAG);
    const WebPChunkId id = is_anim ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM;
    const char* const type_str = is_anim ? "frame" : "fragment";
    int nFrames;

    if (is_anim) {
      WebPMuxAnimParams params;
      err = WebPMuxGetAnimationParams(mux, &params);
      RETURN_IF_ERROR("Failed to retrieve animation parameters\n");
      printf("Background color : 0x%.8X  Loop Count : %d\n",
             params.bgcolor, params.loop_count);
    }

    err = WebPMuxNumChunks(mux, id, &nFrames);
    RETURN_IF_ERROR2("Failed to retrieve number of %ss\n", type_str);

    printf("Number of %ss: %d\n", type_str, nFrames);
    if (nFrames > 0) {
      int i;
      printf("No.: x_offset y_offset ");
      if (is_anim) printf("duration dispose ");
      printf("image_size\n");
      for (i = 1; i <= nFrames; i++) {
        WebPMuxFrameInfo frame;
        err = WebPMuxGetFrame(mux, i, &frame);
        if (err == WEBP_MUX_OK) {
          printf("%3d: %8d %8d ", i, frame.x_offset, frame.y_offset);
          if (is_anim) printf("%8d %7d ", frame.duration, frame.dispose_method);
          printf("%10d\n", (int)frame.bitstream.size);
        }
        WebPDataClear(&frame.bitstream);
        RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
      }
    }
  }

  if (flag & ICCP_FLAG) {
    WebPData icc_profile;
    err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
    RETURN_IF_ERROR("Failed to retrieve the ICC profile\n");
    printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
  }

  if (flag & EXIF_FLAG) {
    WebPData exif;
    err = WebPMuxGetChunk(mux, "EXIF", &exif);
    RETURN_IF_ERROR("Failed to retrieve the EXIF metadata\n");
    printf("Size of the EXIF metadata: %d\n", (int)exif.size);
  }

  if (flag & XMP_FLAG) {
    WebPData xmp;
    err = WebPMuxGetChunk(mux, "XMP ", &xmp);
    RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
    printf("Size of the XMP metadata: %d\n", (int)xmp.size);
  }

  if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) {
    WebPMuxFrameInfo image;
    err = WebPMuxGetFrame(mux, 1, &image);
    if (err == WEBP_MUX_OK) {
      printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
    }
    WebPDataClear(&image.bitstream);
    RETURN_IF_ERROR("Failed to retrieve the image\n");
  }

  return WEBP_MUX_OK;
}
WebPMuxError MuxValidate(const WebPMux* const mux) {
  int num_iccp;
  int num_exif;
  int num_xmp;
  int num_anim;
  int num_frames;
  int num_fragments;
  int num_vp8x;
  int num_images;
  int num_alpha;
  uint32_t flags;
  WebPMuxError err;

  // Verify mux is not NULL.
  if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;

  // Verify mux has at least one image.
  if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT;

  err = WebPMuxGetFeatures(mux, &flags);
  if (err != WEBP_MUX_OK) return err;

  // At most one color profile chunk.
  err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp);
  if (err != WEBP_MUX_OK) return err;

  // At most one EXIF metadata.
  err = ValidateChunk(mux, IDX_EXIF, EXIF_FLAG, flags, 1, &num_exif);
  if (err != WEBP_MUX_OK) return err;

  // At most one XMP metadata.
  err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp);
  if (err != WEBP_MUX_OK) return err;

  // Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent.
  // At most one ANIM chunk.
  err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim);
  if (err != WEBP_MUX_OK) return err;
  err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames);
  if (err != WEBP_MUX_OK) return err;

  {
    const int has_animation = !!(flags & ANIMATION_FLAG);
    if (has_animation && (num_anim == 0 || num_frames == 0)) {
      return WEBP_MUX_INVALID_ARGUMENT;
    }
    if (!has_animation && (num_anim == 1 || num_frames > 0)) {
      return WEBP_MUX_INVALID_ARGUMENT;
    }
  }

  // Fragmentation: FRAGMENTS_FLAG and FRGM chunk(s) are consistent.
  err = ValidateChunk(mux, IDX_FRGM, FRAGMENTS_FLAG, flags, -1, &num_fragments);
  if (err != WEBP_MUX_OK) return err;

  // Verify either VP8X chunk is present OR there is only one elem in
  // mux->images_.
  err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x);
  if (err != WEBP_MUX_OK) return err;
  err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images);
  if (err != WEBP_MUX_OK) return err;
  if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;

  // ALPHA_FLAG & alpha chunk(s) are consistent.
  if (MuxHasAlpha(mux->images_)) {
    if (num_vp8x > 0) {
      // VP8X chunk is present, so it should contain ALPHA_FLAG.
      if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
    } else {
      // VP8X chunk is not present, so ALPH chunks should NOT be present either.
      err = WebPMuxNumChunks(mux, WEBP_CHUNK_ALPHA, &num_alpha);
      if (err != WEBP_MUX_OK) return err;
      if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT;
    }
  } else {  // Mux doesn't need alpha. So, ALPHA_FLAG should NOT be present.
    if (flags & ALPHA_FLAG) return WEBP_MUX_INVALID_ARGUMENT;
  }

  // num_fragments & num_images are consistent.
  if (num_fragments > 0 && num_images != num_fragments) {
    return WEBP_MUX_INVALID_ARGUMENT;
  }

  return WEBP_MUX_OK;
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
  size_t size = 0;
  uint8_t* data = NULL;
  uint8_t* dst = NULL;
  int num_frames;
  int num_loop_chunks;
  WebPMuxError err;

  if (mux == NULL || assembled_data == NULL) {
    return WEBP_MUX_INVALID_ARGUMENT;
  }

  // Remove LOOP chunk if unnecessary.
  err = WebPMuxNumChunks(mux, kChunks[IDX_LOOP].id, &num_loop_chunks);
  if (err != WEBP_MUX_OK) return err;
  if (num_loop_chunks >= 1) {
    err = WebPMuxNumChunks(mux, kChunks[IDX_FRAME].id, &num_frames);
    if (err != WEBP_MUX_OK) return err;
    if (num_frames == 0) {
      err = DeleteLoopCount(mux);
      if (err != WEBP_MUX_OK) return err;
    }
  }

  // Create VP8X chunk.
  err = CreateVP8XChunk(mux);
  if (err != WEBP_MUX_OK) return err;

  // Allocate data.
  size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_)
       + ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_)
       + ChunksListDiskSize(mux->meta_) + ChunksListDiskSize(mux->unknown_)
       + RIFF_HEADER_SIZE;

  data = (uint8_t*)malloc(size);
  if (data == NULL) return WEBP_MUX_MEMORY_ERROR;

  // Emit header & chunks.
  dst = MuxEmitRiffHeader(data, size);
  dst = ChunkListEmit(mux->vp8x_, dst);
  dst = ChunkListEmit(mux->iccp_, dst);
  dst = ChunkListEmit(mux->loop_, dst);
  dst = MuxImageListEmit(mux->images_, dst);
  dst = ChunkListEmit(mux->meta_, dst);
  dst = ChunkListEmit(mux->unknown_, dst);
  assert(dst == data + size);

  // Validate mux.
  err = MuxValidate(mux);
  if (err != WEBP_MUX_OK) {
    free(data);
    data = NULL;
    size = 0;
  }

  // Finalize.
  assembled_data->bytes_ = data;
  assembled_data->size_ = size;

  return err;
}