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).
- Code of Conduct
- Getting Started
- Development Workflow
- CI/CD Process - See comprehensive CI/CD guide
- Code Style
- Testing Requirements
- Performance Testing Guidelines
- Documentation Standards
- Architecture Decision Records
- Pull Request Process
- Issue Guidelines
- Language
We are committed to providing a welcoming and inclusive environment for all contributors. Please treat everyone with respect and kindness.
- 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
- 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
- Xcode 16.0 or later (for Apple platform development)
- Swift 6.2 or later
- Git
- SwiftLint (optional but recommended)
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/J2KSwift.git cd J2KSwift -
Add the upstream repository:
git remote add upstream https://github.com/Raster-Lab/J2KSwift.git
-
Install SwiftLint (optional):
brew install swiftlint
-
Build the project:
swift build
-
Run tests to ensure everything works:
swift test -
Read the architecture overview before making structural changes:
Documentation/ARCHITECTURE.md— module organisation, concurrency model, performance subsystems
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 herefeature/<scope>-<topic>— new workfix/<scope>-<topic>— bug fixesvX.Y.Z-release-candidate— release prep, PR →mainrelease/vX.Y.Z— post-release frozen mirror of the tag (auto-created)hotfix/vX.Y.(Z+1)— patch-level fixes branched offrelease/vX.Y.Z
There is no develop branch. Feature work branches off main and
returns via PR.
-
Create a feature branch from
main:git checkout main git pull upstream main git checkout -b feature/my-feature
-
Make your changes with clear, atomic commits
-
Write/update tests for your changes
-
Update documentation as needed
-
Ensure all tests pass:
swift test -
Run SwiftLint:
swiftlint
-
Push your changes:
git push origin feature/my-feature
-
Open a pull request
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
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
- Types and protocols:
- Whitespace:
- One blank line between type definitions
- One blank line between method definitions
- No trailing whitespace
- Use
async/awaitfor asynchronous operations - Mark types as
Sendablewhen thread-safe - Use actors for mutable shared state
- Follow Swift 6 strict concurrency rules
/// 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
}
}- 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)
}- Test interaction between modules
- Validate end-to-end workflows
- Test with realistic data
- Add performance tests for critical paths
- Use
XCTestCase.measurefor benchmarking - Document performance expectations
Performance is a first-class concern in J2KSwift. The following guidelines ensure that performance regressions are caught early and that benchmarks are reproducible.
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.
# Run all performance tests
swift test --filter PerformanceTests
# Run a single benchmark
swift test --filter PerformanceTests.J2KDWTPerformanceTests/testDWTPerformancePerformance 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.
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.
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.pyThe 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.
- 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
}- 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
- ✅ All tests pass
- ✅ SwiftLint reports no violations
- ✅ Code coverage is maintained or improved
- ✅ Documentation is updated
- ✅ CHANGELOG.md is updated (if applicable)
- ✅ Branch is up to date with target branch
## 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- At least one maintainer must review and approve
- All CI checks must pass
- No unresolved review comments
- Branch must be up to date with target branch
- Maintainers will merge using squash commits
- Your changes will be included in the next release
- You'll be credited in the release notes
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
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
- Check existing issues and documentation first
- Use GitHub Discussions for general questions
- Be specific and provide context
- Include relevant code or examples
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
Contributors will be recognised in:
- Release notes
- CONTRIBUTORS.md file
- GitHub contributors page
- Open an issue with the "question" label
- Start a discussion in GitHub Discussions
- Review existing documentation
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.
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.
| 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 |
| 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 |
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. 🎉