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
57 changes: 57 additions & 0 deletions .github/workflows/test-webp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Test WebP

on:
push:
branches:
- 'main'
paths:
- 'webp/**'
- '.github/workflows/test-webp.yml'
pull_request:
workflow_dispatch:

jobs:
test-webp:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
name: Linux (libwebp)
install: sudo apt-get update && sudo apt-get install -y libwebp-dev
expected-decode: libwebp
expected-encode: libwebp
- os: ubuntu-latest
name: Linux (NgEngine)
install: sudo find /usr -name 'libwebp*.so*' -exec mv {} {}.disabled \;
expected-decode: Java (ngengine)
- os: macos-latest
name: macOS (ImageIO)
expected-decode: macOS ImageIO
- os: windows-latest
name: Windows (WIC)
install: winget install --id 9PG2DK419DRG --accept-package-agreements --accept-source-agreements --source msstore --silent
expected-decode: Windows WIC

name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v6

- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 25

- name: Setup native libraries
if: matrix.install
run: ${{ matrix.install }}

- name: Make gradlew executable
if: runner.os != 'Windows'
run: chmod +x ./gradlew

- name: Run WebP tests
shell: bash
run: ./gradlew :webp:test -Dexpected.decode="${{ matrix.expected-decode }}" -Dexpected.encode="${{ matrix.expected-encode }}"
12 changes: 12 additions & 0 deletions .run/TestingApp.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="TestingApp" type="Application" factoryName="Application" nameIsGenerated="true">
<option name="ALTERNATIVE_JRE_PATH" value="jbr-21" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="MAIN_CLASS_NAME" value="org.redlance.platformtools.testing.TestingApp" />
<module name="platformtools.testing.main" />
<option name="VM_PARAMETERS" value="--add-reads=platformtools.webp=ALL-UNNAMED" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
68 changes: 62 additions & 6 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The library is split into independent modules so you can include only what you n
| **referer** | `referer` | File origin/referrer metadata (Windows & macOS) |
| **favorites** | `favorites` | Finder Sidebar favorites (macOS) |
| **progress** | `progress` | Taskbar/dock progress bars (macOS) |
| **webp** | `webp` | Compact WebP codec (libwebp, macOS ImageIO, Windows WIC, pure-Java ngengine fallback) |

## ✨ Features

Expand All @@ -25,6 +26,11 @@ The library is split into independent modules so you can include only what you n
* **[Taskbar Progress Bars](#4-taskbar-progress-bars)** (`progress`): Display progress indicators on the taskbar/dock icon.
* Supports multiple stacked progress bars (up to 8).
* *Note: Windows taskbar support is planned for future releases.*
* **[WebP Codec](#5-webp-codec)** (`webp`): Compact, pixel-exact WebP decoding/encoding (Java 22+, FFM API).
* **macOS:** ImageIO (CoreGraphics)
* **Windows:** WIC (Windows Imaging Component)
* **Cross-platform:** libwebp (if found on library path)
* **Pure-Java fallback:** [ngengine image-webp-java](https://github.com/NostrGameEngine/image-webp-java) (~92 KB bundled)
* **Java 8 Compatible:** Built on Java 17 but distributed with a Java 8 compatible version (Multi-Release JAR).

## 📦 Installation
Expand All @@ -39,13 +45,14 @@ repositories {

dependencies {
// Include only the modules you need:
implementation "org.redlance.platformtools:accent:3.3.0"
implementation "org.redlance.platformtools:referer:3.3.0"
implementation "org.redlance.platformtools:favorites:3.3.0"
implementation "org.redlance.platformtools:progress:3.3.0"
implementation "org.redlance.platformtools:accent:4.0.0"
implementation "org.redlance.platformtools:referer:4.0.0"
implementation "org.redlance.platformtools:favorites:4.0.0"
implementation "org.redlance.platformtools:progress:4.0.0"
implementation "org.redlance.platformtools:webp:4.0.0"

// For Java 8 (Downgraded version), add classifier:
// implementation("org.redlance.platformtools:accent:3.3.0:java8")
// implementation("org.redlance.platformtools:accent:4.0.0:java8")
}
```

Expand All @@ -62,7 +69,7 @@ dependencies {
<dependency>
<groupId>org.redlance.platformtools</groupId>
<artifactId>accent</artifactId>
<version>3.3.0</version>
<version>4.0.0</version>
</dependency>
```

Expand Down Expand Up @@ -187,6 +194,55 @@ public class ProgressExample {
}
```

### 5. WebP Codec
Compact, pixel-exact WebP decoding and encoding module. The goal is **minimal footprint** and **zero native dependencies** out of the box — the bundled pure-Java decoder adds only ~92 KB (after ProGuard) while producing bit-identical output to libwebp.

> **Note:** Requires Java 22+ (uses the FFM API). Other modules remain Java 8 compatible.

Decoder priority (first available wins):
1. **libwebp** — system-installed native library (fastest, encode + decode)
2. **macOS ImageIO** — CoreGraphics via FFM (decode only, no extra dependencies)
3. **Windows WIC** — Windows Imaging Component via FFM (decode only, requires WebP codec from MS Store)
4. **ngengine** — pure-Java fallback ([image-webp-java](https://github.com/NostrGameEngine/image-webp-java), decode only, ~92 KB bundled)

The `webp` module has two variants:
- **Standard** (`webp`) — uses platform-native APIs or system-installed libwebp. Zero bundled dependencies.
- **Bundled** (`webp` with classifier `bundled`) — includes a shaded and ProGuard-optimized [ngengine image-webp-java](https://github.com/NostrGameEngine/image-webp-java) decoder (~92 KB). Pixel-exact on all platforms without native libraries.

```groovy
// Standard — requires platform support or libwebp on library path
implementation "org.redlance.platformtools:webp:4.0.0"

// Bundled — self-contained, works everywhere (decode only)
implementation "org.redlance.platformtools:webp:4.0.0:bundled"
```

```java
import org.redlance.platformtools.webp.decoder.PlatformWebPDecoder;
import org.redlance.platformtools.webp.decoder.DecodedImage;
import org.redlance.platformtools.webp.encoder.PlatformWebPEncoder;

public class WebPExample {
public void decodeWebP(byte[] webpData) {
// Get image dimensions without full decode
int[] info = PlatformWebPDecoder.INSTANCE.getInfo(webpData);
System.out.println("Size: " + info[0] + "x" + info[1]);

// Full decode to ARGB pixels
DecodedImage image = PlatformWebPDecoder.INSTANCE.decode(webpData);
int[] argb = image.argb(); // width * height pixels
}

public void encodeWebP(int[] argb, int width, int height) {
// Lossless encoding (requires libwebp or platform encoder)
byte[] lossless = PlatformWebPEncoder.INSTANCE.encodeLossless(argb, width, height);

// Lossy encoding (0.0 = smallest, 1.0 = best quality)
byte[] lossy = PlatformWebPEncoder.INSTANCE.encodeLossy(argb, width, height, 0.75f);
}
}
```

## 🛠 System Requirements
* **Java:** 8 or higher.
* **Operating Systems:**
Expand Down
29 changes: 24 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "com.guardsquare:proguard-gradle:7.8.2"
}
}

plugins {
id "xyz.wagyourtail.jvmdowngrader" version "1.3.6" apply false
id "com.gradleup.shadow" version "9.3.2" apply false
}

allprojects {
group = "org.redlance"
version = "3.3.1"
version = "4.0.0"

repositories {
mavenCentral()
Expand Down Expand Up @@ -58,6 +68,8 @@ configure(publishedModules) {
}

shadeDowngradedApi {
onlyIf { !downgradeJar.state.skipped }

downgradeTo = JavaVersion.VERSION_1_8
shadePath {
return "org/redlance/platformtools/"
Expand All @@ -72,10 +84,6 @@ configure(publishedModules) {
publishing {
publications {
mavenJava(MavenPublication) {
artifact(tasks.shadeDowngradedApi) {
classifier = "java8"
extension = "jar"
}

groupId = "org.redlance.platformtools"
artifactId = project.name
Expand Down Expand Up @@ -111,6 +119,17 @@ configure(publishedModules) {
}
}

afterEvaluate {
publications.mavenJava {
if (tasks.downgradeJar.enabled) {
artifact(tasks.shadeDowngradedApi) {
classifier = "java8"
extension = "jar"
}
}
}
}

repositories {
maven {
name = "RedlanceMinecraft"
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ include "referer"
include "favorites"
include "progress"
include "testing"
include "webp"
24 changes: 24 additions & 0 deletions testing/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
plugins {
id "com.gradleup.shadow"
}

dependencies {
implementation(project(":accent"))
implementation(project(":referer"))
implementation(project(":favorites"))
implementation(project(":progress"))
implementation(project(":webp"))
}

tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.release.set(25)
}

jar {
manifest.attributes("Main-Class": "org.redlance.platformtools.testing.TestingApp")
}

shadowJar {
dependencies {
exclude(project(":webp"))
}

dependsOn(":webp:proguardJar")
from(zipTree(project(":webp").file("build/libs/PlatformTools-webp-${version}-bundled.jar")))
mergeServiceFiles()
}
1 change: 1 addition & 0 deletions testing/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
requires platformtools.referer;
requires platformtools.favorites;
requires platformtools.progress;
requires platformtools.webp;
requires java.desktop;
}
Loading