Пример #1
0
// Create data for frame/tile given image data, offsets and duration.
static WebPMuxError CreateFrameTileData(const WebPData* const image,
                                        int x_offset, int y_offset,
                                        int duration, int is_lossless,
                                        int is_frame,
                                        WebPData* const frame_tile) {
  int width;
  int height;
  uint8_t* frame_tile_bytes;
  const size_t frame_tile_size = kChunks[is_frame ? IDX_FRAME : IDX_TILE].size;

  const int ok = is_lossless ?
      VP8LGetInfo(image->bytes_, image->size_, &width, &height, NULL) :
      VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height);
  if (!ok) return WEBP_MUX_INVALID_ARGUMENT;

  assert(width > 0 && height > 0 && duration > 0);
  // Note: assertion on upper bounds is done in PutLE24().

  frame_tile_bytes = (uint8_t*)malloc(frame_tile_size);
  if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;

  PutLE24(frame_tile_bytes + 0, x_offset / 2);
  PutLE24(frame_tile_bytes + 3, y_offset / 2);

  if (is_frame) {
    PutLE24(frame_tile_bytes + 6, width - 1);
    PutLE24(frame_tile_bytes + 9, height - 1);
    PutLE24(frame_tile_bytes + 12, duration - 1);
  }

  frame_tile->bytes_ = frame_tile_bytes;
  frame_tile->size_ = frame_tile_size;
  return WEBP_MUX_OK;
}
Пример #2
0
// Create data for frame/fragment given image data, offsets and duration.
static WebPMuxError CreateFrameFragmentData(
    const WebPData* const image, int x_offset, int y_offset, int duration,
    WebPMuxAnimDispose dispose_method, int is_lossless, int is_frame,
    WebPData* const frame_frgm) {
  int width;
  int height;
  uint8_t* frame_frgm_bytes;
  const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size;

  const int ok = is_lossless ?
      VP8LGetInfo(image->bytes, image->size, &width, &height, NULL) :
      VP8GetInfo(image->bytes, image->size, image->size, &width, &height);
  if (!ok) return WEBP_MUX_INVALID_ARGUMENT;

  assert(width > 0 && height > 0 && duration >= 0);
  assert(dispose_method == (dispose_method & 1));
  // Note: assertion on upper bounds is done in PutLE24().

  frame_frgm_bytes = (uint8_t*)malloc(frame_frgm_size);
  if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;

  PutLE24(frame_frgm_bytes + 0, x_offset / 2);
  PutLE24(frame_frgm_bytes + 3, 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, duration);
    frame_frgm_bytes[15] = (dispose_method & 1);
  }

  frame_frgm->bytes = frame_frgm_bytes;
  frame_frgm->size = frame_frgm_size;
  return WEBP_MUX_OK;
}
Пример #3
0
WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk,
                                    int* const width, int* const height) {
  const uint32_t tag = image_chunk->tag_;
  const WebPData* const data = &image_chunk->data_;
  int w, h;
  int ok;
  assert(image_chunk != NULL);
  assert(tag == kChunks[IDX_VP8].tag || tag ==  kChunks[IDX_VP8L].tag);
  ok = (tag == kChunks[IDX_VP8].tag) ?
      VP8GetInfo(data->bytes_, data->size_, data->size_, &w, &h) :
      VP8LGetInfo(data->bytes_, data->size_, &w, &h, NULL);
  if (ok) {
    *width = w;
    *height = h;
    return WEBP_MUX_OK;
  } else {
    return WEBP_MUX_BAD_DATA;
  }
}
Пример #4
0
int MuxImageFinalize(WebPMuxImage* const wpi) {
  const WebPChunk* const img = wpi->img_;
  const WebPData* const image = &img->data_;
  const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag);
  int w, h;
  int vp8l_has_alpha = 0;
  const int ok = is_lossless ?
      VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) :
      VP8GetInfo(image->bytes, image->size, image->size, &w, &h);
  assert(img != NULL);
  if (ok) {
    // Ignore ALPH chunk accompanying VP8L.
    if (is_lossless && (wpi->alpha_ != NULL)) {
      ChunkDelete(wpi->alpha_);
      wpi->alpha_ = NULL;
    }
    wpi->width_ = w;
    wpi->height_ = h;
    wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL);
  }
  return ok;
}
Пример #5
0
WebPMuxError MuxGetImageInfo(const WebPChunk* const image_chunk,
                             int* const width, int* const height,
                             int* const has_alpha) {
  const uint32_t tag = image_chunk->tag_;
  const WebPData* const data = &image_chunk->data_;
  int w, h;
  int a = 0;
  int ok;
  assert(image_chunk != NULL);
  assert(tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag);
  ok = (tag == kChunks[IDX_VP8].tag) ?
      VP8GetInfo(data->bytes, data->size, data->size, &w, &h) :
      VP8LGetInfo(data->bytes, data->size, &w, &h, &a);
  if (ok) {
    if (width != NULL) *width = w;
    if (height != NULL) *height = h;
    if (has_alpha != NULL) *has_alpha = a;
    return WEBP_MUX_OK;
  } else {
    return WEBP_MUX_BAD_DATA;
  }
}
Пример #6
0
// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
// minimal amount will be read to fetch the remaining parameters.
// If 'headers' is non-NULL this function will attempt to locate both alpha
// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
// considered valid by this function:
// RIFF + VP8(L)
// RIFF + VP8X + (optional chunks) + VP8(L)
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
// VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
                                          size_t data_size,
                                          int* const width,
                                          int* const height,
                                          int* const has_alpha,
                                          int* const has_animation,
                                          int* const format,
                                          WebPHeaderStructure* const headers) {
  int canvas_width = 0;
  int canvas_height = 0;
  int image_width = 0;
  int image_height = 0;
  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;
  WebPHeaderStructure hdrs;

  if (data == NULL || data_size < RIFF_HEADER_SIZE) {
    return VP8_STATUS_NOT_ENOUGH_DATA;
  }
  memset(&hdrs, 0, sizeof(hdrs));
  hdrs.data = data;
  hdrs.data_size = data_size;

  // Skip over RIFF header.
  status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size);
  if (status != VP8_STATUS_OK) {
    return status;   // Wrong RIFF header / insufficient data.
  }
  found_riff = (hdrs.riff_size > 0);

  // Skip over VP8X.
  {
    uint32_t flags = 0;
    status = ParseVP8X(&data, &data_size, &found_vp8x,
                       &canvas_width, &canvas_height, &flags);
    if (status != VP8_STATUS_OK) {
      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.
      return VP8_STATUS_BITSTREAM_ERROR;
    }
    if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
    if (has_animation != NULL) *has_animation = animation_present;
    if (format != NULL) *format = 0;   // default = undefined

    image_width = canvas_width;
    image_height = canvas_height;
    if (found_vp8x && (animation_present || fragments_present) &&
        headers == NULL) {
      status = VP8_STATUS_OK;
      goto ReturnWidthHeight;  // Just return features from VP8X header.
    }
  }

  if (data_size < TAG_SIZE) {
    status = VP8_STATUS_NOT_ENOUGH_DATA;
    goto ReturnWidthHeight;
  }

  // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
  if ((found_riff && found_vp8x) ||
      (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
    status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
                                 &hdrs.alpha_data, &hdrs.alpha_data_size);
    if (status != VP8_STATUS_OK) {
      goto ReturnWidthHeight;  // Invalid chunk size / insufficient data.
    }
  }

  // Skip over VP8/VP8L header.
  status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size,
                          &hdrs.compressed_size, &hdrs.is_lossless);
  if (status != VP8_STATUS_OK) {
    goto ReturnWidthHeight;  // Wrong VP8/VP8L chunk-header / insufficient data.
  }
  if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
    return VP8_STATUS_BITSTREAM_ERROR;
  }

  if (format != NULL && !(animation_present || fragments_present)) {
    *format = hdrs.is_lossless ? 2 : 1;
  }

  if (!hdrs.is_lossless) {
    if (data_size < VP8_FRAME_HEADER_SIZE) {
      status = VP8_STATUS_NOT_ENOUGH_DATA;
      goto ReturnWidthHeight;
    }
    // Validates raw VP8 data.
    if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size,
                    &image_width, &image_height)) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  } else {
    if (data_size < VP8L_FRAME_HEADER_SIZE) {
      status = VP8_STATUS_NOT_ENOUGH_DATA;
      goto ReturnWidthHeight;
    }
    // Validates raw VP8L data.
    if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  }
  // Validates image size coherency.
  if (found_vp8x) {
    if (canvas_width != image_width || canvas_height != image_height) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  }
  if (headers != NULL) {
    *headers = hdrs;
    headers->offset = data - headers->data;
    assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
    assert(headers->offset == headers->data_size - data_size);
  }
 ReturnWidthHeight:
  if (status == VP8_STATUS_OK ||
      (status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) {
    if (has_alpha != NULL) {
      // If the data did not contain a VP8X/VP8L chunk the only definitive way
      // to set this is by looking for alpha data (from an ALPH chunk).
      *has_alpha |= (hdrs.alpha_data != NULL);
    }
    if (width != NULL) *width = image_width;
    if (height != NULL) *height = image_height;
    return VP8_STATUS_OK;
  } else {
    return status;
  }
}
Пример #7
0
// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
// minimal amount will be read to fetch the remaining parameters.
// If 'headers' is non-NULL this function will attempt to locate both alpha
// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
// considered valid by this function:
// RIFF + VP8(L)
// RIFF + VP8X + (optional chunks) + VP8(L)
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
// VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
                                          size_t data_size,
                                          int* const width,
                                          int* const height,
                                          int* const has_alpha,
                                          WebPHeaderStructure* const headers) {
  int found_riff = 0;
  int found_vp8x = 0;
  VP8StatusCode status;
  WebPHeaderStructure hdrs;

  if (data == NULL || data_size < RIFF_HEADER_SIZE) {
    return VP8_STATUS_NOT_ENOUGH_DATA;
  }
  memset(&hdrs, 0, sizeof(hdrs));
  hdrs.data = data;
  hdrs.data_size = data_size;

  // Skip over RIFF header.
  status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
  if (status != VP8_STATUS_OK) {
    return status;   // Wrong RIFF header / insufficient data.
  }
  found_riff = (hdrs.riff_size > 0);

  // Skip over VP8X.
  {
    uint32_t flags = 0;
    status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
    if (status != VP8_STATUS_OK) {
      return status;  // Wrong VP8X / insufficient data.
    }
    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.
      return VP8_STATUS_BITSTREAM_ERROR;
    }
    if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG_BIT);
    if (found_vp8x && headers == NULL) {
      return VP8_STATUS_OK;  // Return features from VP8X header.
    }
  }

  if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;

  // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
  if ((found_riff && found_vp8x) ||
      (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
    status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
                                 &hdrs.alpha_data, &hdrs.alpha_data_size);
    if (status != VP8_STATUS_OK) {
      return status;  // Found an invalid chunk size / insufficient data.
    }
  }

  // Skip over VP8/VP8L header.
  status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
                          &hdrs.compressed_size, &hdrs.is_lossless);
  if (status != VP8_STATUS_OK) {
    return status;  // Wrong VP8/VP8L chunk-header / insufficient data.
  }
  if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
    return VP8_STATUS_BITSTREAM_ERROR;
  }

  if (!hdrs.is_lossless) {
    if (data_size < VP8_FRAME_HEADER_SIZE) {
      return VP8_STATUS_NOT_ENOUGH_DATA;
    }
    // Validates raw VP8 data.
    if (!VP8GetInfo(data, data_size,
                    (uint32_t)hdrs.compressed_size, width, height)) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  } else {
    if (data_size < VP8L_FRAME_HEADER_SIZE) {
      return VP8_STATUS_NOT_ENOUGH_DATA;
    }
    // Validates raw VP8L data.
    if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  }

  if (has_alpha != NULL) {
    // If the data did not contain a VP8X/VP8L chunk the only definitive way
    // to set this is by looking for alpha data (from an ALPH chunk).
    *has_alpha |= (hdrs.alpha_data != NULL);
  }
  if (headers != NULL) {
    *headers = hdrs;
    headers->offset = data - headers->data;
    assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
    assert(headers->offset == headers->data_size - data_size);
  }
  return VP8_STATUS_OK;  // Return features from VP8 header.
}
Пример #8
0
static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
                                          size_t data_size,
                                          int* const width,
                                          int* const height,
                                          int* const has_alpha,
                                          int* const has_animation,
                                          WebPHeaderStructure* const headers) {
  int canvas_width = 0;
  int canvas_height = 0;
  int image_width = 0;
  int image_height = 0;
  int found_riff = 0;
  int found_vp8x = 0;
  VP8StatusCode status;
  WebPHeaderStructure hdrs;

  if (data == NULL || data_size < RIFF_HEADER_SIZE) {
    return VP8_STATUS_NOT_ENOUGH_DATA;
  }
  memset(&hdrs, 0, sizeof(hdrs));
  hdrs.data = data;
  hdrs.data_size = data_size;

  
  status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
  if (status != VP8_STATUS_OK) {
    return status;   
  }
  found_riff = (hdrs.riff_size > 0);

  
  {
    uint32_t flags = 0;
    int animation_present;
    status = ParseVP8X(&data, &data_size, &found_vp8x,
                       &canvas_width, &canvas_height, &flags);
    if (status != VP8_STATUS_OK) {
      return status;  
    }
    animation_present = !!(flags & ANIMATION_FLAG);
    if (!found_riff && found_vp8x) {
      
      
      return VP8_STATUS_BITSTREAM_ERROR;
    }
    if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
    if (has_animation != NULL) *has_animation = animation_present;

    if (found_vp8x && animation_present && headers == NULL) {
      if (width != NULL) *width = canvas_width;
      if (height != NULL) *height = canvas_height;
      return VP8_STATUS_OK;  
    }
  }

  if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;

  
  if ((found_riff && found_vp8x) ||
      (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
    status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
                                 &hdrs.alpha_data, &hdrs.alpha_data_size);
    if (status != VP8_STATUS_OK) {
      return status;  
    }
  }

  
  status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
                          &hdrs.compressed_size, &hdrs.is_lossless);
  if (status != VP8_STATUS_OK) {
    return status;  
  }
  if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
    return VP8_STATUS_BITSTREAM_ERROR;
  }

  if (!hdrs.is_lossless) {
    if (data_size < VP8_FRAME_HEADER_SIZE) {
      return VP8_STATUS_NOT_ENOUGH_DATA;
    }
    
    if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size,
                    &image_width, &image_height)) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  } else {
    if (data_size < VP8L_FRAME_HEADER_SIZE) {
      return VP8_STATUS_NOT_ENOUGH_DATA;
    }
    
    if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  }
  
  if (found_vp8x) {
    if (canvas_width != image_width || canvas_height != image_height) {
      return VP8_STATUS_BITSTREAM_ERROR;
    }
  }
  if (width != NULL) *width = image_width;
  if (height != NULL) *height = image_height;
  if (has_alpha != NULL) {
    
    
    *has_alpha |= (hdrs.alpha_data != NULL);
  }
  if (headers != NULL) {
    *headers = hdrs;
    headers->offset = data - headers->data;
    assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
    assert(headers->offset == headers->data_size - data_size);
  }
  return VP8_STATUS_OK;  
}