libavcodec/msrledec.c
44aa9771
 /*
607694c7
  * Microsoft RLE decoder
44aa9771
  * Copyright (C) 2008 Konstantin Shishkov
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 /**
ba87f080
  * @file
607694c7
  * MS RLE decoder based on decoder by Mike Melanson and my own for TSCC
44aa9771
  * For more information about the MS RLE format, visit:
  *   http://www.multimedia.cx/msrle.txt
  */
 
6a5d31ac
 #include "libavutil/intreadwrite.h"
44aa9771
 #include "avcodec.h"
ccd17ea1
 #include "msrledec.h"
44aa9771
 
3496cec4
 static int msrle_decode_pal4(AVCodecContext *avctx, AVFrame *pic,
992f71e9
                              GetByteContext *gb)
44aa9771
 {
     unsigned char rle_code;
     unsigned char extra_byte, odd_pixel;
     unsigned char stream_byte;
ae6fd730
     int pixel_ptr = 0;
f7e1367f
     int line = avctx->height - 1;
44aa9771
     int i;
 
f7e1367f
     while (line >= 0 && pixel_ptr <= avctx->width) {
992f71e9
         if (bytestream2_get_bytes_left(gb) <= 0) {
             av_log(avctx, AV_LOG_ERROR,
f7e1367f
                    "MS RLE: bytestream overrun, %dx%d left\n",
                    avctx->width - pixel_ptr, line);
992f71e9
             return AVERROR_INVALIDDATA;
         }
         rle_code = stream_byte = bytestream2_get_byteu(gb);
44aa9771
         if (rle_code == 0) {
             /* fetch the next byte to see how to handle escape code */
992f71e9
             stream_byte = bytestream2_get_byte(gb);
44aa9771
             if (stream_byte == 0) {
                 /* line is done, goto the next one */
f7e1367f
                 line--;
44aa9771
                 pixel_ptr = 0;
             } else if (stream_byte == 1) {
                 /* decode is done */
                 return 0;
             } else if (stream_byte == 2) {
                 /* reposition frame decode coordinates */
992f71e9
                 stream_byte = bytestream2_get_byte(gb);
44aa9771
                 pixel_ptr += stream_byte;
992f71e9
                 stream_byte = bytestream2_get_byte(gb);
e856ac23
                 line -= stream_byte;
94ee6c10
             } else {
                 // copy pixels from encoded stream
                 odd_pixel =  stream_byte & 1;
                 rle_code = (stream_byte + 1) / 2;
                 extra_byte = rle_code & 0x01;
f7e1367f
                 if (pixel_ptr + 2*rle_code - odd_pixel > avctx->width ||
992f71e9
                     bytestream2_get_bytes_left(gb) < rle_code) {
                     av_log(avctx, AV_LOG_ERROR,
                            "MS RLE: frame/stream ptr just went out of bounds (copy)\n");
                     return AVERROR_INVALIDDATA;
94ee6c10
                 }
44aa9771
 
94ee6c10
                 for (i = 0; i < rle_code; i++) {
                     if (pixel_ptr >= avctx->width)
                         break;
992f71e9
                     stream_byte = bytestream2_get_byteu(gb);
f7e1367f
                     pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte >> 4;
94ee6c10
                     pixel_ptr++;
                     if (i + 1 == rle_code && odd_pixel)
                         break;
                     if (pixel_ptr >= avctx->width)
                         break;
f7e1367f
                     pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte & 0x0F;
94ee6c10
                     pixel_ptr++;
                 }
44aa9771
 
94ee6c10
                 // if the RLE code is odd, skip a byte in the stream
                 if (extra_byte)
992f71e9
                     bytestream2_skip(gb, 1);
44aa9771
             }
         } else {
             // decode a run of data
f7e1367f
             if (pixel_ptr + rle_code > avctx->width + 1) {
992f71e9
                 av_log(avctx, AV_LOG_ERROR,
c20eab5c
                        "MS RLE: frame ptr just went out of bounds (run) %d %d %d\n", pixel_ptr, rle_code, avctx->width);
992f71e9
                 return AVERROR_INVALIDDATA;
44aa9771
             }
992f71e9
             stream_byte = bytestream2_get_byte(gb);
44aa9771
             for (i = 0; i < rle_code; i++) {
                 if (pixel_ptr >= avctx->width)
                     break;
                 if ((i & 1) == 0)
f7e1367f
                     pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte >> 4;
44aa9771
                 else
f7e1367f
                     pic->data[0][line * pic->linesize[0] + pixel_ptr] = stream_byte & 0x0F;
44aa9771
                 pixel_ptr++;
             }
         }
     }
 
     /* one last sanity check on the way out */
992f71e9
     if (bytestream2_get_bytes_left(gb)) {
         av_log(avctx, AV_LOG_ERROR,
                "MS RLE: ended frame decode with %d bytes left over\n",
                bytestream2_get_bytes_left(gb));
         return AVERROR_INVALIDDATA;
44aa9771
     }
 
     return 0;
 }
 
 
3496cec4
 static int msrle_decode_8_16_24_32(AVCodecContext *avctx, AVFrame *pic,
992f71e9
                                    int depth, GetByteContext *gb)
44aa9771
 {
     uint8_t *output, *output_end;
b3867c93
     int p1, p2, line=avctx->height - 1, pos=0, i;
a8798c7e
     uint16_t pix16;
     uint32_t pix32;
74297831
     unsigned int width= FFABS(pic->linesize[0]) / (depth >> 3);
44aa9771
 
ee41963f
     output     = pic->data[0] + (avctx->height - 1) * pic->linesize[0];
e398990e
     output_end = output + FFABS(pic->linesize[0]);
 
992f71e9
     while (bytestream2_get_bytes_left(gb) > 0) {
         p1 = bytestream2_get_byteu(gb);
44aa9771
         if(p1 == 0) { //Escape code
992f71e9
             p2 = bytestream2_get_byte(gb);
44aa9771
             if(p2 == 0) { //End-of-line
9bd6375d
                 if (--line < 0) {
992f71e9
                     if (bytestream2_get_be16(gb) == 1) { // end-of-picture
                         return 0;
                     } else {
                         av_log(avctx, AV_LOG_ERROR,
                                "Next line is beyond picture bounds (%d bytes left)\n",
                                bytestream2_get_bytes_left(gb));
                         return AVERROR_INVALIDDATA;
                     }
ce60c2d1
                 }
9bd6375d
                 output = pic->data[0] + line * pic->linesize[0];
e398990e
                 output_end = output + FFABS(pic->linesize[0]);
44aa9771
                 pos = 0;
                 continue;
             } else if(p2 == 1) { //End-of-picture
                 return 0;
             } else if(p2 == 2) { //Skip
992f71e9
                 p1 = bytestream2_get_byte(gb);
                 p2 = bytestream2_get_byte(gb);
44aa9771
                 line -= p2;
74297831
                 pos += p1;
                 if (line < 0 || pos >= width){
ce60c2d1
                     av_log(avctx, AV_LOG_ERROR, "Skip beyond picture bounds\n");
44aa9771
                     return -1;
ce60c2d1
                 }
44aa9771
                 output = pic->data[0] + line * pic->linesize[0] + pos * (depth >> 3);
e398990e
                 output_end = pic->data[0] + line * pic->linesize[0] + FFABS(pic->linesize[0]);
44aa9771
                 continue;
             }
             // Copy data
e398990e
             if (output + p2 * (depth >> 3) > output_end) {
992f71e9
                 bytestream2_skip(gb, 2 * (depth >> 3));
44aa9771
                 continue;
992f71e9
             } else if (bytestream2_get_bytes_left(gb) < p2 * (depth >> 3)) {
                 av_log(avctx, AV_LOG_ERROR, "bytestream overrun\n");
                 return AVERROR_INVALIDDATA;
44aa9771
             }
992f71e9
 
44aa9771
             if ((depth == 8) || (depth == 24)) {
66387389
                 bytestream2_get_bufferu(gb, output, p2 * (depth >> 3));
                 output += p2 * (depth >> 3);
 
44aa9771
                 // RLE8 copy is actually padded - and runs are not!
                 if(depth == 8 && (p2 & 1)) {
992f71e9
                     bytestream2_skip(gb, 1);
44aa9771
                 }
             } else if (depth == 16) {
                 for(i = 0; i < p2; i++) {
992f71e9
                     *(uint16_t*)output = bytestream2_get_le16u(gb);
44aa9771
                     output += 2;
                 }
             } else if (depth == 32) {
                 for(i = 0; i < p2; i++) {
992f71e9
                     *(uint32_t*)output = bytestream2_get_le32u(gb);
44aa9771
                     output += 4;
                 }
             }
             pos += p2;
607694c7
         } else { //run of pixels
6149cdbe
             uint8_t pix[3]; //original pixel
e398990e
             if (output + p1 * (depth >> 3) > output_end)
c2992b70
                 continue;
d2e0a276
 
dbaae33c
             switch(depth){
             case  8:
d2e0a276
                 pix[0] = bytestream2_get_byte(gb);
e6e26b8a
                 memset(output, pix[0], p1);
                 output += p1;
dbaae33c
                 break;
             case 16:
d2e0a276
                 pix16  = bytestream2_get_le16(gb);
dbaae33c
                 for(i = 0; i < p1; i++) {
                         *(uint16_t*)output = pix16;
                         output += 2;
                 }
                 break;
             case 24:
d2e0a276
                 pix[0] = bytestream2_get_byte(gb);
                 pix[1] = bytestream2_get_byte(gb);
                 pix[2] = bytestream2_get_byte(gb);
dbaae33c
                 for(i = 0; i < p1; i++) {
                         *output++ = pix[0];
                         *output++ = pix[1];
                         *output++ = pix[2];
                 }
                 break;
             case 32:
d2e0a276
                 pix32  = bytestream2_get_le32(gb);
dbaae33c
                 for(i = 0; i < p1; i++) {
                         *(uint32_t*)output = pix32;
                         output += 4;
44aa9771
                 }
dbaae33c
                 break;
44aa9771
             }
             pos += p1;
         }
     }
 
607694c7
     av_log(avctx, AV_LOG_WARNING, "MS RLE warning: no end-of-picture code\n");
44aa9771
     return 0;
 }
 
 
3496cec4
 int ff_msrle_decode(AVCodecContext *avctx, AVFrame *pic,
992f71e9
                     int depth, GetByteContext *gb)
44aa9771
 {
     switch(depth){
     case  4:
992f71e9
         return msrle_decode_pal4(avctx, pic, gb);
44aa9771
     case  8:
     case 16:
     case 24:
     case 32:
992f71e9
         return msrle_decode_8_16_24_32(avctx, pic, depth, gb);
44aa9771
     default:
         av_log(avctx, AV_LOG_ERROR, "Unknown depth %d\n", depth);
         return -1;
     }
 }