/// <summary> /// /// </summary> /// <param name="avctx"></param> /// <param name="outdata"></param> /// <param name="outdata_size"></param> /// <param name="avpkt"></param> /// <returns></returns> public override int decode(AVCodecContext avctx, ref object outputData, AVPacket avpkt) { Pointer <byte> buf = avpkt.data; int buf_size = avpkt.size; BMPContext s = (BMPContext)avctx.priv_data; AVFrame p = s.picture; uint fsize, hsize; int width, height; int depth; BiCompression comp; uint ihsize; int i, n, linesize; var rgb = new uint[3]; uint alpha = 0; Pointer <byte> ptr; int dsize; Pointer <byte> buf0 = buf; if (buf_size < 14) { log.av_log(avctx, log.AV_LOG_ERROR, "buf size too small (%d)\n", buf_size); return(-1); } if (bytestream.get_byte(ref buf) != 'B' || bytestream.get_byte(ref buf) != 'M') { log.av_log(avctx, log.AV_LOG_ERROR, "bad magic number\n"); return(-1); } fsize = bytestream.get_le32(ref buf); if (buf_size < fsize) { log.av_log(avctx, log.AV_LOG_ERROR, "not enough data (%d < %d), trying to decode anyway\n", buf_size, fsize); fsize = (uint)buf_size; } buf += 2; /* reserved1 */ buf += 2; /* reserved2 */ hsize = bytestream.get_le32(ref buf); /* header size */ ihsize = bytestream.get_le32(ref buf); /* more header size */ if (ihsize + 14 > hsize) { log.av_log(avctx, log.AV_LOG_ERROR, "invalid header size %d\n", hsize); return(-1); } /* sometimes file size is set to some headers size, set a real size in that case */ if (fsize == 14 || fsize == ihsize + 14) { fsize = (uint)(buf_size - 2); } if (fsize <= hsize) { log.av_log(avctx, log.AV_LOG_ERROR, "declared file size is less than header size (%d < %d)\n", fsize, hsize); return(-1); } switch (ihsize) { case 40: // windib case 56: // windib v3 case 64: // OS/2 v2 case 108: // windib v4 case 124: // windib v5 width = (int)bytestream.get_le32(ref buf); height = (int)bytestream.get_le32(ref buf); break; case 12: // OS/2 v1 width = (int)bytestream.get_le16(ref buf); height = (int)bytestream.get_le16(ref buf); break; default: log.av_log(avctx, log.AV_LOG_ERROR, "unsupported BMP file, patch welcome\n"); return(-1); } if (bytestream.get_le16(ref buf) != 1) /* planes */ { log.av_log(avctx, log.AV_LOG_ERROR, "invalid BMP header\n"); return(-1); } depth = bytestream.get_le16(ref buf); if (ihsize == 40 || ihsize == 64 || ihsize == 56) { comp = (BiCompression)bytestream.get_le32(ref buf); } else { comp = BiCompression.BMP_RGB; } if (comp != BiCompression.BMP_RGB && comp != BiCompression.BMP_BITFIELDS && comp != BiCompression.BMP_RLE4 && comp != BiCompression.BMP_RLE8) { log.av_log(avctx, log.AV_LOG_ERROR, "BMP coding %d not supported\n", comp); return(-1); } if (comp == BiCompression.BMP_BITFIELDS) { buf += 20; rgb[0] = bytestream.get_le32(ref buf); rgb[1] = bytestream.get_le32(ref buf); rgb[2] = bytestream.get_le32(ref buf); if (ihsize >= 108) { alpha = bytestream.get_le32(ref buf); } } avctx.width = width; avctx.height = height > 0 ? height : -height; avctx.pix_fmt = PixelFormat.PIX_FMT_NONE; switch (depth) { case 32: if (comp == BiCompression.BMP_BITFIELDS) { if (rgb[0] == 0xFF000000 && rgb[1] == 0x00FF0000 && rgb[2] == 0x0000FF00) { avctx.pix_fmt = (alpha != 0) ? PixelFormat.PIX_FMT_ABGR : PixelFormat.PIX_FMT_0BGR; } else if (rgb[0] == 0x00FF0000 && rgb[1] == 0x0000FF00 && rgb[2] == 0x000000FF) { avctx.pix_fmt = (alpha != 0) ? PixelFormat.PIX_FMT_BGRA : PixelFormat.PIX_FMT_BGR0; } else if (rgb[0] == 0x0000FF00 && rgb[1] == 0x00FF0000 && rgb[2] == 0xFF000000) { avctx.pix_fmt = (alpha != 0) ? PixelFormat.PIX_FMT_ARGB : PixelFormat.PIX_FMT_0RGB; } else if (rgb[0] == 0x000000FF && rgb[1] == 0x0000FF00 && rgb[2] == 0x00FF0000) { avctx.pix_fmt = (alpha != 0) ? PixelFormat.PIX_FMT_RGBA : PixelFormat.PIX_FMT_RGB0; } else { log.av_log(avctx, log.AV_LOG_ERROR, "Unknown bitfields %0X %0X %0X\n", rgb[0], rgb[1], rgb[2]); return(error.AVERROR(error.EINVAL)); } } else { avctx.pix_fmt = PixelFormat.PIX_FMT_BGRA; } break; case 24: avctx.pix_fmt = PixelFormat.PIX_FMT_BGR24; break; case 16: if (comp == BiCompression.BMP_RGB) { avctx.pix_fmt = PixelFormat.PIX_FMT_RGB555; } else if (comp == BiCompression.BMP_BITFIELDS) { if (rgb[0] == 0xF800 && rgb[1] == 0x07E0 && rgb[2] == 0x001F) { avctx.pix_fmt = PixelFormat.PIX_FMT_RGB565; } else if (rgb[0] == 0x7C00 && rgb[1] == 0x03E0 && rgb[2] == 0x001F) { avctx.pix_fmt = PixelFormat.PIX_FMT_RGB555; } else if (rgb[0] == 0x0F00 && rgb[1] == 0x00F0 && rgb[2] == 0x000F) { avctx.pix_fmt = PixelFormat.PIX_FMT_RGB444; } else { log.av_log(avctx, log.AV_LOG_ERROR, "Unknown bitfields %0X %0X %0X\n", rgb[0], rgb[1], rgb[2]); return(error.AVERROR(error.EINVAL)); } } break; case 8: if (hsize - ihsize - 14 > 0) { avctx.pix_fmt = PixelFormat.PIX_FMT_PAL8; } else { avctx.pix_fmt = PixelFormat.PIX_FMT_GRAY8; } break; case 1: case 4: if (hsize - ihsize - 14 > 0) { avctx.pix_fmt = PixelFormat.PIX_FMT_PAL8; } else { log.av_log(avctx, log.AV_LOG_ERROR, "Unknown palette for %d-colour BMP\n", 1 << depth); return(-1); } break; default: log.av_log(avctx, log.AV_LOG_ERROR, "depth %d not supported\n", depth); return(-1); } if (avctx.pix_fmt == PixelFormat.PIX_FMT_NONE) { log.av_log(avctx, log.AV_LOG_ERROR, "unsupported pixel format\n"); return(-1); } if (!p.data[0].IsNull) { avctx.release_buffer(avctx, p); } p.reference = 0; if (avctx.get_buffer(avctx, p) < 0) { log.av_log(avctx, log.AV_LOG_ERROR, "get_buffer() failed\n"); return(-1); } p.pict_type = AVPictureType.AV_PICTURE_TYPE_I; p.key_frame = 1; buf = buf0 + hsize; dsize = (int)(buf_size - hsize); /* Line size in file multiple of 4 */ n = (int)(((avctx.width * depth + 31) / 8) & ~3); if (n * avctx.height > dsize && comp != BiCompression.BMP_RLE4 && comp != BiCompression.BMP_RLE8) { log.av_log(avctx, log.AV_LOG_ERROR, "not enough data (%d < %d)\n", dsize, n * avctx.height); return(-1); } // RLE may skip decoding some picture areas, so blank picture before decoding if (comp == BiCompression.BMP_RLE4 || comp == BiCompression.BMP_RLE8) { CLib.memset(p.data[0], 0, avctx.height * p.linesize[0]); } if (depth == 4 || depth == 8) { CLib.memset(p.data[1], 0, 1024); } if (height > 0) { ptr = p.data[0] + (avctx.height - 1) * p.linesize[0]; linesize = -p.linesize[0]; } else { ptr = p.data[0]; linesize = p.linesize[0]; } if (avctx.pix_fmt == PixelFormat.PIX_FMT_PAL8) { int colors = (1 << depth); if (ihsize >= 36) { int t; buf = buf0 + 46; t = (int)bytestream.get_le32(ref buf); if (t < 0 || t > (int)(1 << depth)) { log.av_log(avctx, log.AV_LOG_ERROR, "Incorrect number of colors - %X for bitdepth %d\n", t, depth); } else if (t != 0) { colors = t; } } buf = buf0 + 14 + ihsize; //palette location if ((hsize - ihsize - 14) < (colors << 2)) // OS/2 bitmap, 3 bytes per palette entry { for (i = 0; i < colors; i++) { var a = p.data[1].CastPointer <uint>(); a[i] = (uint)((0xff << 24) | bytestream.get_le24(ref buf)); } } else { for (i = 0; i < colors; i++) { var a = p.data[1].CastPointer <uint>(); a[i] = (uint)((0xFFU << 24) | bytestream.get_le32(ref buf)); } } buf = buf0 + hsize; } if (comp == BiCompression.BMP_RLE4 || comp == BiCompression.BMP_RLE8) { if (height < 0) { p.data[0] += p.linesize[0] * (avctx.height - 1); p.linesize[0] = -p.linesize[0]; } //ff_msrle_decode(avctx, (AVPicture)p, depth, buf, dsize); Unimplemented.Mark(); if (height < 0) { p.data[0] += p.linesize[0] * (avctx.height - 1); p.linesize[0] = -p.linesize[0]; } } else { switch (depth) { case 1: for (i = 0; i < avctx.height; i++) { for (int j = 0; j < n; j++) { ptr[j * 8 + 0] = (byte)((buf[j] >> 7)); ptr[j * 8 + 1] = (byte)((buf[j] >> 6) & 1); ptr[j * 8 + 2] = (byte)((buf[j] >> 5) & 1); ptr[j * 8 + 3] = (byte)((buf[j] >> 4) & 1); ptr[j * 8 + 4] = (byte)((buf[j] >> 3) & 1); ptr[j * 8 + 5] = (byte)((buf[j] >> 2) & 1); ptr[j * 8 + 6] = (byte)((buf[j] >> 1) & 1); ptr[j * 8 + 7] = (byte)((buf[j] >> 0) & 1); } buf += n; ptr += linesize; } break; case 8: case 24: case 32: for (i = 0; i < avctx.height; i++) { //Console.WriteLine("i={0}, BytesPerRow={1}, linesize={2}", i, n, linesize); CLib.memcpy(ptr, buf, n); buf += n; ptr += linesize; } break; case 4: for (i = 0; i < avctx.height; i++) { for (int j = 0; j < n; j++) { ptr[j * 2 + 0] = (byte)((buf[j] >> 4) & 0xF); ptr[j * 2 + 1] = (byte)(buf[j] & 0xF); } buf += n; ptr += linesize; } break; case 16: for (i = 0; i < avctx.height; i++) { Pointer <ushort> src = buf.CastPointer <ushort>(); Pointer <ushort> dst = ptr.CastPointer <ushort>(); for (int j = 0; j < avctx.width; j++) { dst[0] = av_bswap.av_le2ne16(src[0]); src++; dst++; } buf += n; ptr += linesize; } break; default: log.av_log(avctx, log.AV_LOG_ERROR, "BMP decoder is broken\n"); return(-1); } } outputData = s.picture; return(buf_size); } // decode