Skip to content

Latest commit

 

History

History
547 lines (412 loc) · 15.7 KB

File metadata and controls

547 lines (412 loc) · 15.7 KB

Contributing to J2KSwift

Thank you for your interest in contributing to J2KSwift! This document provides guidelines and instructions for contributing to the project.

All documentation in J2KSwift is written in British English. Please use British spellings throughout your contributions (see Language below and ADR-005).

Table of Contents

Code of Conduct

Our Pledge

We are committed to providing a welcoming and inclusive environment for all contributors. Please treat everyone with respect and kindness.

Expected Behavior

  • Use welcoming and inclusive language
  • Be respectful of differing viewpoints and experiences
  • Gracefully accept constructive criticism
  • Focus on what is best for the community
  • Show empathy towards other community members

Unacceptable Behavior

  • Harassment, discrimination, or offensive comments
  • Trolling, insulting/derogatory comments, and personal attacks
  • Public or private harassment
  • Publishing others' private information without permission
  • Other conduct that could reasonably be considered inappropriate

Getting Started

Prerequisites

  • Xcode 16.0 or later (for Apple platform development)
  • Swift 6.2 or later
  • Git
  • SwiftLint (optional but recommended)

Setting Up Development Environment

  1. Fork the repository on GitHub

  2. Clone your fork locally:

    git clone https://github.com/YOUR_USERNAME/J2KSwift.git
    cd J2KSwift
  3. Add the upstream repository:

    git remote add upstream https://github.com/Raster-Lab/J2KSwift.git
  4. Install SwiftLint (optional):

    brew install swiftlint
  5. Build the project:

    swift build
  6. Run tests to ensure everything works:

    swift test
  7. Read the architecture overview before making structural changes:

Development Workflow

Branching Strategy

J2KSwift uses a trunk-based model. The single source of truth for the branching model and release flow is RELEASING.md. Quick reference:

  • main — trunk; always releasable; tags are cut from here
  • feature/<scope>-<topic> — new work
  • fix/<scope>-<topic> — bug fixes
  • vX.Y.Z-release-candidate — release prep, PR → main
  • release/vX.Y.Z — post-release frozen mirror of the tag (auto-created)
  • hotfix/vX.Y.(Z+1) — patch-level fixes branched off release/vX.Y.Z

There is no develop branch. Feature work branches off main and returns via PR.

Working on a Feature

  1. Create a feature branch from main:

    git checkout main
    git pull upstream main
    git checkout -b feature/my-feature
  2. Make your changes with clear, atomic commits

  3. Write/update tests for your changes

  4. Update documentation as needed

  5. Ensure all tests pass:

    swift test
  6. Run SwiftLint:

    swiftlint
  7. Push your changes:

    git push origin feature/my-feature
  8. Open a pull request

Commit Messages

Follow these guidelines for commit messages:

  • Use the present tense ("Add feature" not "Added feature")
  • Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
  • Limit the first line to 72 characters or less
  • Reference issues and pull requests liberally after the first line
  • Consider starting the commit message with an applicable emoji:
    • 🎨 :art: - Improving structure/format of the code
    • ⚡️ :zap: - Improving performance
    • 🔥 :fire: - Removing code or files
    • 🐛 :bug: - Fixing a bug
    • :sparkles: - Introducing new features
    • 📝 :memo: - Adding or updating documentation
    • :white_check_mark: - Adding or updating tests
    • 🔒 :lock: - Fixing security issues
    • ⬆️ :arrow_up: - Upgrading dependencies
    • ⬇️ :arrow_down: - Downgrading dependencies

Example:

✨ Add support for ROI encoding

- Implement MaxShift ROI method
- Add ROI mask generation
- Update documentation with examples

Closes #123

Code Style

Swift Style Guide

We follow the Swift API Design Guidelines and enforce style with SwiftLint.

Key points:

  • Indentation: 4 spaces (no tabs)
  • Line Length: Maximum 120 characters
  • Naming:
    • Types and protocols: UpperCamelCase
    • Functions, variables, constants: lowerCamelCase
    • Enum cases: lowerCamelCase
  • Whitespace:
    • One blank line between type definitions
    • One blank line between method definitions
    • No trailing whitespace

Concurrency

  • Use async/await for asynchronous operations
  • Mark types as Sendable when thread-safe
  • Use actors for mutable shared state
  • Follow Swift 6 strict concurrency rules

Example

/// Encodes images to JPEG 2000 format.
///
/// This encoder provides high-quality JPEG 2000 encoding with support
/// for various compression modes and quality settings.
public struct J2KEncoder: Sendable {
    /// The configuration to use for encoding.
    public let configuration: J2KConfiguration
    
    /// Creates a new encoder with the specified configuration.
    ///
    /// - Parameter configuration: The encoding configuration.
    public init(configuration: J2KConfiguration = J2KConfiguration()) {
        self.configuration = configuration
    }
    
    /// Encodes an image to JPEG 2000 format.
    ///
    /// - Parameter image: The image to encode.
    /// - Returns: The encoded image data.
    /// - Throws: ``J2KError`` if encoding fails.
    public func encode(_ image: J2KImage) throws -> Data {
        // Implementation
    }
}

Testing Requirements

Unit Tests

  • Every public API must have unit tests
  • Aim for at least 90% code coverage for new code
  • Use descriptive test names that explain what is being tested
  • Follow the Arrange-Act-Assert pattern
  • Use XCTest framework

Example:

func testEncoderWithCustomConfiguration() throws {
    // Arrange
    let config = J2KConfiguration(quality: 0.7, lossless: false)
    let encoder = J2KEncoder(configuration: config)
    
    // Act
    let image = J2KImage(width: 100, height: 100, components: 3)
    let data = try encoder.encode(image)
    
    // Assert
    XCTAssertGreaterThan(data.count, 0)
    XCTAssertEqual(encoder.configuration.quality, 0.7, accuracy: 0.001)
}

Integration Tests

  • Test interaction between modules
  • Validate end-to-end workflows
  • Test with realistic data

Performance Tests

  • Add performance tests for critical paths
  • Use XCTestCase.measure for benchmarking
  • Document performance expectations

Performance Testing Guidelines

Performance is a first-class concern in J2KSwift. The following guidelines ensure that performance regressions are caught early and that benchmarks are reproducible.

Using XCTestCase.measure { }

Wrap the code under test in measure { } to record wall-clock time across ten iterations. XCTest computes the mean and standard deviation automatically.

func testDWTPerformance() {
    let image = J2KImage.syntheticRGB(width: 1024, height: 1024)
    measure {
        _ = try? J2KDWT2D().forward(image.components[0])
    }
}

Place performance tests in the Tests/PerformanceTests/ directory, not alongside unit tests, so that CI can run them separately on dedicated hardware.

Running the Performance Test Suite

# Run all performance tests
swift test --filter PerformanceTests

# Run a single benchmark
swift test --filter PerformanceTests.J2KDWTPerformanceTests/testDWTPerformance

Performance tests are excluded from the default swift test run in CI to avoid flaky results on shared runners. They run on dedicated bare-metal GitHub Actions runners on every pull request that touches codec, transform, or accelerate code.

Performance Regression Thresholds

The following thresholds apply to the reference hardware (Apple M2 Pro, 16 GB):

Operation Target Regression threshold
Lossy encode — 4K RGB ≥ 500 MP/s < 450 MP/s
Lossless encode — 4K RGB ≥ 350 MP/s < 315 MP/s
Lossy decode — 4K RGB ≥ 600 MP/s < 540 MP/s
DWT forward pass (1024×1024) ≤ 4 ms > 5 ms
ICT colour transform (1024×1024) ≤ 1 ms > 1.5 ms

A CI job compares the measured baseline against these thresholds and fails the build if any threshold is exceeded. Results are posted as a PR comment.

Using the Cross-Codec Benchmark Drivers

Scripts/benchmarks/cross_codec_warm_bench.py compares J2KSwift against the reference codecs (OpenJPH, Grok, Kakadu, OpenJPEG where installed) on the 33-fixture corpus. Run it locally before submitting performance-sensitive changes (the older Scripts/benchmark_openjpeg.sh was Removed in v11.0.0):

python3 Scripts/benchmarks/cross_codec_warm_bench.py

The driver outputs Markdown tables suitable for pasting into a pull request description; JSON captures are committed under Documentation/Benchmarks/data/. See Documentation/BENCHMARK.md for the full methodology.

Documentation Standards

Code Documentation

  • All public APIs must have documentation comments
  • Use Swift's markup format for documentation
  • Include:
    • Summary description
    • Detailed description (if needed)
    • Parameters with descriptions
    • Return value description
    • Throws description
    • Example usage (for complex APIs)

Example:

/// Decodes JPEG 2000 data into an image.
///
/// This method performs complete decoding of a JPEG 2000 codestream,
/// including entropy decoding, inverse wavelet transform, and dequantisation.
///
/// - Parameter data: The JPEG 2000 data to decode. Must be a valid
///   JPEG 2000 codestream or file format.
/// - Returns: The decoded image with all components.
/// - Throws: ``J2KError/invalidParameter(_:)`` if the data is invalid.
/// - Throws: ``J2KError/internalError(_:)`` if decoding fails.
///
/// Example:
/// ```swift
/// let decoder = J2KDecoder()
/// let image = try decoder.decode(jpegData)
/// print("Decoded: \(image.width)x\(image.height)")
/// ```
public func decode(_ data: Data) throws -> J2KImage {
    // Implementation
}

README and Guides

  • Keep README.md up to date with major changes
  • Update tutorials when APIs change
  • Add examples for new features
  • Document migration paths for breaking changes

Pull Request Process

Before Submitting

  1. ✅ All tests pass
  2. ✅ SwiftLint reports no violations
  3. ✅ Code coverage is maintained or improved
  4. ✅ Documentation is updated
  5. ✅ CHANGELOG.md is updated (if applicable)
  6. ✅ Branch is up to date with target branch

PR Description Template

## Description
Brief description of changes

## Motivation
Why are these changes needed?

## Changes
- List of specific changes
- Be as detailed as necessary

## Testing
- Describe testing performed
- Include any manual testing steps

## Screenshots (if applicable)
Add screenshots for UI changes

## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] SwiftLint passes
- [ ] All tests pass
- [ ] Breaking changes documented

## Related Issues
Closes #123

Review Process

  1. At least one maintainer must review and approve
  2. All CI checks must pass
  3. No unresolved review comments
  4. Branch must be up to date with target branch

After Approval

  • Maintainers will merge using squash commits
  • Your changes will be included in the next release
  • You'll be credited in the release notes

Issue Guidelines

Reporting Bugs

Use the bug report template and include:

  • Clear, descriptive title
  • Steps to reproduce
  • Expected behaviour
  • Actual behaviour
  • Environment details (OS, Swift version, etc.)
  • Relevant code snippets or error messages

Requesting Features

Use the feature request template and include:

  • Clear description of the feature
  • Use cases and motivation
  • Proposed API (if applicable)
  • Alternatives considered
  • Any related issues or discussions

Asking Questions

  • Check existing issues and documentation first
  • Use GitHub Discussions for general questions
  • Be specific and provide context
  • Include relevant code or examples

Priority Levels

Issues and PRs are labeled with priority:

  • P0 - Critical: Security issues, data loss bugs
  • P1 - High: Major bugs, important features
  • P2 - Medium: Minor bugs, nice-to-have features
  • P3 - Low: Polish, optimisations, refactoring

Recognition

Contributors will be recognised in:

  • Release notes
  • CONTRIBUTORS.md file
  • GitHub contributors page

Questions?

  • Open an issue with the "question" label
  • Start a discussion in GitHub Discussions
  • Review existing documentation

Architecture Decision Records

Architecture Decision Records (ADRs) document the significant architectural choices made in J2KSwift — what was decided, why, and what the consequences are.

ADRs live in Documentation/ADR/. The Documentation/ADR/README.md file provides an index of all records.

When to Write an ADR

Write a new ADR when you are:

  • Introducing a new module or changing the module dependency graph.
  • Adopting a new concurrency pattern or changing actor boundaries.
  • Adding a new platform target or GPU backend.
  • Making a breaking change to a public API.
  • Choosing between two or more reasonable technical approaches with different trade-offs.

ADR Lifecycle

Status Meaning
Proposed Under discussion; not yet agreed
Accepted Decision taken and implemented
Deprecated Was accepted but no longer applies
Superseded Replaced by a newer ADR

Current ADRs

ADR Decision
ADR-001 Swift 6 strict concurrency
ADR-002 Value types with copy-on-write storage
ADR-003 Modular GPU backends — Superseded (Apple-only since v8.0.0; J2KVulkan module Removed in v11.0.0)
ADR-004 No DICOM library dependencies
ADR-005 British English in documentation

Language

All documentation and source code comments in J2KSwift use British English. The rationale is recorded in ADR-005.

Common spellings to remember:

Use (British) Avoid (American)
colour color
optimisation optimization
organise organize
behaviour behavior
recognise recognize
analyse analyze
favour favor
artefact artifact
initialisation initialization
parallelisation parallelization

Exception: Swift identifier names (colorSpace, optimize, etc.) use American English to match the Swift standard library and Apple's frameworks.

By contributing to J2KSwift, you agree that your contributions will be licensed under the MIT License.


Thank you for contributing to J2KSwift! Your efforts help make JPEG 2000 support better for the Swift community. 🎉