muxedit.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. // Copyright 2011 Google Inc. All Rights Reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style license
  4. // that can be found in the COPYING file in the root of the source
  5. // tree. An additional intellectual property rights grant can be found
  6. // in the file PATENTS. All contributing project authors may
  7. // be found in the AUTHORS file in the root of the source tree.
  8. // -----------------------------------------------------------------------------
  9. //
  10. // Set and delete APIs for mux.
  11. //
  12. // Authors: Urvang (urvang@google.com)
  13. // Vikas (vikasa@google.com)
  14. #include <assert.h>
  15. #include "./muxi.h"
  16. #include "../utils/utils.h"
  17. //------------------------------------------------------------------------------
  18. // Life of a mux object.
  19. static void MuxInit(WebPMux* const mux) {
  20. assert(mux != NULL);
  21. memset(mux, 0, sizeof(*mux));
  22. mux->canvas_width_ = 0; // just to be explicit
  23. mux->canvas_height_ = 0;
  24. }
  25. WebPMux* WebPNewInternal(int version) {
  26. if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
  27. return NULL;
  28. } else {
  29. WebPMux* const mux = (WebPMux*)WebPSafeMalloc(1ULL, sizeof(WebPMux));
  30. if (mux != NULL) MuxInit(mux);
  31. return mux;
  32. }
  33. }
  34. // Delete all images in 'wpi_list'.
  35. static void DeleteAllImages(WebPMuxImage** const wpi_list) {
  36. while (*wpi_list != NULL) {
  37. *wpi_list = MuxImageDelete(*wpi_list);
  38. }
  39. }
  40. static void MuxRelease(WebPMux* const mux) {
  41. assert(mux != NULL);
  42. DeleteAllImages(&mux->images_);
  43. ChunkListDelete(&mux->vp8x_);
  44. ChunkListDelete(&mux->iccp_);
  45. ChunkListDelete(&mux->anim_);
  46. ChunkListDelete(&mux->exif_);
  47. ChunkListDelete(&mux->xmp_);
  48. ChunkListDelete(&mux->unknown_);
  49. }
  50. void WebPMuxDelete(WebPMux* mux) {
  51. if (mux != NULL) {
  52. MuxRelease(mux);
  53. WebPSafeFree(mux);
  54. }
  55. }
  56. //------------------------------------------------------------------------------
  57. // Helper method(s).
  58. // Handy MACRO, makes MuxSet() very symmetric to MuxGet().
  59. #define SWITCH_ID_LIST(INDEX, LIST) \
  60. if (idx == (INDEX)) { \
  61. err = ChunkAssignData(&chunk, data, copy_data, tag); \
  62. if (err == WEBP_MUX_OK) { \
  63. err = ChunkSetNth(&chunk, (LIST), nth); \
  64. } \
  65. return err; \
  66. }
  67. static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth,
  68. const WebPData* const data, int copy_data) {
  69. WebPChunk chunk;
  70. WebPMuxError err = WEBP_MUX_NOT_FOUND;
  71. const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag);
  72. assert(mux != NULL);
  73. assert(!IsWPI(kChunks[idx].id));
  74. ChunkInit(&chunk);
  75. SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_);
  76. SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_);
  77. SWITCH_ID_LIST(IDX_ANIM, &mux->anim_);
  78. SWITCH_ID_LIST(IDX_EXIF, &mux->exif_);
  79. SWITCH_ID_LIST(IDX_XMP, &mux->xmp_);
  80. SWITCH_ID_LIST(IDX_UNKNOWN, &mux->unknown_);
  81. return err;
  82. }
  83. #undef SWITCH_ID_LIST
  84. // Create data for frame given image data, offsets and duration.
  85. static WebPMuxError CreateFrameData(
  86. int width, int height, const WebPMuxFrameInfo* const info,
  87. WebPData* const frame) {
  88. uint8_t* frame_bytes;
  89. const size_t frame_size = kChunks[IDX_ANMF].size;
  90. assert(width > 0 && height > 0 && info->duration >= 0);
  91. assert(info->dispose_method == (info->dispose_method & 1));
  92. // Note: assertion on upper bounds is done in PutLE24().
  93. frame_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_size);
  94. if (frame_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
  95. PutLE24(frame_bytes + 0, info->x_offset / 2);
  96. PutLE24(frame_bytes + 3, info->y_offset / 2);
  97. PutLE24(frame_bytes + 6, width - 1);
  98. PutLE24(frame_bytes + 9, height - 1);
  99. PutLE24(frame_bytes + 12, info->duration);
  100. frame_bytes[15] =
  101. (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) |
  102. (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0);
  103. frame->bytes = frame_bytes;
  104. frame->size = frame_size;
  105. return WEBP_MUX_OK;
  106. }
  107. // Outputs image data given a bitstream. The bitstream can either be a
  108. // single-image WebP file or raw VP8/VP8L data.
  109. // Also outputs 'is_lossless' to be true if the given bitstream is lossless.
  110. static WebPMuxError GetImageData(const WebPData* const bitstream,
  111. WebPData* const image, WebPData* const alpha,
  112. int* const is_lossless) {
  113. WebPDataInit(alpha); // Default: no alpha.
  114. if (bitstream->size < TAG_SIZE ||
  115. memcmp(bitstream->bytes, "RIFF", TAG_SIZE)) {
  116. // It is NOT webp file data. Return input data as is.
  117. *image = *bitstream;
  118. } else {
  119. // It is webp file data. Extract image data from it.
  120. const WebPMuxImage* wpi;
  121. WebPMux* const mux = WebPMuxCreate(bitstream, 0);
  122. if (mux == NULL) return WEBP_MUX_BAD_DATA;
  123. wpi = mux->images_;
  124. assert(wpi != NULL && wpi->img_ != NULL);
  125. *image = wpi->img_->data_;
  126. if (wpi->alpha_ != NULL) {
  127. *alpha = wpi->alpha_->data_;
  128. }
  129. WebPMuxDelete(mux);
  130. }
  131. *is_lossless = VP8LCheckSignature(image->bytes, image->size);
  132. return WEBP_MUX_OK;
  133. }
  134. static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) {
  135. WebPMuxError err = WEBP_MUX_NOT_FOUND;
  136. assert(chunk_list);
  137. while (*chunk_list) {
  138. WebPChunk* const chunk = *chunk_list;
  139. if (chunk->tag_ == tag) {
  140. *chunk_list = ChunkDelete(chunk);
  141. err = WEBP_MUX_OK;
  142. } else {
  143. chunk_list = &chunk->next_;
  144. }
  145. }
  146. return err;
  147. }
  148. static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, uint32_t tag) {
  149. const WebPChunkId id = ChunkGetIdFromTag(tag);
  150. assert(mux != NULL);
  151. if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT;
  152. return DeleteChunks(MuxGetChunkListFromId(mux, id), tag);
  153. }
  154. //------------------------------------------------------------------------------
  155. // Set API(s).
  156. WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4],
  157. const WebPData* chunk_data, int copy_data) {
  158. uint32_t tag;
  159. WebPMuxError err;
  160. if (mux == NULL || fourcc == NULL || chunk_data == NULL ||
  161. chunk_data->bytes == NULL || chunk_data->size > MAX_CHUNK_PAYLOAD) {
  162. return WEBP_MUX_INVALID_ARGUMENT;
  163. }
  164. tag = ChunkGetTagFromFourCC(fourcc);
  165. // Delete existing chunk(s) with the same 'fourcc'.
  166. err = MuxDeleteAllNamedData(mux, tag);
  167. if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
  168. // Add the given chunk.
  169. return MuxSet(mux, tag, 1, chunk_data, copy_data);
  170. }
  171. // Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'.
  172. static WebPMuxError AddDataToChunkList(
  173. const WebPData* const data, int copy_data, uint32_t tag,
  174. WebPChunk** chunk_list) {
  175. WebPChunk chunk;
  176. WebPMuxError err;
  177. ChunkInit(&chunk);
  178. err = ChunkAssignData(&chunk, data, copy_data, tag);
  179. if (err != WEBP_MUX_OK) goto Err;
  180. err = ChunkSetNth(&chunk, chunk_list, 1);
  181. if (err != WEBP_MUX_OK) goto Err;
  182. return WEBP_MUX_OK;
  183. Err:
  184. ChunkRelease(&chunk);
  185. return err;
  186. }
  187. // Extracts image & alpha data from the given bitstream and then sets wpi.alpha_
  188. // and wpi.img_ appropriately.
  189. static WebPMuxError SetAlphaAndImageChunks(
  190. const WebPData* const bitstream, int copy_data, WebPMuxImage* const wpi) {
  191. int is_lossless = 0;
  192. WebPData image, alpha;
  193. WebPMuxError err = GetImageData(bitstream, &image, &alpha, &is_lossless);
  194. const int image_tag =
  195. is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag;
  196. if (err != WEBP_MUX_OK) return err;
  197. if (alpha.bytes != NULL) {
  198. err = AddDataToChunkList(&alpha, copy_data, kChunks[IDX_ALPHA].tag,
  199. &wpi->alpha_);
  200. if (err != WEBP_MUX_OK) return err;
  201. }
  202. err = AddDataToChunkList(&image, copy_data, image_tag, &wpi->img_);
  203. if (err != WEBP_MUX_OK) return err;
  204. return MuxImageFinalize(wpi) ? WEBP_MUX_OK : WEBP_MUX_INVALID_ARGUMENT;
  205. }
  206. WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream,
  207. int copy_data) {
  208. WebPMuxImage wpi;
  209. WebPMuxError err;
  210. // Sanity checks.
  211. if (mux == NULL || bitstream == NULL || bitstream->bytes == NULL ||
  212. bitstream->size > MAX_CHUNK_PAYLOAD) {
  213. return WEBP_MUX_INVALID_ARGUMENT;
  214. }
  215. if (mux->images_ != NULL) {
  216. // Only one 'simple image' can be added in mux. So, remove present images.
  217. DeleteAllImages(&mux->images_);
  218. }
  219. MuxImageInit(&wpi);
  220. err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi);
  221. if (err != WEBP_MUX_OK) goto Err;
  222. // Add this WebPMuxImage to mux.
  223. err = MuxImagePush(&wpi, &mux->images_);
  224. if (err != WEBP_MUX_OK) goto Err;
  225. // All is well.
  226. return WEBP_MUX_OK;
  227. Err: // Something bad happened.
  228. MuxImageRelease(&wpi);
  229. return err;
  230. }
  231. WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info,
  232. int copy_data) {
  233. WebPMuxImage wpi;
  234. WebPMuxError err;
  235. const WebPData* const bitstream = &info->bitstream;
  236. // Sanity checks.
  237. if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT;
  238. if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT;
  239. if (bitstream->bytes == NULL || bitstream->size > MAX_CHUNK_PAYLOAD) {
  240. return WEBP_MUX_INVALID_ARGUMENT;
  241. }
  242. if (mux->images_ != NULL) {
  243. const WebPMuxImage* const image = mux->images_;
  244. const uint32_t image_id = (image->header_ != NULL) ?
  245. ChunkGetIdFromTag(image->header_->tag_) : WEBP_CHUNK_IMAGE;
  246. if (image_id != info->id) {
  247. return WEBP_MUX_INVALID_ARGUMENT; // Conflicting frame types.
  248. }
  249. }
  250. MuxImageInit(&wpi);
  251. err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi);
  252. if (err != WEBP_MUX_OK) goto Err;
  253. assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful.
  254. {
  255. WebPData frame;
  256. const uint32_t tag = kChunks[IDX_ANMF].tag;
  257. WebPMuxFrameInfo tmp = *info;
  258. tmp.x_offset &= ~1; // Snap offsets to even.
  259. tmp.y_offset &= ~1;
  260. if (tmp.x_offset < 0 || tmp.x_offset >= MAX_POSITION_OFFSET ||
  261. tmp.y_offset < 0 || tmp.y_offset >= MAX_POSITION_OFFSET ||
  262. (tmp.duration < 0 || tmp.duration >= MAX_DURATION) ||
  263. tmp.dispose_method != (tmp.dispose_method & 1)) {
  264. err = WEBP_MUX_INVALID_ARGUMENT;
  265. goto Err;
  266. }
  267. err = CreateFrameData(wpi.width_, wpi.height_, &tmp, &frame);
  268. if (err != WEBP_MUX_OK) goto Err;
  269. // Add frame chunk (with copy_data = 1).
  270. err = AddDataToChunkList(&frame, 1, tag, &wpi.header_);
  271. WebPDataClear(&frame); // frame owned by wpi.header_ now.
  272. if (err != WEBP_MUX_OK) goto Err;
  273. }
  274. // Add this WebPMuxImage to mux.
  275. err = MuxImagePush(&wpi, &mux->images_);
  276. if (err != WEBP_MUX_OK) goto Err;
  277. // All is well.
  278. return WEBP_MUX_OK;
  279. Err: // Something bad happened.
  280. MuxImageRelease(&wpi);
  281. return err;
  282. }
  283. WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
  284. const WebPMuxAnimParams* params) {
  285. WebPMuxError err;
  286. uint8_t data[ANIM_CHUNK_SIZE];
  287. const WebPData anim = { data, ANIM_CHUNK_SIZE };
  288. if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
  289. if (params->loop_count < 0 || params->loop_count >= MAX_LOOP_COUNT) {
  290. return WEBP_MUX_INVALID_ARGUMENT;
  291. }
  292. // Delete any existing ANIM chunk(s).
  293. err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
  294. if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
  295. // Set the animation parameters.
  296. PutLE32(data, params->bgcolor);
  297. PutLE16(data + 4, params->loop_count);
  298. return MuxSet(mux, kChunks[IDX_ANIM].tag, 1, &anim, 1);
  299. }
  300. WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
  301. int width, int height) {
  302. WebPMuxError err;
  303. if (mux == NULL) {
  304. return WEBP_MUX_INVALID_ARGUMENT;
  305. }
  306. if (width < 0 || height < 0 ||
  307. width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
  308. return WEBP_MUX_INVALID_ARGUMENT;
  309. }
  310. if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
  311. return WEBP_MUX_INVALID_ARGUMENT;
  312. }
  313. if ((width * height) == 0 && (width | height) != 0) {
  314. // one of width / height is zero, but not both -> invalid!
  315. return WEBP_MUX_INVALID_ARGUMENT;
  316. }
  317. // If we already assembled a VP8X chunk, invalidate it.
  318. err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
  319. if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
  320. mux->canvas_width_ = width;
  321. mux->canvas_height_ = height;
  322. return WEBP_MUX_OK;
  323. }
  324. //------------------------------------------------------------------------------
  325. // Delete API(s).
  326. WebPMuxError WebPMuxDeleteChunk(WebPMux* mux, const char fourcc[4]) {
  327. if (mux == NULL || fourcc == NULL) return WEBP_MUX_INVALID_ARGUMENT;
  328. return MuxDeleteAllNamedData(mux, ChunkGetTagFromFourCC(fourcc));
  329. }
  330. WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) {
  331. if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
  332. return MuxImageDeleteNth(&mux->images_, nth);
  333. }
  334. //------------------------------------------------------------------------------
  335. // Assembly of the WebP RIFF file.
  336. static WebPMuxError GetFrameInfo(
  337. const WebPChunk* const frame_chunk,
  338. int* const x_offset, int* const y_offset, int* const duration) {
  339. const WebPData* const data = &frame_chunk->data_;
  340. const size_t expected_data_size = ANMF_CHUNK_SIZE;
  341. assert(frame_chunk->tag_ == kChunks[IDX_ANMF].tag);
  342. assert(frame_chunk != NULL);
  343. if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT;
  344. *x_offset = 2 * GetLE24(data->bytes + 0);
  345. *y_offset = 2 * GetLE24(data->bytes + 3);
  346. *duration = GetLE24(data->bytes + 12);
  347. return WEBP_MUX_OK;
  348. }
  349. static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
  350. int* const x_offset, int* const y_offset,
  351. int* const duration,
  352. int* const width, int* const height) {
  353. const WebPChunk* const frame_chunk = wpi->header_;
  354. WebPMuxError err;
  355. assert(wpi != NULL);
  356. assert(frame_chunk != NULL);
  357. // Get offsets and duration from ANMF chunk.
  358. err = GetFrameInfo(frame_chunk, x_offset, y_offset, duration);
  359. if (err != WEBP_MUX_OK) return err;
  360. // Get width and height from VP8/VP8L chunk.
  361. if (width != NULL) *width = wpi->width_;
  362. if (height != NULL) *height = wpi->height_;
  363. return WEBP_MUX_OK;
  364. }
  365. // Returns the tightest dimension for the canvas considering the image list.
  366. static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux,
  367. int* const width, int* const height) {
  368. WebPMuxImage* wpi = NULL;
  369. assert(mux != NULL);
  370. assert(width != NULL && height != NULL);
  371. wpi = mux->images_;
  372. assert(wpi != NULL);
  373. assert(wpi->img_ != NULL);
  374. if (wpi->next_ != NULL) {
  375. int max_x = 0, max_y = 0;
  376. // if we have a chain of wpi's, header_ is necessarily set
  377. assert(wpi->header_ != NULL);
  378. // Aggregate the bounding box for animation frames.
  379. for (; wpi != NULL; wpi = wpi->next_) {
  380. int x_offset = 0, y_offset = 0, duration = 0, w = 0, h = 0;
  381. const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset,
  382. &duration, &w, &h);
  383. const int max_x_pos = x_offset + w;
  384. const int max_y_pos = y_offset + h;
  385. if (err != WEBP_MUX_OK) return err;
  386. assert(x_offset < MAX_POSITION_OFFSET);
  387. assert(y_offset < MAX_POSITION_OFFSET);
  388. if (max_x_pos > max_x) max_x = max_x_pos;
  389. if (max_y_pos > max_y) max_y = max_y_pos;
  390. }
  391. *width = max_x;
  392. *height = max_y;
  393. } else {
  394. // For a single image, canvas dimensions are same as image dimensions.
  395. *width = wpi->width_;
  396. *height = wpi->height_;
  397. }
  398. return WEBP_MUX_OK;
  399. }
  400. // VP8X format:
  401. // Total Size : 10,
  402. // Flags : 4 bytes,
  403. // Width : 3 bytes,
  404. // Height : 3 bytes.
  405. static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
  406. WebPMuxError err = WEBP_MUX_OK;
  407. uint32_t flags = 0;
  408. int width = 0;
  409. int height = 0;
  410. uint8_t data[VP8X_CHUNK_SIZE];
  411. const WebPData vp8x = { data, VP8X_CHUNK_SIZE };
  412. const WebPMuxImage* images = NULL;
  413. assert(mux != NULL);
  414. images = mux->images_; // First image.
  415. if (images == NULL || images->img_ == NULL ||
  416. images->img_->data_.bytes == NULL) {
  417. return WEBP_MUX_INVALID_ARGUMENT;
  418. }
  419. // If VP8X chunk(s) is(are) already present, remove them (and later add new
  420. // VP8X chunk with updated flags).
  421. err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
  422. if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
  423. // Set flags.
  424. if (mux->iccp_ != NULL && mux->iccp_->data_.bytes != NULL) {
  425. flags |= ICCP_FLAG;
  426. }
  427. if (mux->exif_ != NULL && mux->exif_->data_.bytes != NULL) {
  428. flags |= EXIF_FLAG;
  429. }
  430. if (mux->xmp_ != NULL && mux->xmp_->data_.bytes != NULL) {
  431. flags |= XMP_FLAG;
  432. }
  433. if (images->header_ != NULL) {
  434. if (images->header_->tag_ == kChunks[IDX_ANMF].tag) {
  435. // This is an image with animation.
  436. flags |= ANIMATION_FLAG;
  437. }
  438. }
  439. if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) {
  440. flags |= ALPHA_FLAG; // Some images have an alpha channel.
  441. }
  442. err = GetAdjustedCanvasSize(mux, &width, &height);
  443. if (err != WEBP_MUX_OK) return err;
  444. if (width <= 0 || height <= 0) {
  445. return WEBP_MUX_INVALID_ARGUMENT;
  446. }
  447. if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
  448. return WEBP_MUX_INVALID_ARGUMENT;
  449. }
  450. if (mux->canvas_width_ != 0 || mux->canvas_height_ != 0) {
  451. if (width > mux->canvas_width_ || height > mux->canvas_height_) {
  452. return WEBP_MUX_INVALID_ARGUMENT;
  453. }
  454. width = mux->canvas_width_;
  455. height = mux->canvas_height_;
  456. }
  457. if (flags == 0 && mux->unknown_ == NULL) {
  458. // For simple file format, VP8X chunk should not be added.
  459. return WEBP_MUX_OK;
  460. }
  461. if (MuxHasAlpha(images)) {
  462. // This means some frames explicitly/implicitly contain alpha.
  463. // Note: This 'flags' update must NOT be done for a lossless image
  464. // without a VP8X chunk!
  465. flags |= ALPHA_FLAG;
  466. }
  467. PutLE32(data + 0, flags); // VP8X chunk flags.
  468. PutLE24(data + 4, width - 1); // canvas width.
  469. PutLE24(data + 7, height - 1); // canvas height.
  470. return MuxSet(mux, kChunks[IDX_VP8X].tag, 1, &vp8x, 1);
  471. }
  472. // Cleans up 'mux' by removing any unnecessary chunks.
  473. static WebPMuxError MuxCleanup(WebPMux* const mux) {
  474. int num_frames;
  475. int num_anim_chunks;
  476. // If we have an image with a single frame, and its rectangle
  477. // covers the whole canvas, convert it to a non-animated image
  478. // (to avoid writing ANMF chunk unnecessarily).
  479. WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames);
  480. if (err != WEBP_MUX_OK) return err;
  481. if (num_frames == 1) {
  482. WebPMuxImage* frame = NULL;
  483. err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame);
  484. assert(err == WEBP_MUX_OK); // We know that one frame does exist.
  485. assert(frame != NULL);
  486. if (frame->header_ != NULL &&
  487. ((mux->canvas_width_ == 0 && mux->canvas_height_ == 0) ||
  488. (frame->width_ == mux->canvas_width_ &&
  489. frame->height_ == mux->canvas_height_))) {
  490. assert(frame->header_->tag_ == kChunks[IDX_ANMF].tag);
  491. ChunkDelete(frame->header_); // Removes ANMF chunk.
  492. frame->header_ = NULL;
  493. num_frames = 0;
  494. }
  495. }
  496. // Remove ANIM chunk if this is a non-animated image.
  497. err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks);
  498. if (err != WEBP_MUX_OK) return err;
  499. if (num_anim_chunks >= 1 && num_frames == 0) {
  500. err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
  501. if (err != WEBP_MUX_OK) return err;
  502. }
  503. return WEBP_MUX_OK;
  504. }
  505. // Total size of a list of images.
  506. static size_t ImageListDiskSize(const WebPMuxImage* wpi_list) {
  507. size_t size = 0;
  508. while (wpi_list != NULL) {
  509. size += MuxImageDiskSize(wpi_list);
  510. wpi_list = wpi_list->next_;
  511. }
  512. return size;
  513. }
  514. // Write out the given list of images into 'dst'.
  515. static uint8_t* ImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) {
  516. while (wpi_list != NULL) {
  517. dst = MuxImageEmit(wpi_list, dst);
  518. wpi_list = wpi_list->next_;
  519. }
  520. return dst;
  521. }
  522. WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
  523. size_t size = 0;
  524. uint8_t* data = NULL;
  525. uint8_t* dst = NULL;
  526. WebPMuxError err;
  527. if (assembled_data == NULL) {
  528. return WEBP_MUX_INVALID_ARGUMENT;
  529. }
  530. // Clean up returned data, in case something goes wrong.
  531. memset(assembled_data, 0, sizeof(*assembled_data));
  532. if (mux == NULL) {
  533. return WEBP_MUX_INVALID_ARGUMENT;
  534. }
  535. // Finalize mux.
  536. err = MuxCleanup(mux);
  537. if (err != WEBP_MUX_OK) return err;
  538. err = CreateVP8XChunk(mux);
  539. if (err != WEBP_MUX_OK) return err;
  540. // Allocate data.
  541. size = ChunkListDiskSize(mux->vp8x_) + ChunkListDiskSize(mux->iccp_)
  542. + ChunkListDiskSize(mux->anim_) + ImageListDiskSize(mux->images_)
  543. + ChunkListDiskSize(mux->exif_) + ChunkListDiskSize(mux->xmp_)
  544. + ChunkListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE;
  545. data = (uint8_t*)WebPSafeMalloc(1ULL, size);
  546. if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
  547. // Emit header & chunks.
  548. dst = MuxEmitRiffHeader(data, size);
  549. dst = ChunkListEmit(mux->vp8x_, dst);
  550. dst = ChunkListEmit(mux->iccp_, dst);
  551. dst = ChunkListEmit(mux->anim_, dst);
  552. dst = ImageListEmit(mux->images_, dst);
  553. dst = ChunkListEmit(mux->exif_, dst);
  554. dst = ChunkListEmit(mux->xmp_, dst);
  555. dst = ChunkListEmit(mux->unknown_, dst);
  556. assert(dst == data + size);
  557. // Validate mux.
  558. err = MuxValidate(mux);
  559. if (err != WEBP_MUX_OK) {
  560. WebPSafeFree(data);
  561. data = NULL;
  562. size = 0;
  563. }
  564. // Finalize data.
  565. assembled_data->bytes = data;
  566. assembled_data->size = size;
  567. return err;
  568. }
  569. //------------------------------------------------------------------------------