From 688e4364b5e54bcad608852d5855618ca506bdf4 Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 14 Feb 2026 00:24:55 +0800 Subject: [PATCH 1/7] docs: restructure documentation and optimize README - Consolidate usage.md into README Manual section - Add C++ custom filter examples and threading guidelines - Extract filter rules to docs/filter-rules.md (437 lines) - Reduce README from 254 to 193 lines (-24%) - Add Key Classes table and GL threading warnings - Move contributing/release docs to .github/ - Create AI instruction files (.github/skills/, copilot-instructions.md) - Add .copilotignore to exclude build artifacts BREAKING: docs/usage.md removed (content merged into README) --- .copilotignore | 23 + .github/CONTRIBUTING.md | 64 +++ .github/RELEASE.md | 56 ++ .github/copilot-instructions.md | 73 ++- .github/instructions/cpp.instructions.md | 5 +- .github/instructions/java.instructions.md | 4 +- .github/skills/pr-review/SKILL.md | 67 +++ .github/skills/pr-submit/SKILL.md | 60 +++ .github/workflows/release.yml | 593 ++++++++++++++++++---- README.md | 195 +++---- docs/build.md | 102 ++++ docs/filter-rules.md | 437 ++++++++++++++++ 12 files changed, 1418 insertions(+), 261 deletions(-) create mode 100644 .copilotignore create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/RELEASE.md create mode 100644 .github/skills/pr-review/SKILL.md create mode 100644 .github/skills/pr-submit/SKILL.md create mode 100644 docs/build.md create mode 100644 docs/filter-rules.md diff --git a/.copilotignore b/.copilotignore new file mode 100644 index 00000000..49835436 --- /dev/null +++ b/.copilotignore @@ -0,0 +1,23 @@ +# Build outputs +build/ +.gradle/ +.cxx/ +*.apk +*.aab + +# Prebuilt native libraries (large binaries) +library/src/main/libs/ +library/src/main/jni/ffmpeg/ +library/src/main/jni/ffmpeg-16kb/ + +# IDE configuration +.idea/ +*.iml +local.properties + +# OS artifacts +.DS_Store +Thumbs.db + +# Release artifacts +demoRelease/ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..668ac05e --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# Contributing Guide + +## Getting Started + +1. Fork the repository +2. Create a feature branch from `master` (`feat/xxx`, `fix/xxx`, `refactor/xxx`) +3. Make your changes +4. Verify the build: `bash tasks.sh --enable-cmake --build` +5. Submit a pull request to `master` + +## Code Standards + +- Use **English** for all code comments, commit messages, and PR descriptions +- Follow conventional-commit format for commits: `feat:`, `fix:`, `refactor:`, `chore:` +- One logical change per commit + +### C++ (Native) + +- Follow `.clang-format` (Allman braces, 4-space indent) +- All code in `namespace CGE {}` +- No C++ exceptions (compiled with `-fno-exceptions`) +- Use logging macros: `CGE_LOG_INFO`, `CGE_LOG_ERROR`, `CGE_LOG_KEEP` +- Inline GLSL via `CGE_SHADER_STRING_PRECISION_H()` / `CGE_SHADER_STRING_PRECISION_M()` +- Release GL resources (programs, textures, FBOs) along the handler lifecycle + +### Java (Android) + +- GL operations in `GLSurfaceView` subclasses must use `queueEvent(...)` +- Load native libraries through `NativeLibraryLoader` only +- Check `BuildConfig.CGE_USE_VIDEO_MODULE` before referencing FFmpeg classes + +### JNI Bridge + +- JNI function names: `Java_org_wysaid_nativePort__` +- Always validate JNI parameters (null checks) before dereferencing +- Never store JNI local references across calls + +## Compatibility Rules + +- **Do not change** method signatures in `org.wysaid.nativePort.*` +- **Do not change** existing JNI function names +- **Do not change** existing filter rule string syntax +- New methods, filters, and syntax additions are welcome + +## FFmpeg / Video Module + +All FFmpeg-dependent code must be optional: + +- C++: guard with `#ifdef CGE_USE_VIDEO_MODULE` +- Java: check `BuildConfig.CGE_USE_VIDEO_MODULE` +- Verify the project compiles with `--disable-video-module` + +## Dual Build System + +When adding new native source files under `library/src/main/jni/`: + +- **CMake**: auto-discovered via `GLOB_RECURSE` — usually no action needed +- **ndk-build**: update `Android.mk` to explicitly list the new files + +## CI + +Pull requests trigger CI on three platforms (macOS, Ubuntu, Windows). PRs run a reduced matrix (1 combination per platform); merges to `master` run the full 16-combination matrix. + +Ensure all CI checks pass before requesting review. diff --git a/.github/RELEASE.md b/.github/RELEASE.md new file mode 100644 index 00000000..f373e5d0 --- /dev/null +++ b/.github/RELEASE.md @@ -0,0 +1,56 @@ +# Release Process + +This project uses an automated release workflow triggered by version tags. + +## Creating a Release + +### 1. Update the Version + +Edit `versionName` in `build.gradle`: + +```gradle +ext { + android = [ + ... + versionName: "3.1.1", + ... + ] +} +``` + +### 2. Commit and Push + +```bash +git add build.gradle +git commit -m "chore: bump version to 3.1.1" +git push +``` + +### 3. Tag and Push + +```bash +git tag v3.1.1 +git push origin v3.1.1 +``` + +## Automated Workflow + +When a tag matching `v*.*.*` is pushed, the [release workflow](../.github/workflows/release.yml) will: + +1. **Validate** that the tag version matches `versionName` in `build.gradle` +2. **Build** four AAR variants: + - `gpuimage-plus-{version}.aar` — Full with FFmpeg, 4KB page size + - `gpuimage-plus-{version}-16k.aar` — Full with FFmpeg, 16KB page size + - `gpuimage-plus-{version}-min.aar` — Image-only, 4KB page size + - `gpuimage-plus-{version}-16k-min.aar` — Image-only, 16KB page size +3. **Build** the demo APK (with video module) +4. **Create** a GitHub release with all artifacts and release notes + +## Tag Format + +- Official: `v3.1.1` +- Beta: `v3.1.1-beta1` +- Alpha: `v3.1.1-alpha1` +- Release candidate: `v3.1.1-rc1` + +All version parts must be pure numbers. The tag version must exactly match `versionName` in `build.gradle`. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d9e47533..e72f0fbb 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,57 +1,52 @@ # GitHub Copilot Instructions — android-gpuimage-plus -This repo is a widely-used Android GPU filter library. Prefer **small, safe, backward-compatible** changes. +Android GPU filter library (C++ & Java). Prefer **small, safe, backward-compatible** changes. -## Non‑negotiables (stability first) +## Stability Rules -- **Do not break compatibility**: avoid changing public Java APIs, JNI method signatures, or filter rule string syntax. -- Prefer **additive** changes (new classes/methods/filters) over refactors. -- Keep FFmpeg/video code **fully optional** (both Java and native side guards). +- **Never break public API**: `org.wysaid.nativePort.*` Java signatures, JNI method names, and filter rule string syntax are stable contracts. +- Prefer **additive** changes over refactoring existing code. +- FFmpeg/video code must remain **fully optional** — guard with `disableVideoModule` (Java/Gradle) / `CGE_USE_VIDEO_MODULE` (C++). -## Where things live (only what’s non-obvious) +## Project Layout -- Java public API / JNI front door: `library/src/main/java/org/wysaid/nativePort/` - - `CGENativeLibrary`, `CGEImageHandler`, `CGEFrameRenderer`, `CGEFrameRecorder` -- Native engine: `library/src/main/jni/cge/` -- JNI bridge (Java ↔ C++): `library/src/main/jni/interface/` -- Extension filters (built as `libCGEExt.so`): `library/src/main/jni/custom/` -- Prebuilt FFmpeg variants: `library/src/main/jni/ffmpeg/` and `ffmpeg-16kb/` -- Demo app (`cgeDemo/`) is for examples; avoid coupling new library APIs to demo-only code. +| Path | Purpose | +|------|---------| +| `library/src/main/java/org/wysaid/nativePort/` | Public Java API (`CGENativeLibrary`, `CGEImageHandler`, etc.) | +| `library/src/main/jni/cge/` | Core C++ engine | +| `library/src/main/jni/interface/` | JNI bridge (Java ↔ C++) | +| `library/src/main/jni/custom/` | Extension filters (`libCGEExt.so`) | +| `library/src/main/jni/ffmpeg/`, `ffmpeg-16kb/` | Prebuilt FFmpeg variants | +| `cgeDemo/` | Demo app — don't couple library APIs to demo-only code | -## Build toggles you must respect +## Build -These flags come from `local.properties` (see `settings.gradle`): +Use `tasks.sh` for all operations (`bash tasks.sh --help`). Key `local.properties` flags: `usingCMakeCompile`, `disableVideoModule`, `enable16kPageSizes`. See `docs/build.md` for details. -- `usingCMakeCompile`: build native code via CMake; otherwise use prebuilt `.so` in `library/src/main/libs/`. -- `usingCMakeCompileDebug`: toggles Debug vs Release native build. -- `disableVideoModule`: when true, **no FFmpeg / no recording**. -- `enable16kPageSizes`: enables 16KB page-size linking flags and uses `ffmpeg-16kb`. -- `deployArtifacts`: enables maven publishing tasks. +**Important**: CMake uses `GLOB_RECURSE`; ndk-build (`Android.mk`) lists sources **explicitly** — update both when adding native files. -Important: CMake uses recursive globs, but **ndk-build (`Android.mk`) lists sources explicitly**. If you add native files that must compile in ndk-build mode, update `Android.mk` accordingly. +## Code Conventions -## Native/C++ conventions that matter here +Language-specific rules live in `.github/instructions/` and are auto-applied by file path — see `cpp.instructions.md`, `java.instructions.md`, `jni-bridge.instructions.md`. -- Follow `.clang-format` (Allman braces, 4 spaces, no tabs). Keep code in `namespace CGE {}`. -- **No C++ exceptions** (compiled with `-fno-exceptions`). Use return values and log errors. -- Use project logging macros: `CGE_LOG_INFO`, `CGE_LOG_KEEP`, `CGE_LOG_ERROR`. -- Inline shader strings via `CGE_SHADER_STRING_PRECISION_H()` / `CGE_SHADER_STRING_PRECISION_M()`. +For detailed standards, see `.github/CONTRIBUTING.md`. -## Java/OpenGL thread rules (project-specific pitfalls) +## Documentation -- For `GLSurfaceView`-based classes, do GL operations on the GL thread via `queueEvent(...)`. -- `NativeLibraryLoader` controls native library loading order; don’t unconditionally load FFmpeg when video module is disabled. +- `docs/build.md` — Full build guide and configuration options +- `docs/usage.md` — API usage, custom filters, and code samples +- `.github/RELEASE.md` — Release process and versioning +- `.github/CONTRIBUTING.md` — Contributing guidelines and code standards -## Adding a new GPU filter / feature (minimal checklist) +## Skills -1. Implement the filter in `library/src/main/jni/custom/` (or `cge/filters/` if it’s core). -2. Initialize shaders/uniforms in `init()`; handle compile/link failures without crashing. -3. Ensure GL resources are released (programs/textures/FBOs) along the existing lifecycle. -4. If the feature is exposed to Java, add/update JNI wrappers under `library/src/main/jni/interface/` and keep signatures stable. -5. If it should be reachable from rule strings, update the parsing/registration in the native engine (don’t change existing syntax). -6. Keep FFmpeg/video-dependent logic behind `disableVideoModule` / `CGE_USE_VIDEO_MODULE` guards. +- **Submit PR:** Follow `.github/skills/pr-submit/SKILL.md` to create or update pull requests +- **Review PR:** Follow `.github/skills/pr-review/SKILL.md` to review pull requests -## Communication & docs +## Behavior Constraints -- Use **English** for code comments, commit messages, and PR descriptions. -- Don’t add new “how to build” tutorials here—put long-form docs in `README.md` (this file should stay short). +- **Validation:** After native code changes, ALWAYS run `bash tasks.sh --enable-cmake --build`. +- **Commit Policy:** Only commit to feature branches, never force-push to `master`. +- **Completeness:** Implement fully or request clarification — no TODOs in committed code. +- **Dual Build:** When adding native files, update both `CMakeLists.txt` and `Android.mk`. +- **Thread Safety:** Never call OpenGL ES functions outside the GL thread. diff --git a/.github/instructions/cpp.instructions.md b/.github/instructions/cpp.instructions.md index 74e14dd3..ad7dd945 100644 --- a/.github/instructions/cpp.instructions.md +++ b/.github/instructions/cpp.instructions.md @@ -1,9 +1,6 @@ --- description: "Use when editing C++ native code. Covers CGE engine conventions, shader patterns, memory safety, and OpenGL ES resource management." -applyTo: - - "library/src/main/jni/cge/**/*.{cpp,h,c}" - - "library/src/main/jni/custom/**/*.{cpp,h,c}" - - "library/src/main/jni/interface/**/*.{cpp,h,c}" +applyTo: "library/src/main/jni/{cge,custom,interface}/**/*.{cpp,h,c}" --- # C++ / Native Layer diff --git a/.github/instructions/java.instructions.md b/.github/instructions/java.instructions.md index 064d2a5c..7ea50219 100644 --- a/.github/instructions/java.instructions.md +++ b/.github/instructions/java.instructions.md @@ -1,8 +1,6 @@ --- description: "Use when editing Java Android library or demo code. Covers GL thread safety and native interop patterns." -applyTo: - - "library/src/main/java/**/*.java" - - "cgeDemo/src/main/java/**/*.java" +applyTo: "library/src/main/java/**/*.java|cgeDemo/src/main/java/**/*.java" --- # Java / Android Layer diff --git a/.github/skills/pr-review/SKILL.md b/.github/skills/pr-review/SKILL.md new file mode 100644 index 00000000..01055654 --- /dev/null +++ b/.github/skills/pr-review/SKILL.md @@ -0,0 +1,67 @@ +--- +name: pr-review +description: Review a GitHub Pull Request for API stability, build compliance, native code safety, and code quality +--- + +## When to Use + +- Need to review a pull request before merge +- Validate PR changes against project rules +- Provide structured review feedback + +## Constraints + +- All review comments **must** be in English +- Work through checks **in order** — stop and flag any blocker immediately +- Reference `.github/CONTRIBUTING.md` for detailed code standards + +## Procedure + +Follow `.github/CONTRIBUTING.md` for detailed rules. Check in order: + +1. **API Stability** (Blocker): + - No changes to `org.wysaid.nativePort.*` method signatures + - No changes to JNI function names + - Filter rule string syntax backward-compatible +2. **Build Toggle Compliance** (Blocker): + - FFmpeg/video code properly guarded (C++: `#ifdef CGE_USE_VIDEO_MODULE`, Java: `BuildConfig.CGE_USE_VIDEO_MODULE`) + - Compiles with `disableVideoModule=true` +3. **Native Code Safety** (High): + - No C++ exceptions, JNI null checks, GL resource cleanup, shader error handling +4. **Dual Build System** (Medium): + - New native files → `Android.mk` updated +5. **Code Quality**: Follow CONTRIBUTING.md standards +6. **CI Status**: All platform workflows pass + +## Output + +Structure the review as: + +```markdown +## PR Review: + +### Summary +One-sentence assessment: Approve / Request Changes / Needs Discussion + +### Findings + +#### Blockers +- (API stability or build toggle violations) + +#### Issues +- (correctness, safety, or quality problems) + +#### Suggestions +- (optional improvements, not blocking) + +### Verdict +APPROVE / REQUEST_CHANGES with rationale +``` + +## Severity Guide + +| Severity | Criteria | Action | +|----------|----------|--------| +| **Blocker** | API break, build toggle violation, crash bug | Request changes | +| **Issue** | Memory/GL resource leak, missing null check, wrong thread | Request changes if risky, else comment | +| **Suggestion** | Style, naming, minor optimization | Comment only | diff --git a/.github/skills/pr-submit/SKILL.md b/.github/skills/pr-submit/SKILL.md new file mode 100644 index 00000000..87e81359 --- /dev/null +++ b/.github/skills/pr-submit/SKILL.md @@ -0,0 +1,60 @@ +--- +name: pr-submit +description: Create or update a GitHub Pull Request after completing code changes +--- + +## When to Use + +- Code changes are complete and ready for review +- Need to create a new PR or update an existing one + +## Constraints + +- PR title, description, and comments **must** be in English +- Follow conventional-commit format for titles: `feat:`, `fix:`, `refactor:`, `chore:` +- Target branch: `master` +- **Never** force-push to `master` +- Follow `.github/CONTRIBUTING.md` for code standards and compatibility rules + +## Procedure + +1. **Verify Build**: Run `bash tasks.sh --enable-cmake --build` + - If touching FFmpeg/video code: verify with `--disable-video-module` + - If new native files added: confirm `Android.mk` updated +2. **Check Compatibility**: Review `.github/CONTRIBUTING.md` compatibility rules + - Confirm no public API signature changes in `org.wysaid.nativePort.*` +3. **Branch & Commit**: + - Create feature branch if on `master` (`feat/`, `fix/`, `refactor/`) + - Follow conventional-commit format (see CONTRIBUTING.md) +4. **Create PR**: Push branch and create PR against `master` + - Use conventional-commit format for title + - Fill in description template below +5. **Verify CI**: Ensure all platform workflows pass + +## PR Description Template + +```markdown +## Summary + +Brief description of what this PR does and why. + +## Changes + +- Bullet list of key changes + +## Compatibility + +- [ ] No public API changes (`org.wysaid.nativePort.*`) +- [ ] No filter rule string syntax changes +- [ ] FFmpeg/video code guarded with `disableVideoModule` / `CGE_USE_VIDEO_MODULE` (or N/A) +- [ ] Both CMake and ndk-build updated (or N/A — no new native source files) + +## Testing + +How was this tested (e.g., "built with CMake + no-ffmpeg on macOS", "ran cgeDemo on Pixel 7"). +``` + +## Output + +- PR with English title and description +- All CI checks passing diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1475c10..3816921a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,74 +1,37 @@ -name: Release APKs +name: Build and Release on: push: - branches: - - "*workflow*" tags: - - 'v*.*.*' - - 'v*.*.*-beta*' - - 'v*.*.*-alpha*' - - 'v*.*.*-rc*' + - 'v*.*.*' # Official release tags (e.g., v3.1.1) + - 'v*.*.*-beta*' # Beta releases (e.g., v3.1.1-beta1) + - 'v*.*.*-alpha*' # Alpha releases (e.g., v3.1.1-alpha1) + - 'v*.*.*-rc*' # Release candidates (e.g., v3.1.1-rc1) workflow_dispatch: inputs: version: - description: 'Version tag (e.g. v1.0.0-test)' + description: 'Version number (e.g., 3.1.1) - Manual trigger will NOT publish to Release page' required: true - default: 'v0.1.0-test' - create_release: - description: 'Whether to create GitHub Release' - type: boolean - default: false + type: string -permissions: - contents: write +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false jobs: - build: - name: "Release | ${{ matrix.artifact_name }}" + validate-and-release: + name: Validate Version and Build Release runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - include: - - artifact_name: gpuimage-plus - ffmpeg_flag: --enable-video-module - page_flag: --disable-16kb-page-size - - artifact_name: gpuimage-plus-16k - ffmpeg_flag: --enable-video-module - page_flag: --enable-16kb-page-size - - artifact_name: gpuimage-plus-min - ffmpeg_flag: --disable-video-module - page_flag: --disable-16kb-page-size - - artifact_name: gpuimage-plus-16k-min - ffmpeg_flag: --disable-video-module - page_flag: --enable-16kb-page-size - + + permissions: + contents: write # Required for creating releases and uploading artifacts + steps: - name: Checkout code uses: actions/checkout@v4 - - - name: Resolve release tag - shell: bash - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "RELEASE_TAG=${{ inputs.version }}" >> $GITHUB_ENV - echo "SHOULD_RELEASE=${{ inputs.create_release }}" >> $GITHUB_ENV - elif [ "${{ github.ref_type }}" = "branch" ]; then - VERSION=$(grep -E 'versionName' build.gradle | head -n 1 | sed -E 's/.*versionName[^\"]*\"([^\"]+)\".*/\1/') - if [ -z "$VERSION" ]; then - echo "Error: Failed to parse versionName from build.gradle" - exit 1 - fi - echo "RELEASE_TAG=v${VERSION}-test" >> $GITHUB_ENV - echo "SHOULD_RELEASE=false" >> $GITHUB_ENV - else - echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV - echo "SHOULD_RELEASE=true" >> $GITHUB_ENV - fi - echo "APK_NAME=cgeDemo-${{ matrix.artifact_name }}.apk" >> $GITHUB_ENV - + with: + fetch-depth: 0 + - name: Setup NDK uses: nttld/setup-ndk@v1.4.2 id: setup-ndk @@ -76,46 +39,502 @@ jobs: ndk-version: r26d link-to-sdk: true add-to-path: true - - - name: Install build tools (Ubuntu) - run: sudo apt-get update && sudo apt-get install -y ninja-build - - - name: Setup JDK 17 + + - name: Install build tools + run: sudo apt-get update && sudo apt-get install -y ninja-build jq rsync + + - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' cache: gradle - + + - name: Cache NDK and CMake builds + uses: actions/cache@v4 + with: + path: | + ~/.android/build-cache + library/.cxx + library/build + key: ${{ runner.os }}-ndk-${{ hashFiles('**/*.gradle*', '**/CMakeLists.txt') }} + restore-keys: | + ${{ runner.os }}-ndk- + - name: Grant execute permission for scripts - run: chmod +x gradlew tasks.sh - - - name: Build APK - shell: bash + run: chmod +x gradlew tasks.sh publish.sh + + - name: Extract tag version + id: tag_version + run: | + # Determine if this is a manual trigger or tag push + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + # Manual trigger - use input version + VERSION="${{ github.event.inputs.version }}" + TAG_NAME="v$VERSION" + IS_MANUAL="true" + IS_PRERELEASE="false" + BASE_VERSION="$VERSION" + echo "🔧 Manual trigger detected" + else + # Tag push - extract from ref + TAG_NAME="${GITHUB_REF#refs/tags/}" + IS_MANUAL="false" + + # Extract version numbers supporting prerelease tags (e.g., v3.1.1-beta1 -> 3.1.1-beta1) + if [[ $TAG_NAME =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-[a-zA-Z0-9\.]+)?$ ]]; then + BASE_VERSION="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}" + PRERELEASE_SUFFIX="${BASH_REMATCH[4]}" + VERSION="${BASE_VERSION}${PRERELEASE_SUFFIX}" + + if [ -n "$PRERELEASE_SUFFIX" ]; then + IS_PRERELEASE="true" + echo "🧪 Prerelease detected: $PRERELEASE_SUFFIX" + else + IS_PRERELEASE="false" + fi + else + echo "❌ Invalid tag format: $TAG_NAME" + echo "Tag must be: v{major}.{minor}.{patch}[-prerelease] (e.g., v3.1.1 or v3.1.1-beta1)" + exit 1 + fi + fi + + # Validate base version format + if [[ ! $BASE_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Invalid base version format: $BASE_VERSION" + echo "Version must be in format: \${major}.\${minor}.\${patch} (e.g., 3.1.1)" + exit 1 + fi + + echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "base_version=$BASE_VERSION" >> $GITHUB_OUTPUT + echo "is_manual=$IS_MANUAL" >> $GITHUB_OUTPUT + echo "is_prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT + echo "✅ Valid version: $VERSION (tag: $TAG_NAME, prerelease: $IS_PRERELEASE, manual: $IS_MANUAL)" + + - name: Validate version matches project version + run: | + PROJECT_VERSION=$(grep -E 'versionName\s*:' build.gradle | sed -E 's/.*versionName\s*:\s*"([^"]+)".*/\1/') + TAG_BASE_VERSION="${{ steps.tag_version.outputs.base_version }}" + TAG_VERSION="${{ steps.tag_version.outputs.version }}" + + echo "Project version: $PROJECT_VERSION" + echo "Tag base version: $TAG_BASE_VERSION" + echo "Tag full version: $TAG_VERSION" + + # For prerelease versions, only validate against base version + if [ "$PROJECT_VERSION" != "$TAG_BASE_VERSION" ]; then + echo "❌ Version mismatch!" + echo "Tag base version ($TAG_BASE_VERSION) does not match project version ($PROJECT_VERSION)" + echo "Please update the versionName in build.gradle to match the tag, or use the correct tag." + exit 1 + fi + + echo "✅ Version validation passed: $TAG_BASE_VERSION (full: $TAG_VERSION)" + + - name: Setup local.properties for publishing + run: | + # Create local.properties with necessary configurations + mkdir -p /tmp/maven-repo + cat > local.properties << EOF + usingCMakeCompile=true + usingCMakeCompileDebug=false + deployArtifacts=true + localRepoDir=/tmp/maven-repo + EOF + + echo "✅ local.properties configured for publishing" + cat local.properties + + - name: Build all artifact variants + id: build_variants + run: | + BUILD_START=$(date +%s) + echo "🔨 Building all 4 artifact variants..." + bash -e ./publish.sh + BUILD_END=$(date +%s) + BUILD_TIME=$((BUILD_END - BUILD_START)) + + echo "✅ All artifacts built successfully" + echo "⏱️ Build time: $((BUILD_TIME / 60)) minutes $((BUILD_TIME % 60)) seconds" + echo "📦 Generated artifacts:" + find /tmp/maven-repo -type f \( -name "*.aar" -o -name "*.pom" \) | sort + + echo "build_time=$BUILD_TIME" >> $GITHUB_OUTPUT + + - name: Build demo APK with video module + run: | + echo "🔨 Building demo APK with video module (default configuration)..." + ./tasks.sh --enable-cmake --release --enable-video-module --disable-16kb-page-size --build + + # Find and copy the APK (look specifically for release build output) + APK_PATH=$(find "cgeDemo/build/outputs/apk/release" \( -name "*-release.apk" -o -name "*-release-unsigned.apk" \) | head -1) + if [ -z "$APK_PATH" ]; then + # Fallback: search in build directory + APK_PATH=$(find "cgeDemo/build" -path "*/release/*" -name "*.apk" | head -1) + fi + if [ -z "$APK_PATH" ]; then + echo "❌ APK not found!" + echo "Searched locations:" + find "cgeDemo/build" -name "*.apk" || echo "No APK files found" + exit 1 + fi + + mkdir -p /tmp/release-artifacts + cp "$APK_PATH" /tmp/release-artifacts/cgeDemo-${{ steps.tag_version.outputs.version }}.apk + + echo "✅ Demo APK built successfully" + ls -lh /tmp/release-artifacts/ + + - name: Validate APK size and properties + run: | + echo "🔍 Checking APK size and basic properties..." + APK_FILE=$(ls /tmp/release-artifacts/*.apk | head -1) + APK_SIZE=$(stat -c%s "$APK_FILE" 2>/dev/null || stat -f%z "$APK_FILE") + APK_SIZE_MB=$((APK_SIZE / 1024 / 1024)) + + echo "📦 APK size: ${APK_SIZE_MB} MB" + + # Warn if APK is unusually large + if [ $APK_SIZE -gt 104857600 ]; then # 100MB + echo "::warning::APK size (${APK_SIZE_MB} MB) exceeds 100MB, consider optimization" + fi + + # Warn if APK is unusually small (might indicate build issue) + if [ $APK_SIZE -lt 5242880 ]; then # 5MB + echo "::warning::APK size (${APK_SIZE_MB} MB) is unusually small, please verify" + fi + + echo "✅ APK validation passed" + + - name: Package AAR artifacts + run: | + echo "📦 Packaging AAR artifacts..." + + VERSION="${{ steps.tag_version.outputs.version }}" + MAVEN_REPO="/tmp/maven-repo" + ARTIFACTS_DIR="/tmp/release-artifacts" + + # Find and copy all AAR files from the maven repository + # Expected structure: /tmp/maven-repo/org/wysaid/gpuimage-plus/{version}/gpuimage-plus-{version}.aar + while IFS= read -r aar_file; do + if [ -n "$aar_file" ]; then + filename=$(basename "$aar_file") + cp "$aar_file" "$ARTIFACTS_DIR/$filename" + echo " ✓ Packaged: $filename" + fi + done < <(find "$MAVEN_REPO/org/wysaid/gpuimage-plus" -name "*.aar") + + # Validate that we have exactly 4 AAR files + AAR_COUNT=$(find "$ARTIFACTS_DIR" -name "*.aar" | wc -l) + if [ "$AAR_COUNT" -ne 4 ]; then + echo "❌ Expected 4 AAR files but found $AAR_COUNT" + exit 1 + fi + + echo "✅ All AAR artifacts packaged ($AAR_COUNT files)" + echo "📦 Final artifacts:" + ls -lh "$ARTIFACTS_DIR/" + + - name: Generate artifact checksums run: | - ./tasks.sh --release --enable-cmake ${{ matrix.ffmpeg_flag }} ${{ matrix.page_flag }} --build + echo "🔐 Generating SHA256 checksums for all artifacts..." + cd /tmp/release-artifacts + + # Generate checksums for all files + sha256sum *.apk *.aar > SHA256SUMS.txt 2>/dev/null || shasum -a 256 *.apk *.aar > SHA256SUMS.txt + + echo "✅ Checksums generated:" + cat SHA256SUMS.txt + + # Also create individual checksum files for convenience + for file in *.apk *.aar; do + if [ -f "$file" ]; then + sha256sum "$file" > "${file}.sha256" 2>/dev/null || shasum -a 256 "$file" > "${file}.sha256" + fi + done + + echo "✅ Individual checksum files created" - - name: Collect APK - shell: bash + - name: Sync artifacts to maven repo and open PR + if: steps.tag_version.outputs.is_manual != 'true' + id: sync_maven_repo run: | - APK_FILE=$(find "cgeDemo/build" -name "*.apk" | grep -i release | head -n 1) - mkdir -p artifacts - cp "$APK_FILE" "artifacts/${{ env.APK_NAME }}" - echo "APK saved as: artifacts/${{ env.APK_NAME }}" + set -euo pipefail - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.artifact_name }} - path: artifacts/*.apk - compression-level: 0 - retention-days: 15 - if-no-files-found: error - - - name: Upload to GitHub Release - if: env.SHOULD_RELEASE == 'true' + VERSION="${{ steps.tag_version.outputs.version }}" + TAG_NAME="${{ steps.tag_version.outputs.tag_name }}" + ARTIFACT_REPO_TOKEN="${{ secrets.ARTIFACT_REPO_TOKEN }}" + + if [[ -z "${ARTIFACT_REPO_TOKEN:-}" ]]; then + echo "❌ Missing secret: ARTIFACT_REPO_TOKEN (repo:write to android-gpuimage-plus-maven)" + exit 1 + fi + + ARTIFACT_REPO="wysaid/android-gpuimage-plus-maven" + WORKDIR="/tmp/maven-repo-target" + SOURCE_REPO="/tmp/maven-repo" + BRANCH="sync/v${VERSION}" + + echo "🔄 Cloning artifact repo..." + git clone --depth 1 "https://${ARTIFACT_REPO_TOKEN}@github.com/${ARTIFACT_REPO}.git" "$WORKDIR" + + echo "🚚 Syncing generated Maven artifacts..." + mkdir -p "$WORKDIR/org/wysaid/gpuimage-plus" + rsync -av --delete "${SOURCE_REPO}/org/wysaid/gpuimage-plus/" "$WORKDIR/org/wysaid/gpuimage-plus/" + + echo "🧭 Regenerating index pages" + pushd "$WORKDIR" + chmod +x genSites.sh + ./genSites.sh + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git checkout -b "$BRANCH" + git add org/wysaid/gpuimage-plus || true + + echo "artifact_pr_url=" >> $GITHUB_OUTPUT + + if git diff --cached --quiet; then + echo "ℹ️ No changes to publish; skipping PR." + exit 0 + fi + + git commit -m "Publish artifacts ${TAG_NAME}" + git push origin "$BRANCH" + + echo "📝 Creating pull request..." + PR_BODY="Automated artifact sync for ${TAG_NAME}.\n\nGenerated by main repo release workflow." + API_JSON=$(jq -n --arg title "Publish ${TAG_NAME} artifacts" \ + --arg head "$BRANCH" \ + --arg base "master" \ + --arg body "$PR_BODY" \ + '{title:$title, head:$head, base:$base, body:$body}') + + PR_RESPONSE=$(curl -sS -X POST \ + -H "Authorization: token ${ARTIFACT_REPO_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + -d "$API_JSON" \ + "https://api.github.com/repos/${ARTIFACT_REPO}/pulls") + + echo "PR response: $PR_RESPONSE" + PR_URL=$(echo "$PR_RESPONSE" | jq -r '.html_url') + if [[ "$PR_URL" == "null" || -z "$PR_URL" ]]; then + echo "❌ Failed to create PR" + exit 1 + fi + + echo "✅ PR created: $PR_URL" + echo "artifact_pr_url=$PR_URL" >> $GITHUB_OUTPUT + popd + + - name: Generate release notes + id: release_notes + run: | + VERSION="${{ steps.tag_version.outputs.version }}" + TAG_NAME="${{ steps.tag_version.outputs.tag_name }}" + cat > /tmp/release_notes.md << EOF + ## 🚀 Android GPUImage Plus $TAG_NAME + + ### 📦 Downloads + + **Demo APK:** + - \`cgeDemo-$VERSION.apk\` - Demo application with full video features + + **AAR Library Artifacts:** + + Four variants are available for different use cases: + + 1. **gpuimage-plus-$VERSION.aar** + - Page size: 4KB (default) + - Full-featured with FFmpeg bundled + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + 2. **gpuimage-plus-$VERSION-16k.aar** + - Page size: 16KB + - Full-featured with FFmpeg bundled + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + 3. **gpuimage-plus-$VERSION-min.aar** + - Page size: 4KB (default) + - Image-only version (no video features or FFmpeg) + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + 4. **gpuimage-plus-$VERSION-16k-min.aar** + - Page size: 16KB + - Image-only version (no video features or FFmpeg) + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + ### 🔧 Gradle Dependency + + \`\`\`gradle + allprojects { + repositories { + maven { + url 'https://maven.wysaid.org/' + } + } + } + + dependencies { + // Choose one of the following based on your needs: + + // Full-featured with FFmpeg (4KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION' + + // Full-featured with FFmpeg (16KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION-16k' + + // Image-only, no video (4KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION-min' + + // Image-only, no video (16KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION-16k-min' + } + \`\`\` + + ### 📋 System Requirements + + - **Minimum Android SDK**: API 21 (Android 5.0) + - **Target Android SDK**: API 25 + - **NDK Version**: r26d + - **Supported Architectures**: armeabi-v7a, arm64-v8a, x86, x86_64 + + ### 📚 Documentation + + - **GitHub Repository**: https://github.com/wysaid/android-gpuimage-plus + - **Wiki**: https://github.com/wysaid/android-gpuimage-plus/wiki + - **Filter Rule Documentation**: https://github.com/wysaid/android-gpuimage-plus/wiki/Parsing-String-Rule-(EN) + + --- + + For complete changelog, see the auto-generated content below. + EOF + + echo "✅ Release notes generated" + + - name: Create GitHub Release + if: steps.tag_version.outputs.is_manual != 'true' uses: softprops/action-gh-release@v2 with: - tag_name: ${{ env.RELEASE_TAG }} - name: ${{ env.RELEASE_TAG }} - files: artifacts/*.apk + tag_name: ${{ steps.tag_version.outputs.tag_name }} + name: Release ${{ steps.tag_version.outputs.tag_name }} + body_path: /tmp/release_notes.md + files: | + /tmp/release-artifacts/*.apk + /tmp/release-artifacts/*.aar + draft: false + prerelease: ${{ steps.tag_version.outputs.is_prerelease == 'true' }} + generate_release_notes: true + make_latest: ${{ steps.tag_version.outputs.is_prerelease != 'true' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload artifacts (Manual trigger) + if: steps.tag_version.outputs.is_manual == 'true' + uses: actions/upload-artifact@v4 + with: + name: release-artifacts-${{ steps.tag_version.outputs.version }} + path: /tmp/release-artifacts/ + retention-days: 7 + + - name: Manual trigger success message + if: steps.tag_version.outputs.is_manual == 'true' + run: | + echo "🎉 ============================================" + echo "🎉 Manual build completed successfully!" + echo "🎉 ============================================" + echo "" + echo "📦 Version: ${{ steps.tag_version.outputs.version }}" + echo "📦 Artifacts are available for download from the workflow run." + echo "" + echo "⚠️ Note: This is a manual trigger build." + echo "⚠️ Artifacts are NOT published to the Release page." + echo "⚠️ To create an official release, push a tag (e.g., git tag v${{ steps.tag_version.outputs.version }} && git push origin v${{ steps.tag_version.outputs.version }})" + echo "" + echo "📦 Built artifacts:" + ls -lh /tmp/release-artifacts/ + + - name: Summary + run: | + IS_MANUAL="${{ steps.tag_version.outputs.is_manual }}" + VERSION="${{ steps.tag_version.outputs.version }}" + TAG_NAME="${{ steps.tag_version.outputs.tag_name }}" + + if [ "$IS_MANUAL" = "true" ]; then + echo "## 🔧 Manual Build Completed Successfully!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version**: $VERSION" >> $GITHUB_STEP_SUMMARY + echo "**Trigger**: Manual (workflow_dispatch)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ⚠️ Note" >> $GITHUB_STEP_SUMMARY + echo "This is a **manual trigger build**. Artifacts are **NOT published** to the Release page." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "To create an official release, push a tag:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "git tag v$VERSION && git push origin v$VERSION" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Built Artifacts (available for download):" >> $GITHUB_STEP_SUMMARY + else + echo "## 🎉 Release Created Successfully!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Release**: $TAG_NAME" >> $GITHUB_STEP_SUMMARY + echo "**Version**: $VERSION" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ✅ Version Validation" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Tag format validated: v\${major}.\${minor}.\${patch}" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Version matches project version in build.gradle" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Released Artifacts:" >> $GITHUB_STEP_SUMMARY + fi + + echo "**Demo APK:**" >> $GITHUB_STEP_SUMMARY + echo "- cgeDemo-$VERSION.apk" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**AAR Libraries:**" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION.aar (4KB, with video)" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION-16k.aar (16KB, with video)" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION-min.aar (4KB, image-only)" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION-16k-min.aar (16KB, image-only)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$IS_MANUAL" != "true" ]; then + echo "Release page: ${{ github.server_url }}/${{ github.repository }}/releases/tag/$TAG_NAME" >> $GITHUB_STEP_SUMMARY + fi + + ARTIFACT_PR_URL="${{ steps.sync_maven_repo.outputs.artifact_pr_url }}" + if [ -n "$ARTIFACT_PR_URL" ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📮 Artifact Repository PR" >> $GITHUB_STEP_SUMMARY + echo "- $ARTIFACT_PR_URL" >> $GITHUB_STEP_SUMMARY + fi + + # Add build time if available + BUILD_TIME="${{ steps.build_variants.outputs.build_time }}" + if [ -n "$BUILD_TIME" ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ⏱️ Build Performance" >> $GITHUB_STEP_SUMMARY + echo "- Total build time: $((BUILD_TIME / 60)) minutes $((BUILD_TIME % 60)) seconds" >> $GITHUB_STEP_SUMMARY + fi + + - name: Build failure notification + if: failure() + run: | + echo "::error::Release build failed for version ${{ steps.tag_version.outputs.version }}" + echo "" >> $GITHUB_STEP_SUMMARY + echo "## ❌ Build Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version**: ${{ steps.tag_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "**Tag**: ${{ steps.tag_version.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🔍 Common Issues" >> $GITHUB_STEP_SUMMARY + echo "- Version mismatch between tag and build.gradle" >> $GITHUB_STEP_SUMMARY + echo "- NDK/CMake configuration issues" >> $GITHUB_STEP_SUMMARY + echo "- Missing dependencies or build tools" >> $GITHUB_STEP_SUMMARY + echo "- Network issues during artifact sync" >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index 4ed9db89..45dd081a 100644 --- a/README.md +++ b/README.md @@ -49,189 +49,128 @@ dependencies { ## Build -* Important options in `local.properties`: - * `usingCMakeCompile=true`: Set to true to compile the native library with CMake. Defaults to false, allowing the use of prebuilt libraries. - * `usingCMakeCompileDebug=true`: Set to true to compile the native library in Debug Mode. Defaults to false. - * `disableVideoModule=true`: Set to true to disable the video recording feature, which is useful for image-only scenarios. This reduces the size of the native library significantly. Defaults to false. - * `enable16kPageSizes=true`: Set to true to enable [16k page sizes](https://developer.android.com/guide/practices/page-sizes#16-kb-impact) for the native library, applicable only in CMake compile mode. Defaults to false. - -* Build with `Android Studio` and CMake: (Recommended) - * Put `usingCMakeCompile=true` in your `local.properties` - * Open the repo with the latest version of `Android Studio` - * Waiting for the initialization. (NDK/cmake install) - * Done. - -* Using `Visual Studio Code`: (Requires __[WSL(Recommended)](https://learn.microsoft.com/en-us/windows/wsl/install)__/__[MinGW](https://osdn.net/projects/mingw/)__/__[Cygwin](https://www.cygwin.com/)__ on Windows.) - * Setup ENV variable `ANDROID_HOME` to your Android SDK installation directory. - * Open the repo with `Visual Studio Code` - * Press `⌘ + shift + B` (Mac) or `ctrl + shift + B` (Win/Linux), choose the option `Enable CMake And Build Project With CMake`. - * Done. - -* Build with preset tasks: (Requires __[WSL(Recommended)](https://learn.microsoft.com/en-us/windows/wsl/install)__/__[MinGW](https://osdn.net/projects/mingw/)__/__[Cygwin](https://www.cygwin.com/)__ on Windows.) - - ```shell - # define the environment variable "ANDROID_HOME" - # If using Windows, define ANDROID_HOME in Windows Environment Settings by yourself. - export ANDROID_HOME=/path/to/android/sdk - - # Setup Project - bash tasks.sh --setup-project - - # Compile with CMake Debug - bash tasks.sh --debug --enable-cmake --build - # Compile with CMake Release - bash tasks.sh --release --enable-cmake --build - - # Start Demo By Command - bash tasks.sh --run - ``` - -* Build `JNI` part with ndk-build: (Not recommended) - - ```shell - export NDK=path/of/your/ndk - cd folder/of/jni (android-gpuimage-plus/library/src/main/jni) - - # Enable 16kb page sizes (optional) - export CGE_ENABLE_16KB_PAGE_SIZE=1 - - #This will make all arch: armeabi, armeabi-v7a arm64-v8a, x86, mips - ./buildJNI - #Or use "sh buildJNI" - - #Try this if you failed to run the shell above - export CGE_USE_VIDEO_MODULE=1 - $NDK/ndk-build - - #If you don't want anything except the image filter, - #Do as below to build with only cge module - #No ffmpeg, opencv or faceTracker. - #And remove the loading part of ffmpeg&facetracker - $NDK/ndk-build - - #For Windows user, you should include the `.cmd` extension to `ndk-build` like this: - cd \library\src\main\jni - \ndk-build.cmd - - #Also remember to comment out these line in NativeLibraryLoader - //System.loadLibrary("ffmpeg"); - //CGEFFmpegNativeLibrary.avRegisterAll(); - ``` - -> You can find precompiled libs here: [android-gpuimage-plus-libs](https://github.com/wysaid/android-gpuimage-plus-libs) (The precompiled '.so' files are generated with NDK-r23b) - -Note that the generated file "libFaceTracker.so" is not necessary. So just remove this file if you don't want any feature of it. - -* iOS version: [https://github.com/wysaid/ios-gpuimage-plus](https://github.com/wysaid/ios-gpuimage-plus "http://wysaid.org") +Quick start with Android Studio: + +1. Add `usingCMakeCompile=true` to `local.properties` +2. Open the project in Android Studio +3. Wait for NDK/CMake initialization +4. Build and run + +For detailed build instructions, configuration options, and alternative build methods (VS Code, command line, ndk-build), see [docs/build.md](docs/build.md). + +> Precompiled libraries: [android-gpuimage-plus-libs](https://github.com/wysaid/android-gpuimage-plus-libs) +> iOS version: [ios-gpuimage-plus](https://github.com/wysaid/ios-gpuimage-plus) ## Manual ### 1. Usage -___Sample Code for doing a filter with Bitmap___ +Apply a filter with a rule string: ```java -//Simply apply a filter to a Bitmap. -@Override -protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - Bitmap srcImage = ...; - - //HSL Adjust (hue: 0.02, saturation: -0.31, luminance: -0.17) - //Please see the manual for more details. - String ruleString = "@adjust hsl 0.02 -0.31 -0.17"; +Bitmap srcImage = ...; - Bitmap dstImage = CGENativeLibrary.filterImage_MultipleEffects(src, ruleString, 1.0f); +// HSL Adjust (hue: 0.02, saturation: -0.31, luminance: -0.17) +String ruleString = "@adjust hsl 0.02 -0.31 -0.17"; - //Then the dstImage is applied with the filter. +Bitmap dstImage = CGENativeLibrary.filterImage_MultipleEffects(srcImage, ruleString, 1.0f); - //Save the result image to /sdcard/libCGE/rec_???.jpg. - ImageUtil.saveBitmap(dstImage); -} +// Save result +ImageUtil.saveBitmap(dstImage); ``` ### 2. Custom Shader Filter -#### 2.1 Write your own filter +#### 2.1 Writing a Filter ->Your filter must inherit [CGEImageFilterInterfaceAbstract](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/jni/include/cgeImageFilter.h#L42) or its child class. Most of the filters are inherited from [CGEImageFilterInterface](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/jni/include/cgeImageFilter.h#L57) because it has many useful functions. +Custom filters inherit from [`CGEImageFilterInterface`](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/jni/include/cgeImageFilter.h#L57): ```cpp -// A simple customized filter to do a color reversal. +// A simple color reversal filter class MyCustomFilter : public CGE::CGEImageFilterInterface { public: - bool init() { CGEConstString fragmentShaderString = CGE_SHADER_STRING_PRECISION_H ( - varying vec2 textureCoordinate; //defined in 'g_vshDefaultWithoutTexCoord' - uniform sampler2D inputImageTexture; // the same to above. + varying vec2 textureCoordinate; + uniform sampler2D inputImageTexture; void main() { vec4 src = texture2D(inputImageTexture, textureCoordinate); - src.rgb = 1.0 - src.rgb; //Simply reverse all channels. + src.rgb = 1.0 - src.rgb; // Reverse all channels gl_FragColor = src; } ); - //m_program is defined in 'CGEImageFilterInterface' - return m_program.initWithShaderStrings(g_vshDefaultWithoutTexCoord, s_fsh); + return m_program.initWithShaderStrings(g_vshDefaultWithoutTexCoord, fragmentShaderString); } - - //void render2Texture(CGE::CGEImageHandlerInterface* handler, GLuint srcTexture, GLuint vertexBufferID) - //{ - // //Your own render functions here. - // //Do not override this function to use the CGEImageFilterInterface's. - //} }; ``` ->Note: To add your own shader filter with c++. [Please see the demo for further details](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/jni/source/customFilter_N.cpp). +> See [customFilter_N.cpp](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/jni/source/customFilter_N.cpp) for a complete example. -#### 2.2 Run your own filter +#### 2.2 Running a Custom Filter -__In C++, you can use a CGEImageHandler to do that:__ +__In C++:__ ```cpp -//Assume the gl context already exists: -//JNIEnv* env = ...; -//jobject bitmap = ...; +// Assumes GL context exists CGEImageHandlerAndroid handler; -CustomFilterType* customFilter = new CustomFilterType(); +auto* customFilter = new MyCustomFilter(); -//You should handle the return value (false is returned when failed.) customFilter->init(); handler.initWithBitmap(env, bitmap); +handler.addImageFilter(customFilter); // Handler takes ownership -//The customFilter will be released when the handler' destructor is called. -//So you don't have to call 'delete customFilter' if you add it into the handler. -handler.addImageFilter(customFilter); - -handler.processingFilters(); //Run the filters. - +handler.processingFilters(); jobject resultBitmap = handler.getResultBitmap(env); ``` ->If no gl context exists, the class [CGESharedGLContext](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/jni/interface/cgeSharedGLContext.h#L22) may be helpful. +> If no GL context exists, use [`CGESharedGLContext`](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/jni/interface/cgeSharedGLContext.h#L22). -__In Java, you can simply follow the sample:__ +__In Java:__ -See: [CGENativeLibrary.cgeFilterImageWithCustomFilter](https://github.com/wysaid/android-gpuimage-plus/blob/master/cgeDemo/src/main/java/org/wysaid/cgeDemo/TestCaseActivity.java#L123) +Use [`CGENativeLibrary.cgeFilterImageWithCustomFilter`](https://github.com/wysaid/android-gpuimage-plus/blob/master/cgeDemo/src/main/java/org/wysaid/cgeDemo/TestCaseActivity.java#L123) or [`CGEImageHandler`](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/java/org/wysaid/nativePort/CGEImageHandler.java#L93) directly. -__Or to do with a [CGEImageHandler](https://github.com/wysaid/android-gpuimage-plus/blob/master/library/src/main/java/org/wysaid/nativePort/CGEImageHandler.java#L93)__ +### 3. Filter Rule Strings -### 3. Filter Rule String ### +Filter effects are defined by text-based rule strings. See [docs/filter-rules.md](docs/filter-rules.md) for complete syntax reference. -Doc: +Quick example: + +```java +// Chain multiple filters with @ separator +String filter = "@curve RGB (0,0) (128,150) (255,255) " + + "@adjust hsl 0.02 -0.31 -0.17 " + + "@blend overlay texture.jpg 80"; +``` + +### 4. Key Classes & Threading + +| Class | Purpose | +|-------|---------| +| `CGENativeLibrary` | Main entry — static filter methods | +| `CGEImageHandler` | Image handler wrapper | +| `CGEFrameRenderer` | Real-time camera/video renderer | +| `CGEFrameRecorder` | Video recording (requires FFmpeg) | + +**Important**: GL operations must run on the GL thread: + +```java +glSurfaceView.queueEvent(() -> { + // Your GL operations here +}); +``` -En: [https://github.com/wysaid/android-gpuimage-plus/wiki/Parsing-String-Rule-(EN)](https://github.com/wysaid/android-gpuimage-plus/wiki/Parsing-String-Rule-(EN) "http://wysaid.org") +### Documentation -Ch: [https://github.com/wysaid/android-gpuimage-plus/wiki/Parsing-String-Rule-(ZH)](https://github.com/wysaid/android-gpuimage-plus/wiki/Parsing-String-Rule-(ZH) "http://wysaid.org") +- [docs/build.md](docs/build.md) — Full build guide and configuration options +- [docs/filter-rules.md](docs/filter-rules.md) — Complete filter syntax reference +- [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) — Contributing guidelines +- [.github/RELEASE.md](.github/RELEASE.md) — Release process ## Tool diff --git a/docs/build.md b/docs/build.md new file mode 100644 index 00000000..d1521faa --- /dev/null +++ b/docs/build.md @@ -0,0 +1,102 @@ +# Build Guide + +## Build Configuration + +All build options are controlled via `local.properties` in the project root: + +| Property | Default | Effect | +|----------|---------|--------| +| `usingCMakeCompile` | `false` | `true` = build native code with CMake; `false` = use prebuilt `.so` from `library/src/main/libs/` | +| `usingCMakeCompileDebug` | `false` | `true` = Debug native build; `false` = Release | +| `disableVideoModule` | `false` | `true` = exclude FFmpeg and video recording features | +| `enable16kPageSizes` | `false` | `true` = enable [16KB page-size](https://developer.android.com/guide/practices/page-sizes#16-kb-impact) linking and use `ffmpeg-16kb` | +| `deployArtifacts` | `false` | `true` = enable Maven publishing tasks | + +## Build Methods + +### Android Studio (Recommended) + +1. Set `usingCMakeCompile=true` in `local.properties` +2. Open the project in Android Studio (latest version) +3. Wait for NDK/CMake initialization +4. Build and run + +### Visual Studio Code + +> Requires [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) (recommended), [MinGW](https://osdn.net/projects/mingw/), or [Cygwin](https://www.cygwin.com/) on Windows. + +1. Set environment variable `ANDROID_HOME` to your Android SDK directory +2. Open the project in VS Code +3. Press `⌘ + Shift + B` (Mac) or `Ctrl + Shift + B` (Win/Linux) +4. Choose "Enable CMake And Build Project With CMake" + +### Command Line (tasks.sh) + +```bash +# Set Android SDK path (Linux/Mac) +export ANDROID_HOME=/path/to/android/sdk + +# Setup project +bash tasks.sh --setup-project + +# Build (Debug, CMake) +bash tasks.sh --debug --enable-cmake --build + +# Build (Release, CMake) +bash tasks.sh --release --enable-cmake --build + +# Run demo +bash tasks.sh --run + +# Build without video module +bash tasks.sh --enable-cmake --disable-video-module --build + +# Enable 16KB page size +bash tasks.sh --enable-cmake --enable-16kb-page-size --build +``` + +Run `bash tasks.sh --help` for all available options. + +### ndk-build (Legacy) + +> **Not recommended.** Use CMake instead. This method is kept for backward compatibility. + +```bash +export NDK=/path/to/ndk +cd library/src/main/jni + +# Build all architectures with video module +export CGE_USE_VIDEO_MODULE=1 +./buildJNI + +# Build without video module +unset CGE_USE_VIDEO_MODULE +$NDK/ndk-build + +# Enable 16KB page sizes (optional) +export CGE_ENABLE_16KB_PAGE_SIZE=1 +./buildJNI +``` + +> Precompiled libs (NDK-r23b): [android-gpuimage-plus-libs](https://github.com/wysaid/android-gpuimage-plus-libs) + +## Dual Build System + +This project supports both CMake and ndk-build: + +- **CMake** (`CMakeLists.txt`): Uses `GLOB_RECURSE` — new source files are picked up automatically. +- **ndk-build** (`Android.mk`): Lists source files **explicitly** — you must update it manually when adding files. + +When adding new native source files, always ensure both build systems are updated. + +## Publish AAR to Maven Local + +```bash +# Full-featured +bash tasks.sh --enable-cmake --enable-video-module --publish + +# Image-only (no FFmpeg) +bash tasks.sh --enable-cmake --disable-video-module --publish +``` + +This publishes to the local Maven repository configured by `localRepoDir` in `local.properties`. diff --git a/docs/filter-rules.md b/docs/filter-rules.md new file mode 100644 index 00000000..1b032c15 --- /dev/null +++ b/docs/filter-rules.md @@ -0,0 +1,437 @@ +# Filter Rule String Reference + +Complete guide to CGE filter effect rule strings. Use the [cge-tools editor](https://github.com/wysaid/cge-tools) to generate complex filter configurations visually. + +## Rule String Syntax + +Each filter step starts with `@` followed by the filter name and parameters. Multiple filters can be chained: + +``` +@method1 @method2 @method3 +``` + +Whitespace between `@` and method name is optional: both `@method` and `@ method` are valid. + +### Special Directives + +#### `#unpack` (Advanced) + +Add `#unpack` at the start to bypass the `MultipleEffects` wrapper and add filters directly to the handler: + +``` +#unpack @adjust brightness 0.5 +``` + +## Filter Methods + +### 1. curve — Color Curve Adjustment + +Apply color curves to RGB channels individually or combined. + +**Format**: `@curve [ ]...` + +**Channels**: +- `RGB` — Apply to all channels +- `R` / `G` / `B` — Individual red, green, blue channels + +**Points**: `(x1, y1) (x2, y2) ... (xn, yn)` +- Range: `[0, 255]` for both x and y +- Minimum 2 points required +- Parentheses and coordinate separators (space, comma) are flexible + +**Examples**: + +``` +① RGB curve with 3 control points: +@curve RGB (0, 0) (100, 200) (255, 255) + +② Multiple channel curves: +@curve R (0, 0) (50, 25) (255, 255) G (0, 0) (100, 150) (255, 255) RGB (0, 0) (200, 150) (255, 255) + +③ Merging consecutive curves (more efficient): +@curve RGB (0, 0) (100, 200) (255, 255) R (0, 0) (50, 25) (255, 255) G (0, 0) (100, 150) (255, 255) +``` + +**Performance Tip**: When applying multiple curves consecutively, merge them into one `@curve` command to reduce overhead. + +--- + +### 2. blend — Texture Blending + +Blend an image texture with various blend modes. The texture is scaled to fit the destination image. + +#### 2.1 Normal Blend + +**Format**: `@blend ` + +**Blend Modes** (with abbreviations in brackets): + +| Category | Modes | +|----------|-------| +| **Basic** | `mix`, `dissolve` `[dsv]` | +| **Darken** | `darken` `[dk]`, `multiply` `[mp]`, `colorburn` `[cb]`, `linearburn` `[lb]`, `darkercolor` `[dc]` | +| **Lighten** | `lighten` `[lt]`, `screen` `[sr]`, `colordodge` `[cd]`, `lineardodge` `[ld]`, `lightercolor` `[lc]` | +| **Contrast** | `overlay` `[ol]`, `softlight` `[sl]`, `hardlight` `[hl]`, `vividlight` `[vvl]`, `linearlight` `[ll]`, `pinlight` `[pl]`, `hardmix` `[hm]` | +| **Difference** | `difference` `[dif]`, `exclude` `[ec]`, `subtract` `[sub]`, `divide` `[div]` | +| **Component** | `hue`, `saturation` `[sat]`, `color` `[cl]`, `luminosity` `[lum]` | +| **Special** | `add`, `addrev`, `colorbw` | + +See [Photoshop Blend Modes](https://en.wikipedia.org/wiki/Blend_modes) for detailed mode descriptions. + +**Parameters**: +- ``: Image filename with extension (e.g., `"image.jpg"`) + - **Advanced**: Use `[texID,width,height]` for direct texture ID (e.g., `[10,1024,768]`). ⚠️ Use with caution — incorrect ID can break the app. +- ``: Blend strength, range `(0, 100]` + +**Example**: + +``` +80% overlay blend with "src.jpg": +@blend overlay src.jpg 80 +@blend ol src.jpg 80 // Abbreviation works too +``` + +#### 2.2 krblend — Aspect-Ratio Preserving Blend + +Same as `blend` but maintains the texture's aspect ratio (like AspectFill). + +**Format**: `@krblend ` + +#### 2.3 pixblend — Solid Color Blend + +Blend with a solid color instead of a texture. + +**Format**: `@pixblend ` + +**Color Parameters**: +- Range: `[0, 1]` or `[0, 255]` +- If alpha > 1.0, all values are interpreted as `[0, 255]` + +**Example**: + +``` +90% overlay with pure red: +@pixblend overlay 1 0 0 1 90 +@pixblend overlay 255 0 0 255 90 // Equivalent +``` + +#### 2.4 selfblend — Self Blend + +Blend the image with itself (no texture parameter). + +**Format**: `@selfblend ` + +#### 2.5 vigblend — Vignette Blend + +Blend with color using a vignette mask. + +**Format**: `@vigblend [kind]` + +**Vignette Parameters**: +- ``, ``: Control vignette falloff +- ``, ``: Vignette center (typically `0.5` for image center) +- `[kind]`: Optional vignette mode (`0`-`3`) + - `0`: Linear, no alpha (default) + - `1`: Linear with alpha blending + - `2`: Bilinear, no alpha + - `3`: Bilinear with alpha + +**Example**: + +``` +@vigblend overlay 0 0 0 1 50 0.02 0.45 0.5 0.5 0 +@vigblend mix 10 10 30 255 100 0 1.5 0.5 0.5 1 +``` + +--- + +### 3. adjust — Image Adjustments + +Apply common image adjustments. + +**Format**: `@adjust ` + +| Function | Parameters | Range | Description | +|----------|------------|-------|-------------| +| **brightness** | `intensity` | `[-1, 1]` | Brightness adjustment | +| **contrast** | `intensity` | `> 0` | `< 1`: lower, `= 1`: none, `> 1`: higher | +| **saturation** | `intensity` | `≥ 0` | `0`: monochrome, `1`: none, `> 1`: higher | +| **monochrome** | `red green blue cyan magenta yellow` | `[-2, 3]` | Black & white adjustment (Photoshop-style) | +| **sharpen** | `intensity` | `[0, 10]` | Sharpness (`0` = no effect) | +| **blur** | `intensity` | `[0, 10]` | Blur (`0` = no effect, not recommended — use `@blur` instead) | +| **whitebalance** | `temperature tint` | `[-1, 1]`, `[0, 5]` | `temperature` `0` = none, `tint` `1` = none | +| **shadowhighlight** `[shl]` | `shadow highlight` | `[-200, 100]`, `[-100, 200]` | `0` = no effect | +| **hsv** | `red green blue magenta yellow cyan` | `[-1, 1]` | HSV adjustment per hue | +| **hsl** | `hue saturation luminance` | `[-1, 1]` | HSL adjustment | +| **level** | `dark light gamma` | `[0, 1]`, `dark < light` | Levels adjustment | +| **exposure** | `intensity` | `[-2, 2]` | Exposure adjustment | +| **colorbalance** | `redShift greenShift blueShift` | `[-1, 1]` | Color balance | +| **lut** | `filename` | — | Lookup table (512×512 PNG) | + +**Examples**: + +``` +@adjust brightness 0.5 +@adjust hsl -0.66 0.34 0.15 +@adjust lut lookup.png // Requires 512×512 LUT image +``` + +--- + +### 4. vignette — Vignette Effect + +Add a radial vignette to darken image edges. + +**Format**: `@vignette [ ]` + +**Parameters**: +- ``, ``: Control vignette intensity and falloff +- ``, ``: Optional center (default `0.5 0.5`) + +**Examples**: + +``` +@vignette 0.1 0.9 +@vignette 0.1 0.9 0.5 0.5 +``` + +--- + +### 5. colormul — Color Multiplication + +Directly multiply pixel color values. + +**Format**: `@colormul ` + +#### Functions: + +**① `flt` — Scalar Multiplication** + +Multiply all RGB channels by a scalar. + +``` +@colormul flt 1.5 // Brighten by 1.5× +Result: dst.rgb = src.rgb * 1.5 +``` + +**② `vec` — Vector Multiplication** + +Multiply each channel independently. + +``` +@colormul vec 0.2 1.5 0.3 +Result: dst.rgb = src.rgb * vec3(0.2, 1.5, 0.3) + +// Pure red filter (faster than curve): +@colormul vec 1 0 0 +``` + +**③ `mat` — Matrix Multiplication** + +Multiply by a 3×3 color matrix (9 parameters, column-major). + +``` +// Monochrome (standard luminance weights): +@colormul mat 0.299 0.587 0.114 0.299 0.587 0.114 0.299 0.587 0.114 + +// Swap red and green channels: +@colormul mat 0 1 0 1 0 0 0 0 1 +``` + +--- + +### 6. selcolor — Selective Color + +Adjust specific color ranges (like Photoshop's Selective Color). + +**Format**: `@selcolor ( ) [ ( )]...` + +**Colors**: `red`, `green`, `blue`, `cyan`, `magenta`, `yellow`, `white`, `gray`, `black` + +**CMYK Shifts**: Range `[-1, 1]` for cyan, magenta, yellow, black adjustments + +**Example**: + +``` +@selcolor red (0, -0.29, 0, 0) green (0, 0, 0.07, 0) blue (0, 0.39, 0, 0) yellow (-0.39, 0.39, 0.39, -0.39) white (0, 0, 0.24, 0) +``` + +--- + +### 7. style — Artistic Styles + +Apply artistic image effects. + +**Format**: `@style ` + +| Function | Parameters | Description | +|----------|------------|-------------| +| **crosshatch** | `spacing` `[0, 0.1]`, `lineWidth` `(0, 0.01]` | Cross-hatching pattern | +| **edge** | `mix` `[0, 1]`, `stride` `(0, 5]` | Edge detection (Sobel) | +| **emboss** | `mix` `[0, 1]`, `stride` `[1, 5]`, `angle` `[0, 2π]` | Emboss effect | +| **halftone** | `dotSize` `≥ 1` | Halftone dot pattern | +| **haze** | `distance` `[-0.5, 0.5]`, `slope` `[-0.5, 0.5]`, `r g b` `[0, 1]` | Haze/fog effect | +| **polkadot** | `dotScaling` `(0, 1]` | Polka dot pattern | +| **sketch** | `intensity` `[0, 1]` | Pencil sketch effect | +| **max** | — | Maximum filter | +| **min** | — | Minimum filter | +| **mid** | — | Median filter | + +**Examples**: + +``` +@style crosshatch 0.03 0.003 +@style edge 0.8 2.0 +@style emboss 1.0 1.5 1.57 +@style sketch 0.7 +``` + +--- + +### 8. beautify — Beauty Filters + +Skin smoothing and beautification effects. + +**Format**: `@beautify ` + +| Function | Parameters | Description | +|----------|------------|-------------| +| **bilateral** | `blurScale` `[-100, 100]`, `distanceFactor` `[1, 20]`, `[repeat]` `≥ 1` | Bilateral filter (edge-preserving blur) | +| **face** | `intensity` `(0, 1]`, `[width]`, `[height]` | Face beautification (defaults: 720×1280) | + +**Examples**: + +``` +@beautify bilateral 5 10 2 // radius=5, tolerance=10, repeat 2 times +@beautify face 1.0 720 1280 +@beautify face 0.8 // Auto dimensions +``` + +--- + +### 9. blur — Advanced Blur + +Specialized blur algorithms (better than `@adjust blur`). + +**Format**: `@blur ` + +| Function | Parameters | Description | +|----------|------------|-------------| +| **lerp** | `level` `[0, 1]`, `base` `[0.6, 2.0]` | Lerp blur algorithm | + +**Example**: + +``` +@blur lerp 0.7 1.2 +``` + +--- + +### 10. dynamic — Animated Effects + +Dynamic filters with motion over time (not supported in static editors). + +**Format**: `@dynamic ` + +| Function | Parameters | Description | +|----------|------------|-------------| +| **wave** | `autoMotionSpeed` | Auto-animated wave ripple | +| | `motion angle strength [autoMotionSpeed]` | Manual wave parameters | +| **motionFlow** `[mf]` | `totalFrames frameDelay` | Motion trail effect | + +**Examples**: + +``` +@dynamic wave 0.5 // Auto motion with speed 0.5 +@dynamic wave 0.0 1.57 5.0 // Static wave: motion=0, angle=π/2, strength=5 +@dynamic wave 0.0 1.57 5.0 0.3 // Animated with custom params +@dynamic motionFlow 10 2 // 10 frames with 2-frame delay +``` + +--- + +### 11. lomo — Lomo Effect + +Lomography-style effect combining vignette and color grading. + +**Format**: `@lomo ` + +**Parameters**: +- ``, ``: Vignette parameters (typically `< 1`) +- ``, ``: Color scale adjustments (typically `< 1`) +- ``: Saturation multiplier `[0, 1]` +- ``: `0` or `1` for vignette type + +**Example**: + +``` +@lomo 0.2 0.8 0.1 0.2 1.0 0 +``` + +--- + +### 12. cvlomo — Lomo with Curve + +Lomo effect with custom curve adjustment. + +**Format**: `@cvlomo ` + +**Note**: `` is a curve definition **without** the `@curve` prefix. + +**Example**: + +``` +@cvlomo 0.2 0.8 0.1 0.2 1.0 RGB (0, 0) (255, 255) +``` + +--- + +### 13. colorscale — Color Scaling + +Apply color scaling adjustments (CPU-intensive, may affect performance). + +**Format**: `@colorscale ` + +--- + +### 14. special — Custom Effects + +Hardcoded special effects by index. + +**Format**: `@special ` + +``: Effect index number (implementation-specific) + +--- + +## Complete Example + +Combining multiple filters: + +``` +@curve RGB (0, 0) (128, 150) (255, 255) +@adjust hsl 0.02 -0.31 -0.17 +@blend overlay texture.jpg 80 +@vignette 0.1 0.9 +@beautify face 0.8 +``` + +## Notes + +- **Spaces**: Flexible between parameters and parentheses +- **Abbreviations**: Use short forms for faster typing (e.g., `ol` for `overlay`) +- **Case**: Channel names are case-insensitive (`R` = `r`, `RGB` = `rgb`) +- **Performance**: Merge consecutive `@curve` commands; prefer `@colormul vec` over curves for simple channel operations +- **Texture Loading**: Requires custom image-loading callback (see demo for implementation) +- **Tool**: Use [cge-tools](https://github.com/wysaid/cge-tools) for visual filter editing + +## Version History + +- **v0.2.1.x**: Methods 1-12 +- **v0.3.2.1**: Added `vigblend` +- **2014-11-12**: Added `selcolor` +- **2015-02-05**: Added `style` methods +- **2015-03-19**: Added `beautify` methods +- **2015-08-07**: Added `#unpack` directive +- **2015-08-08**: Added `@blur` methods +- **2015-11-18**: Added `@dynamic` methods From 2cb4ee9f3c5be60e9f04eb8e84eaf71687256543 Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 14 Feb 2026 00:40:51 +0800 Subject: [PATCH 2/7] ci: optimize Ubuntu workflow to reduce PR check count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PR: only run 2 critical configs (down from 16) - NDK-r27c + cmake + ffmpeg + 16kb (latest recommended) - NDK-r26d + ndk-build + no-ffmpeg + 4kb (legacy coverage) - master push: keep all 16 configs for full coverage - Total PR jobs: 35+ → 5 (Windows:1 + macOS:1 + Ubuntu:2 + Lint:1) --- .github/workflows/ubuntu-build.yml | 117 +---------------------------- 1 file changed, 1 insertion(+), 116 deletions(-) diff --git a/.github/workflows/ubuntu-build.yml b/.github/workflows/ubuntu-build.yml index 16ec1fc2..0e766cb0 100644 --- a/.github/workflows/ubuntu-build.yml +++ b/.github/workflows/ubuntu-build.yml @@ -19,122 +19,7 @@ jobs: strategy: fail-fast: false matrix: - include: - # ============================================================ - # Ubuntu: 16 jobs (2 NDK × 2 build-system × 2 ffmpeg × 2 page-size) - # ============================================================ - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: cmake - ffmpeg: with-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: cmake - ffmpeg: with-ffmpeg - page-size: 16kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: cmake - ffmpeg: no-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: cmake - ffmpeg: no-ffmpeg - page-size: 16kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: ndk-build - ffmpeg: with-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: ndk-build - ffmpeg: with-ffmpeg - page-size: 16kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: ndk-build - ffmpeg: no-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r26d - build-system: ndk-build - ffmpeg: no-ffmpeg - page-size: 16kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: cmake - ffmpeg: with-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: cmake - ffmpeg: with-ffmpeg - page-size: 16kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: cmake - ffmpeg: no-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: cmake - ffmpeg: no-ffmpeg - page-size: 16kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: ndk-build - ffmpeg: with-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: ndk-build - ffmpeg: with-ffmpeg - page-size: 16kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: ndk-build - ffmpeg: no-ffmpeg - page-size: 4kb - run-on: always - - os: ubuntu-latest - os-name: Ubuntu - ndk: r27c - build-system: ndk-build - ffmpeg: no-ffmpeg - page-size: 16kb - run-on: always + include: ${{ fromJson(github.event_name == 'pull_request' && '[{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"cmake","ffmpeg":"with-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"ndk-build","ffmpeg":"no-ffmpeg","page-size":"4kb"}]' || '[{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"cmake","ffmpeg":"with-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"cmake","ffmpeg":"with-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"cmake","ffmpeg":"no-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"cmake","ffmpeg":"no-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"ndk-build","ffmpeg":"with-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"ndk-build","ffmpeg":"with-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"ndk-build","ffmpeg":"no-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r26d","build-system":"ndk-build","ffmpeg":"no-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"cmake","ffmpeg":"with-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"cmake","ffmpeg":"with-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"cmake","ffmpeg":"no-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"cmake","ffmpeg":"no-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"ndk-build","ffmpeg":"with-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"ndk-build","ffmpeg":"with-ffmpeg","page-size":"16kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"ndk-build","ffmpeg":"no-ffmpeg","page-size":"4kb"},{"os":"ubuntu-latest","os-name":"Ubuntu","ndk":"r27c","build-system":"ndk-build","ffmpeg":"no-ffmpeg","page-size":"16kb"}]') }} steps: - name: Checkout code From 582c2e8850e3dc7ca98c7a69c25f45bdd512e871 Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 14 Feb 2026 01:19:25 +0800 Subject: [PATCH 3/7] refactor: change workflow trigger condition from *workflow* to test_workflow* - Update macOS build workflow trigger - Update Ubuntu build workflow trigger - Update Windows build workflow trigger This prevents accidental full-matrix builds on branches containing 'workflow' in the name. Only branches starting with 'test_workflow' will trigger the full test matrix. --- .github/workflows/macos-build.yml | 2 +- .github/workflows/ubuntu-build.yml | 2 +- .github/workflows/windows-build.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macos-build.yml b/.github/workflows/macos-build.yml index a863a386..b687441c 100644 --- a/.github/workflows/macos-build.yml +++ b/.github/workflows/macos-build.yml @@ -4,7 +4,7 @@ on: push: branches: - "master" - - "*workflow*" + - "test_workflow*" pull_request: branches: [ "master" ] diff --git a/.github/workflows/ubuntu-build.yml b/.github/workflows/ubuntu-build.yml index 0e766cb0..8734d569 100644 --- a/.github/workflows/ubuntu-build.yml +++ b/.github/workflows/ubuntu-build.yml @@ -4,7 +4,7 @@ on: push: branches: - "master" - - "*workflow*" + - "test_workflow*" pull_request: branches: [ "master" ] diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 9974da29..11a6dc6d 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -4,7 +4,7 @@ on: push: branches: - "master" - - "*workflow*" + - "test_workflow*" pull_request: branches: [ "master" ] From 7344caffbefb7a5e003badb94c2c0ceb4d62e85a Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 14 Feb 2026 01:33:35 +0800 Subject: [PATCH 4/7] better skills --- .github/skills/pr-review/SKILL.md | 70 ++++++++----------------------- .github/skills/pr-submit/SKILL.md | 57 ++++++------------------- 2 files changed, 30 insertions(+), 97 deletions(-) diff --git a/.github/skills/pr-review/SKILL.md b/.github/skills/pr-review/SKILL.md index 01055654..c943245e 100644 --- a/.github/skills/pr-review/SKILL.md +++ b/.github/skills/pr-review/SKILL.md @@ -1,67 +1,31 @@ --- name: pr-review -description: Review a GitHub Pull Request for API stability, build compliance, native code safety, and code quality +description: Address review comments from GitHub PR using gh CLI and resolve feedback --- -## When to Use +## When to use -- Need to review a pull request before merge -- Validate PR changes against project rules -- Provide structured review feedback +- Current branch has a PR with review comments requiring responses +- Need to systematically evaluate and resolve reviewer feedback ## Constraints -- All review comments **must** be in English -- Work through checks **in order** — stop and flag any blocker immediately -- Reference `.github/CONTRIBUTING.md` for detailed code standards +- **ALWAYS** set `PAGER=cat` before calling `gh` to avoid pagination issues +- Use `gh pr view --comments` to fetch review comments +- Use `gh pr list --head ` to find the PR number for the current branch ## Procedure -Follow `.github/CONTRIBUTING.md` for detailed rules. Check in order: - -1. **API Stability** (Blocker): - - No changes to `org.wysaid.nativePort.*` method signatures - - No changes to JNI function names - - Filter rule string syntax backward-compatible -2. **Build Toggle Compliance** (Blocker): - - FFmpeg/video code properly guarded (C++: `#ifdef CGE_USE_VIDEO_MODULE`, Java: `BuildConfig.CGE_USE_VIDEO_MODULE`) - - Compiles with `disableVideoModule=true` -3. **Native Code Safety** (High): - - No C++ exceptions, JNI null checks, GL resource cleanup, shader error handling -4. **Dual Build System** (Medium): - - New native files → `Android.mk` updated -5. **Code Quality**: Follow CONTRIBUTING.md standards -6. **CI Status**: All platform workflows pass +1. **Find Issues**: Use `gh` to locate the PR for current branch and retrieve all review comments +2. **Evaluate**: Analyze each comment in project context: + - If valid: implement the necessary code improvements + - If invalid: document reasons and address any underlying real issues +3. **Summarize**: Provide a summary covering: + - Valid points and how they were resolved + - Invalid points and reasons for rejection + - Any additional issues discovered and fixed +4. **Commit**: Generate commit message, then `git commit` and `git push` ## Output -Structure the review as: - -```markdown -## PR Review: - -### Summary -One-sentence assessment: Approve / Request Changes / Needs Discussion - -### Findings - -#### Blockers -- (API stability or build toggle violations) - -#### Issues -- (correctness, safety, or quality problems) - -#### Suggestions -- (optional improvements, not blocking) - -### Verdict -APPROVE / REQUEST_CHANGES with rationale -``` - -## Severity Guide - -| Severity | Criteria | Action | -|----------|----------|--------| -| **Blocker** | API break, build toggle violation, crash bug | Request changes | -| **Issue** | Memory/GL resource leak, missing null check, wrong thread | Request changes if risky, else comment | -| **Suggestion** | Style, naming, minor optimization | Comment only | +- Clear mapping between review comments and their resolutions \ No newline at end of file diff --git a/.github/skills/pr-submit/SKILL.md b/.github/skills/pr-submit/SKILL.md index 87e81359..fc6762b0 100644 --- a/.github/skills/pr-submit/SKILL.md +++ b/.github/skills/pr-submit/SKILL.md @@ -3,58 +3,27 @@ name: pr-submit description: Create or update a GitHub Pull Request after completing code changes --- -## When to Use +## When to use - Code changes are complete and ready for review -- Need to create a new PR or update an existing one +- Need to create a new PR or update an existing one with new commits ## Constraints -- PR title, description, and comments **must** be in English -- Follow conventional-commit format for titles: `feat:`, `fix:`, `refactor:`, `chore:` -- Target branch: `master` -- **Never** force-push to `master` -- Follow `.github/CONTRIBUTING.md` for code standards and compatibility rules +- **ALWAYS** set `PAGER=cat` before calling `gh` to avoid pagination issues ## Procedure -1. **Verify Build**: Run `bash tasks.sh --enable-cmake --build` - - If touching FFmpeg/video code: verify with `--disable-video-module` - - If new native files added: confirm `Android.mk` updated -2. **Check Compatibility**: Review `.github/CONTRIBUTING.md` compatibility rules - - Confirm no public API signature changes in `org.wysaid.nativePort.*` -3. **Branch & Commit**: - - Create feature branch if on `master` (`feat/`, `fix/`, `refactor/`) - - Follow conventional-commit format (see CONTRIBUTING.md) -4. **Create PR**: Push branch and create PR against `master` - - Use conventional-commit format for title - - Fill in description template below -5. **Verify CI**: Ensure all platform workflows pass - -## PR Description Template - -```markdown -## Summary - -Brief description of what this PR does and why. - -## Changes - -- Bullet list of key changes - -## Compatibility - -- [ ] No public API changes (`org.wysaid.nativePort.*`) -- [ ] No filter rule string syntax changes -- [ ] FFmpeg/video code guarded with `disableVideoModule` / `CGE_USE_VIDEO_MODULE` (or N/A) -- [ ] Both CMake and ndk-build updated (or N/A — no new native source files) - -## Testing - -How was this tested (e.g., "built with CMake + no-ffmpeg on macOS", "ran cgeDemo on Pixel 7"). -``` +1. **Verify**: Review conversation history and current changes—ensure completeness, correctness, no performance issues +2. **Clean**: Remove temporary files, debug artifacts, and unintended changes +3. **Commit & Push**: + - If on `master`, create a new feature branch (avoid name conflicts) + - Commit all changes and push to remote +4. **PR**: Use `gh` to create or update the Pull Request: + - Check if current branch has an existing PR + - **If exists**: Fetch title/description, incorporate new commits, update the PR + - **If not**: Create PR against remote `master`, draft title/description from changes ## Output -- PR with English title and description -- All CI checks passing +- Focus on: what changed, why, impact, verification \ No newline at end of file From 137cbd1514b155f3aa4108433f6e953485b05e7f Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 14 Feb 2026 01:52:56 +0800 Subject: [PATCH 5/7] fix: harden release workflow and align docs references --- .github/RELEASE.md | 2 +- .github/copilot-instructions.md | 2 +- .github/instructions/cpp.instructions.md | 2 +- .github/instructions/java.instructions.md | 2 +- .github/workflows/release.yml | 86 ++++++++++++++--------- 5 files changed, 58 insertions(+), 36 deletions(-) diff --git a/.github/RELEASE.md b/.github/RELEASE.md index f373e5d0..4e65067a 100644 --- a/.github/RELEASE.md +++ b/.github/RELEASE.md @@ -53,4 +53,4 @@ When a tag matching `v*.*.*` is pushed, the [release workflow](../.github/workfl - Alpha: `v3.1.1-alpha1` - Release candidate: `v3.1.1-rc1` -All version parts must be pure numbers. The tag version must exactly match `versionName` in `build.gradle`. +The core `X.Y.Z` segments must be numeric. Prerelease suffixes (for example `-beta1`, `-alpha1`, `-rc1`) are allowed, and the full tag version (including any suffix) must exactly match `versionName` in `build.gradle`. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e72f0fbb..9e032825 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -34,7 +34,7 @@ For detailed standards, see `.github/CONTRIBUTING.md`. ## Documentation - `docs/build.md` — Full build guide and configuration options -- `docs/usage.md` — API usage, custom filters, and code samples +- `README.md` — API usage, custom filters, and code samples - `.github/RELEASE.md` — Release process and versioning - `.github/CONTRIBUTING.md` — Contributing guidelines and code standards diff --git a/.github/instructions/cpp.instructions.md b/.github/instructions/cpp.instructions.md index ad7dd945..0e4aada0 100644 --- a/.github/instructions/cpp.instructions.md +++ b/.github/instructions/cpp.instructions.md @@ -1,6 +1,6 @@ --- description: "Use when editing C++ native code. Covers CGE engine conventions, shader patterns, memory safety, and OpenGL ES resource management." -applyTo: "library/src/main/jni/{cge,custom,interface}/**/*.{cpp,h,c}" +applyTo: "library/src/main/jni/cge/**/*.cpp,library/src/main/jni/cge/**/*.h,library/src/main/jni/cge/**/*.c,library/src/main/jni/custom/**/*.cpp,library/src/main/jni/custom/**/*.h,library/src/main/jni/custom/**/*.c,library/src/main/jni/interface/**/*.cpp,library/src/main/jni/interface/**/*.h,library/src/main/jni/interface/**/*.c" --- # C++ / Native Layer diff --git a/.github/instructions/java.instructions.md b/.github/instructions/java.instructions.md index 7ea50219..842bdaf8 100644 --- a/.github/instructions/java.instructions.md +++ b/.github/instructions/java.instructions.md @@ -1,6 +1,6 @@ --- description: "Use when editing Java Android library or demo code. Covers GL thread safety and native interop patterns." -applyTo: "library/src/main/java/**/*.java|cgeDemo/src/main/java/**/*.java" +applyTo: "library/src/main/java/**/*.java,cgeDemo/src/main/java/**/*.java" --- # Java / Android Layer diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3816921a..8fd040a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ on: workflow_dispatch: inputs: version: - description: 'Version number (e.g., 3.1.1) - Manual trigger will NOT publish to Release page' + description: 'Version number (e.g., 3.1.1 or 3.1.1-beta1, without leading v) - Manual trigger will NOT publish to Release page' required: true type: string @@ -66,38 +66,59 @@ jobs: - name: Extract tag version id: tag_version + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_VERSION: ${{ github.event.inputs.version }} run: | # Determine if this is a manual trigger or tag push - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - # Manual trigger - use input version - VERSION="${{ github.event.inputs.version }}" + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + # Manual trigger - sanitize and normalize input version + RAW_VERSION="$INPUT_VERSION" + RAW_VERSION="${RAW_VERSION#${RAW_VERSION%%[![:space:]]*}}" + RAW_VERSION="${RAW_VERSION%${RAW_VERSION##*[![:space:]]}}" + + if [ -z "$RAW_VERSION" ]; then + echo "❌ Version input cannot be empty" + exit 1 + fi + + VERSION="${RAW_VERSION#v}" + if [ "$VERSION" != "$RAW_VERSION" ]; then + echo "⚠️ Input starts with 'v'; auto-stripped to '$VERSION'" + fi + + if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9\.]+)?$ ]]; then + echo "❌ Invalid version input: $RAW_VERSION" + echo "Expected format: {major}.{minor}.{patch}[-prerelease], e.g., 3.1.1 or 3.1.1-beta1" + exit 1 + fi + TAG_NAME="v$VERSION" IS_MANUAL="true" - IS_PRERELEASE="false" - BASE_VERSION="$VERSION" echo "🔧 Manual trigger detected" else # Tag push - extract from ref TAG_NAME="${GITHUB_REF#refs/tags/}" IS_MANUAL="false" - - # Extract version numbers supporting prerelease tags (e.g., v3.1.1-beta1 -> 3.1.1-beta1) - if [[ $TAG_NAME =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-[a-zA-Z0-9\.]+)?$ ]]; then - BASE_VERSION="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}" - PRERELEASE_SUFFIX="${BASH_REMATCH[4]}" - VERSION="${BASE_VERSION}${PRERELEASE_SUFFIX}" - - if [ -n "$PRERELEASE_SUFFIX" ]; then - IS_PRERELEASE="true" - echo "🧪 Prerelease detected: $PRERELEASE_SUFFIX" - else - IS_PRERELEASE="false" - fi + fi + + # Extract and validate version numbers supporting prerelease tags + # e.g., v3.1.1-beta1 -> base=3.1.1, version=3.1.1-beta1 + if [[ $TAG_NAME =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-[a-zA-Z0-9\.]+)?$ ]]; then + BASE_VERSION="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}" + PRERELEASE_SUFFIX="${BASH_REMATCH[4]}" + VERSION="${BASE_VERSION}${PRERELEASE_SUFFIX}" + + if [ -n "$PRERELEASE_SUFFIX" ]; then + IS_PRERELEASE="true" + echo "🧪 Prerelease detected: $PRERELEASE_SUFFIX" else - echo "❌ Invalid tag format: $TAG_NAME" - echo "Tag must be: v{major}.{minor}.{patch}[-prerelease] (e.g., v3.1.1 or v3.1.1-beta1)" - exit 1 + IS_PRERELEASE="false" fi + else + echo "❌ Invalid tag format: $TAG_NAME" + echo "Tag must be: v{major}.{minor}.{patch}[-prerelease] (e.g., v3.1.1 or v3.1.1-beta1)" + exit 1 fi # Validate base version format @@ -117,22 +138,20 @@ jobs: - name: Validate version matches project version run: | PROJECT_VERSION=$(grep -E 'versionName\s*:' build.gradle | sed -E 's/.*versionName\s*:\s*"([^"]+)".*/\1/') - TAG_BASE_VERSION="${{ steps.tag_version.outputs.base_version }}" TAG_VERSION="${{ steps.tag_version.outputs.version }}" echo "Project version: $PROJECT_VERSION" - echo "Tag base version: $TAG_BASE_VERSION" echo "Tag full version: $TAG_VERSION" - # For prerelease versions, only validate against base version - if [ "$PROJECT_VERSION" != "$TAG_BASE_VERSION" ]; then + # Tag version must exactly match versionName in build.gradle + if [ "$PROJECT_VERSION" != "$TAG_VERSION" ]; then echo "❌ Version mismatch!" - echo "Tag base version ($TAG_BASE_VERSION) does not match project version ($PROJECT_VERSION)" + echo "Tag version ($TAG_VERSION) does not match project version ($PROJECT_VERSION)" echo "Please update the versionName in build.gradle to match the tag, or use the correct tag." exit 1 fi - echo "✅ Version validation passed: $TAG_BASE_VERSION (full: $TAG_VERSION)" + echo "✅ Version validation passed: $TAG_VERSION" - name: Setup local.properties for publishing run: | @@ -261,12 +280,13 @@ jobs: - name: Sync artifacts to maven repo and open PR if: steps.tag_version.outputs.is_manual != 'true' id: sync_maven_repo + env: + ARTIFACT_REPO_TOKEN: ${{ secrets.ARTIFACT_REPO_TOKEN }} run: | set -euo pipefail VERSION="${{ steps.tag_version.outputs.version }}" TAG_NAME="${{ steps.tag_version.outputs.tag_name }}" - ARTIFACT_REPO_TOKEN="${{ secrets.ARTIFACT_REPO_TOKEN }}" if [[ -z "${ARTIFACT_REPO_TOKEN:-}" ]]; then echo "❌ Missing secret: ARTIFACT_REPO_TOKEN (repo:write to android-gpuimage-plus-maven)" @@ -307,7 +327,7 @@ jobs: git push origin "$BRANCH" echo "📝 Creating pull request..." - PR_BODY="Automated artifact sync for ${TAG_NAME}.\n\nGenerated by main repo release workflow." + PR_BODY=$(printf "Automated artifact sync for %s.\n\nGenerated by main repo release workflow." "$TAG_NAME") API_JSON=$(jq -n --arg title "Publish ${TAG_NAME} artifacts" \ --arg head "$BRANCH" \ --arg base "master" \ @@ -406,8 +426,8 @@ jobs: ### 📚 Documentation - **GitHub Repository**: https://github.com/wysaid/android-gpuimage-plus - - **Wiki**: https://github.com/wysaid/android-gpuimage-plus/wiki - - **Filter Rule Documentation**: https://github.com/wysaid/android-gpuimage-plus/wiki/Parsing-String-Rule-(EN) + - **Build & Integration Guide**: https://github.com/wysaid/android-gpuimage-plus/blob/master/docs/build.md + - **Filter Rule Documentation**: https://github.com/wysaid/android-gpuimage-plus/blob/master/docs/filter-rules.md --- @@ -426,6 +446,8 @@ jobs: files: | /tmp/release-artifacts/*.apk /tmp/release-artifacts/*.aar + /tmp/release-artifacts/SHA256SUMS.txt + /tmp/release-artifacts/*.sha256 draft: false prerelease: ${{ steps.tag_version.outputs.is_prerelease == 'true' }} generate_release_notes: true From aea85736951b3c29ef058f069ba634497e8b3f4f Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 14 Feb 2026 02:02:10 +0800 Subject: [PATCH 6/7] fix: preserve artifact history and harden failure summary --- .github/workflows/release.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8fd040a6..fdf098e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -303,7 +303,14 @@ jobs: echo "🚚 Syncing generated Maven artifacts..." mkdir -p "$WORKDIR/org/wysaid/gpuimage-plus" - rsync -av --delete "${SOURCE_REPO}/org/wysaid/gpuimage-plus/" "$WORKDIR/org/wysaid/gpuimage-plus/" + + if [ ! -d "${SOURCE_REPO}/org/wysaid/gpuimage-plus" ]; then + echo "❌ Source artifacts not found: ${SOURCE_REPO}/org/wysaid/gpuimage-plus" + exit 1 + fi + + # Additive sync: keep previously published versions in artifact repo. + rsync -av "${SOURCE_REPO}/org/wysaid/gpuimage-plus/" "$WORKDIR/org/wysaid/gpuimage-plus/" echo "🧭 Regenerating index pages" pushd "$WORKDIR" @@ -545,13 +552,16 @@ jobs: - name: Build failure notification if: failure() + env: + FAILURE_VERSION: ${{ steps.tag_version.outputs.version || github.event.inputs.version || 'unknown' }} + FAILURE_TAG: ${{ steps.tag_version.outputs.tag_name || github.ref_name || 'unknown' }} run: | - echo "::error::Release build failed for version ${{ steps.tag_version.outputs.version }}" + echo "::error::Release build failed for version ${FAILURE_VERSION}" echo "" >> $GITHUB_STEP_SUMMARY echo "## ❌ Build Failed" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "**Version**: ${{ steps.tag_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "**Tag**: ${{ steps.tag_version.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY + echo "**Version**: ${FAILURE_VERSION}" >> $GITHUB_STEP_SUMMARY + echo "**Tag**: ${FAILURE_TAG}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY From 8a7b7ed991c0e9740dd23da4d948241b7d60536e Mon Sep 17 00:00:00 2001 From: wy Date: Sat, 14 Feb 2026 02:18:25 +0800 Subject: [PATCH 7/7] remove donate --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index 45dd081a..a072d83a 100644 --- a/README.md +++ b/README.md @@ -180,14 +180,4 @@ Some utils are available for creating filters: [https://github.com/wysaid/cge-to ## License -[MIT License](https://github.com/wysaid/android-gpuimage-plus/blob/master/LICENSE) - -## Donate - -Alipay: - -![Alipay](https://raw.githubusercontent.com/wysaid/android-gpuimage-plus/master/screenshots/alipay.jpg "alipay") - -Paypal: - -[![Paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif "Paypal")](http://blog.wysaid.org/p/donate.html) +[MIT License](https://github.com/wysaid/android-gpuimage-plus/blob/master/LICENSE) \ No newline at end of file