mirror of
				https://github.com/webmproject/libwebp.git
				synced 2025-10-31 10:25:46 +01:00 
			
		
		
		
	Incremental support for some of the mux APIs.
This CL adds incremental support for WebPMuxCreate() and WebPMuxGetFeatures() Change-Id: I312122f5e817d58366e18ae238cb9e2a493811fb
This commit is contained in:
		| @@ -355,15 +355,16 @@ static int ReadData(const char* filename, void** data_ptr, uint32_t* size_ptr) { | ||||
| static int ReadFile(const char* const filename, WebPMux** mux) { | ||||
|   uint32_t size = 0; | ||||
|   void* data = NULL; | ||||
|   WebPMuxState mux_state; | ||||
|  | ||||
|   assert(mux != NULL); | ||||
|  | ||||
|   if (!ReadData(filename, &data, &size)) return 0; | ||||
|   *mux = WebPMuxCreate((const uint8_t*)data, size, 1); | ||||
|   *mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state); | ||||
|   free(data); | ||||
|   if (*mux != NULL) return 1; | ||||
|   fprintf(stderr, "Failed to create mux object from file %s.\n", | ||||
|           filename); | ||||
|   if (*mux != NULL && mux_state == WEBP_MUX_STATE_COMPLETE) return 1; | ||||
|   fprintf(stderr, "Failed to create mux object from file %s. mux_state = %d.\n", | ||||
|           filename, mux_state); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| @@ -377,14 +378,16 @@ static int ReadImage(const char* filename, | ||||
|   WebPMux* mux; | ||||
|   WebPMuxError err; | ||||
|   int ok = 0; | ||||
|   WebPMuxState mux_state; | ||||
|  | ||||
|   if (!ReadData(filename, &data, &size)) return 0; | ||||
|  | ||||
|   mux = WebPMuxCreate((const uint8_t*)data, size, 1); | ||||
|   mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state); | ||||
|   free(data); | ||||
|   if (mux == NULL) { | ||||
|     fprintf(stderr, "Failed to create mux object from file %s.\n", | ||||
|             filename); | ||||
|   if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) { | ||||
|     fprintf(stderr, | ||||
|             "Failed to create mux object from file %s. mux_state = %d.\n", | ||||
|             filename, mux_state); | ||||
|     return 0; | ||||
|   } | ||||
|   err = WebPMuxGetImage(mux, (const uint8_t**)&data, &size, | ||||
|   | ||||
| @@ -23,6 +23,7 @@ extern "C" { | ||||
| static int MuxInit(WebPMux* const mux) { | ||||
|   if (mux == NULL) return 0; | ||||
|   memset(mux, 0, sizeof(*mux)); | ||||
|   mux->state_ = WEBP_MUX_STATE_PARTIAL; | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| @@ -179,8 +180,11 @@ static WebPMuxError GetImageData(const uint8_t* data, uint32_t size, | ||||
|     // It is webp file data. Extract image data from it. | ||||
|     WebPMux* mux; | ||||
|     WebPMuxError err; | ||||
|     mux = WebPMuxCreate(data, size, 0); | ||||
|     if (mux == NULL) return WEBP_MUX_BAD_DATA; | ||||
|     WebPMuxState mux_state; | ||||
|     mux = WebPMuxCreate(data, size, 0, &mux_state); | ||||
|     if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) { | ||||
|       return WEBP_MUX_BAD_DATA; | ||||
|     } | ||||
|  | ||||
|     err = WebPMuxGetImage(mux, image_data, image_size, alpha_data, alpha_size); | ||||
|     WebPMuxDelete(mux); | ||||
| @@ -634,6 +638,9 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux, uint8_t** output_data, | ||||
|   err = CreateVP8XChunk(mux); | ||||
|   if (err != WEBP_MUX_OK) return err; | ||||
|  | ||||
|   // Mark mux as complete. | ||||
|   mux->state_ = WEBP_MUX_STATE_COMPLETE; | ||||
|  | ||||
|   // Allocate data. | ||||
|   size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_) | ||||
|       + ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_) | ||||
|   | ||||
| @@ -59,6 +59,7 @@ struct WebPMuxImage { | ||||
|  | ||||
| // Main mux object. Stores data chunks. | ||||
| struct WebPMux { | ||||
|   WebPMuxState    state_; | ||||
|   WebPMuxImage*   images_; | ||||
|   WebPChunk*      iccp_; | ||||
|   WebPChunk*      meta_; | ||||
| @@ -121,6 +122,10 @@ static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) { | ||||
|   PutLE16(data + 2, val >> 16); | ||||
| } | ||||
|  | ||||
| static WEBP_INLINE uint32_t SizeWithPadding(uint32_t chunk_size) { | ||||
|   return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U); | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Chunk object management. | ||||
|  | ||||
| @@ -154,7 +159,7 @@ WebPChunk* ChunkDelete(WebPChunk* const chunk); | ||||
| // Size of a chunk including header and padding. | ||||
| static WEBP_INLINE uint32_t ChunkDiskSize(const WebPChunk* chunk) { | ||||
|   assert(chunk->payload_size_ < MAX_CHUNK_PAYLOAD); | ||||
|   return CHUNK_HEADER_SIZE + ((chunk->payload_size_ + 1) & ~1U); | ||||
|   return SizeWithPadding(chunk->payload_size_); | ||||
| } | ||||
|  | ||||
| // Total size of a list of chunks. | ||||
| @@ -169,6 +174,9 @@ uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst); | ||||
| // Initialize. | ||||
| void MuxImageInit(WebPMuxImage* const wpi); | ||||
|  | ||||
| // Delete image 'wpi'. | ||||
| WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi); | ||||
|  | ||||
| // Delete all images in 'wpi_list'. | ||||
| void MuxImageDeleteAll(WebPMuxImage** const wpi_list); | ||||
|  | ||||
|   | ||||
| @@ -344,7 +344,7 @@ WebPMuxError MuxImageSetNth(const WebPMuxImage* wpi, WebPMuxImage** wpi_list, | ||||
| //------------------------------------------------------------------------------ | ||||
| // MuxImage deletion methods. | ||||
|  | ||||
| static WebPMuxImage* DeleteImage(WebPMuxImage* const wpi) { | ||||
| WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi) { | ||||
|   WebPMuxImage* const next = MuxImageRelease(wpi); | ||||
|   free(wpi); | ||||
|   return next; | ||||
| @@ -352,7 +352,7 @@ static WebPMuxImage* DeleteImage(WebPMuxImage* const wpi) { | ||||
|  | ||||
| void MuxImageDeleteAll(WebPMuxImage** const wpi_list) { | ||||
|   while (*wpi_list) { | ||||
|     *wpi_list = DeleteImage(*wpi_list); | ||||
|     *wpi_list = MuxImageDelete(*wpi_list); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -362,7 +362,7 @@ WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth, | ||||
|   if (!SearchImageToGetOrDelete(wpi_list, nth, id, &wpi_list)) { | ||||
|     return WEBP_MUX_NOT_FOUND; | ||||
|   } | ||||
|   *wpi_list = DeleteImage(*wpi_list); | ||||
|   *wpi_list = MuxImageDelete(*wpi_list); | ||||
|   return WEBP_MUX_OK; | ||||
| } | ||||
|  | ||||
| @@ -474,7 +474,12 @@ WebPMuxError WebPMuxValidate(const WebPMux* const mux) { | ||||
|   WebPMuxError err; | ||||
|  | ||||
|   // Verify mux is not NULL. | ||||
|   if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; | ||||
|   if (mux == NULL || mux->state_ == WEBP_MUX_STATE_ERROR) { | ||||
|     return WEBP_MUX_INVALID_ARGUMENT; | ||||
|   } | ||||
|  | ||||
|   // No further checks if mux is partial. | ||||
|   if (mux->state_ == WEBP_MUX_STATE_PARTIAL) return WEBP_MUX_OK; | ||||
|  | ||||
|   // Verify mux has at least one image. | ||||
|   if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT; | ||||
|   | ||||
| @@ -53,107 +53,142 @@ static WebPMuxError MuxGet(const WebPMux* const mux, TAG_ID id, uint32_t nth, | ||||
| // Fill the chunk with the given data, after verifying that the data size | ||||
| // doesn't exceed 'max_size'. | ||||
| static WebPMuxError ChunkAssignData(WebPChunk* chunk, const uint8_t* data, | ||||
|                                     uint32_t max_size, int copy_data) { | ||||
|   uint32_t size = 0; | ||||
|   assert(max_size >= CHUNK_HEADER_SIZE); | ||||
|                                     uint32_t data_size, uint32_t riff_size, | ||||
|                                     int copy_data) { | ||||
|   uint32_t chunk_size; | ||||
|  | ||||
|   size = GetLE32(data + 4); | ||||
|   assert(size <= MAX_CHUNK_PAYLOAD); | ||||
|   if (size + CHUNK_HEADER_SIZE > max_size) { | ||||
|     return WEBP_MUX_INVALID_ARGUMENT; | ||||
|   // Sanity checks. | ||||
|   if (data_size < TAG_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA; | ||||
|   chunk_size = GetLE32(data + TAG_SIZE); | ||||
|  | ||||
|   { | ||||
|     const uint32_t chunk_disk_size = SizeWithPadding(chunk_size); | ||||
|     if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA; | ||||
|     if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA; | ||||
|   } | ||||
|  | ||||
|   return ChunkAssignDataImageInfo(chunk, data + CHUNK_HEADER_SIZE, size, NULL, | ||||
|                                   copy_data, GetLE32(data + 0)); | ||||
|   // Data assignment. | ||||
|   return ChunkAssignDataImageInfo(chunk, data + CHUNK_HEADER_SIZE, chunk_size, | ||||
|                                   NULL, copy_data, GetLE32(data + 0)); | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Create a mux object from WebP-RIFF data. | ||||
|  | ||||
| WebPMux* WebPMuxCreate(const uint8_t* data, uint32_t size, int copy_data) { | ||||
|   uint32_t mux_size; | ||||
| WebPMux* WebPMuxCreate(const uint8_t* data, uint32_t size, int copy_data, | ||||
|                        WebPMuxState* const mux_state) { | ||||
|   uint32_t riff_size; | ||||
|   uint32_t tag; | ||||
|   const uint8_t* end; | ||||
|   TAG_ID id; | ||||
|   WebPMux* mux; | ||||
|   WebPMuxImage wpi; | ||||
|   WebPMux* mux = NULL; | ||||
|   WebPMuxImage* wpi = NULL; | ||||
|  | ||||
|   // Sanity checks on size and leading bytes. | ||||
|   if (data == NULL) return NULL; | ||||
|   if (size < RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE) { | ||||
|     return NULL; | ||||
|   } | ||||
|   if (mux_state) *mux_state = WEBP_MUX_STATE_PARTIAL; | ||||
|  | ||||
|   // Sanity checks. | ||||
|   if (data == NULL) goto Err; | ||||
|   if (size < RIFF_HEADER_SIZE) return NULL; | ||||
|   if (GetLE32(data + 0) != mktag('R', 'I', 'F', 'F') || | ||||
|       GetLE32(data + 8) != mktag('W', 'E', 'B', 'P')) { | ||||
|     return NULL; | ||||
|       GetLE32(data + CHUNK_HEADER_SIZE) != mktag('W', 'E', 'B', 'P')) { | ||||
|     goto Err; | ||||
|   } | ||||
|   mux_size = CHUNK_HEADER_SIZE + GetLE32(data + TAG_SIZE); | ||||
|   if (mux_size > size) { | ||||
|     return NULL; | ||||
|  | ||||
|   mux = WebPMuxNew(); | ||||
|   if (mux == NULL) goto Err; | ||||
|  | ||||
|   if (size < RIFF_HEADER_SIZE + TAG_SIZE) { | ||||
|     mux->state_ = WEBP_MUX_STATE_PARTIAL; | ||||
|     goto Ok; | ||||
|   } | ||||
|  | ||||
|   tag = GetLE32(data + RIFF_HEADER_SIZE); | ||||
|   if (tag != kChunks[IMAGE_ID].chunkTag && tag != kChunks[VP8X_ID].chunkTag) { | ||||
|     // First chunk should be either VP8X or VP8. | ||||
|     return NULL; | ||||
|     goto Err; | ||||
|   } | ||||
|   end = data + mux_size; | ||||
|  | ||||
|   riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE)); | ||||
|   if (riff_size > MAX_CHUNK_PAYLOAD) { | ||||
|     goto Err; | ||||
|   } else if (riff_size > size) { | ||||
|     mux->state_ = WEBP_MUX_STATE_PARTIAL; | ||||
|   } else { | ||||
|     mux->state_ = WEBP_MUX_STATE_COMPLETE; | ||||
|     if (riff_size < size) {  // Redundant data after last chunk. | ||||
|       size = riff_size;  // To make sure we don't read any data beyond mux_size. | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   end = data + size; | ||||
|   data += RIFF_HEADER_SIZE; | ||||
|   mux_size -= RIFF_HEADER_SIZE; | ||||
|   size -= RIFF_HEADER_SIZE; | ||||
|  | ||||
|   mux = WebPMuxNew(); | ||||
|   if (mux == NULL) return NULL; | ||||
|  | ||||
|   MuxImageInit(&wpi); | ||||
|   wpi = (WebPMuxImage*)malloc(sizeof(*wpi)); | ||||
|   MuxImageInit(wpi); | ||||
|  | ||||
|   // Loop over chunks. | ||||
|   while (data != end) { | ||||
|     WebPChunk chunk; | ||||
|     uint32_t data_size; | ||||
|     WebPMuxError err; | ||||
|  | ||||
|     ChunkInit(&chunk); | ||||
|     if (ChunkAssignData(&chunk, data, mux_size, copy_data) != WEBP_MUX_OK) { | ||||
|       goto Err; | ||||
|     err = ChunkAssignData(&chunk, data, size, riff_size, copy_data); | ||||
|     if (err != WEBP_MUX_OK) { | ||||
|       if (err == WEBP_MUX_NOT_ENOUGH_DATA && | ||||
|           mux->state_ == WEBP_MUX_STATE_PARTIAL) { | ||||
|         goto Ok; | ||||
|       } else { | ||||
|         goto Err; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     data_size = ChunkDiskSize(&chunk); | ||||
|     id = ChunkGetIdFromTag(chunk.tag_); | ||||
|  | ||||
|     if (IsWPI(id)) {  // An image chunk (frame/tile/alpha/vp8). | ||||
|       WebPChunk** wpi_chunk_ptr; | ||||
|       wpi_chunk_ptr = MuxImageGetListFromId(&wpi, id);  // Image chunk to set. | ||||
|       wpi_chunk_ptr = MuxImageGetListFromId(wpi, id);  // Image chunk to set. | ||||
|       assert(wpi_chunk_ptr != NULL); | ||||
|       if (*wpi_chunk_ptr != NULL) goto Err;  // Consecutive alpha chunks or | ||||
|                                              // consecutive frame/tile chunks. | ||||
|       if (ChunkSetNth(&chunk, wpi_chunk_ptr, 1) != WEBP_MUX_OK) goto Err; | ||||
|       if (id == IMAGE_ID) { | ||||
|         wpi.is_partial_ = 0;  // wpi is completely filled. | ||||
|         wpi->is_partial_ = 0;  // wpi is completely filled. | ||||
|         // Add this to mux->images_ list. | ||||
|         if (MuxImageSetNth(&wpi, &mux->images_, 0) != WEBP_MUX_OK) goto Err; | ||||
|         MuxImageInit(&wpi);  // Reset for reading next image. | ||||
|         if (MuxImageSetNth(wpi, &mux->images_, 0) != WEBP_MUX_OK) goto Err; | ||||
|         MuxImageInit(wpi);  // Reset for reading next image. | ||||
|       } else { | ||||
|         wpi.is_partial_ = 1;  // wpi is only partially filled. | ||||
|         wpi->is_partial_ = 1;  // wpi is only partially filled. | ||||
|       } | ||||
|     } else {  // A non-image chunk. | ||||
|       WebPChunk** chunk_list; | ||||
|       if (wpi.is_partial_) goto Err;  // Encountered a non-image chunk before | ||||
|                                       // getting all chunks of an image. | ||||
|       if (wpi->is_partial_) goto Err;  // Encountered a non-image chunk before | ||||
|                                        // getting all chunks of an image. | ||||
|       chunk_list = GetChunkListFromId(mux, id);  // List for adding this chunk. | ||||
|       if (chunk_list == NULL) chunk_list = (WebPChunk**)&mux->unknown_; | ||||
|       if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err; | ||||
|     } | ||||
|  | ||||
|     data += data_size; | ||||
|     mux_size -= data_size; | ||||
|     { | ||||
|       const uint32_t data_size = ChunkDiskSize(&chunk); | ||||
|       data += data_size; | ||||
|       size -= data_size; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Validate mux. | ||||
|   // Validate mux if complete. | ||||
|   if (WebPMuxValidate(mux) != WEBP_MUX_OK) goto Err; | ||||
|  | ||||
|  Ok: | ||||
|   MuxImageDelete(wpi); | ||||
|   if (mux_state) *mux_state = mux->state_; | ||||
|   return mux;  // All OK; | ||||
|  | ||||
|  Err:  // Something bad happened. | ||||
|    WebPMuxDelete(mux); | ||||
|    return NULL; | ||||
|   MuxImageDelete(wpi); | ||||
|   WebPMuxDelete(mux); | ||||
|   if (mux_state) *mux_state = WEBP_MUX_STATE_ERROR; | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| @@ -165,18 +200,29 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) { | ||||
|   WebPMuxError err; | ||||
|  | ||||
|   if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT; | ||||
|   *flags = 0; | ||||
|  | ||||
|   // Check if VP8X chunk is present. | ||||
|   err = MuxGet(mux, VP8X_ID, 1, &data, &data_size); | ||||
|   if (err == WEBP_MUX_NOT_FOUND) {  // Single image case. | ||||
|     *flags = 0; | ||||
|     return WEBP_MUX_OK; | ||||
|   if (err == WEBP_MUX_NOT_FOUND) { | ||||
|     // Check if VP8 chunk is present. | ||||
|     err = WebPMuxGetImage(mux, &data, &data_size, NULL, NULL); | ||||
|     if (err == WEBP_MUX_NOT_FOUND &&              // Data not available (yet). | ||||
|         mux->state_ == WEBP_MUX_STATE_PARTIAL) {  // Incremental case. | ||||
|       return WEBP_MUX_NOT_ENOUGH_DATA; | ||||
|     } else { | ||||
|       return err; | ||||
|     } | ||||
|   } else if (err != WEBP_MUX_OK) { | ||||
|     return err; | ||||
|   } | ||||
|  | ||||
|   // Multiple image case. | ||||
|   if (err != WEBP_MUX_OK) return err; | ||||
|   // TODO(urvang): Add a '#define CHUNK_SIZE_BYTES 4' and use it instead of | ||||
|   // hard-coded value of 4 everywhere. | ||||
|   if (data_size < 4) return WEBP_MUX_BAD_DATA; | ||||
|   *flags = GetLE32(data); | ||||
|  | ||||
|   // All OK. Fill up flags. | ||||
|   *flags = GetLE32(data); | ||||
|   return WEBP_MUX_OK; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -34,7 +34,7 @@ | ||||
| // | ||||
| //   int copy_data = 0; | ||||
| //   // ... (Read data from file). | ||||
| //   WebPMux* mux = WebPMuxCreate(data, data_size, copy_data); | ||||
| //   WebPMux* mux = WebPMuxCreate(data, data_size, copy_data, NULL); | ||||
| //   WebPMuxGetImage(mux, &image_data, &image_data_size, | ||||
| //                   &alpha_data, &alpha_size); | ||||
| //   // ... (Consume image_data; e.g. call WebPDecode() to decode the data). | ||||
| @@ -60,9 +60,16 @@ typedef enum { | ||||
|   WEBP_MUX_INVALID_ARGUMENT   = -2, | ||||
|   WEBP_MUX_INVALID_PARAMETER  = -3, | ||||
|   WEBP_MUX_BAD_DATA           = -4, | ||||
|   WEBP_MUX_MEMORY_ERROR       = -5 | ||||
|   WEBP_MUX_MEMORY_ERROR       = -5, | ||||
|   WEBP_MUX_NOT_ENOUGH_DATA    = -6 | ||||
| } WebPMuxError; | ||||
|  | ||||
| typedef enum { | ||||
|   WEBP_MUX_STATE_PARTIAL  =  0, | ||||
|   WEBP_MUX_STATE_COMPLETE =  1, | ||||
|   WEBP_MUX_STATE_ERROR    = -1 | ||||
| } WebPMuxState; | ||||
|  | ||||
| // Flag values for different features used in VP8X chunk. | ||||
| typedef enum { | ||||
|   TILE_FLAG       = 0x00000001, | ||||
| @@ -96,11 +103,13 @@ WEBP_EXTERN(void) WebPMuxDelete(WebPMux* const mux); | ||||
| //   size - (in) size of raw data | ||||
| //   copy_data - (in) value 1 indicates given data WILL copied to the mux, and | ||||
| //               value 0 indicates data will NOT be copied. | ||||
| //   mux_state - (out) indicates the state of the mux returned. Can be passed | ||||
| //               NULL if not required. | ||||
| // Returns: | ||||
| //   A pointer to the mux object created from given data - on success. | ||||
| //   NULL - In case of invalid data or memory error. | ||||
| WEBP_EXTERN(WebPMux*) WebPMuxCreate(const uint8_t* data, uint32_t size, | ||||
|                                     int copy_data); | ||||
|                                     int copy_data, WebPMuxState* mux_state); | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| // Single Image. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user