From eb522af4fec58876ac1b0a73ad9bcdae2d82d33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 6 Mar 2020 16:19:35 +0100 Subject: Cleanup: move Alembic, AVI, Collada, and USD to `source/blender/io` This moves the `alembic`, `avi`, `collada`, and `usd` modules into a common `io` directory. This also cleans up some `#include "../../{somedir}/{somefile}.h"` by adding `../../io/{somedir}` to `CMakeLists.txt` and then just using `#include "{somefile}.h"`. No functional changes. --- source/blender/io/avi/AVI_avi.h | 299 ++++++++ source/blender/io/avi/CMakeLists.txt | 53 ++ source/blender/io/avi/intern/avi.c | 1056 ++++++++++++++++++++++++++++ source/blender/io/avi/intern/avi_codecs.c | 138 ++++ source/blender/io/avi/intern/avi_endian.c | 203 ++++++ source/blender/io/avi/intern/avi_endian.h | 40 ++ source/blender/io/avi/intern/avi_intern.h | 65 ++ source/blender/io/avi/intern/avi_mjpeg.c | 547 ++++++++++++++ source/blender/io/avi/intern/avi_mjpeg.h | 30 + source/blender/io/avi/intern/avi_options.c | 148 ++++ source/blender/io/avi/intern/avi_rgb.c | 154 ++++ source/blender/io/avi/intern/avi_rgb.h | 30 + source/blender/io/avi/intern/avi_rgb32.c | 94 +++ source/blender/io/avi/intern/avi_rgb32.h | 30 + 14 files changed, 2887 insertions(+) create mode 100644 source/blender/io/avi/AVI_avi.h create mode 100644 source/blender/io/avi/CMakeLists.txt create mode 100644 source/blender/io/avi/intern/avi.c create mode 100644 source/blender/io/avi/intern/avi_codecs.c create mode 100644 source/blender/io/avi/intern/avi_endian.c create mode 100644 source/blender/io/avi/intern/avi_endian.h create mode 100644 source/blender/io/avi/intern/avi_intern.h create mode 100644 source/blender/io/avi/intern/avi_mjpeg.c create mode 100644 source/blender/io/avi/intern/avi_mjpeg.h create mode 100644 source/blender/io/avi/intern/avi_options.c create mode 100644 source/blender/io/avi/intern/avi_rgb.c create mode 100644 source/blender/io/avi/intern/avi_rgb.h create mode 100644 source/blender/io/avi/intern/avi_rgb32.c create mode 100644 source/blender/io/avi/intern/avi_rgb32.h (limited to 'source/blender/io/avi') diff --git a/source/blender/io/avi/AVI_avi.h b/source/blender/io/avi/AVI_avi.h new file mode 100644 index 00000000000..4f3aa720da3 --- /dev/null +++ b/source/blender/io/avi/AVI_avi.h @@ -0,0 +1,299 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * \section avi_about About the AVI module + * + * This is external code. It provides avi file import/export and + * conversions. It has been adapted to make use of Blender memory + * management functions, and because of this it needs module + * blenlib. You need to provide this lib when linking with libavi.a . + * + * \subsection avi_issues Known issues with AVI + * + * - avi uses #MEM_mallocN, #MEM_freeN from blenlib. + * - Not all functions that are used externally are properly + * prototyped. + * + * This header has not been split, since it interleaves type defines + * and functions. You would need the types to be able to include the + * function headers anyway. And, after all, it is someone else's + * code. So we keep it like this. + */ + +#ifndef __AVI_AVI_H__ +#define __AVI_AVI_H__ + +#include "BLI_sys_types.h" +#include /* for FILE */ + +typedef struct _AviChunk { + int fcc; + int size; +} AviChunk; + +typedef struct _AviList { + int fcc; + int size; + int ids; +} AviList; + +typedef struct _AviMainHeader { + int fcc; + int size; + int MicroSecPerFrame; /* MicroSecPerFrame - timing between frames */ + int MaxBytesPerSec; /* MaxBytesPerSec - approx bps system must handle */ + int PaddingGranularity; + int Flags; + + /** had idx1 chunk */ +#define AVIF_HASINDEX 0x00000010 + /** must use idx1 chunk to determine order */ +#define AVIF_MUSTUSEINDEX 0x00000020 + /** AVI file is interleaved */ +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 + /** specially allocated used for capturing real time video */ +#define AVIF_WASCAPTUREFILE 0x00010000 + /** contains copyrighted data */ +#define AVIF_COPYRIGHTED 0x00020000 + + int TotalFrames; + int InitialFrames; /* InitialFrames - initial frame before interleaving */ + int Streams; + int SuggestedBufferSize; + int Width; + int Height; + int Reserved[4]; +} AviMainHeader; + +typedef struct _AviStreamHeader { + int fcc; + int size; + int Type; +#define AVIST_VIDEO FCC("vids") +#define AVIST_AUDIO FCC("auds") +#define AVIST_MIDI FCC("mids") +#define AVIST_TEXT FCC("txts") + + int Handler; + int Flags; +#define AVISF_DISABLED 0x00000001 +#define AVISF_VIDEO_PALCHANGES 0x00010000 + + short Priority; + short Language; + int InitialFrames; + int Scale; + int Rate; + int Start; + int Length; + int SuggestedBufferSize; + int Quality; + int SampleSize; + short left; + short top; + short right; + short bottom; +} AviStreamHeader; + +typedef struct _AviBitmapInfoHeader { + int fcc; + int size; + int Size; + int Width; + int Height; + short Planes; + short BitCount; + int Compression; + int SizeImage; + int XPelsPerMeter; + int YPelsPerMeter; + int ClrUsed; + int ClrImportant; +} AviBitmapInfoHeader; + +typedef struct _AviMJPEGUnknown { + int a; + int b; + int c; + int d; + int e; + int f; + int g; +} AviMJPEGUnknown; + +typedef struct _AviIndexEntry { + int ChunkId; + int Flags; +#define AVIIF_LIST 0x00000001 +#define AVIIF_KEYFRAME 0x00000010 +#define AVIIF_NO_TIME 0x00000100 +#define AVIIF_COMPRESSOR 0x0FFF0000 + int Offset; + int Size; +} AviIndexEntry; + +typedef struct _AviIndex { + int fcc; + int size; + AviIndexEntry *entrys; +} AviIndex; + +typedef enum { + /** The most basic of forms, 3 bytes per pixel, 1 per r, g, b. */ + AVI_FORMAT_RGB24, + /** The second most basic of forms, 4 bytes per pixel, 1 per r, g, b, alpha. */ + AVI_FORMAT_RGB32, + /** Same as above, but is in the weird AVI order (bottom to top, left to right). */ + AVI_FORMAT_AVI_RGB, + /** Motion-JPEG. */ + AVI_FORMAT_MJPEG, +} AviFormat; + +typedef struct _AviStreamRec { + AviStreamHeader sh; + void *sf; + int sf_size; + AviFormat format; +} AviStreamRec; + +typedef struct _AviMovie { + FILE *fp; + + int type; +#define AVI_MOVIE_READ 0 +#define AVI_MOVIE_WRITE 1 + + int64_t size; + + AviMainHeader *header; + AviStreamRec *streams; + AviIndexEntry *entries; + int index_entries; + + int64_t movi_offset; + int64_t read_offset; + int64_t *offset_table; + + /* Local data goes here */ + int interlace; + int odd_fields; +} AviMovie; + +typedef enum { + AVI_ERROR_NONE = 0, + AVI_ERROR_COMPRESSION, + AVI_ERROR_OPEN, + AVI_ERROR_READING, + AVI_ERROR_WRITING, + AVI_ERROR_FORMAT, + AVI_ERROR_ALLOC, + AVI_ERROR_FOUND, + AVI_ERROR_OPTION, +} AviError; + +/* belongs to the option-setting function. */ +typedef enum { + AVI_OPTION_WIDTH = 0, + AVI_OPTION_HEIGHT, + AVI_OPTION_QUALITY, + AVI_OPTION_FRAMERATE, +} AviOption; + +/* The offsets that will always stay the same in AVI files we + * write... used to seek around to the places where we need to write + * the sizes */ + +#define AVI_RIFF_SOFF 4L +#define AVI_HDRL_SOFF 16L + +/** + * This is a sort of MAKE_ID thing. Used in imbuf :( It is used + * through options in the AVI header (AviStreamHeader). */ +#define FCC(ch4) (ch4[0] | ch4[1] << 8 | ch4[2] << 16 | ch4[3] << 24) + +/** + * Test whether this is an avi-format. + */ +bool AVI_is_avi(const char *name); + +/** + * Open a compressed file, decompress it into memory. + */ +AviError AVI_open_compress(char *name, AviMovie *movie, int streams, ...); + +/** + * Finalize a compressed output stream. + */ +AviError AVI_close_compress(AviMovie *movie); + +/** + * Choose a compression option for \. Possible options are + * AVI_OPTION_TYPE_MAIN, AVI_OPTION_TYPE_STRH, AVI_OPTION_TYPE_STRF + */ +AviError AVI_set_compress_option( + AviMovie *movie, int option_type, int stream, AviOption option, void *opt_data); +/* Hmmm... there should be some explanation about what these mean */ +/** + * Compression option, for use in avi_set_compress_option + */ +#define AVI_OPTION_TYPE_MAIN 0 +/** + * Compression option, for use in avi_set_compress_option + */ +#define AVI_OPTION_TYPE_STRH 1 +/** + * Compression option, for use in avi_set_compress_option + */ +#define AVI_OPTION_TYPE_STRF 2 + +/** + * Direct the streams \ to \. Redirect \ + * streams. + */ +int AVI_get_stream(AviMovie *movie, int avist_type, int stream_num); + +/** + * Open a movie stream from file. + */ +AviError AVI_open_movie(const char *name, AviMovie *movie); + +/** + * Read a frame from a movie stream. + */ +void *AVI_read_frame(AviMovie *movie, AviFormat format, int frame, int stream); +/** + * Close an open movie stream. + */ +AviError AVI_close(AviMovie *movie); + +/** + * Write frames to a movie stream. + */ +AviError AVI_write_frame(AviMovie *movie, int frame_num, ...); + +/** + * Unused but still external + */ +AviError AVI_print_error(AviError error); + +#endif /* __AVI_AVI_H__ */ diff --git a/source/blender/io/avi/CMakeLists.txt b/source/blender/io/avi/CMakeLists.txt new file mode 100644 index 00000000000..76c90353673 --- /dev/null +++ b/source/blender/io/avi/CMakeLists.txt @@ -0,0 +1,53 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../../blenlib + ../../imbuf + ../../../../intern/guardedalloc +) + +set(INC_SYS + ${JPEG_INCLUDE_DIR} +) + +set(SRC + intern/avi.c + intern/avi_codecs.c + intern/avi_endian.c + intern/avi_mjpeg.c + intern/avi_options.c + intern/avi_rgb.c + intern/avi_rgb32.c + + AVI_avi.h + intern/avi_endian.h + intern/avi_intern.h + intern/avi_mjpeg.h + intern/avi_rgb.h + intern/avi_rgb32.h +) + +set(LIB + ${JPEG_LIBRARIES} +) + +blender_add_lib(bf_avi "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/avi/intern/avi.c b/source/blender/io/avi/intern/avi.c new file mode 100644 index 00000000000..22eb0be0cc0 --- /dev/null +++ b/source/blender/io/avi/intern/avi.c @@ -0,0 +1,1056 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. + */ + +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_sys_types.h" +#include "BLI_utildefines.h" +#include "BLI_fileops.h" + +#include "AVI_avi.h" +#include "avi_intern.h" + +#include "avi_endian.h" + +static int AVI_DEBUG = 0; +static char DEBUG_FCC[4]; + +#define DEBUG_PRINT(x) \ + if (AVI_DEBUG) { \ + printf("AVI DEBUG: " x); \ + } \ + (void)0 + +/* local functions */ +char *fcc_to_char(unsigned int fcc); +char *tcc_to_char(unsigned int tcc); + +/* implementation */ + +unsigned int GET_FCC(FILE *fp) +{ + unsigned char tmp[4]; + + tmp[0] = getc(fp); + tmp[1] = getc(fp); + tmp[2] = getc(fp); + tmp[3] = getc(fp); + + return FCC(tmp); +} + +unsigned int GET_TCC(FILE *fp) +{ + char tmp[5]; + + tmp[0] = getc(fp); + tmp[1] = getc(fp); + tmp[2] = 0; + tmp[3] = 0; + + return FCC(tmp); +} + +char *fcc_to_char(unsigned int fcc) +{ + DEBUG_FCC[0] = (fcc)&127; + DEBUG_FCC[1] = (fcc >> 8) & 127; + DEBUG_FCC[2] = (fcc >> 16) & 127; + DEBUG_FCC[3] = (fcc >> 24) & 127; + + return DEBUG_FCC; +} + +char *tcc_to_char(unsigned int tcc) +{ + DEBUG_FCC[0] = (tcc)&127; + DEBUG_FCC[1] = (tcc >> 8) & 127; + DEBUG_FCC[2] = 0; + DEBUG_FCC[3] = 0; + + return DEBUG_FCC; +} + +int AVI_get_stream(AviMovie *movie, int avist_type, int stream_num) +{ + int cur_stream; + + if (movie == NULL) { + return -AVI_ERROR_OPTION; + } + + for (cur_stream = 0; cur_stream < movie->header->Streams; cur_stream++) { + if (movie->streams[cur_stream].sh.Type == avist_type) { + if (stream_num == 0) { + return cur_stream; + } + else { + stream_num--; + } + } + } + + return -AVI_ERROR_FOUND; +} + +static int fcc_get_stream(int fcc) +{ + char fccs[4]; + + fccs[0] = fcc; + fccs[1] = fcc >> 8; + fccs[2] = fcc >> 16; + fccs[3] = fcc >> 24; + + return 10 * (fccs[0] - '0') + (fccs[1] - '0'); +} + +static bool fcc_is_data(int fcc) +{ + char fccs[4]; + + fccs[0] = fcc; + fccs[1] = fcc >> 8; + fccs[2] = fcc >> 16; + fccs[3] = fcc >> 24; + + if (!isdigit(fccs[0]) || !isdigit(fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w')) { + return 0; + } + if (fccs[3] != 'b' && fccs[3] != 'c') { + return 0; + } + + return 1; +} + +AviError AVI_print_error(AviError in_error) +{ + int error; + + if ((int)in_error < 0) { + error = -in_error; + } + else { + error = in_error; + } + + switch (error) { + case AVI_ERROR_NONE: + break; + case AVI_ERROR_COMPRESSION: + printf("AVI ERROR: compressed in an unsupported format\n"); + break; + case AVI_ERROR_OPEN: + printf("AVI ERROR: could not open file\n"); + break; + case AVI_ERROR_READING: + printf("AVI ERROR: could not read from file\n"); + break; + case AVI_ERROR_WRITING: + printf("AVI ERROR: could not write to file\n"); + break; + case AVI_ERROR_FORMAT: + printf("AVI ERROR: file is in an illegal or unrecognized format\n"); + break; + case AVI_ERROR_ALLOC: + printf("AVI ERROR: error encountered while allocating memory\n"); + break; + case AVI_ERROR_OPTION: + printf("AVI ERROR: program made illegal request\n"); + break; + case AVI_ERROR_FOUND: + printf("AVI ERROR: movie did not contain expected item\n"); + break; + default: + break; + } + + return in_error; +} + +bool AVI_is_avi(const char *name) +{ + int temp, fcca, j; + AviMovie movie = {NULL}; + AviMainHeader header; + AviBitmapInfoHeader bheader; + int movie_tracks = 0; + + DEBUG_PRINT("opening movie\n"); + + movie.type = AVI_MOVIE_READ; + movie.fp = BLI_fopen(name, "rb"); + movie.offset_table = NULL; + + if (movie.fp == NULL) { + return 0; + } + + if (GET_FCC(movie.fp) != FCC("RIFF") || !(movie.size = GET_FCC(movie.fp))) { + fclose(movie.fp); + return 0; + } + + movie.header = &header; + + if (GET_FCC(movie.fp) != FCC("AVI ") || GET_FCC(movie.fp) != FCC("LIST") || !GET_FCC(movie.fp) || + GET_FCC(movie.fp) != FCC("hdrl") || (movie.header->fcc = GET_FCC(movie.fp)) != FCC("avih") || + !(movie.header->size = GET_FCC(movie.fp))) { + DEBUG_PRINT("bad initial header info\n"); + fclose(movie.fp); + return 0; + } + + movie.header->MicroSecPerFrame = GET_FCC(movie.fp); + movie.header->MaxBytesPerSec = GET_FCC(movie.fp); + movie.header->PaddingGranularity = GET_FCC(movie.fp); + movie.header->Flags = GET_FCC(movie.fp); + movie.header->TotalFrames = GET_FCC(movie.fp); + movie.header->InitialFrames = GET_FCC(movie.fp); + movie.header->Streams = GET_FCC(movie.fp); + movie.header->SuggestedBufferSize = GET_FCC(movie.fp); + movie.header->Width = GET_FCC(movie.fp); + movie.header->Height = GET_FCC(movie.fp); + movie.header->Reserved[0] = GET_FCC(movie.fp); + movie.header->Reserved[1] = GET_FCC(movie.fp); + movie.header->Reserved[2] = GET_FCC(movie.fp); + movie.header->Reserved[3] = GET_FCC(movie.fp); + + fseek(movie.fp, movie.header->size - 14 * 4, SEEK_CUR); + + /* Limit number of streams to some reasonable amount to prevent + * buffer overflow vulnerabilities. */ + if (movie.header->Streams < 1 || movie.header->Streams > 65536) { + DEBUG_PRINT("Number of streams should be in range 1-65536\n"); + fclose(movie.fp); + return 0; + } + + movie.streams = (AviStreamRec *)MEM_calloc_arrayN( + movie.header->Streams, sizeof(AviStreamRec), "moviestreams"); + + for (temp = 0; temp < movie.header->Streams; temp++) { + + if (GET_FCC(movie.fp) != FCC("LIST") || !GET_FCC(movie.fp) || + GET_FCC(movie.fp) != FCC("strl") || + (movie.streams[temp].sh.fcc = GET_FCC(movie.fp)) != FCC("strh") || + !(movie.streams[temp].sh.size = GET_FCC(movie.fp))) { + DEBUG_PRINT("bad stream header information\n"); + + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + + movie.streams[temp].sh.Type = GET_FCC(movie.fp); + movie.streams[temp].sh.Handler = GET_FCC(movie.fp); + + fcca = movie.streams[temp].sh.Handler; + + if (movie.streams[temp].sh.Type == FCC("vids")) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + movie.streams[temp].format = AVI_FORMAT_AVI_RGB; + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie.streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + movie_tracks++; + } + + movie.streams[temp].sh.Flags = GET_FCC(movie.fp); + movie.streams[temp].sh.Priority = GET_TCC(movie.fp); + movie.streams[temp].sh.Language = GET_TCC(movie.fp); + movie.streams[temp].sh.InitialFrames = GET_FCC(movie.fp); + movie.streams[temp].sh.Scale = GET_FCC(movie.fp); + movie.streams[temp].sh.Rate = GET_FCC(movie.fp); + movie.streams[temp].sh.Start = GET_FCC(movie.fp); + movie.streams[temp].sh.Length = GET_FCC(movie.fp); + movie.streams[temp].sh.SuggestedBufferSize = GET_FCC(movie.fp); + movie.streams[temp].sh.Quality = GET_FCC(movie.fp); + movie.streams[temp].sh.SampleSize = GET_FCC(movie.fp); + movie.streams[temp].sh.left = GET_TCC(movie.fp); + movie.streams[temp].sh.top = GET_TCC(movie.fp); + movie.streams[temp].sh.right = GET_TCC(movie.fp); + movie.streams[temp].sh.bottom = GET_TCC(movie.fp); + + fseek(movie.fp, movie.streams[temp].sh.size - 14 * 4, SEEK_CUR); + + if (GET_FCC(movie.fp) != FCC("strf")) { + DEBUG_PRINT("no stream format information\n"); + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + + movie.streams[temp].sf_size = GET_FCC(movie.fp); + if (movie.streams[temp].sh.Type == FCC("vids")) { + j = movie.streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8); + if (j >= 0) { + AviBitmapInfoHeader *bi; + + movie.streams[temp].sf = &bheader; + bi = (AviBitmapInfoHeader *)movie.streams[temp].sf; + + bi->fcc = FCC("strf"); + bi->size = movie.streams[temp].sf_size; + bi->Size = GET_FCC(movie.fp); + bi->Width = GET_FCC(movie.fp); + bi->Height = GET_FCC(movie.fp); + bi->Planes = GET_TCC(movie.fp); + bi->BitCount = GET_TCC(movie.fp); + bi->Compression = GET_FCC(movie.fp); + bi->SizeImage = GET_FCC(movie.fp); + bi->XPelsPerMeter = GET_FCC(movie.fp); + bi->YPelsPerMeter = GET_FCC(movie.fp); + bi->ClrUsed = GET_FCC(movie.fp); + bi->ClrImportant = GET_FCC(movie.fp); + + fcca = bi->Compression; + + if (movie.streams[temp].format == AVI_FORMAT_AVI_RGB) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + /* pass */ + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie.streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + } + } + if (j > 0) { + fseek(movie.fp, j, SEEK_CUR); + } + } + else { + fseek(movie.fp, movie.streams[temp].sf_size, SEEK_CUR); + } + + /* Walk to the next LIST */ + while (GET_FCC(movie.fp) != FCC("LIST")) { + temp = GET_FCC(movie.fp); + if (temp < 0 || ftell(movie.fp) > movie.size) { + DEBUG_PRINT("incorrect size in header or error in AVI\n"); + + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + fseek(movie.fp, temp, SEEK_CUR); + } + + fseek(movie.fp, -4L, SEEK_CUR); + } + + MEM_freeN(movie.streams); + fclose(movie.fp); + + /* at least one video track is needed */ + return (movie_tracks != 0); +} + +AviError AVI_open_movie(const char *name, AviMovie *movie) +{ + int temp, fcca, size, j; + + DEBUG_PRINT("opening movie\n"); + + memset(movie, 0, sizeof(AviMovie)); + + movie->type = AVI_MOVIE_READ; + movie->fp = BLI_fopen(name, "rb"); + movie->offset_table = NULL; + + if (movie->fp == NULL) { + return AVI_ERROR_OPEN; + } + + if (GET_FCC(movie->fp) != FCC("RIFF") || !(movie->size = GET_FCC(movie->fp))) { + return AVI_ERROR_FORMAT; + } + + movie->header = (AviMainHeader *)MEM_mallocN(sizeof(AviMainHeader), "movieheader"); + + if (GET_FCC(movie->fp) != FCC("AVI ") || GET_FCC(movie->fp) != FCC("LIST") || + !GET_FCC(movie->fp) || GET_FCC(movie->fp) != FCC("hdrl") || + (movie->header->fcc = GET_FCC(movie->fp)) != FCC("avih") || + !(movie->header->size = GET_FCC(movie->fp))) { + DEBUG_PRINT("bad initial header info\n"); + return AVI_ERROR_FORMAT; + } + + movie->header->MicroSecPerFrame = GET_FCC(movie->fp); + movie->header->MaxBytesPerSec = GET_FCC(movie->fp); + movie->header->PaddingGranularity = GET_FCC(movie->fp); + movie->header->Flags = GET_FCC(movie->fp); + movie->header->TotalFrames = GET_FCC(movie->fp); + movie->header->InitialFrames = GET_FCC(movie->fp); + movie->header->Streams = GET_FCC(movie->fp); + movie->header->SuggestedBufferSize = GET_FCC(movie->fp); + movie->header->Width = GET_FCC(movie->fp); + movie->header->Height = GET_FCC(movie->fp); + movie->header->Reserved[0] = GET_FCC(movie->fp); + movie->header->Reserved[1] = GET_FCC(movie->fp); + movie->header->Reserved[2] = GET_FCC(movie->fp); + movie->header->Reserved[3] = GET_FCC(movie->fp); + + fseek(movie->fp, movie->header->size - 14 * 4, SEEK_CUR); + + /* Limit number of streams to some reasonable amount to prevent + * buffer overflow vulnerabilities. */ + if (movie->header->Streams < 1 || movie->header->Streams > 65536) { + DEBUG_PRINT("Number of streams should be in range 1-65536\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams = (AviStreamRec *)MEM_calloc_arrayN( + movie->header->Streams, sizeof(AviStreamRec), "moviestreams"); + + for (temp = 0; temp < movie->header->Streams; temp++) { + + if (GET_FCC(movie->fp) != FCC("LIST") || !GET_FCC(movie->fp) || + GET_FCC(movie->fp) != FCC("strl") || + (movie->streams[temp].sh.fcc = GET_FCC(movie->fp)) != FCC("strh") || + !(movie->streams[temp].sh.size = GET_FCC(movie->fp))) { + DEBUG_PRINT("bad stream header information\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams[temp].sh.Type = GET_FCC(movie->fp); + movie->streams[temp].sh.Handler = GET_FCC(movie->fp); + + fcca = movie->streams[temp].sh.Handler; + + if (movie->streams[temp].sh.Type == FCC("vids")) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + movie->streams[temp].format = AVI_FORMAT_AVI_RGB; + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie->streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + return AVI_ERROR_COMPRESSION; + } + } + + movie->streams[temp].sh.Flags = GET_FCC(movie->fp); + movie->streams[temp].sh.Priority = GET_TCC(movie->fp); + movie->streams[temp].sh.Language = GET_TCC(movie->fp); + movie->streams[temp].sh.InitialFrames = GET_FCC(movie->fp); + movie->streams[temp].sh.Scale = GET_FCC(movie->fp); + movie->streams[temp].sh.Rate = GET_FCC(movie->fp); + movie->streams[temp].sh.Start = GET_FCC(movie->fp); + movie->streams[temp].sh.Length = GET_FCC(movie->fp); + movie->streams[temp].sh.SuggestedBufferSize = GET_FCC(movie->fp); + movie->streams[temp].sh.Quality = GET_FCC(movie->fp); + movie->streams[temp].sh.SampleSize = GET_FCC(movie->fp); + movie->streams[temp].sh.left = GET_TCC(movie->fp); + movie->streams[temp].sh.top = GET_TCC(movie->fp); + movie->streams[temp].sh.right = GET_TCC(movie->fp); + movie->streams[temp].sh.bottom = GET_TCC(movie->fp); + + fseek(movie->fp, movie->streams[temp].sh.size - 14 * 4, SEEK_CUR); + + if (GET_FCC(movie->fp) != FCC("strf")) { + DEBUG_PRINT("no stream format information\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams[temp].sf_size = GET_FCC(movie->fp); + if (movie->streams[temp].sh.Type == FCC("vids")) { + j = movie->streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8); + if (j >= 0) { + AviBitmapInfoHeader *bi; + + movie->streams[temp].sf = MEM_mallocN(sizeof(AviBitmapInfoHeader), "streamformat"); + + bi = (AviBitmapInfoHeader *)movie->streams[temp].sf; + + bi->fcc = FCC("strf"); + bi->size = movie->streams[temp].sf_size; + bi->Size = GET_FCC(movie->fp); + bi->Width = GET_FCC(movie->fp); + bi->Height = GET_FCC(movie->fp); + bi->Planes = GET_TCC(movie->fp); + bi->BitCount = GET_TCC(movie->fp); + bi->Compression = GET_FCC(movie->fp); + bi->SizeImage = GET_FCC(movie->fp); + bi->XPelsPerMeter = GET_FCC(movie->fp); + bi->YPelsPerMeter = GET_FCC(movie->fp); + bi->ClrUsed = GET_FCC(movie->fp); + bi->ClrImportant = GET_FCC(movie->fp); + + fcca = bi->Compression; + + if (movie->streams[temp].format == AVI_FORMAT_AVI_RGB) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + /* pass */ + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie->streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + return AVI_ERROR_COMPRESSION; + } + } + } + if (j > 0) { + fseek(movie->fp, j, SEEK_CUR); + } + } + else { + fseek(movie->fp, movie->streams[temp].sf_size, SEEK_CUR); + } + + /* Walk to the next LIST */ + while (GET_FCC(movie->fp) != FCC("LIST")) { + temp = GET_FCC(movie->fp); + if (temp < 0 || ftell(movie->fp) > movie->size) { + DEBUG_PRINT("incorrect size in header or error in AVI\n"); + return AVI_ERROR_FORMAT; + } + fseek(movie->fp, temp, SEEK_CUR); + } + + fseek(movie->fp, -4L, SEEK_CUR); + } + + while (1) { + temp = GET_FCC(movie->fp); + size = GET_FCC(movie->fp); + + if (size == 0) { + break; + } + + if (temp == FCC("LIST")) { + if (GET_FCC(movie->fp) == FCC("movi")) { + break; + } + else { + fseek(movie->fp, size - 4, SEEK_CUR); + } + } + else { + fseek(movie->fp, size, SEEK_CUR); + } + if (ftell(movie->fp) > movie->size) { + DEBUG_PRINT("incorrect size in header or error in AVI\n"); + return AVI_ERROR_FORMAT; + } + } + + movie->movi_offset = ftell(movie->fp); + movie->read_offset = movie->movi_offset; + + /* Read in the index if the file has one, otherwise create one */ + if (movie->header->Flags & AVIF_HASINDEX) { + fseek(movie->fp, size - 4, SEEK_CUR); + + if (GET_FCC(movie->fp) != FCC("idx1")) { + DEBUG_PRINT("bad index informatio\n"); + return AVI_ERROR_FORMAT; + } + + movie->index_entries = GET_FCC(movie->fp) / sizeof(AviIndexEntry); + if (movie->index_entries == 0) { + DEBUG_PRINT("no index entries\n"); + return AVI_ERROR_FORMAT; + } + + movie->entries = (AviIndexEntry *)MEM_mallocN(movie->index_entries * sizeof(AviIndexEntry), + "movieentries"); + + for (temp = 0; temp < movie->index_entries; temp++) { + movie->entries[temp].ChunkId = GET_FCC(movie->fp); + movie->entries[temp].Flags = GET_FCC(movie->fp); + movie->entries[temp].Offset = GET_FCC(movie->fp); + movie->entries[temp].Size = GET_FCC(movie->fp); + + if (AVI_DEBUG) { + printf("Index entry %04d: ChunkId:%s Flags:%d Offset:%d Size:%d\n", + temp, + fcc_to_char(movie->entries[temp].ChunkId), + movie->entries[temp].Flags, + movie->entries[temp].Offset, + movie->entries[temp].Size); + } + } + + /* Some AVI's have offset entries in absolute coordinates + * instead of an offset from the movie beginning... this is... + * wacky, but we need to handle it. The wacky offset always + * starts at movi_offset it seems... so we'll check that. + * Note the offset needs an extra 4 bytes for some + * undetermined reason */ + + if (movie->entries[0].Offset == movie->movi_offset) { + movie->read_offset = 4; + } + } + + DEBUG_PRINT("movie successfully opened\n"); + return AVI_ERROR_NONE; +} + +void *AVI_read_frame(AviMovie *movie, AviFormat format, int frame, int stream) +{ + int cur_frame = -1, i = 0, rewind = 1; + void *buffer; + + /* Retrieve the record number of the desired frame in the index + * If a chunk has Size 0 we need to rewind to previous frame */ + while (rewind && frame > -1) { + i = 0; + cur_frame = -1; + rewind = 0; + + while (cur_frame < frame && i < movie->index_entries) { + if (fcc_is_data(movie->entries[i].ChunkId) && + fcc_get_stream(movie->entries[i].ChunkId) == stream) { + if ((cur_frame == frame - 1) && (movie->entries[i].Size == 0)) { + rewind = 1; + frame = frame - 1; + } + else { + cur_frame++; + } + } + i++; + } + } + + if (cur_frame != frame) { + return NULL; + } + + fseek(movie->fp, movie->read_offset + movie->entries[i - 1].Offset, SEEK_SET); + + size_t size = GET_FCC(movie->fp); + buffer = MEM_mallocN(size, "readbuffer"); + + if (fread(buffer, 1, size, movie->fp) != size) { + MEM_freeN(buffer); + + return NULL; + } + + buffer = avi_format_convert(movie, stream, buffer, movie->streams[stream].format, format, &size); + + return buffer; +} + +AviError AVI_close(AviMovie *movie) +{ + int i; + + fclose(movie->fp); + + for (i = 0; i < movie->header->Streams; i++) { + if (movie->streams[i].sf != NULL) { + MEM_freeN(movie->streams[i].sf); + } + } + + MEM_freeN(movie->header); + MEM_freeN(movie->streams); + + if (movie->entries != NULL) { + MEM_freeN(movie->entries); + } + if (movie->offset_table != NULL) { + MEM_freeN(movie->offset_table); + } + + return AVI_ERROR_NONE; +} + +AviError AVI_open_compress(char *name, AviMovie *movie, int streams, ...) +{ + va_list ap; + AviList list; + AviChunk chunk; + int i; + int64_t header_pos1, header_pos2; + int64_t stream_pos1, stream_pos2; + int64_t junk_pos; + + movie->type = AVI_MOVIE_WRITE; + movie->fp = BLI_fopen(name, "wb"); + + movie->index_entries = 0; + + if (movie->fp == NULL) { + return AVI_ERROR_OPEN; + } + + movie->offset_table = (int64_t *)MEM_mallocN((1 + streams * 2) * sizeof(int64_t), "offsettable"); + + for (i = 0; i < 1 + streams * 2; i++) { + movie->offset_table[i] = -1L; + } + + movie->entries = NULL; + + movie->header = (AviMainHeader *)MEM_mallocN(sizeof(AviMainHeader), "movieheader"); + + movie->header->fcc = FCC("avih"); + movie->header->size = 56; + movie->header->MicroSecPerFrame = 66667; + movie->header->MaxBytesPerSec = 0; + movie->header->PaddingGranularity = 0; + movie->header->Flags = AVIF_HASINDEX | AVIF_MUSTUSEINDEX; + movie->header->TotalFrames = 0; + movie->header->InitialFrames = 0; + movie->header->Streams = streams; + movie->header->SuggestedBufferSize = 0; + movie->header->Width = 0; + movie->header->Height = 0; + movie->header->Reserved[0] = 0; + movie->header->Reserved[1] = 0; + movie->header->Reserved[2] = 0; + movie->header->Reserved[3] = 0; + + /* Limit number of streams to some reasonable amount to prevent + * buffer overflow vulnerabilities. */ + if (movie->header->Streams < 0 || movie->header->Streams > 65536) { + DEBUG_PRINT("Number of streams should be in range 0-65536\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams = (AviStreamRec *)MEM_mallocN(sizeof(AviStreamRec) * movie->header->Streams, + "moviestreams"); + + va_start(ap, streams); + + for (i = 0; i < movie->header->Streams; i++) { + movie->streams[i].format = va_arg(ap, AviFormat); + + movie->streams[i].sh.fcc = FCC("strh"); + movie->streams[i].sh.size = 56; + movie->streams[i].sh.Type = avi_get_format_type(movie->streams[i].format); + if (movie->streams[i].sh.Type == 0) { + va_end(ap); + return AVI_ERROR_FORMAT; + } + + movie->streams[i].sh.Handler = avi_get_format_fcc(movie->streams[i].format); + if (movie->streams[i].sh.Handler == 0) { + va_end(ap); + return AVI_ERROR_FORMAT; + } + + movie->streams[i].sh.Flags = 0; + movie->streams[i].sh.Priority = 0; + movie->streams[i].sh.Language = 0; + movie->streams[i].sh.InitialFrames = 0; + movie->streams[i].sh.Scale = 66667; + movie->streams[i].sh.Rate = 1000000; + movie->streams[i].sh.Start = 0; + movie->streams[i].sh.Length = 0; + movie->streams[i].sh.SuggestedBufferSize = 0; + movie->streams[i].sh.Quality = 10000; + movie->streams[i].sh.SampleSize = 0; + movie->streams[i].sh.left = 0; + movie->streams[i].sh.top = 0; + movie->streams[i].sh.right = 0; + movie->streams[i].sh.bottom = 0; + + if (movie->streams[i].sh.Type == FCC("vids")) { + movie->streams[i].sf = MEM_mallocN(sizeof(AviBitmapInfoHeader), "moviestreamformatS"); + movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader); + + ((AviBitmapInfoHeader *)movie->streams[i].sf)->fcc = FCC("strf"); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->size = movie->streams[i].sf_size - 8; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Size = movie->streams[i].sf_size - 8; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Width = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Height = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Planes = 1; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->BitCount = 24; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Compression = avi_get_format_compression( + movie->streams[i].format); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->XPelsPerMeter = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->YPelsPerMeter = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->ClrUsed = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->ClrImportant = 0; + } + } + + list.fcc = FCC("RIFF"); + list.size = 0; + list.ids = FCC("AVI "); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("hdrl"); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + header_pos1 = ftell(movie->fp); + + movie->offset_table[0] = ftell(movie->fp); + + awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); + + for (i = 0; i < movie->header->Streams; i++) { + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("strl"); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + stream_pos1 = ftell(movie->fp); + + movie->offset_table[1 + i * 2] = ftell(movie->fp); + awrite(movie, &movie->streams[i].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH); + + movie->offset_table[1 + i * 2 + 1] = ftell(movie->fp); + awrite(movie, movie->streams[i].sf, 1, movie->streams[i].sf_size, movie->fp, AVI_BITMAPH); + + stream_pos2 = ftell(movie->fp); + + fseek(movie->fp, stream_pos1 - 8, SEEK_SET); + + PUT_FCCN((stream_pos2 - stream_pos1 + 4L), movie->fp); + + fseek(movie->fp, stream_pos2, SEEK_SET); + } + + junk_pos = ftell(movie->fp); + + if (junk_pos < 2024 - 8) { + chunk.fcc = FCC("JUNK"); + chunk.size = 2024 - 8 - (int)junk_pos; + + awrite(movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK); + + for (i = 0; i < chunk.size; i++) { + putc(0, movie->fp); + } + } + + header_pos2 = ftell(movie->fp); + + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("movi"); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + movie->movi_offset = ftell(movie->fp) - 8L; + + fseek(movie->fp, AVI_HDRL_SOFF, SEEK_SET); + + PUT_FCCN((header_pos2 - header_pos1 + 4L), movie->fp); + + va_end(ap); + + return AVI_ERROR_NONE; +} + +AviError AVI_write_frame(AviMovie *movie, int frame_num, ...) +{ + AviList list; + AviChunk chunk; + va_list ap; + int stream; + int64_t rec_off; + AviFormat format; + void *buffer; + + if (frame_num < 0) { + return AVI_ERROR_OPTION; + } + + /* Allocate the new memory for the index entry */ + + if (frame_num >= movie->index_entries) { + const size_t entry_size = (movie->header->Streams + 1) * sizeof(AviIndexEntry); + movie->entries = (AviIndexEntry *)MEM_recallocN(movie->entries, (frame_num + 1) * entry_size); + movie->index_entries = frame_num + 1; + } + + /* Slap a new record entry onto the end of the file */ + + fseek(movie->fp, 0L, SEEK_END); + + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("rec "); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + rec_off = ftell(movie->fp) - 8L; + + /* Write a frame for every stream */ + + va_start(ap, frame_num); + + for (stream = 0; stream < movie->header->Streams; stream++) { + unsigned int tbuf = 0; + + format = va_arg(ap, AviFormat); + buffer = va_arg(ap, void *); + size_t size = va_arg(ap, int); + + /* Convert the buffer into the output format */ + buffer = avi_format_convert( + movie, stream, buffer, format, movie->streams[stream].format, &size); + + /* Write the header info for this data chunk */ + + fseek(movie->fp, 0L, SEEK_END); + + chunk.fcc = avi_get_data_id(format, stream); + chunk.size = size; + + if (size % 4) { + chunk.size += 4 - size % 4; + } + + awrite(movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK); + + /* Write the index entry for this data chunk */ + + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].ChunkId = chunk.fcc; + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Flags = AVIIF_KEYFRAME; + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Offset = + (int)(ftell(movie->fp) - 12L - movie->movi_offset); + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Size = chunk.size; + + /* Write the chunk */ + awrite(movie, buffer, 1, size, movie->fp, AVI_RAW); + MEM_freeN(buffer); + + if (size % 4) { + awrite(movie, &tbuf, 1, 4 - size % 4, movie->fp, AVI_RAW); + } + + /* Update the stream headers length field */ + movie->streams[stream].sh.Length++; + fseek(movie->fp, movie->offset_table[1 + stream * 2], SEEK_SET); + awrite(movie, &movie->streams[stream].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH); + } + va_end(ap); + + /* Record the entry for the new record */ + + fseek(movie->fp, 0L, SEEK_END); + + movie->entries[frame_num * (movie->header->Streams + 1)].ChunkId = FCC("rec "); + movie->entries[frame_num * (movie->header->Streams + 1)].Flags = AVIIF_LIST; + movie->entries[frame_num * (movie->header->Streams + 1)].Offset = (int)(rec_off - 8L - + movie->movi_offset); + movie->entries[frame_num * (movie->header->Streams + 1)].Size = (int)(ftell(movie->fp) - + (rec_off + 4L)); + + /* Update the record size */ + fseek(movie->fp, rec_off, SEEK_SET); + PUT_FCCN(movie->entries[frame_num * (movie->header->Streams + 1)].Size, movie->fp); + + /* Update the main header information in the file */ + movie->header->TotalFrames++; + fseek(movie->fp, movie->offset_table[0], SEEK_SET); + awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); + + return AVI_ERROR_NONE; +} + +AviError AVI_close_compress(AviMovie *movie) +{ + int temp, movi_size, i; + + if (movie->fp == NULL) { + /* none of the allocations below were done if the file failed to open */ + return AVI_ERROR_FOUND; + } + + fseek(movie->fp, 0L, SEEK_END); + movi_size = (int)ftell(movie->fp); + + PUT_FCC("idx1", movie->fp); + PUT_FCCN((movie->index_entries * (movie->header->Streams + 1) * 16), movie->fp); + + for (temp = 0; temp < movie->index_entries * (movie->header->Streams + 1); temp++) { + awrite(movie, &movie->entries[temp], 1, sizeof(AviIndexEntry), movie->fp, AVI_INDEXE); + } + + temp = (int)ftell(movie->fp); + + fseek(movie->fp, AVI_RIFF_SOFF, SEEK_SET); + + PUT_FCCN((temp - 8L), movie->fp); + + fseek(movie->fp, movie->movi_offset, SEEK_SET); + + PUT_FCCN((movi_size - (movie->movi_offset + 4L)), movie->fp); + + fclose(movie->fp); + + for (i = 0; i < movie->header->Streams; i++) { + if (movie->streams && (movie->streams[i].sf != NULL)) { + MEM_freeN(movie->streams[i].sf); + } + } + + MEM_freeN(movie->header); + + if (movie->entries != NULL) { + MEM_freeN(movie->entries); + } + if (movie->streams != NULL) { + MEM_freeN(movie->streams); + } + if (movie->offset_table != NULL) { + MEM_freeN(movie->offset_table); + } + return AVI_ERROR_NONE; +} diff --git a/source/blender/io/avi/intern/avi_codecs.c b/source/blender/io/avi/intern/avi_codecs.c new file mode 100644 index 00000000000..15f498ac653 --- /dev/null +++ b/source/blender/io/avi/intern/avi_codecs.c @@ -0,0 +1,138 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Identify and convert different avi-files. + */ + +#include "AVI_avi.h" +#include "avi_intern.h" + +#include "avi_rgb.h" +#include "avi_mjpeg.h" +#include "avi_rgb32.h" + +void *avi_format_convert( + AviMovie *movie, int stream, void *buffer, AviFormat from, AviFormat to, size_t *size) +{ + if (from == to) { + return buffer; + } + + if (from != AVI_FORMAT_RGB24 && to != AVI_FORMAT_RGB24) { + return avi_format_convert( + movie, + stream, + avi_format_convert(movie, stream, buffer, from, AVI_FORMAT_RGB24, size), + AVI_FORMAT_RGB24, + to, + size); + } + + switch (to) { + case AVI_FORMAT_RGB24: + switch (from) { + case AVI_FORMAT_AVI_RGB: + buffer = avi_converter_from_avi_rgb(movie, stream, buffer, size); + break; + case AVI_FORMAT_MJPEG: + buffer = avi_converter_from_mjpeg(movie, stream, buffer, size); + break; + case AVI_FORMAT_RGB32: + buffer = avi_converter_from_rgb32(movie, stream, buffer, size); + break; + default: + break; + } + break; + case AVI_FORMAT_AVI_RGB: + buffer = avi_converter_to_avi_rgb(movie, stream, buffer, size); + break; + case AVI_FORMAT_MJPEG: + buffer = avi_converter_to_mjpeg(movie, stream, buffer, size); + break; + case AVI_FORMAT_RGB32: + buffer = avi_converter_to_rgb32(movie, stream, buffer, size); + break; + default: + break; + } + + return buffer; +} + +int avi_get_data_id(AviFormat format, int stream) +{ + char fcc[5]; + + if (avi_get_format_type(format) == FCC("vids")) { + sprintf(fcc, "%2.2ddc", stream); + } + else if (avi_get_format_type(format) == FCC("auds")) { + sprintf(fcc, "%2.2ddc", stream); + } + else { + return 0; + } + + return FCC(fcc); +} + +int avi_get_format_type(AviFormat format) +{ + switch (format) { + case AVI_FORMAT_RGB24: + case AVI_FORMAT_RGB32: + case AVI_FORMAT_AVI_RGB: + case AVI_FORMAT_MJPEG: + return FCC("vids"); + default: + return 0; + } +} + +int avi_get_format_fcc(AviFormat format) +{ + switch (format) { + case AVI_FORMAT_RGB24: + case AVI_FORMAT_RGB32: + case AVI_FORMAT_AVI_RGB: + return FCC("DIB "); + case AVI_FORMAT_MJPEG: + return FCC("MJPG"); + default: + return 0; + } +} + +int avi_get_format_compression(AviFormat format) +{ + switch (format) { + case AVI_FORMAT_RGB24: + case AVI_FORMAT_RGB32: + case AVI_FORMAT_AVI_RGB: + return 0; + case AVI_FORMAT_MJPEG: + return FCC("MJPG"); + default: + return 0; + } +} diff --git a/source/blender/io/avi/intern/avi_endian.c b/source/blender/io/avi/intern/avi_endian.c new file mode 100644 index 00000000000..56474e9e329 --- /dev/null +++ b/source/blender/io/avi/intern/avi_endian.c @@ -0,0 +1,203 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Streams bytes to output depending on the + * endianness of the system. + */ + +#include +#include +#include + +#include "AVI_avi.h" +#include "avi_endian.h" +#include "avi_intern.h" + +#ifdef __BIG_ENDIAN__ +# include "MEM_guardedalloc.h" +#endif + +#ifdef __BIG_ENDIAN__ + +/* copied from BLI_endian_switch_inline.h */ +static void invert(int *val) +{ + int tval = *val; + *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); +} + +static void sinvert(short int *val) +{ + short tval = *val; + *val = (tval >> 8) | (tval << 8); +} + +static void Ichunk(AviChunk *chunk) +{ + invert(&chunk->fcc); + invert(&chunk->size); +} +#endif + +#ifdef __BIG_ENDIAN__ +static void Ilist(AviList *list) +{ + invert(&list->fcc); + invert(&list->size); + invert(&list->ids); +} + +static void Imainh(AviMainHeader *mainh) +{ + invert(&mainh->fcc); + invert(&mainh->size); + invert(&mainh->MicroSecPerFrame); + invert(&mainh->MaxBytesPerSec); + invert(&mainh->PaddingGranularity); + invert(&mainh->Flags); + invert(&mainh->TotalFrames); + invert(&mainh->InitialFrames); + invert(&mainh->Streams); + invert(&mainh->SuggestedBufferSize); + invert(&mainh->Width); + invert(&mainh->Height); + invert(&mainh->Reserved[0]); + invert(&mainh->Reserved[1]); + invert(&mainh->Reserved[2]); + invert(&mainh->Reserved[3]); +} + +static void Istreamh(AviStreamHeader *streamh) +{ + invert(&streamh->fcc); + invert(&streamh->size); + invert(&streamh->Type); + invert(&streamh->Handler); + invert(&streamh->Flags); + sinvert(&streamh->Priority); + sinvert(&streamh->Language); + invert(&streamh->InitialFrames); + invert(&streamh->Scale); + invert(&streamh->Rate); + invert(&streamh->Start); + invert(&streamh->Length); + invert(&streamh->SuggestedBufferSize); + invert(&streamh->Quality); + invert(&streamh->SampleSize); + sinvert(&streamh->left); + sinvert(&streamh->right); + sinvert(&streamh->top); + sinvert(&streamh->bottom); +} + +static void Ibitmaph(AviBitmapInfoHeader *bitmaph) +{ + invert(&bitmaph->fcc); + invert(&bitmaph->size); + invert(&bitmaph->Size); + invert(&bitmaph->Width); + invert(&bitmaph->Height); + sinvert(&bitmaph->Planes); + sinvert(&bitmaph->BitCount); + invert(&bitmaph->Compression); + invert(&bitmaph->SizeImage); + invert(&bitmaph->XPelsPerMeter); + invert(&bitmaph->YPelsPerMeter); + invert(&bitmaph->ClrUsed); + invert(&bitmaph->ClrImportant); +} + +static void Imjpegu(AviMJPEGUnknown *mjpgu) +{ + invert(&mjpgu->a); + invert(&mjpgu->b); + invert(&mjpgu->c); + invert(&mjpgu->d); + invert(&mjpgu->e); + invert(&mjpgu->f); + invert(&mjpgu->g); +} + +static void Iindexe(AviIndexEntry *indexe) +{ + invert(&indexe->ChunkId); + invert(&indexe->Flags); + invert(&indexe->Offset); + invert(&indexe->Size); +} +#endif /* __BIG_ENDIAN__ */ + +void awrite(AviMovie *movie, void *datain, int block, int size, FILE *fp, int type) +{ +#ifdef __BIG_ENDIAN__ + void *data; + + data = MEM_mallocN(size, "avi endian"); + + memcpy(data, datain, size); + + switch (type) { + case AVI_RAW: + fwrite(data, block, size, fp); + break; + case AVI_CHUNK: + Ichunk((AviChunk *)data); + fwrite(data, block, size, fp); + break; + case AVI_LIST: + Ilist((AviList *)data); + fwrite(data, block, size, fp); + break; + case AVI_MAINH: + Imainh((AviMainHeader *)data); + fwrite(data, block, size, fp); + break; + case AVI_STREAMH: + Istreamh((AviStreamHeader *)data); + fwrite(data, block, size, fp); + break; + case AVI_BITMAPH: + Ibitmaph((AviBitmapInfoHeader *)data); + if (size == sizeof(AviBitmapInfoHeader) + sizeof(AviMJPEGUnknown)) { + Imjpegu((AviMJPEGUnknown *)((char *)data + sizeof(AviBitmapInfoHeader))); + } + fwrite(data, block, size, fp); + break; + case AVI_MJPEGU: + Imjpegu((AviMJPEGUnknown *)data); + fwrite(data, block, size, fp); + break; + case AVI_INDEXE: + Iindexe((AviIndexEntry *)data); + fwrite(data, block, size, fp); + break; + default: + break; + } + + MEM_freeN(data); +#else /* __BIG_ENDIAN__ */ + (void)movie; /* unused */ + (void)type; /* unused */ + fwrite(datain, block, size, fp); +#endif /* __BIG_ENDIAN__ */ +} diff --git a/source/blender/io/avi/intern/avi_endian.h b/source/blender/io/avi/intern/avi_endian.h new file mode 100644 index 00000000000..d1253f488e7 --- /dev/null +++ b/source/blender/io/avi/intern/avi_endian.h @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. + */ + +#ifndef __AVI_ENDIAN_H__ +#define __AVI_ENDIAN_H__ + +#define AVI_RAW 0 +#define AVI_CHUNK 1 +#define AVI_LIST 2 +#define AVI_MAINH 3 +#define AVI_STREAMH 4 +#define AVI_BITMAPH 5 +#define AVI_INDEXE 6 +#define AVI_MJPEGU 7 + +void awrite(AviMovie *movie, void *datain, int block, int size, FILE *fp, int type); + +#endif /* __AVI_ENDIAN_H__ */ diff --git a/source/blender/io/avi/intern/avi_intern.h b/source/blender/io/avi/intern/avi_intern.h new file mode 100644 index 00000000000..6ce91ce7f70 --- /dev/null +++ b/source/blender/io/avi/intern/avi_intern.h @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_INTERN_H__ +#define __AVI_INTERN_H__ + +#include /* for FILE */ + +unsigned int GET_FCC(FILE *fp); +unsigned int GET_TCC(FILE *fp); + +#define PUT_FCC(ch4, fp) \ + { \ + putc(ch4[0], fp); \ + putc(ch4[1], fp); \ + putc(ch4[2], fp); \ + putc(ch4[3], fp); \ + } \ + (void)0 + +#define PUT_FCCN(num, fp) \ + { \ + putc((num >> 0) & 0377, fp); \ + putc((num >> 8) & 0377, fp); \ + putc((num >> 16) & 0377, fp); \ + putc((num >> 24) & 0377, fp); \ + } \ + (void)0 + +#define PUT_TCC(ch2, fp) \ + { \ + putc(ch2[0], fp); \ + putc(ch2[1], fp); \ + } \ + (void)0 + +void *avi_format_convert( + AviMovie *movie, int stream, void *buffer, AviFormat from, AviFormat to, size_t *size); + +int avi_get_data_id(AviFormat format, int stream); +int avi_get_format_type(AviFormat format); +int avi_get_format_fcc(AviFormat format); +int avi_get_format_compression(AviFormat format); + +#endif diff --git a/source/blender/io/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c new file mode 100644 index 00000000000..d4c7378964e --- /dev/null +++ b/source/blender/io/avi/intern/avi_mjpeg.c @@ -0,0 +1,547 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Converts between avi and mpeg/jpeg. + */ + +#include +#include + +#include "AVI_avi.h" + +#include "MEM_guardedalloc.h" + +#include "IMB_imbuf.h" + +#include "jpeglib.h" +#include "jerror.h" + +#include "avi_mjpeg.h" + +static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize); +static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize); + +static size_t numbytes; + +static void add_huff_table(j_decompress_ptr dinfo, + JHUFF_TBL **htblptr, + const UINT8 *bits, + const UINT8 *val) +{ + if (*htblptr == NULL) { + *htblptr = jpeg_alloc_huff_table((j_common_ptr)dinfo); + } + + memcpy((*htblptr)->bits, bits, sizeof((*htblptr)->bits)); + memcpy((*htblptr)->huffval, val, sizeof((*htblptr)->huffval)); + + /* Initialize sent_table false so table will be written to JPEG file. */ + (*htblptr)->sent_table = false; +} + +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ + +static void std_huff_tables(j_decompress_ptr dinfo) +{ + static const UINT8 bits_dc_luminance[17] = { + /* 0-base */ + 0, + 0, + 1, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }; + static const UINT8 val_dc_luminance[] = { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + }; + + static const UINT8 bits_dc_chrominance[17] = { + /* 0-base */ + 0, + 0, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + }; + static const UINT8 val_dc_chrominance[] = { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + }; + + static const UINT8 bits_ac_luminance[17] = { + /* 0-base */ + 0, + 0, + 2, + 1, + 3, + 3, + 2, + 4, + 3, + 5, + 5, + 4, + 4, + 0, + 0, + 1, + 0x7d, + }; + static const UINT8 val_ac_luminance[] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, + 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, + }; + static const UINT8 bits_ac_chrominance[17] = { + /* 0-base */ + 0, + 0, + 2, + 1, + 2, + 4, + 4, + 3, + 4, + 7, + 5, + 4, + 4, + 0, + 1, + 2, + 0x77, + }; + static const UINT8 val_ac_chrominance[] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, + 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, + 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, + }; + + add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[0], bits_dc_luminance, val_dc_luminance); + add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[0], bits_ac_luminance, val_ac_luminance); + add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[1], bits_dc_chrominance, val_dc_chrominance); + add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[1], bits_ac_chrominance, val_ac_chrominance); +} + +static int Decode_JPEG(unsigned char *inBuffer, + unsigned char *outBuffer, + unsigned int width, + unsigned int height, + size_t bufsize) +{ + struct jpeg_decompress_struct dinfo; + struct jpeg_error_mgr jerr; + + (void)width; /* unused */ + + numbytes = 0; + + dinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&dinfo); + jpegmemsrcmgr_build(&dinfo, inBuffer, bufsize); + jpeg_read_header(&dinfo, true); + if (dinfo.dc_huff_tbl_ptrs[0] == NULL) { + std_huff_tables(&dinfo); + } + dinfo.out_color_space = JCS_RGB; + dinfo.dct_method = JDCT_IFAST; + + jpeg_start_decompress(&dinfo); + + size_t rowstride = dinfo.output_width * dinfo.output_components; + for (size_t y = 0; y < dinfo.output_height; y++) { + jpeg_read_scanlines(&dinfo, (JSAMPARRAY)&outBuffer, 1); + outBuffer += rowstride; + } + jpeg_finish_decompress(&dinfo); + + if (dinfo.output_height >= height) { + return 0; + } + + inBuffer += numbytes; + jpegmemsrcmgr_build(&dinfo, inBuffer, bufsize - numbytes); + + numbytes = 0; + jpeg_read_header(&dinfo, true); + if (dinfo.dc_huff_tbl_ptrs[0] == NULL) { + std_huff_tables(&dinfo); + } + + jpeg_start_decompress(&dinfo); + rowstride = dinfo.output_width * dinfo.output_components; + for (size_t y = 0; y < dinfo.output_height; y++) { + jpeg_read_scanlines(&dinfo, (JSAMPARRAY)&outBuffer, 1); + outBuffer += rowstride; + } + jpeg_finish_decompress(&dinfo); + jpeg_destroy_decompress(&dinfo); + + return 1; +} + +static void Compress_JPEG(int quality, + unsigned char *outbuffer, + const unsigned char *inBuffer, + int width, + int height, + size_t bufsize) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char marker[60]; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpegmemdestmgr_build(&cinfo, outbuffer, bufsize); + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + jpeg_set_quality(&cinfo, quality, true); + + cinfo.dc_huff_tbl_ptrs[0]->sent_table = true; + cinfo.dc_huff_tbl_ptrs[1]->sent_table = true; + cinfo.ac_huff_tbl_ptrs[0]->sent_table = true; + cinfo.ac_huff_tbl_ptrs[1]->sent_table = true; + + cinfo.comp_info[0].component_id = 0; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].component_id = 1; + cinfo.comp_info[2].component_id = 2; + + cinfo.write_JFIF_header = false; + + jpeg_start_compress(&cinfo, false); + + int i = 0; + marker[i++] = 'A'; + marker[i++] = 'V'; + marker[i++] = 'I'; + marker[i++] = '1'; + marker[i++] = 0; + while (i < 60) { + marker[i++] = 32; + } + + jpeg_write_marker(&cinfo, JPEG_APP0, marker, 60); + + i = 0; + while (i < 60) { + marker[i++] = 0; + } + + jpeg_write_marker(&cinfo, JPEG_COM, marker, 60); + + size_t rowstride = cinfo.image_width * cinfo.input_components; + for (size_t y = 0; y < cinfo.image_height; y++) { + jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&inBuffer, 1); + inBuffer += rowstride; + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); +} + +static void interlace(unsigned char *to, unsigned char *from, int width, int height) +{ + size_t i, rowstride = width * 3; + + for (i = 0; i < height; i++) { + if (i & 1) { + memcpy(&to[i * rowstride], &from[(i / 2 + height / 2) * rowstride], rowstride); + } + else { + memcpy(&to[i * rowstride], &from[(i / 2) * rowstride], rowstride); + } + } +} + +static void deinterlace(int odd, unsigned char *to, unsigned char *from, int width, int height) +{ + size_t i, rowstride = width * 3; + + for (i = 0; i < height; i++) { + if ((i & 1) == odd) { + memcpy(&to[(i / 2 + height / 2) * rowstride], &from[i * rowstride], rowstride); + } + else { + memcpy(&to[(i / 2) * rowstride], &from[i * rowstride], rowstride); + } + } +} + +void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + int deint; + unsigned char *buf; + + (void)stream; /* unused */ + + buf = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_from_mjpeg 1"); + if (!buf) { + return NULL; + } + + deint = Decode_JPEG(buffer, buf, movie->header->Width, movie->header->Height, *size); + + MEM_freeN(buffer); + + if (deint) { + buffer = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_from_mjpeg 2"); + if (buffer) { + interlace(buffer, buf, movie->header->Width, movie->header->Height); + } + MEM_freeN(buf); + + buf = buffer; + } + + return buf; +} + +void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + size_t bufsize = *size; + + numbytes = 0; + *size = 0; + + buf = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_to_mjpeg 1"); + if (!buf) { + return NULL; + } + + if (!movie->interlace) { + Compress_JPEG(movie->streams[stream].sh.Quality / 100, + buf, + buffer, + movie->header->Width, + movie->header->Height, + bufsize); + *size += numbytes; + } + else { + deinterlace(movie->odd_fields, buf, buffer, movie->header->Width, movie->header->Height); + MEM_freeN(buffer); + + buffer = buf; + buf = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_to_mjpeg 1"); + + if (buf) { + Compress_JPEG(movie->streams[stream].sh.Quality / 100, + buf, + buffer, + movie->header->Width, + movie->header->Height / 2, + bufsize / 2); + *size += numbytes; + numbytes = 0; + Compress_JPEG(movie->streams[stream].sh.Quality / 100, + buf + *size, + buffer + + (size_t)(movie->header->Height / 2) * (size_t)movie->header->Width * 3, + movie->header->Width, + movie->header->Height / 2, + bufsize / 2); + *size += numbytes; + } + } + + MEM_freeN(buffer); + return buf; +} + +/* Compression from memory */ + +static void jpegmemdestmgr_init_destination(j_compress_ptr cinfo) +{ + (void)cinfo; /* unused */ +} + +static boolean jpegmemdestmgr_empty_output_buffer(j_compress_ptr cinfo) +{ + (void)cinfo; /* unused */ + return true; +} + +static void jpegmemdestmgr_term_destination(j_compress_ptr cinfo) +{ + numbytes -= cinfo->dest->free_in_buffer; + + MEM_freeN(cinfo->dest); +} + +static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize) +{ + cinfo->dest = MEM_mallocN(sizeof(*(cinfo->dest)), "avi.jpegmemdestmgr_build"); + + cinfo->dest->init_destination = jpegmemdestmgr_init_destination; + cinfo->dest->empty_output_buffer = jpegmemdestmgr_empty_output_buffer; + cinfo->dest->term_destination = jpegmemdestmgr_term_destination; + + cinfo->dest->next_output_byte = buffer; + cinfo->dest->free_in_buffer = bufsize; + + numbytes = bufsize; +} + +/* Decompression from memory */ + +static void jpegmemsrcmgr_init_source(j_decompress_ptr dinfo) +{ + (void)dinfo; +} + +static boolean jpegmemsrcmgr_fill_input_buffer(j_decompress_ptr dinfo) +{ + unsigned char *buf = (unsigned char *)dinfo->src->next_input_byte - 2; + + /* if we get called, must have run out of data */ + WARNMS(dinfo, JWRN_JPEG_EOF); + + buf[0] = (JOCTET)0xFF; + buf[1] = (JOCTET)JPEG_EOI; + + dinfo->src->next_input_byte = buf; + dinfo->src->bytes_in_buffer = 2; + + return true; +} + +static void jpegmemsrcmgr_skip_input_data(j_decompress_ptr dinfo, long skipcnt) +{ + if (dinfo->src->bytes_in_buffer < skipcnt) { + skipcnt = dinfo->src->bytes_in_buffer; + } + + dinfo->src->next_input_byte += skipcnt; + dinfo->src->bytes_in_buffer -= skipcnt; +} + +static void jpegmemsrcmgr_term_source(j_decompress_ptr dinfo) +{ + numbytes -= dinfo->src->bytes_in_buffer; + + MEM_freeN(dinfo->src); +} + +static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize) +{ + dinfo->src = MEM_mallocN(sizeof(*(dinfo->src)), "avi.jpegmemsrcmgr_build"); + + dinfo->src->init_source = jpegmemsrcmgr_init_source; + dinfo->src->fill_input_buffer = jpegmemsrcmgr_fill_input_buffer; + dinfo->src->skip_input_data = jpegmemsrcmgr_skip_input_data; + dinfo->src->resync_to_restart = jpeg_resync_to_restart; + dinfo->src->term_source = jpegmemsrcmgr_term_source; + + dinfo->src->bytes_in_buffer = bufsize; + dinfo->src->next_input_byte = buffer; + + numbytes = bufsize; +} diff --git a/source/blender/io/avi/intern/avi_mjpeg.h b/source/blender/io/avi/intern/avi_mjpeg.h new file mode 100644 index 00000000000..30e46bf1d0c --- /dev/null +++ b/source/blender/io/avi/intern/avi_mjpeg.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_MJPEG_H__ +#define __AVI_MJPEG_H__ + +void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); + +#endif /* __AVI_MJPEG_H__ */ diff --git a/source/blender/io/avi/intern/avi_options.c b/source/blender/io/avi/intern/avi_options.c new file mode 100644 index 00000000000..65db8c19397 --- /dev/null +++ b/source/blender/io/avi/intern/avi_options.c @@ -0,0 +1,148 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Sets some compression related options + * (width, height quality, framerate). + */ + +#include "AVI_avi.h" +#include "avi_intern.h" +#include "avi_endian.h" + +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + +/* avi_set_compress_options gets its own file... now don't WE feel important? */ + +AviError AVI_set_compress_option( + AviMovie *movie, int option_type, int stream, AviOption option, void *opt_data) +{ + int i; + int useconds; + + (void)stream; /* unused */ + + if (movie->header->TotalFrames != 0) { + /* Can't change params after we have already started writing frames. */ + return AVI_ERROR_OPTION; + } + + switch (option_type) { + case AVI_OPTION_TYPE_MAIN: + switch (option) { + case AVI_OPTION_WIDTH: + movie->header->Width = *((int *)opt_data); + movie->header->SuggestedBufferSize = movie->header->Width * movie->header->Height * 3; + + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Width = *((int *)opt_data); + movie->streams[i].sh.SuggestedBufferSize = movie->header->SuggestedBufferSize; + movie->streams[i].sh.right = *((int *)opt_data); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = + movie->header->SuggestedBufferSize; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + + break; + + case AVI_OPTION_HEIGHT: + movie->header->Height = *((int *)opt_data); + movie->header->SuggestedBufferSize = movie->header->Width * movie->header->Height * 3; + + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Height = *((int *)opt_data); + movie->streams[i].sh.SuggestedBufferSize = movie->header->SuggestedBufferSize; + movie->streams[i].sh.bottom = *((int *)opt_data); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = + movie->header->SuggestedBufferSize; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + + break; + + case AVI_OPTION_QUALITY: + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + movie->streams[i].sh.Quality = (*((int *)opt_data)) * 100; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + break; + + case AVI_OPTION_FRAMERATE: + useconds = (int)(1000000 / (*((double *)opt_data))); + if (useconds) { + movie->header->MicroSecPerFrame = useconds; + } + + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + movie->streams[i].sh.Scale = movie->header->MicroSecPerFrame; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + break; + } + + fseek(movie->fp, movie->offset_table[0], SEEK_SET); + awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); + + break; + case AVI_OPTION_TYPE_STRH: + break; + case AVI_OPTION_TYPE_STRF: + break; + default: + return AVI_ERROR_OPTION; + } + + return AVI_ERROR_NONE; +} diff --git a/source/blender/io/avi/intern/avi_rgb.c b/source/blender/io/avi/intern/avi_rgb.c new file mode 100644 index 00000000000..d449556e79b --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb.c @@ -0,0 +1,154 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Converts rgb-type avi-s. + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "AVI_avi.h" +#include "avi_rgb.h" + +#include "IMB_imbuf.h" + +/* implementation */ + +void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + AviBitmapInfoHeader *bi; + short bits = 32; + + (void)size; /* unused */ + + bi = (AviBitmapInfoHeader *)movie->streams[stream].sf; + if (bi) { + bits = bi->BitCount; + } + + if (bits == 16) { + unsigned short *pxl; + unsigned char *to; +#ifdef __BIG_ENDIAN__ + unsigned char *pxla; +#endif + + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromavirgbbuf"); + + if (buf) { + size_t y = movie->header->Height; + to = buf; + + while (y--) { + pxl = (unsigned short *)(buffer + y * movie->header->Width * 2); + +#ifdef __BIG_ENDIAN__ + pxla = (unsigned char *)pxl; +#endif + + size_t x = movie->header->Width; + while (x--) { +#ifdef __BIG_ENDIAN__ + int i = pxla[0]; + pxla[0] = pxla[1]; + pxla[1] = i; + + pxla += 2; +#endif + + *(to++) = ((*pxl >> 10) & 0x1f) * 8; + *(to++) = ((*pxl >> 5) & 0x1f) * 8; + *(to++) = (*pxl & 0x1f) * 8; + pxl++; + } + } + } + + MEM_freeN(buffer); + + return buf; + } + else { + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromavirgbbuf"); + + if (buf) { + size_t rowstride = movie->header->Width * 3; + if ((bits != 16) && (movie->header->Width % 2)) { + rowstride++; + } + + for (size_t y = 0; y < movie->header->Height; y++) { + memcpy(&buf[y * movie->header->Width * 3], + &buffer[((movie->header->Height - 1) - y) * rowstride], + movie->header->Width * 3); + } + + for (size_t y = 0; y < (size_t)movie->header->Height * (size_t)movie->header->Width * 3; + y += 3) { + int i = buf[y]; + buf[y] = buf[y + 2]; + buf[y + 2] = i; + } + } + + MEM_freeN(buffer); + + return buf; + } +} + +void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + + (void)stream; /* unused */ + + size_t rowstride = movie->header->Width * 3; + /* AVI files has uncompressed lines 4-byte aligned */ + rowstride = (rowstride + 3) & ~3; + + *size = movie->header->Height * rowstride; + buf = MEM_mallocN(*size, "toavirgbbuf"); + + for (size_t y = 0; y < movie->header->Height; y++) { + memcpy(&buf[y * rowstride], + &buffer[((movie->header->Height - 1) - y) * movie->header->Width * 3], + movie->header->Width * 3); + } + + for (size_t y = 0; y < movie->header->Height; y++) { + for (size_t x = 0; x < movie->header->Width * 3; x += 3) { + int i = buf[y * rowstride + x]; + buf[y * rowstride + x] = buf[y * rowstride + x + 2]; + buf[y * rowstride + x + 2] = i; + } + } + + MEM_freeN(buffer); + + return buf; +} diff --git a/source/blender/io/avi/intern/avi_rgb.h b/source/blender/io/avi/intern/avi_rgb.h new file mode 100644 index 00000000000..7c8ce590d27 --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_RGB_H__ +#define __AVI_RGB_H__ + +void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); + +#endif /* __AVI_RGB_H__ */ diff --git a/source/blender/io/avi/intern/avi_rgb32.c b/source/blender/io/avi/intern/avi_rgb32.c new file mode 100644 index 00000000000..3efa4814c70 --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb32.c @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Converts between rgb32 and avi. + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "IMB_imbuf.h" + +#include "AVI_avi.h" +#include "avi_rgb32.h" + +void *avi_converter_from_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + + (void)stream; /* unused */ + + *size = (size_t)movie->header->Height * (size_t)movie->header->Width * 3; + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromrgb32buf"); + if (!buf) { + return NULL; + } + + size_t rowstridea = movie->header->Width * 3; + size_t rowstrideb = movie->header->Width * 4; + + for (size_t y = 0; y < movie->header->Height; y++) { + for (size_t x = 0; x < movie->header->Width; x++) { + buf[y * rowstridea + x * 3 + 0] = buffer[y * rowstrideb + x * 4 + 3]; + buf[y * rowstridea + x * 3 + 1] = buffer[y * rowstrideb + x * 4 + 2]; + buf[y * rowstridea + x * 3 + 2] = buffer[y * rowstrideb + x * 4 + 1]; + } + } + + MEM_freeN(buffer); + + return buf; +} + +void *avi_converter_to_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + unsigned char *to, *from; + + (void)stream; /* unused */ + + *size = (size_t)movie->header->Height * (size_t)movie->header->Width * 4; + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 4, sizeof(unsigned char), "torgb32buf"); + if (!buf) { + return NULL; + } + + memset(buf, 255, *size); + + to = buf; + from = buffer; + size_t i = (size_t)movie->header->Height * (size_t)movie->header->Width; + + while (i--) { + memcpy(to, from, 3); + to += 4; + from += 3; + } + + MEM_freeN(buffer); + + return buf; +} diff --git a/source/blender/io/avi/intern/avi_rgb32.h b/source/blender/io/avi/intern/avi_rgb32.h new file mode 100644 index 00000000000..eb4b9ca4e21 --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb32.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_RGB32_H__ +#define __AVI_RGB32_H__ + +void *avi_converter_from_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_to_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); + +#endif /* __AVI_RGB32_H__ */ -- cgit v1.2.3