Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Romelium committed Feb 15, 2025
0 parents commit 635e5e1
Show file tree
Hide file tree
Showing 32 changed files with 27,984 additions and 0 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Publish to PyPI

on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+*'

jobs:
publish:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Hatch
uses: pypa/hatch@a3c83ab3d481fbc2dc91dd0088628817488dd1d5

- name: Build
run: hatch build

- name: Publish to PyPI
env:
HATCH_INDEX_USER: __token__
HATCH_INDEX_AUTH: ${{ secrets.PYPI_TOKEN }}
run: hatch publish
26 changes: 26 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Tests

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "**" ]

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v4

- name: Install Hatch
uses: pypa/hatch@a3c83ab3d481fbc2dc91dd0088628817488dd1d5

- name: Run tests
run: hatch test -a

- name: Check formatting and lint
run: hatch fmt --check
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
*.spec
.ruff_cache

# Virtual environments
.venv

# Testing
.pytest_cache
coverage.xml
.coverage*

# Misc
dircat.md
test.md
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Romelium

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.
227 changes: 227 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<h1 align="center">
<img height="250" src="images/framesvg.svg" alt="FrameSVG" />
</h1>

<p align="center">
<strong>Convert animated GIFs to animated SVGs.</strong>
</p>

<p align="center">
<a href="https://github.com/romelium/framesvg/actions/workflows/tests.yml"><img alt="Build Status" src="https://img.shields.io/github/actions/workflow/status/romelium/framesvg/main.yml?branch=main"></a>
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
<a href="https://pypi.org/project/framesvg/"><img alt="PyPI" src="https://img.shields.io/pypi/v/framesvg"></a>
<a href="https://pypi.org/project/framesvg/"><img alt="Python Versions" src="https://img.shields.io/pypi/pyversions/framesvg"></a>
</p>

<!-- Be on the left and right of next paragraph -->
<img align="left" hspace=10 src="images/kyubey.svg"/>
<img align="right" hspace=10 src="images/kyubey.svg"/>

`framesvg` is a command-line tool and Python library that converts animated GIFs into animated SVGs. It leverages the power of [VTracer](https://www.visioncortex.org/vtracer/) for raster-to-vector conversion, producing smooth, scalable, and *true vector* animations. This is a significant improvement over embedding raster images (like GIFs) directly within SVGs, as `framesvg` generates genuine vector output that plays automatically and scales beautifully. Ideal for readmes, documentation, and web graphics.

<p align="center">
Preferred to be viewed on <a href="https://github.com/Romelium/FrameSVG?tab=readme-ov-file">GitHub</a>
</p>

<br clear="both"/>

## Why Use framesvg?

* **True Vector Output:** Unlike simply embedding a GIF within an SVG, `framesvg` creates a true vector animation. This means:
* **Scalability:** The SVG can be resized to any dimensions without losing quality.
* **Smaller File Size (Potentially):** For many GIFs, the resulting SVG will be smaller, especially for graphics with large areas of solid color or simple shapes. Complex, photographic GIFs may be larger, however.
* **Automatic Playback:** The generated SVGs are designed to play automatically in any environment that supports SVG animations (web browsers, github, many image viewers, etc.).
* **Easy to Use:** Simple command-line interface and a clean Python API.
* **Customizable:** Control the frame rate and fine-tune the VTracer conversion process for optimal results.

## Examples

The following examples demonstrate the conversion of GIFs (left) to SVGs (right) using `framesvg`.

<p align="center">
<img height=200 src="images/Code Coding GIF by EscuelaDevRock Git.gif"/>
<img height=200 src="images/Code Coding GIF by EscuelaDevRock Git.svg"/>
<img height=200 src="images/Code Coding GIF by EscuelaDevRock GitHub.gif"/>
<img height=200 src="images/Code Coding GIF by EscuelaDevRock GitHub.svg"/>
<img height=200 src="images/Code Coding GIF by EscuelaDevRock VSCode.gif"/>
<img height=200 src="images/Code Coding GIF by EscuelaDevRock VSCode.svg"/>
<img height=200 src="images/Code Coding GIF by EscuelaDevRock Sublime.gif"/>
<img height=200 src="images/Code Coding GIF by EscuelaDevRock Sublime.svg"/>
</p>

### More Examples

<p align="center">
<img height=200 src="images/Good_Morning_GIF_by_Hello_All.gif"/>
<img height=200 src="images/Good_Morning_GIF_by_Hello_All.svg"/>
<img height=200 src="images/icon_loading_GIF.gif"/>
<img height=200 src="images/icon_loading_GIF.svg"/>
<img height=200 src="images/voila hands GIF by brontron.gif"/>
<img height=200 src="images/voila hands GIF by brontron.svg"/>
</p>

### Complex Examples (Transparent Backgrounds)

These examples demonstrate `binary` color mode. All bright colors in `binary` color mode turns transparent. (If they appear dark, it is due to the transparency. They will look correct on light backgrounds)

<p align="center">
<img height=200 src="images/black and white loop GIF by Sculpture.gif"/>
<img height=200 src="images/black and white loop GIF by Sculpture.svg"/>
<img height=200 src="images/Black And White Loop GIF by Pi-Slices.gif"/>
<img height=200 src="images/Black And White Loop GIF by Pi-Slices.svg"/>
</p>

## Installation

### Using pip (Recommended)

The easiest way to install `framesvg` is via pip:

```bash
pip install framesvg
```

This installs both the command-line tool and the Python library.

### From Source

1. **Clone the repository:**

```bash
git clone https://github.com/romelium/framesvg
cd framesvg
```

2. **Install:**

```bash
pip install .
```

## Usage

### Command-Line Interface

```bash
framesvg input.gif [output.svg] [options]
```

* **`input.gif`:** (Required) Path to the input GIF file.
* **`output.svg`:** (Optional) Path to save the output SVG file. If omitted, the output file will have the same name as the input, but with a `.svg` extension.

**Options:**

* **`-f`, `--fps <value>`:** Sets the frames per second (FPS) for the animation. (Default: 10). Lower values can reduce file size.
* **`-l`, `--log-level <level>`:** Sets the logging level. (Default: INFO). Choices: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`, `NONE`. `DEBUG` provides detailed output for troubleshooting.

* **VTracer Options:** These options control the raster-to-vector conversion process performed by VTracer. Refer to the [VTracer Documentation](https://www.visioncortex.org/vtracer-docs) and [Online Demo](https://www.visioncortex.org/vtracer/) for detailed explanations.

* `-c`, `--colormode <mode>`: Color mode. (Default: `color`). Choices: `color`, `binary`.
* `-i`, `--hierarchical <mode>`: Hierarchy mode. (Default: `stacked`). Choices: `stacked`, `cutout`.
* `-m`, `--mode <mode>`: Conversion mode. (Default: `polygon`). Choices: `spline`, `polygon`, `none`. `spline` creates smoother curves, but `polygon` often results in smaller files.
* `-s`, `--filter-speckle <value>`: Reduces noise and small details. (Default: 4). *This is a key parameter for controlling file size.* Higher values = smaller files, but less detail.
* `-p`, `--color-precision <value>`: Number of significant bits for color quantization. (Default: 8). Lower values = smaller files, but fewer colors.
* `-d`, `--layer-difference <value>`: Controls the number of layers. (Default: 16). Higher values can reduce file size.
* `--corner-threshold <value>`: Angle threshold for corner detection. (Default: 60).
* `--length-threshold <value>`: Minimum path length. (Default: 4.0).
* `--max-iterations <value>`: Maximum number of optimization iterations. (Default: 10).
* `--splice-threshold <value>`: Angle threshold for splitting splines. (Default: 45).
* `--path-precision <value>`: Number of decimal places for path coordinates. (Default: 8).

**Command-Line Examples:**

```bash
# Basic conversion with default settings
framesvg input.gif
# Specify output file and set FPS to 24
framesvg input.gif output.svg -f 24
# Optimize for smaller file size (less detail)
framesvg input.gif -s 8 -p 3 -d 128
# Enable debug logging
framesvg input.gif -l DEBUG
```

### Python API

```python
from framesvg import gif_to_animated_svg_write, gif_to_animated_svg
# Example 1: Convert and save to a file
gif_to_animated_svg_write("input.gif", "output.svg", fps=30)
# Example 2: Get the SVG as a string
animated_svg_string = gif_to_animated_svg("input.gif", fps=12)
print(f"Generated SVG length: {len(animated_svg_string)}")
# ... do something with the string (e.g., save to file, display in a web app)
# Example 3: Customize VTracer options
custom_options = {
"mode": "spline",
"filter_speckle": 2,
}
gif_to_animated_svg_write("input.gif", "output_custom.svg", vtracer_options=custom_options)
```

### API Reference

* **`gif_to_animated_svg_write(gif_path, output_svg_path, vtracer_options=None, fps=10.0, image_loader=None, vtracer_instance=None)`:**
* `gif_path` (str): Path to the input GIF file.
* `output_svg_path` (str): Path to save the output SVG file.
* `vtracer_options` (dict, optional): A dictionary of VTracer options. If `None`, uses `DEFAULT_VTRACER_OPTIONS`.
* `fps` (float, optional): Frames per second. Defaults to 10.0.
* `image_loader` (ImageLoader, optional): Custom image loader.
* `vtracer_instance` (VTracer, optional): Custom VTracer instance.
* Raises: `FileNotFoundError`, `NotAnimatedGifError`, `NoValidFramesError`, `DimensionError`, `ExtractionError`, `FramesvgError`, `IsADirectoryError`.

* **`gif_to_animated_svg(gif_path, vtracer_options=None, fps=10.0, image_loader=None, vtracer_instance=None)`:**
* `gif_path` (str): Path to the input GIF file.
* `vtracer_options` (dict, optional): A dictionary of VTracer options. If `None`, uses `DEFAULT_VTRACER_OPTIONS`.
* `fps` (float, optional): Frames per second. Defaults to 10.0.
* `image_loader` (ImageLoader, optional): Custom image loader.
* `vtracer_instance` (VTracer, optional): Custom VTracer instance.
* Returns: The animated SVG as a string.
* Raises: `FileNotFoundError`, `NotAnimatedGifError`, `NoValidFramesError`, `DimensionError`, `ExtractionError`, `FramesvgError`.

## Tips for Optimizing Large File Size (> 1MB)

* **[Online Demo](https://www.visioncortex.org/vtracer/):** Use this to visualize tweaking values. Experiment to find the best balance between size and quality.
* **`filter-speckle`:** *This is the most impactful setting for reducing file size, especially on complex images.* Increasing it removes small details.
* **`--mode polygon`:** Use the default polygon mode unless smooth curves (spline mode) are absolutely necessary. Polygon mode can significantly reduce file size by a factor of 5 or more.
* **`layer-difference`:** Increase this to reduce the number of layers.
* **`color-precision`:** Reduce the number of colors by lowering this value.

## Dev

### Install Hatch (Recommended)

follow [this](https://hatch.pypa.io/latest/install)

or just

```bash
pip install hatch
```

### Format and lint

```bash
hatch fmt
```

### Testing
```bash
hatch test
```

### Other Hatch Commands

```bash
hatch -h
```

## Contributing

Contributions are welcome! Please submit pull requests or open issues on the [GitHub repository](https://github.com/romelium/framesvg).
Binary file added images/Black And White Loop GIF by Pi-Slices.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 635e5e1

Please sign in to comment.