Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Swift code from thorvg-swift #2

Merged
merged 24 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2362b5f
Migrate Swift code from thorvg-swift
andyf-canva Jun 19, 2024
bc83e74
Update submodule reference to andyf-canva fork
andyf-canva Jun 19, 2024
dfc52f5
Update gitmodules to include specific branch
andyf-canva Aug 13, 2024
5440e25
Add savers/gif to the exclusion list
andyf-canva Aug 13, 2024
7de9978
Support the last frame
andyf-canva Aug 13, 2024
0965d37
Rename error enum
andyf-canva Aug 13, 2024
5cf39e8
Remove unsued mime types
andyf-canva Aug 13, 2024
bd4ec6c
Remove force unwrapping inside snapshot tests
andyf-canva Aug 21, 2024
5432b3f
Use Float instead of Int for frame numbers
andyf-canva Aug 21, 2024
81d91e9
Rename default to main
andyf-canva Aug 21, 2024
7116c40
Remove force unwrapping inside snapshot tests - number 2
andyf-canva Aug 21, 2024
e285f0e
Update Package to include pthread details
andyf-canva Aug 21, 2024
5816d2f
Make Engine initialiser public
andyf-canva Aug 21, 2024
0539567
Add jerryscript headers
andyf-canva Aug 21, 2024
2144bbe
Point path to a directory higher
andyf-canva Aug 21, 2024
9871ef7
Restructure package to include scripts and config folders
andyf-canva Aug 25, 2024
e17efd7
Bump up submodule reference to v0.14.7
andyf-canva Aug 25, 2024
ceca80c
Remove remote flag in git submodule update
andyf-canva Aug 25, 2024
6efc77b
Regenerate snapshots
andyf-canva Aug 25, 2024
3150a0d
Simplify Package structure
andyf-canva Aug 25, 2024
1779463
Exclude tools directory
andyf-canva Aug 25, 2024
034db8e
Update README
andyf-canva Aug 25, 2024
55a2969
Update README and config.h
andyf-canva Sep 12, 2024
0f7f0a0
Clean up
andyf-canva Sep 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## User settings
xcuserdata/

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
.swiftpm

.build/
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "thorvg"]
path = thorvg
url = [email protected]:thorvg/thorvg.git
andyf-canva marked this conversation as resolved.
Show resolved Hide resolved
branch = v0.14.x
23 changes: 23 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"pins" : [
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "8e68404f641300bfd0e37d478683bb275926760c",
"version" : "1.15.2"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
"version" : "509.1.1"
}
}
],
"version" : 2
}
92 changes: 92 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// swift-tools-version: 5.9

import PackageDescription

let package = Package(
name: "ThorVGSwift",
platforms: [
.iOS(.v13)
],
products: [
.library(
name: "ThorVGSwift",
targets: ["ThorVGSwift"]),
],
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", .upToNextMinor(from: "1.15.0")),
],
targets: [
.target(
name: "ThorVGSwift",
dependencies: ["thorvg"],
path: "swift"
),
.target(
name: "thorvg",
path: "thorvg",
exclude: [
"cross",
"docs",
"examples",
"pc",
"res",
"src/bindings/wasm",
"src/loaders/external_jpg",
"src/loaders/external_png",
"src/loaders/external_webp",
"src/renderer/gl_engine",
"src/renderer/wg_engine",
"src/savers/gif",
"test",
"tools"
],
publicHeadersPath: "src/bindings/capi",
cxxSettings: [
.headerSearchPath("inc"),
.headerSearchPath("src/common"),
.headerSearchPath("src/bindings"),
.headerSearchPath("src/loaders/jpg"),
.headerSearchPath("src/loaders/lottie"),
.headerSearchPath("src/loaders/lottie"),
.headerSearchPath("src/loaders/lottie/jerryscript"),
.headerSearchPath("src/loaders/lottie/rapidjson"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/jcontext"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/lit"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/include"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/parser"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/jrt"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/vm"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/ecma"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/jmem"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/api"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/parser/regexp"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/parser/js"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/ecma/operations"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/ecma/base"),
.headerSearchPath("src/loaders/lottie/jerryscript/jerry-core/ecma/builtin-objects/typedarray"),
.headerSearchPath("src/loaders/lottie/rapidjson/internal"),
.headerSearchPath("src/loaders/lottie/rapidjson/error"),
.headerSearchPath("src/loaders/png"),
.headerSearchPath("src/loaders/raw"),
.headerSearchPath("src/loaders/svg"),
.headerSearchPath("src/loaders/ttf"),
.headerSearchPath("src/loaders/tvg"),
.headerSearchPath("src/renderer"),
.headerSearchPath("src/renderer/sw_engine"),
]
),
.testTarget(
name: "ThorVGSwift-tests",
dependencies: [
"ThorVGSwift",
.product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
],
path: "swift-tests",
exclude: ["SnapshotTests/__Snapshots__"],
resources: [.process("Resources")]
),
],
cxxLanguageStandard: .cxx14
)
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
# ThroVG for Swift
# ThorVG for Swift
<p align="center">
<img width="800" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/logo/512/thorvg-banner.png">
</p>

A Swift wrapper on top of the ThorVG C++ API.

The upstream ThorVG repository is included in this repository as a submodule. To view the upstream documentation, click here.

Current upstream ThorVG version: [`v0.14.7`](https://github.com/thorvg/thorvg/releases/tag/v0.14.7), commit SHA: [`e3a6bf`](https://github.com/thorvg/thorvg/commit/e3a6bf5229a9671c385ee78bc33e6e6b611a9729).

andyf-canva marked this conversation as resolved.
Show resolved Hide resolved
### Contributing
andyf-canva marked this conversation as resolved.
Show resolved Hide resolved
Before building the Swift Package in Xcode, ensure that you update the submodule and run the copy_config.sh script.

Both of these commands are bundled into a setup script that you can run easily:

```bash
./setup.sh
```

**Note:** The ThorVG source code uses the meson build system to generate build artifacts, including a `config.h` file that is required during compilation.

Since Swift Package Manager (SPM) requires all necessary files to be present in the target directory at compile time, we need to generate this `config.h` file ahead of time so that SPM can access it and successfully build all of the C++ source files.

The `setup.sh` script takes care of copying a pre-built `config.h` file into the correct location within the `thorvg/src` directory, so you don't have to worry about it.

**Important:** As a result of this process, contributors will always see an extra `config.h` file in the git status of the ThorVG submodule. This is expected, and you can simply ignore this file when reviewing changes or making commits.
11 changes: 11 additions & 0 deletions config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#define THORVG_LOTTIE_LOADER_SUPPORT 1

#define THORVG_SVG_LOADER_SUPPORT 1

#define THORVG_SW_RASTER_SUPPORT 1

#define THORVG_TVG_LOADER_SUPPORT 1

#define THORVG_VERSION_STRING "0.13.99"
andyf-canva marked this conversation as resolved.
Show resolved Hide resolved
23 changes: 23 additions & 0 deletions copy_config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

# Define the source and destination paths
SOURCE_PATH="config.h"
DESTINATION_DIR="thorvg/src/renderer"
DESTINATION_PATH="$DESTINATION_DIR/config.h"

# Ensure the destination directory exists
mkdir -p "$DESTINATION_DIR"

# Remove any existing config.h file at the destination
rm -f "$DESTINATION_PATH"

# Copy the config.h file to the destination
cp "$SOURCE_PATH" "$DESTINATION_PATH"

# Check if the operation was successful
if [ $? -eq 0 ]; then
echo "Successfully copied $SOURCE_PATH to $DESTINATION_PATH."
else
echo "Failed to copy $SOURCE_PATH to $DESTINATION_PATH."
exit 1
fi
11 changes: 11 additions & 0 deletions setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

# Update submodules
echo "Updating submodules..."
git submodule update --init --recursive

# Run the copy_config.sh script to copy the config.h file
echo "Setting up config files..."
./copy_config.sh

echo "Setup complete. 🎉"
75 changes: 75 additions & 0 deletions swift-tests/LottieRendererTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import CoreMedia
import XCTest

@testable import ThorVGSwift

final class LottieRendererTests: XCTestCase {

let size = CGSize(width: 1024, height: 1024)
let contentRect = CGRect(x: 0, y: 0, width: 1024, height: 1024)
let pixelFormat = PixelFormat.argb

var lottie: Lottie {
get throws {
guard let url = Bundle.module.url(forResource: "test", withExtension: "json") else {
preconditionFailure("Required resource for testing not found.")
}
return try Lottie(path: url.path)
}
}

func testRender_WithValidFrameIndex_BufferPopulatedWithContent() throws {
var buffer = [UInt32](repeating: 0, count: Int(size.width * size.height))
let renderer = LottieRenderer(try lottie, size: size, buffer: &buffer, stride: Int(size.width), pixelFormat: pixelFormat)

try renderer.render(frameIndex: 0, contentRect: contentRect)

let bufferHasContent = buffer.contains { $0 != 0 }
XCTAssertTrue(bufferHasContent, "Buffer should have non-zero values after rendering.")
}

func testRender_WithAllFrames_Succeeds() throws {
var buffer = [UInt32](repeating: 0, count: Int(size.width * size.height))
let renderer = LottieRenderer(try lottie, size: size, buffer: &buffer, stride: Int(size.width), pixelFormat: pixelFormat)

do {
for index in stride(from: 0, through: try lottie.numberOfFrames, by: 1.0) {
try renderer.render(frameIndex: index, contentRect: contentRect)
}
} catch {
XCTFail("Expected to render all lottie frames successfully, but \(error) error was thrown")
}
}

func testRenderFrame_WithFrameIndexBelowBounds_ThrowsError() throws {
var buffer = [UInt32](repeating: 0, count: Int(size.width * size.height))
let renderer = LottieRenderer(try lottie, size: size, buffer: &buffer, stride: Int(size.width), pixelFormat: pixelFormat)

do {
try renderer.render(frameIndex: -1, contentRect: contentRect)

XCTFail("Expected frameIndexOutOfRange error to be thrown, but no error was thrown.")
} catch {
XCTAssertEqual(error as? LottieRenderingError, .frameIndexOutOfRange)
}
}

func testRenderFrame_WithFrameIndexAboveBounds_ThrowsError() throws {
var buffer = [UInt32](repeating: 0, count: Int(size.width * size.height))
let renderer = LottieRenderer(try lottie, size: size, buffer: &buffer, stride: Int(size.width), pixelFormat: pixelFormat)

do {
try renderer.render(frameIndex: 181, contentRect: contentRect)

XCTFail("Expected frameIndexOutOfRange error to be thrown, but no error was thrown.")
} catch {
XCTAssertEqual(error as? LottieRenderingError, .frameIndexOutOfRange)
}
}
}

private extension CMTime {
init(seconds: TimeInterval) {
self.init(seconds: seconds, preferredTimescale: 600)
}
}
54 changes: 54 additions & 0 deletions swift-tests/LottieTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import CoreMedia
import XCTest

@testable import ThorVGSwift

final class LottieTests: XCTestCase {

let testLottieUrl = Bundle.module.url(forResource: "test", withExtension: "json")!

func testInit_WithValidPath_ReturnsCorrectNumberOfFrames() throws {
let lottie = try Lottie(path: testLottieUrl.path)

XCTAssertEqual(lottie.numberOfFrames, 180)
}

func testInit_WithValidPath_ReturnsCorrectDuration() throws {
let lottie = try Lottie(path: testLottieUrl.path)

XCTAssertEqual(lottie.duration, CMTime(seconds: 3))
}

func testInit_WithInvalidPath_ThrowsError() {
do {
_ = try Lottie(path: "")

XCTFail("Expected failedToLoadFromPath error to be thrown, but no error was thrown.")
} catch {
XCTAssertEqual(error as? LottieRenderingError, .failedToLoadFromPath)
}
}

func testInit_WithValidString_Succeeds() throws {
let animationJson = try NSMutableString(contentsOf: testLottieUrl, encoding: String.Encoding.utf8.rawValue) as String
let lottie = try Lottie(string: animationJson)

XCTAssertEqual(lottie.numberOfFrames, 180)
}

func testInit_WithInvalidString_ThrowsError() throws {
do {
_ = try Lottie(string: "")

XCTFail("Expected failedToLoadFromString error to be thrown, but no error was thrown.")
} catch {
XCTAssertEqual(error as? LottieRenderingError, .failedToLoadFromDataString)
}
}
}

private extension CMTime {
init(seconds: TimeInterval) {
self.init(seconds: seconds, preferredTimescale: 600)
}
}
1 change: 1 addition & 0 deletions swift-tests/Resources/test.json

Large diffs are not rendered by default.

Loading