diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/bmp/avi_rle_decoder.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/bmp/avi_rle_decoder.cpp')
-rw-r--r-- | Src/bmp/avi_rle_decoder.cpp | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/Src/bmp/avi_rle_decoder.cpp b/Src/bmp/avi_rle_decoder.cpp new file mode 100644 index 00000000..20d682f2 --- /dev/null +++ b/Src/bmp/avi_rle_decoder.cpp @@ -0,0 +1,225 @@ +#include "avi_rle_decoder.h" +#include "../Winamp/wa_ipc.h" +#include <limits.h> +#include "rle.h" +#include <intsafe.h> + +AVIRLE *AVIRLE::CreateDecoder(nsavi::video_format *stream_format) +{ + if (stream_format->bits_per_pixel == 4) + return 0; + + size_t bytes_per_pixel = stream_format->bits_per_pixel / 8U; + if (bytes_per_pixel > 4) + return 0; + + size_t image_size=0; + if (SizeTMult(stream_format->width, stream_format->height, &image_size) != S_OK || SizeTMult(image_size, bytes_per_pixel, &image_size) != S_OK) + return 0; + + void *video_frame = (uint8_t *)malloc(image_size); + if (!video_frame) + return 0; + + AVIRLE *decoder = new AVIRLE(video_frame, stream_format, image_size); + if (!decoder) + { + free(video_frame); + return 0; + } + + return decoder; +} + + +AVIRLE::AVIRLE(void *video_frame, nsavi::video_format *stream_format, size_t video_frame_size) : stream_format(stream_format), video_frame((uint8_t *)video_frame), video_frame_size(video_frame_size) +{ + memset(palette, 0, sizeof(palette)); + memcpy(palette, (uint8_t *)stream_format + 44, 1024); + video_outputted=false; + palette_retrieved=false; +} + +int AVIRLE::GetPalette(RGB32 **palette) +{ + if (!palette_retrieved) + { + *palette = (RGB32 *)(this->palette); + palette_retrieved=true; + return AVI_SUCCESS; + } + else + { + return AVI_FAILURE; + } + +} + +int AVIRLE::GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio, int *flip) +{ + if (stream_format) + { + *x = stream_format->width; + *y = stream_format->height; + *flip = 1; + switch(stream_format->bits_per_pixel) + { + case 4: + *color_format = '8BGR'; + break; + case 8: + *color_format = '8BGR'; + break; + case 16: + *color_format = '555R'; + break; + case 24: + *color_format = '42GR'; + break; + case 32: + *color_format = '23GR'; + break; + default: + return AVI_FAILURE; + } + return AVI_SUCCESS; + } + + return AVI_FAILURE; +} + +static bool CheckOverflow(size_t total_size, int current_position, int read_size) +{ + if (read_size > (int)total_size) // check separate to avoid overflow + return true; + if (((int)total_size - read_size) < current_position) + return true; + return false; +} + +int AVIRLE::DecodeChunk(uint16_t type, const void *inputBuffer, size_t inputBufferBytes) +{ + if (stream_format) + { + uint32_t bytes_per_pixel = stream_format->bits_per_pixel / 8; + const uint8_t * const rle = (const uint8_t *)inputBuffer; + if (bytes_per_pixel == 2) + { + RLE16(rle, inputBufferBytes, (uint16_t *)video_frame, video_frame_size, stream_format->width); + } + else if (bytes_per_pixel == 1) + { + RLE8(rle, inputBufferBytes, (uint8_t *)video_frame, video_frame_size, stream_format->width); + } + else + { + + int input = 0; + int output = 0; + + int next_line = output + bytes_per_pixel*stream_format->width; + while (input < (int)inputBufferBytes && output < (int)video_frame_size) + { + if (CheckOverflow(inputBufferBytes, input, 2)) // we always read at least two bytes + break; + + uint8_t b0 = rle[input++]; + if (b0) + { + if (CheckOverflow(inputBufferBytes, input, bytes_per_pixel)) + break; + + if (CheckOverflow(video_frame_size, output, b0*bytes_per_pixel)) + break; + + uint8_t pixel[4]; + memcpy(pixel, &rle[input], bytes_per_pixel); + input += bytes_per_pixel; + while (b0--) + { + memcpy(&video_frame[output], &pixel, bytes_per_pixel); + output+=bytes_per_pixel; + } + } + else + { + uint8_t b1 = rle[input++]; + if (b1 == 0) + { + output = next_line; + next_line = output + bytes_per_pixel*stream_format->width; + } + else if (b1 == 1) + { + break; + } + else if (b1 == 2) + { + if (CheckOverflow(inputBufferBytes, input, 2)) + break; + + uint8_t p1 = rle[input++]; + uint8_t p2 = rle[input++]; + output += bytes_per_pixel*p1; + output += bytes_per_pixel*p2*stream_format->width; + next_line += bytes_per_pixel*p2*stream_format->width; + } + else + { + if (CheckOverflow(inputBufferBytes, input, b1*bytes_per_pixel)) + break; + + if (CheckOverflow(video_frame_size, output, b1*bytes_per_pixel)) + break; + + memcpy(&video_frame[output], &rle[input], b1*bytes_per_pixel); + input += b1*bytes_per_pixel; + output += b1*bytes_per_pixel; + if (bytes_per_pixel == 1 && (b1 & 1)) + input++; + } + } + } + + } + video_outputted=false; + return AVI_SUCCESS; + } + + return AVI_FAILURE; +} + +void AVIRLE::Flush() +{ + +} + +int AVIRLE::GetPicture(void **data, void **decoder_data) +{ + if (!video_outputted && video_frame) + { + *data = video_frame; + *decoder_data=0; + video_outputted=true; + return AVI_SUCCESS; + } + + return AVI_FAILURE; +} + +void AVIRLE::Close() +{ + free(video_frame); + delete this; +} + +#define CBCLASS AVIRLE +START_DISPATCH; +CB(GET_OUTPUT_PROPERTIES, GetOutputProperties) +CB(DECODE_CHUNK, DecodeChunk) +VCB(FLUSH, Flush) +VCB(CLOSE, Close) +CB(GET_PICTURE, GetPicture) +CB(GET_PALETTE, GetPalette) +END_DISPATCH; +#undef CBCLASS |