Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,176 @@ jobs:
with:
name: slimg-kotlin-${{ env.VERSION }}
path: bindings/kotlin/build/libs/*.jar

# ─────────────────────────────────────────────
# Build Python wheels for all platforms
# ─────────────────────────────────────────────
build-python-wheels:
name: Build Python wheel (${{ matrix.target }})
runs-on: ${{ matrix.runner }}
env:
SYSTEM_DEPS_DAV1D_BUILD_INTERNAL: always
strategy:
fail-fast: false
matrix:
include:
- { target: aarch64-apple-darwin, runner: macos-latest }
- { target: x86_64-apple-darwin, runner: macos-13 }
- { target: x86_64-unknown-linux-gnu, runner: ubuntu-latest }
- { target: aarch64-unknown-linux-gnu, runner: ubuntu-24.04-arm }
- { target: x86_64-pc-windows-msvc, runner: windows-latest }
steps:
- uses: actions/checkout@v4

- name: Install system dependencies (macOS)
if: runner.os == 'macOS'
run: brew install nasm meson ninja

- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y nasm meson ninja-build

- name: Setup MSVC environment (Windows)
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1

- name: Add NASM to PATH (Windows)
if: runner.os == 'Windows'
shell: bash
run: echo "C:/Program Files/NASM" >> $GITHUB_PATH

- name: Install build tools (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
choco install nasm ninja pkgconfiglite -y
pip install meson

- name: Force MSVC compiler for meson (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
echo "CC=cl" >> $GITHUB_ENV
echo "CXX=cl" >> $GITHUB_ENV

- name: Build wheel
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
working-directory: bindings/python

- uses: actions/upload-artifact@v4
with:
name: python-wheels-${{ matrix.target }}
path: bindings/python/dist/*.whl

# ─────────────────────────────────────────────
# Build Python source distribution
# ─────────────────────────────────────────────
build-python-sdist:
name: Build Python sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
working-directory: bindings/python

- uses: actions/upload-artifact@v4
with:
name: python-wheels-sdist
path: bindings/python/dist/*.tar.gz

# ─────────────────────────────────────────────
# Verify Python version matches release tag
# ─────────────────────────────────────────────
verify-python-version:
name: Verify Python package version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check version consistency
shell: bash
run: |
RELEASE_VERSION="${VERSION#v}"
PKG_VERSION=$(python3 -c "
import tomllib
with open('bindings/python/pyproject.toml', 'rb') as f:
print(tomllib.load(f)['project']['version'])
")
if [ "$RELEASE_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Version mismatch: release tag=$RELEASE_VERSION, pyproject.toml=$PKG_VERSION"
exit 1
fi
echo "Version verified: $RELEASE_VERSION"

# ─────────────────────────────────────────────
# Smoke test built Python wheels
# ─────────────────────────────────────────────
smoke-test-python:
name: Smoke test Python wheel (${{ matrix.os }})
needs: [build-python-wheels]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.13'

- uses: actions/download-artifact@v4
with:
pattern: python-wheels-*
path: dist/
merge-multiple: true

- name: Install and smoke test
shell: bash
run: |
pip install dist/*.whl
python -c "
import slimg
print('Format:', slimg.Format.PNG)
print('Extension:', slimg.Format.PNG.extension)
print('Can encode:', slimg.Format.JPEG.can_encode)
# Create a small test image and round-trip it
data = bytes(4 * 4 * 4) # 4x4 RGBA black
img = slimg.Image(4, 4, data)
result = slimg.convert(img, 'png')
assert len(result.data) > 0, 'PNG encode failed'
print('Smoke test passed!')
"

# ─────────────────────────────────────────────
# Publish Python package to PyPI
# ─────────────────────────────────────────────
publish-python:
name: Publish to PyPI
needs: [build-python-wheels, build-python-sdist, verify-python-version, smoke-test-python]
runs-on: ubuntu-latest
permissions:
id-token: write
environment: pypi
steps:
- uses: actions/download-artifact@v4
with:
pattern: python-wheels-*
path: dist/
merge-multiple: true

- uses: actions/download-artifact@v4
with:
name: python-wheels-sdist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
84 changes: 84 additions & 0 deletions .github/workflows/python-bindings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Python Bindings

on:
push:
branches: [main]
paths:
- 'crates/slimg-ffi/**'
- 'crates/slimg-core/**'
- 'bindings/python/**'
- '.github/workflows/python-bindings.yml'
pull_request:
paths:
- 'crates/slimg-ffi/**'
- 'crates/slimg-core/**'
- 'bindings/python/**'
- '.github/workflows/python-bindings.yml'

jobs:
build-and-test:
runs-on: ${{ matrix.os }}
env:
SYSTEM_DEPS_DAV1D_BUILD_INTERNAL: always
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: ['3.9', '3.13']

steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install system dependencies (macOS)
if: runner.os == 'macOS'
run: brew install nasm meson ninja

- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y nasm meson ninja-build

- name: Setup MSVC environment (Windows)
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1

- name: Add NASM to PATH (Windows)
if: runner.os == 'Windows'
shell: bash
run: echo "C:/Program Files/NASM" >> $GITHUB_PATH

- name: Install build tools (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
choco install nasm ninja pkgconfiglite -y
pip install meson

- name: Force MSVC compiler for meson (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
echo "CC=cl" >> $GITHUB_ENV
echo "CXX=cl" >> $GITHUB_ENV

- name: Install maturin and pytest
run: pip install maturin pytest

- name: Build wheel
working-directory: bindings/python
run: maturin build --out dist

- name: Install wheel
working-directory: bindings/python
shell: bash
run: pip install dist/*.whl

- name: Run tests
working-directory: bindings/python
run: pytest tests/ -v
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions bindings/kotlin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ Supports macOS (Apple Silicon, Intel), Linux (x86_64, ARM64), and Windows (x86_6

```kotlin
dependencies {
implementation("io.clroot.slimg:slimg-kotlin:0.1.2")
implementation("io.clroot.slimg:slimg-kotlin:0.3.1")
}
```

### Gradle (Groovy)

```groovy
dependencies {
implementation 'io.clroot.slimg:slimg-kotlin:0.1.2'
implementation 'io.clroot.slimg:slimg-kotlin:0.3.1'
}
```

Expand All @@ -28,7 +28,7 @@ dependencies {
<dependency>
<groupId>io.clroot.slimg</groupId>
<artifactId>slimg-kotlin</artifactId>
<version>0.1.2</version>
<version>0.3.1</version>
</dependency>
```

Expand Down Expand Up @@ -71,6 +71,9 @@ val resized = convert(result.image, PipelineOptions(
| `decode(data: ByteArray)` | Decode image from bytes |
| `decodeFile(path: String)` | Decode image from file path |
| `convert(image, options)` | Convert image to a different format |
| `crop(image, mode)` | Crop an image by region or aspect ratio |
| `extend(image, mode, fill)` | Extend (pad) image canvas |
| `resize(image, mode)` | Resize an image |
| `optimize(data: ByteArray, quality: UByte)` | Re-encode to reduce file size |
| `outputPath(input, format, output?)` | Generate output file path |
| `formatExtension(format)` | Get file extension for a format |
Expand All @@ -84,11 +87,14 @@ val resized = convert(result.image, PipelineOptions(
|------|-------------|
| `Format` | `JPEG`, `PNG`, `WEB_P`, `AVIF`, `JXL`, `QOI` |
| `ResizeMode` | `Width`, `Height`, `Exact`, `Fit`, `Scale` |
| `PipelineOptions` | `format`, `quality`, `resize` |
| `CropMode` | `Region`, `AspectRatio` |
| `ExtendMode` | `AspectRatio`, `Size` |
| `FillColor` | `Transparent`, `Solid(r, g, b, a)` |
| `PipelineOptions` | `format`, `quality`, `resize`, `crop`, `extend`, `fillColor` |
| `PipelineResult` | `data` (ByteArray), `format` |
| `DecodeResult` | `image` (ImageData), `format` |
| `ImageData` | `width`, `height`, `data` (raw pixels) |
| `SlimgException` | Error with subclasses: `Decode`, `Encode`, `Resize`, `Io`, `Image` |
| `SlimgException` | Error with subclasses: `UnsupportedFormat`, `UnknownFormat`, `EncodingNotSupported`, `Decode`, `Encode`, `Resize`, `Crop`, `Extend`, `Io`, `Image` |

## Supported Platforms

Expand Down
2 changes: 1 addition & 1 deletion bindings/kotlin/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
group=io.clroot.slimg
version=0.3.0
version=0.3.1
kotlin.code.style=official
21 changes: 21 additions & 0 deletions bindings/kotlin/src/main/kotlin/io/clroot/slimg/slimg_ffi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,8 @@ internal object IntegrityCheckingUniffiLib {
): Short
external fun uniffi_slimg_ffi_checksum_func_output_path(
): Short
external fun uniffi_slimg_ffi_checksum_func_resize(
): Short
external fun ffi_slimg_ffi_uniffi_contract_version(
): Int

Expand Down Expand Up @@ -692,6 +694,8 @@ internal object UniffiLib {
): RustBuffer.ByValue
external fun uniffi_slimg_ffi_fn_func_output_path(`input`: RustBuffer.ByValue,`format`: RustBuffer.ByValue,`output`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_slimg_ffi_fn_func_resize(`image`: RustBuffer.ByValue,`mode`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun ffi_slimg_ffi_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun ffi_slimg_ffi_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,uniffi_out_err: UniffiRustCallStatus,
Expand Down Expand Up @@ -844,6 +848,9 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) {
if (lib.uniffi_slimg_ffi_checksum_func_output_path() != 60410.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_slimg_ffi_checksum_func_resize() != 48866.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
}

/**
Expand Down Expand Up @@ -2442,4 +2449,18 @@ public object FfiConverterOptionalTypeResizeMode: FfiConverterRustBuffer<ResizeM
}


/**
* Resize an image according to the given mode.
*/
@Throws(SlimgException::class) fun `resize`(`image`: ImageData, `mode`: ResizeMode): ImageData {
return FfiConverterTypeImageData.lift(
uniffiRustCallWithError(SlimgException) { _status ->
UniffiLib.uniffi_slimg_ffi_fn_func_resize(

FfiConverterTypeImageData.lower(`image`),FfiConverterTypeResizeMode.lower(`mode`),_status)
}
)
}



Loading
Loading