diff --git a/examples/vwebp.c b/examples/vwebp.c index 99c6cd21..6da5b9c0 100644 --- a/examples/vwebp.c +++ b/examples/vwebp.c @@ -488,10 +488,6 @@ int main(int argc, char *argv[]) { goto Error; } - if (WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & FRAGMENTS_FLAG) { - fprintf(stderr, "Image fragments are not supported for now!\n"); - goto Error; - } kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH); kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT); if (kParams.print_info) { diff --git a/examples/webpmux.c b/examples/webpmux.c index 91e21fe7..21f47752 100644 --- a/examples/webpmux.c +++ b/examples/webpmux.c @@ -28,7 +28,6 @@ webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp Extract relevant data from WebP container file: - webpmux -get frgm n in.webp -o out_fragment.webp webpmux -get frame n in.webp -o out_frame.webp webpmux -get icc in.webp -o image_profile.icc webpmux -get exif in.webp -o image_metadata.exif @@ -89,17 +88,16 @@ typedef enum { FEATURE_XMP, FEATURE_ICCP, FEATURE_ANMF, - FEATURE_FRGM, LAST_FEATURE } FeatureType; static const char* const kFourccList[LAST_FEATURE] = { - NULL, "EXIF", "XMP ", "ICCP", "ANMF", "FRGM" + NULL, "EXIF", "XMP ", "ICCP", "ANMF" }; static const char* const kDescriptions[LAST_FEATURE] = { NULL, "EXIF metadata", "XMP metadata", "ICC profile", - "Animation frame", "Image fragment" + "Animation frame" }; typedef struct { @@ -183,7 +181,6 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { 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) { @@ -194,26 +191,22 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { // 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"; + if (flag & ANIMATION_FLAG) { + const WebPChunkId id = WEBP_CHUNK_ANMF; + const char* const type_str = "frame"; int nFrames; - if (is_anim) { - WebPMuxAnimParams params; - err = WebPMuxGetAnimationParams(mux, ¶ms); - assert(err == WEBP_MUX_OK); - printf("Background color : 0x%.8X Loop Count : %d\n", - params.bgcolor, params.loop_count); - } + WebPMuxAnimParams params; + err = WebPMuxGetAnimationParams(mux, ¶ms); + 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); @@ -222,7 +215,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { if (nFrames > 0) { int i; printf("No.: width height alpha x_offset y_offset "); - if (is_anim) printf("duration dispose blend "); + printf("duration dispose blend "); printf("image_size compression\n"); for (i = 1; i <= nFrames; i++) { WebPMuxFrameInfo frame; @@ -236,7 +229,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { 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"; @@ -276,7 +269,7 @@ static WebPMuxError DisplayInfo(const WebPMux* mux) { printf("Size of the XMP metadata: %d\n", (int)xmp.size); } - if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | FRAGMENTS_FLAG))) { + if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) { WebPMuxFrameInfo image; err = WebPMuxGetFrame(mux, 1, &image); if (err == WEBP_MUX_OK) { @@ -453,13 +446,6 @@ static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) { return 1; } -static int ParseFragmentArgs(const char* args, WebPMuxFrameInfo* const info) { - const int ok = - (sscanf(args, "+%d+%d", &info->x_offset, &info->y_offset) == 2); - if (ok) WarnAboutOddOffset(info); - return ok; -} - static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) { uint32_t a, r, g, b; if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0; @@ -488,7 +474,6 @@ static void DeleteConfig(WebPMuxConfig* config) { static int ValidateCommandLine(int argc, const char* argv[], int* num_feature_args) { int num_frame_args; - int num_frgm_args; int num_loop_args; int num_bgcolor_args; int ok = 1; @@ -515,7 +500,6 @@ static int ValidateCommandLine(int argc, const char* argv[], // Compound checks. num_frame_args = CountOccurrences(argv, argc, "-frame"); - num_frgm_args = CountOccurrences(argv, argc, "-frgm"); num_loop_args = CountOccurrences(argv, argc, "-loop"); num_bgcolor_args = CountOccurrences(argv, argc, "-bgcolor"); @@ -530,22 +514,14 @@ static int ValidateCommandLine(int argc, const char* argv[], ERROR_GOTO1("ERROR: Loop count and background color are relevant only in " "case of animation.\n", ErrValidate); } - if (num_frame_args > 0 && num_frgm_args > 0) { - ERROR_GOTO1("ERROR: Only one of frames & fragments can be specified at a " - "time.\n", ErrValidate); - } assert(ok == 1); - if (num_frame_args == 0 && num_frgm_args == 0) { + if (num_frame_args == 0) { // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action). *num_feature_args = 1; } else { - // Multiple arguments ('set' action for animation or fragmented image). - if (num_frame_args > 0) { - *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args; - } else { - *num_feature_args = num_frgm_args; - } + // Multiple arguments ('set' action for animation) + *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args; } ErrValidate: @@ -698,8 +674,7 @@ static int ParseCommandLine(int argc, const char* argv[], } else if (!strcmp(argv[i], "frame") && (config->action_type_ == ACTION_GET)) { CHECK_NUM_ARGS_LESS(2, ErrParse); - feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_ANMF : - FEATURE_FRGM; + feature->type_ = FEATURE_ANMF; arg->params_ = argv[i + 1]; ++feature_arg_index; i += 2; @@ -737,8 +712,7 @@ static int ValidateConfig(WebPMuxConfig* config) { if (config->input_ == NULL) { if (config->action_type_ != ACTION_SET) { ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); - } else if (feature->type_ != FEATURE_ANMF && - feature->type_ != FEATURE_FRGM) { + } else if (feature->type_ != FEATURE_ANMF) { ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); } } @@ -790,14 +764,13 @@ static int InitializeConfig(int argc, const char* argv[], //------------------------------------------------------------------------------ // Processing. -static int GetFrameFragment(const WebPMux* mux, - const WebPMuxConfig* config, int is_frame) { +static int GetFrame(const WebPMux* mux, const WebPMuxConfig* config) { WebPMuxError err = WEBP_MUX_OK; WebPMux* mux_single = NULL; int num = 0; int ok = 1; int parse_error = 0; - const WebPChunkId id = is_frame ? WEBP_CHUNK_ANMF : WEBP_CHUNK_FRGM; + const WebPChunkId id = WEBP_CHUNK_ANMF; WebPMuxFrameInfo info; WebPDataInit(&info.bitstream); @@ -848,9 +821,7 @@ static int Process(const WebPMuxConfig* config) { if (!ok) goto Err2; switch (feature->type_) { case FEATURE_ANMF: - case FEATURE_FRGM: - ok = GetFrameFragment(mux, config, - (feature->type_ == FEATURE_ANMF) ? 1 : 0); + ok = GetFrame(mux, config); break; case FEATURE_ICCP: @@ -942,35 +913,6 @@ static int Process(const WebPMuxConfig* config) { break; } - case FEATURE_FRGM: { - int i; - mux = WebPMuxNew(); - if (mux == NULL) { - ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n", - ErrorString(WEBP_MUX_MEMORY_ERROR), Err2); - } - for (i = 0; i < feature->arg_count_; ++i) { - WebPMuxFrameInfo frgm; - frgm.id = WEBP_CHUNK_FRGM; - ok = ReadFileToWebPData(feature->args_[i].filename_, - &frgm.bitstream); - if (!ok) goto Err2; - ok = ParseFragmentArgs(feature->args_[i].params_, &frgm); - if (!ok) { - WebPDataClear(&frgm.bitstream); - ERROR_GOTO1("ERROR: Could not parse fragment properties.\n", - Err2); - } - err = WebPMuxPushFrame(mux, &frgm, 1); - WebPDataClear(&frgm.bitstream); - if (err != WEBP_MUX_OK) { - ERROR_GOTO3("ERROR (%s): Could not add a fragment at index %d.\n", - ErrorString(err), i, Err2); - } - } - break; - } - case FEATURE_ICCP: case FEATURE_EXIF: case FEATURE_XMP: { diff --git a/src/dec/webp.c b/src/dec/webp.c index d0b912f0..a546ab4b 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -39,8 +39,8 @@ // 20..23 VP8X flags bit-map corresponding to the chunk-types present. // 24..26 Width of the Canvas Image. // 27..29 Height of the Canvas Image. -// There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8, -// VP8L, XMP, EXIF ...) +// There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L, +// XMP, EXIF ...) // All sizes are in little-endian order. // Note: chunk data size must be padded to multiple of 2 when written. @@ -289,7 +289,6 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, int found_riff = 0; int found_vp8x = 0; int animation_present = 0; - int fragments_present = 0; const int have_all_data = (headers != NULL) ? headers->have_all_data : 0; VP8StatusCode status; @@ -318,7 +317,6 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, return status; // Wrong VP8X / insufficient data. } animation_present = !!(flags & ANIMATION_FLAG); - fragments_present = !!(flags & FRAGMENTS_FLAG); if (!found_riff && found_vp8x) { // Note: This restriction may be removed in the future, if it becomes // necessary to send VP8X chunk to the decoder. @@ -330,8 +328,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, image_width = canvas_width; image_height = canvas_height; - if (found_vp8x && (animation_present || fragments_present) && - headers == NULL) { + if (found_vp8x && animation_present && headers == NULL) { status = VP8_STATUS_OK; goto ReturnWidthHeight; // Just return features from VP8X header. } @@ -362,7 +359,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, return VP8_STATUS_BITSTREAM_ERROR; } - if (format != NULL && !(animation_present || fragments_present)) { + if (format != NULL && !animation_present) { *format = hdrs.is_lossless ? 2 : 1; } diff --git a/src/demux/demux.c b/src/demux/demux.c index 0d2989f6..67152542 100644 --- a/src/demux/demux.c +++ b/src/demux/demux.c @@ -590,7 +590,6 @@ static int CheckFrameBounds(const Frame* const frame, int exact, static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); - const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); const Frame* f = dmux->frames_; if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; @@ -598,7 +597,7 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; if (dmux->loop_count_ < 0) return 0; if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0; - if (is_fragmented) return 0; + if (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream while (f != NULL) { const int cur_frame_set = f->frame_num_; diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c index 9bbed42b..d2c53053 100644 --- a/src/mux/muxedit.c +++ b/src/mux/muxedit.c @@ -93,34 +93,32 @@ static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth, } #undef SWITCH_ID_LIST -// Create data for frame/fragment given image data, offsets and duration. -static WebPMuxError CreateFrameFragmentData( - int width, int height, const WebPMuxFrameInfo* const info, int is_frame, - WebPData* const frame_frgm) { - uint8_t* frame_frgm_bytes; - const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size; +// Create data for frame given image data, offsets and duration. +static WebPMuxError CreateFrameData( + int width, int height, const WebPMuxFrameInfo* const info, + WebPData* const frame) { + uint8_t* frame_bytes; + const size_t frame_size = kChunks[IDX_ANMF].size; assert(width > 0 && height > 0 && info->duration >= 0); assert(info->dispose_method == (info->dispose_method & 1)); // Note: assertion on upper bounds is done in PutLE24(). - frame_frgm_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_frgm_size); - if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; + frame_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_size); + if (frame_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; - PutLE24(frame_frgm_bytes + 0, info->x_offset / 2); - PutLE24(frame_frgm_bytes + 3, info->y_offset / 2); + PutLE24(frame_bytes + 0, info->x_offset / 2); + PutLE24(frame_bytes + 3, info->y_offset / 2); - if (is_frame) { - PutLE24(frame_frgm_bytes + 6, width - 1); - PutLE24(frame_frgm_bytes + 9, height - 1); - PutLE24(frame_frgm_bytes + 12, info->duration); - frame_frgm_bytes[15] = - (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) | - (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0); - } + PutLE24(frame_bytes + 6, width - 1); + PutLE24(frame_bytes + 9, height - 1); + PutLE24(frame_bytes + 12, info->duration); + frame_bytes[15] = + (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) | + (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0); - frame_frgm->bytes = frame_frgm_bytes; - frame_frgm->size = frame_frgm_size; + frame->bytes = frame_bytes; + frame->size = frame_size; return WEBP_MUX_OK; } @@ -264,23 +262,16 @@ WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream, return err; } -WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, +WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info, int copy_data) { WebPMuxImage wpi; WebPMuxError err; - int is_frame; - const WebPData* const bitstream = &frame->bitstream; + const WebPData* const bitstream = &info->bitstream; // Sanity checks. - if (mux == NULL || frame == NULL) return WEBP_MUX_INVALID_ARGUMENT; + if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT; - is_frame = (frame->id == WEBP_CHUNK_ANMF); - if (!(is_frame || (frame->id == WEBP_CHUNK_FRGM))) { - return WEBP_MUX_INVALID_ARGUMENT; - } - if (frame->id == WEBP_CHUNK_FRGM) { // Dead experiment. - return WEBP_MUX_INVALID_ARGUMENT; - } + if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT; if (bitstream->bytes == NULL || bitstream->size > MAX_CHUNK_PAYLOAD) { return WEBP_MUX_INVALID_ARGUMENT; @@ -290,7 +281,7 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, const WebPMuxImage* const image = mux->images_; const uint32_t image_id = (image->header_ != NULL) ? ChunkGetIdFromTag(image->header_->tag_) : WEBP_CHUNK_IMAGE; - if (image_id != frame->id) { + if (image_id != info->id) { return WEBP_MUX_INVALID_ARGUMENT; // Conflicting frame types. } } @@ -301,16 +292,11 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful. { - WebPData frame_frgm; - const uint32_t tag = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].tag; - WebPMuxFrameInfo tmp = *frame; + WebPData frame; + const uint32_t tag = kChunks[IDX_ANMF].tag; + WebPMuxFrameInfo tmp = *info; tmp.x_offset &= ~1; // Snap offsets to even. tmp.y_offset &= ~1; - if (!is_frame) { // Reset unused values. - tmp.duration = 1; - tmp.dispose_method = WEBP_MUX_DISPOSE_NONE; - tmp.blend_method = WEBP_MUX_BLEND; - } if (tmp.x_offset < 0 || tmp.x_offset >= MAX_POSITION_OFFSET || tmp.y_offset < 0 || tmp.y_offset >= MAX_POSITION_OFFSET || (tmp.duration < 0 || tmp.duration >= MAX_DURATION) || @@ -318,12 +304,11 @@ WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* frame, err = WEBP_MUX_INVALID_ARGUMENT; goto Err; } - err = CreateFrameFragmentData(wpi.width_, wpi.height_, &tmp, is_frame, - &frame_frgm); + err = CreateFrameData(wpi.width_, wpi.height_, &tmp, &frame); if (err != WEBP_MUX_OK) goto Err; - // Add frame/fragment chunk (with copy_data = 1). - err = AddDataToChunkList(&frame_frgm, 1, tag, &wpi.header_); - WebPDataClear(&frame_frgm); // frame_frgm owned by wpi.header_ now. + // Add frame chunk (with copy_data = 1). + err = AddDataToChunkList(&frame, 1, tag, &wpi.header_); + WebPDataClear(&frame); // frame owned by wpi.header_ now. if (err != WEBP_MUX_OK) goto Err; } @@ -402,21 +387,18 @@ WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) { //------------------------------------------------------------------------------ // Assembly of the WebP RIFF file. -static WebPMuxError GetFrameFragmentInfo( - const WebPChunk* const frame_frgm_chunk, +static WebPMuxError GetFrameInfo( + const WebPChunk* const frame_chunk, int* const x_offset, int* const y_offset, int* const duration) { - const uint32_t tag = frame_frgm_chunk->tag_; - const int is_frame = (tag == kChunks[IDX_ANMF].tag); - const WebPData* const data = &frame_frgm_chunk->data_; - const size_t expected_data_size = - is_frame ? ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE; - assert(frame_frgm_chunk != NULL); - assert(tag == kChunks[IDX_ANMF].tag || tag == kChunks[IDX_FRGM].tag); + const WebPData* const data = &frame_chunk->data_; + const size_t expected_data_size = ANMF_CHUNK_SIZE; + assert(frame_chunk->tag_ == kChunks[IDX_ANMF].tag); + assert(frame_chunk != NULL); if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; *x_offset = 2 * GetLE24(data->bytes + 0); *y_offset = 2 * GetLE24(data->bytes + 3); - if (is_frame) *duration = GetLE24(data->bytes + 12); + *duration = GetLE24(data->bytes + 12); return WEBP_MUX_OK; } @@ -424,13 +406,13 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, int* const x_offset, int* const y_offset, int* const duration, int* const width, int* const height) { - const WebPChunk* const frame_frgm_chunk = wpi->header_; + const WebPChunk* const frame_chunk = wpi->header_; WebPMuxError err; assert(wpi != NULL); - assert(frame_frgm_chunk != NULL); + assert(frame_chunk != NULL); - // Get offsets and duration from ANMF/FRGM chunk. - err = GetFrameFragmentInfo(frame_frgm_chunk, x_offset, y_offset, duration); + // Get offsets and duration from ANMF chunk. + err = GetFrameInfo(frame_chunk, x_offset, y_offset, duration); if (err != WEBP_MUX_OK) return err; // Get width and height from VP8/VP8L chunk. @@ -441,7 +423,6 @@ static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, // Returns the tightest dimension for the canvas considering the image list. static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, - uint32_t flags, int* const width, int* const height) { WebPMuxImage* wpi = NULL; assert(mux != NULL); @@ -452,12 +433,10 @@ static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, assert(wpi->img_ != NULL); if (wpi->next_ != NULL) { - int max_x = 0; - int max_y = 0; - int64_t image_area = 0; + int max_x = 0, max_y = 0; // if we have a chain of wpi's, header_ is necessarily set assert(wpi->header_ != NULL); - // Aggregate the bounding box for animation frames & fragmented images. + // Aggregate the bounding box for animation frames. for (; wpi != NULL; wpi = wpi->next_) { int x_offset = 0, y_offset = 0, duration = 0, w = 0, h = 0; const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset, @@ -470,19 +449,9 @@ static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux, if (max_x_pos > max_x) max_x = max_x_pos; if (max_y_pos > max_y) max_y = max_y_pos; - image_area += w * h; } *width = max_x; *height = max_y; - // Crude check to validate that there are no image overlaps/holes for - // fragmented images. Check that the aggregated image area for individual - // fragments exactly matches the image area of the constructed canvas. - // However, the area-match is necessary but not sufficient condition. - if ((flags & FRAGMENTS_FLAG) && (image_area != (max_x * max_y))) { - *width = 0; - *height = 0; - return WEBP_MUX_INVALID_ARGUMENT; - } } else { // For a single image, canvas dimensions are same as image dimensions. *width = wpi->width_; @@ -528,10 +497,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { flags |= XMP_FLAG; } if (images->header_ != NULL) { - if (images->header_->tag_ == kChunks[IDX_FRGM].tag) { - // This is a fragmented image. - flags |= FRAGMENTS_FLAG; - } else if (images->header_->tag_ == kChunks[IDX_ANMF].tag) { + if (images->header_->tag_ == kChunks[IDX_ANMF].tag) { // This is an image with animation. flags |= ANIMATION_FLAG; } @@ -540,7 +506,7 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { flags |= ALPHA_FLAG; // Some images have an alpha channel. } - err = GetAdjustedCanvasSize(mux, flags, &width, &height); + err = GetAdjustedCanvasSize(mux, &width, &height); if (err != WEBP_MUX_OK) return err; if (width <= 0 || height <= 0) { @@ -580,31 +546,26 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { // 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 a single fragment or frame, and its rectangle - // covers the whole canvas, convert it to a non-animated non-fragmented image - // (to avoid writing FRGM/ANMF chunk unnecessarily). + // 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; - 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. - assert(frame_frag != NULL); - if (frame_frag->header_ != NULL && + 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_frag->width_ == mux->canvas_width_ && - frame_frag->height_ == mux->canvas_height_))) { - 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; + (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; - num_fragments = 0; } } // Remove ANIM chunk if this is a non-animated image. diff --git a/src/mux/muxi.h b/src/mux/muxi.h index d4d5cbad..06c290dd 100644 --- a/src/mux/muxi.h +++ b/src/mux/muxi.h @@ -36,16 +36,16 @@ struct WebPChunk { uint32_t tag_; int owner_; // True if *data_ memory is owned internally. // VP8X, ANIM, and other internally created chunks - // like ANMF/FRGM are always owned. + // like ANMF are always owned. WebPData data_; WebPChunk* next_; }; -// MuxImage object. Store a full WebP image (including ANMF/FRGM chunk, ALPH +// MuxImage object. Store a full WebP image (including ANMF chunk, ALPH // chunk and VP8/VP8L chunk), typedef struct WebPMuxImage WebPMuxImage; struct WebPMuxImage { - WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF/WEBP_CHUNK_FRGM. + WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF. WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA. WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE. WebPChunk* unknown_; // Corresponds to WEBP_CHUNK_UNKNOWN. @@ -79,7 +79,6 @@ typedef enum { IDX_ICCP, IDX_ANIM, IDX_ANMF, - IDX_FRGM, IDX_ALPHA, IDX_VP8, IDX_VP8L, @@ -185,7 +184,6 @@ int MuxImageFinalize(WebPMuxImage* const wpi); static WEBP_INLINE int IsWPI(WebPChunkId id) { switch (id) { case WEBP_CHUNK_ANMF: - case WEBP_CHUNK_FRGM: case WEBP_CHUNK_ALPHA: case WEBP_CHUNK_IMAGE: return 1; default: return 0; diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c index 4babbe82..a1ad59e5 100644 --- a/src/mux/muxinternal.c +++ b/src/mux/muxinternal.c @@ -23,7 +23,6 @@ const ChunkInfo kChunks[] = { { MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('A', 'N', 'I', 'M'), WEBP_CHUNK_ANIM, ANIM_CHUNK_SIZE }, { MKFOURCC('A', 'N', 'M', 'F'), WEBP_CHUNK_ANMF, ANMF_CHUNK_SIZE }, - { MKFOURCC('F', 'R', 'G', 'M'), WEBP_CHUNK_FRGM, FRGM_CHUNK_SIZE }, { MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, { MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE }, @@ -251,8 +250,7 @@ static WebPChunk** GetChunkListFromId(const WebPMuxImage* const wpi, WebPChunkId id) { assert(wpi != NULL); switch (id) { - case WEBP_CHUNK_ANMF: - case WEBP_CHUNK_FRGM: return (WebPChunk**)&wpi->header_; + case WEBP_CHUNK_ANMF: return (WebPChunk**)&wpi->header_; case WEBP_CHUNK_ALPHA: return (WebPChunk**)&wpi->alpha_; case WEBP_CHUNK_IMAGE: return (WebPChunk**)&wpi->img_; default: return NULL; @@ -372,13 +370,12 @@ size_t MuxImageDiskSize(const WebPMuxImage* const wpi) { return size; } -// Special case as ANMF/FRGM chunk encapsulates other image chunks. +// Special case as ANMF chunk encapsulates other image chunks. static uint8_t* ChunkEmitSpecial(const WebPChunk* const header, size_t total_size, uint8_t* dst) { const size_t header_size = header->data_.size; const size_t offset_to_next = total_size - CHUNK_HEADER_SIZE; - assert(header->tag_ == kChunks[IDX_ANMF].tag || - header->tag_ == kChunks[IDX_FRGM].tag); + assert(header->tag_ == kChunks[IDX_ANMF].tag); PutLE32(dst + 0, header->tag_); PutLE32(dst + TAG_SIZE, (uint32_t)offset_to_next); assert(header_size == (uint32_t)header_size); @@ -391,7 +388,7 @@ static uint8_t* ChunkEmitSpecial(const WebPChunk* const header, uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) { // Ordering of chunks to be emitted is strictly as follows: - // 1. ANMF/FRGM chunk (if present). + // 1. ANMF chunk (if present). // 2. ALPH chunk (if present). // 3. VP8/VP8L chunk. assert(wpi); @@ -465,7 +462,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { int num_xmp; int num_anim; int num_frames; - int num_fragments; int num_vp8x; int num_images; int num_alpha; @@ -510,10 +506,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { } } - // 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); @@ -537,11 +529,6 @@ WebPMuxError MuxValidate(const WebPMux* const mux) { 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; } diff --git a/src/mux/muxread.c b/src/mux/muxread.c index 8957a1e4..410acd91 100644 --- a/src/mux/muxread.c +++ b/src/mux/muxread.c @@ -104,17 +104,15 @@ static int MuxImageParse(const WebPChunk* const chunk, int copy_data, size_t subchunk_size; ChunkInit(&subchunk); - assert(chunk->tag_ == kChunks[IDX_ANMF].tag || - chunk->tag_ == kChunks[IDX_FRGM].tag); + assert(chunk->tag_ == kChunks[IDX_ANMF].tag); assert(!wpi->is_partial_); - // ANMF/FRGM. + // ANMF. { - const size_t hdr_size = (chunk->tag_ == kChunks[IDX_ANMF].tag) ? - ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE; + const size_t hdr_size = ANMF_CHUNK_SIZE; const WebPData temp = { bytes, hdr_size }; - // Each of ANMF and FRGM chunk contain a header at the beginning. So, its - // size should at least be 'hdr_size'. + // Each of ANMF chunk contain a header at the beginning. So, its size should + // be at least 'hdr_size'. if (size < hdr_size) goto Fail; ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_); } @@ -292,16 +290,15 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, static WebPMuxError ValidateForSingleImage(const WebPMux* const mux) { const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE); const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_ANMF); - const int num_fragments = MuxImageCount(mux->images_, WEBP_CHUNK_FRGM); if (num_images == 0) { // No images in mux. return WEBP_MUX_NOT_FOUND; - } else if (num_images == 1 && num_frames == 0 && num_fragments == 0) { + } else if (num_images == 1 && num_frames == 0) { // Valid case (single image). return WEBP_MUX_OK; } else { - // Frame/Fragment case OR an invalid mux. + // Frame case OR an invalid mux. return WEBP_MUX_INVALID_ARGUMENT; } } @@ -379,7 +376,7 @@ static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi, const int need_vp8x = (wpi->alpha_ != NULL); const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0; const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0; - // Note: No need to output ANMF/FRGM chunk for a single image. + // Note: No need to output ANMF chunk for a single image. const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size + ChunkDiskSize(wpi->img_); uint8_t* const data = (uint8_t*)WebPSafeMalloc(1ULL, size); @@ -436,29 +433,24 @@ static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi, return SynthesizeBitstream(wpi, &info->bitstream); } -static WebPMuxError MuxGetFrameFragmentInternal(const WebPMuxImage* const wpi, - WebPMuxFrameInfo* const frame) { +static WebPMuxError MuxGetFrameInternal(const WebPMuxImage* const wpi, + WebPMuxFrameInfo* const frame) { const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag); - const CHUNK_INDEX idx = is_frame ? IDX_ANMF : IDX_FRGM; - const WebPData* frame_frgm_data; + const WebPData* frame_data; if (!is_frame) return WEBP_MUX_INVALID_ARGUMENT; assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame(). - // Get frame/fragment chunk. - frame_frgm_data = &wpi->header_->data_; - if (frame_frgm_data->size < kChunks[idx].size) return WEBP_MUX_BAD_DATA; + // Get frame chunk. + frame_data = &wpi->header_->data_; + if (frame_data->size < kChunks[IDX_ANMF].size) return WEBP_MUX_BAD_DATA; // Extract info. - frame->x_offset = 2 * GetLE24(frame_frgm_data->bytes + 0); - frame->y_offset = 2 * GetLE24(frame_frgm_data->bytes + 3); - if (is_frame) { - const uint8_t bits = frame_frgm_data->bytes[15]; - frame->duration = GetLE24(frame_frgm_data->bytes + 12); + frame->x_offset = 2 * GetLE24(frame_data->bytes + 0); + frame->y_offset = 2 * GetLE24(frame_data->bytes + 3); + { + const uint8_t bits = frame_data->bytes[15]; + frame->duration = GetLE24(frame_data->bytes + 12); frame->dispose_method = (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE; frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND; - } else { // Defaults for unused values. - frame->duration = 1; - frame->dispose_method = WEBP_MUX_DISPOSE_NONE; - frame->blend_method = WEBP_MUX_BLEND; } frame->id = ChunkGetIdFromTag(wpi->header_->tag_); return SynthesizeBitstream(wpi, &frame->bitstream); @@ -482,7 +474,7 @@ WebPMuxError WebPMuxGetFrame( if (wpi->header_ == NULL) { return MuxGetImageInternal(wpi, frame); } else { - return MuxGetFrameFragmentInternal(wpi, frame); + return MuxGetFrameInternal(wpi, frame); } } diff --git a/src/webp/format_constants.h b/src/webp/format_constants.h index b6e78a64..329fc8a3 100644 --- a/src/webp/format_constants.h +++ b/src/webp/format_constants.h @@ -72,14 +72,13 @@ typedef enum { #define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP"). #define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk. #define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk. -#define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk. #define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk. #define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height. #define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height. #define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count #define MAX_DURATION (1 << 24) // maximum duration -#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/fragment x/y offset +#define MAX_POSITION_OFFSET (1 << 24) // maximum frame x/y offset // Maximum chunk payload is such that adding the header and padding won't // overflow a uint32_t. diff --git a/src/webp/mux.h b/src/webp/mux.h index b72658c7..de46ee97 100644 --- a/src/webp/mux.h +++ b/src/webp/mux.h @@ -21,13 +21,13 @@ extern "C" { #endif -#define WEBP_MUX_ABI_VERSION 0x0106 // MAJOR(8b) + MINOR(8b) +#define WEBP_MUX_ABI_VERSION 0x0107 // MAJOR(8b) + MINOR(8b) //------------------------------------------------------------------------------ // Mux API // // This API allows manipulation of WebP container images containing features -// like color profile, metadata, animation and fragmented images. +// like color profile, metadata, animation. // // Code Example#1: Create a WebPMux object with image data, color profile and // XMP metadata. @@ -81,16 +81,16 @@ typedef enum WebPMuxError { // IDs for different types of chunks. typedef enum WebPChunkId { - WEBP_CHUNK_VP8X, // VP8X - WEBP_CHUNK_ICCP, // ICCP - WEBP_CHUNK_ANIM, // ANIM - WEBP_CHUNK_ANMF, // ANMF - WEBP_CHUNK_FRGM, // FRGM - WEBP_CHUNK_ALPHA, // ALPH - WEBP_CHUNK_IMAGE, // VP8/VP8L - WEBP_CHUNK_EXIF, // EXIF - WEBP_CHUNK_XMP, // XMP - WEBP_CHUNK_UNKNOWN, // Other chunks. + WEBP_CHUNK_VP8X, // VP8X + WEBP_CHUNK_ICCP, // ICCP + WEBP_CHUNK_ANIM, // ANIM + WEBP_CHUNK_ANMF, // ANMF + WEBP_CHUNK_DEPRECATED, // (deprecated from FRGM) + WEBP_CHUNK_ALPHA, // ALPH + WEBP_CHUNK_IMAGE, // VP8/VP8L + WEBP_CHUNK_EXIF, // EXIF + WEBP_CHUNK_XMP, // XMP + WEBP_CHUNK_UNKNOWN, // Other chunks. WEBP_CHUNK_NIL } WebPChunkId; @@ -142,7 +142,7 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, // Non-image chunks. // Note: Only non-image related chunks should be managed through chunk APIs. -// (Image related chunks are: "ANMF", "FRGM", "VP8 ", "VP8L" and "ALPH"). +// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH"). // To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(), // WebPMuxGetFrame() and WebPMuxDeleteFrame(). @@ -195,7 +195,7 @@ WEBP_EXTERN(WebPMuxError) WebPMuxDeleteChunk( //------------------------------------------------------------------------------ // Images. -// Encapsulates data about a single frame/fragment. +// Encapsulates data about a single frame. struct WebPMuxFrameInfo { WebPData bitstream; // image data: can be a raw VP8/VP8L bitstream // or a single-image WebP file. @@ -203,19 +203,19 @@ struct WebPMuxFrameInfo { int y_offset; // y-offset of the frame. int duration; // duration of the frame (in milliseconds). - WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF, - // WEBP_CHUNK_FRGM or WEBP_CHUNK_IMAGE + WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF + // or WEBP_CHUNK_IMAGE WebPMuxAnimDispose dispose_method; // Disposal method for the frame. WebPMuxAnimBlend blend_method; // Blend operation for the frame. uint32_t pad[1]; // padding for later use }; -// Sets the (non-animated and non-fragmented) image in the mux object. -// Note: Any existing images (including frames/fragments) will be removed. +// Sets the (non-animated) image in the mux object. +// Note: Any existing images (including frames) will be removed. // Parameters: // mux - (in/out) object in which the image is to be set // bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image -// WebP file (non-animated and non-fragmented) +// WebP file (non-animated) // copy_data - (in) value 1 indicates given data WILL be copied to the mux // object and value 0 indicates data will NOT be copied. // Returns: @@ -226,9 +226,8 @@ WEBP_EXTERN(WebPMuxError) WebPMuxSetImage( WebPMux* mux, const WebPData* bitstream, int copy_data); // Adds a frame at the end of the mux object. -// Notes: (1) frame.id should be one of WEBP_CHUNK_ANMF or WEBP_CHUNK_FRGM -// (2) For setting a non-animated non-fragmented image, use -// WebPMuxSetImage() instead. +// Notes: (1) frame.id should be WEBP_CHUNK_ANMF +// (2) For setting a non-animated image, use WebPMuxSetImage() instead. // (3) Type of frame being pushed must be same as the frames in mux. // (4) As WebP only supports even offsets, any odd offset will be snapped // to an even location using: offset &= ~1 diff --git a/src/webp/mux_types.h b/src/webp/mux_types.h index c94043a3..b37e2c67 100644 --- a/src/webp/mux_types.h +++ b/src/webp/mux_types.h @@ -31,12 +31,13 @@ typedef struct WebPData WebPData; // VP8X Feature Flags. typedef enum WebPFeatureFlags { - FRAGMENTS_FLAG = 0x00000001, ANIMATION_FLAG = 0x00000002, XMP_FLAG = 0x00000004, EXIF_FLAG = 0x00000008, ALPHA_FLAG = 0x00000010, - ICCP_FLAG = 0x00000020 + ICCP_FLAG = 0x00000020, + + ALL_VALID_FLAGS = 0x0000003e } WebPFeatureFlags; // Dispose method (animation only). Indicates how the area used by the current