pax_global_header00006660000000000000000000000064146343547560014533gustar00rootroot0000000000000052 comment=025ac5e56ea35be1d4c02d014e6d26e2eb9ccfa9 cgif-0.4.1/000077500000000000000000000000001463435475600124455ustar00rootroot00000000000000cgif-0.4.1/.github/000077500000000000000000000000001463435475600140055ustar00rootroot00000000000000cgif-0.4.1/.github/FUNDING.yml000066400000000000000000000012211463435475600156160ustar00rootroot00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] cgif-0.4.1/.github/dependabot.yml000066400000000000000000000001661463435475600166400ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" cgif-0.4.1/.github/workflows/000077500000000000000000000000001463435475600160425ustar00rootroot00000000000000cgif-0.4.1/.github/workflows/ci.yml000066400000000000000000000037631463435475600171710ustar00rootroot00000000000000name: CI on: - push - pull_request jobs: build: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: matrix: include: - name: "Linux x64 (Ubuntu 22.04)" os: ubuntu-22.04 config: {} - name: "Linux 32-bit (Ubuntu 22.04)" os: ubuntu-22.04 config: { cflags: "-m32"} - name: "macOS 12" os: macos-12 config: {} - name: "Windows 2022" os: windows-2022 config: {} env: CFLAGS: ${{ matrix.config.cflags }} steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - name: Dependencies (ubuntu) if: contains(matrix.os, 'ubuntu') run: sudo apt update && sudo apt install -y gifsicle valgrind meson - name: Dependencies (ubuntu 32-bit) if: contains(matrix.name, '32-bit') run: sudo apt update && sudo apt install -y gcc-multilib - name: Dependencies (macos) if: contains(matrix.os, 'macos') run: brew install gifsicle meson - name: Dependencies (windows) if: contains(matrix.os, 'windows') run: choco install gifsicle && pip3 install meson ninja - name: Setup run: meson setup build -Dtests=true -Dfuzzer=true - name: Build run: meson compile -C build - name: Test run: meson test -C build - name: Verify GIF test output (gifsicle) run: for f in build/*.gif; do gifsicle --no-ignore-errors --info $f || exit 1; done shell: bash - name: Valgrind if: contains(matrix.name, 'Linux x64') run: meson test -C build --list | grep -v zip | grep -v checksums | sed 's/^/"/;s/$/"/' | xargs meson test -C build/ --wrap "valgrind --memcheck:leak-check=full --memcheck:show-leak-kinds=definite --memcheck:error-exitcode=1" shell: bash - name: Valgrind output if: contains(matrix.name, 'Linux x64') run: cat build/meson-logs/testlog-valgrind.txt cgif-0.4.1/.github/workflows/cifuzz.yml000066400000000000000000000014741463435475600201050ustar00rootroot00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest strategy: fail-fast: false matrix: sanitizer: [address, undefined, memory] steps: - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'cgif' language: c sanitizer: ${{ matrix.sanitizer }} - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'cgif' language: c sanitizer: ${{ matrix.sanitizer }} fuzz-seconds: 600 - name: Upload Crash uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts cgif-0.4.1/.vscode/000077500000000000000000000000001463435475600140065ustar00rootroot00000000000000cgif-0.4.1/.vscode/launch.json000066400000000000000000000015531463435475600161570ustar00rootroot00000000000000{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "lldb", "request": "launch", "name": "Debug example", "program": "${workspaceFolder}/.debug_usr/bin/cgif_example", "preLaunchTask": "debug_build", "args": [], "cwd": "${workspaceFolder}" }, { "type": "lldb", "request": "launch", "name": "Debug example animation", "program": "${workspaceFolder}/.debug_usr/bin/cgif_example_video", "preLaunchTask": "debug_build", "args": [], "cwd": "${workspaceFolder}" }, ] } cgif-0.4.1/.vscode/tasks.json000066400000000000000000000015001463435475600160220ustar00rootroot00000000000000{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [{ "label": "setup_debug_build", "command": "meson", "args": ["setup", "--prefix=${workspaceFolder}/.debug_usr/", "--reconfigure", "-Ddefault_library=static", "--buildtype", "debug", "-Dexamples=true", "-Dinstall_examples=true", "-Dtests=false", ".debug_build/"], "type": "shell" }, { "label": "debug_build", "command": "meson", "args": ["install", "-C", ".debug_build/"], "type": "shell", "dependsOrder": "sequence", "dependsOn": ["setup_debug_build"] }] } cgif-0.4.1/AUTHORS000066400000000000000000000003011463435475600135070ustar00rootroot00000000000000cgif has been initially developed and is being maintained by: - Daniel Löbl - Matthias Löbl We thank the following people for valuable contributions: - Lovell Fuller - Kleis Auke Wolthuizen cgif-0.4.1/INSTALL.md000066400000000000000000000015111463435475600140730ustar00rootroot00000000000000# Installation cgif is packaged for [Homebrew](https://formulae.brew.sh/formula/cgif) and several Linux distributions already: [![Packaging status](https://repology.org/badge/vertical-allrepos/cgif.svg)](https://repology.org/project/cgif/versions) ## Ubuntu >= 22.04 `sudo apt-get install libcgif0` for development files `sudo apt-get install libcgif-dev` ## Homebrew `$ brew install cgif` ## Fedora >= 35 `$ sudo yum install libcgif` for development files `$ sudo yum install libcgif-devel` ## Manual installation ### Requirements for compiling cgif: - C compiler (e.g. gcc, clang) - meson build system ### Steps 1. `$ git clone https://github.com/dloebl/cgif` 2. `$ cd cgif/` set the prefix of your choice: 3. `$ meson setup --prefix=/usr build` compile & install cgif as shared library: 4. `$ meson install -C build` cgif-0.4.1/LICENSE000066400000000000000000000021131463435475600134470ustar00rootroot00000000000000MIT License Copyright (c) 2021-2023, Daniel Löbl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cgif-0.4.1/README.md000066400000000000000000000134251463435475600137310ustar00rootroot00000000000000[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/cgif.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:cgif) ## CGIF, a GIF encoder written in C A fast and lightweight GIF encoder that can create GIF animations and images. Summary of the main features: - user-defined global or local color-palette with up to 256 colors (limit of the GIF format) - size-optimizations for GIF animations: - option to set a pixel to transparent if it has identical color in the previous frame (transparency optimization) - do encoding just for the rectangular area that differs from the previous frame (width/height optimization) - fast: a GIF with 256 colors and 1024x1024 pixels can be created in below 50 ms even on a minimalistic system - MIT license (permissive) - different options for GIF animations: static image, N repetitions, infinite repetitions - additional source-code for verifying the encoder after making changes - user-defined delay time from one frame to the next (can be set independently for each frame) - source-code conforms to the C99 standard ## Examples To get started, we suggest that you have a look at our code examples. ```examples/cgif_example_video.c``` is an example that creates a GIF animation. ```examples/cgif_example.c``` is an example for a static GIF image. ## Overview To get an overview of the API, we recommend having a look at our wiki (https://github.com/dloebl/cgif/wiki/General-API) where types and functions are described. The corresponding implementations can be found in ```src/cgif.c``` and ```src/cgif_raw.c```. Here the most important types and functions: ```C // These are the four struct types that contain all GIF data and parameters: typedef CGIF_Config // global cofinguration parameters of the GIF typedef CGIF_FrameConfig // local configuration parameters for a frame typedef CGIF // struct for the full GIF typedef CGIF_Frame // struct for a single frame // The user needs only these three functions to create a GIF image: CGIF* cgif_newgif (CGIF_Config* pConfig); // creates a new GIF int cgif_addframe (CGIF* pGIF, CGIF_FrameConfig* pConfig); // adds a frame to an existing GIF int cgif_close (CGIF* pGIF); // close the created file and free memory ``` With our encoder you can create animated or static GIFs, you can or cannot use certain optimizations, and so on. You can switch between all these different options easily using the two attributes ```attrFlags``` and ```genFlags``` in the configurations ```CGIF_Config``` and ```CGIF_FrameConfig```. These attributes are of type ```uint32_t``` and bundle yes/no-options with a bit-wise logic. So far only a few of the 32 bits are used leaving space to include further functionalities ensuring backward compatibility. We provide the following flag settings which can be combined by bit-wise or-operations: ```C CGIF_ATTR_IS_ANIMATED // make an animated GIF (default is non-animated GIF) CGIF_ATTR_NO_GLOBAL_TABLE // disable global color table (global color table is default) CGIF_ATTR_HAS_TRANSPARENCY // first entry in color table contains transparency (alpha channel) CGIF_ATTR_NO_LOOP // run GIF animation only one time. numLoops is ignored (no repetitions) CGIF_GEN_KEEP_IDENT_FRAMES // keep frames that are identical to previous frame (default is to drop them) CGIF_FRAME_ATTR_USE_LOCAL_TABLE // use a local color table for a frame (not used by default) CGIF_FRAME_ATTR_HAS_ALPHA // frame contains alpha channel (index set via transIndex field) CGIF_FRAME_ATTR_HAS_SET_TRANS // transparency setting provided by user (transIndex field) CGIF_FRAME_ATTR_INTERLACED // encode frame interlaced CGIF_FRAME_GEN_USE_TRANSPARENCY // use transparency optimization (size optimization) CGIF_FRAME_GEN_USE_DIFF_WINDOW // do encoding just for the sub-window that changed (size optimization) ``` If you didn't understand the point of ```attrFlags``` and ```genFlags``` and the flags, please don't worry. The example files ```examples/cgif_example.c``` and ```examples/cgif_example_video.c``` are all you need to get started and the used default settings for ```attrFlags``` and ```genFlags``` cover most cases quite well. ## Compiling the example An example can be compiled and tested simply by: ``` $ c99 -o cgif_example -Iinc examples/cgif_example_video.c src/cgif.c src/cgif_raw.c $ ./cgif_example ``` ## Validating the encoder In the folder ```tests```, we provide several testing routines that you can run via the script ```tests/performtests.sh```. To perform the tests you need to install the programs [ImageMagick](https://github.com/ImageMagick/ImageMagick), [gifsicle](https://github.com/kohler/gifsicle) and [tcc (tiny c compiler)](https://bellard.org/tcc/). With the provided tests you can validate that the encoder still generates correct GIF files after making changes on the encoder itself. ## Further explanations The GIF format employs the [Lempel-Ziv-Welch (LZW)](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch) algorithm for image compression. If you are interested in details of the GIF format, please have a look at the official GIF documentation (https://www.w3.org/Graphics/GIF/spec-gif89a.txt). ## Versioning scheme Releases of cgif follow the semantic versioning scheme as described here: [semver.org](https://semver.org/) The following additional guarantees are provided: * Public API of versions 0.x.x are stable. ## Debugging There is a Visual Studio Code debug configuration with launch targets for the two examples. You need to install the C/C++ extension and a LLDB extension (debugger) to debug cgif. ## License Licensed under the MIT license (permissive). For more details please see ```LICENSE``` cgif-0.4.1/examples/000077500000000000000000000000001463435475600142635ustar00rootroot00000000000000cgif-0.4.1/examples/cgif_example.c000066400000000000000000000047261463435475600170630ustar00rootroot00000000000000#include #include #include #include "cgif.h" #define WIDTH 1024 #define HEIGHT 1024 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->pImageData = pImageData; } /* This is an example code that creates a GIF-image with random pixels. */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue uint16_t numColors = 3; // number of colors in aPalette (up to 256 possible) // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "example_cgif.gif", WIDTH, HEIGHT, aPalette, numColors); pGIF = cgif_newgif(&gConfig); // create image frame with stripe pattern pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data for (int i = 0; i < (WIDTH * HEIGHT); ++i) { // Generate pattern pImageData[i] = (unsigned char)((i % WIDTH)/4 % numColors); // stripe pattern (4 pixels per stripe) } // add frame to GIF initFrameConfig(&fConfig, pImageData); // initialize the frame-configuration cgif_addframe(pGIF, &fConfig); // add a new frame to the GIF free(pImageData); // free image data when frame is added // close GIF and free allocated space cgif_close(pGIF); return 0; } cgif-0.4.1/examples/cgif_example_video.c000066400000000000000000000054671463435475600202540ustar00rootroot00000000000000#include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; pConfig->attrFlags = CGIF_ATTR_IS_ANIMATED; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->delay = delay; pConfig->pImageData = pImageData; } /* This is an example code that creates a GIF-animation with a moving stripe-pattern. */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue uint16_t numColors = 3; // number of colors in aPalette (up to 256 possible) int numFrames = 12; // number of frames in the video // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "example_video_cgif.gif", WIDTH, HEIGHT, aPalette, numColors); pGIF = cgif_newgif(&gConfig); // create image frames and add them to GIF pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data for (int f = 0; f < numFrames; ++f) { for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = (unsigned char)((f + i % WIDTH ) / 4 % numColors); // moving stripe pattern (4 pixels per stripe) } initFrameConfig(&fConfig, pImageData, 10); // initialize the frame-configuration cgif_addframe(pGIF, &fConfig); // append the new frame } free(pImageData); // free image data when all frames are added // close created GIF-file and free allocated space cgif_close(pGIF); return 0; } cgif-0.4.1/examples/meson.build000066400000000000000000000005761463435475600164350ustar00rootroot00000000000000examples = [ 'cgif_example', 'cgif_example_video', ] b_install_examples = get_option('install_examples') foreach e : examples ex_exe = executable( e, e + '.c', dependencies : [libcgif_dep], include_directories : ['../inc/'], install : b_install_examples, ) if get_option('tests') and not meson.is_cross_build() test(e, ex_exe) endif endforeach cgif-0.4.1/fuzz/000077500000000000000000000000001463435475600134435ustar00rootroot00000000000000cgif-0.4.1/fuzz/cgif_create_fuzz_seed.c000066400000000000000000000060031463435475600201170ustar00rootroot00000000000000/* Create seed corpus from our test cases (tests/). Wrap cgifs API and write configuration to CGIF_OUTPATH. */ #include #include #include #include #ifndef CGIF_OUTPATH #error "please set a output path: define CGIF_OUTPATH" #endif struct st_gif { CGIF_Config config; FILE* pFile; cgif_result curResult; }; static int writedata(FILE* pFile, const void* pData, uint32_t size) { int r = fwrite(pData, size, 1, pFile); if(r != 1) return 1; return 0; } static int write_gifconfig(FILE* pFile, const CGIF_Config* pConfig) { int r; // width : U16 // height : U16 // attrFlags : U32 // genFlags : U32 // sizeGCT : U16 // numLoops : U16 // pGCT : U8[sizeGCT * 3] r = writedata(pFile, &pConfig->width, 2); r |= writedata(pFile, &pConfig->height, 2); r |= writedata(pFile, &pConfig->attrFlags, 4); r |= writedata(pFile, &pConfig->genFlags, 4); r |= writedata(pFile, &pConfig->numGlobalPaletteEntries, 2); if(!(pConfig->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE)) { r |= writedata(pFile, pConfig->pGlobalPalette, pConfig->numGlobalPaletteEntries * 3); } return (r ? 1 : 0); } static int write_frameconfig(FILE* pFile, const CGIF_FrameConfig* pConfig, uint32_t sizeImageData) { int r; // attrFlags : U16 // genFlags : U16 // delay : U16 // transIndex : U8 // sizeLCT : U16 // pLCT : U8[sizeLCT * 3] // pImageData : U8[numBytes] r = writedata(pFile, &pConfig->attrFlags, 2); r |= writedata(pFile, &pConfig->genFlags, 2); r |= writedata(pFile, &pConfig->delay, 2); r |= writedata(pFile, &pConfig->transIndex, 1); r |= writedata(pFile, &pConfig->numLocalPaletteEntries, 2); if(pConfig->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) { r |= writedata(pFile, pConfig->pLocalPalette, pConfig->numLocalPaletteEntries * 3); } r |= writedata(pFile, pConfig->pImageData, sizeImageData); return (r ? 1 : 0); } /* wrapper functions for cgifs API */ CGIF* cgif_newgif(CGIF_Config* pConfig) { CGIF* pGIF; FILE* pFile; int r; pFile = fopen(CGIF_OUTPATH, "wb"); // check for errors if(pFile == NULL) { return NULL; } r = write_gifconfig(pFile, pConfig); // check for write errors if(r) { fclose(pFile); return NULL; } pGIF = malloc(sizeof(CGIF)); // check for alloc errors if(pGIF == NULL) { fclose(pFile); return NULL; } pGIF->config = *pConfig; pGIF->pFile = pFile; pGIF->curResult = CGIF_OK; return pGIF; } int cgif_addframe(CGIF* pGIF, CGIF_FrameConfig* pConfig) { int r; const uint32_t sizeImageData = (uint32_t)pGIF->config.width * (uint32_t)pGIF->config.height; r = write_frameconfig(pGIF->pFile, pConfig, sizeImageData); if(r) { pGIF->curResult = CGIF_ERROR; } return CGIF_OK; } int cgif_close(CGIF* pGIF) { const int result = pGIF->curResult; fclose(pGIF->pFile); free(pGIF); return result; } cgif-0.4.1/fuzz/cgif_fuzzer.c000066400000000000000000000075511463435475600161340ustar00rootroot00000000000000#include #include #include #include #include typedef struct { const uint8_t* pData; size_t numBytes; size_t curPos; } ByteStream; static int readdata(ByteStream* pStream, void* pDest, size_t size) { if((pStream->curPos + size) <= pStream->numBytes) { memcpy(pDest, &pStream->pData[pStream->curPos], size); pStream->curPos += size; return 0; } // error: out of bounds pStream->curPos = pStream->numBytes; // mark stream as out of sync return -1; } static int read_gifconfig(ByteStream* pStream, CGIF_Config* pDest) { int r; memset(pDest, 0, sizeof(CGIF_Config)); // width : U16 // height : U16 // attrFlags : U32 // genFlags : U32 // sizeGCT : U16 // numLoops : U16 // pGCT : U8[sizeGCT * 3] r = readdata(pStream, &pDest->width, 2); r |= readdata(pStream, &pDest->height, 2); r |= readdata(pStream, &pDest->attrFlags, 4); r |= readdata(pStream, &pDest->genFlags, 4); r |= readdata(pStream, &pDest->numGlobalPaletteEntries, 2); if(!(pDest->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE)) { pDest->pGlobalPalette = (uint8_t*)malloc(pDest->numGlobalPaletteEntries * 3); if(pDest->pGlobalPalette == NULL) return 0; // malloc failed r |= readdata(pStream, pDest->pGlobalPalette, pDest->numGlobalPaletteEntries * 3); } if(r) { free(pDest->pGlobalPalette); } return (r ? 0 : 1); } static int read_frameconfig(ByteStream* pStream, CGIF_FrameConfig* pDest, size_t sizeImageData) { int r; memset(pDest, 0, sizeof(CGIF_FrameConfig)); // attrFlags : U16 // genFlags : U16 // delay : U16 // transIndex : U8 // sizeLCT : U16 // pLCT : U8[sizeLCT * 3] // pImageData : U8[sizeImageData] r = readdata(pStream, &pDest->attrFlags, 2); r |= readdata(pStream, &pDest->genFlags, 2); r |= readdata(pStream, &pDest->delay, 2); r |= readdata(pStream, &pDest->transIndex, 1); r |= readdata(pStream, &pDest->numLocalPaletteEntries, 2); if(pDest->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) { pDest->pLocalPalette = (uint8_t*)malloc(pDest->numLocalPaletteEntries * 3); if(pDest->pLocalPalette == NULL) return 0; // malloc failed r |= readdata(pStream, pDest->pLocalPalette, pDest->numLocalPaletteEntries * 3); } pDest->pImageData = (uint8_t*)malloc(sizeImageData); if(pDest->pImageData == NULL) return 0; // malloc failed r |= readdata(pStream, pDest->pImageData, sizeImageData); if(r) { free(pDest->pImageData); free(pDest->pLocalPalette); } return (r ? 0 : 1); } static int writecb(void* pContext, const uint8_t* pData, size_t size) { (void)pContext; (void)pData; (void)size; // eat input return 0; } static int processInput(ByteStream* pStream) { CGIF_Config gconfig; CGIF_FrameConfig fconfig; CGIF* pGIF; size_t sizeImageData; int r; r = read_gifconfig(pStream, &gconfig); if(!r) { return -1; } sizeImageData = (uint32_t)gconfig.width * (uint32_t)gconfig.height; // limit dimensions of GIF to be created if(sizeImageData > (10000 * 10000)) { free(gconfig.pGlobalPalette); return -1; } gconfig.pWriteFn = writecb; // discard output pGIF = cgif_newgif(&gconfig); free(gconfig.pGlobalPalette); if(pGIF == NULL) { return -1; } r = read_frameconfig(pStream, &fconfig, sizeImageData); while(r) { cgif_addframe(pGIF, &fconfig); free(fconfig.pImageData); free(fconfig.pLocalPalette); r = read_frameconfig(pStream, &fconfig, sizeImageData); } r = cgif_close(pGIF); return r; } #ifdef __cplusplus extern "C" #endif int LLVMFuzzerTestOneInput(const uint8_t* pData, size_t numBytes) { ByteStream input = { pData, numBytes, 0 }; processInput(&input); return 0; } cgif-0.4.1/fuzz/cgif_fuzzer_standalone.c000066400000000000000000000015071463435475600203370ustar00rootroot00000000000000#include #include #include int LLVMFuzzerTestOneInput(const uint8_t* pData, size_t size); int main(int argn, char** pArgs) { FILE* pFile; if(argn != 2) { fprintf(stderr, "invalid number of arguments\n"); return 1; } pFile = fopen(pArgs[1], "rb"); if(pFile == NULL) { fprintf(stderr, "failed to open file\n"); return 2; } // get size of input fseek(pFile, 0, SEEK_END); const long size = ftell(pFile); fseek(pFile, 0, SEEK_SET); // read input uint8_t* pData = malloc(size); if(pData == NULL) { fclose(pFile); return 3; } size_t r = fread(pData, size, 1, pFile); fclose(pFile); if(r != 1) { fprintf(stderr, "read failed\n"); free(pData); return 4; } // test input LLVMFuzzerTestOneInput(pData, size); free(pData); return 0; } cgif-0.4.1/fuzz/meson.build000066400000000000000000000032611463435475600156070ustar00rootroot00000000000000# TBD: replace with custom python zip tool (just like scripts/sha256sum.py) # TBD: allow to generate the seed corpus without having to invoke all unit tests. zip = find_program('zip', required : false) foreach t : tests name = t.get('name') src = [join_paths(meson.source_root(), 'tests', name + '.c'), 'cgif_create_fuzz_seed.c'] cflags = ['-D', 'CGIF_OUTPATH="' + name + '.seed' + '"'] exe = executable(name + '_genseed', sources : src, c_args : cflags, include_directories : ['../inc']) test('generate ' + name + '.seed', exe, workdir : join_paths(meson.build_root(), 'fuzz'), priority : -2, should_fail : t.get('seed_should_fail')) endforeach # get the ordering right: # sha256sum check on fuzz seed corpus should be run once all of the above tests are done. test('check fuzz seed checksums', sha256sumc, args : [join_paths(meson.source_root(), 'fuzz/seeds.sha256')], workdir : join_paths(meson.build_root(), 'fuzz'), priority : -3, is_parallel : false) src = ['cgif_fuzzer_standalone.c', 'cgif_fuzzer.c'] exe = executable( 'cgif_fuzzer_standalone', sources : src, dependencies : [libcgif_dep], include_directories : ['../inc'], ) # test seed corpus with standalone fuzzer foreach t : tests seedpath = t.get('name') + '.seed' test('run cgif_fuzzer_standalone ' + seedpath, exe, args : seedpath, workdir : join_paths(meson.build_root(), 'fuzz'), priority : -4) endforeach # generate seed corpus zip archive, if possible if zip.found() seeds = [] foreach t : tests seeds += t.get('name') + '.seed' endforeach test('create fuzz seed corpus zip archive', zip, args : ['cgif_fuzzer_seed_corpus.zip', seeds], workdir : join_paths(meson.build_root(), 'fuzz'), priority : -4) endif cgif-0.4.1/fuzz/seeds.sha256000066400000000000000000000071401463435475600155020ustar00rootroot000000000000005496eb57d6e50a60657ed36c5413a1a7483a42057fd40212ee916d0d05bcb08b all_optim.seed 123c8933f13b7e18f88e1b260af6fc98c8cb7d5a5000529f274e099b58111227 alpha.seed cbdd22c6777494464ca6139ce5f9444ee7928fed7f69c8c56db6397036f80840 animated_color_gradient.seed 6b85a73c6c98ff22b9ab122024c9c05056bf3e63d4b16ab6c34655a4e65c9812 animated_interlaced.seed 855f496df19ddcc070ae9fbf4ad40b43fff47150c5a86ac3b61066adbfda9be1 animated_single_pixel.seed 3565b0f827e83dc088f13e7a4de189228f3eb58705b39e47b428a7d3902921f4 animated_snake.seed ff74c1018b3afef3866e1559bf4bfef3168c52e2d3e604809ea14280ed38ffb4 animated_stripe_pattern_2.seed 591f80e038b3daa2c4640f74f1bd14a3e4da34a0ccb92961e92a9d5b830c50f4 animated_stripe_pattern.seed 8ee1ae415c752d88db700ef0da252ac522d2d42acdb6d42484064decf54a2557 animated_stripes_horizontal.seed 50e347edd541f9552629d676456ecff51d44046b784ad5286dc1b550a1de6dc2 duplicate_frames.seed b954e36e1e8ed3846ab478d4d722c21f6e1e205650d87d15bd80b64a1014c6e4 earlyclose.seed 58c1cc135728af7338db3f7addb3e748878d40558ae3083d1b6e34e458724bd6 eindex_anim.seed f7dc2a899cd62db103d7ed37b901c43609669359f5f25c38e1a0ca9eca8291d4 eindex.seed 561a446bb68285dd4604fff13e51db72abb39bef245f2d7144e832ed93b54df8 enopalette.seed d25b11413af77a8ef71d154fd5c9eeebbff0e989ddf99c01cfaefc6f433ebcd6 ewrite.seed bd8db66ec28d90a6072df5c91e3c69509e91e4ab73f7342768f42139ce457b66 ezeroheight.seed 3837fd01de36c327f793e95d63b5492de43c356434d4b363a7fbf6a0b1d23bfb ezerowidthheight.seed 435c1d34671f056129ae203181a3f5bf0a0aaba2e1f7a73e892be93b81e2265a ezerowidth.seed 9567da94b9e61c50b97f63292bb02200ea26e5673d08ce5f1469a27e6608b332 global_plus_local_table.seed 9d1d5aba809da1cc8fd923ca05080445403d77ff370453f7b75fd025344c7154 global_plus_local_table_with_optim.seed e347062cd069090108f2b3ccaafae795499a42cf3872f146af589936d7813b86 has_transparency_2.seed 7faa327253d42a879f7a26321c1fb10bdc0f3bb23e7c3c641c0ded11bd23526d has_transparency.seed 1bb3a8da61ea9442553599972a7f76b985eb8b21acca2381b19eac724d4f7d45 local_transp.seed b80dea9c117bed31898ea952ec60c1860e210032ad69a8d64c70c2f247bac553 max_color_table_test.seed 7b32633b7c84f8a500f429bcadebfce8d2bfbecf4290c808f0c598e612458357 min_color_table_test.seed 34a722916cb2ccef087cc0218cc464d2c0561447e8255b5be51c00919cd51a4e min_size.seed 226fc23a09aae227256448bc662f699ca7f248ae09006e68855d3f1532bf8d68 more_than_256_colors.seed 104445d398f628dda7e06d8cb756ee76b6f6ecd5f9defc310a58a3c55b57b367 noise256.seed 4268978cd890231eefc836e3c1d1ad1bf9b03a2066737775aea7be65e0150385 noise6_interlaced.seed 46781eed32fce969b4f27f0225c73aa6384419265226c4437a0c5cf7f79dd728 noise6.seed b91efbdc4a1cf6adaa88e7b6b5f164bdd83c595716f6389f99ca47adad988a25 noloop.seed 6b34ddd5d624189de8de19ece2fe7f78fa567be38b2bd6fbadbbf3dc3b420795 one_full_block.seed 6549502c18c1e71143a1c265daf84009cd04dd0091e326434fe8ace379a9d969 only_local_table.seed db2dc8c30c6798c95c0ec7540cd6709a7bb2080865f82111673fea8353ed486f overlap_everything_only_trans.seed 4c202068bb4fc3dc2ef748764d33de8bb01c226a14f37ff98228b0f3b4eef962 overlap_everything.seed 47afa9dc559286902845428e7762febdc298d47c3995d2024c18384c538cb8b6 overlap_some_rows.seed 2af4014c037689974bed0568aab76ac373440d4067be42735c50e69fd7f245eb single_frame_alpha.seed 02645bd3a4da0dd85e66e6a009284ea00fdfc2f478fd74546736dcee5b04d51d stripe_pattern_interlaced.seed 59728a9bc2f736e6945137abcbdf7b068a72b292fcd491964dab4e5f055c0a23 switchpattern.seed da88a80735ddcb18d22c406041fddadde9b64f5799c5c89eb0eae9797494fa3e trans_inc_initdict.seed ae42942ba4741d2043597556dea9bc1c42a94f6811051c236183ead0e3b61151 user_trans.seed d25b11413af77a8ef71d154fd5c9eeebbff0e989ddf99c01cfaefc6f433ebcd6 write_fn.seed cgif-0.4.1/inc/000077500000000000000000000000001463435475600132165ustar00rootroot00000000000000cgif-0.4.1/inc/cgif.h000066400000000000000000000116621463435475600143050ustar00rootroot00000000000000#ifndef CGIF_H #define CGIF_H #include #include #ifdef __cplusplus extern "C" { #endif // flags to set the GIF/frame-attributes #define CGIF_ATTR_IS_ANIMATED (1uL << 1) // make an animated GIF (default is non-animated GIF) #define CGIF_ATTR_NO_GLOBAL_TABLE (1uL << 2) // disable global color table (global color table is default) #define CGIF_ATTR_HAS_TRANSPARENCY (1uL << 3) // first entry in color table contains transparency (alpha channel) #define CGIF_ATTR_NO_LOOP (1uL << 4) // don't loop a GIF animation: only play it one time. #define CGIF_GEN_KEEP_IDENT_FRAMES (1uL << 0) // keep frames that are identical to previous frame (default is to drop them) #define CGIF_FRAME_ATTR_USE_LOCAL_TABLE (1uL << 0) // use a local color table for a frame (local color table is not used by default) #define CGIF_FRAME_ATTR_HAS_ALPHA (1uL << 1) // alpha channel index provided by user (transIndex field) #define CGIF_FRAME_ATTR_HAS_SET_TRANS (1uL << 2) // transparency setting provided by user (transIndex field) #define CGIF_FRAME_ATTR_INTERLACED (1uL << 3) // encode frame interlaced (default is not interlaced) // flags to decrease GIF-size #define CGIF_FRAME_GEN_USE_TRANSPARENCY (1uL << 0) // use transparency optimization (setting pixels identical to previous frame transparent) #define CGIF_FRAME_GEN_USE_DIFF_WINDOW (1uL << 1) // do encoding just for the sub-window that has changed from previous frame #define CGIF_INFINITE_LOOP (0x0000uL) // for animated GIF: 0 specifies infinite loop typedef enum { CGIF_ERROR = -1, // something unspecified failed CGIF_OK = 0, // everything OK CGIF_EWRITE, // writing GIF data failed CGIF_EALLOC, // allocating memory failed CGIF_ECLOSE, // final call to fclose failed CGIF_EOPEN, // failed to open output file CGIF_EINDEX, // invalid index in image data provided by user // internal section (values subject to change) CGIF_PENDING, } cgif_result; typedef struct st_gif CGIF; // struct for the full GIF typedef struct st_gifconfig CGIF_Config; // global cofinguration parameters of the GIF typedef struct st_frameconfig CGIF_FrameConfig; // local configuration parameters for a frame typedef int cgif_write_fn(void* pContext, const uint8_t* pData, const size_t numBytes); // callback function for stream-based output // prototypes CGIF* cgif_newgif (CGIF_Config* pConfig); // creates a new GIF (returns pointer to new GIF or NULL on error) int cgif_addframe (CGIF* pGIF, CGIF_FrameConfig* pConfig); // adds the next frame to an existing GIF (returns 0 on success) int cgif_close (CGIF* pGIF); // close file and free allocated memory (returns 0 on success) // CGIF_Config type (parameters passed by user) // note: must stay AS IS for backward compatibility struct st_gifconfig { uint8_t* pGlobalPalette; // global color table of the GIF const char* path; // path of the GIF to be created, mutually exclusive with pWriteFn uint32_t attrFlags; // fixed attributes of the GIF (e.g. whether it is animated or not) uint32_t genFlags; // flags that determine how the GIF is generated (e.g. optimization) uint16_t width; // width of each frame in the GIF uint16_t height; // height of each frame in the GIF uint16_t numGlobalPaletteEntries; // size of the global color table uint16_t numLoops; // number of repetitons of an animated GIF (set to INFINITE_LOOP for infinite loop) cgif_write_fn *pWriteFn; // callback function for chunks of output data, mutually exclusive with path void* pContext; // opaque pointer passed as the first parameter to pWriteFn }; // CGIF_FrameConfig type (parameters passed by user) // note: must stay AS IS for backward compatibility struct st_frameconfig { uint8_t* pLocalPalette; // local color table of a frame uint8_t* pImageData; // image data to be encoded uint32_t attrFlags; // fixed attributes of the GIF frame uint32_t genFlags; // flags that determine how the GIF frame is created (e.g. optimization) uint16_t delay; // delay before the next frame is shown (units of 0.01 s) uint16_t numLocalPaletteEntries; // size of the local color table uint8_t transIndex; // introduced with V0.2.0 }; #ifdef __cplusplus } #endif #endif // CGIF_H cgif-0.4.1/inc/cgif_raw.h000066400000000000000000000055171463435475600151600ustar00rootroot00000000000000#ifndef CGIF_RAW_H #define CGIF_RAW_H #include #include "cgif.h" #ifdef __cplusplus extern "C" { #endif #define DISPOSAL_METHOD_LEAVE (1uL << 2) #define DISPOSAL_METHOD_BACKGROUND (2uL << 2) #define DISPOSAL_METHOD_PREVIOUS (3uL << 2) // flags to set the GIF attributes #define CGIF_RAW_ATTR_IS_ANIMATED (1uL << 0) // make an animated GIF (default is non-animated GIF) #define CGIF_RAW_ATTR_NO_LOOP (1uL << 1) // don't loop a GIF animation: only play it one time. // flags to set the Frame attributes #define CGIF_RAW_FRAME_ATTR_HAS_TRANS (1uL << 0) // provided transIndex should be set #define CGIF_RAW_FRAME_ATTR_INTERLACED (1uL << 1) // encode frame interlaced // CGIFRaw_Config type // note: internal sections, subject to change. typedef struct { cgif_write_fn *pWriteFn; // callback function for chunks of output data void* pContext; // opaque pointer passed as the first parameter to pWriteFn uint8_t* pGCT; // global color table of the GIF uint32_t attrFlags; // fixed attributes of the GIF (e.g. whether it is animated or not) uint16_t width; // effective width of each frame in the GIF uint16_t height; // effective height of each frame in the GIF uint16_t sizeGCT; // size of the global color table (GCT) uint16_t numLoops; // number of repetitons of an animated GIF (set to INFINITE_LOOP resp. 0 for infinite loop, use CGIF_ATTR_NO_LOOP if you don't want any repetition) } CGIFRaw_Config; // CGIFRaw_FrameConfig type // note: internal sections, subject to chage. typedef struct { uint8_t* pLCT; // local color table of the frame (LCT) uint8_t* pImageData; // image data to be encoded (indices to CT) uint32_t attrFlags; // fixed attributes of the GIF frame uint16_t width; // width of frame uint16_t height; // height of frame uint16_t top; // top offset of frame uint16_t left; // left offset of frame uint16_t delay; // delay before the next frame is shown (units of 0.01 s [cs]) uint16_t sizeLCT; // size of the local color table (LCT) uint8_t disposalMethod; // specifies how this frame should be disposed after being displayed. uint8_t transIndex; // transparency index } CGIFRaw_FrameConfig; // CGIFRaw type // note: internal sections, subject to change. typedef struct { CGIFRaw_Config config; // configutation parameters of the GIF (see above) cgif_result curResult; // current result status of GIFRaw stream } CGIFRaw; // prototypes CGIFRaw* cgif_raw_newgif (const CGIFRaw_Config* pConfig); cgif_result cgif_raw_addframe (CGIFRaw* pGIF, const CGIFRaw_FrameConfig* pConfig); cgif_result cgif_raw_close (CGIFRaw* pGIF); #ifdef __cplusplus } #endif #endif // CGIF_RAW_H cgif-0.4.1/meson.build000066400000000000000000000012551463435475600146120ustar00rootroot00000000000000project( 'libcgif', 'c', version : '0.4.1', license : 'MIT', default_options : ['c_std=c99'], ) cgif_sources = ['src/cgif.c', 'src/cgif_raw.c'] lib = library( 'cgif', cgif_sources, include_directories : ['inc/'], soversion : '0', version : meson.project_version(), install : true, ) install_headers('inc/cgif.h') import('pkgconfig').generate( lib, name : 'cgif', description : 'A fast and lightweight GIF encoder', ) libcgif_dep = declare_dependency(link_with : lib) if get_option('tests') and not meson.is_cross_build() subdir('tests') if get_option('fuzzer') subdir('fuzz') endif endif if get_option('examples') subdir('examples') endif cgif-0.4.1/meson_options.txt000066400000000000000000000007121463435475600161020ustar00rootroot00000000000000option( 'examples', type : 'boolean', value : true, description : 'build examples', ) option( 'fuzzer', type : 'boolean', value : false, description : 'generate fuzz seed corpus and test it with standalone fuzzer', ) option( 'tests', type : 'boolean', value : true, description : 'build tests', ) # for debugging purposes option( 'install_examples', type : 'boolean', value : false, description : 'install examples', ) cgif-0.4.1/src/000077500000000000000000000000001463435475600132345ustar00rootroot00000000000000cgif-0.4.1/src/cgif.c000066400000000000000000000465521463435475600143240ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #include "cgif_raw.h" #define MULU16(a, b) (((uint32_t)a) * ((uint32_t)b)) // helper macro to correctly multiply two U16's without default signed int promotion #define SIZE_FRAME_QUEUE (3) // CGIF_Frame type // note: internal sections, subject to change in future versions typedef struct { CGIF_FrameConfig config; uint8_t disposalMethod; uint8_t transIndex; } CGIF_Frame; // CGIF type // note: internal sections, subject to change in future versions struct st_gif { CGIF_Frame* aFrames[SIZE_FRAME_QUEUE]; // (internal) we need to keep the last three frames in memory. CGIF_Config config; // (internal) configuration parameters of the GIF CGIFRaw* pGIFRaw; // (internal) raw GIF stream FILE* pFile; cgif_result curResult; int iHEAD; // (internal) index to current HEAD frame in aFrames queue }; // dimension result type typedef struct { uint16_t width; uint16_t height; uint16_t top; uint16_t left; } DimResult; /* calculate next power of two exponent of given number (n MUST be <= 256) */ static uint8_t calcNextPower2Ex(uint16_t n) { uint8_t nextPow2; for (nextPow2 = 0; n > (1uL << nextPow2); ++nextPow2); return nextPow2; } /* write callback. returns 0 on success or -1 on error. */ static int writecb(void* pContext, const uint8_t* pData, const size_t numBytes) { CGIF* pGIF; size_t r; pGIF = (CGIF*)pContext; if(pGIF->pFile) { r = fwrite(pData, 1, numBytes, pGIF->pFile); if(r == numBytes) return 0; else return -1; } else if(pGIF->config.pWriteFn) { return pGIF->config.pWriteFn(pGIF->config.pContext, pData, numBytes); } return 0; } /* free space allocated for CGIF struct */ static void freeCGIF(CGIF* pGIF) { if((pGIF->config.attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE) == 0) { free(pGIF->config.pGlobalPalette); } free(pGIF); } /* create a new GIF */ CGIF* cgif_newgif(CGIF_Config* pConfig) { FILE* pFile; CGIF* pGIF; CGIFRaw* pGIFRaw; // raw GIF stream CGIFRaw_Config rawConfig = {0}; // width or heigth cannot be zero if(!pConfig->width || !pConfig->height) { return NULL; } pFile = NULL; // open output file (if necessary) if(pConfig->path) { pFile = fopen(pConfig->path, "wb"); if(pFile == NULL) { return NULL; // error: fopen failed } } // allocate space for CGIF context pGIF = malloc(sizeof(CGIF)); if(pGIF == NULL) { if(pFile) { fclose(pFile); } return NULL; // error -> malloc failed } memset(pGIF, 0, sizeof(CGIF)); pGIF->pFile = pFile; pGIF->iHEAD = 1; memcpy(&(pGIF->config), pConfig, sizeof(CGIF_Config)); // make a deep copy of global color tabele (GCT), if required. if((pConfig->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE) == 0) { pGIF->config.pGlobalPalette = malloc(pConfig->numGlobalPaletteEntries * 3); memcpy(pGIF->config.pGlobalPalette, pConfig->pGlobalPalette, pConfig->numGlobalPaletteEntries * 3); } rawConfig.pGCT = pConfig->pGlobalPalette; rawConfig.sizeGCT = (pConfig->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE) ? 0 : pConfig->numGlobalPaletteEntries; // translate CGIF_ATTR_* to CGIF_RAW_ATTR_* flags rawConfig.attrFlags = (pConfig->attrFlags & CGIF_ATTR_IS_ANIMATED) ? CGIF_RAW_ATTR_IS_ANIMATED : 0; rawConfig.attrFlags |= (pConfig->attrFlags & CGIF_ATTR_NO_LOOP) ? CGIF_RAW_ATTR_NO_LOOP : 0; rawConfig.width = pConfig->width; rawConfig.height = pConfig->height; rawConfig.numLoops = pConfig->numLoops; rawConfig.pWriteFn = writecb; rawConfig.pContext = (void*)pGIF; // pass config down and create a new raw GIF stream. pGIFRaw = cgif_raw_newgif(&rawConfig); // check for errors if(pGIFRaw == NULL) { if(pFile) { fclose(pFile); } freeCGIF(pGIF); return NULL; } pGIF->pGIFRaw = pGIFRaw; // assume error per default. // set to CGIF_OK by the first successful cgif_addframe() call, as a GIF without frames is invalid. pGIF->curResult = CGIF_PENDING; return pGIF; } /* compare given pixel indices using the correct local or global color table; returns 0 if the two pixels are RGB equal */ static int cmpPixel(const CGIF* pGIF, const CGIF_FrameConfig* pCur, const CGIF_FrameConfig* pBef, const uint8_t iCur, const uint8_t iBef) { uint8_t* pBefCT; // color table to use for pBef uint8_t* pCurCT; // color table to use for pCur if((pCur->attrFlags & CGIF_FRAME_ATTR_HAS_SET_TRANS) && iCur == pCur->transIndex) { return 0; // identical } if((pBef->attrFlags & CGIF_FRAME_ATTR_HAS_SET_TRANS) && iBef == pBef->transIndex) { return 1; // done: cannot compare } // safety bounds check const uint16_t sizeCTBef = (pBef->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) ? pBef->numLocalPaletteEntries : pGIF->config.numGlobalPaletteEntries; const uint16_t sizeCTCur = (pCur->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) ? pCur->numLocalPaletteEntries : pGIF->config.numGlobalPaletteEntries; if((iBef >= sizeCTBef) || (iCur >= sizeCTCur)) { return 1; // error: out-of-bounds - cannot compare } pBefCT = (pBef->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) ? pBef->pLocalPalette : pGIF->config.pGlobalPalette; // local or global table used? pCurCT = (pCur->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) ? pCur->pLocalPalette : pGIF->config.pGlobalPalette; // local or global table used? return memcmp(pBefCT + iBef * 3, pCurCT + iCur * 3, 3); } /* optimize GIF file size by only redrawing the rectangular area that differs from previous frame */ static uint8_t* doWidthHeightOptim(CGIF* pGIF, CGIF_FrameConfig* pCur, CGIF_FrameConfig* pBef, DimResult* pResult) { uint8_t* pNewImageData; const uint8_t* pCurImageData; const uint8_t* pBefImageData; uint16_t i, x; uint16_t newHeight, newWidth, newLeft, newTop; const uint16_t width = pGIF->config.width; const uint16_t height = pGIF->config.height; uint8_t iCur, iBef; pCurImageData = pCur->pImageData; pBefImageData = pBef->pImageData; // find top i = 0; while(i < height) { for(int c = 0; c < width; ++c) { iCur = *(pCurImageData + MULU16(i, width) + c); iBef = *(pBefImageData + MULU16(i, width) + c); if(cmpPixel(pGIF, pCur, pBef, iCur, iBef) != 0) { goto FoundTop; } } ++i; } FoundTop: if(i == height) { // need dummy pixel (frame is identical with one before) // TBD we might make it possible to merge identical frames in the future newWidth = 1; newHeight = 1; newLeft = 0; newTop = 0; goto Done; } newTop = i; // find actual height i = height - 1; while(i > newTop) { for(int c = 0; c < width; ++c) { iCur = *(pCurImageData + MULU16(i, width) + c); iBef = *(pBefImageData + MULU16(i, width) + c); if(cmpPixel(pGIF, pCur, pBef, iCur, iBef) != 0) { goto FoundHeight; } } --i; } FoundHeight: newHeight = (i + 1) - newTop; // find left i = newTop; x = 0; while(cmpPixel(pGIF, pCur, pBef, pCurImageData[MULU16(i, width) + x], pBefImageData[MULU16(i, width) + x]) == 0) { ++i; if(i > (newTop + newHeight - 1)) { ++x; //(x==width cannot happen as goto Done is triggered in the only possible case before) i = newTop; } } newLeft = x; // find actual width i = newTop; x = width - 1; while(cmpPixel(pGIF, pCur, pBef, pCurImageData[MULU16(i, width) + x], pBefImageData[MULU16(i, width) + x]) == 0) { ++i; if(i > (newTop + newHeight - 1)) { --x; //(xwidth = newWidth; pResult->height = newHeight; pResult->top = newTop; pResult->left = newLeft; return pNewImageData; } /* move frame down to the raw GIF API */ static cgif_result flushFrame(CGIF* pGIF, CGIF_Frame* pCur, CGIF_Frame* pBef) { CGIFRaw_FrameConfig rawConfig; DimResult dimResult; uint8_t* pTmpImageData; uint8_t* pBefImageData; int isFirstFrame, useLCT, hasAlpha, hasSetTransp; uint16_t numPaletteEntries; uint16_t imageWidth, imageHeight, width, height, top, left; uint8_t transIndex, disposalMethod; cgif_result r; imageWidth = pGIF->config.width; imageHeight = pGIF->config.height; isFirstFrame = (pBef == NULL) ? 1 : 0; useLCT = (pCur->config.attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) ? 1 : 0; // LCT stands for "local color table" hasAlpha = ((pGIF->config.attrFlags & CGIF_ATTR_HAS_TRANSPARENCY) || (pCur->config.attrFlags & CGIF_FRAME_ATTR_HAS_ALPHA)) ? 1 : 0; hasSetTransp = (pCur->config.attrFlags & CGIF_FRAME_ATTR_HAS_SET_TRANS) ? 1 : 0; disposalMethod = pCur->disposalMethod; transIndex = pCur->transIndex; // deactivate impossible size optimizations // => in case alpha channel is used // CGIF_FRAME_GEN_USE_TRANSPARENCY and CGIF_FRAME_GEN_USE_DIFF_WINDOW are not possible if(isFirstFrame || hasAlpha) { pCur->config.genFlags &= ~(CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW); } // transparency setting (which areas are identical to the frame before) provided by user: // CGIF_FRAME_GEN_USE_TRANSPARENCY not possible if(hasSetTransp) { pCur->config.genFlags &= ~(CGIF_FRAME_GEN_USE_TRANSPARENCY); } numPaletteEntries = (useLCT) ? pCur->config.numLocalPaletteEntries : pGIF->config.numGlobalPaletteEntries; // switch off transparency optimization if color table is full (no free spot for the transparent index), TBD: count used colors, adapt table if(numPaletteEntries == 256) { pCur->config.genFlags &= ~CGIF_FRAME_GEN_USE_TRANSPARENCY; } // purge overlap of current frame and frame before (width - height optim), if required (CGIF_FRAME_GEN_USE_DIFF_WINDOW set) if(pCur->config.genFlags & CGIF_FRAME_GEN_USE_DIFF_WINDOW) { pTmpImageData = doWidthHeightOptim(pGIF, &pCur->config, &pBef->config, &dimResult); width = dimResult.width; height = dimResult.height; top = dimResult.top; left = dimResult.left; } else { pTmpImageData = NULL; width = imageWidth; height = imageHeight; top = 0; left = 0; } // mark matching areas of the previous frame as transparent, if required (CGIF_FRAME_GEN_USE_TRANSPARENCY set) if(pCur->config.genFlags & CGIF_FRAME_GEN_USE_TRANSPARENCY) { // set transIndex to next free index int pow2 = calcNextPower2Ex(numPaletteEntries); pow2 = (pow2 < 2) ? 2 : pow2; // TBD keep transparency index behavior as in V0.1.0 (for now) transIndex = (1 << pow2) - 1; if(transIndex < numPaletteEntries) { transIndex = (1 << (pow2 + 1)) - 1; } if(pTmpImageData == NULL) { pTmpImageData = malloc(MULU16(imageWidth, imageHeight)); // TBD check return value of malloc memcpy(pTmpImageData, pCur->config.pImageData, MULU16(imageWidth, imageHeight)); } pBefImageData = pBef->config.pImageData; for(int i = 0; i < height; ++i) { for(int x = 0; x < width; ++x) { if(cmpPixel(pGIF, &pCur->config, &pBef->config, pTmpImageData[MULU16(i, width) + x], pBefImageData[MULU16(top + i, imageWidth) + (left + x)]) == 0) { pTmpImageData[MULU16(i, width) + x] = transIndex; } } } } // move frame down to GIF raw API rawConfig.pLCT = pCur->config.pLocalPalette; rawConfig.pImageData = (pTmpImageData) ? pTmpImageData : pCur->config.pImageData; rawConfig.attrFlags = 0; if(hasAlpha || (pCur->config.genFlags & CGIF_FRAME_GEN_USE_TRANSPARENCY) || hasSetTransp) { rawConfig.attrFlags |= CGIF_RAW_FRAME_ATTR_HAS_TRANS; } rawConfig.attrFlags |= (pCur->config.attrFlags & CGIF_FRAME_ATTR_INTERLACED) ? CGIF_RAW_FRAME_ATTR_INTERLACED : 0; rawConfig.width = width; rawConfig.height = height; rawConfig.top = top; rawConfig.left = left; rawConfig.delay = pCur->config.delay; rawConfig.sizeLCT = (useLCT) ? pCur->config.numLocalPaletteEntries : 0; rawConfig.disposalMethod = disposalMethod; rawConfig.transIndex = transIndex; r = cgif_raw_addframe(pGIF->pGIFRaw, &rawConfig); free(pTmpImageData); return r; } static void freeFrame(CGIF_Frame* pFrame) { if(pFrame) { free(pFrame->config.pImageData); if(pFrame->config.attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) { free(pFrame->config.pLocalPalette); } free(pFrame); } } static void copyFrameConfig(CGIF_FrameConfig* pDest, CGIF_FrameConfig* pSrc) { pDest->pLocalPalette = pSrc->pLocalPalette; // might need a deep copy pDest->pImageData = pSrc->pImageData; // might need a deep copy pDest->attrFlags = pSrc->attrFlags; pDest->genFlags = pSrc->genFlags; pDest->delay = pSrc->delay; pDest->numLocalPaletteEntries = pSrc->numLocalPaletteEntries; // copy transIndex if necessary (field added with V0.2.0; avoid binary incompatibility) if(pSrc->attrFlags & (CGIF_FRAME_ATTR_HAS_ALPHA | CGIF_FRAME_ATTR_HAS_SET_TRANS)) { pDest->transIndex = pSrc->transIndex; } } /* queue a new GIF frame */ int cgif_addframe(CGIF* pGIF, CGIF_FrameConfig* pConfig) { CGIF_Frame* pNewFrame; int hasAlpha, hasSetTransp; int i; cgif_result r; // check for previous errors if(pGIF->curResult != CGIF_OK && pGIF->curResult != CGIF_PENDING) { return pGIF->curResult; } hasAlpha = ((pGIF->config.attrFlags & CGIF_ATTR_HAS_TRANSPARENCY) || (pConfig->attrFlags & CGIF_FRAME_ATTR_HAS_ALPHA)) ? 1 : 0; // alpha channel is present hasSetTransp = (pConfig->attrFlags & CGIF_FRAME_ATTR_HAS_SET_TRANS) ? 1 : 0; // user provided transparency setting (identical areas marked by user) // check for invalid configs: // cannot set alpha channel and user-provided transparency at the same time. if(hasAlpha && hasSetTransp) { pGIF->curResult = CGIF_ERROR; return pGIF->curResult; } // cannot set global and local alpha channel at the same time if((pGIF->config.attrFlags & CGIF_ATTR_HAS_TRANSPARENCY) && (pConfig->attrFlags & CGIF_FRAME_ATTR_HAS_ALPHA)) { pGIF->curResult = CGIF_ERROR; return pGIF->curResult; } // sanity check: // at least one valid CT needed (global or local) if(!(pConfig->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) && (pGIF->config.attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE)) { pGIF->curResult = CGIF_ERROR; return CGIF_ERROR; // invalid config } // if frame matches previous frame, drop it completely and sum the frame delay if(pGIF->aFrames[pGIF->iHEAD] != NULL) { const uint32_t frameDelay = pConfig->delay + pGIF->aFrames[pGIF->iHEAD]->config.delay; if(frameDelay <= 0xFFFF && !(pGIF->config.genFlags & CGIF_GEN_KEEP_IDENT_FRAMES)) { int sameFrame = 1; for(i = 0; i < pGIF->config.width * pGIF->config.height; i++) { if(cmpPixel(pGIF, pConfig, &pGIF->aFrames[pGIF->iHEAD]->config, pConfig->pImageData[i], pGIF->aFrames[pGIF->iHEAD]->config.pImageData[i])) { sameFrame = 0; break; } } if (sameFrame) { pGIF->aFrames[pGIF->iHEAD]->config.delay = frameDelay; return CGIF_OK; } } } // search for free slot in frame queue for(i = pGIF->iHEAD; i < SIZE_FRAME_QUEUE && pGIF->aFrames[i] != NULL; ++i); // check whether the queue is full // when queue is full: we need to flush one frame. if(i == SIZE_FRAME_QUEUE) { r = flushFrame(pGIF, pGIF->aFrames[1], pGIF->aFrames[0]); freeFrame(pGIF->aFrames[0]); pGIF->aFrames[0] = NULL; // avoid potential double free in cgif_close // check for errors if(r != CGIF_OK) { pGIF->curResult = r; return pGIF->curResult; } i = SIZE_FRAME_QUEUE - 1; // keep the flushed frame in memory, as we might need it to write the next one. pGIF->aFrames[0] = pGIF->aFrames[1]; pGIF->aFrames[1] = pGIF->aFrames[2]; } // create new Frame struct + make a deep copy of pConfig. pNewFrame = malloc(sizeof(CGIF_Frame)); copyFrameConfig(&(pNewFrame->config), pConfig); pNewFrame->config.pImageData = malloc(MULU16(pGIF->config.width, pGIF->config.height)); memcpy(pNewFrame->config.pImageData, pConfig->pImageData, MULU16(pGIF->config.width, pGIF->config.height)); // make a deep copy of the local color table, if required. if(pConfig->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) { pNewFrame->config.pLocalPalette = malloc(pConfig->numLocalPaletteEntries * 3); memcpy(pNewFrame->config.pLocalPalette, pConfig->pLocalPalette, pConfig->numLocalPaletteEntries * 3); } pNewFrame->disposalMethod = DISPOSAL_METHOD_LEAVE; pNewFrame->transIndex = 0; pGIF->aFrames[i] = pNewFrame; // add frame to queue pGIF->iHEAD = i; // update HEAD index // check whether we need to adapt the disposal method of the frame before. if(pGIF->config.attrFlags & CGIF_ATTR_HAS_TRANSPARENCY) { pGIF->aFrames[i]->disposalMethod = DISPOSAL_METHOD_BACKGROUND; // TBD might be removed pGIF->aFrames[i]->transIndex = 0; if(pGIF->aFrames[i - 1] != NULL) { pGIF->aFrames[i - 1]->config.genFlags &= ~(CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW); pGIF->aFrames[i - 1]->disposalMethod = DISPOSAL_METHOD_BACKGROUND; // restore to background color } } // set per-frame alpha channel (we need to adapt the disposal method of the frame before) if(pConfig->attrFlags & CGIF_FRAME_ATTR_HAS_ALPHA) { pGIF->aFrames[i]->transIndex = pConfig->transIndex; if(pGIF->aFrames[i - 1] != NULL) { pGIF->aFrames[i - 1]->config.genFlags &= ~(CGIF_FRAME_GEN_USE_DIFF_WINDOW); // width/height optim not possible for frame before pGIF->aFrames[i - 1]->disposalMethod = DISPOSAL_METHOD_BACKGROUND; // restore to background color } } // user provided transparency setting if(hasSetTransp) { pGIF->aFrames[i]->transIndex = pConfig->transIndex; } pGIF->curResult = CGIF_OK; return pGIF->curResult; } /* close the GIF-file and free allocated space */ int cgif_close(CGIF* pGIF) { int r; cgif_result result; // check for previous errors if(pGIF->curResult != CGIF_OK) { goto CGIF_CLOSE_Cleanup; } // flush all remaining frames in queue for(int i = 1; i < SIZE_FRAME_QUEUE; ++i) { if(pGIF->aFrames[i] != NULL) { r = flushFrame(pGIF, pGIF->aFrames[i], pGIF->aFrames[i - 1]); if(r != CGIF_OK) { pGIF->curResult = r; break; } } } // cleanup CGIF_CLOSE_Cleanup: r = cgif_raw_close(pGIF->pGIFRaw); // close raw GIF stream // check for errors if(r != CGIF_OK) { pGIF->curResult = r; } if(pGIF->pFile) { r = fclose(pGIF->pFile); // we are done at this point => close the file if(r) { pGIF->curResult = CGIF_ECLOSE; // error: fclose failed } } for(int i = 0; i < SIZE_FRAME_QUEUE; ++i) { freeFrame(pGIF->aFrames[i]); } result = pGIF->curResult; freeCGIF(pGIF); // catch internal value CGIF_PENDING if(result == CGIF_PENDING) { result = CGIF_ERROR; } return result; // return previous result } cgif-0.4.1/src/cgif_raw.c000066400000000000000000000716361463435475600151760ustar00rootroot00000000000000#include #include #include "cgif_raw.h" #define SIZE_MAIN_HEADER (13) #define SIZE_APP_EXT (19) #define SIZE_FRAME_HEADER (10) #define SIZE_GRAPHIC_EXT ( 8) #define HEADER_OFFSET_SIGNATURE (0x00) #define HEADER_OFFSET_VERSION (0x03) #define HEADER_OFFSET_WIDTH (0x06) #define HEADER_OFFSET_HEIGHT (0x08) #define HEADER_OFFSET_PACKED_FIELD (0x0A) #define HEADER_OFFSET_BACKGROUND (0x0B) #define HEADER_OFFSET_MAP (0x0C) #define IMAGE_OFFSET_LEFT (0x01) #define IMAGE_OFFSET_TOP (0x03) #define IMAGE_OFFSET_WIDTH (0x05) #define IMAGE_OFFSET_HEIGHT (0x07) #define IMAGE_OFFSET_PACKED_FIELD (0x09) #define IMAGE_PACKED_FIELD(a) (*((uint8_t*) (a + IMAGE_OFFSET_PACKED_FIELD))) #define APPEXT_OFFSET_NAME (0x03) #define APPEXT_NETSCAPE_OFFSET_LOOPS (APPEXT_OFFSET_NAME + 13) #define GEXT_OFFSET_DELAY (0x04) #define MAX_CODE_LEN 12 // maximum code length for lzw #define MAX_DICT_LEN (1uL << MAX_CODE_LEN) // maximum length of the dictionary #define BLOCK_SIZE 0xFF // number of bytes in one block of the image data #define MULU16(a, b) (((uint32_t)a) * ((uint32_t)b)) // helper macro to correctly multiply two U16's without default signed int promotion typedef struct { uint8_t* pRasterData; uint32_t sizeRasterData; } LZWResult; typedef struct { uint16_t* pTreeInit; // LZW dictionary tree for the initial dictionary (0-255 max) uint16_t* pTreeList; // LZW dictionary tree as list (max. number of children per node = 1) uint16_t* pTreeMap; // LZW dictionary tree as map (backup to pTreeList in case more than 1 child is present) uint16_t* pLZWData; // pointer to LZW data const uint8_t* pImageData; // pointer to image data uint32_t numPixel; // number of pixels per frame uint32_t LZWPos; // position of the current LZW code uint16_t dictPos; // currrent position in dictionary, we need to store 0-4096 -- so there are at least 13 bits needed here uint16_t mapPos; // current position in LZW tree mapping table } LZWGenState; /* converts host U16 to little-endian (LE) U16 */ static uint16_t hU16toLE(const uint16_t n) { int isBE; uint16_t newVal; uint16_t one; one = 1; isBE = *((uint8_t*)&one) ? 0 : 1; if(isBE) { newVal = (n >> 8) | (n << 8); } else { newVal = n; // already LE } return newVal; } /* calculate next power of two exponent of given number (n MUST be <= 256) */ static uint8_t calcNextPower2Ex(uint16_t n) { uint8_t nextPow2; for (nextPow2 = 0; n > (1uL << nextPow2); ++nextPow2); return nextPow2; } /* compute which initial LZW-code length is needed */ static uint8_t calcInitCodeLen(uint16_t numEntries) { uint8_t index; index = calcNextPower2Ex(numEntries); return (index < 3) ? 3 : index + 1; } /* reset the dictionary of known LZW codes -- will reset the current code length as well */ static void resetDict(LZWGenState* pContext, const uint16_t initDictLen) { pContext->dictPos = initDictLen + 2; // reset current position in dictionary (number of colors + 2 for start and end code) pContext->mapPos = 1; pContext->pLZWData[pContext->LZWPos] = initDictLen; // issue clear-code ++(pContext->LZWPos); // increment position in LZW data // reset LZW list memset(pContext->pTreeInit, 0, initDictLen * sizeof(uint16_t) * initDictLen); memset(pContext->pTreeList, 0, ((sizeof(uint16_t) * 2) + sizeof(uint16_t)) * MAX_DICT_LEN); } /* add new child node */ static void add_child(LZWGenState* pContext, const uint16_t parentIndex, const uint16_t LZWIndex, const uint16_t initDictLen, const uint8_t nextColor) { uint16_t* pTreeList; uint16_t mapPos; pTreeList = pContext->pTreeList; mapPos = pTreeList[parentIndex * (2 + 1)]; if(!mapPos) { // if pTreeMap is not used yet for the parent node if(pTreeList[parentIndex * (2 + 1) + 2]) { // if at least one child node exists, switch to pTreeMap mapPos = pContext->mapPos; // add child to mapping table (pTreeMap) memset(pContext->pTreeMap + ((mapPos - 1) * initDictLen), 0, initDictLen * sizeof(uint16_t)); pContext->pTreeMap[(mapPos - 1) * initDictLen + nextColor] = LZWIndex; pTreeList[parentIndex * (2 + 1)] = mapPos; ++(pContext->mapPos); } else { // use the free spot in pTreeList for the child node pTreeList[parentIndex * (2 + 1) + 1] = nextColor; // color that leads to child node pTreeList[parentIndex * (2 + 1) + 2] = LZWIndex; // position of child node } } else { // directly add child node to pTreeMap pContext->pTreeMap[(mapPos - 1) * initDictLen + nextColor] = LZWIndex; } ++(pContext->dictPos); // increase current position in the dictionary } /* find next LZW code representing the longest pixel sequence that is still in the dictionary*/ static int lzw_crawl_tree(LZWGenState* pContext, uint32_t* pStrPos, uint16_t parentIndex, const uint16_t initDictLen) { uint16_t* pTreeInit; uint16_t* pTreeList; uint32_t strPos; uint16_t nextParent; uint16_t mapPos; if(parentIndex >= initDictLen) { return CGIF_EINDEX; // error: index in image data out-of-bounds } pTreeInit = pContext->pTreeInit; pTreeList = pContext->pTreeList; strPos = *pStrPos; // get the next LZW code from pTreeInit: // the initial nodes (0-255 max) have more children on average. // use the mapping approach right from the start for these nodes. if(strPos < (pContext->numPixel - 1)) { if(pContext->pImageData[strPos + 1] >= initDictLen) { return CGIF_EINDEX; // error: index in image data out-of-bounds } nextParent = pTreeInit[parentIndex * initDictLen + pContext->pImageData[strPos + 1]]; if(nextParent) { parentIndex = nextParent; ++strPos; } else { pContext->pLZWData[pContext->LZWPos] = parentIndex; // write last LZW code in LZW data ++(pContext->LZWPos); if(pContext->dictPos < MAX_DICT_LEN) { pTreeInit[parentIndex * initDictLen + pContext->pImageData[strPos + 1]] = pContext->dictPos; ++(pContext->dictPos); } else { resetDict(pContext, initDictLen); } ++strPos; *pStrPos = strPos; return CGIF_OK; } } // inner loop for codes > initDictLen while(strPos < (pContext->numPixel - 1)) { if(pContext->pImageData[strPos + 1] >= initDictLen) { return CGIF_EINDEX; // error: index in image data out-of-bounds } // first try to find child in LZW list if(pTreeList[parentIndex * (2 + 1) + 2] && pTreeList[parentIndex * (2 + 1) + 1] == pContext->pImageData[strPos + 1]) { parentIndex = pTreeList[parentIndex * (2 + 1) + 2]; ++strPos; continue; } // not found child yet? try to look into the LZW mapping table mapPos = pContext->pTreeList[parentIndex * (2 + 1)]; if(mapPos) { nextParent = pContext->pTreeMap[(mapPos - 1) * initDictLen + pContext->pImageData[strPos + 1]]; if(nextParent) { parentIndex = nextParent; ++strPos; continue; } } // still not found child? add current parentIndex to LZW data and add new child pContext->pLZWData[pContext->LZWPos] = parentIndex; // write last LZW code in LZW data ++(pContext->LZWPos); if(pContext->dictPos < MAX_DICT_LEN) { // if LZW-dictionary is not full yet add_child(pContext, parentIndex, pContext->dictPos, initDictLen, pContext->pImageData[strPos + 1]); // add new LZW code to dictionary } else { // the dictionary reached its maximum code => reset it (not required by GIF-standard but mostly done like this) resetDict(pContext, initDictLen); } ++strPos; *pStrPos = strPos; return CGIF_OK; } pContext->pLZWData[pContext->LZWPos] = parentIndex; // if the end of the image is reached, write last LZW code ++(pContext->LZWPos); ++strPos; *pStrPos = strPos; return CGIF_OK; } /* generate LZW-codes that compress the image data*/ static int lzw_generate(LZWGenState* pContext, uint16_t initDictLen) { uint32_t strPos; int r; uint8_t parentIndex; strPos = 0; // start at beginning of the image data resetDict(pContext, initDictLen); // reset dictionary and issue clear-code at first while(strPos < pContext->numPixel) { // while there are still image data to be encoded parentIndex = pContext->pImageData[strPos]; // start at root node // get longest sequence that is still in dictionary, return new position in image data r = lzw_crawl_tree(pContext, &strPos, (uint16_t)parentIndex, initDictLen); if(r != CGIF_OK) { return r; // error: return error code to callee } } pContext->pLZWData[pContext->LZWPos] = initDictLen + 1; // termination code ++(pContext->LZWPos); return CGIF_OK; } /* pack the LZW data into a byte sequence*/ static uint32_t create_byte_list(uint8_t *byteList, uint32_t lzwPos, uint16_t *lzwStr, uint16_t initDictLen, uint8_t initCodeLen){ uint32_t i; uint32_t dictPos; // counting new LZW codes uint16_t n = 2 * initDictLen; // if n - initDictLen == dictPos, the LZW code size is incremented by 1 bit uint32_t bytePos = 0; // position of current byte uint8_t bitOffset = 0; // number of bits used in the last byte uint8_t lzwCodeLen = initCodeLen; // dynamically increasing length of the LZW codes int correctLater = 0; // 1: one empty byte too much if end is reached after current code, 0 otherwise byteList[0] = 0; // except from the 1st byte all other bytes should be initialized stepwise (below) // the very first symbol might be the clear-code. However, this is not mandatory. Quote: // "Encoders should output a Clear code as the first code of each image data stream." // We keep the option to NOT output the clear code as the first symbol in this function. dictPos = 1; for(i = 0; i < lzwPos; ++i) { // loop over all LZW codes if((lzwCodeLen < MAX_CODE_LEN) && ((uint32_t)(n - (initDictLen)) == dictPos)) { // larger code is used for the 1st time at i = 256 ...+ 512 ...+ 1024 -> 256, 768, 1792 ++lzwCodeLen; // increment the length of the LZW codes (bit units) n *= 2; // set threshold for next increment of LZW code size } correctLater = 0; // 1 indicates that one empty byte is too much at the end byteList[bytePos] |= ((uint8_t)(lzwStr[i] << bitOffset)); // add 1st bits of the new LZW code to the byte containing part of the previous code if(lzwCodeLen + bitOffset >= 8) { // if the current byte is not enough of the LZW code if(lzwCodeLen + bitOffset == 8) { // if just this byte is filled exactly byteList[++bytePos] = 0; // byte is full -- go to next byte and initialize as 0 correctLater = 1; // use if one 0byte to much at the end } else if(lzwCodeLen + bitOffset < 16) { // if the next byte is not completely filled byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (8-bitOffset)); } else if(lzwCodeLen + bitOffset == 16) { // if the next byte is exactly filled by LZW code byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (8-bitOffset)); byteList[++bytePos] = 0; // byte is full -- go to next byte and initialize as 0 correctLater = 1; // use if one 0byte to much at the end } else { // lzw-code ranges over 3 bytes in total byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (8-bitOffset)); // write part of LZW code to next byte byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (16-bitOffset)); // write part of LZW code to byte after next byte } } bitOffset = (lzwCodeLen + bitOffset) % 8; // how many bits of the last byte are used? ++dictPos; // increment count of LZW codes if(lzwStr[i] == initDictLen) { // if a clear code appears in the LZW data lzwCodeLen = initCodeLen; // reset length of LZW codes n = 2 * initDictLen; // reset threshold for next increment of LZW code length dictPos = 1; // reset (see comment below) // take first code already into account to increment lzwCodeLen exactly when the code length cannot represent the current maximum symbol. // Note: This is usually done implicitly, as the very first symbol is a clear-code itself. } } // comment: the last byte can be zero in the following case only: // terminate code has been written (initial dict length + 1), but current code size is larger so padding zero bits were added and extend into the next byte(s). if(correctLater) { // if an unneccessaray empty 0-byte was initialized at the end --bytePos; // don't consider the last empty byte } return bytePos; } /* put byte sequence in blocks as required by GIF-format */ static uint32_t create_byte_list_block(uint8_t *byteList, uint8_t *byteListBlock, const uint32_t numBytes) { uint32_t i; uint32_t numBlock = numBytes / BLOCK_SIZE; // number of byte blocks with length BLOCK_SIZE uint8_t numRest = numBytes % BLOCK_SIZE; // number of bytes in last block (if not completely full) for(i = 0; i < numBlock; ++i) { // loop over all blocks byteListBlock[i * (BLOCK_SIZE+1)] = BLOCK_SIZE; // number of bytes in the following block memcpy(byteListBlock + 1+i*(BLOCK_SIZE+1), byteList + i*BLOCK_SIZE, BLOCK_SIZE); // copy block from byteList to byteListBlock } if(numRest>0) { byteListBlock[numBlock*(BLOCK_SIZE+1)] = numRest; // number of bytes in the following block memcpy(byteListBlock + 1+numBlock*(BLOCK_SIZE+1), byteList + numBlock*BLOCK_SIZE, numRest); // copy block from byteList to byteListBlock byteListBlock[1 + numBlock * (BLOCK_SIZE + 1) + numRest] = 0; // set 0 at end of frame return 1 + numBlock * (BLOCK_SIZE + 1) + numRest; // index of last entry in byteListBlock } // all LZW blocks in the frame have the same block size (BLOCK_SIZE), so there are no remaining bytes to be writen. byteListBlock[numBlock *(BLOCK_SIZE + 1)] = 0; // set 0 at end of frame return numBlock *(BLOCK_SIZE + 1); // index of last entry in byteListBlock } /* create all LZW raster data in GIF-format */ static int LZW_GenerateStream(LZWResult* pResult, const uint32_t numPixel, const uint8_t* pImageData, const uint16_t initDictLen, const uint8_t initCodeLen){ LZWGenState* pContext; uint32_t lzwPos, bytePos; uint32_t bytePosBlock; int r; // TBD recycle LZW tree list and map (if possible) to decrease the number of allocs pContext = malloc(sizeof(LZWGenState)); // TBD check return value of malloc pContext->pTreeInit = malloc((initDictLen * sizeof(uint16_t)) * initDictLen); // TBD check return value of malloc pContext->pTreeList = malloc(((sizeof(uint16_t) * 2) + sizeof(uint16_t)) * MAX_DICT_LEN); // TBD check return value of malloc TBD check size pContext->pTreeMap = malloc(((MAX_DICT_LEN / 2) + 1) * (initDictLen * sizeof(uint16_t))); // TBD check return value of malloc pContext->numPixel = numPixel; pContext->pImageData = pImageData; pContext->pLZWData = malloc(sizeof(uint16_t) * (numPixel + 2)); // TBD check return value of malloc pContext->LZWPos = 0; // actually generate the LZW sequence. r = lzw_generate(pContext, initDictLen); if(r != CGIF_OK) { goto LZWGENERATE_Cleanup; } lzwPos = pContext->LZWPos; // pack the generated LZW data into blocks of 255 bytes uint8_t *byteList; // lzw-data packed in byte-list uint8_t *byteListBlock; // lzw-data packed in byte-list with 255-block structure uint64_t MaxByteListLen = MAX_CODE_LEN * lzwPos / 8ull + 2ull + 1ull; // conservative upper bound uint64_t MaxByteListBlockLen = MAX_CODE_LEN * lzwPos * (BLOCK_SIZE + 1ull) / 8ull / BLOCK_SIZE + 2ull + 1ull +1ull; // conservative upper bound byteList = malloc(MaxByteListLen); // TBD check return value of malloc byteListBlock = malloc(MaxByteListBlockLen); // TBD check return value of malloc bytePos = create_byte_list(byteList,lzwPos, pContext->pLZWData, initDictLen, initCodeLen); bytePosBlock = create_byte_list_block(byteList, byteListBlock, bytePos+1); free(byteList); pResult->sizeRasterData = bytePosBlock + 1; // save pResult->pRasterData = byteListBlock; LZWGENERATE_Cleanup: free(pContext->pLZWData); free(pContext->pTreeInit); free(pContext->pTreeList); free(pContext->pTreeMap); free(pContext); return r; } /* initialize the header of the GIF */ static void initMainHeader(const CGIFRaw_Config* pConfig, uint8_t* pHeader) { uint16_t width, height; uint8_t pow2GlobalPalette; width = pConfig->width; height = pConfig->height; // set header to a clean state memset(pHeader, 0, SIZE_MAIN_HEADER); // set Signature field to value "GIF" pHeader[HEADER_OFFSET_SIGNATURE] = 'G'; pHeader[HEADER_OFFSET_SIGNATURE + 1] = 'I'; pHeader[HEADER_OFFSET_SIGNATURE + 2] = 'F'; // set Version field to value "89a" pHeader[HEADER_OFFSET_VERSION] = '8'; pHeader[HEADER_OFFSET_VERSION + 1] = '9'; pHeader[HEADER_OFFSET_VERSION + 2] = 'a'; // set width of screen (LE ordering) const uint16_t widthLE = hU16toLE(width); memcpy(pHeader + HEADER_OFFSET_WIDTH, &widthLE, sizeof(uint16_t)); // set height of screen (LE ordering) const uint16_t heightLE = hU16toLE(height); memcpy(pHeader + HEADER_OFFSET_HEIGHT, &heightLE, sizeof(uint16_t)); // init packed field if(pConfig->sizeGCT) { pHeader[HEADER_OFFSET_PACKED_FIELD] = (1 << 7); // M = 1 (see GIF specc): global color table is present // calculate needed size of global color table (GCT). // MUST be a power of two. pow2GlobalPalette = calcNextPower2Ex(pConfig->sizeGCT); pow2GlobalPalette = (pow2GlobalPalette < 1) ? 1 : pow2GlobalPalette; // minimum size is 2^1 pHeader[HEADER_OFFSET_PACKED_FIELD] |= ((pow2GlobalPalette - 1) << 0); // set size of GCT (0 - 7 in header + 1) } } /* initialize NETSCAPE app extension block (needed for animation) */ static void initAppExtBlock(uint8_t* pAppExt, uint16_t numLoops) { memset(pAppExt, 0, SIZE_APP_EXT); // set data pAppExt[0] = 0x21; pAppExt[1] = 0xFF; // start of block pAppExt[2] = 0x0B; // eleven bytes to follow // write identifier for Netscape animation extension pAppExt[APPEXT_OFFSET_NAME] = 'N'; pAppExt[APPEXT_OFFSET_NAME + 1] = 'E'; pAppExt[APPEXT_OFFSET_NAME + 2] = 'T'; pAppExt[APPEXT_OFFSET_NAME + 3] = 'S'; pAppExt[APPEXT_OFFSET_NAME + 4] = 'C'; pAppExt[APPEXT_OFFSET_NAME + 5] = 'A'; pAppExt[APPEXT_OFFSET_NAME + 6] = 'P'; pAppExt[APPEXT_OFFSET_NAME + 7] = 'E'; pAppExt[APPEXT_OFFSET_NAME + 8] = '2'; pAppExt[APPEXT_OFFSET_NAME + 9] = '.'; pAppExt[APPEXT_OFFSET_NAME + 10] = '0'; pAppExt[APPEXT_OFFSET_NAME + 11] = 0x03; // 3 bytes to follow pAppExt[APPEXT_OFFSET_NAME + 12] = 0x01; // TBD clarify // set number of repetitions (animation; LE ordering) const uint16_t netscapeLE = hU16toLE(numLoops); memcpy(pAppExt + APPEXT_NETSCAPE_OFFSET_LOOPS, &netscapeLE, sizeof(uint16_t)); } /* write numBytes dummy bytes */ static int writeDummyBytes(cgif_write_fn* pWriteFn, void* pContext, int numBytes) { int rWrite = 0; const uint8_t dummyByte = 0; for(int i = 0; i < numBytes; ++i) { rWrite |= pWriteFn(pContext, &dummyByte, 1); } return rWrite; } CGIFRaw* cgif_raw_newgif(const CGIFRaw_Config* pConfig) { uint8_t aAppExt[SIZE_APP_EXT]; uint8_t aHeader[SIZE_MAIN_HEADER]; CGIFRaw* pGIF; int rWrite; // check for invalid GCT size if(pConfig->sizeGCT > 256) { return NULL; // invalid GCT size } pGIF = malloc(sizeof(CGIFRaw)); if(!pGIF) { return NULL; } memcpy(&(pGIF->config), pConfig, sizeof(CGIFRaw_Config)); // initiate all sections we can at this stage: // - main GIF header // - global color table (GCT), if required // - netscape application extension (for animation), if required initMainHeader(pConfig, aHeader); rWrite = pConfig->pWriteFn(pConfig->pContext, aHeader, SIZE_MAIN_HEADER); // GCT required? => write it. if(pConfig->sizeGCT) { rWrite |= pConfig->pWriteFn(pConfig->pContext, pConfig->pGCT, pConfig->sizeGCT * 3); uint8_t pow2GCT = calcNextPower2Ex(pConfig->sizeGCT); pow2GCT = (pow2GCT < 1) ? 1 : pow2GCT; // minimum size is 2^1 const uint16_t numBytesLeft = ((1 << pow2GCT) - pConfig->sizeGCT) * 3; rWrite |= writeDummyBytes(pConfig->pWriteFn, pConfig->pContext, numBytesLeft); } // GIF should be animated? => init & write app extension header ("NETSCAPE2.0") // No loop? Don't write NETSCAPE extension. if((pConfig->attrFlags & CGIF_RAW_ATTR_IS_ANIMATED) && !(pConfig->attrFlags & CGIF_RAW_ATTR_NO_LOOP)) { initAppExtBlock(aAppExt, pConfig->numLoops); rWrite |= pConfig->pWriteFn(pConfig->pContext, aAppExt, SIZE_APP_EXT); } // check for write errors if(rWrite) { free(pGIF); return NULL; } // assume error per default. // set to CGIF_OK by the first successful cgif_raw_addframe() call, as a GIF without frames is invalid. pGIF->curResult = CGIF_PENDING; return pGIF; } /* add new frame to the raw GIF stream */ cgif_result cgif_raw_addframe(CGIFRaw* pGIF, const CGIFRaw_FrameConfig* pConfig) { uint8_t aFrameHeader[SIZE_FRAME_HEADER]; uint8_t aGraphicExt[SIZE_GRAPHIC_EXT]; LZWResult encResult; int r, rWrite; const int useLCT = pConfig->sizeLCT; // LCT stands for "local color table" const int isInterlaced = (pConfig->attrFlags & CGIF_RAW_FRAME_ATTR_INTERLACED) ? 1 : 0; uint16_t numEffColors; // number of effective colors uint16_t initDictLen; uint8_t pow2LCT, initCodeLen; if(pGIF->curResult != CGIF_OK && pGIF->curResult != CGIF_PENDING) { return pGIF->curResult; // return previous error } // check for invalid LCT size if(pConfig->sizeLCT > 256) { pGIF->curResult = CGIF_ERROR; // invalid LCT size return pGIF->curResult; } rWrite = 0; // set frame header to a clean state memset(aFrameHeader, 0, SIZE_FRAME_HEADER); // set needed fields in frame header aFrameHeader[0] = ','; // set frame seperator if(useLCT) { pow2LCT = calcNextPower2Ex(pConfig->sizeLCT); pow2LCT = (pow2LCT < 1) ? 1 : pow2LCT; // minimum size is 2^1 IMAGE_PACKED_FIELD(aFrameHeader) = (1 << 7); // set size of local color table (0-7 in header + 1) IMAGE_PACKED_FIELD(aFrameHeader) |= ((pow2LCT- 1) << 0); numEffColors = pConfig->sizeLCT; } else { numEffColors = pGIF->config.sizeGCT; // global color table in use } // encode frame interlaced? IMAGE_PACKED_FIELD(aFrameHeader) |= (isInterlaced << 6); // transparency in use? we might need to increase numEffColors if((pGIF->config.attrFlags & (CGIF_RAW_ATTR_IS_ANIMATED)) && (pConfig->attrFlags & (CGIF_RAW_FRAME_ATTR_HAS_TRANS)) && pConfig->transIndex >= numEffColors) { numEffColors = pConfig->transIndex + 1; } // calculate initial code length and initial dict length initCodeLen = calcInitCodeLen(numEffColors); initDictLen = 1uL << (initCodeLen - 1); const uint8_t initialCodeSize = initCodeLen - 1; const uint16_t frameWidthLE = hU16toLE(pConfig->width); const uint16_t frameHeightLE = hU16toLE(pConfig->height); const uint16_t frameTopLE = hU16toLE(pConfig->top); const uint16_t frameLeftLE = hU16toLE(pConfig->left); memcpy(aFrameHeader + IMAGE_OFFSET_WIDTH, &frameWidthLE, sizeof(uint16_t)); memcpy(aFrameHeader + IMAGE_OFFSET_HEIGHT, &frameHeightLE, sizeof(uint16_t)); memcpy(aFrameHeader + IMAGE_OFFSET_TOP, &frameTopLE, sizeof(uint16_t)); memcpy(aFrameHeader + IMAGE_OFFSET_LEFT, &frameLeftLE, sizeof(uint16_t)); // apply interlaced pattern // TBD creating a copy of pImageData is not ideal, but changes on the LZW encoding would // be necessary otherwise. if(isInterlaced) { uint8_t* pInterlaced = malloc(MULU16(pConfig->width, pConfig->height)); if(pInterlaced == NULL) { pGIF->curResult = CGIF_EALLOC; return pGIF->curResult; } uint8_t* p = pInterlaced; // every 8th row (starting with row 0) for(uint32_t i = 0; i < pConfig->height; i += 8) { memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); p += pConfig->width; } // every 8th row (starting with row 4) for(uint32_t i = 4; i < pConfig->height; i += 8) { memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); p += pConfig->width; } // every 4th row (starting with row 2) for(uint32_t i = 2; i < pConfig->height; i += 4) { memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); p += pConfig->width; } // every 2th row (starting with row 1) for(uint32_t i = 1; i < pConfig->height; i += 2) { memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); p += pConfig->width; } r = LZW_GenerateStream(&encResult, MULU16(pConfig->width, pConfig->height), pInterlaced, initDictLen, initCodeLen); free(pInterlaced); } else { r = LZW_GenerateStream(&encResult, MULU16(pConfig->width, pConfig->height), pConfig->pImageData, initDictLen, initCodeLen); } // generate LZW raster data (actual image data) // check for errors if(r != CGIF_OK) { pGIF->curResult = r; return r; } // check whether the Graphic Control Extension is required or not: // It's required for animations and frames with transparency. int needsGraphicCtrlExt = (pGIF->config.attrFlags & CGIF_RAW_ATTR_IS_ANIMATED) | (pConfig->attrFlags & CGIF_RAW_FRAME_ATTR_HAS_TRANS); // do things for animation / transparency, if required. if(needsGraphicCtrlExt) { memset(aGraphicExt, 0, SIZE_GRAPHIC_EXT); aGraphicExt[0] = 0x21; aGraphicExt[1] = 0xF9; aGraphicExt[2] = 0x04; aGraphicExt[3] = pConfig->disposalMethod; // set flag indicating that transparency is used, if required. if(pConfig->attrFlags & CGIF_RAW_FRAME_ATTR_HAS_TRANS) { aGraphicExt[3] |= 0x01; aGraphicExt[6] = pConfig->transIndex; } // set delay (LE ordering) const uint16_t delayLE = hU16toLE(pConfig->delay); memcpy(aGraphicExt + GEXT_OFFSET_DELAY, &delayLE, sizeof(uint16_t)); // write Graphic Control Extension rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, aGraphicExt, SIZE_GRAPHIC_EXT); } // write frame rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, aFrameHeader, SIZE_FRAME_HEADER); if(useLCT) { rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, pConfig->pLCT, pConfig->sizeLCT * 3); const uint16_t numBytesLeft = ((1 << pow2LCT) - pConfig->sizeLCT) * 3; rWrite |= writeDummyBytes(pGIF->config.pWriteFn, pGIF->config.pContext, numBytesLeft); } rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, &initialCodeSize, 1); rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, encResult.pRasterData, encResult.sizeRasterData); // check for write errors if(rWrite) { pGIF->curResult = CGIF_EWRITE; } else { pGIF->curResult = CGIF_OK; } // cleanup free(encResult.pRasterData); return pGIF->curResult; } cgif_result cgif_raw_close(CGIFRaw* pGIF) { int rWrite; cgif_result result; rWrite = pGIF->config.pWriteFn(pGIF->config.pContext, (unsigned char*) ";", 1); // write term symbol // check for write errors if(rWrite) { pGIF->curResult = CGIF_EWRITE; } result = pGIF->curResult; free(pGIF); return result; } cgif-0.4.1/tests/000077500000000000000000000000001463435475600136075ustar00rootroot00000000000000cgif-0.4.1/tests/all_optim.c000066400000000000000000000044751463435475600157450ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 static void initGIFConfig(CGIF_Config* pConfig, uint8_t* pGlobalPalette, uint16_t numColors, uint32_t attrFlags, uint16_t width, uint16_t height) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->attrFlags = attrFlags; pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pGlobalPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = "all_optim.gif"; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay, uint32_t genFlags) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->pImageData = pImageData; pConfig->delay = delay; pConfig->genFlags = genFlags; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; uint8_t numColors = 2; // number of colors in aPalette // // create new GIF initGIFConfig(&gConfig, aPalette, numColors, CGIF_ATTR_IS_ANIMATED, WIDTH, HEIGHT); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); initFrameConfig(&fConfig, pImageData, 100, CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW); cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData + 7, 1, 41); memset(pImageData + 7 + WIDTH, 1, 41); memset(pImageData + 7 + WIDTH * 2, 1, 41); memset(pImageData + 7 + WIDTH * 2, 0, 29); memset(pImageData + 7 + WIDTH * 3, 1, 41); memset(pImageData + 7 + WIDTH * 3, 0, 23); memset(pImageData + 7 + WIDTH * 4, 0, 37); r = cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // Free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/alpha.c000066400000000000000000000032111463435475600150350ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 99 #define HEIGHT 99 int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0xFF, 0x00, 0x00, // green 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.path = "alpha.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // create an off/on pattern for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = i % 2; } cgif_addframe(pGIF, &fConfig); // create transparent frame (alpha channel) for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = 2; } fConfig.attrFlags = CGIF_FRAME_ATTR_HAS_ALPHA; fConfig.transIndex = 2; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/animated_color_gradient.c000066400000000000000000000051751463435475600206200ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 256 #define HEIGHT 16 /* This is an example code that creates a GIF-animation with a moving red color gradient with the maximum number of colors (1st and last color is not red)*/ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[3*WIDTH]; cgif_result r; // define red color gradient for (int j = 0; j < WIDTH; ++j) { aPalette[j*3] = j; aPalette[j*3+1] = 0; aPalette[j*3+2] = 0; } // set first color to blue aPalette[0] = 0; aPalette[1] = 0; aPalette[2] = 0xFF; // set last color to green aPalette[3*(WIDTH-1)] = 0; aPalette[3*(WIDTH-1)+1] = 0xFF; aPalette[3*(WIDTH-1)+2] = 0; uint16_t numColors = (uint16_t)(WIDTH); // number of colors in aPalette int numFrames = WIDTH; // number of frames in the video memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; // set needed attribution flag (as GIF is animated) gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "animated_color_gradient.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // actual image data fConfig.genFlags = CGIF_FRAME_GEN_USE_DIFF_WINDOW | CGIF_FRAME_GEN_USE_TRANSPARENCY; // it should be automatically detected that transparency optimization is not possible if 256 colors are used fConfig.pImageData = pImageData; // set pointer to image data fConfig.delay = 5; // set time before next frame (in units of 0.01 s) for (int f = 0; f < numFrames; ++f) { for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = (unsigned char)((i + f) % WIDTH); // ceate a moving stripe pattern } r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/animated_interlaced.c000066400000000000000000000103641463435475600177330ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 40 #define HEIGHT 40 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; pConfig->attrFlags = CGIF_ATTR_IS_ANIMATED; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->delay = delay; pConfig->pImageData = pImageData; pConfig->attrFlags = CGIF_FRAME_ATTR_INTERLACED; pConfig->genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW; // use optimizations } /* This is an example code that creates a GIF-animation with a moving snake. */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) int x,y,x1,y1; // position of snake beginning and end cgif_result r; uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue uint8_t numColors = 3; // number of colors in aPalette (up to 256 possible) int numFrames = 37*4; // number of frames in the video // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "animated_interlaced.gif", WIDTH, HEIGHT, aPalette, numColors); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // create image frames and add them to GIF pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data memset(pImageData, 0, WIDTH * HEIGHT); // set the background color to 1st color in palette x = 1; // set start position... y = 1; x1 = 1; y1 = 15; for (int i = 1; i < 15; ++i) { // draw the snake pImageData[1 + i*WIDTH] = 1; } for (int f = 0; f < numFrames; ++f) { // loop over all frames pImageData[x + y*WIDTH] = 0; // remove end of the snake pImageData[x1 + y1*WIDTH] = 1; // moving pixel at snake head if (x==1 && y1){ y--; } else{ x--; } if (x1==1 && y11){ y1--; } else{ x1--; } initFrameConfig(&fConfig, pImageData, 5); // initialize the frame-configuration r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // free image data when all frames are added // close created GIF-file and free allocated space r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/animated_single_pixel.c000066400000000000000000000077631463435475600203140ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 40 #define HEIGHT 40 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; pConfig->attrFlags = CGIF_ATTR_IS_ANIMATED; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->delay = delay; pConfig->pImageData = pImageData; pConfig->genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW; // use optimizations } /* This is an example code that creates a GIF-animation with a single moving pixel. */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) int x,y; // position of the pixel uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue cgif_result r; uint8_t numColors = 3; // number of colors in aPalette (up to 256 possible) int numFrames = 39*4; // number of frames in the video // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "animated_single_pixel.gif", WIDTH, HEIGHT, aPalette, numColors); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // create image frames and add them to GIF pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data memset(pImageData, 0, WIDTH * HEIGHT); // set everything to the first color x = 0; // start position of the pixel (x) y = 0; // start position of the pixel (y) for (int f = 0; f < numFrames; ++f) { // loop over all frames pImageData[x + y*WIDTH] = 0; // pixel at previous position back to the 1st color if (x==0 && y0){ y--; } else{ x--; } pImageData[x + y*WIDTH] = 1; // set color of next pixel to green initFrameConfig(&fConfig, pImageData, 5); // initialize the frame-configuration r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // free image data when all frames are added // close created GIF-file and free allocated space r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/animated_snake.c000066400000000000000000000102771463435475600167250ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 40 #define HEIGHT 40 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; pConfig->attrFlags = CGIF_ATTR_IS_ANIMATED; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->delay = delay; pConfig->pImageData = pImageData; pConfig->genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW; // use optimizations } /* This is an example code that creates a GIF-animation with a moving snake. */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) int x,y,x1,y1; // position of snake beginning and end cgif_result r; uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue uint8_t numColors = 3; // number of colors in aPalette (up to 256 possible) int numFrames = 37*4; // number of frames in the video // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "animated_snake.gif", WIDTH, HEIGHT, aPalette, numColors); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // create image frames and add them to GIF pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data memset(pImageData, 0, WIDTH * HEIGHT); // set the background color to 1st color in palette x = 1; // set start position... y = 1; x1 = 1; y1 = 15; for (int i = 1; i < 15; ++i) { // draw the snake pImageData[1 + i*WIDTH] = 1; } for (int f = 0; f < numFrames; ++f) { // loop over all frames pImageData[x + y*WIDTH] = 0; // remove end of the snake pImageData[x1 + y1*WIDTH] = 1; // moving pixel at snake head if (x==1 && y1){ y--; } else{ x--; } if (x1==1 && y11){ y1--; } else{ x1--; } initFrameConfig(&fConfig, pImageData, 5); // initialize the frame-configuration r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // free image data when all frames are added // close created GIF-file and free allocated space r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/animated_stripe_pattern.c000066400000000000000000000043021463435475600206570ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-animation with a moving stripe-pattern. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white 0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF, // blue }; cgif_result r; uint8_t numColors = 5; // number of colors in aPalette int numFrames = 30; // number of frames in the video memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; // set needed attribution flag (as GIF is animated) gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "animated_stripe_pattern.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // actual image data fConfig.genFlags = CGIF_FRAME_GEN_USE_DIFF_WINDOW | CGIF_FRAME_GEN_USE_TRANSPARENCY; fConfig.pImageData = pImageData; // set pointer to image data fConfig.delay = 10; // set time before next frame (in units of 0.01 s) for (int f = 0; f < numFrames; ++f) { for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = (unsigned char)((f + i % WIDTH) % numColors); // ceate a moving stripe pattern } r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/animated_stripe_pattern_2.c000066400000000000000000000067651463435475600211170ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; pConfig->attrFlags = CGIF_ATTR_IS_ANIMATED; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->delay = delay; pConfig->pImageData = pImageData; pConfig->genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW; // use optimizations } /* This is an example code that creates a GIF-animation with a moving stripe-pattern in part of the frame. */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue cgif_result r; uint8_t numColors = 3; // number of colors in aPalette (up to 256 possible) int numFrames = 24; // number of frames in the video // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "animated_stripe_pattern_2.gif", WIDTH, HEIGHT, aPalette, numColors); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // create image frames and add them to GIF pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data memset(pImageData, 0, WIDTH * HEIGHT); // set everything to 1st color in the palette for (int f = 0; f < numFrames; ++f) { // loop over all frames for (int i = (WIDTH * HEIGHT)/2; i < (WIDTH * HEIGHT)/4*3; ++i) { // only draw in certain window of the frame pImageData[i] = (unsigned char)((f + i % WIDTH ) / 8 % numColors); // moving stripe pattern (4 pixels per stripe) } initFrameConfig(&fConfig, pImageData, 10); // initialize the frame-configuration r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // free image data when all frames are added // close created GIF-file and free allocated space r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/animated_stripes_horizontal.c000066400000000000000000000043451463435475600215650ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-animation with a moving stripe-pattern. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0xFF, 0x00, 0x00, 0xEE, 0x00, 0x00, 0xDD, 0x00, 0x00, 0xCC, 0x00, 0x00, 0xBB, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x99, 0x00, 0x00, 0x88, 0x00, 0x00, 0x77, 0x00, 0x00, 0x66, 0x00, 0x00, }; cgif_result r; uint8_t numColors = 10; // number of colors in aPalette int numFrames = 10; // number of frames in the video memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; // set needed attribution flag (as GIF is animated) gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "animated_stripes_horizontal.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // actual image data fConfig.genFlags = CGIF_FRAME_GEN_USE_DIFF_WINDOW | CGIF_FRAME_GEN_USE_TRANSPARENCY; fConfig.pImageData = pImageData; // set pointer to image data fConfig.delay = 50; // set time before next frame (in units of 0.01 s) for (int f = 0; f < numFrames; ++f) { for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = (unsigned char)((f + numColors * i / WIDTH / HEIGHT) % numColors); // ceate a moving stripe pattern } r = cgif_addframe(pGIF, &fConfig); // append the new frame } free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/duplicate_frames.c000066400000000000000000000035761463435475600172750ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-animation with two identical frames in sequence*/ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; uint8_t numColors = 2; // number of colors in aPalette cgif_result r; // // Create new GIF // memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "duplicate_frames.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // Add frames to GIF // pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // set time before next frame (in units of 0.01 s) r = cgif_addframe(pGIF, &fConfig); // append the new frame r = cgif_addframe(pGIF, &fConfig); // append the next frame r = cgif_addframe(pGIF, &fConfig); // append the next frame r = cgif_addframe(pGIF, &fConfig); // append the next frame r = cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/earlyclose.c000066400000000000000000000024521463435475600161200ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; // just ignore GIF data return 0; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.pWriteFn = pWriteFn; gConfig.pContext = NULL; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // close GIF without adding frames before r = cgif_close(pGIF); // free allocated space at the end of the session // check for correct error if(r != CGIF_OK) { return 0; } fputs("CGIF_ERROR expected as result code\n", stderr); return 2; } cgif-0.4.1/tests/eindex.c000066400000000000000000000031121463435475600152240ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; // just ignore GIF data return 0; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.pWriteFn = pWriteFn; gConfig.pContext = NULL; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); pImageData[2] = 32; // 32 is not a valid index in this case. fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for correct error: CGIF_EINDEX if(r == CGIF_EINDEX) { return 0; } fputs("CGIF_EINDEX expected as result code\n", stderr); return 2; } cgif-0.4.1/tests/eindex_anim.c000066400000000000000000000032711463435475600162360ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; // just ignore GIF data return 0; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.pWriteFn = pWriteFn; gConfig.pContext = NULL; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); pImageData[2] = 32; // 32 is not a valid index in this case. r = cgif_addframe(pGIF, &fConfig); r = cgif_addframe(pGIF, &fConfig); r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for correct error: CGIF_EINDEX if(r == CGIF_EINDEX) { return 0; } fputs("CGIF_EINDEX expected as result code\n", stderr); return 2; } cgif-0.4.1/tests/enopalette.c000066400000000000000000000032121463435475600161110ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 1 #define HEIGHT 1 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; // just ignore GIF data return 0; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = NULL; gConfig.numGlobalPaletteEntries = 4; // set the number global palette entries to an invalid value gConfig.pWriteFn = pWriteFn; gConfig.pContext = NULL; gConfig.attrFlags = CGIF_ATTR_NO_GLOBAL_TABLE | CGIF_ATTR_IS_ANIMATED; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for correct error: CGIF_ERROR // error is expected, because we don't provide any color palette. if(r == CGIF_ERROR) { return 0; } fputs("CGIF_ERROR expected as result code\n", stderr); return 2; } cgif-0.4.1/tests/ewrite.c000066400000000000000000000027501463435475600152560ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; return -1; // return error code -1: should lead to EWRITE } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.pWriteFn = pWriteFn; gConfig.pContext = NULL; // // create new GIF (might call pWriteFn as well) pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { return 0; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r == CGIF_EWRITE) { return 0; } fputs("CGIF_EWRITE expected as result code\n", stderr); return 1; } cgif-0.4.1/tests/ezeroheight.c000066400000000000000000000041371463435475600162750ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 1 #define HEIGHT 0 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; // just ignore GIF data return 0; } static void initGIFConfig(CGIF_Config* pConfig, uint8_t* pGlobalPalette, uint16_t numColors, uint32_t attrFlags, uint16_t width, uint16_t height) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->attrFlags = attrFlags; pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pGlobalPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->pWriteFn = pWriteFn; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay, uint32_t genFlags) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->pImageData = pImageData; pConfig->delay = delay; pConfig->genFlags = genFlags; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; uint8_t numColors = 2; // number of colors in aPalette // // create new GIF initGIFConfig(&gConfig, aPalette, numColors, CGIF_ATTR_IS_ANIMATED, WIDTH, HEIGHT); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { // expected: it isn't possible to create a GIF with zero width/height return 0; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); initFrameConfig(&fConfig, pImageData, 100, CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW); cgif_addframe(pGIF, &fConfig); // append the new frame cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // Free allocated space at the end of the session cgif_close(pGIF); // invalid dimensions should have been catched earlier return 1; } cgif-0.4.1/tests/ezerowidth.c000066400000000000000000000041371463435475600161440ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 0 #define HEIGHT 1 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; // just ignore GIF data return 0; } static void initGIFConfig(CGIF_Config* pConfig, uint8_t* pGlobalPalette, uint16_t numColors, uint32_t attrFlags, uint16_t width, uint16_t height) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->attrFlags = attrFlags; pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pGlobalPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->pWriteFn = pWriteFn; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay, uint32_t genFlags) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->pImageData = pImageData; pConfig->delay = delay; pConfig->genFlags = genFlags; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; uint8_t numColors = 2; // number of colors in aPalette // // create new GIF initGIFConfig(&gConfig, aPalette, numColors, CGIF_ATTR_IS_ANIMATED, WIDTH, HEIGHT); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { // expected: it isn't possible to create a GIF with zero width/height return 0; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); initFrameConfig(&fConfig, pImageData, 100, CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW); cgif_addframe(pGIF, &fConfig); // append the new frame cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // Free allocated space at the end of the session cgif_close(pGIF); // invalid dimensions should have been catched earlier return 1; } cgif-0.4.1/tests/ezerowidthheight.c000066400000000000000000000041361463435475600173340ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 0 #define HEIGHT 0 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { (void)pContext; (void)pData; (void)numBytes; // just ignore GIF data return 0; } static void initGIFConfig(CGIF_Config* pConfig, uint8_t* pGlobalPalette, uint16_t numColors, uint32_t attrFlags, uint16_t width, uint16_t height) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->attrFlags = attrFlags; pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pGlobalPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->pWriteFn = pWriteFn; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay, uint32_t genFlags) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->pImageData = pImageData; pConfig->delay = delay; pConfig->genFlags = genFlags; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; int8_t numColors = 2; // number of colors in aPalette // // create new GIF initGIFConfig(&gConfig, aPalette, numColors, CGIF_ATTR_IS_ANIMATED, WIDTH, HEIGHT); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { // expected: it isn't possible to create a GIF with zero width/height return 0; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); initFrameConfig(&fConfig, pImageData, 100, CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW); cgif_addframe(pGIF, &fConfig); // append the new frame cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // Free allocated space at the end of the session cgif_close(pGIF); // invalid dimensions should have been catched earlier return 1; } cgif-0.4.1/tests/global_plus_local_table.c000066400000000000000000000042471463435475600206060ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 110 #define HEIGHT 125 /* This is an example code that creates a GIF-animation with a moving stripe-pattern. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aLocalPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; uint8_t aGlobalPalette[] = { 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF, // blue 0xFF, 0x00, 0x00, // red }; cgif_result r; uint8_t numColorsLocal = 2; // number of colors in aPalette uint8_t numColorsGlobal = 3; // number of colors in aPalette // // Create new GIF // memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.pGlobalPalette = aGlobalPalette; gConfig.numGlobalPaletteEntries = numColorsGlobal; gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.path = "global_plus_local_table.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // Add frame to GIF // pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 1, WIDTH * HEIGHT); memset(pImageData + WIDTH * 2, 0, WIDTH * 3); fConfig.pImageData = pImageData; fConfig.pLocalPalette = aLocalPalette; fConfig.numLocalPaletteEntries = numColorsLocal; fConfig.attrFlags = CGIF_FRAME_ATTR_USE_LOCAL_TABLE; fConfig.delay = 100; r = cgif_addframe(pGIF, &fConfig); // append the new frame // // add next frame fConfig.attrFlags = 0; fConfig.genFlags = 0; memset(pImageData + WIDTH * 9, 2, WIDTH * 10); r = cgif_addframe(pGIF, &fConfig); // append the new frame // free(pImageData); // // Free allocated space at the end of the session // r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/global_plus_local_table_with_optim.c000066400000000000000000000122341463435475600230440ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 40 #define HEIGHT 40 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; pConfig->attrFlags = CGIF_ATTR_IS_ANIMATED; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->delay = delay; pConfig->pImageData = pImageData; pConfig->genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW; // use optimizations } /* This is an example code that creates a GIF-animation with global and local color table, also using the main optimizations to reduce the GIF size */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) int f; // frame number uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue uint8_t aPalette_local[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0x00}; // black cgif_result r; uint8_t numColors_global = 3; // number of colors in global color palette (up to 256 possible) uint8_t numColors_local = 3; // number of colors in local color palette (up to 256 possible) int numFrames = 10; // number of frames in the animation // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "global_plus_local_table_with_optim.gif", WIDTH, HEIGHT, aPalette, numColors_global); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // create image frames and add them to GIF (use global color table first) pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data memset(pImageData, 0, WIDTH * HEIGHT); // set the background color to 1st color in global palette for (f = 0; f < numFrames-2; ++f) { pImageData[f + WIDTH * (HEIGHT/2)] = 1; initFrameConfig(&fConfig, pImageData, 20); // initialize the frame-configuration (3rd parameter is the delay in 0.01 s) r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } // add frame with local color table (one additional black pixel) pImageData[WIDTH * (HEIGHT/2) + f] = 2; fConfig.pImageData = pImageData; fConfig.pLocalPalette = aPalette_local; fConfig.numLocalPaletteEntries = numColors_local; fConfig.attrFlags = CGIF_FRAME_ATTR_USE_LOCAL_TABLE; fConfig.delay = 200; // delay in 0.01 s r = cgif_addframe(pGIF, &fConfig); // append the new frame // add another frame with local color table pImageData[WIDTH * (HEIGHT/2) + f + 2] = 1; pImageData[WIDTH * (HEIGHT/2) + f + 4] = 1; pImageData[WIDTH * (HEIGHT/2) + f + 6] = 1; pImageData[WIDTH * (HEIGHT/2) + f + 8] = 1; r = cgif_addframe(pGIF, &fConfig); // append the new frame // refresh pattern and go on with global color table memset(pImageData, 0, WIDTH * HEIGHT); // set the background color to 1st color in global palette for (f = 0; f < numFrames-2; ++f) { pImageData[2*f + WIDTH * (HEIGHT/2)] = 2; initFrameConfig(&fConfig, pImageData, 50); // initialize the frame-configuration (3rd parameter is the delay in 0.01 s) r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // free image data when all frames are added // close created GIF-file and free allocated space r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/has_transparency.c000066400000000000000000000032321463435475600173170ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 99 #define HEIGHT 99 int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black (transparent) 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED | CGIF_ATTR_HAS_TRANSPARENCY; // first entry in color table is transparency gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.path = "has_transparency.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // create an off/on pattern for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = i % 2; } cgif_addframe(pGIF, &fConfig); // create opposite pattern for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = 1 - (i % 2); } r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/has_transparency_2.c000066400000000000000000000032521463435475600175420ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 99 #define HEIGHT 99 int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black (transparent) 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED | CGIF_ATTR_HAS_TRANSPARENCY; // first entry in color table is transparency gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.path = "has_transparency_2.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // create an off/on pattern for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = (i/10) % 2; } r = cgif_addframe(pGIF, &fConfig); // create opposite pattern for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = 1 - ((i/10) % 2); } r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/local_transp.c000066400000000000000000000035441463435475600164420ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 103 #define HEIGHT 104 /* This is an example code that creates a GIF-animation with a moving stripe-pattern. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[3 * 256] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; uint16_t numColors = 256; // number of colors in aPalette // // Create new GIF // memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_NO_GLOBAL_TABLE | CGIF_ATTR_IS_ANIMATED; gConfig.genFlags = CGIF_GEN_KEEP_IDENT_FRAMES; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.path = "local_transp.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // Add frame to GIF // pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 1, WIDTH * HEIGHT); memset(pImageData + WIDTH * 2, 0, WIDTH * 3); fConfig.pImageData = pImageData; fConfig.pLocalPalette = aPalette; fConfig.numLocalPaletteEntries = numColors; fConfig.attrFlags = CGIF_FRAME_ATTR_USE_LOCAL_TABLE; fConfig.genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY; r = cgif_addframe(pGIF, &fConfig); // append the new frame r = cgif_addframe(pGIF, &fConfig); // append the new frame // free(pImageData); // // Free allocated space at the end of the session // r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/max_color_table_test.c000066400000000000000000000027541463435475600201540ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-image with all green pixels. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t* aPalette; cgif_result r; uint16_t numColors = 256; // number of colors in aPalette // // create an image aPalette = malloc(256 * 3); memset(aPalette, 0, 256 * 3); pImageData = malloc(WIDTH * HEIGHT); // actual image data memset(pImageData, 0, WIDTH * HEIGHT); // // create new GIF memset(&gConfig, 0, sizeof(CGIF_Config)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "max_color_table_test.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); free(aPalette); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/max_size.c000066400000000000000000000026501463435475600155750ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 0xFFFFUL #define HEIGHT 0xFFFFUL /* This is an example code that creates a GIF-image with all green pixels. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0xFF, 0x00, 0x00 }; cgif_result r; uint16_t numColors = 1; // number of colors in aPalette // // create an image pImageData = malloc(WIDTH * HEIGHT); // actual image data memset(pImageData, 0, WIDTH * HEIGHT); // // create new GIF memset(&gConfig, 0, sizeof(CGIF_Config)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "max_size.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/meson.build000066400000000000000000000102561463435475600157550ustar00rootroot00000000000000# some unit tests are expected to fail when we generate the fuzzer seed corpus. # these tests might fail, as the cgif API wrapper (fuzz/cgif_create_fuzz_seed.c) does not behave exactly as cgif itself: # seed_should_fail should be set to true if that's the case. tests = [ { 'name' : 'all_optim', 'seed_should_fail' : false}, { 'name' : 'alpha', 'seed_should_fail' : false}, { 'name' : 'animated_color_gradient', 'seed_should_fail' : false}, { 'name' : 'animated_interlaced', 'seed_should_fail' : false}, { 'name' : 'animated_single_pixel', 'seed_should_fail' : false}, { 'name' : 'animated_snake', 'seed_should_fail' : false}, { 'name' : 'animated_stripes_horizontal', 'seed_should_fail' : false}, { 'name' : 'animated_stripe_pattern', 'seed_should_fail' : false}, { 'name' : 'animated_stripe_pattern_2', 'seed_should_fail' : false}, { 'name' : 'duplicate_frames', 'seed_should_fail' : false}, { 'name' : 'earlyclose', 'seed_should_fail' : true }, { 'name' : 'eindex', 'seed_should_fail' : true }, { 'name' : 'eindex_anim', 'seed_should_fail' : true }, { 'name' : 'enopalette', 'seed_should_fail' : true }, { 'name' : 'ewrite', 'seed_should_fail' : true }, { 'name' : 'ezeroheight', 'seed_should_fail' : true}, { 'name' : 'ezerowidth', 'seed_should_fail' : true}, { 'name' : 'ezerowidthheight', 'seed_should_fail' : true}, { 'name' : 'global_plus_local_table', 'seed_should_fail' : false}, { 'name' : 'global_plus_local_table_with_optim', 'seed_should_fail' : false}, { 'name' : 'has_transparency', 'seed_should_fail' : false}, { 'name' : 'has_transparency_2', 'seed_should_fail' : false}, { 'name' : 'local_transp', 'seed_should_fail' : false}, { 'name' : 'max_color_table_test', 'seed_should_fail' : false}, { 'name' : 'min_color_table_test', 'seed_should_fail' : false}, { 'name' : 'min_size', 'seed_should_fail' : false}, { 'name' : 'more_than_256_colors', 'seed_should_fail' : false}, { 'name' : 'noise256', 'seed_should_fail' : false}, { 'name' : 'noise256_large', 'seed_should_fail' : false}, { 'name' : 'noise6', 'seed_should_fail' : false}, { 'name' : 'noise6_interlaced', 'seed_should_fail' : false}, { 'name' : 'noloop', 'seed_should_fail' : false}, { 'name' : 'one_full_block', 'seed_should_fail' : false}, { 'name' : 'only_local_table', 'seed_should_fail' : false}, { 'name' : 'overlap_everything', 'seed_should_fail' : false}, { 'name' : 'overlap_everything_only_trans', 'seed_should_fail' : false}, { 'name' : 'overlap_some_rows', 'seed_should_fail' : false}, { 'name' : 'single_frame_alpha', 'seed_should_fail' : false}, { 'name' : 'stripe_pattern_interlaced', 'seed_should_fail' : false}, { 'name' : 'switchpattern', 'seed_should_fail' : false}, { 'name' : 'trans_inc_initdict', 'seed_should_fail' : false}, { 'name' : 'user_trans', 'seed_should_fail' : false}, { 'name' : 'write_fn', 'seed_should_fail' : false}, ] foreach t : tests name = t.get('name') test_exe = executable( 'test_' + name, name + '.c', dependencies : [libcgif_dep], include_directories : ['../inc/'], ) test(name, test_exe, priority : 0) endforeach sha256sumc = find_program('scripts/sha256sum.py') # get the ordering right: # md5sum check on output GIFs should be run once all of the above tests are done. test('check test checksums', sha256sumc, args : [join_paths(meson.source_root(), 'tests/tests.sha256')], workdir : meson.build_root(), priority : -1, is_parallel : false) cgif-0.4.1/tests/min_color_table_test.c000066400000000000000000000027401463435475600201450ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-image with all green pixels. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0xFF, 0x00 }; // green cgif_result r; uint8_t numColors = 1; // number of colors in aPalette // // create an image with all green pixels pImageData = malloc(WIDTH * HEIGHT); // actual image data memset(pImageData, 0, WIDTH * HEIGHT); // set to all green // // create new GIF memset(&gConfig, 0, sizeof(CGIF_Config)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "min_color_table_test.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/min_size.c000066400000000000000000000026341463435475600155750ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 1 #define HEIGHT 1 /* This is an example code that creates a GIF-image with all green pixels. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0xFF, 0x00, 0x00 }; cgif_result r; uint16_t numColors = 1; // number of colors in aPalette // // create an image pImageData = malloc(WIDTH * HEIGHT); // actual image data memset(pImageData, 0, WIDTH * HEIGHT); // // create new GIF memset(&gConfig, 0, sizeof(CGIF_Config)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "min_size.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/more_than_256_colors.c000066400000000000000000000063101463435475600177040ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 255 #define HEIGHT 255 /* This code is an example of how a GIF with frames including more than 256 colors can be created by reusing colors from a previous frame. The general restriction of GIF that only 256 new colors can be introduced in one frame remains of course */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint16_t numColorsLocal = 255; uint8_t aLocalPalette[3*numColorsLocal]; // local palette uint8_t aPalette[3*numColorsLocal]; // global palette (only used for 1st frame here) cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColorsLocal; gConfig.path = "more_than_256_colors.gif"; // define the global color palette for(int i=0; i #include #include #include #include "cgif.h" #define WIDTH 500 #define HEIGHT 500 static uint64_t seed; // unsigned integer overflow expected __attribute__((no_sanitize("integer"))) int psdrand(void) { // simple pseudo random function from musl libc seed = 6364136223846793005ULL * seed + 1; return seed >> 33; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; cgif_result r; uint8_t aPalette[256 * 3]; seed = 22; for(int i = 0; i < 256; ++i) { aPalette[i * 3] = psdrand() % 256; aPalette[i * 3 + 1] = psdrand() % 256; aPalette[i * 3 + 2] = psdrand() % 256; } memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 256; gConfig.path = "noise256.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); for(int i = 0; i < WIDTH * HEIGHT; ++i) pImageData[i] = psdrand() % 256; fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/noise256_large.c000066400000000000000000000032501463435475600164770ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 3000 #define HEIGHT 3000 static uint64_t seed; // unsigned integer overflow expected __attribute__((no_sanitize("integer"))) int psdrand(void) { // simple pseudo random function from musl libc seed = 6364136223846793005ULL * seed + 1; return seed >> 33; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; cgif_result r; uint8_t aPalette[256 * 3]; seed = 22; for(int i = 0; i < 256; ++i) { aPalette[i * 3] = psdrand() % 256; aPalette[i * 3 + 1] = psdrand() % 256; aPalette[i * 3 + 2] = psdrand() % 256; } memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 256; gConfig.path = "noise256_large.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); for(int i = 0; i < WIDTH * HEIGHT; ++i) pImageData[i] = psdrand() % 256; fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/noise6.c000066400000000000000000000032271463435475600151620ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 700 #define HEIGHT 320 static uint64_t seed; // unsigned integer overflow expected __attribute__((no_sanitize("integer"))) int psdrand(void) { // simple pseudo random function from musl libc seed = 6364136223846793005ULL * seed + 1; return seed >> 33; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; cgif_result r; uint8_t aPalette[6 * 3]; seed = 42; for(int i = 0; i < 6; ++i) { aPalette[i * 3] = psdrand() % 256; aPalette[i * 3 + 1] = psdrand() % 256; aPalette[i * 3 + 2] = psdrand() % 256; } memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 6; gConfig.path = "noise6.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); for(int i = 0; i < WIDTH * HEIGHT; ++i) pImageData[i] = psdrand() % 6; fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/noise6_interlaced.c000066400000000000000000000033231463435475600173510ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 700 #define HEIGHT 320 static uint64_t seed; // unsigned integer overflow expected __attribute__((no_sanitize("integer"))) int psdrand(void) { // simple pseudo random function from musl libc seed = 6364136223846793005ULL * seed + 1; return seed >> 33; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; cgif_result r; uint8_t aPalette[6 * 3]; seed = 42; for(int i = 0; i < 6; ++i) { aPalette[i * 3] = psdrand() % 256; aPalette[i * 3 + 1] = psdrand() % 256; aPalette[i * 3 + 2] = psdrand() % 256; } memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 6; gConfig.path = "noise6_interlaced.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); for(int i = 0; i < WIDTH * HEIGHT; ++i) pImageData[i] = psdrand() % 6; fConfig.pImageData = pImageData; fConfig.attrFlags = CGIF_FRAME_ATTR_INTERLACED; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/noloop.c000066400000000000000000000043211463435475600152610ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 50 #define HEIGHT 50 static void initGIFConfig(CGIF_Config* pConfig, uint8_t* pGlobalPalette, uint16_t numColors, uint32_t attrFlags, uint16_t width, uint16_t height) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->attrFlags = attrFlags; pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pGlobalPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = "noloop.gif"; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay, uint32_t genFlags) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->pImageData = pImageData; pConfig->delay = delay; pConfig->genFlags = genFlags; } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0xFF, 0xFF, 0xFF, // white 0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green }; cgif_result r; const uint16_t numColors = 3; // number of colors in aPalette // // create new GIF initGIFConfig(&gConfig, aPalette, numColors, CGIF_ATTR_IS_ANIMATED | CGIF_ATTR_NO_LOOP, WIDTH, HEIGHT); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); initFrameConfig(&fConfig, pImageData, 50, CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW); r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData, 1, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the next frame memset(pImageData, 2, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // Free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/one_full_block.c000066400000000000000000000030371463435475600167330ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 1 #define HEIGHT 224 // ceil((224 + 2) * 9bit) = 255byte (+2 for start- and clear-code) /* This test creates a GIF-frame with exactly 255 LZW bytes */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t* aPalette = malloc(3 * HEIGHT); cgif_result r; uint16_t numColors = HEIGHT; // number of colors in aPalette memset(aPalette, 0, 3 * HEIGHT); // // create an image pImageData = malloc(WIDTH * HEIGHT); // actual image data for(int i = 0; i < HEIGHT; ++i) pImageData[i] = i; // // create new GIF memset(&gConfig, 0, sizeof(CGIF_Config)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "one_full_block.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(aPalette); free(pImageData); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/only_local_table.c000066400000000000000000000032241463435475600172560ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 103 #define HEIGHT 104 /* This is an example code that creates a GIF-animation with a moving stripe-pattern. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; uint8_t numColors = 2; // number of colors in aPalette // // Create new GIF // memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_NO_GLOBAL_TABLE; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.path = "only_local_table.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // Add frame to GIF // pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 1, WIDTH * HEIGHT); memset(pImageData + WIDTH * 2, 0, WIDTH * 3); fConfig.pImageData = pImageData; fConfig.pLocalPalette = aPalette; fConfig.numLocalPaletteEntries = numColors; fConfig.attrFlags = CGIF_FRAME_ATTR_USE_LOCAL_TABLE; r = cgif_addframe(pGIF, &fConfig); // append the new frame // free(pImageData); // // Free allocated space at the end of the session // r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/overlap_everything.c000066400000000000000000000035511463435475600176730ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-animation with two identical black frames in sequence, using CGIF_FRAME_GEN_USE_DIFF_WINDOW*/ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; uint8_t numColors = 2; // number of colors in aPalette cgif_result r; // // Create new GIF // memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.genFlags = CGIF_GEN_KEEP_IDENT_FRAMES; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "overlap_everything.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // Add frames to GIF // pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // set time before next frame (in units of 0.01 s) fConfig.genFlags = CGIF_FRAME_GEN_USE_DIFF_WINDOW; r = cgif_addframe(pGIF, &fConfig); // append the new frame r = cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/overlap_everything_only_trans.c000066400000000000000000000035661463435475600221510ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-animation with two identical black frames in sequence, using CGIF_FRAME_GEN_USE_TRANSPARENCY*/ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; uint8_t numColors = 2; // number of colors in aPalette cgif_result r; // // Create new GIF // memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.genFlags = CGIF_GEN_KEEP_IDENT_FRAMES; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "overlap_everything_only_trans.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // Add frames to GIF // pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // set time before next frame (in units of 0.01 s) fConfig.genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY; r = cgif_addframe(pGIF, &fConfig); // append the new frame r = cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // free allocated space at the end of the session r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/overlap_some_rows.c000066400000000000000000000036371463435475600175310ustar00rootroot00000000000000#include #include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 /* This is an example code that creates a GIF-animation with a moving stripe-pattern. */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; uint8_t numColors = 2; // number of colors in aPalette cgif_result r; // // Create new GIF // memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "overlap_some_rows.gif"; pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // Add frames to GIF // pImageData = malloc(WIDTH * HEIGHT); // Actual image data memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // set time before next frame (in units of 0.01 s) fConfig.genFlags = CGIF_FRAME_GEN_USE_DIFF_WINDOW; r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData + 7, 1, 55); memset(pImageData + 7 + WIDTH, 1, 55); memset(pImageData + 7 + WIDTH * 2, 1, 55); memset(pImageData + 7 + WIDTH * 3, 1, 55); r = cgif_addframe(pGIF, &fConfig); // append the next frame // free(pImageData); // // Free allocated space at the end of the session // r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/scripts/000077500000000000000000000000001463435475600152765ustar00rootroot00000000000000cgif-0.4.1/tests/scripts/sha256sum.py000077500000000000000000000026251463435475600174150ustar00rootroot00000000000000#!/usr/bin/env python3 import hashlib import re import sys # returns 0 if the md5sums are identical def checkFile(sExpSHA256Hex, sFileName): try: f = open(sFileName, "rb") except OSError: print("%s: FAILED to open" % sFileName) return 2 byteData = f.read() f.close() # compare SHA256 HEX digest sRealSHA256Hex = hashlib.sha256(byteData).hexdigest() if sRealSHA256Hex == sExpSHA256Hex: print("%s: OK" % sFileName) return 0 else: print("%s: FAILED" % sFileName) return 3 # handle input line def handleLine(sLine): reComment = re.match("^\\s*#", sLine) if reComment != None: return 0 # line is comment # extract SHA256 HEX digest and filename mLine = re.fullmatch("^\\s*([0-9a-fA-F]{64})\\s*(.*)\\n$", sLine) if mLine == None: return 1 # error # extract info sSHA256Hex = mLine.group(1) sFileName = mLine.group(2) return checkFile(sSHA256Hex, sFileName) # ./sha256sum.py if len(sys.argv) != 2: print("Invalid number of arguments.") exit(1) # try to open sha256 input file try: f = open(sys.argv[1], "rt") except OSError: print("Failed to open ") exit(2) r = 0 sNextLine = f.readline() while sNextLine != "": r |= handleLine(sNextLine) sNextLine = f.readline() f.close() if r: print("sha256sum.py: ERROR: At least 1 computed checksum did NOT match or error while parsing input checksums.") exit(3) cgif-0.4.1/tests/single_frame_alpha.c000066400000000000000000000024511463435475600175550ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 1 #define HEIGHT 1 int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 1; gConfig.path = "single_frame_alpha.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 1, WIDTH * HEIGHT); // set all transparent fConfig.pImageData = pImageData; fConfig.attrFlags = CGIF_FRAME_ATTR_HAS_ALPHA; fConfig.transIndex = 1; cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/stripe_pattern_interlaced.c000066400000000000000000000027261463435475600212170ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 1000 #define HEIGHT 1000 /* horizontal stripe pattern with 1pixel linewidth, to test interlaced mode */ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; cgif_result r; uint8_t aPalette[] = { 0x00, 0xFF, 0x00, // green 0xFF, 0x00, 0xFF, // purple }; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.path = "stripe_pattern_interlaced.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); for(int i = 0; i < HEIGHT; ++i){ memset(pImageData + i*WIDTH, i % 2, WIDTH); } fConfig.pImageData = pImageData; fConfig.attrFlags = CGIF_FRAME_ATTR_INTERLACED; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/switchpattern.c000066400000000000000000000072651463435475600166640ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 20 #define HEIGHT 20 /* Small helper functions to initialize GIF- and frame-configuration */ static void initGIFConfig(CGIF_Config* pConfig, char* path, uint16_t width, uint16_t height, uint8_t* pPalette, uint16_t numColors) { memset(pConfig, 0, sizeof(CGIF_Config)); pConfig->width = width; pConfig->height = height; pConfig->pGlobalPalette = pPalette; pConfig->numGlobalPaletteEntries = numColors; pConfig->path = path; pConfig->attrFlags = CGIF_ATTR_IS_ANIMATED; } static void initFrameConfig(CGIF_FrameConfig* pConfig, uint8_t* pImageData, uint16_t delay) { memset(pConfig, 0, sizeof(CGIF_FrameConfig)); pConfig->delay = delay; pConfig->pImageData = pImageData; pConfig->genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY | CGIF_FRAME_GEN_USE_DIFF_WINDOW; // use optimizations } /* This is a test that creates a GIF-animation with alternating colors. */ int main(void) { CGIF* pGIF; // struct containing the GIF CGIF_Config gConfig; // global configuration parameters for the GIF CGIF_FrameConfig fConfig; // configuration parameters for a frame uint8_t* pImageData; // image data (an array of color-indices) cgif_result r; uint8_t aPalette[] = {0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green 0x00, 0x00, 0xFF}; // blue uint8_t numColors = 3; // number of colors in aPalette (up to 256 possible) // initialize the GIF-configuration and create a new GIF initGIFConfig(&gConfig, "switchpattern.gif", WIDTH, HEIGHT, aPalette, numColors); pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // create image frames and add them to GIF pImageData = malloc(WIDTH * HEIGHT); // allocate memory for image data memset(pImageData, 0, WIDTH * HEIGHT); initFrameConfig(&fConfig, pImageData, 20); // initialize the frame-configuration r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData, 1, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData, 0, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData, 1, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData, 2, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData, 1, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the new frame memset(pImageData, 2, WIDTH * HEIGHT); r = cgif_addframe(pGIF, &fConfig); // append the new frame free(pImageData); // free image data when all frames were added // close created GIF-file and free allocated space r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/tests.sha256000066400000000000000000000064221463435475600157070ustar00rootroot00000000000000150d5d8e3aedd105bd7b3609547e375ce5432c435509b9a88842119ec6afe8e6 all_optim.gif 1c45ad2d19b1435a7ded09b4d98817f57d4157eabf04f0817bc479b139edaf0b alpha.gif fe2c3217ac41089bbf95c30e558f9d00fd4e003e76b4c81531f4dec4abe22e8e animated_color_gradient.gif bed405ac125e51b30503922d29a0e66f604458ad769194397319b5c692b671e0 animated_interlaced.gif 88665c8e961a0915c8982b8adac5dcf715eaaec2655b8be455e967a0b4d5b350 animated_single_pixel.gif 48a8b13f2401c969553740c95d5aa7f8d27a8f307dc5600b6d9c5a646ddec1af animated_snake.gif 97183d1ebe62c46df0654089733994630309dc5e76fb8857ac9286f229ec3629 animated_stripe_pattern_2.gif ddd8636222c99e04ffedf66d2d001052f97eabeb7b106fdf3eef398297b58283 animated_stripe_pattern.gif bb9aacefe647f92f87e9494e4e2ed3ba68d252fbeef5adc1e277d60e7177d8b6 animated_stripes_horizontal.gif 7a2d4525c4cd8596f5dd6486e7de90c1c94fd83695c7c41e0a87a251296d5b4f duplicate_frames.gif 6710654279650c40e56cd482cebe9f1c5273943c5ef8ac42e8c65ff2b9255aa0 example_cgif.gif 3a526f38941f73bc0899baa5c11ac47c4c18ebd6f8d865af17c63baa42d98e9c example_video_cgif.gif 51d678c873b3abf6e53a897c593a040b1a8c99b8d295e76bac7db3d9e485681c global_plus_local_table.gif f3eeec3d7b611f5fc57f6931a884ca65a66cc8b7f21970ce5c6e8479585b0938 global_plus_local_table_with_optim.gif 0ffb38a12bba549e6b1930d40ff937cf10c5a9a12b488a3f6a026cccbf83d365 has_transparency_2.gif 11828b8bf0d1720770cbaacb641d8353dd3c8fe703afc016c8598ddb295bedd5 has_transparency.gif 15bad648f783e6b809f160c121d0deb3ba0d6751b5a5350e0d7fcf6079eab449 local_transp.gif 37de6191fe5bbb8bbd8ddd1222db770642ec8ce799ce852161555e4220a75df0 max_color_table_test.gif # too large for CI: 34b121749669c90c347089e0e9b0caeb74443f50d91dd6854327e8cf07d0a565 max_size.gif eeb9acd181da401748c9f39c59dbb5ecd71fd6f8f1685002f767de2ec0329bf4 min_color_table_test.gif b263ce6bde416426b6676c320846f73870d40b2125bfc9630087c3c54df21ac8 min_size.gif 5244e4ca77ea3923d27e790b9dddb6032a686e0272ba3d78ff5fa4af0523647b more_than_256_colors.gif 11c8de343ccd528e319afbe3ec0d0c657c1e6292faf6cb318713bcaf2979ab0d noise256.gif 75a2a7b04a21b785977fb2506e6468478418703835876755e78453223820b924 noise256_large.gif ff1da19b0448af07d8561f0ee62b184f99ac7dcb75f1a2215474190ec6648901 noise6.gif 14cd8b2486e725d4136e1af656aa2398a882694fd1774ebfb23c57dae5ae00da noise6_interlaced.gif 98ec5ef9223f2c51bc2f4f94da4468f3b1e3c537a64d8050649787aaf433d13d noloop.gif 09202e6c98b67dc37502b5b977184261de4e768bdb19a50366fc789b17fd0263 one_full_block.gif 5a02b240c82405f198d2542f93bf1ea3aeb7b3e75d6937893232ddea239bf688 only_local_table.gif 73993734f5d68ad0432e43f65c76f3c6703f3c5de70ec6e6748a6d28915381b3 overlap_everything.gif 498e032274236caeda5319e27d7a0a76d74be380c237ad41b10469f9b9186ab7 overlap_everything_only_trans.gif 71ecd6f1ba2ce4b3476ce820628cde03a1a5d292b892d9aad3b9169e1c34b032 overlap_some_rows.gif 6feca8f68f8735a840f77b4989aa189abc6dd5b2904f03866874a36488b25a1a single_frame_alpha.gif 161a132972bfbc060ea0359d8c40513d2e22e65b27a7c11553f883fe3856fe30 stripe_pattern_interlaced.gif b8a7e72024a1263229e85f27168800400489cf993f7b90f45f00d39323763261 switchpattern.gif 1eb29910b6633bc1c6be49fc85ba7f5c24f415083d81f35c366ea50d55bcdf8d trans_inc_initdict.gif 55b64d9c9a359f9daeecdf55c83e485aca3f90d2a6dd29f86d5b14a0e1396770 user_trans.gif 5d301094a0b54287cabcc71c6972f7be417a5bea87cde01d85089a8893fd17fd write_fn.gif cgif-0.4.1/tests/trans_inc_initdict.c000066400000000000000000000043201463435475600176210ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 200 #define HEIGHT 200 /* This is an example code that creates a moving stripe pattern. Special case: using CGIF_FRAME_GEN_USE_TRANSPARENCY requires increasing color table to next power of 2*/ int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white 0xFF, 0x00, 0x00, // red 0x00, 0xFF, 0x00, // green }; cgif_result r; uint8_t numColors = 4; // number of colors in aPalette int numFrames = 30; // number of frames in the video memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; // set needed attribution flag (as GIF is animated) gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = numColors; gConfig.path = "trans_inc_initdict.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); // actual image data fConfig.genFlags = CGIF_FRAME_GEN_USE_TRANSPARENCY; fConfig.pImageData = pImageData; // set pointer to image data fConfig.delay = 10; // set time before next frame (in units of 0.01 s) for (int f = 0; f < numFrames; ++f) { for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = (unsigned char)((f + i % WIDTH) % numColors); // ceate a moving stripe pattern } r = cgif_addframe(pGIF, &fConfig); // append the new frame if(r != CGIF_OK) { break; } } free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/user_trans.c000066400000000000000000000033121463435475600161370ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0xFF, 0x00, // green 0xFF, 0xFF, 0xFF, // white }; cgif_result r; memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.attrFlags = CGIF_ATTR_IS_ANIMATED; gConfig.genFlags = CGIF_GEN_KEEP_IDENT_FRAMES; gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.path = "user_trans.gif"; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; fConfig.delay = 100; // create an off/on pattern for (int i = 0; i < (WIDTH * HEIGHT); ++i) { pImageData[i] = i % 2; } cgif_addframe(pGIF, &fConfig); // set everything to transparent (frame from before shines through) memset(pImageData, 2, WIDTH * HEIGHT); fConfig.attrFlags = CGIF_FRAME_ATTR_HAS_SET_TRANS; fConfig.transIndex = 2; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; } cgif-0.4.1/tests/write_fn.c000066400000000000000000000031441463435475600155720ustar00rootroot00000000000000#include #include #include #include #include "cgif.h" #define WIDTH 100 #define HEIGHT 100 static int pWriteFn(void* pContext, const uint8_t* pData, const size_t numBytes) { size_t r = fwrite(pData, 1, numBytes, (FILE*) pContext); if(r == numBytes) { return 0; } else { return -1; } } int main(void) { CGIF* pGIF; CGIF_Config gConfig; CGIF_FrameConfig fConfig; uint8_t* pImageData; uint8_t aPalette[] = { 0x00, 0x00, 0x00, // black 0xFF, 0xFF, 0xFF, // white }; cgif_result r; FILE* file = fopen("write_fn.gif", "wb"); memset(&gConfig, 0, sizeof(CGIF_Config)); memset(&fConfig, 0, sizeof(CGIF_FrameConfig)); gConfig.width = WIDTH; gConfig.height = HEIGHT; gConfig.pGlobalPalette = aPalette; gConfig.numGlobalPaletteEntries = 2; gConfig.pWriteFn = pWriteFn; gConfig.pContext = (void*) file; // // create new GIF pGIF = cgif_newgif(&gConfig); if(pGIF == NULL) { fputs("failed to create new GIF via cgif_newgif()\n", stderr); return 1; } // // add frames to GIF pImageData = malloc(WIDTH * HEIGHT); memset(pImageData, 0, WIDTH * HEIGHT); fConfig.pImageData = pImageData; r = cgif_addframe(pGIF, &fConfig); free(pImageData); // // write GIF to file r = cgif_close(pGIF); // free allocated space at the end of the session fclose(file); // check for errors if(r != CGIF_OK) { fprintf(stderr, "failed to create GIF. error code: %d\n", r); return 2; } return 0; }