Compare commits

...

56 Commits

Author SHA1 Message Date
laurenspriem
a318977001 sort imports 2025-07-23 19:48:22 +02:00
laurenspriem
eff471c739 Resolve merge conflicts 2025-07-23 19:47:44 +02:00
laurenspriem
5454b262a4 Merge branch 'main' into rust_processing 2025-07-23 19:38:05 +02:00
Laurens Priem
e9ef9d55a4 [mob][photos] Face thumbnail lower severity logging (#6617)
## Description

## Tests
2025-07-23 13:50:19 +02:00
laurenspriem
968f04c04a Lower severity logging 2025-07-23 13:45:37 +02:00
Laurens Priem
59cb3f091e [mob][photos] Face thumbnail fix + smooth scroll (#6616)
## Description

- Fix internal issue with face thumbnail generation
- Make all people page scroll more smooth 

## Tests

Tested on internal build.
2025-07-23 13:40:25 +02:00
Neeraj
630f5a2706 [mob/photos] [fix] Handle duplicate fileID during addOrCopy (#6614)
## Description
If others file contains two files with same hash, we are returning same
FileID twice for add or copy operation. This change fixes that
behaviour.

## Tests
2025-07-23 16:53:13 +05:30
Neeraj Gupta
4a743be322 [mob]Handle duplicate fileID during addOrCopy 2025-07-23 16:46:37 +05:30
Neeraj
c2db1f7da9 [web] Update download link for auth apps (#6615)
## Description

## Tests
2025-07-23 16:45:35 +05:30
Neeraj
843e956a8a [web] Update download link for auth apps 2025-07-23 16:45:04 +05:30
laurenspriem
c2d1c66888 keep alive face thumbnail when scrolling fast 2025-07-23 12:45:09 +02:00
Aman Raj Singh Mourya
e2aabfb95a [auth] add custom icon for Startmail (#6611)
Adding Custom Icon for Startmail.com

## Description
Add custom SVG icon for [Startmail](https://www.startmail.com/) to
support branding in UI components.
## Tests
2025-07-23 16:05:49 +05:30
Neeraj
dbf88c7bed [mob] Skip dup fileID from src collection during copy (#6612)
## Description

## Tests
2025-07-23 15:46:16 +05:30
Neeraj Gupta
a06a5be983 [mob] Skip dup fileID from src collection during copy 2025-07-23 15:45:20 +05:30
max977
3bba125f1c custom-icon-startmail
Adding Custom Icon for Startmail
2025-07-23 11:51:25 +02:00
laurenspriem
1718e5d1d6 More careful logging 2025-07-23 11:33:30 +02:00
laurenspriem
b16c9af36b Logging in super isolate when starting operation 2025-07-23 10:47:01 +02:00
laurenspriem
1cc3499019 face thumbnail fix pragma entry point 2025-07-23 10:34:19 +02:00
laurenspriem
4260c3c769 Remove redundant code 2025-07-23 10:33:31 +02:00
laurenspriem
209291e09a Rename isolate components for clarity 2025-07-23 10:32:58 +02:00
Aman Raj Singh Mourya
dd08ca82fe add unitedhealthgroup icona and added more altnames to previously added colorado icon (#6607)
## Description

## Tests
2025-07-23 00:01:21 +05:30
slacker-treat-deferred-unbuckled-jiffy
8d71a6bb58 Update custom-icons.json 2025-07-22 10:12:45 -06:00
slacker-treat-deferred-unbuckled-jiffy
c583fa4742 Add files via upload 2025-07-22 10:10:35 -06:00
Aman Raj Singh Mourya
ec0d3c4266 [auth] Add numerai and nasdaq icons (#6586)
## Description

## Tests
2025-07-22 14:09:05 +05:30
Murad Khalil
55cc92e57d Update custom-icons.json
fixed nasdaq entry
2025-07-22 10:06:25 +02:00
Murad Khalil
3f71d491e9 Merge branch 'ente-io:main' into main 2025-07-22 10:02:14 +02:00
Aman Raj Singh Mourya
304daf0b09 Minor Fix 2025-07-22 13:24:12 +05:30
Aman Raj Singh Mourya
e1281657ba Add icons for availity, bestbuy, colorado, emeritihealth, lincolnfinancial (#6601)
## Description

## Tests
2025-07-22 13:16:49 +05:30
Aman Raj Singh Mourya
595871f571 feat(ente-auth): Add custom icon for Pangolin (#6604)
## Description
This PR adds the Pangolin icon for auth
2025-07-22 13:14:22 +05:30
Manav Rathi
d31127c2e3 [docs] Simplify (#6606) 2025-07-22 12:03:22 +05:30
Manav Rathi
09d7b82c08 Simplify 2025-07-22 10:56:01 +05:30
Rafael Ieda
88c9f4943b feat(ente-auth): Add custom icon for Pangolin 2025-07-21 22:50:50 -03:00
slacker-treat-deferred-unbuckled-jiffy
cacc7dc85a Add files via upload 2025-07-21 13:55:34 -06:00
slacker-treat-deferred-unbuckled-jiffy
24a30709cd Update custom-icons.json 2025-07-21 13:55:12 -06:00
Murad Khalil
054ad8b480 add numerai and nasdaq icons 2025-07-20 23:15:27 +02:00
laurenspriem
e5e86fb41a Merge branch 'main' into rust_processing 2025-01-14 15:37:51 +05:30
laurenspriem
1e9cc64a64 [mob][photos] Remove temp logging 2024-11-21 16:27:49 +05:30
laurenspriem
0205bec30a [mob][photos] Temp log embeddings 2024-11-19 15:21:25 +05:30
laurenspriem
fad0c4559f [mob][photos] Proper letterbox processing for yolo face 2024-11-09 12:17:53 +05:30
laurenspriem
2e4866d302 [mob][photos] Remove old separate rust methods 2024-11-08 09:57:04 +05:30
laurenspriem
aadbe75c50 [mob][photos] Correct time logging 2024-11-08 09:53:38 +05:30
laurenspriem
899bf79460 [mob][photos] Clean up old dart preprocessing methods 2024-11-08 09:25:00 +05:30
laurenspriem
21af6d0070 [mob][photos] Timing logs 2024-11-08 08:51:42 +05:30
laurenspriem
1bad2b3555 [mob][photos] Mix of flutter and rust decoding 2024-11-08 08:41:14 +05:30
laurenspriem
ffa50df43e [mob][photos] Decode heic in rust 2024-11-08 07:56:28 +05:30
laurenspriem
8fdc7dcd89 [mob][photos] Decode only in rust 2024-11-06 16:04:29 +05:30
laurenspriem
1ed26567a5 [mob][photos] translations 2024-11-06 15:29:19 +05:30
laurenspriem
6101570c9d [mob][photos] Add missing supported formats 2024-11-06 15:29:03 +05:30
laurenspriem
a33bbb22ae [mob][photos] Single rust method for faces and clip preprocessing 2024-11-06 14:46:53 +05:30
laurenspriem
a2661ef6ed [mob][photos] clip rust processing 2024-11-06 12:10:22 +05:30
laurenspriem
8daa22e423 [mob][photos] improve rust face preprocessing 2024-11-05 17:05:03 +05:30
laurenspriem
aeb2235875 [mob][photos] Time rust processing 2024-11-05 11:02:04 +05:30
laurenspriem
bf903562f6 [mob][photos] flutter rust bridge configuration 2024-11-05 10:59:32 +05:30
laurenspriem
9cb7c01481 [mob][photos] Use image processing for face detection 2024-11-04 13:54:53 +05:30
laurenspriem
233d1715e9 [mob][photos] flutter rust bridge generate 2024-11-04 11:56:32 +05:30
laurenspriem
e3c019f7ed [mob][photos] Rust bridge auto format 2024-11-04 11:51:51 +05:30
102 changed files with 7852 additions and 440 deletions

View File

@@ -1,39 +1,22 @@
name: Report a bug
description: For regressions only (things that were working earlier)
description: Things that were working earlier but don't anymore
labels: []
body:
- type: markdown
attributes:
value: |
Before opening a new issue, **please** ensure
1. You are on the latest version,
2. You've searched for existing issues,
3. It was working earlier (otherwise use [this](https://github.com/ente-io/ente/discussions/categories/enhancements))
4. It is not about self hosting (otherwise use [this](https://github.com/ente-io/ente/discussions/categories/q-a))
**Checklist**
1. You've searched existing [issues](https://github.com/search?q=repo%3Aente-io%2Fente+&type=issues) and [discussions](https://github.com/search?q=repo%3Aente-io%2Fente+&type=discussions)
2. It was working earlier (otherwise use [enhancements](https://github.com/ente-io/ente/discussions/categories/enhancements))
3. It is not about self hosting (for those use [this](https://github.com/ente-io/ente/discussions/categories/q-a))
- type: textarea
attributes:
label: Description
description: >
Describe the bug and steps to reproduce the behaviour, and how it
differs from the previously working behaviour.
validations:
required: true
- type: input
attributes:
label: Version
description: The version can be seen at the bottom of settings.
placeholder: e.g. v1.2.3
- type: input
attributes:
label: Last working version
description: >
The version where things were last known to be working. It is fine
if you don't remember the exact version (mention roughly then),
but **if there just isn't a last working version, then please file
it as an
[enhancement](https://github.com/ente-io/ente/discussions/categories/enhancements))**
(where the community upvotes can be used to help prioritize).
placeholder: e.g. v1.2.3
- type: dropdown
attributes:
label: What product are you using?

View File

@@ -1,54 +1,42 @@
# Contributing
First and foremost, thank you for your interest in contributing to Ente 🙏
There are many ways to contribute, and most of them don't require writing code.
* [Spread the word](#spread-the-word)
* [Engage with the community](#engage-with-the-community)
* [Translate](#translate)
* [Document](#document)
- [Spread the word](#spread-the-word)
- [Engage with the community](#engage-with-the-community)
- [Translate](#translate)
- [Document](#document)
## Spread the word
This is perhaps the most impactful contribution you can make. [Spread the
word](https://help.ente.io/photos/features/referral-program/). Online on your
favorite social media channels. Offline to your friends and family who are
looking for a privacy-friendly alternative to big tech.
**This is the most impactful contribution you can make**.
[Spread the word](https://help.ente.io/photos/features/referral-program/). Online on your favorite social media channels. Offline to your friends and family who are looking for a privacy-friendly alternative to big tech.
## Engage with the community
Just hang around, enjoy the vibe. Answer someone's query on our
[Discord](https://discord.gg/z2YVKkycX3), or pile on in the sporadic #off-topic
rants there. Chuckle (or wince!) at our [Twitter](https://twitter.com/enteio)
memes. Suggest a new feature in our [Github
Discussions](https://github.com/ente-io/ente/discussions/new?category=enhancements),
or upvote the existing ones that you feel we should focus on first. Provide your
opinion on existing threads.
Just hang around, enjoy the vibe. The Ente community — the people who are building Ente, and the people who are using Ente — hang out at various places depending on their proclivity:
These might seem like small things, but it provides us energy. Knowing that
there is a community of people who care for what we are building.
- [Discord](https://discord.ente.io)
- [Mastodon](https://fosstodon.org/@ente)
- [X / Twitter](https://twitter.com/enteio)
- [Github Discussions](https://github.com/ente-io/ente/discussions)
Just being around might seem a small thing, but it provides us energy. Knowing that there is a community of people who care for what we are building, **who want us to do better**.
## Translate
If you're interested in helping out with translation, please visit our Crowdin
projects to get started:
| Project | |
| ------------- | ------------- |
| [Auth](https://crowdin.com/project/ente-authenticator-app) | [![Crowdin](https://badges.crowdin.net/ente-authenticator-app/localized.svg)](https://crowdin.com/project/ente-authenticator-app) |
| [Photos](https://crowdin.com/project/ente-photos-app) | [![Crowdin](https://badges.crowdin.net/ente-photos-app/localized.svg)](https://crowdin.com/project/ente-photos-app) |
| [Photos Web / Desktop](https://crowdin.com/project/ente-photos-web) | [![Crowdin](https://badges.crowdin.net/ente-photos-web/localized.svg)](https://crowdin.com/project/ente-photos-web) |
Visit our Crowdin projects to help with translations:
| Project | |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| [Auth](https://crowdin.com/project/ente-authenticator-app) | [![Crowdin](https://badges.crowdin.net/ente-authenticator-app/localized.svg)](https://crowdin.com/project/ente-authenticator-app) |
| [Photos](https://crowdin.com/project/ente-photos-app) | [![Crowdin](https://badges.crowdin.net/ente-photos-app/localized.svg)](https://crowdin.com/project/ente-photos-app) |
| [Photos Web / Desktop](https://crowdin.com/project/ente-photos-web) | [![Crowdin](https://badges.crowdin.net/ente-photos-web/localized.svg)](https://crowdin.com/project/ente-photos-web) |
If your language is not listed for translation, please [create a GitHub
issue](https://github.com/ente-io/ente/issues/new?title=Request+for+New+Language+Translation&body=Language+name%3A+%0AProject%3A+auth%2Fphotos%2Fboth)
to have it added. It is okay to have partial translations. Once ~90% of the
strings in a language get translated, we will start surfacing it in the apps.
Thank you for your support.
## Document
The help guides and FAQs for users of Ente products are also open source, and
@@ -60,25 +48,9 @@ See [docs/](docs/README.md) for how to edit these documents.
## Code contributions
Code is a small aspect of community, and the ways mentioned above are more
important in helping us. But if you'd _really_ like to contribute code, it is
best to start small. Consider some well-scoped changes, say like adding more
[custom icons to auth](auth/docs/adding-icons.md).
If you'd like to contribute code, it is best to start small. Consider some well-scoped changes, say like adding more [custom icons to auth](auth/docs/adding-icons.md), or fixing a specific bug.
Each of the individual product/platform specific directories in this repository
have instructions on setting up a dev environment.
For anything beyond trivial bug fixes, please use
[discussions](https://github.com/ente-io/ente/discussions) instead of performing
code changes directly.
> [!TIP]
>
> Please remember that code is a important, but small, part of the overall big
> picture that makes a product a joy to use. Something that's easy in code is
> not necessarily the right choice for the product as a whole. So we'll repeat -
> there are other ways to contribute than code that we'd request you to
> consider.
Code that changes the behaviour of the product might not get merged, at least not initially. The PR can serve as a discussion bed, but you might find it easier to just start a discussion instead, or post your perspective in the (likely) existing thread about the behaviour change or new feature you wish for.
## Leave a review or star

View File

@@ -71,6 +71,9 @@
],
"hex": "fd4b2d"
},
{
"title": "availity"
},
{
"title": "AzurHosts",
"slug": "azurhosts",
@@ -121,6 +124,13 @@
{
"title": "Belo"
},
{
"title": "bestbuy",
"altNames": [
"Best Buy",
"bestbuy.com"
]
},
{
"title": "Bethesda",
"altNames": [
@@ -292,6 +302,25 @@
{
"title": "CoinDCX"
},
{
"title": "colorado",
"altNames": [
"Colorado.gov",
"Colorado Gov",
"Colorado Government",
"Colorado Government Portal",
"Colorado COVES Death Certificates",
"Colorado COVES",
"Colorado Official State Web Portal",
"Colorado State Web Portal",
"Colorado State Portal",
"Colorado Web Portal",
"Colorado Portal",
"Colorado State",
"Colorado PEAK",
"myColorado"
]
},
{
"title": "ConfigCat"
},
@@ -403,6 +432,13 @@
"Murena"
]
},
{
"title": "emeritihealth",
"altNames": [
"Emeriti Health",
"Emeriti Retirement Health",
]
},
{
"title": "eneba"
},
@@ -706,6 +742,14 @@
{
"title": "Letterboxd"
},
{
"title": "lincolnfinancial",
"altNames": [
"Lincoln Financial",
"Lincoln Financial Group",
"LFG"
]
},
{
"title": "LinkedIn",
"slug": "linkedin"
@@ -842,6 +886,9 @@
"title": "Name.com",
"slug": "name_com"
},
{
"title": "nasdaq"
},
{
"title": "Nextcloud",
"slug": "nextcloud"
@@ -926,6 +973,9 @@
{
"title": "NuCommunity"
},
{
"title": "numerai"
},
{
"title": "NVIDIA"
},
@@ -956,6 +1006,10 @@
"title": "Oracle Cloud",
"slug": "oracle_cloud"
},
{
"title": "Pangolin",
"slug": "pangolin"
},
{
"title": "Parqet",
"slug": "parqet"
@@ -1241,6 +1295,10 @@
"PAYDAY 3"
]
},
{
"title": "Startmail",
"slug": "startmail"
},
{
"title": "STRATO",
"hex": "FF8800"
@@ -1374,6 +1432,16 @@
"title": "Ubuntu One",
"slug": "ubuntu_one"
},
{
"title": "unitedhealthgroup",
"altNames": [
"Unitedhealth Group",
"United Healthgroup",
"UHG",
"uhg.com",
"unitedhealthgroup.com"
]
},
{
"title": "Unity",
"hex": "858585"

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 638 638"><defs><style>.cls-1{fill:#104ab8;}.cls-1,.cls-2,.cls-3,.cls-4{fill-rule:evenodd;}.cls-2{fill:#fff;}.cls-3{fill:#ffed31;}.cls-4{fill:#1c252c;}</style></defs><title>Best Buy Logo Vector</title><g id="logo-en"><polygon class="cls-1" points="0 0 638 0 638 638 0 638 0 0 0 0"/><path class="cls-2" d="M232,347.26V482.49h70.8c27.87,0,53.62-9.78,53.62-38.51,0-19.42-14-28.29-29.38-32.93,9.37-3.76,20-11.66,20-28.23,0-21.2-21-35.56-49.35-35.56Zm43.37,31.5h15.95c6.49,0,11.44,5.08,11.44,10.24,0,4.8-5.14,9.93-11.44,9.93H275.41V378.76Zm0,48.23h21c7.35,0,13.52,5.59,13.52,12,0,6.78-5.77,12.4-14.79,12.4H275.41V427Z" transform="translate(-181 -181)"/><path class="cls-2" d="M267.12,497.24V632.46h70.8c27.87,0,53.62-9.78,53.62-38.51,0-19.42-14-28.29-29.38-32.93,9.38-3.76,20-11.66,20-28.22,0-21.2-21-35.56-49.35-35.56Zm43.37,31.49h15.95c6.49,0,11.44,5.08,11.44,10.25,0,4.79-5.14,9.92-11.44,9.92H310.49V528.73Zm0,48.24h21.05c7.35,0,13.52,5.58,13.52,12,0,6.77-5.77,12.4-14.79,12.4H310.49V577Z" transform="translate(-181 -181)"/><polygon class="cls-2" points="178.87 301.47 178.87 166.25 287.94 166.25 287.94 198.9 222.11 198.9 222.11 216.64 275.56 216.64 275.56 247.25 222.11 247.25 222.11 268.94 287.94 268.94 287.94 301.47 178.87 301.47 178.87 301.47"/><path class="cls-2" d="M528,486.34c30.7,0,55.26-17.51,55.26-45.89,0-45.8-59.58-38.75-59.58-53,0-5.51,5.79-8.43,11.73-8.43,10.24,0,17.64,6.74,17.64,6.74l25.63-24.41c-10.25-9.75-26.44-18-48.26-18-32.75,0-54.19,19.45-54.19,42.9,0,46.38,58.73,39.65,58.73,54.2,0,5.11-4.91,10.24-13.81,10.24-10.11,0-18.12-6.1-24.35-11.28L471,464c10.38,10.12,27,22.32,57,22.32Z" transform="translate(-181 -181)"/><polygon class="cls-2" points="435.66 301.47 435.66 198.78 399.94 198.78 399.94 166.25 514.75 166.25 514.75 198.78 479.04 198.78 479.04 301.47 435.66 301.47 435.66 301.47"/><path class="cls-2" d="M393,497.14h43.25v81c0,8.22,8.19,15.8,16.72,15.8,8.05,0,16.19-6.84,16.19-16.23V497.14h43.12v80c0,31.76-26.36,57.09-60.45,57.09-34.28,0-58.83-27.57-58.83-58.82V497.14Z" transform="translate(-181 -181)"/><polygon class="cls-2" points="384.63 451.37 384.63 403.05 333.81 316.14 377.56 316.14 406.32 362.7 435.18 316.14 479.04 316.14 428.01 403.54 428.01 451.37 384.63 451.37 384.63 451.37"/><polygon class="cls-3" points="479.04 390.15 453.52 415.66 453.52 450.11 479.04 475.62 586.96 475.62 586.96 390.15 479.04 390.15 479.04 390.15"/><path class="cls-4" d="M655.85,613.87a5.75,5.75,0,1,1-5.75-5.74,5.74,5.74,0,0,1,5.75,5.74Z" transform="translate(-181 -181)"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="254" height="197">
<path d="M0 0 C83.82 0 167.64 0 254 0 C254 65.01 254 130.02 254 197 C170.18 197 86.36 197 0 197 C0 131.99 0 66.98 0 0 Z " fill="#255E39" transform="translate(0,0)"/>
<path d="M0 0 C83.82 0 167.64 0 254 0 C254 20.13 254 40.26 254 61 C252 58 252 58 250.44140625 55.1015625 C245.56357778 46.07605867 239.90237971 38.57068879 233 31 C232.40574219 30.32324219 231.81148437 29.64648438 231.19921875 28.94921875 C220.24437377 17.12451014 203.62450839 8.5338504 188 5 C186.79472656 4.72285156 185.58945313 4.44570312 184.34765625 4.16015625 C158.77417322 -1.25753788 132.93293113 2.2138374 110.6875 16.3125 C104.6016815 20.31637252 99.20467262 24.91572017 94 30 C92.94425781 31.01513672 92.94425781 31.01513672 91.8671875 32.05078125 C88.51106627 35.20766805 88.51106627 35.20766805 86 39 C81.91471475 37.63823825 81.46098573 35.78559446 79.3828125 32.05859375 C79.04377899 31.45749283 78.70474548 30.85639191 78.35543823 30.23707581 C77.26990008 28.30862811 76.19708095 26.37345578 75.125 24.4375 C73.71411291 21.91357977 72.29846937 19.39234086 70.8828125 16.87109375 C70.54493713 16.26711761 70.20706177 15.66314148 69.85894775 15.04086304 C67.36815994 10.6040756 64.75182831 6.27882324 62 2 C59.33448576 4.40817141 57.69828306 6.99238148 55.91796875 10.1015625 C55.32242188 11.13708252 54.726875 12.17260254 54.11328125 13.23950195 C53.47777344 14.35671631 52.84226563 15.47393066 52.1875 16.625 C51.52109934 17.78945891 50.85442872 18.95376337 50.1875 20.11791992 C40.93068749 36.30218373 31.75265124 52.53667981 23 69 C27.95 68.01 32.9 67.02 38 66 C35.92604704 72.67301098 33.33060795 78.79820286 30.25 85.0625 C29.27859712 87.06444928 28.30863363 89.06709742 27.33984375 91.0703125 C26.54876221 92.70468262 26.54876221 92.70468262 25.74169922 94.37207031 C22.3730026 101.36581172 19.10119743 108.40590787 15.8125 115.4375 C15.13382358 116.88746733 14.45511339 118.33741885 13.77636719 119.78735352 C12.18329197 123.1911238 10.59131013 126.59540409 9 130 C12.34653694 130 15.42039041 128.66450025 18.58984375 127.65625 C21 127 21 127 24 127 C22.01427175 133.18398243 19.7878242 139.22422353 17.34765625 145.2421875 C17.00625107 146.09120636 16.66484589 146.94022522 16.31309509 147.81497192 C15.23207037 150.50266587 14.14761226 153.188954 13.0625 155.875 C6.77912222 171.34098251 6.77912222 171.34098251 1 187 C2.11632812 186.61328125 3.23265625 186.2265625 4.3828125 185.828125 C33.68892175 175.93751189 66.60784235 172.70374295 97 180 C97.66676758 180.1551709 98.33353516 180.3103418 99.02050781 180.47021484 C108.69943089 182.7670275 118.15151684 185.81999107 127.46875 189.29296875 C128.6546875 189.73253906 129.840625 190.17210937 131.0625 190.625 C132.12597656 191.02976563 133.18945312 191.43453125 134.28515625 191.8515625 C143.57746226 194.72426278 152.62428287 195.36040388 162.3125 195.3125 C164.10361206 195.30404053 164.10361206 195.30404053 165.9309082 195.29541016 C174.3610904 195.1791794 181.93838254 194.62771049 190 192 C190.93513428 191.70198486 190.93513428 191.70198486 191.88916016 191.39794922 C217.24006336 183.15751684 235.08132548 168.03878987 248.72729492 145.21801758 C251.80476766 140.09761617 251.80476766 140.09761617 254 139 C254 158.14 254 177.28 254 197 C170.18 197 86.36 197 0 197 C0 131.99 0 66.98 0 0 Z " fill="#FDFDFD" transform="translate(0,0)"/>
<path d="M0 0 C0.65613281 0.47308594 1.31226563 0.94617187 1.98828125 1.43359375 C8.19172898 6.28906392 12.5611382 12.98165036 16 20 C24.23839404 17.77769262 32.0090674 14.65817187 39.875 11.375 C42.26721473 10.38036914 44.66040281 9.38807517 47.0546875 8.3984375 C48.10124512 7.96192871 49.14780273 7.52541992 50.22607422 7.07568359 C53 6 53 6 57 5 C57 30.41 57 55.82 57 82 C56.01 82.495 56.01 82.495 55 83 C55 81.35 55 79.7 55 78 C51.21452654 76.50479006 47.4282773 75.01156244 43.64135742 73.52001953 C42.35827504 73.01435393 41.0753909 72.50818496 39.79272461 72.00146484 C31.90172043 68.88456448 23.99310874 65.84575736 16 63 C15.525625 64.093125 15.05125 65.18625 14.5625 66.3125 C8.10206161 78.96512328 -2.35460415 87.70575421 -15.4375 93 C-29.87838266 97.0535811 -45.55564041 96.50754481 -58.859375 89.4921875 C-73.15897526 80.99933099 -81.70929125 69.15197361 -86.08203125 53.2265625 C-88.73289686 42.16808167 -87.6277083 31.38695655 -83 21 C-82.39865234 19.61554688 -82.39865234 19.61554688 -81.78515625 18.203125 C-75.22740726 4.7454836 -63.31358344 -4.70987829 -49.4375 -9.9375 C-32.12753124 -14.39739533 -14.1778871 -10.52355405 0 0 Z " fill="#FBFAFA" transform="translate(197,57)"/>
<path d="M0 0 C4.17781518 3.50676714 8.11713034 7.1703933 12 11 C12.80566406 11.79277344 13.61132812 12.58554687 14.44140625 13.40234375 C22.19006532 21.50137697 28.59401999 31.29414841 32 42 C31.29689697 42.29277832 30.59379395 42.58555664 29.86938477 42.88720703 C28.88252686 43.29825684 27.89566895 43.70930664 26.87890625 44.1328125 C25.7820752 44.58946289 24.68524414 45.04611328 23.55517578 45.51660156 C21.21112215 46.49465882 18.86831579 47.47571148 16.52685547 48.45996094 C13.00083028 49.94082264 9.46957562 51.40863582 5.9375 52.875 C4.72900391 53.38546875 3.52050781 53.8959375 2.27539062 54.421875 C1.12103516 54.89882812 -0.03332031 55.37578125 -1.22265625 55.8671875 C-2.26172119 56.30144043 -3.30078613 56.73569336 -4.37133789 57.18310547 C-7 58 -7 58 -10 57 C-11.4296875 54.92578125 -11.4296875 54.92578125 -12.875 52.3125 C-19.42601148 40.51709955 -29.84217649 32.8446492 -42.5625 28.65625 C-56.42990775 24.92271714 -71.13091577 25.7886915 -83.85546875 32.5078125 C-97.34889321 40.51080907 -106.24401068 52.0414341 -110.5546875 67.203125 C-111.02309404 70.14504676 -111.01460289 72.9684764 -110.99194336 75.94140625 C-111.02545456 84.76399115 -111.02545456 84.76399115 -114.08203125 88.640625 C-116.9592845 90.92925563 -119.95266091 92.94464306 -123 95 C-125.07005107 96.76090923 -127.03276148 98.62570442 -129 100.5 C-143.70570026 114.18796331 -143.70570026 114.18796331 -150 117 C-159.57148912 93.63001896 -160.76344158 66.4060235 -151.12304688 42.85791016 C-141.87944936 21.5593662 -127.22320872 4.39468338 -107 -7 C-106.02675781 -7.56203125 -105.05351563 -8.1240625 -104.05078125 -8.703125 C-71.86898073 -26.08920004 -28.99166492 -22.13908958 0 0 Z " fill="#C30230" transform="translate(222,20)"/>
<path d="M0 0 C4.45538794 1.64894619 8.90185574 3.3200767 13.34472656 5.00244141 C15.22690683 5.70955694 17.1119842 6.40898279 18.99902344 7.10302734 C22.15076109 8.26257362 25.29431886 9.44248243 28.4375 10.625 C29.46238525 10.99681396 30.48727051 11.36862793 31.54321289 11.75170898 C32.97395142 12.29613647 32.97395142 12.29613647 34.43359375 12.8515625 C35.71834595 13.32843506 35.71834595 13.32843506 37.02905273 13.81494141 C39 15 39 15 39.84375 16.9699707 C40.49342913 25.41072381 30.94776473 35.75864029 26 42 C25.40703125 42.75925781 24.8140625 43.51851562 24.203125 44.30078125 C9.68750758 61.76294494 -14.28234764 74.8665569 -37 77 C-61.22105191 78.03276946 -85.35950409 76.70549321 -105 61 C-105.74378906 60.44441406 -106.48757812 59.88882813 -107.25390625 59.31640625 C-123.79829816 46.85775982 -123.79829816 46.85775982 -126 40 C-125.10539062 39.23816406 -124.21078125 38.47632813 -123.2890625 37.69140625 C-116.87637214 32.21935059 -110.50956553 26.71190714 -104.265625 21.046875 C-100.88076288 17.98882715 -97.44363278 14.99153574 -94 12 C-90.74459922 13.40639025 -88.85406513 15.05982643 -86.5 17.6875 C-77.43916873 26.95043159 -66.1810049 31.99274637 -53.34033203 32.2980957 C-46.39490631 32.33973156 -39.63170864 32.26258295 -33 30 C-32.21882813 29.75507813 -31.43765625 29.51015625 -30.6328125 29.2578125 C-17.23315198 24.54079805 -7.75035638 15.14019465 -1.25 2.6328125 C-0.8375 1.76398438 -0.425 0.89515625 0 0 Z " fill="#021A71" transform="translate(213,119)"/>
<path d="M0 0 C5.35552828 3.39157167 9.73048255 7.37353935 14.31640625 11.7578125 C16.86387172 13.88626254 18.8665855 14.9914145 22 16 C22.95261719 15.09636719 23.90523437 14.19273438 24.88671875 13.26171875 C26.15342443 12.07007711 27.42036387 10.87868391 28.6875 9.6875 C29.31333984 9.09259766 29.93917969 8.49769531 30.58398438 7.88476562 C33.87475275 4.80074709 36.98137595 2.12841083 41 0 C41.66644531 0.61746094 42.33289063 1.23492187 43.01953125 1.87109375 C48.7281423 7.11662866 54.5269502 12.15258764 60.58984375 16.9921875 C66.81970816 22.18205834 66.81970816 22.18205834 67.53125 25.41015625 C67.38452482 37.86345569 62.48204938 48.68984739 54.2265625 58 C46.31381371 65.47871616 35.90872336 71.06835437 25 72 C24.06671875 72.08121094 23.1334375 72.16242188 22.171875 72.24609375 C8.50219507 72.83653804 -2.2982359 67.43653936 -13.1875 59.75 C-21.40487698 51.71523139 -27.73929647 40.75799942 -28.75 29.125 C-28.43874508 24.7355076 -26.48070743 22.6315565 -23.25 19.8046875 C-22.5075 19.20914063 -21.765 18.61359375 -21 18 C-20.154375 17.30132812 -19.30875 16.60265625 -18.4375 15.8828125 C-16.58822798 14.36451814 -14.73128441 12.85552457 -12.8671875 11.35546875 C-8.44244171 7.72042555 -4.21848422 3.87075667 0 0 Z " fill="#031A71" transform="translate(144,75)"/>
<path d="M0 0 C0.89460938 0.43441406 1.78921875 0.86882813 2.7109375 1.31640625 C13.56733148 7.178859 20.58301944 16.62066024 25 28 C26.34950368 32.74608059 27 37.065874 27 42 C23.55220914 40.49993609 21.17639467 38.49590738 18.4375 35.9375 C12.27265981 30.28487205 5.96967741 24.64645161 -1 20 C-1.6290625 20.56847656 -2.258125 21.13695313 -2.90625 21.72265625 C-8.23211968 26.5271682 -13.58511587 31.29600848 -19 36 C-23.98743064 34.50843196 -26.93142754 31.54332502 -30.5625 28 C-35.25906082 23.26431569 -35.25906082 23.26431569 -41 20 C-41.66644531 20.61746094 -42.33289063 21.23492187 -43.01953125 21.87109375 C-49.49783882 27.82388826 -56.14900967 33.47570939 -63 39 C-65.00712736 40.65839866 -67.00426503 42.32789773 -69 44 C-69.89444705 31.15474652 -65.74873183 21.17814981 -58 11 C-42.95796817 -5.60359869 -19.74821462 -9.8220012 0 0 Z " fill="#FDD204" transform="translate(185,56)"/>
<path d="M0 0 C4.28792054 2.72349458 7.96086865 5.64265272 11.6875 9.125 C14.40593357 11.65919672 16.90611339 13.93740893 20 16 C18.45427224 19.81628832 16.11228755 21.97025743 13.0625 24.6875 C12.57450928 25.12392822 12.08651855 25.56035645 11.58374023 26.01000977 C7.44438884 29.68168288 3.1542964 33.15674428 -1.15625 36.625 C-4.88158413 39.73626806 -8.42845194 43.02239671 -11.94116211 46.37084961 C-14.47323772 48.75074994 -16.77334734 50.66509642 -20 52 C-20.96345444 50.12762629 -21.92053102 48.2519701 -22.875 46.375 C-23.40867187 45.33085938 -23.94234375 44.28671875 -24.4921875 43.2109375 C-27.26231047 37.31186734 -29.44678461 31.53349556 -28 25 C-26.12378629 22.11112789 -23.67216304 20.14329744 -21 18 C-20.154375 17.30132812 -19.30875 16.60265625 -18.4375 15.8828125 C-16.58822798 14.36451814 -14.73128441 12.85552457 -12.8671875 11.35546875 C-8.44244171 7.72042555 -4.21848422 3.87075667 0 0 Z " fill="#7A853B" transform="translate(144,75)"/>
<path d="M0 0 C0.30292969 1.05058594 0.60585937 2.10117188 0.91796875 3.18359375 C3.21554955 10.95006406 5.53141385 18.15450633 10 25 C9.34515625 25.52207031 8.6903125 26.04414063 8.015625 26.58203125 C2.85533541 30.74379023 -2.14926847 34.98462376 -7 39.5 C-12.46969905 44.59155144 -18.15506859 49.34016295 -24 54 C-27.98232778 50.33096724 -30.64316353 46.66489768 -33.25 41.9375 C-33.600625 41.32583984 -33.95125 40.71417969 -34.3125 40.08398438 C-35.98121521 37.11559674 -37.4271484 34.37813958 -38 31 C-31.44231174 24.87720206 -24.8192745 18.95918641 -17.8125 13.34765625 C-14.05017212 10.20715756 -10.48696844 6.86610283 -6.97070312 3.453125 C-3.32463848 0 -3.32463848 0 0 0 Z " fill="#6D3C5E" transform="translate(111,106)"/>
<path d="M0 0 C5.83043424 4.10970437 11.09789229 8.68615219 16.30078125 13.5625 C18.89058893 15.98553895 18.89058893 15.98553895 21.48046875 17.8125 C24.59352223 20.10458893 26.65980595 21.65401484 27.62890625 25.48828125 C28.05445689 35.19358123 24.02478887 43.92444646 19 52 C18.071875 51.0409375 18.071875 51.0409375 17.125 50.0625 C11.25501774 44.20900645 5.00673282 38.77135771 -1.4375 33.5625 C-7.88257776 28.34767925 -14.13523865 22.86476135 -20 17 C-19.31035156 16.443125 -18.62070313 15.88625 -17.91015625 15.3125 C-11.80242122 10.35258349 -5.83030237 5.28326166 0 0 Z " fill="#35647D" transform="translate(184,75)"/>
<path d="M0 0 C0.66 0.33 1.32 0.66 2 1 C-1.63 4.63 -5.26 8.26 -9 12 C-9.66 11.67 -10.32 11.34 -11 11 C-9.92173635 9.72486237 -8.83714481 8.45507431 -7.75 7.1875 C-7.14671875 6.47980469 -6.5434375 5.77210937 -5.921875 5.04296875 C-4.07629602 3.08110329 -2.20054668 1.54330375 0 0 Z " fill="#FEDE22" transform="translate(135,60)"/>
<path d="M0 0 C3.75931429 1.03381143 5.16972619 2.16972619 8 5 C4.37 4.01 0.74 3.02 -3 2 C-2.01 1.34 -1.02 0.68 0 0 Z " fill="#45755B" transform="translate(109,181)"/>
<path d="M0 0 C0.33 0.66 0.66 1.32 1 2 C6.445 1.505 6.445 1.505 12 1 C12 1.33 12 1.66 12 2 C7.71 2.66 3.42 3.32 -1 4 C-0.67 2.68 -0.34 1.36 0 0 Z " fill="#4F7F60" transform="translate(24,65)"/>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="121" height="111">
<path d="M0 0 C39.93 0 79.86 0 121 0 C121 36.63 121 73.26 121 111 C81.07 111 41.14 111 0 111 C0 74.37 0 37.74 0 0 Z " fill="#FDFDFC" transform="translate(0,0)"/>
<path d="M0 0 C1.34094727 -0.00483398 1.34094727 -0.00483398 2.70898438 -0.00976562 C15.92329958 0.09656756 29.05768111 5.15920937 38.69921875 14.3671875 C46.33623486 22.91378081 51.45414229 32.03867724 53.4375 43.3125 C55.4175 42.9825 57.3975 42.6525 59.4375 42.3125 C56.40996635 50.06943534 46.82853593 55.97682128 39.5 59.3125 C37.82136046 60.00117263 36.13406518 60.66925254 34.4375 61.3125 C33.5609375 61.67085937 32.684375 62.02921875 31.78125 62.3984375 C20.17960201 66.92308022 6.59150797 64.44771949 -4.48828125 59.66015625 C-6.56191878 58.74121933 -8.62982096 57.81032187 -10.69677734 56.87646484 C-21.71786864 51.93615813 -31.96757967 49.00024219 -43.6875 53.25 C-49.85297476 56.02724089 -53.58090596 59.9493408 -57.5625 65.3125 C-58.41134766 66.43978516 -58.41134766 66.43978516 -59.27734375 67.58984375 C-59.70144531 68.15832031 -60.12554687 68.72679688 -60.5625 69.3125 C-60.21265815 63.71503042 -56.42242893 59.17242893 -52.5625 55.3125 C-52.45417831 53.23031638 -52.37676459 51.14650871 -52.3125 49.0625 C-51.32728272 34.37380601 -44.38377625 22.27099897 -33.859375 12.1953125 C-23.67156706 3.84408218 -13.05318857 -0.23629958 0 0 Z " fill="#7D9C07" transform="translate(60.5625,2.6875)"/>
<path d="M0 0 C39.93 0 79.86 0 121 0 C121 15.18 121 30.36 121 46 C118.69 46 116.38 46 114 46 C113.72027344 45.10539062 113.44054687 44.21078125 113.15234375 43.2890625 C110.46208014 35.17501984 107.34684612 28.77440212 102 22 C101.401875 21.113125 100.80375 20.22625 100.1875 19.3125 C91.26978576 9.88520209 79.12630771 4.33913818 66.27734375 3.7265625 C48.74009638 3.25612695 36.53738751 7.13555151 23.3125 19.1875 C14.84180419 28.0987521 10.10977052 38.99431955 9.6875 51.125 C9.34944073 57.79987234 7.81473954 60.61643116 3 65 C2.01 66.32 1.02 67.64 0 69 C0 46.23 0 23.46 0 0 Z " fill="#FDFEFC" transform="translate(0,0)"/>
<path d="M0 0 C7.7514671 5.16764473 13.82626339 13.90836132 16.703125 22.71875 C16.703125 24.03875 16.703125 25.35875 16.703125 26.71875 C11.46215703 26.16706916 7.64974874 24.39540175 3.078125 21.96875 C-14.90773287 12.27142089 -14.90773287 12.27142089 -34.734375 12.4375 C-39.88111467 14.59356662 -45.70693353 17.53886707 -48.296875 22.71875 C-47.08 22.615625 -45.863125 22.5125 -44.609375 22.40625 C-34.14474874 22.43895196 -24.29281839 28.5635433 -15.1953125 33.265625 C-10.48229066 35.6284877 -6.28828923 36.00534278 -1.109375 36.28125 C0.01500977 36.34602539 0.01500977 36.34602539 1.16210938 36.41210938 C3.00890875 36.51797685 4.85599334 36.6188476 6.703125 36.71875 C3.65258646 39.76928854 0.59053342 39.47031177 -3.60205078 39.58984375 C-11.64954951 39.3417513 -18.79427187 36.1503666 -26.171875 33.21875 C-39.2911509 28.00909237 -50.24239432 25.58953741 -64.296875 28.71875 C-63.91593636 18.62387614 -58.87170481 10.32516481 -51.57421875 3.50390625 C-35.74413322 -8.9049258 -17.299832 -9.65024642 0 0 Z " fill="#FBFCF9" transform="translate(85.296875,20.28125)"/>
<path d="M0 0 C0.07476562 0.59425781 0.14953125 1.18851563 0.2265625 1.80078125 C1.89490592 12.31788733 7.03959282 21.85443765 15 29 C18.20627298 31.27541953 21.4909817 33.22982559 25 35 C26.60294922 35.81017578 26.60294922 35.81017578 28.23828125 36.63671875 C32.50890201 38.42008659 36.60502154 38.5364421 41.1875 38.5 C42.02394043 38.49427979 42.86038086 38.48855957 43.72216797 38.48266602 C54.81538411 38.16495288 64.10602396 33.8155071 72 26 C76.47244994 20.53700746 79.32082187 14.4918547 82 8 C85.96 8 89.92 8 94 8 C91.39683113 21.53647814 84.16445962 32.6524866 73.296875 41.1015625 C60.88291389 49.43665068 45.9146679 53.47483724 31.00488281 50.61767578 C22.25796227 48.44289499 14.98853845 44.65676037 8 39 C6.948125 38.236875 5.89625 37.47375 4.8125 36.6875 C-1.36142088 30.78797561 -5.25051243 24.03047722 -8.4375 16.1875 C-8.69660156 15.55344238 -8.95570312 14.91938477 -9.22265625 14.26611328 C-10.58441341 10.72610583 -11.29621798 7.7930351 -11 4 C-4.5 0 -4.5 0 0 0 Z " fill="#7D9C08" transform="translate(20,59)"/>
<path d="M0 0 C2.01050607 0.26263368 4.00961749 0.61336248 6 1 C8.78756093 1.25820896 11.57995448 1.40834288 14.375 1.5625 C15.11105469 1.60568359 15.84710938 1.64886719 16.60546875 1.69335938 C18.40348068 1.79843799 20.20171958 1.89962093 22 2 C19.47636907 4.52363093 17.87673704 4.2605272 14.4296875 4.2734375 C13.71039063 4.26570313 12.99109375 4.25796875 12.25 4.25 C11.54617187 4.25773437 10.84234375 4.26546875 10.1171875 4.2734375 C5.70222524 4.257025 2.15942781 3.64807517 -2 2 C-1.34 1.34 -0.68 0.68 0 0 Z " fill="#DFE7C3" transform="translate(70,55)"/>
<path d="M0 0 C0.99 0.33 1.98 0.66 3 1 C2.60167969 1.45890625 2.20335937 1.9178125 1.79296875 2.390625 C-0.91142062 5.52271585 -3.57991771 8.63877459 -6 12 C-5.54300722 6.97307937 -3.00758252 3.85103739 0 0 Z " fill="#9DB444" transform="translate(6,60)"/>
<path d="M0 0 C0 0.66 0 1.32 0 2 C0.66 2.33 1.32 2.66 2 3 C-4.625 2.25 -4.625 2.25 -8 0 C-5.13822943 -1.43088528 -3.06624227 -0.59991697 0 0 Z " fill="#E9EED6" transform="translate(76,56)"/>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,12 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 70.1 60" style="enable-background:new 0 0 70.1 60;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#0090BA;}
</style>
<g>
<path class="st0" d="M53.3,0L37.7,43c-0.4,1.1-1.4,1.8-2.5,2v0h16.5c1.3,0,2.4-0.8,2.8-2L70.1,0H53.3z M33.9,44.3
c1,0,1.8-0.5,2.3-1.2c0.1-0.1,0.2-0.3,0.4-0.7l5.7-15.8l-3.3-9.2c-0.4-1-1.4-1.7-2.6-1.7c-1,0-1.8,0.5-2.3,1.2
c-0.1,0.1-0.3,0.4-0.4,0.7l-5.7,15.8l3.4,9.2C31.7,43.6,32.7,44.3,33.9,44.3z M18.4,15h16.7v0c-1.2,0.1-2.3,0.9-2.7,2L16.8,60H0
l15.6-43C16,15.9,17.1,15,18.4,15z">
</path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 719 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="900.82861"
height="955.20648"
viewBox="0 0 238.34422 252.7317"
version="1.1"
id="svg420"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs417" />
<g
id="layer1"
transform="translate(-13.119542,-5.9258171)">
<path
d="m 213.66176,90.072122 c 4.95655,0 8.97383,4.018046 8.97383,8.973827 0,4.956581 -4.01728,8.974621 -8.97383,8.974621 -4.95657,0 -8.97462,-4.01804 -8.97462,-8.974621 0,-4.955781 4.01805,-8.973827 8.97462,-8.973827 z m 35.2316,37.450998 c -0.90048,29.80928 -23.66033,69.21262 -54.51292,79.34466 -36.04206,11.836 -63.40991,-5.92226 -72.08409,-26.74061 -6.75754,-16.21966 -1.65117,-35.62363 10.96266,-43.83669 10.6506,-6.93533 30.48543,-8.76736 47.15454,2.19144 -5.85627,-15.34246 -21.62491,-25.4256 -35.59101,-28.49424 -13.96613,-3.06867 -28.38324,0.43858 -38.74504,5.69946 13.29071,-14.68572 44.40801,-28.946049 78.24077,-10.95958 22.67676,12.05491 32.43775,28.93208 42.0489,51.72763 C 251.59637,117.87858 234.026,71.411066 203.39074,43.794029 172.15544,15.636686 129.95516,4.340214 97.668803,6.103155 108.32483,12.678273 120.84625,22.06586 132.41209,33.053363 81.298533,26.697169 39.174705,38.314245 13.119542,73.749217 27.67508,70.878527 46.868833,69.073666 65.974711,70.016861 28.737658,96.252107 7.1124298,140.38147 18.105298,186.43137 c 6.718497,-11.74129 16.767711,-25.84558 28.726275,-38.62863 -3.677175,34.36994 1.42836,80.83745 45.62293,110.85478 -2.25587,-9.42394 -4.08014,-20.88443 -4.91466,-33.0154 20.673197,16.1282 50.685067,29.42205 87.917917,20.24096 65.77679,-16.21975 83.34719,-79.78335 73.4356,-118.35996"
style="fill:#f36118;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0776283"
id="path32" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="500px" height="500px" viewBox="0 0 500 500" style="enable-background:new 0 0 500 500;" xml:space="preserve">
<style type="text/css">
.st0{fill:#6573FF;}
.st1{fill:#202945;}
</style>
<g>
<path class="st0" d="M500,47.2C500,20.9,478.6,0,452.9,0H47.7C21.4-0.5,0,20.9,0,47.2v43.9c0,0,186.4,180.6,250.5,180.6
C319.6,271.7,500,92.2,500,92.2S500,56.5,500,47.2z"/>
<path class="st1" d="M0,452.8C0,479.1,21.4,500,47.2,500h405.6c26.3,0,47.2-21.4,47.2-47.2V142.7c0,0-159.2,184.4-249.7,184.4
C160.8,327.1,0,178.4,0,178.4C0,236.6,0,395.2,0,452.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 843 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,9 @@
rust_input: crate::api
rust_root: rust/
dart_output: lib/src/rust
dart_preamble: |
// ignore_for_file: require_trailing_commas
full_dep: true
web: false

View File

@@ -91,10 +91,9 @@ Future<void> dismissUpdateAppDialog(WidgetTester tester) async {
await tester.pumpAndSettle();
}
///Use this widget as floating action buttom in HomeWidget so that frames
///are built and rendered continuously so that timeline trace has continuous
///data. Change the duraiton in `_startTimer()` to control the duraiton of
///are built and rendered continuously so that timeline trace has continuous
///data. Change the duraiton in `_startTimer()` to control the duraiton of
///test on app init.
// class TempWidget extends StatefulWidget {
@@ -127,4 +126,4 @@ Future<void> dismissUpdateAppDialog(WidgetTester tester) async {
// ? const CircularProgressIndicator()
// : const SizedBox.shrink();
// }
// }
// }

View File

@@ -1,4 +1,3 @@
import "dart:io";
import 'package:photos/core/cache/lru_map.dart';

View File

@@ -383,8 +383,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
}
}
if (personID == null && clusterID == null) {
_logger.severe("personID and clusterID cannot be null both");
throw Exception("personID and clusterID cannot be null");
}
_logger.severe(
"Something went wrong finding a face from `getCoverFaceForPerson` (personID: $personID, clusterID: $clusterID)",
);
return null;
}

View File

@@ -5,7 +5,6 @@ class SetupSRPRequest {
final String srpA;
final bool isUpdate;
SetupSRPRequest({
required this.srpUserID,
required this.srpSalt,
@@ -82,6 +81,7 @@ class CompleteSRPSetupRequest {
);
}
}
class SrpAttributes {
final String srpUserID;
final String srpSalt;

View File

@@ -1427,12 +1427,20 @@ class CollectionsService {
}
// group files by collectionID
final Map<int, List<EnteFile>> filesByCollection = {};
final Map<int, Set<int>> fileSeenByCollection = {};
for (final file in filesToCopy) {
if (filesByCollection.containsKey(file.collectionID!)) {
filesByCollection[file.collectionID!]!.add(file.copyWith());
} else {
filesByCollection[file.collectionID!] = [file.copyWith()];
fileSeenByCollection.putIfAbsent(file.collectionID!, () => <int>{});
if (fileSeenByCollection[file.collectionID]!
.contains(file.uploadedFileID)) {
_logger.warning(
"skip copy, duplicate ID: ${file.uploadedFileID} in collection "
"${file.collectionID}",
);
continue;
}
filesByCollection
.putIfAbsent(file.collectionID!, () => [])
.add(file.copyWith());
}
for (final entry in filesByCollection.entries) {
final srcCollectionID = entry.key;
@@ -1579,9 +1587,6 @@ class CollectionsService {
params["files"] = [];
for (final batchFile in batch) {
final fileKey = getFileKey(batchFile);
_logger.info(
"srcCollection : $srcCollectionID file: ${batchFile.uploadedFileID} key: ${CryptoUtil.bin2base64(fileKey)} ",
);
final encryptedKeyData =
CryptoUtil.encryptSync(fileKey, getCollectionKey(dstCollectionID));
batchFile.encryptedKey =
@@ -1643,17 +1648,27 @@ class CollectionsService {
);
final List<EnteFile> filesToCopy = [];
final List<EnteFile> filesToAdd = [];
final Set<int> seenForAdd = {};
final Set<int> seenForCopy = {};
for (final EnteFile file in othersFile) {
if (hashToUserFile.containsKey(file.hash ?? '')) {
final userFile = hashToUserFile[file.hash]!;
if (userFile.fileType == file.fileType) {
filesToAdd.add(userFile);
} else {
filesToCopy.add(file);
}
} else {
filesToCopy.add(file);
final userFile = hashToUserFile[file.hash ?? ''];
final bool shouldAdd =
userFile != null && userFile.fileType == file.fileType;
final targetList = shouldAdd ? filesToAdd : filesToCopy;
final seenSet = shouldAdd ? seenForAdd : seenForCopy;
final fileToProcess = shouldAdd ? userFile : file;
final uploadID = fileToProcess.uploadedFileID;
if (seenSet.contains(uploadID)) {
final action = shouldAdd ? "adding" : "copying";
_logger.warning(
"skip $action file $uploadID as it is already ${action}ed",
);
continue;
}
targetList.add(fileToProcess);
seenSet.add(uploadID!);
}
return (filesToAdd, filesToCopy);
}

View File

@@ -8,11 +8,11 @@ import "package:ml_linalg/dtype.dart";
import "package:ml_linalg/vector.dart";
import "package:photos/generated/protos/ente/common/vector.pb.dart";
import "package:photos/models/base/id.dart";
import "package:photos/services/isolate_functions.dart";
import "package:photos/services/isolate_service.dart";
import "package:photos/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart";
import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart";
import "package:photos/services/machine_learning/ml_result.dart";
import "package:photos/utils/isolate/isolate_operations.dart";
import "package:photos/utils/isolate/super_isolate.dart";
class FaceInfo {
final String faceID;
@@ -507,7 +507,8 @@ ClusteringResult _runCompleteClustering(Map args) {
EVector.fromBuffer(entry.value).values,
dtype: DType.float32,
),
fileCreationTime: fileIDToCreationTime?[getFileIdFromFaceId<int>(entry.key)],
fileCreationTime:
fileIDToCreationTime?[getFileIdFromFaceId<int>(entry.key)],
),
);
}

View File

@@ -89,8 +89,8 @@ class FaceDetectionRelative extends Detection {
// Calculate the scaling
final double scaleX = originalSize.width / newSize.width;
final double scaleY = originalSize.height / newSize.height;
const double translateX = 0;
const double translateY = 0;
final double translateX = - ((originalSize.width - newSize.width) ~/ 2) / originalSize.width;
final double translateY = - ((originalSize.height - newSize.height) ~/ 2) / originalSize.height;
// Transform Box
_transformBox(box, scaleX, scaleY, translateX, translateY);

View File

@@ -1,6 +1,5 @@
import "dart:async";
import 'dart:typed_data' show Float32List, Uint8List;
import 'dart:ui' as ui show Image;
import 'package:logging/logging.dart';
import "package:onnx_dart/onnx_dart.dart";
@@ -44,8 +43,9 @@ class FaceDetectionService extends MlModel {
/// Detects faces in the given image data.
static Future<List<FaceDetectionRelative>> predict(
ui.Image image,
Uint8List rawRgbaBytes,
Uint8List resizedBytes,
int resizedHeight,
int resizedWidth,
int sessionAddress,
) async {
assert(
@@ -57,9 +57,11 @@ class FaceDetectionService extends MlModel {
final startTime = DateTime.now();
final (inputImageList, scaledSize) = await preprocessImageYoloFace(
image,
rawRgbaBytes,
final scaledSize = Dimensions(width: resizedWidth, height: resizedHeight);
final inputImageList = await resizedToPreprocessedYoloFace(
resizedBytes,
scaledSize.width,
scaledSize.height,
);
final preprocessingTime = DateTime.now();
final preprocessingMs =

View File

@@ -1,11 +1,11 @@
import "dart:async" show unawaited;
import "dart:typed_data" show Uint8List, Float32List;
import "dart:ui" show Image;
import "package:logging/logging.dart";
import "package:photos/core/event_bus.dart";
import "package:photos/events/diff_sync_complete_event.dart";
import "package:photos/events/people_changed_event.dart";
import "package:photos/models/ml/face/dimension.dart";
import "package:photos/services/machine_learning/face_ml/face_detection/detection.dart";
import "package:photos/services/machine_learning/face_ml/face_detection/face_detection_service.dart";
import "package:photos/services/machine_learning/face_ml/face_embedding/face_embedding_service.dart";
@@ -73,8 +73,11 @@ class FaceRecognitionService {
static Future<List<FaceResult>> runFacesPipeline(
int enteFileID,
Image image,
Dimensions imageDimensions,
Uint8List rawRgbaBytes,
Uint8List resizedRgbBytes,
int resizedHeight,
int resizedWidth,
int faceDetectionAddress,
int faceEmbeddingAddress,
) async {
@@ -85,8 +88,9 @@ class FaceRecognitionService {
final List<FaceDetectionRelative> faceDetectionResult =
await _detectFacesSync(
enteFileID,
image,
rawRgbaBytes,
resizedRgbBytes,
resizedHeight,
resizedWidth,
faceDetectionAddress,
faceResults,
);
@@ -103,7 +107,7 @@ class FaceRecognitionService {
// Align the faces
final Float32List faceAlignmentResult = await _alignFacesSync(
image,
imageDimensions,
rawRgbaBytes,
faceDetectionResult,
faceResults,
@@ -133,8 +137,9 @@ class FaceRecognitionService {
/// Runs face recognition on the given image data.
static Future<List<FaceDetectionRelative>> _detectFacesSync(
int fileID,
Image image,
Uint8List rawRgbaBytes,
Uint8List resizedBytes,
int resizedHeight,
int resizedWidth,
int interpreterAddress,
List<FaceResult> faceResults,
) async {
@@ -142,8 +147,9 @@ class FaceRecognitionService {
// Get the bounding boxes of the faces
final List<FaceDetectionRelative> faces =
await FaceDetectionService.predict(
image,
rawRgbaBytes,
resizedBytes,
resizedHeight,
resizedWidth,
interpreterAddress,
);
@@ -169,7 +175,7 @@ class FaceRecognitionService {
/// Aligns multiple faces from the given image data.
/// Returns a list of the aligned faces as image data.
static Future<Float32List> _alignFacesSync(
Image image,
Dimensions imageDimensions,
Uint8List rawRgbaBytes,
List<FaceDetectionRelative> faces,
List<FaceResult> faceResults,
@@ -177,7 +183,7 @@ class FaceRecognitionService {
try {
final (alignedFaces, alignmentResults, _, blurValues, _) =
await preprocessToMobileFaceNetFloat32List(
image,
imageDimensions,
rawRgbaBytes,
faces,
);

View File

@@ -1,15 +1,13 @@
import 'dart:async';
import 'dart:typed_data' show Uint8List;
import "package:computer/computer.dart";
import "package:logging/logging.dart";
import "package:photos/models/ml/face/box.dart";
import "package:photos/services/isolate_functions.dart";
import "package:photos/services/isolate_service.dart";
import "package:photos/utils/image_ml_util.dart";
import "package:photos/utils/isolate/isolate_operations.dart";
import "package:photos/utils/isolate/super_isolate.dart";
final Computer _computer = Computer.shared();
@pragma('vm:entry-point')
class FaceThumbnailGenerator extends SuperIsolate {
@override
Logger get logger => _logger;
@@ -37,20 +35,30 @@ class FaceThumbnailGenerator extends SuperIsolate {
String imagePath,
List<FaceBox> faceBoxes,
) async {
final List<Map<String, dynamic>> faceBoxesJson =
faceBoxes.map((box) => box.toJson()).toList();
final List<Uint8List> faces = await runInIsolate(
IsolateOperation.generateFaceThumbnails,
{
'imagePath': imagePath,
'faceBoxesList': faceBoxesJson,
},
).then((value) => value.cast<Uint8List>());
final compressedFaces =
await compressFaceThumbnails({'listPngBytes': faces});
_logger.fine(
"Compressed face thumbnails from sizes ${faces.map((e) => e.length / 1024).toList()} to ${compressedFaces.map((e) => e.length / 1024).toList()} kilobytes",
);
return compressedFaces;
try {
_logger.info(
"Generating face thumbnails for ${faceBoxes.length} face boxes in $imagePath",
);
final List<Map<String, dynamic>> faceBoxesJson =
faceBoxes.map((box) => box.toJson()).toList();
final List<Uint8List> faces = await runInIsolate(
IsolateOperation.generateFaceThumbnails,
{
'imagePath': imagePath,
'faceBoxesList': faceBoxesJson,
},
).then((value) => value.cast<Uint8List>());
_logger.info("Generated face thumbnails");
final compressedFaces =
await compressFaceThumbnails({'listPngBytes': faces});
_logger.fine(
"Compressed face thumbnails from sizes ${faces.map((e) => e.length / 1024).toList()} to ${compressedFaces.map((e) => e.length / 1024).toList()} kilobytes",
);
return compressedFaces;
} catch (e, s) {
_logger.severe("Failed to generate face thumbnails", e, s);
rethrow;
}
}
}

View File

@@ -2,12 +2,12 @@ import 'dart:async';
import "package:logging/logging.dart";
import "package:photos/models/ml/vector.dart";
import "package:photos/services/isolate_functions.dart";
import "package:photos/services/isolate_service.dart";
import "package:photos/services/machine_learning/ml_constants.dart";
import "package:photos/services/machine_learning/semantic_search/clip/clip_text_encoder.dart";
import "package:photos/services/machine_learning/semantic_search/query_result.dart";
import "package:photos/services/remote_assets_service.dart";
import "package:photos/utils/isolate/isolate_operations.dart";
import "package:photos/utils/isolate/super_isolate.dart";
import "package:synchronized/synchronized.dart";
class MLComputer extends SuperIsolate {

View File

@@ -1,4 +1,3 @@
class GeneralFaceMlException implements Exception {
final String message;
@@ -26,4 +25,4 @@ class CouldNotRunFaceDetector implements Exception {}
class CouldNotWarpAffine implements Exception {}
class CouldNotRunFaceEmbeddor implements Exception {}
class CouldNotRunFaceEmbeddor implements Exception {}

View File

@@ -2,14 +2,14 @@ import "dart:async";
import "package:flutter/foundation.dart" show debugPrint;
import "package:logging/logging.dart";
import "package:photos/services/isolate_functions.dart";
import "package:photos/services/isolate_service.dart";
import 'package:photos/services/machine_learning/face_ml/face_detection/face_detection_service.dart';
import 'package:photos/services/machine_learning/face_ml/face_embedding/face_embedding_service.dart';
import "package:photos/services/machine_learning/ml_models_overview.dart";
import 'package:photos/services/machine_learning/ml_result.dart';
import "package:photos/services/machine_learning/semantic_search/clip/clip_image_encoder.dart";
import "package:photos/services/remote_assets_service.dart";
import "package:photos/utils/isolate/isolate_operations.dart";
import "package:photos/utils/isolate/super_isolate.dart";
import "package:photos/utils/ml_util.dart";
import "package:photos/utils/network_util.dart";
import "package:synchronized/synchronized.dart";

View File

@@ -1,5 +1,4 @@
import "dart:typed_data" show Uint8List, Float32List;
import "dart:ui" show Image;
import "package:logging/logging.dart";
import "package:onnx_dart/onnx_dart.dart";
@@ -28,16 +27,23 @@ class ClipImageEncoder extends MlModel {
factory ClipImageEncoder() => instance;
static Future<List<double>> predict(
Image image,
Uint8List rawRgbaBytes,
Uint8List resizedBytes,
int resizedHeight,
int resizedWidth,
int sessionAddress, [
int? enteFileID,
]) async {
final startTime = DateTime.now();
final inputList = await preprocessImageClip(image, rawRgbaBytes);
final inputList = await resizedToPreprocessedClip(
resizedBytes,
resizedWidth,
resizedHeight,
);
final preprocessingTime = DateTime.now();
final preprocessingMs =
preprocessingTime.difference(startTime).inMilliseconds;
late List<double> result;
try {
if (MlModel.usePlatformPlugin) {
@@ -57,7 +63,7 @@ class ClipImageEncoder extends MlModel {
final inferenceMs = inferTime.difference(preprocessingTime).inMilliseconds;
final totalMs = inferTime.difference(startTime).inMilliseconds;
_logger.info(
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""} (inference: $inferenceMs ms, preprocessing: $preprocessingMs ms)",
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""} (preprocessing: $preprocessingMs ms, inference: $inferenceMs ms)",
);
return result;
}

View File

@@ -1,7 +1,6 @@
import "dart:async" show Timer, unawaited;
import "dart:developer" as dev show log;
import "dart:math" show min;
import "dart:ui" show Image;
import "package:flutter/foundation.dart";
import "package:logging/logging.dart";
@@ -294,13 +293,15 @@ class SemanticSearchService {
static Future<ClipResult> runClipImage(
int enteFileID,
Image image,
Uint8List rawRgbaBytes,
Uint8List resizedBytes,
int resizedHeight,
int resizedWidth,
int clipImageAddress,
) async {
final embedding = await ClipImageEncoder.predict(
image,
rawRgbaBytes,
resizedBytes,
resizedHeight,
resizedWidth,
clipImageAddress,
enteFileID,
);

View File

@@ -0,0 +1,45 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: require_trailing_commas
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:photos/src/rust/frb_generated.dart';
// These functions are ignored because they are not marked as `pub`: `process_image_ml`
Future<
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
)> processImageMlFromPath(
{required String imagePath}) =>
RustLib.instance.api
.crateApiImageProcessingProcessImageMlFromPath(imagePath: imagePath);
Future<
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
)> processImageMlFromData(
{required List<int> rgbaData,
required int width,
required int height}) =>
RustLib.instance.api.crateApiImageProcessingProcessImageMlFromData(
rgbaData: rgbaData, width: width, height: height);

View File

@@ -0,0 +1,12 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: require_trailing_commas
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:photos/src/rust/frb_generated.dart';
String greet({required String name}) =>
RustLib.instance.api.crateApiSimpleGreet(name: name);

View File

@@ -0,0 +1,9 @@
import "package:photos/src/rust/frb_generated.dart";
bool _isInitFrb = false;
Future<void> initFrb() async {
if (_isInitFrb) return;
await RustLib.init();
_isInitFrb = true;
}

View File

@@ -0,0 +1,525 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: require_trailing_commas
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
import 'dart:async';
import 'dart:convert';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:photos/src/rust/api/image_processing.dart';
import 'package:photos/src/rust/api/simple.dart';
import 'package:photos/src/rust/frb_generated.dart';
import 'package:photos/src/rust/frb_generated.io.dart'
if (dart.library.js_interop) 'frb_generated.web.dart';
/// Main entrypoint of the Rust API
class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
@internal
static final instance = RustLib._();
RustLib._();
/// Initialize flutter_rust_bridge
static Future<void> init({
RustLibApi? api,
BaseHandler? handler,
ExternalLibrary? externalLibrary,
bool forceSameCodegenVersion = true,
}) async {
await instance.initImpl(
api: api,
handler: handler,
externalLibrary: externalLibrary,
forceSameCodegenVersion: forceSameCodegenVersion,
);
}
/// Initialize flutter_rust_bridge in mock mode.
/// No libraries for FFI are loaded.
static void initMock({
required RustLibApi api,
}) {
instance.initMockImpl(
api: api,
);
}
/// Dispose flutter_rust_bridge
///
/// The call to this function is optional, since flutter_rust_bridge (and everything else)
/// is automatically disposed when the app stops.
static void dispose() => instance.disposeImpl();
@override
ApiImplConstructor<RustLibApiImpl, RustLibWire> get apiImplConstructor =>
RustLibApiImpl.new;
@override
WireConstructor<RustLibWire> get wireConstructor =>
RustLibWire.fromExternalLibrary;
@override
Future<void> executeRustInitializers() async {
await api.crateApiSimpleInitApp();
}
@override
ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig =>
kDefaultExternalLibraryLoaderConfig;
@override
String get codegenVersion => '2.11.1';
@override
int get rustContentHash => 1140226238;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
stem: 'rust_lib_photos',
ioDirectory: 'rust/target/release/',
webPrefix: 'pkg/',
);
}
abstract class RustLibApi extends BaseApi {
String crateApiSimpleGreet({required String name});
Future<void> crateApiSimpleInitApp();
Future<
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
)>
crateApiImageProcessingProcessImageMlFromData(
{required List<int> rgbaData,
required int width,
required int height});
Future<
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
)>
crateApiImageProcessingProcessImageMlFromPath(
{required String imagePath});
}
class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
RustLibApiImpl({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@override
String crateApiSimpleGreet({required String name}) {
return handler.executeSync(SyncTask(
callFfi: () {
final arg0 = cst_encode_String(name);
return wire.wire__crate__api__simple__greet(arg0);
},
codec: DcoCodec(
decodeSuccessData: dco_decode_String,
decodeErrorData: null,
),
constMeta: kCrateApiSimpleGreetConstMeta,
argValues: [name],
apiImpl: this,
));
}
TaskConstMeta get kCrateApiSimpleGreetConstMeta => const TaskConstMeta(
debugName: "greet",
argNames: ["name"],
);
@override
Future<void> crateApiSimpleInitApp() {
return handler.executeNormal(NormalTask(
callFfi: (port_) {
return wire.wire__crate__api__simple__init_app(port_);
},
codec: DcoCodec(
decodeSuccessData: dco_decode_unit,
decodeErrorData: null,
),
constMeta: kCrateApiSimpleInitAppConstMeta,
argValues: [],
apiImpl: this,
));
}
TaskConstMeta get kCrateApiSimpleInitAppConstMeta => const TaskConstMeta(
debugName: "init_app",
argNames: [],
);
@override
Future<
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
)>
crateApiImageProcessingProcessImageMlFromData(
{required List<int> rgbaData,
required int width,
required int height}) {
return handler.executeNormal(NormalTask(
callFfi: (port_) {
final arg0 = cst_encode_list_prim_u_8_loose(rgbaData);
final arg1 = cst_encode_u_32(width);
final arg2 = cst_encode_u_32(height);
return wire
.wire__crate__api__image_processing__process_image_ml_from_data(
port_, arg0, arg1, arg2);
},
codec: DcoCodec(
decodeSuccessData:
dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize,
decodeErrorData: null,
),
constMeta: kCrateApiImageProcessingProcessImageMlFromDataConstMeta,
argValues: [rgbaData, width, height],
apiImpl: this,
));
}
TaskConstMeta get kCrateApiImageProcessingProcessImageMlFromDataConstMeta =>
const TaskConstMeta(
debugName: "process_image_ml_from_data",
argNames: ["rgbaData", "width", "height"],
);
@override
Future<
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
)>
crateApiImageProcessingProcessImageMlFromPath(
{required String imagePath}) {
return handler.executeNormal(NormalTask(
callFfi: (port_) {
final arg0 = cst_encode_String(imagePath);
return wire
.wire__crate__api__image_processing__process_image_ml_from_path(
port_, arg0);
},
codec: DcoCodec(
decodeSuccessData:
dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize,
decodeErrorData: null,
),
constMeta: kCrateApiImageProcessingProcessImageMlFromPathConstMeta,
argValues: [imagePath],
apiImpl: this,
));
}
TaskConstMeta get kCrateApiImageProcessingProcessImageMlFromPathConstMeta =>
const TaskConstMeta(
debugName: "process_image_ml_from_path",
argNames: ["imagePath"],
);
@protected
String dco_decode_String(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as String;
}
@protected
List<int> dco_decode_list_prim_u_8_loose(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as List<int>;
}
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as Uint8List;
}
@protected
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List<dynamic>;
if (arr.length != 9) {
throw Exception('Expected 9 elements, got ${arr.length}');
}
return (
dco_decode_list_prim_u_8_strict(arr[0]),
dco_decode_usize(arr[1]),
dco_decode_usize(arr[2]),
dco_decode_list_prim_u_8_strict(arr[3]),
dco_decode_usize(arr[4]),
dco_decode_usize(arr[5]),
dco_decode_list_prim_u_8_strict(arr[6]),
dco_decode_usize(arr[7]),
dco_decode_usize(arr[8]),
);
}
@protected
int dco_decode_u_32(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as int;
}
@protected
int dco_decode_u_8(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return raw as int;
}
@protected
void dco_decode_unit(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return;
}
@protected
BigInt dco_decode_usize(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
return dcoDecodeU64(raw);
}
@protected
String sse_decode_String(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
final inner = sse_decode_list_prim_u_8_strict(deserializer);
return utf8.decoder.convert(inner);
}
@protected
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
final len_ = sse_decode_i_32(deserializer);
return deserializer.buffer.getUint8List(len_);
}
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
final len_ = sse_decode_i_32(deserializer);
return deserializer.buffer.getUint8List(len_);
}
@protected
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) sse_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
final var_field0 = sse_decode_list_prim_u_8_strict(deserializer);
final var_field1 = sse_decode_usize(deserializer);
final var_field2 = sse_decode_usize(deserializer);
final var_field3 = sse_decode_list_prim_u_8_strict(deserializer);
final var_field4 = sse_decode_usize(deserializer);
final var_field5 = sse_decode_usize(deserializer);
final var_field6 = sse_decode_list_prim_u_8_strict(deserializer);
final var_field7 = sse_decode_usize(deserializer);
final var_field8 = sse_decode_usize(deserializer);
return (
var_field0,
var_field1,
var_field2,
var_field3,
var_field4,
var_field5,
var_field6,
var_field7,
var_field8
);
}
@protected
int sse_decode_u_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getUint32();
}
@protected
int sse_decode_u_8(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getUint8();
}
@protected
void sse_decode_unit(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
}
@protected
BigInt sse_decode_usize(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getBigUint64();
}
@protected
int sse_decode_i_32(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getInt32();
}
@protected
bool sse_decode_bool(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
return deserializer.buffer.getUint8() != 0;
}
@protected
int cst_encode_u_32(int raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
return raw;
}
@protected
int cst_encode_u_8(int raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
return raw;
}
@protected
void cst_encode_unit(void raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
return raw;
}
@protected
void sse_encode_String(String self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer);
}
@protected
void sse_encode_list_prim_u_8_loose(
List<int> self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self.length, serializer);
serializer.buffer
.putUint8List(self is Uint8List ? self : Uint8List.fromList(self));
}
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_i_32(self.length, serializer);
serializer.buffer.putUint8List(self);
}
@protected
void
sse_encode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) self,
SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
sse_encode_list_prim_u_8_strict(self.$1, serializer);
sse_encode_usize(self.$2, serializer);
sse_encode_usize(self.$3, serializer);
sse_encode_list_prim_u_8_strict(self.$4, serializer);
sse_encode_usize(self.$5, serializer);
sse_encode_usize(self.$6, serializer);
sse_encode_list_prim_u_8_strict(self.$7, serializer);
sse_encode_usize(self.$8, serializer);
sse_encode_usize(self.$9, serializer);
}
@protected
void sse_encode_u_32(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putUint32(self);
}
@protected
void sse_encode_u_8(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putUint8(self);
}
@protected
void sse_encode_unit(void self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
}
@protected
void sse_encode_usize(BigInt self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putBigUint64(self);
}
@protected
void sse_encode_i_32(int self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putInt32(self);
}
@protected
void sse_encode_bool(bool self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
serializer.buffer.putUint8(self ? 1 : 0);
}
}

View File

@@ -0,0 +1,420 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: require_trailing_commas
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
import 'api/image_processing.dart';
import 'api/simple.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:ffi' as ffi;
import 'frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
RustLibApiImplPlatform({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@protected
String dco_decode_String(dynamic raw);
@protected
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
dynamic raw);
@protected
int dco_decode_u_32(dynamic raw);
@protected
int dco_decode_u_8(dynamic raw);
@protected
void dco_decode_unit(dynamic raw);
@protected
BigInt dco_decode_usize(dynamic raw);
@protected
String sse_decode_String(SseDeserializer deserializer);
@protected
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) sse_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
SseDeserializer deserializer);
@protected
int sse_decode_u_32(SseDeserializer deserializer);
@protected
int sse_decode_u_8(SseDeserializer deserializer);
@protected
void sse_decode_unit(SseDeserializer deserializer);
@protected
BigInt sse_decode_usize(SseDeserializer deserializer);
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
bool sse_decode_bool(SseDeserializer deserializer);
@protected
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_String(String raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
return cst_encode_list_prim_u_8_strict(utf8.encoder.convert(raw));
}
@protected
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_encode_list_prim_u_8_loose(
List<int> raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
final ans = wire.cst_new_list_prim_u_8_loose(raw.length);
ans.ref.ptr.asTypedList(raw.length).setAll(0, raw);
return ans;
}
@protected
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_list_prim_u_8_strict(
Uint8List raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
final ans = wire.cst_new_list_prim_u_8_strict(raw.length);
ans.ref.ptr.asTypedList(raw.length).setAll(0, raw);
return ans;
}
@protected
int cst_encode_usize(BigInt raw) {
// Codec=Cst (C-struct based), see doc to use other codecs
return raw.toSigned(64).toInt();
}
@protected
void cst_api_fill_to_wire_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) apiObj,
wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
wireObj) {
wireObj.field0 = cst_encode_list_prim_u_8_strict(apiObj.$1);
wireObj.field1 = cst_encode_usize(apiObj.$2);
wireObj.field2 = cst_encode_usize(apiObj.$3);
wireObj.field3 = cst_encode_list_prim_u_8_strict(apiObj.$4);
wireObj.field4 = cst_encode_usize(apiObj.$5);
wireObj.field5 = cst_encode_usize(apiObj.$6);
wireObj.field6 = cst_encode_list_prim_u_8_strict(apiObj.$7);
wireObj.field7 = cst_encode_usize(apiObj.$8);
wireObj.field8 = cst_encode_usize(apiObj.$9);
}
@protected
int cst_encode_u_32(int raw);
@protected
int cst_encode_u_8(int raw);
@protected
void cst_encode_unit(void raw);
@protected
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);
@protected
void
sse_encode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
(
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) self,
SseSerializer serializer);
@protected
void sse_encode_u_32(int self, SseSerializer serializer);
@protected
void sse_encode_u_8(int self, SseSerializer serializer);
@protected
void sse_encode_unit(void self, SseSerializer serializer);
@protected
void sse_encode_usize(BigInt self, SseSerializer serializer);
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
}
// Section: wire_class
// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
// ignore_for_file: type=lint
/// generated by flutter_rust_bridge
class RustLibWire implements BaseWire {
factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) =>
RustLibWire(lib.ffiDynamicLibrary);
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
RustLibWire.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) lookup,
) : _lookup = lookup;
void store_dart_post_cobject(DartPostCObjectFnType ptr) {
return _store_dart_post_cobject(ptr);
}
late final _store_dart_post_cobjectPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(DartPostCObjectFnType)>>(
'store_dart_post_cobject',
);
late final _store_dart_post_cobject = _store_dart_post_cobjectPtr
.asFunction<void Function(DartPostCObjectFnType)>();
WireSyncRust2DartDco wire__crate__api__simple__greet(
ffi.Pointer<wire_cst_list_prim_u_8_strict> name,
) {
return _wire__crate__api__simple__greet(name);
}
late final _wire__crate__api__simple__greetPtr = _lookup<
ffi.NativeFunction<
WireSyncRust2DartDco Function(
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
'frbgen_photos_wire__crate__api__simple__greet');
late final _wire__crate__api__simple__greet =
_wire__crate__api__simple__greetPtr.asFunction<
WireSyncRust2DartDco Function(
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
)>();
void wire__crate__api__simple__init_app(int port_) {
return _wire__crate__api__simple__init_app(port_);
}
late final _wire__crate__api__simple__init_appPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
'frbgen_photos_wire__crate__api__simple__init_app',
);
late final _wire__crate__api__simple__init_app =
_wire__crate__api__simple__init_appPtr.asFunction<void Function(int)>();
void wire__crate__api__image_processing__process_image_ml_from_data(
int port_,
ffi.Pointer<wire_cst_list_prim_u_8_loose> rgba_data,
int width,
int height,
) {
return _wire__crate__api__image_processing__process_image_ml_from_data(
port_,
rgba_data,
width,
height,
);
}
late final _wire__crate__api__image_processing__process_image_ml_from_dataPtr =
_lookup<
ffi.NativeFunction<
ffi.Void Function(
ffi.Int64,
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
ffi.Uint32,
ffi.Uint32,
)>>(
'frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_data',
);
late final _wire__crate__api__image_processing__process_image_ml_from_data =
_wire__crate__api__image_processing__process_image_ml_from_dataPtr
.asFunction<
void Function(
int,
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
int,
int,
)>();
void wire__crate__api__image_processing__process_image_ml_from_path(
int port_,
ffi.Pointer<wire_cst_list_prim_u_8_strict> image_path,
) {
return _wire__crate__api__image_processing__process_image_ml_from_path(
port_,
image_path,
);
}
late final _wire__crate__api__image_processing__process_image_ml_from_pathPtr =
_lookup<
ffi.NativeFunction<
ffi.Void Function(
ffi.Int64,
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
)>>(
'frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_path',
);
late final _wire__crate__api__image_processing__process_image_ml_from_path =
_wire__crate__api__image_processing__process_image_ml_from_pathPtr
.asFunction<
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_new_list_prim_u_8_loose(
int len,
) {
return _cst_new_list_prim_u_8_loose(len);
}
late final _cst_new_list_prim_u_8_loosePtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<wire_cst_list_prim_u_8_loose> Function(
ffi.Int32)>>('frbgen_photos_cst_new_list_prim_u_8_loose');
late final _cst_new_list_prim_u_8_loose = _cst_new_list_prim_u_8_loosePtr
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_loose> Function(int)>();
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_new_list_prim_u_8_strict(
int len,
) {
return _cst_new_list_prim_u_8_strict(len);
}
late final _cst_new_list_prim_u_8_strictPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(
ffi.Int32)>>('frbgen_photos_cst_new_list_prim_u_8_strict');
late final _cst_new_list_prim_u_8_strict = _cst_new_list_prim_u_8_strictPtr
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(int)>();
int dummy_method_to_enforce_bundling() {
return _dummy_method_to_enforce_bundling();
}
late final _dummy_method_to_enforce_bundlingPtr =
_lookup<ffi.NativeFunction<ffi.Int64 Function()>>(
'dummy_method_to_enforce_bundling',
);
late final _dummy_method_to_enforce_bundling =
_dummy_method_to_enforce_bundlingPtr.asFunction<int Function()>();
}
typedef DartPostCObjectFnType
= ffi.Pointer<ffi.NativeFunction<DartPostCObjectFnTypeFunction>>;
typedef DartPostCObjectFnTypeFunction = ffi.Bool Function(
DartPort port_id, ffi.Pointer<ffi.Void> message);
typedef DartDartPostCObjectFnTypeFunction = bool Function(
DartDartPort port_id, ffi.Pointer<ffi.Void> message);
typedef DartPort = ffi.Int64;
typedef DartDartPort = int;
final class wire_cst_list_prim_u_8_strict extends ffi.Struct {
external ffi.Pointer<ffi.Uint8> ptr;
@ffi.Int32()
external int len;
}
final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
external ffi.Pointer<ffi.Uint8> ptr;
@ffi.Int32()
external int len;
}
final class wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
extends ffi.Struct {
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field0;
@ffi.UintPtr()
external int field1;
@ffi.UintPtr()
external int field2;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field3;
@ffi.UintPtr()
external int field4;
@ffi.UintPtr()
external int field5;
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field6;
@ffi.UintPtr()
external int field7;
@ffi.UintPtr()
external int field8;
}

View File

@@ -18,8 +18,9 @@ class BottomOfTitleBarWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: showCloseButton ? MainAxisAlignment.spaceBetween :
MainAxisAlignment.start,
mainAxisAlignment: showCloseButton
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.start,
children: [
Flexible(
child: Padding(

View File

@@ -100,7 +100,7 @@ class ExtentsPageView extends StatefulWidget {
int? itemCount,
this.dragStartBehavior = DragStartBehavior.start,
this.openDrawer,
}) : childrenDelegate = SliverChildBuilderDelegate(
}) : childrenDelegate = SliverChildBuilderDelegate(
itemBuilder,
childCount: itemCount,
addAutomaticKeepAlives: false,

View File

@@ -1,4 +1,3 @@
import "package:flutter/material.dart";
import 'package:photos/models/file/file.dart';
import "package:photos/ui/viewer/file/zoomable_image.dart";

View File

@@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import "package:photos/generated/l10n.dart";
import 'package:photos/models/file/file.dart';

View File

@@ -21,6 +21,7 @@ class PersonFaceWidget extends StatefulWidget {
final String? clusterID;
final bool useFullFile;
final VoidCallback? onErrorCallback;
final bool keepAlive;
// PersonFaceWidget constructor checks that both personId and clusterID are not null
// and that the file is not null
@@ -29,6 +30,7 @@ class PersonFaceWidget extends StatefulWidget {
this.clusterID,
this.useFullFile = true,
this.onErrorCallback,
this.keepAlive = false,
super.key,
}) : assert(
personId != null || clusterID != null,
@@ -39,12 +41,16 @@ class PersonFaceWidget extends StatefulWidget {
State<PersonFaceWidget> createState() => _PersonFaceWidgetState();
}
class _PersonFaceWidgetState extends State<PersonFaceWidget> {
class _PersonFaceWidgetState extends State<PersonFaceWidget>
with AutomaticKeepAliveClientMixin {
Future<Uint8List?>? faceCropFuture;
EnteFile? fileForFaceCrop;
bool get isPerson => widget.personId != null;
@override
bool get wantKeepAlive => widget.keepAlive;
@override
void initState() {
super.initState();
@@ -64,6 +70,10 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
@override
Widget build(BuildContext context) {
super.build(
context,
); // Calling super.build for AutomaticKeepAliveClientMixin
return FutureBuilder<Uint8List?>(
future: faceCropFuture,
builder: (context, snapshot) {
@@ -163,7 +173,7 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
}
}
if (fileForFaceCrop == null) {
_logger.warning(
_logger.severe(
"No suitable file found for face crop for person: ${widget.personId} or cluster: ${widget.clusterID}",
);
return null;
@@ -176,7 +186,7 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
clusterID: widget.clusterID,
);
if (face == null) {
debugPrint(
_logger.severe(
"No cover face for person: ${widget.personId} or cluster ${widget.clusterID} and fileID ${fileForFaceCrop.uploadedFileID!}",
);
return null;
@@ -188,7 +198,13 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
personOrClusterID: personOrClusterId,
useTempCache: false,
);
return cropMap?[face.faceID];
final result = cropMap?[face.faceID];
if (result == null) {
_logger.severe(
"Null cover face crop for person: ${widget.personId} or cluster ${widget.clusterID} and fileID ${fileForFaceCrop.uploadedFileID!}",
);
}
return result;
} catch (e, s) {
_logger.severe(
"Error getting cover face for person: ${widget.personId} or cluster ${widget.clusterID}",

View File

@@ -95,12 +95,14 @@ class SelectablePersonSearchExample extends StatelessWidget {
final GenericSearchResult searchResult;
final double size;
final SelectedPeople selectedPeople;
final bool isDefaultFace;
const SelectablePersonSearchExample({
super.key,
required this.searchResult,
required this.selectedPeople,
this.size = 102,
this.isDefaultFace = false,
});
void _handleTap(BuildContext context) {
@@ -192,7 +194,10 @@ class SelectablePersonSearchExample extends StatelessWidget {
searchResult.previewThumbnail()!,
shouldShowSyncStatus: false,
)
: FaceSearchResult(searchResult);
: FaceSearchResult(
searchResult,
isDefaultFace: isDefaultFace,
);
} else {
child = const NoThumbnailWidget(
addBorder: false,
@@ -301,8 +306,13 @@ class SelectablePersonSearchExample extends StatelessWidget {
class FaceSearchResult extends StatelessWidget {
final SearchResult searchResult;
final bool isDefaultFace;
const FaceSearchResult(this.searchResult, {super.key});
const FaceSearchResult(
this.searchResult, {
super.key,
this.isDefaultFace = false,
});
@override
Widget build(BuildContext context) {
@@ -313,6 +323,7 @@ class FaceSearchResult extends StatelessWidget {
key: params.containsKey(kPersonWidgetKey)
? ValueKey(params[kPersonWidgetKey])
: ValueKey(params[kPersonParamID] ?? params[kClusterParamId]),
keepAlive: isDefaultFace,
);
}
}
@@ -486,6 +497,7 @@ class _PeopleSectionAllWidgetState extends State<PeopleSectionAllWidget> {
searchResult: normalFaces[index],
size: itemSize,
selectedPeople: widget.selectedPeople!,
isDefaultFace: true,
)
: PersonSearchExample(
searchResult: normalFaces[index],
@@ -525,6 +537,7 @@ class _PeopleSectionAllWidgetState extends State<PeopleSectionAllWidget> {
searchResult: extraFaces[index],
size: itemSize,
selectedPeople: widget.selectedPeople!,
isDefaultFace: false,
)
: PersonSearchExample(
searchResult: extraFaces[index],

View File

@@ -136,7 +136,7 @@ Future<Map<String, Uint8List>?> getCachedFaceCrops(
facesWithoutCrops[face.faceID] = face.detection.box;
}
} catch (e, s) {
_logger.severe(
_logger.warning(
"Error reading cached face crop for faceID ${face.faceID} from file ${faceCropCacheFile.path}",
e,
s,
@@ -212,7 +212,7 @@ Future<Map<String, Uint8List>?> getCachedFaceCrops(
milliseconds: 100 * pow(2, fetchAttempt + 1).toInt(),
);
await Future.delayed(backoff);
_logger.warning(
_logger.fine(
"Error getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()}, retrying (attempt ${fetchAttempt + 1}) in ${backoff.inMilliseconds} ms",
e,
s,
@@ -225,13 +225,13 @@ Future<Map<String, Uint8List>?> getCachedFaceCrops(
useTempCache: useTempCache,
);
}
_logger.severe(
_logger.warning(
"Error getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()}",
e,
s,
);
} else {
_logger.info(
_logger.severe(
"Stopped getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()} due to $e",
);
}
@@ -334,12 +334,14 @@ Future<Map<String, Uint8List>?> _getFaceCrops(
if (useFullFile && file.fileType != FileType.video) {
final File? ioFile = await getFile(file);
if (ioFile == null) {
_logger.severe("Failed to get file for face crop generation");
return null;
}
imagePath = ioFile.path;
} else {
final thumbnail = await getThumbnailForUploadedFile(file);
if (thumbnail == null) {
_logger.severe("Failed to get thumbnail for face crop generation");
return null;
}
imagePath = thumbnail.path;

View File

@@ -1,6 +1,6 @@
import "dart:async";
import "dart:io" show File, Platform;
import "dart:math" show exp, max, min, pi;
import "dart:math" show max, min;
import "dart:typed_data" show Float32List, Uint8List;
import "dart:ui";
@@ -24,14 +24,23 @@ final _logger = Logger("ImageMlUtil");
/// These are 8 bit unsigned integers in range 0-255 for each RGB channel
typedef RGB = (int, int, int);
const gaussianKernelSize = 5;
const gaussianKernelRadius = gaussianKernelSize ~/ 2;
const gaussianSigma = 10.0;
final List<List<double>> gaussianKernel =
create2DGaussianKernel(gaussianKernelSize, gaussianSigma);
const maxKernelSize = gaussianKernelSize;
const maxKernelRadius = maxKernelSize ~/ 2;
const List<String> supportedRustImageFormats = [
'bmp',
'dds',
'farbfeld',
'gif',
'hdr',
'ico',
'jpg',
'jpeg',
'exr',
'png',
'pnm',
'qoi',
'tga',
'tiff',
'webp',
];
// Face thumbnail compression constants
const int _faceThumbnailCompressionQuality = 90;
@@ -102,6 +111,11 @@ Future<DecodedImage> decodeImageFromPath(
return DecodedImage(image, rawRgbaBytes);
}
bool canRustDecodeImage(String imagePath) {
final format = imagePath.split('.').last;
return supportedRustImageFormats.contains(format);
}
/// Decodes [Uint8List] image data to an ui.[Image] object.
Future<Image> decodeImageFromData(Uint8List imageData) async {
// Decoding using flutter paint. This is the fastest and easiest method.
@@ -218,18 +232,19 @@ Future<List<Uint8List>> generateFaceThumbnailsUsingCanvas(
}
}
Future<(Float32List, Dimensions)> preprocessImageYoloFace(
Image image,
Uint8List rawRgbaBytes,
Future<Float32List> resizedToPreprocessedYoloFace(
Uint8List rgbBytes,
int rgbWidth,
int rgbHeight,
) async {
const requiredWidth = 640;
const requiredHeight = 640;
final scale = min(requiredWidth / image.width, requiredHeight / image.height);
final scaledWidth = (image.width * scale).round().clamp(0, requiredWidth);
final scaledHeight = (image.height * scale).round().clamp(0, requiredHeight);
final int letterboxWidth = requiredWidth - rgbWidth;
final int letterboxHeight = requiredHeight - rgbHeight;
final int letterboxWidthHalf = letterboxWidth ~/ 2;
final int letterboxHeightHalf = letterboxHeight ~/ 2;
final processedBytes = Float32List(3 * requiredHeight * requiredWidth);
final buffer = Float32List.view(processedBytes.buffer);
int pixelIndex = 0;
const int channelOffsetGreen = requiredHeight * requiredWidth;
@@ -237,14 +252,18 @@ Future<(Float32List, Dimensions)> preprocessImageYoloFace(
for (var h = 0; h < requiredHeight; h++) {
for (var w = 0; w < requiredWidth; w++) {
late RGB pixel;
if (w >= scaledWidth || h >= scaledHeight) {
if (w < letterboxWidthHalf ||
w >= rgbWidth + letterboxWidthHalf ||
h < letterboxHeightHalf ||
h >= rgbHeight + letterboxHeightHalf) {
pixel = const (114, 114, 114);
} else {
pixel = _getPixelBilinear(
w / scale,
h / scale,
image,
rawRgbaBytes,
final int byteIndex = 3 *
(rgbWidth * (h - letterboxHeightHalf) + (w - letterboxWidthHalf));
pixel = (
rgbBytes[byteIndex],
rgbBytes[byteIndex + 1],
rgbBytes[byteIndex + 2]
);
}
buffer[pixelIndex] = pixel.$1 / 255;
@@ -254,40 +273,35 @@ Future<(Float32List, Dimensions)> preprocessImageYoloFace(
}
}
return (processedBytes, Dimensions(width: scaledWidth, height: scaledHeight));
return processedBytes;
}
Future<Float32List> preprocessImageClip(
Image image,
Uint8List rawRgbaBytes,
Future<Float32List> resizedToPreprocessedClip(
Uint8List rgbBytes,
int rgbWidth,
int rgbHeight,
) async {
const int requiredWidth = 256;
const int requiredHeight = 256;
const int requiredSize = 3 * requiredWidth * requiredHeight;
final scale = max(requiredWidth / image.width, requiredHeight / image.height);
final bool useAntiAlias = scale < 0.8;
final scaledWidth = (image.width * scale).round();
final scaledHeight = (image.height * scale).round();
final widthOffset = max(0, scaledWidth - requiredWidth) / 2;
final heightOffset = max(0, scaledHeight - requiredHeight) / 2;
const requiredWidth = 256;
const requiredHeight = 256;
final processedBytes = Float32List(requiredSize);
final processedBytes = Float32List(3 * requiredHeight * requiredWidth);
final buffer = Float32List.view(processedBytes.buffer);
int pixelIndex = 0;
const int greenOff = requiredHeight * requiredWidth;
const int blueOff = 2 * requiredHeight * requiredWidth;
for (var h = 0 + heightOffset; h < scaledHeight - heightOffset; h++) {
for (var w = 0 + widthOffset; w < scaledWidth - widthOffset; w++) {
final RGB pixel = _getPixelBilinear(
w / scale,
h / scale,
image,
rawRgbaBytes,
antiAlias: useAntiAlias,
const int channelOffsetGreen = requiredHeight * requiredWidth;
const int channelOffsetBlue = 2 * requiredHeight * requiredWidth;
final widthOffset = max(0, rgbWidth - requiredWidth) ~/ 2;
final heightOffset = max(0, rgbHeight - requiredHeight) ~/ 2;
for (var h = 0 + heightOffset; h < heightOffset + requiredHeight; h++) {
for (var w = 0 + widthOffset; w < widthOffset + requiredWidth; w++) {
final int byteIndex = 3 * (rgbWidth * h + w);
final RGB pixel = (
rgbBytes[byteIndex],
rgbBytes[byteIndex + 1],
rgbBytes[byteIndex + 2]
);
buffer[pixelIndex] = pixel.$1 / 255;
buffer[pixelIndex + greenOff] = pixel.$2 / 255;
buffer[pixelIndex + blueOff] = pixel.$3 / 255;
buffer[pixelIndex + channelOffsetGreen] = pixel.$2 / 255;
buffer[pixelIndex + channelOffsetBlue] = pixel.$3 / 255;
pixelIndex++;
}
}
@@ -297,20 +311,20 @@ Future<Float32List> preprocessImageClip(
Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
preprocessToMobileFaceNetFloat32List(
Image image,
Dimensions imageDimensions,
Uint8List rawRgbaBytes,
List<FaceDetectionRelative> relativeFaces, {
int width = 112,
int height = 112,
}) async {
final Size originalSize =
Size(image.width.toDouble(), image.height.toDouble());
Size(imageDimensions.width.toDouble(), imageDimensions.height.toDouble());
final List<FaceDetectionAbsolute> absoluteFaces =
relativeToAbsoluteDetections(
relativeDetections: relativeFaces,
imageWidth: image.width,
imageHeight: image.height,
imageWidth: imageDimensions.width,
imageHeight: imageDimensions.height,
);
final alignedImagesFloat32List =
@@ -334,7 +348,7 @@ Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
alignmentResults.add(alignmentResult);
_warpAffineFloat32List(
image,
imageDimensions,
rawRgbaBytes,
alignmentResult.affineMatrix,
alignedImagesFloat32List,
@@ -368,14 +382,11 @@ Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
RGB _readPixelColor(
int x,
int y,
Image image,
Dimensions image,
Uint8List rgbaBytes,
) {
if (y < 0 || y >= image.height || x < 0 || x >= image.width) {
if (y < -maxKernelRadius ||
y >= image.height + maxKernelRadius ||
x < -maxKernelRadius ||
x >= image.width + maxKernelRadius) {
if (y < -2 || y >= image.height + 2 || x < -2 || x >= image.width + 2) {
_logger.severe(
'`readPixelColor`: Invalid pixel coordinates, out of bounds. x: $x, y: $y',
);
@@ -393,29 +404,6 @@ RGB _readPixelColor(
);
}
RGB _getPixelBlurred(
int x,
int y,
Image image,
Uint8List rgbaBytes,
) {
double r = 0, g = 0, b = 0;
for (int ky = 0; ky < gaussianKernelSize; ky++) {
for (int kx = 0; kx < gaussianKernelSize; kx++) {
final int px = (x - gaussianKernelRadius + kx);
final int py = (y - gaussianKernelRadius + ky);
final RGB pixelRgbTuple = _readPixelColor(px, py, image, rgbaBytes);
final double weight = gaussianKernel[ky][kx];
r += pixelRgbTuple.$1 * weight;
g += pixelRgbTuple.$2 * weight;
b += pixelRgbTuple.$3 * weight;
}
}
return (r.round(), g.round(), b.round());
}
List<List<int>> _createGrayscaleIntMatrixFromNormalized2List(
Float32List imageList,
int startIndex, {
@@ -473,7 +461,7 @@ Future<Image> _cropImage(
}
void _warpAffineFloat32List(
Image inputImage,
Dimensions inputImageDimensions,
Uint8List rawRgbaBytes,
List<List<double>> affineMatrix,
Float32List outputList,
@@ -528,8 +516,12 @@ void _warpAffineFloat32List(
final num xOrigin = (xTrans - b00) * a00Prime + (yTrans - b10) * a01Prime;
final num yOrigin = (xTrans - b00) * a10Prime + (yTrans - b10) * a11Prime;
final RGB pixel =
_getPixelBicubic(xOrigin, yOrigin, inputImage, rawRgbaBytes);
final RGB pixel = _getPixelBicubic(
xOrigin,
yOrigin,
inputImageDimensions,
rawRgbaBytes,
);
// Set the new pixel
outputList[startIndex + 3 * (yTrans * width + xTrans)] =
@@ -584,56 +576,14 @@ Future<List<Uint8List>> compressFaceThumbnails(Map args) async {
}
}
RGB _getPixelBilinear(
RGB _getPixelBicubic(
num fx,
num fy,
Image image,
Uint8List rawRgbaBytes, {
bool antiAlias = false,
}) {
// Clamp to image boundaries
fx = fx.clamp(0, image.width - 1);
fy = fy.clamp(0, image.height - 1);
// Get the surrounding coordinates and their weights
final int x0 = fx.floor();
final int x1 = fx.ceil();
final int y0 = fy.floor();
final int y1 = fy.ceil();
final dx = fx - x0;
final dy = fy - y0;
final dx1 = 1.0 - dx;
final dy1 = 1.0 - dy;
// Get the original pixels (with gaussian blur if antialias)
final RGB Function(int, int, Image, Uint8List) readPixel =
antiAlias ? _getPixelBlurred : _readPixelColor;
final RGB pixel1 = readPixel(x0, y0, image, rawRgbaBytes);
final RGB pixel2 = readPixel(x1, y0, image, rawRgbaBytes);
final RGB pixel3 = readPixel(x0, y1, image, rawRgbaBytes);
final RGB pixel4 = readPixel(x1, y1, image, rawRgbaBytes);
int bilinear(
num val1,
num val2,
num val3,
num val4,
) =>
(val1 * dx1 * dy1 + val2 * dx * dy1 + val3 * dx1 * dy + val4 * dx * dy)
.round();
// Calculate the weighted sum of pixels
final int r = bilinear(pixel1.$1, pixel2.$1, pixel3.$1, pixel4.$1);
final int g = bilinear(pixel1.$2, pixel2.$2, pixel3.$2, pixel4.$2);
final int b = bilinear(pixel1.$3, pixel2.$3, pixel3.$3, pixel4.$3);
return (r, g, b);
}
/// Get the pixel value using Bicubic Interpolation. Code taken mainly from https://github.com/brendan-duncan/image/blob/6e407612752ffdb90b28cd5863c7f65856349348/lib/src/image/image.dart#L697
RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
fx = fx.clamp(0, image.width - 1);
fy = fy.clamp(0, image.height - 1);
Dimensions imageDimensions,
Uint8List rawRgbaBytes,
) {
fx = fx.clamp(0, imageDimensions.width - 1);
fy = fy.clamp(0, imageDimensions.height - 1);
final x = fx.toInt() - (fx >= 0.0 ? 0 : 1);
final px = x - 1;
@@ -652,62 +602,69 @@ RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
dx * dx * (2 * ipp - 5 * icp + 4 * inp - iap) +
dx * dx * dx * (-ipp + 3 * icp - 3 * inp + iap));
final icc = _readPixelColor(x, y, image, rawRgbaBytes);
final icc = _readPixelColor(x, y, imageDimensions, rawRgbaBytes);
final ipp =
px < 0 || py < 0 ? icc : _readPixelColor(px, py, image, rawRgbaBytes);
final icp = px < 0 ? icc : _readPixelColor(x, py, image, rawRgbaBytes);
final inp = py < 0 || nx >= image.width
final ipp = px < 0 || py < 0
? icc
: _readPixelColor(nx, py, image, rawRgbaBytes);
final iap = ax >= image.width || py < 0
: _readPixelColor(px, py, imageDimensions, rawRgbaBytes);
final icp =
px < 0 ? icc : _readPixelColor(x, py, imageDimensions, rawRgbaBytes);
final inp = py < 0 || nx >= imageDimensions.width
? icc
: _readPixelColor(ax, py, image, rawRgbaBytes);
: _readPixelColor(nx, py, imageDimensions, rawRgbaBytes);
final iap = ax >= imageDimensions.width || py < 0
? icc
: _readPixelColor(ax, py, imageDimensions, rawRgbaBytes);
final ip0 = cubic(dx, ipp.$1, icp.$1, inp.$1, iap.$1);
final ip1 = cubic(dx, ipp.$2, icp.$2, inp.$2, iap.$2);
final ip2 = cubic(dx, ipp.$3, icp.$3, inp.$3, iap.$3);
// final ip3 = cubic(dx, ipp.a, icp.a, inp.a, iap.a);
final ipc = px < 0 ? icc : _readPixelColor(px, y, image, rawRgbaBytes);
final inc =
nx >= image.width ? icc : _readPixelColor(nx, y, image, rawRgbaBytes);
final iac =
ax >= image.width ? icc : _readPixelColor(ax, y, image, rawRgbaBytes);
final ipc =
px < 0 ? icc : _readPixelColor(px, y, imageDimensions, rawRgbaBytes);
final inc = nx >= imageDimensions.width
? icc
: _readPixelColor(nx, y, imageDimensions, rawRgbaBytes);
final iac = ax >= imageDimensions.width
? icc
: _readPixelColor(ax, y, imageDimensions, rawRgbaBytes);
final ic0 = cubic(dx, ipc.$1, icc.$1, inc.$1, iac.$1);
final ic1 = cubic(dx, ipc.$2, icc.$2, inc.$2, iac.$2);
final ic2 = cubic(dx, ipc.$3, icc.$3, inc.$3, iac.$3);
// final ic3 = cubic(dx, ipc.a, icc.a, inc.a, iac.a);
final ipn = px < 0 || ny >= image.height
final ipn = px < 0 || ny >= imageDimensions.height
? icc
: _readPixelColor(px, ny, image, rawRgbaBytes);
final icn =
ny >= image.height ? icc : _readPixelColor(x, ny, image, rawRgbaBytes);
final inn = nx >= image.width || ny >= image.height
: _readPixelColor(px, ny, imageDimensions, rawRgbaBytes);
final icn = ny >= imageDimensions.height
? icc
: _readPixelColor(nx, ny, image, rawRgbaBytes);
final ian = ax >= image.width || ny >= image.height
: _readPixelColor(x, ny, imageDimensions, rawRgbaBytes);
final inn = nx >= imageDimensions.width || ny >= imageDimensions.height
? icc
: _readPixelColor(ax, ny, image, rawRgbaBytes);
: _readPixelColor(nx, ny, imageDimensions, rawRgbaBytes);
final ian = ax >= imageDimensions.width || ny >= imageDimensions.height
? icc
: _readPixelColor(ax, ny, imageDimensions, rawRgbaBytes);
final in0 = cubic(dx, ipn.$1, icn.$1, inn.$1, ian.$1);
final in1 = cubic(dx, ipn.$2, icn.$2, inn.$2, ian.$2);
final in2 = cubic(dx, ipn.$3, icn.$3, inn.$3, ian.$3);
// final in3 = cubic(dx, ipn.a, icn.a, inn.a, ian.a);
final ipa = px < 0 || ay >= image.height
final ipa = px < 0 || ay >= imageDimensions.height
? icc
: _readPixelColor(px, ay, image, rawRgbaBytes);
final ica =
ay >= image.height ? icc : _readPixelColor(x, ay, image, rawRgbaBytes);
final ina = nx >= image.width || ay >= image.height
: _readPixelColor(px, ay, imageDimensions, rawRgbaBytes);
final ica = ay >= imageDimensions.height
? icc
: _readPixelColor(nx, ay, image, rawRgbaBytes);
final iaa = ax >= image.width || ay >= image.height
: _readPixelColor(x, ay, imageDimensions, rawRgbaBytes);
final ina = nx >= imageDimensions.width || ay >= imageDimensions.height
? icc
: _readPixelColor(ax, ay, image, rawRgbaBytes);
: _readPixelColor(nx, ay, imageDimensions, rawRgbaBytes);
final iaa = ax >= imageDimensions.width || ay >= imageDimensions.height
? icc
: _readPixelColor(ax, ay, imageDimensions, rawRgbaBytes);
final ia0 = cubic(dx, ipa.$1, ica.$1, ina.$1, iaa.$1);
final ia1 = cubic(dx, ipa.$2, ica.$2, ina.$2, iaa.$2);
@@ -721,30 +678,3 @@ RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
return (c0, c1, c2); // (red, green, blue)
}
List<List<double>> create2DGaussianKernel(int size, double sigma) {
final List<List<double>> kernel =
List.generate(size, (_) => List<double>.filled(size, 0));
double sum = 0.0;
final int center = size ~/ 2;
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
final int dx = x - center;
final int dy = y - center;
final double g = (1 / (2 * pi * sigma * sigma)) *
exp(-(dx * dx + dy * dy) / (2 * sigma * sigma));
kernel[y][x] = g;
sum += g;
}
}
// Normalize the kernel
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
kernel[y][x] /= sum;
}
}
return kernel;
}

View File

@@ -7,9 +7,10 @@ import "package:flutter/services.dart";
import "package:logging/logging.dart";
import "package:photos/core/error-reporting/isolate_logging.dart";
import "package:photos/models/base/id.dart";
import "package:photos/services/isolate_functions.dart";
import "package:photos/utils/isolate/isolate_operations.dart";
import "package:synchronized/synchronized.dart";
@pragma('vm:entry-point')
abstract class SuperIsolate {
Logger get logger;
@@ -80,6 +81,8 @@ abstract class SuperIsolate {
if (rootToken != null) {
BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken);
}
final logger = Logger('SuperIsolate');
logger.info('IsolateMain started');
receivePort.listen((message) async {
final taskID = message[0] as String;
@@ -87,6 +90,7 @@ abstract class SuperIsolate {
final function = IsolateOperation.values[functionIndex];
final args = message[2] as Map<String, dynamic>;
final sendPort = message[3] as SendPort;
logger.info("Starting isolate operation $function in isolate");
late final Object data;
try {

View File

@@ -1,5 +1,6 @@
import "dart:io" show File, Platform;
import "dart:math" as math show sqrt, min, max;
import "dart:typed_data" show Uint8List;
import "package:flutter/services.dart" show PlatformException;
import "package:logging/logging.dart";
@@ -22,6 +23,8 @@ import "package:photos/services/machine_learning/ml_result.dart";
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
import "package:photos/services/search_service.dart";
import "package:photos/services/sync/local_sync_service.dart";
import "package:photos/src/rust/api/image_processing.dart";
import "package:photos/src/rust/custom/init_frb.dart";
import "package:photos/utils/file_util.dart";
import "package:photos/utils/image_ml_util.dart";
import "package:photos/utils/network_util.dart";
@@ -410,27 +413,60 @@ Future<MLResult> analyzeImageStatic(Map args) async {
_logger.info(
"Start analyzeImageStatic for fileID $enteFileID (runFaces: $runFaces, runClip: $runClip)",
);
await initFrb();
final startTime = DateTime.now();
// Decode the image once to use for both face detection and alignment
final decodedImage =
await decodeImageFromPath(imagePath, includeRgbaBytes: true);
final image = decodedImage.image;
final rawRgbaBytes = decodedImage.rawRgbaBytes!;
final bool decodeInRust = canRustDecodeImage(imagePath);
late (
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt,
Uint8List,
BigInt,
BigInt
) rustResults;
if (decodeInRust) {
rustResults = await processImageMlFromPath(imagePath: imagePath);
} else {
final (image, decodedRgbaBytes) = await decodeImageFromPath(imagePath);
rustResults = await processImageMlFromData(
rgbaData: decodedRgbaBytes,
width: image.width,
height: image.height,
);
}
final (
rawRgbaBytes,
imageHeight,
imageWidth,
faceBytes,
faceHeight,
faceWidth,
clipBytes,
clipHeight,
clipWidth
) = rustResults;
final decodedImageSize =
Dimensions(height: image.height, width: image.width);
final result = MLResult.fromEnteFileID(enteFileID);
result.decodedImageSize = decodedImageSize;
Dimensions(height: imageHeight.toInt(), width: imageWidth.toInt());
final decodeTime = DateTime.now();
final decodeMs = decodeTime.difference(startTime).inMilliseconds;
final result = MLResult.fromEnteFileID(enteFileID);
result.decodedImageSize = decodedImageSize;
String faceMsString = "", clipMsString = "";
final pipelines = await Future.wait([
runFaces
? FaceRecognitionService.runFacesPipeline(
enteFileID,
image,
decodedImageSize,
rawRgbaBytes,
faceBytes,
faceHeight.toInt(),
faceWidth.toInt(),
faceDetectionAddress,
faceEmbeddingAddress,
).then((result) {
@@ -442,8 +478,9 @@ Future<MLResult> analyzeImageStatic(Map args) async {
runClip
? SemanticSearchService.runClipImage(
enteFileID,
image,
rawRgbaBytes,
clipBytes,
clipHeight.toInt(),
clipWidth.toInt(),
clipImageAddress,
).then((result) {
clipMsString =

View File

@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev"
source: hosted
version: "72.0.0"
version: "76.0.0"
_flutterfire_internals:
dependency: transitive
description:
@@ -21,7 +21,7 @@ packages:
dependency: transitive
description: dart
source: sdk
version: "0.3.2"
version: "0.3.3"
adaptive_theme:
dependency: "direct main"
description:
@@ -34,10 +34,10 @@ packages:
dependency: transitive
description:
name: analyzer
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev"
source: hosted
version: "6.7.0"
version: "6.11.0"
android_intent_plus:
dependency: "direct main"
description:
@@ -130,10 +130,10 @@ packages:
dependency: "direct main"
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.11.0"
version: "2.12.0"
battery_info:
dependency: "direct main"
description:
@@ -155,10 +155,10 @@ packages:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
brotli:
dependency: transitive
description:
@@ -175,6 +175,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.4.1"
build_cli_annotations:
dependency: transitive
description:
name: build_cli_annotations
sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172
url: "https://pub.dev"
source: hosted
version: "2.1.0"
build_config:
dependency: transitive
description:
@@ -268,10 +276,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
@@ -301,10 +309,10 @@ packages:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
code_builder:
dependency: transitive
description:
@@ -317,10 +325,10 @@ packages:
dependency: "direct main"
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.19.1"
computer:
dependency: "direct main"
description:
@@ -619,10 +627,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
fast_base58:
dependency: "direct main"
description:
@@ -639,6 +647,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
ffigen:
dependency: "direct dev"
description:
name: ffigen
sha256: a0ca4853028c6a9e4d9a0a40bb744fceb898c89d75931d08e87b3987d0087060
url: "https://pub.dev"
source: hosted
version: "15.0.0"
ffmpeg_kit_flutter:
dependency: "direct main"
description:
@@ -668,10 +684,10 @@ packages:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.0"
version: "7.0.1"
file_saver:
dependency: "direct main"
description:
@@ -774,7 +790,7 @@ packages:
source: hosted
version: "0.6.0"
flutter_driver:
dependency: "direct dev"
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
@@ -906,14 +922,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
@@ -1007,6 +1015,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.24"
flutter_rust_bridge:
dependency: "direct main"
description:
name: flutter_rust_bridge
sha256: "37ef40bc6f863652e865f0b2563ea07f0d3c58d8efad803cc01933a4b2ee067e"
url: "https://pub.dev"
source: hosted
version: "2.11.1"
flutter_secure_storage:
dependency: "direct main"
description:
@@ -1416,18 +1432,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev"
source: hosted
version: "10.0.5"
version: "10.0.8"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.9"
leak_tracker_testing:
dependency: transitive
description:
@@ -1536,10 +1552,10 @@ packages:
dependency: transitive
description:
name: macros
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.2-main.4"
version: "0.1.3-main.0"
maps_launcher:
dependency: "direct main"
description:
@@ -1552,10 +1568,10 @@ packages:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
@@ -1645,10 +1661,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
source: hosted
version: "1.15.0"
version: "1.16.0"
mgrs_dart:
dependency: transitive
description:
@@ -1859,10 +1875,10 @@ packages:
dependency: "direct main"
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
version: "1.9.1"
path_drawing:
dependency: transitive
description:
@@ -2019,10 +2035,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.5"
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
@@ -2048,7 +2064,7 @@ packages:
source: hosted
version: "1.0.1"
pool:
dependency: transitive
dependency: "direct main"
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
@@ -2068,10 +2084,10 @@ packages:
dependency: transitive
description:
name: process
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
url: "https://pub.dev"
source: hosted
version: "5.0.2"
version: "5.0.3"
proj4dart:
dependency: transitive
description:
@@ -2081,7 +2097,7 @@ packages:
source: hosted
version: "2.1.0"
protobuf:
dependency: "direct overridden"
dependency: "direct main"
description:
name: protobuf
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
@@ -2089,7 +2105,7 @@ packages:
source: hosted
version: "3.1.0"
provider:
dependency: transitive
dependency: "direct main"
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
@@ -2113,7 +2129,7 @@ packages:
source: hosted
version: "1.4.0"
quiver:
dependency: transitive
dependency: "direct main"
description:
name: quiver
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
@@ -2137,6 +2153,13 @@ packages:
url: "https://github.com/KasemJaffer/receive_sharing_intent.git"
source: git
version: "1.8.1"
rust_lib_photos:
dependency: "direct main"
description:
path: rust_builder
relative: true
source: path
version: "0.0.1"
rxdart:
dependency: transitive
description:
@@ -2309,7 +2332,7 @@ packages:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
version: "0.0.0"
source_gen:
dependency: transitive
description:
@@ -2346,10 +2369,10 @@ packages:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.10.1"
sprintf:
dependency: transitive
description:
@@ -2434,10 +2457,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "1.12.1"
step_progress_indicator:
dependency: "direct main"
description:
@@ -2450,10 +2473,10 @@ packages:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.4"
stream_transform:
dependency: transitive
description:
@@ -2466,10 +2489,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.4.1"
styled_text:
dependency: "direct main"
description:
@@ -2522,34 +2545,34 @@ packages:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
version: "1.2.2"
test:
dependency: "direct dev"
description:
name: test
sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e"
sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
url: "https://pub.dev"
source: hosted
version: "1.25.7"
version: "1.25.15"
test_api:
dependency: transitive
description:
name: test_api
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev"
source: hosted
version: "0.7.2"
version: "0.7.4"
test_core:
dependency: transitive
description:
name: test_core
sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696"
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
url: "https://pub.dev"
source: hosted
version: "0.6.4"
version: "0.6.8"
thermal:
dependency: "direct main"
description:
@@ -2813,10 +2836,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev"
source: hosted
version: "14.2.5"
version: "14.3.1"
volume_controller:
dependency: transitive
description:
@@ -2877,10 +2900,10 @@ packages:
dependency: transitive
description:
name: webdriver
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.4"
webkit_inspection_protocol:
dependency: transitive
description:
@@ -2977,6 +3000,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.3"
yaml_edit:
dependency: transitive
description:
name: yaml_edit
sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5
url: "https://pub.dev"
source: hosted
version: "2.2.2"
sdks:
dart: ">=3.5.0 <4.0.0"
dart: ">=3.7.0-0 <4.0.0"
flutter: ">=3.24.0"

View File

@@ -95,6 +95,7 @@ dependencies:
flutter_map: ^6.2.0
flutter_map_marker_cluster: ^1.3.6
flutter_password_strength: ^0.1.6
flutter_rust_bridge: 2.11.1
# Do not upgrade this package unless this issue is resolved:
# https://github.com/juliansteenbakker/flutter_secure_storage/issues/870
# On v9.2.4, keys related to lockscreen persist even after reintsall. For context see:
@@ -169,14 +170,20 @@ dependencies:
photo_view: ^0.14.0
pinput: ^5.0.0
pointycastle: ^3.7.3
pool: ^1.5.1
privacy_screen: # pub.dev is behind
git:
url: https://github.com/eddyuan/privacy_screen.git
ref: 855418e
protobuf: ^3.1.0
provider: ^6.0.0
quiver: ^3.0.1
receive_sharing_intent: # pub.dev is behind
git:
url: https://github.com/KasemJaffer/receive_sharing_intent.git
ref: 2cea396
rust_lib_photos:
path: rust_builder
screenshot: ^3.0.0
scrollable_positioned_list: ^0.3.5
sentry: ^8.14.1
@@ -257,9 +264,7 @@ flutter_intl:
dev_dependencies:
build_runner: ^2.4.7
flutter_driver:
sdk: flutter
flutter_launcher_icons: ^0.13.1
ffigen: ^15.0.0
flutter_lints: ^3.0.1
flutter_native_splash: ^2.4.4
flutter_test:

1
mobile/apps/photos/rust/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

1656
mobile/apps/photos/rust/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
[package]
name = "rust_lib_photos"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "staticlib"]
[dependencies]
flutter_rust_bridge = "=2.11.1"
image = "0.25.4"
resize = "0.8.7"
rgb = "0.8.50"
bytemuck = "1.16.0"

View File

@@ -0,0 +1,151 @@
use image::ImageBuffer;
use resize::{px::RGB, Pixel::RGB8, Type::Lanczos3, Type::Mitchell};
use rgb::FromSlice;
pub fn process_image_ml_from_path(
image_path: &str,
) -> (
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
) {
// Load the image from the path (~200ms)
let img: image::DynamicImage = image::open(image_path).expect("Failed to open image");
// Process the image
let results = process_image_ml(img);
results
}
pub fn process_image_ml_from_data(
rgba_data: Vec<u8>,
width: u32,
height: u32,
) -> (
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
) {
// Load the image from the data
let img = image::DynamicImage::from(
ImageBuffer::<image::Rgb<u8>, _>::from_raw(width, height, rgba_data)
.expect("Failed to create image buffer"),
);
// Process the image
let results = process_image_ml(img);
results
}
fn process_image_ml(
img: image::DynamicImage,
) -> (
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
) {
// Get dimensions for resized images (0ms)
let (width, height) = (img.width() as usize, img.height() as usize);
let scale_face = f32::min(640.0 / width as f32, 640.0 / height as f32);
let scale_clip = f32::max(256.0 / width as f32, 256.0 / height as f32);
let (new_width_face, new_height_face) = (
f32::round(width as f32 * scale_face) as usize,
f32::round(height as f32 * scale_face) as usize,
);
let (new_width_clip, new_height_clip) = (
f32::round(width as f32 * scale_clip) as usize,
f32::round(height as f32 * scale_clip) as usize,
);
let mut interpolation_face = Lanczos3;
if scale_face > 1.0 {
interpolation_face = Mitchell;
}
let mut interpolation_clip = Lanczos3;
if scale_clip > 1.0 {
interpolation_clip = Mitchell;
}
// Convert image to RGB8 (~150ms)
let rgba_decoded = img.to_rgba8().to_vec();
let rgb_img = img.into_rgb8();
// Convert RGB8 to Vec<RGB> (~30ms)
let rgb_vec = rgb_img.to_vec();
// Create resizer (~20ms)
let mut resizer_face = resize::new(
width,
height,
new_width_face,
new_height_face,
RGB8,
interpolation_face,
)
.unwrap();
let mut resizer_clip = resize::new(
width,
height,
new_width_clip,
new_height_clip,
RGB8,
interpolation_clip,
)
.unwrap();
// Create buffer for resized image (~120ms)
let mut dst_face = vec![RGB::new(0, 0, 0); new_width_face * new_height_face];
let mut dst_clip = vec![RGB::new(0, 0, 0); new_width_clip * new_height_clip];
// Resize the image (~120ms)
resizer_face
.resize(rgb_vec.as_rgb(), &mut dst_face)
.unwrap();
resizer_clip
.resize(rgb_vec.as_rgb(), &mut dst_clip)
.unwrap();
// Return resized images as Vec<u8> (~120ms)
let mut result_face = Vec::with_capacity(new_width_face * new_height_face * 3);
for pixel in dst_face {
result_face.push(pixel.r);
result_face.push(pixel.g);
result_face.push(pixel.b);
}
let mut result_clip = Vec::with_capacity(new_width_clip * new_height_clip * 3);
for pixel in dst_clip {
result_clip.push(pixel.r);
result_clip.push(pixel.g);
result_clip.push(pixel.b);
}
(
rgba_decoded,
height,
width,
result_face,
new_height_face,
new_width_face,
result_clip,
new_height_clip,
new_width_clip,
)
}

View File

@@ -0,0 +1,2 @@
pub mod simple;
pub mod image_processing;

View File

@@ -0,0 +1,10 @@
#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
pub fn greet(name: String) -> String {
format!("Hello, {name}!")
}
#[flutter_rust_bridge::frb(init)]
pub fn init_app() {
// Default utilities - feel free to customize
flutter_rust_bridge::setup_default_user_utils();
}

View File

@@ -0,0 +1,517 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
#![allow(
non_camel_case_types,
unused,
non_snake_case,
clippy::needless_return,
clippy::redundant_closure_call,
clippy::redundant_closure,
clippy::useless_conversion,
clippy::unit_arg,
clippy::unused_unit,
clippy::double_parens,
clippy::let_and_return,
clippy::too_many_arguments,
clippy::match_single_binding,
clippy::clone_on_copy,
clippy::let_unit_value,
clippy::deref_addrof,
clippy::explicit_auto_deref,
clippy::borrow_deref_ref,
clippy::needless_borrow
)]
// Section: imports
use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
use flutter_rust_bridge::{Handler, IntoIntoDart};
// Section: boilerplate
flutter_rust_bridge::frb_generated_boilerplate!(
default_stream_sink_codec = DcoCodec,
default_rust_opaque = RustOpaqueNom,
default_rust_auto_opaque = RustAutoOpaqueNom,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1140226238;
// Section: executor
flutter_rust_bridge::frb_generated_default_handler!();
// Section: wire_funcs
fn wire__crate__api__simple__greet_impl(
name: impl CstDecode<String>,
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartDco {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::<flutter_rust_bridge::for_generated::DcoCodec, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "greet",
port: None,
mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync,
},
move || {
let api_name = name.cst_decode();
transform_result_dco::<_, _, ()>((move || {
let output_ok = Result::<_, ()>::Ok(crate::api::simple::greet(api_name))?;
Ok(output_ok)
})())
},
)
}
fn wire__crate__api__simple__init_app_impl(port_: flutter_rust_bridge::for_generated::MessagePort) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "init_app",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
move |context| {
transform_result_dco::<_, _, ()>((move || {
let output_ok = Result::<_, ()>::Ok({
crate::api::simple::init_app();
})?;
Ok(output_ok)
})())
}
},
)
}
fn wire__crate__api__image_processing__process_image_ml_from_data_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
rgba_data: impl CstDecode<Vec<u8>>,
width: impl CstDecode<u32>,
height: impl CstDecode<u32>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "process_image_ml_from_data",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_rgba_data = rgba_data.cst_decode();
let api_width = width.cst_decode();
let api_height = height.cst_decode();
move |context| {
transform_result_dco::<_, _, ()>((move || {
let output_ok = Result::<_, ()>::Ok(
crate::api::image_processing::process_image_ml_from_data(
api_rgba_data,
api_width,
api_height,
),
)?;
Ok(output_ok)
})())
}
},
)
}
fn wire__crate__api__image_processing__process_image_ml_from_path_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
image_path: impl CstDecode<String>,
) {
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
flutter_rust_bridge::for_generated::TaskInfo {
debug_name: "process_image_ml_from_path",
port: Some(port_),
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
},
move || {
let api_image_path = image_path.cst_decode();
move |context| {
transform_result_dco::<_, _, ()>((move || {
let output_ok = Result::<_, ()>::Ok(
crate::api::image_processing::process_image_ml_from_path(&api_image_path),
)?;
Ok(output_ok)
})())
}
},
)
}
// Section: dart2rust
impl CstDecode<u32> for u32 {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> u32 {
self
}
}
impl CstDecode<u8> for u8 {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> u8 {
self
}
}
impl CstDecode<usize> for usize {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> usize {
self
}
}
impl SseDecode for String {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut inner = <Vec<u8>>::sse_decode(deserializer);
return String::from_utf8(inner).unwrap();
}
}
impl SseDecode for Vec<u8> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut len_ = <i32>::sse_decode(deserializer);
let mut ans_ = vec![];
for idx_ in 0..len_ {
ans_.push(<u8>::sse_decode(deserializer));
}
return ans_;
}
}
impl SseDecode
for (
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
)
{
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
let mut var_field0 = <Vec<u8>>::sse_decode(deserializer);
let mut var_field1 = <usize>::sse_decode(deserializer);
let mut var_field2 = <usize>::sse_decode(deserializer);
let mut var_field3 = <Vec<u8>>::sse_decode(deserializer);
let mut var_field4 = <usize>::sse_decode(deserializer);
let mut var_field5 = <usize>::sse_decode(deserializer);
let mut var_field6 = <Vec<u8>>::sse_decode(deserializer);
let mut var_field7 = <usize>::sse_decode(deserializer);
let mut var_field8 = <usize>::sse_decode(deserializer);
return (
var_field0, var_field1, var_field2, var_field3, var_field4, var_field5, var_field6,
var_field7, var_field8,
);
}
}
impl SseDecode for u32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_u32::<NativeEndian>().unwrap()
}
}
impl SseDecode for u8 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_u8().unwrap()
}
}
impl SseDecode for () {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {}
}
impl SseDecode for usize {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_u64::<NativeEndian>().unwrap() as _
}
}
impl SseDecode for i32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_i32::<NativeEndian>().unwrap()
}
}
impl SseDecode for bool {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
deserializer.cursor.read_u8().unwrap() != 0
}
}
fn pde_ffi_dispatcher_primary_impl(
func_id: i32,
port: flutter_rust_bridge::for_generated::MessagePort,
ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len: i32,
data_len: i32,
) {
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
match func_id {
_ => unreachable!(),
}
}
fn pde_ffi_dispatcher_sync_impl(
func_id: i32,
ptr: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
rust_vec_len: i32,
data_len: i32,
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
// Codec=Pde (Serialization + dispatch), see doc to use other codecs
match func_id {
_ => unreachable!(),
}
}
// Section: rust2dart
impl SseEncode for String {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<Vec<u8>>::sse_encode(self.into_bytes(), serializer);
}
}
impl SseEncode for Vec<u8> {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<i32>::sse_encode(self.len() as _, serializer);
for item in self {
<u8>::sse_encode(item, serializer);
}
}
}
impl SseEncode
for (
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
Vec<u8>,
usize,
usize,
)
{
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
<Vec<u8>>::sse_encode(self.0, serializer);
<usize>::sse_encode(self.1, serializer);
<usize>::sse_encode(self.2, serializer);
<Vec<u8>>::sse_encode(self.3, serializer);
<usize>::sse_encode(self.4, serializer);
<usize>::sse_encode(self.5, serializer);
<Vec<u8>>::sse_encode(self.6, serializer);
<usize>::sse_encode(self.7, serializer);
<usize>::sse_encode(self.8, serializer);
}
}
impl SseEncode for u32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_u32::<NativeEndian>(self).unwrap();
}
}
impl SseEncode for u8 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_u8(self).unwrap();
}
}
impl SseEncode for () {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {}
}
impl SseEncode for usize {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer
.cursor
.write_u64::<NativeEndian>(self as _)
.unwrap();
}
}
impl SseEncode for i32 {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_i32::<NativeEndian>(self).unwrap();
}
}
impl SseEncode for bool {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
serializer.cursor.write_u8(self as _).unwrap();
}
}
#[cfg(not(target_family = "wasm"))]
mod io {
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// Section: imports
use super::*;
use flutter_rust_bridge::for_generated::byteorder::{
NativeEndian, ReadBytesExt, WriteBytesExt,
};
use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, Lockable};
use flutter_rust_bridge::{Handler, IntoIntoDart};
// Section: boilerplate
flutter_rust_bridge::frb_generated_boilerplate_io!();
// Section: dart2rust
impl CstDecode<String> for *mut wire_cst_list_prim_u_8_strict {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> String {
let vec: Vec<u8> = self.cst_decode();
String::from_utf8(vec).unwrap()
}
}
impl CstDecode<Vec<u8>> for *mut wire_cst_list_prim_u_8_loose {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<u8> {
unsafe {
let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self);
flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len)
}
}
}
impl CstDecode<Vec<u8>> for *mut wire_cst_list_prim_u_8_strict {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> Vec<u8> {
unsafe {
let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self);
flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len)
}
}
}
impl CstDecode<(Vec<u8>,usize,usize,Vec<u8>,usize,usize,Vec<u8>,usize,usize,)> for wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize {
// Codec=Cst (C-struct based), see doc to use other codecs
fn cst_decode(self) -> (Vec<u8>,usize,usize,Vec<u8>,usize,usize,Vec<u8>,usize,usize,) {
(self.field0.cst_decode(),self.field1.cst_decode(),self.field2.cst_decode(),self.field3.cst_decode(),self.field4.cst_decode(),self.field5.cst_decode(),self.field6.cst_decode(),self.field7.cst_decode(),self.field8.cst_decode(),)
}
}
impl NewWithNullPtr for wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize {
fn new_with_null_ptr() -> Self {
Self { field0: core::ptr::null_mut(),
field1: Default::default(),
field2: Default::default(),
field3: core::ptr::null_mut(),
field4: Default::default(),
field5: Default::default(),
field6: core::ptr::null_mut(),
field7: Default::default(),
field8: Default::default(), }
}
}
impl Default for wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize {
fn default() -> Self {
Self::new_with_null_ptr()
}
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_photos_wire__crate__api__simple__greet(
name: *mut wire_cst_list_prim_u_8_strict,
) -> flutter_rust_bridge::for_generated::WireSyncRust2DartDco {
wire__crate__api__simple__greet_impl(name)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_photos_wire__crate__api__simple__init_app(port_: i64) {
wire__crate__api__simple__init_app_impl(port_)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_data(
port_: i64,
rgba_data: *mut wire_cst_list_prim_u_8_loose,
width: u32,
height: u32,
) {
wire__crate__api__image_processing__process_image_ml_from_data_impl(
port_, rgba_data, width, height,
)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_path(
port_: i64,
image_path: *mut wire_cst_list_prim_u_8_strict,
) {
wire__crate__api__image_processing__process_image_ml_from_path_impl(port_, image_path)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_photos_cst_new_list_prim_u_8_loose(
len: i32,
) -> *mut wire_cst_list_prim_u_8_loose {
let ans = wire_cst_list_prim_u_8_loose {
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(Default::default(), len),
len,
};
flutter_rust_bridge::for_generated::new_leak_box_ptr(ans)
}
#[unsafe(no_mangle)]
pub extern "C" fn frbgen_photos_cst_new_list_prim_u_8_strict(
len: i32,
) -> *mut wire_cst_list_prim_u_8_strict {
let ans = wire_cst_list_prim_u_8_strict {
ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr(Default::default(), len),
len,
};
flutter_rust_bridge::for_generated::new_leak_box_ptr(ans)
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_prim_u_8_loose {
ptr: *mut u8,
len: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_list_prim_u_8_strict {
ptr: *mut u8,
len: i32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
{
field0: *mut wire_cst_list_prim_u_8_strict,
field1: usize,
field2: usize,
field3: *mut wire_cst_list_prim_u_8_strict,
field4: usize,
field5: usize,
field6: *mut wire_cst_list_prim_u_8_strict,
field7: usize,
field8: usize,
}
}
#[cfg(not(target_family = "wasm"))]
pub use io::*;

View File

@@ -0,0 +1,2 @@
pub mod api;
mod frb_generated;

View File

@@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/

View File

@@ -0,0 +1 @@
Please ignore this folder, which is just glue to build Rust with Flutter.

View File

@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

View File

@@ -0,0 +1,56 @@
// The Android Gradle Plugin builds the native code with the Android NDK.
group 'com.flutter_rust_bridge.rust_lib_photos'
version '1.0'
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
// The Android Gradle Plugin knows how to build native code with the NDK.
classpath 'com.android.tools.build:gradle:7.3.0'
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
android {
if (project.android.hasProperty("namespace")) {
namespace 'com.flutter_rust_bridge.rust_lib_photos'
}
// Bumping the plugin compileSdkVersion requires all clients of this plugin
// to bump the version in their app.
compileSdkVersion 33
// Use the NDK version
// declared in /android/app/build.gradle file of the Flutter project.
// Replace it with a version number if this plugin requires a specfic NDK version.
// (e.g. ndkVersion "23.1.7779620")
ndkVersion android.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 19
}
}
apply from: "../cargokit/gradle/plugin.gradle"
cargokit {
manifestDir = "../../rust"
libname = "rust_lib_photos"
}

View File

@@ -0,0 +1 @@
rootProject.name = 'rust_lib_photos'

View File

@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.flutter_rust_bridge.rust_lib_photos">
</manifest>

View File

@@ -0,0 +1,4 @@
target
.dart_tool
*.iml
!pubspec.lock

View File

@@ -0,0 +1,11 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
Experimental repository to provide glue for seamlessly integrating cargo build
with flutter plugins and packages.
See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/
for a tutorial on how to use Cargokit.
Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin.

View File

@@ -0,0 +1,58 @@
#!/bin/sh
set -e
BASEDIR=$(dirname "$0")
# Workaround for https://github.com/dart-lang/pub/issues/4010
BASEDIR=$(cd "$BASEDIR" ; pwd -P)
# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project
NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"`
export PATH=${NEW_PATH%?} # remove trailing :
env
# Platform name (macosx, iphoneos, iphonesimulator)
export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME
# Arctive architectures (arm64, armv7, x86_64), space separated.
export CARGOKIT_DARWIN_ARCHS=$ARCHS
# Current build configuration (Debug, Release)
export CARGOKIT_CONFIGURATION=$CONFIGURATION
# Path to directory containing Cargo.toml.
export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1
# Temporary directory for build artifacts.
export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR
# Output directory for final artifacts.
export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME
# Directory to store built tool artifacts.
export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool
# Directory inside root project. Not necessarily the top level directory of root project.
export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT
FLUTTER_EXPORT_BUILD_ENVIRONMENT=(
"$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS
"$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS
)
for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}"
do
if [[ -f "$path" ]]; then
source "$path"
fi
done
sh "$BASEDIR/run_build_tool.sh" build-pod "$@"
# Make a symlink from built framework to phony file, which will be used as input to
# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate
# attribute on custom build phase)
ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony"
ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out"

View File

@@ -0,0 +1,5 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
A sample command-line application with an entrypoint in `bin/`, library code
in `lib/`, and example unit test in `test/`.

View File

@@ -0,0 +1,34 @@
# This is copied from Cargokit (which is the official way to use it currently)
# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
linter:
rules:
- prefer_relative_imports
- directives_ordering
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,8 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'package:build_tool/build_tool.dart' as build_tool;
void main(List<String> arguments) {
build_tool.runMain(arguments);
}

View File

@@ -0,0 +1,8 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'src/build_tool.dart' as build_tool;
Future<void> runMain(List<String> args) async {
return build_tool.runMain(args);
}

View File

@@ -0,0 +1,195 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'dart:isolate';
import 'dart:math' as math;
import 'package:collection/collection.dart';
import 'package:path/path.dart' as path;
import 'package:version/version.dart';
import 'target.dart';
import 'util.dart';
class AndroidEnvironment {
AndroidEnvironment({
required this.sdkPath,
required this.ndkVersion,
required this.minSdkVersion,
required this.targetTempDir,
required this.target,
});
static void clangLinkerWrapper(List<String> args) {
final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG'];
if (clang == null) {
throw Exception(
"cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var");
}
final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET'];
if (target == null) {
throw Exception(
"cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var");
}
runCommand(clang, [
target,
...args,
]);
}
/// Full path to Android SDK.
final String sdkPath;
/// Full version of Android NDK.
final String ndkVersion;
/// Minimum supported SDK version.
final int minSdkVersion;
/// Target directory for build artifacts.
final String targetTempDir;
/// Target being built.
final Target target;
bool ndkIsInstalled() {
final ndkPath = path.join(sdkPath, 'ndk', ndkVersion);
final ndkPackageXml = File(path.join(ndkPath, 'package.xml'));
return ndkPackageXml.existsSync();
}
void installNdk({
required String javaHome,
}) {
final sdkManagerExtension = Platform.isWindows ? '.bat' : '';
final sdkManager = path.join(
sdkPath,
'cmdline-tools',
'latest',
'bin',
'sdkmanager$sdkManagerExtension',
);
log.info('Installing NDK $ndkVersion');
runCommand(sdkManager, [
'--install',
'ndk;$ndkVersion',
], environment: {
'JAVA_HOME': javaHome,
});
}
Future<Map<String, String>> buildEnvironment() async {
final hostArch = Platform.isMacOS
? "darwin-x86_64"
: (Platform.isLinux ? "linux-x86_64" : "windows-x86_64");
final ndkPath = path.join(sdkPath, 'ndk', ndkVersion);
final toolchainPath = path.join(
ndkPath,
'toolchains',
'llvm',
'prebuilt',
hostArch,
'bin',
);
final minSdkVersion =
math.max(target.androidMinSdkVersion!, this.minSdkVersion);
final exe = Platform.isWindows ? '.exe' : '';
final arKey = 'AR_${target.rust}';
final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe']
.map((e) => path.join(toolchainPath, e))
.firstWhereOrNull((element) => File(element).existsSync());
if (arValue == null) {
throw Exception('Failed to find ar for $target in $toolchainPath');
}
final targetArg = '--target=${target.rust}$minSdkVersion';
final ccKey = 'CC_${target.rust}';
final ccValue = path.join(toolchainPath, 'clang$exe');
final cfFlagsKey = 'CFLAGS_${target.rust}';
final cFlagsValue = targetArg;
final cxxKey = 'CXX_${target.rust}';
final cxxValue = path.join(toolchainPath, 'clang++$exe');
final cxxFlagsKey = 'CXXFLAGS_${target.rust}';
final cxxFlagsValue = targetArg;
final linkerKey =
'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase();
final ranlibKey = 'RANLIB_${target.rust}';
final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe');
final ndkVersionParsed = Version.parse(ndkVersion);
final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS';
final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed);
final runRustTool =
Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh';
final packagePath = (await Isolate.resolvePackageUri(
Uri.parse('package:build_tool/buildtool.dart')))!
.toFilePath();
final selfPath = path.canonicalize(path.join(
packagePath,
'..',
'..',
'..',
runRustTool,
));
// Make sure that run_build_tool is working properly even initially launched directly
// through dart run.
final toolTempDir =
Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir;
return {
arKey: arValue,
ccKey: ccValue,
cfFlagsKey: cFlagsValue,
cxxKey: cxxValue,
cxxFlagsKey: cxxFlagsValue,
ranlibKey: ranlibValue,
rustFlagsKey: rustFlagsValue,
linkerKey: selfPath,
// Recognized by main() so we know when we're acting as a wrapper
'_CARGOKIT_NDK_LINK_TARGET': targetArg,
'_CARGOKIT_NDK_LINK_CLANG': ccValue,
'CARGOKIT_TOOL_TEMP_DIR': toolTempDir,
};
}
// Workaround for libgcc missing in NDK23, inspired by cargo-ndk
String _libGccWorkaround(String buildDir, Version ndkVersion) {
final workaroundDir = path.join(
buildDir,
'cargokit',
'libgcc_workaround',
'${ndkVersion.major}',
);
Directory(workaroundDir).createSync(recursive: true);
if (ndkVersion.major >= 23) {
File(path.join(workaroundDir, 'libgcc.a'))
.writeAsStringSync('INPUT(-lunwind)');
} else {
// Other way around, untested, forward libgcc.a from libunwind once Rust
// gets updated for NDK23+.
File(path.join(workaroundDir, 'libunwind.a'))
.writeAsStringSync('INPUT(-lgcc)');
}
var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? '';
if (rustFlags.isNotEmpty) {
rustFlags = '$rustFlags\x1f';
}
rustFlags = '$rustFlags-L\x1f$workaroundDir';
return rustFlags;
}
}

View File

@@ -0,0 +1,266 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:ed25519_edwards/ed25519_edwards.dart';
import 'package:http/http.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'builder.dart';
import 'crate_hash.dart';
import 'options.dart';
import 'precompile_binaries.dart';
import 'rustup.dart';
import 'target.dart';
class Artifact {
/// File system location of the artifact.
final String path;
/// Actual file name that the artifact should have in destination folder.
final String finalFileName;
AritifactType get type {
if (finalFileName.endsWith('.dll') ||
finalFileName.endsWith('.dll.lib') ||
finalFileName.endsWith('.pdb') ||
finalFileName.endsWith('.so') ||
finalFileName.endsWith('.dylib')) {
return AritifactType.dylib;
} else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) {
return AritifactType.staticlib;
} else {
throw Exception('Unknown artifact type for $finalFileName');
}
}
Artifact({
required this.path,
required this.finalFileName,
});
}
final _log = Logger('artifacts_provider');
class ArtifactProvider {
ArtifactProvider({
required this.environment,
required this.userOptions,
});
final BuildEnvironment environment;
final CargokitUserOptions userOptions;
Future<Map<Target, List<Artifact>>> getArtifacts(List<Target> targets) async {
final result = await _getPrecompiledArtifacts(targets);
final pendingTargets = List.of(targets);
pendingTargets.removeWhere((element) => result.containsKey(element));
if (pendingTargets.isEmpty) {
return result;
}
final rustup = Rustup();
for (final target in targets) {
final builder = RustBuilder(target: target, environment: environment);
builder.prepare(rustup);
_log.info('Building ${environment.crateInfo.packageName} for $target');
final targetDir = await builder.build();
// For local build accept both static and dynamic libraries.
final artifactNames = <String>{
...getArtifactNames(
target: target,
libraryName: environment.crateInfo.packageName,
aritifactType: AritifactType.dylib,
remote: false,
),
...getArtifactNames(
target: target,
libraryName: environment.crateInfo.packageName,
aritifactType: AritifactType.staticlib,
remote: false,
)
};
final artifacts = artifactNames
.map((artifactName) => Artifact(
path: path.join(targetDir, artifactName),
finalFileName: artifactName,
))
.where((element) => File(element.path).existsSync())
.toList();
result[target] = artifacts;
}
return result;
}
Future<Map<Target, List<Artifact>>> _getPrecompiledArtifacts(
List<Target> targets) async {
if (userOptions.usePrecompiledBinaries == false) {
_log.info('Precompiled binaries are disabled');
return {};
}
if (environment.crateOptions.precompiledBinaries == null) {
_log.fine('Precompiled binaries not enabled for this crate');
return {};
}
final start = Stopwatch()..start();
final crateHash = CrateHash.compute(environment.manifestDir,
tempStorage: environment.targetTempDir);
_log.fine(
'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms');
final downloadedArtifactsDir =
path.join(environment.targetTempDir, 'precompiled', crateHash);
Directory(downloadedArtifactsDir).createSync(recursive: true);
final res = <Target, List<Artifact>>{};
for (final target in targets) {
final requiredArtifacts = getArtifactNames(
target: target,
libraryName: environment.crateInfo.packageName,
remote: true,
);
final artifactsForTarget = <Artifact>[];
for (final artifact in requiredArtifacts) {
final fileName = PrecompileBinaries.fileName(target, artifact);
final downloadedPath = path.join(downloadedArtifactsDir, fileName);
if (!File(downloadedPath).existsSync()) {
final signatureFileName =
PrecompileBinaries.signatureFileName(target, artifact);
await _tryDownloadArtifacts(
crateHash: crateHash,
fileName: fileName,
signatureFileName: signatureFileName,
finalPath: downloadedPath,
);
}
if (File(downloadedPath).existsSync()) {
artifactsForTarget.add(Artifact(
path: downloadedPath,
finalFileName: artifact,
));
} else {
break;
}
}
// Only provide complete set of artifacts.
if (artifactsForTarget.length == requiredArtifacts.length) {
_log.fine('Found precompiled artifacts for $target');
res[target] = artifactsForTarget;
}
}
return res;
}
static Future<Response> _get(Uri url, {Map<String, String>? headers}) async {
int attempt = 0;
const maxAttempts = 10;
while (true) {
try {
return await get(url, headers: headers);
} on SocketException catch (e) {
// Try to detect reset by peer error and retry.
if (attempt++ < maxAttempts &&
(e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) {
_log.severe(
'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...');
await Future.delayed(Duration(seconds: 1));
continue;
} else {
rethrow;
}
}
}
}
Future<void> _tryDownloadArtifacts({
required String crateHash,
required String fileName,
required String signatureFileName,
required String finalPath,
}) async {
final precompiledBinaries = environment.crateOptions.precompiledBinaries!;
final prefix = precompiledBinaries.uriPrefix;
final url = Uri.parse('$prefix$crateHash/$fileName');
final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName');
_log.fine('Downloading signature from $signatureUrl');
final signature = await _get(signatureUrl);
if (signature.statusCode == 404) {
_log.warning(
'Precompiled binaries not available for crate hash $crateHash ($fileName)');
return;
}
if (signature.statusCode != 200) {
_log.severe(
'Failed to download signature $signatureUrl: status ${signature.statusCode}');
return;
}
_log.fine('Downloading binary from $url');
final res = await _get(url);
if (res.statusCode != 200) {
_log.severe('Failed to download binary $url: status ${res.statusCode}');
return;
}
if (verify(
precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) {
File(finalPath).writeAsBytesSync(res.bodyBytes);
} else {
_log.shout('Signature verification failed! Ignoring binary.');
}
}
}
enum AritifactType {
staticlib,
dylib,
}
AritifactType artifactTypeForTarget(Target target) {
if (target.darwinPlatform != null) {
return AritifactType.staticlib;
} else {
return AritifactType.dylib;
}
}
List<String> getArtifactNames({
required Target target,
required String libraryName,
required bool remote,
AritifactType? aritifactType,
}) {
aritifactType ??= artifactTypeForTarget(target);
if (target.darwinArch != null) {
if (aritifactType == AritifactType.staticlib) {
return ['lib$libraryName.a'];
} else {
return ['lib$libraryName.dylib'];
}
} else if (target.rust.contains('-windows-')) {
if (aritifactType == AritifactType.staticlib) {
return ['$libraryName.lib'];
} else {
return [
'$libraryName.dll',
'$libraryName.dll.lib',
if (!remote) '$libraryName.pdb'
];
}
} else if (target.rust.contains('-linux-')) {
if (aritifactType == AritifactType.staticlib) {
return ['lib$libraryName.a'];
} else {
return ['lib$libraryName.so'];
}
} else {
throw Exception("Unsupported target: ${target.rust}");
}
}

View File

@@ -0,0 +1,40 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:path/path.dart' as path;
import 'artifacts_provider.dart';
import 'builder.dart';
import 'environment.dart';
import 'options.dart';
import 'target.dart';
class BuildCMake {
final CargokitUserOptions userOptions;
BuildCMake({required this.userOptions});
Future<void> build() async {
final targetPlatform = Environment.targetPlatform;
final target = Target.forFlutterName(Environment.targetPlatform);
if (target == null) {
throw Exception("Unknown target platform: $targetPlatform");
}
final environment = BuildEnvironment.fromEnvironment(isAndroid: false);
final provider =
ArtifactProvider(environment: environment, userOptions: userOptions);
final artifacts = await provider.getArtifacts([target]);
final libs = artifacts[target]!;
for (final lib in libs) {
if (lib.type == AritifactType.dylib) {
File(lib.path)
.copySync(path.join(Environment.outputDir, lib.finalFileName));
}
}
}
}

View File

@@ -0,0 +1,49 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'artifacts_provider.dart';
import 'builder.dart';
import 'environment.dart';
import 'options.dart';
import 'target.dart';
final log = Logger('build_gradle');
class BuildGradle {
BuildGradle({required this.userOptions});
final CargokitUserOptions userOptions;
Future<void> build() async {
final targets = Environment.targetPlatforms.map((arch) {
final target = Target.forFlutterName(arch);
if (target == null) {
throw Exception(
"Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}");
}
return target;
}).toList();
final environment = BuildEnvironment.fromEnvironment(isAndroid: true);
final provider =
ArtifactProvider(environment: environment, userOptions: userOptions);
final artifacts = await provider.getArtifacts(targets);
for (final target in targets) {
final libs = artifacts[target]!;
final outputDir = path.join(Environment.outputDir, target.android!);
Directory(outputDir).createSync(recursive: true);
for (final lib in libs) {
if (lib.type == AritifactType.dylib) {
File(lib.path).copySync(path.join(outputDir, lib.finalFileName));
}
}
}
}
}

View File

@@ -0,0 +1,89 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:path/path.dart' as path;
import 'artifacts_provider.dart';
import 'builder.dart';
import 'environment.dart';
import 'options.dart';
import 'target.dart';
import 'util.dart';
class BuildPod {
BuildPod({required this.userOptions});
final CargokitUserOptions userOptions;
Future<void> build() async {
final targets = Environment.darwinArchs.map((arch) {
final target = Target.forDarwin(
platformName: Environment.darwinPlatformName, darwinAarch: arch);
if (target == null) {
throw Exception(
"Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}");
}
return target;
}).toList();
final environment = BuildEnvironment.fromEnvironment(isAndroid: false);
final provider =
ArtifactProvider(environment: environment, userOptions: userOptions);
final artifacts = await provider.getArtifacts(targets);
void performLipo(String targetFile, Iterable<String> sourceFiles) {
runCommand("lipo", [
'-create',
...sourceFiles,
'-output',
targetFile,
]);
}
final outputDir = Environment.outputDir;
Directory(outputDir).createSync(recursive: true);
final staticLibs = artifacts.values
.expand((element) => element)
.where((element) => element.type == AritifactType.staticlib)
.toList();
final dynamicLibs = artifacts.values
.expand((element) => element)
.where((element) => element.type == AritifactType.dylib)
.toList();
final libName = environment.crateInfo.packageName;
// If there is static lib, use it and link it with pod
if (staticLibs.isNotEmpty) {
final finalTargetFile = path.join(outputDir, "lib$libName.a");
performLipo(finalTargetFile, staticLibs.map((e) => e.path));
} else {
// Otherwise try to replace bundle dylib with our dylib
final bundlePaths = [
'$libName.framework/Versions/A/$libName',
'$libName.framework/$libName',
];
for (final bundlePath in bundlePaths) {
final targetFile = path.join(outputDir, bundlePath);
if (File(targetFile).existsSync()) {
performLipo(targetFile, dynamicLibs.map((e) => e.path));
// Replace absolute id with @rpath one so that it works properly
// when moved to Frameworks.
runCommand("install_name_tool", [
'-id',
'@rpath/$bundlePath',
targetFile,
]);
return;
}
}
throw Exception('Unable to find bundle for dynamic library');
}
}
}

View File

@@ -0,0 +1,271 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:ed25519_edwards/ed25519_edwards.dart';
import 'package:github/github.dart';
import 'package:hex/hex.dart';
import 'package:logging/logging.dart';
import 'android_environment.dart';
import 'build_cmake.dart';
import 'build_gradle.dart';
import 'build_pod.dart';
import 'logging.dart';
import 'options.dart';
import 'precompile_binaries.dart';
import 'target.dart';
import 'util.dart';
import 'verify_binaries.dart';
final log = Logger('build_tool');
abstract class BuildCommand extends Command {
Future<void> runBuildCommand(CargokitUserOptions options);
@override
Future<void> run() async {
final options = CargokitUserOptions.load();
if (options.verboseLogging ||
Platform.environment['CARGOKIT_VERBOSE'] == '1') {
enableVerboseLogging();
}
await runBuildCommand(options);
}
}
class BuildPodCommand extends BuildCommand {
@override
final name = 'build-pod';
@override
final description = 'Build cocoa pod library';
@override
Future<void> runBuildCommand(CargokitUserOptions options) async {
final build = BuildPod(userOptions: options);
await build.build();
}
}
class BuildGradleCommand extends BuildCommand {
@override
final name = 'build-gradle';
@override
final description = 'Build android library';
@override
Future<void> runBuildCommand(CargokitUserOptions options) async {
final build = BuildGradle(userOptions: options);
await build.build();
}
}
class BuildCMakeCommand extends BuildCommand {
@override
final name = 'build-cmake';
@override
final description = 'Build CMake library';
@override
Future<void> runBuildCommand(CargokitUserOptions options) async {
final build = BuildCMake(userOptions: options);
await build.build();
}
}
class GenKeyCommand extends Command {
@override
final name = 'gen-key';
@override
final description = 'Generate key pair for signing precompiled binaries';
@override
void run() {
final kp = generateKey();
final private = HEX.encode(kp.privateKey.bytes);
final public = HEX.encode(kp.publicKey.bytes);
print("Private Key: $private");
print("Public Key: $public");
}
}
class PrecompileBinariesCommand extends Command {
PrecompileBinariesCommand() {
argParser
..addOption(
'repository',
mandatory: true,
help: 'Github repository slug in format owner/name',
)
..addOption(
'manifest-dir',
mandatory: true,
help: 'Directory containing Cargo.toml',
)
..addMultiOption('target',
help: 'Rust target triple of artifact to build.\n'
'Can be specified multiple times or omitted in which case\n'
'all targets for current platform will be built.')
..addOption(
'android-sdk-location',
help: 'Location of Android SDK (if available)',
)
..addOption(
'android-ndk-version',
help: 'Android NDK version (if available)',
)
..addOption(
'android-min-sdk-version',
help: 'Android minimum rquired version (if available)',
)
..addOption(
'temp-dir',
help: 'Directory to store temporary build artifacts',
)
..addFlag(
"verbose",
abbr: "v",
defaultsTo: false,
help: "Enable verbose logging",
);
}
@override
final name = 'precompile-binaries';
@override
final description = 'Prebuild and upload binaries\n'
'Private key must be passed through PRIVATE_KEY environment variable. '
'Use gen_key through generate priave key.\n'
'Github token must be passed as GITHUB_TOKEN environment variable.\n';
@override
Future<void> run() async {
final verbose = argResults!['verbose'] as bool;
if (verbose) {
enableVerboseLogging();
}
final privateKeyString = Platform.environment['PRIVATE_KEY'];
if (privateKeyString == null) {
throw ArgumentError('Missing PRIVATE_KEY environment variable');
}
final githubToken = Platform.environment['GITHUB_TOKEN'];
if (githubToken == null) {
throw ArgumentError('Missing GITHUB_TOKEN environment variable');
}
final privateKey = HEX.decode(privateKeyString);
if (privateKey.length != 64) {
throw ArgumentError('Private key must be 64 bytes long');
}
final manifestDir = argResults!['manifest-dir'] as String;
if (!Directory(manifestDir).existsSync()) {
throw ArgumentError('Manifest directory does not exist: $manifestDir');
}
String? androidMinSdkVersionString =
argResults!['android-min-sdk-version'] as String?;
int? androidMinSdkVersion;
if (androidMinSdkVersionString != null) {
androidMinSdkVersion = int.tryParse(androidMinSdkVersionString);
if (androidMinSdkVersion == null) {
throw ArgumentError(
'Invalid android-min-sdk-version: $androidMinSdkVersionString');
}
}
final targetStrigns = argResults!['target'] as List<String>;
final targets = targetStrigns.map((target) {
final res = Target.forRustTriple(target);
if (res == null) {
throw ArgumentError('Invalid target: $target');
}
return res;
}).toList(growable: false);
final precompileBinaries = PrecompileBinaries(
privateKey: PrivateKey(privateKey),
githubToken: githubToken,
manifestDir: manifestDir,
repositorySlug: RepositorySlug.full(argResults!['repository'] as String),
targets: targets,
androidSdkLocation: argResults!['android-sdk-location'] as String?,
androidNdkVersion: argResults!['android-ndk-version'] as String?,
androidMinSdkVersion: androidMinSdkVersion,
tempDir: argResults!['temp-dir'] as String?,
);
await precompileBinaries.run();
}
}
class VerifyBinariesCommand extends Command {
VerifyBinariesCommand() {
argParser.addOption(
'manifest-dir',
mandatory: true,
help: 'Directory containing Cargo.toml',
);
}
@override
final name = "verify-binaries";
@override
final description = 'Verifies published binaries\n'
'Checks whether there is a binary published for each targets\n'
'and checks the signature.';
@override
Future<void> run() async {
final manifestDir = argResults!['manifest-dir'] as String;
final verifyBinaries = VerifyBinaries(
manifestDir: manifestDir,
);
await verifyBinaries.run();
}
}
Future<void> runMain(List<String> args) async {
try {
// Init logging before options are loaded
initLogging();
if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) {
return AndroidEnvironment.clangLinkerWrapper(args);
}
final runner = CommandRunner('build_tool', 'Cargokit built_tool')
..addCommand(BuildPodCommand())
..addCommand(BuildGradleCommand())
..addCommand(BuildCMakeCommand())
..addCommand(GenKeyCommand())
..addCommand(PrecompileBinariesCommand())
..addCommand(VerifyBinariesCommand());
await runner.run(args);
} on ArgumentError catch (e) {
stderr.writeln(e.toString());
exit(1);
} catch (e, s) {
log.severe(kDoubleSeparator);
log.severe('Cargokit BuildTool failed with error:');
log.severe(kSeparator);
log.severe(e);
// This tells user to install Rust, there's no need to pollute the log with
// stack trace.
if (e is! RustupNotFoundException) {
log.severe(kSeparator);
log.severe(s);
log.severe(kSeparator);
log.severe('BuildTool arguments: $args');
}
log.severe(kDoubleSeparator);
exit(1);
}
}

View File

@@ -0,0 +1,198 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'android_environment.dart';
import 'cargo.dart';
import 'environment.dart';
import 'options.dart';
import 'rustup.dart';
import 'target.dart';
import 'util.dart';
final _log = Logger('builder');
enum BuildConfiguration {
debug,
release,
profile,
}
extension on BuildConfiguration {
bool get isDebug => this == BuildConfiguration.debug;
String get rustName => switch (this) {
BuildConfiguration.debug => 'debug',
BuildConfiguration.release => 'release',
BuildConfiguration.profile => 'release',
};
}
class BuildException implements Exception {
final String message;
BuildException(this.message);
@override
String toString() {
return 'BuildException: $message';
}
}
class BuildEnvironment {
final BuildConfiguration configuration;
final CargokitCrateOptions crateOptions;
final String targetTempDir;
final String manifestDir;
final CrateInfo crateInfo;
final bool isAndroid;
final String? androidSdkPath;
final String? androidNdkVersion;
final int? androidMinSdkVersion;
final String? javaHome;
BuildEnvironment({
required this.configuration,
required this.crateOptions,
required this.targetTempDir,
required this.manifestDir,
required this.crateInfo,
required this.isAndroid,
this.androidSdkPath,
this.androidNdkVersion,
this.androidMinSdkVersion,
this.javaHome,
});
static BuildConfiguration parseBuildConfiguration(String value) {
// XCode configuration adds the flavor to configuration name.
final firstSegment = value.split('-').first;
final buildConfiguration = BuildConfiguration.values.firstWhereOrNull(
(e) => e.name == firstSegment,
);
if (buildConfiguration == null) {
_log.warning('Unknown build configuraiton $value, will assume release');
return BuildConfiguration.release;
}
return buildConfiguration;
}
static BuildEnvironment fromEnvironment({
required bool isAndroid,
}) {
final buildConfiguration =
parseBuildConfiguration(Environment.configuration);
final manifestDir = Environment.manifestDir;
final crateOptions = CargokitCrateOptions.load(
manifestDir: manifestDir,
);
final crateInfo = CrateInfo.load(manifestDir);
return BuildEnvironment(
configuration: buildConfiguration,
crateOptions: crateOptions,
targetTempDir: Environment.targetTempDir,
manifestDir: manifestDir,
crateInfo: crateInfo,
isAndroid: isAndroid,
androidSdkPath: isAndroid ? Environment.sdkPath : null,
androidNdkVersion: isAndroid ? Environment.ndkVersion : null,
androidMinSdkVersion:
isAndroid ? int.parse(Environment.minSdkVersion) : null,
javaHome: isAndroid ? Environment.javaHome : null,
);
}
}
class RustBuilder {
final Target target;
final BuildEnvironment environment;
RustBuilder({
required this.target,
required this.environment,
});
void prepare(
Rustup rustup,
) {
final toolchain = _toolchain;
if (rustup.installedTargets(toolchain) == null) {
rustup.installToolchain(toolchain);
}
if (toolchain == 'nightly') {
rustup.installRustSrcForNightly();
}
if (!rustup.installedTargets(toolchain)!.contains(target.rust)) {
rustup.installTarget(target.rust, toolchain: toolchain);
}
}
CargoBuildOptions? get _buildOptions =>
environment.crateOptions.cargo[environment.configuration];
String get _toolchain => _buildOptions?.toolchain.name ?? 'stable';
/// Returns the path of directory containing build artifacts.
Future<String> build() async {
final extraArgs = _buildOptions?.flags ?? [];
final manifestPath = path.join(environment.manifestDir, 'Cargo.toml');
runCommand(
'rustup',
[
'run',
_toolchain,
'cargo',
'build',
...extraArgs,
'--manifest-path',
manifestPath,
'-p',
environment.crateInfo.packageName,
if (!environment.configuration.isDebug) '--release',
'--target',
target.rust,
'--target-dir',
environment.targetTempDir,
],
environment: await _buildEnvironment(),
);
return path.join(
environment.targetTempDir,
target.rust,
environment.configuration.rustName,
);
}
Future<Map<String, String>> _buildEnvironment() async {
if (target.android == null) {
return {};
} else {
final sdkPath = environment.androidSdkPath;
final ndkVersion = environment.androidNdkVersion;
final minSdkVersion = environment.androidMinSdkVersion;
if (sdkPath == null) {
throw BuildException('androidSdkPath is not set');
}
if (ndkVersion == null) {
throw BuildException('androidNdkVersion is not set');
}
if (minSdkVersion == null) {
throw BuildException('androidMinSdkVersion is not set');
}
final env = AndroidEnvironment(
sdkPath: sdkPath,
ndkVersion: ndkVersion,
minSdkVersion: minSdkVersion,
targetTempDir: environment.targetTempDir,
target: target,
);
if (!env.ndkIsInstalled() && environment.javaHome != null) {
env.installNdk(javaHome: environment.javaHome!);
}
return env.buildEnvironment();
}
}
}

View File

@@ -0,0 +1,48 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:toml/toml.dart';
class ManifestException {
ManifestException(this.message, {required this.fileName});
final String? fileName;
final String message;
@override
String toString() {
if (fileName != null) {
return 'Failed to parse package manifest at $fileName: $message';
} else {
return 'Failed to parse package manifest: $message';
}
}
}
class CrateInfo {
CrateInfo({required this.packageName});
final String packageName;
static CrateInfo parseManifest(String manifest, {final String? fileName}) {
final toml = TomlDocument.parse(manifest);
final package = toml.toMap()['package'];
if (package == null) {
throw ManifestException('Missing package section', fileName: fileName);
}
final name = package['name'];
if (name == null) {
throw ManifestException('Missing package name', fileName: fileName);
}
return CrateInfo(packageName: name);
}
static CrateInfo load(String manifestDir) {
final manifestFile = File(path.join(manifestDir, 'Cargo.toml'));
final manifest = manifestFile.readAsStringSync();
return parseManifest(manifest, fileName: manifestFile.path);
}
}

View File

@@ -0,0 +1,124 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:path/path.dart' as path;
class CrateHash {
/// Computes a hash uniquely identifying crate content. This takes into account
/// content all all .rs files inside the src directory, as well as Cargo.toml,
/// Cargo.lock, build.rs and cargokit.yaml.
///
/// If [tempStorage] is provided, computed hash is stored in a file in that directory
/// and reused on subsequent calls if the crate content hasn't changed.
static String compute(String manifestDir, {String? tempStorage}) {
return CrateHash._(
manifestDir: manifestDir,
tempStorage: tempStorage,
)._compute();
}
CrateHash._({
required this.manifestDir,
required this.tempStorage,
});
String _compute() {
final files = getFiles();
final tempStorage = this.tempStorage;
if (tempStorage != null) {
final quickHash = _computeQuickHash(files);
final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash'));
quickHashFolder.createSync(recursive: true);
final quickHashFile = File(path.join(quickHashFolder.path, quickHash));
if (quickHashFile.existsSync()) {
return quickHashFile.readAsStringSync();
}
final hash = _computeHash(files);
quickHashFile.writeAsStringSync(hash);
return hash;
} else {
return _computeHash(files);
}
}
/// Computes a quick hash based on files stat (without reading contents). This
/// is used to cache the real hash, which is slower to compute since it involves
/// reading every single file.
String _computeQuickHash(List<File> files) {
final output = AccumulatorSink<Digest>();
final input = sha256.startChunkedConversion(output);
final data = ByteData(8);
for (final file in files) {
input.add(utf8.encode(file.path));
final stat = file.statSync();
data.setUint64(0, stat.size);
input.add(data.buffer.asUint8List());
data.setUint64(0, stat.modified.millisecondsSinceEpoch);
input.add(data.buffer.asUint8List());
}
input.close();
return base64Url.encode(output.events.single.bytes);
}
String _computeHash(List<File> files) {
final output = AccumulatorSink<Digest>();
final input = sha256.startChunkedConversion(output);
void addTextFile(File file) {
// text Files are hashed by lines in case we're dealing with github checkout
// that auto-converts line endings.
final splitter = LineSplitter();
if (file.existsSync()) {
final data = file.readAsStringSync();
final lines = splitter.convert(data);
for (final line in lines) {
input.add(utf8.encode(line));
}
}
}
for (final file in files) {
addTextFile(file);
}
input.close();
final res = output.events.single;
// Truncate to 128bits.
final hash = res.bytes.sublist(0, 16);
return hex.encode(hash);
}
List<File> getFiles() {
final src = Directory(path.join(manifestDir, 'src'));
final files = src
.listSync(recursive: true, followLinks: false)
.whereType<File>()
.toList();
files.sortBy((element) => element.path);
void addFile(String relative) {
final file = File(path.join(manifestDir, relative));
if (file.existsSync()) {
files.add(file);
}
}
addFile('Cargo.toml');
addFile('Cargo.lock');
addFile('build.rs');
addFile('cargokit.yaml');
return files;
}
final String manifestDir;
final String? tempStorage;
}

View File

@@ -0,0 +1,68 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
extension on String {
String resolveSymlink() => File(this).resolveSymbolicLinksSync();
}
class Environment {
/// Current build configuration (debug or release).
static String get configuration =>
_getEnv("CARGOKIT_CONFIGURATION").toLowerCase();
static bool get isDebug => configuration == 'debug';
static bool get isRelease => configuration == 'release';
/// Temporary directory where Rust build artifacts are placed.
static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR");
/// Final output directory where the build artifacts are placed.
static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR');
/// Path to the crate manifest (containing Cargo.toml).
static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR');
/// Directory inside root project. Not necessarily root folder. Symlinks are
/// not resolved on purpose.
static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR');
// Pod
/// Platform name (macosx, iphoneos, iphonesimulator).
static String get darwinPlatformName =>
_getEnv("CARGOKIT_DARWIN_PLATFORM_NAME");
/// List of architectures to build for (arm64, armv7, x86_64).
static List<String> get darwinArchs =>
_getEnv("CARGOKIT_DARWIN_ARCHS").split(' ');
// Gradle
static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION");
static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION");
static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR");
static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME");
static List<String> get targetPlatforms =>
_getEnv("CARGOKIT_TARGET_PLATFORMS").split(',');
// CMAKE
static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM");
static String _getEnv(String key) {
final res = Platform.environment[key];
if (res == null) {
throw Exception("Missing environment variable $key");
}
return res;
}
static String _getEnvPath(String key) {
final res = _getEnv(key);
if (Directory(res).existsSync()) {
return res.resolveSymlink();
} else {
return res;
}
}
}

View File

@@ -0,0 +1,52 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:logging/logging.dart';
const String kSeparator = "--";
const String kDoubleSeparator = "==";
bool _lastMessageWasSeparator = false;
void _log(LogRecord rec) {
final prefix = '${rec.level.name}: ';
final out = rec.level == Level.SEVERE ? stderr : stdout;
if (rec.message == kSeparator) {
if (!_lastMessageWasSeparator) {
out.write(prefix);
out.writeln('-' * 80);
_lastMessageWasSeparator = true;
}
return;
} else if (rec.message == kDoubleSeparator) {
out.write(prefix);
out.writeln('=' * 80);
_lastMessageWasSeparator = true;
return;
}
out.write(prefix);
out.writeln(rec.message);
_lastMessageWasSeparator = false;
}
void initLogging() {
Logger.root.level = Level.INFO;
Logger.root.onRecord.listen((LogRecord rec) {
final lines = rec.message.split('\n');
for (final line in lines) {
if (line.isNotEmpty || lines.length == 1 || line != lines.last) {
_log(LogRecord(
rec.level,
line,
rec.loggerName,
));
}
}
});
}
void enableVerboseLogging() {
Logger.root.level = Level.ALL;
}

View File

@@ -0,0 +1,309 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:ed25519_edwards/ed25519_edwards.dart';
import 'package:hex/hex.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'package:source_span/source_span.dart';
import 'package:yaml/yaml.dart';
import 'builder.dart';
import 'environment.dart';
import 'rustup.dart';
final _log = Logger('options');
/// A class for exceptions that have source span information attached.
class SourceSpanException implements Exception {
// This is a getter so that subclasses can override it.
/// A message describing the exception.
String get message => _message;
final String _message;
// This is a getter so that subclasses can override it.
/// The span associated with this exception.
///
/// This may be `null` if the source location can't be determined.
SourceSpan? get span => _span;
final SourceSpan? _span;
SourceSpanException(this._message, this._span);
/// Returns a string representation of `this`.
///
/// [color] may either be a [String], a [bool], or `null`. If it's a string,
/// it indicates an ANSI terminal color escape that should be used to
/// highlight the span's text. If it's `true`, it indicates that the text
/// should be highlighted using the default color. If it's `false` or `null`,
/// it indicates that the text shouldn't be highlighted.
@override
String toString({Object? color}) {
if (span == null) return message;
return 'Error on ${span!.message(message, color: color)}';
}
}
enum Toolchain {
stable,
beta,
nightly,
}
class CargoBuildOptions {
final Toolchain toolchain;
final List<String> flags;
CargoBuildOptions({
required this.toolchain,
required this.flags,
});
static Toolchain _toolchainFromNode(YamlNode node) {
if (node case YamlScalar(value: String name)) {
final toolchain =
Toolchain.values.firstWhereOrNull((element) => element.name == name);
if (toolchain != null) {
return toolchain;
}
}
throw SourceSpanException(
'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.',
node.span);
}
static CargoBuildOptions parse(YamlNode node) {
if (node is! YamlMap) {
throw SourceSpanException('Cargo options must be a map', node.span);
}
Toolchain toolchain = Toolchain.stable;
List<String> flags = [];
for (final MapEntry(:key, :value) in node.nodes.entries) {
if (key case YamlScalar(value: 'toolchain')) {
toolchain = _toolchainFromNode(value);
} else if (key case YamlScalar(value: 'extra_flags')) {
if (value case YamlList(nodes: List<YamlNode> list)) {
if (list.every((element) {
if (element case YamlScalar(value: String _)) {
return true;
}
return false;
})) {
flags = list.map((e) => e.value as String).toList();
continue;
}
}
throw SourceSpanException(
'Extra flags must be a list of strings', value.span);
} else {
throw SourceSpanException(
'Unknown cargo option type. Must be "toolchain" or "extra_flags".',
key.span);
}
}
return CargoBuildOptions(toolchain: toolchain, flags: flags);
}
}
extension on YamlMap {
/// Map that extracts keys so that we can do map case check on them.
Map<dynamic, YamlNode> get valueMap =>
nodes.map((key, value) => MapEntry(key.value, value));
}
class PrecompiledBinaries {
final String uriPrefix;
final PublicKey publicKey;
PrecompiledBinaries({
required this.uriPrefix,
required this.publicKey,
});
static PublicKey _publicKeyFromHex(String key, SourceSpan? span) {
final bytes = HEX.decode(key);
if (bytes.length != 32) {
throw SourceSpanException(
'Invalid public key. Must be 32 bytes long.', span);
}
return PublicKey(bytes);
}
static PrecompiledBinaries parse(YamlNode node) {
if (node case YamlMap(valueMap: Map<dynamic, YamlNode> map)) {
if (map
case {
'url_prefix': YamlNode urlPrefixNode,
'public_key': YamlNode publicKeyNode,
}) {
final urlPrefix = switch (urlPrefixNode) {
YamlScalar(value: String urlPrefix) => urlPrefix,
_ => throw SourceSpanException(
'Invalid URL prefix value.', urlPrefixNode.span),
};
final publicKey = switch (publicKeyNode) {
YamlScalar(value: String publicKey) =>
_publicKeyFromHex(publicKey, publicKeyNode.span),
_ => throw SourceSpanException(
'Invalid public key value.', publicKeyNode.span),
};
return PrecompiledBinaries(
uriPrefix: urlPrefix,
publicKey: publicKey,
);
}
}
throw SourceSpanException(
'Invalid precompiled binaries value. '
'Expected Map with "url_prefix" and "public_key".',
node.span);
}
}
/// Cargokit options specified for Rust crate.
class CargokitCrateOptions {
CargokitCrateOptions({
this.cargo = const {},
this.precompiledBinaries,
});
final Map<BuildConfiguration, CargoBuildOptions> cargo;
final PrecompiledBinaries? precompiledBinaries;
static CargokitCrateOptions parse(YamlNode node) {
if (node is! YamlMap) {
throw SourceSpanException('Cargokit options must be a map', node.span);
}
final options = <BuildConfiguration, CargoBuildOptions>{};
PrecompiledBinaries? precompiledBinaries;
for (final entry in node.nodes.entries) {
if (entry
case MapEntry(
key: YamlScalar(value: 'cargo'),
value: YamlNode node,
)) {
if (node is! YamlMap) {
throw SourceSpanException('Cargo options must be a map', node.span);
}
for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) {
if (key case YamlScalar(value: String name)) {
final configuration = BuildConfiguration.values
.firstWhereOrNull((element) => element.name == name);
if (configuration != null) {
options[configuration] = CargoBuildOptions.parse(value);
continue;
}
}
throw SourceSpanException(
'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.',
key.span);
}
} else if (entry.key case YamlScalar(value: 'precompiled_binaries')) {
precompiledBinaries = PrecompiledBinaries.parse(entry.value);
} else {
throw SourceSpanException(
'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".',
entry.key.span);
}
}
return CargokitCrateOptions(
cargo: options,
precompiledBinaries: precompiledBinaries,
);
}
static CargokitCrateOptions load({
required String manifestDir,
}) {
final uri = Uri.file(path.join(manifestDir, "cargokit.yaml"));
final file = File.fromUri(uri);
if (file.existsSync()) {
final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri);
return parse(contents);
} else {
return CargokitCrateOptions();
}
}
}
class CargokitUserOptions {
// When Rustup is installed always build locally unless user opts into
// using precompiled binaries.
static bool defaultUsePrecompiledBinaries() {
return Rustup.executablePath() == null;
}
CargokitUserOptions({
required this.usePrecompiledBinaries,
required this.verboseLogging,
});
CargokitUserOptions._()
: usePrecompiledBinaries = defaultUsePrecompiledBinaries(),
verboseLogging = false;
static CargokitUserOptions parse(YamlNode node) {
if (node is! YamlMap) {
throw SourceSpanException('Cargokit options must be a map', node.span);
}
bool usePrecompiledBinaries = defaultUsePrecompiledBinaries();
bool verboseLogging = false;
for (final entry in node.nodes.entries) {
if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) {
if (entry.value case YamlScalar(value: bool value)) {
usePrecompiledBinaries = value;
continue;
}
throw SourceSpanException(
'Invalid value for "use_precompiled_binaries". Must be a boolean.',
entry.value.span);
} else if (entry.key case YamlScalar(value: 'verbose_logging')) {
if (entry.value case YamlScalar(value: bool value)) {
verboseLogging = value;
continue;
}
throw SourceSpanException(
'Invalid value for "verbose_logging". Must be a boolean.',
entry.value.span);
} else {
throw SourceSpanException(
'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".',
entry.key.span);
}
}
return CargokitUserOptions(
usePrecompiledBinaries: usePrecompiledBinaries,
verboseLogging: verboseLogging,
);
}
static CargokitUserOptions load() {
String fileName = "cargokit_options.yaml";
var userProjectDir = Directory(Environment.rootProjectDir);
while (userProjectDir.parent.path != userProjectDir.path) {
final configFile = File(path.join(userProjectDir.path, fileName));
if (configFile.existsSync()) {
final contents = loadYamlNode(
configFile.readAsStringSync(),
sourceUrl: configFile.uri,
);
final res = parse(contents);
if (res.verboseLogging) {
_log.info('Found user options file at ${configFile.path}');
}
return res;
}
userProjectDir = userProjectDir.parent;
}
return CargokitUserOptions._();
}
final bool usePrecompiledBinaries;
final bool verboseLogging;
}

View File

@@ -0,0 +1,202 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:ed25519_edwards/ed25519_edwards.dart';
import 'package:github/github.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'artifacts_provider.dart';
import 'builder.dart';
import 'cargo.dart';
import 'crate_hash.dart';
import 'options.dart';
import 'rustup.dart';
import 'target.dart';
final _log = Logger('precompile_binaries');
class PrecompileBinaries {
PrecompileBinaries({
required this.privateKey,
required this.githubToken,
required this.repositorySlug,
required this.manifestDir,
required this.targets,
this.androidSdkLocation,
this.androidNdkVersion,
this.androidMinSdkVersion,
this.tempDir,
});
final PrivateKey privateKey;
final String githubToken;
final RepositorySlug repositorySlug;
final String manifestDir;
final List<Target> targets;
final String? androidSdkLocation;
final String? androidNdkVersion;
final int? androidMinSdkVersion;
final String? tempDir;
static String fileName(Target target, String name) {
return '${target.rust}_$name';
}
static String signatureFileName(Target target, String name) {
return '${target.rust}_$name.sig';
}
Future<void> run() async {
final crateInfo = CrateInfo.load(manifestDir);
final targets = List.of(this.targets);
if (targets.isEmpty) {
targets.addAll([
...Target.buildableTargets(),
if (androidSdkLocation != null) ...Target.androidTargets(),
]);
}
_log.info('Precompiling binaries for $targets');
final hash = CrateHash.compute(manifestDir);
_log.info('Computed crate hash: $hash');
final String tagName = 'precompiled_$hash';
final github = GitHub(auth: Authentication.withToken(githubToken));
final repo = github.repositories;
final release = await _getOrCreateRelease(
repo: repo,
tagName: tagName,
packageName: crateInfo.packageName,
hash: hash,
);
final tempDir = this.tempDir != null
? Directory(this.tempDir!)
: Directory.systemTemp.createTempSync('precompiled_');
tempDir.createSync(recursive: true);
final crateOptions = CargokitCrateOptions.load(
manifestDir: manifestDir,
);
final buildEnvironment = BuildEnvironment(
configuration: BuildConfiguration.release,
crateOptions: crateOptions,
targetTempDir: tempDir.path,
manifestDir: manifestDir,
crateInfo: crateInfo,
isAndroid: androidSdkLocation != null,
androidSdkPath: androidSdkLocation,
androidNdkVersion: androidNdkVersion,
androidMinSdkVersion: androidMinSdkVersion,
);
final rustup = Rustup();
for (final target in targets) {
final artifactNames = getArtifactNames(
target: target,
libraryName: crateInfo.packageName,
remote: true,
);
if (artifactNames.every((name) {
final fileName = PrecompileBinaries.fileName(target, name);
return (release.assets ?? []).any((e) => e.name == fileName);
})) {
_log.info("All artifacts for $target already exist - skipping");
continue;
}
_log.info('Building for $target');
final builder =
RustBuilder(target: target, environment: buildEnvironment);
builder.prepare(rustup);
final res = await builder.build();
final assets = <CreateReleaseAsset>[];
for (final name in artifactNames) {
final file = File(path.join(res, name));
if (!file.existsSync()) {
throw Exception('Missing artifact: ${file.path}');
}
final data = file.readAsBytesSync();
final create = CreateReleaseAsset(
name: PrecompileBinaries.fileName(target, name),
contentType: "application/octet-stream",
assetData: data,
);
final signature = sign(privateKey, data);
final signatureCreate = CreateReleaseAsset(
name: signatureFileName(target, name),
contentType: "application/octet-stream",
assetData: signature,
);
bool verified = verify(public(privateKey), data, signature);
if (!verified) {
throw Exception('Signature verification failed');
}
assets.add(create);
assets.add(signatureCreate);
}
_log.info('Uploading assets: ${assets.map((e) => e.name)}');
for (final asset in assets) {
// This seems to be failing on CI so do it one by one
int retryCount = 0;
while (true) {
try {
await repo.uploadReleaseAssets(release, [asset]);
break;
} on Exception catch (e) {
if (retryCount == 10) {
rethrow;
}
++retryCount;
_log.shout(
'Upload failed (attempt $retryCount, will retry): ${e.toString()}');
await Future.delayed(Duration(seconds: 2));
}
}
}
}
_log.info('Cleaning up');
tempDir.deleteSync(recursive: true);
}
Future<Release> _getOrCreateRelease({
required RepositoriesService repo,
required String tagName,
required String packageName,
required String hash,
}) async {
Release release;
try {
_log.info('Fetching release $tagName');
release = await repo.getReleaseByTagName(repositorySlug, tagName);
} on ReleaseNotFound {
_log.info('Release not found - creating release $tagName');
release = await repo.createRelease(
repositorySlug,
CreateRelease.from(
tagName: tagName,
name: 'Precompiled binaries ${hash.substring(0, 8)}',
targetCommitish: null,
isDraft: false,
isPrerelease: false,
body: 'Precompiled binaries for crate $packageName, '
'crate hash $hash.',
));
}
return release;
}
}

View File

@@ -0,0 +1,136 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:path/path.dart' as path;
import 'util.dart';
class _Toolchain {
_Toolchain(
this.name,
this.targets,
);
final String name;
final List<String> targets;
}
class Rustup {
List<String>? installedTargets(String toolchain) {
final targets = _installedTargets(toolchain);
return targets != null ? List.unmodifiable(targets) : null;
}
void installToolchain(String toolchain) {
log.info("Installing Rust toolchain: $toolchain");
runCommand("rustup", ['toolchain', 'install', toolchain]);
_installedToolchains
.add(_Toolchain(toolchain, _getInstalledTargets(toolchain)));
}
void installTarget(
String target, {
required String toolchain,
}) {
log.info("Installing Rust target: $target");
runCommand("rustup", [
'target',
'add',
'--toolchain',
toolchain,
target,
]);
_installedTargets(toolchain)?.add(target);
}
final List<_Toolchain> _installedToolchains;
Rustup() : _installedToolchains = _getInstalledToolchains();
List<String>? _installedTargets(String toolchain) => _installedToolchains
.firstWhereOrNull(
(e) => e.name == toolchain || e.name.startsWith('$toolchain-'))
?.targets;
static List<_Toolchain> _getInstalledToolchains() {
String extractToolchainName(String line) {
// ignore (default) after toolchain name
final parts = line.split(' ');
return parts[0];
}
final res = runCommand("rustup", ['toolchain', 'list']);
// To list all non-custom toolchains, we need to filter out lines that
// don't start with "stable", "beta", or "nightly".
Pattern nonCustom = RegExp(r"^(stable|beta|nightly)");
final lines = res.stdout
.toString()
.split('\n')
.where((e) => e.isNotEmpty && e.startsWith(nonCustom))
.map(extractToolchainName)
.toList(growable: true);
return lines
.map(
(name) => _Toolchain(
name,
_getInstalledTargets(name),
),
)
.toList(growable: true);
}
static List<String> _getInstalledTargets(String toolchain) {
final res = runCommand("rustup", [
'target',
'list',
'--toolchain',
toolchain,
'--installed',
]);
final lines = res.stdout
.toString()
.split('\n')
.where((e) => e.isNotEmpty)
.toList(growable: true);
return lines;
}
bool _didInstallRustSrcForNightly = false;
void installRustSrcForNightly() {
if (_didInstallRustSrcForNightly) {
return;
}
// Useful for -Z build-std
runCommand(
"rustup",
['component', 'add', 'rust-src', '--toolchain', 'nightly'],
);
_didInstallRustSrcForNightly = true;
}
static String? executablePath() {
final envPath = Platform.environment['PATH'];
final envPathSeparator = Platform.isWindows ? ';' : ':';
final home = Platform.isWindows
? Platform.environment['USERPROFILE']
: Platform.environment['HOME'];
final paths = [
if (home != null) path.join(home, '.cargo', 'bin'),
if (envPath != null) ...envPath.split(envPathSeparator),
];
for (final p in paths) {
final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup';
final rustupPath = path.join(p, rustup);
if (File(rustupPath).existsSync()) {
return rustupPath;
}
}
return null;
}
}

View File

@@ -0,0 +1,140 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:collection/collection.dart';
import 'util.dart';
class Target {
Target({
required this.rust,
this.flutter,
this.android,
this.androidMinSdkVersion,
this.darwinPlatform,
this.darwinArch,
});
static final all = [
Target(
rust: 'armv7-linux-androideabi',
flutter: 'android-arm',
android: 'armeabi-v7a',
androidMinSdkVersion: 16,
),
Target(
rust: 'aarch64-linux-android',
flutter: 'android-arm64',
android: 'arm64-v8a',
androidMinSdkVersion: 21,
),
Target(
rust: 'i686-linux-android',
flutter: 'android-x86',
android: 'x86',
androidMinSdkVersion: 16,
),
Target(
rust: 'x86_64-linux-android',
flutter: 'android-x64',
android: 'x86_64',
androidMinSdkVersion: 21,
),
Target(
rust: 'x86_64-pc-windows-msvc',
flutter: 'windows-x64',
),
Target(
rust: 'x86_64-unknown-linux-gnu',
flutter: 'linux-x64',
),
Target(
rust: 'aarch64-unknown-linux-gnu',
flutter: 'linux-arm64',
),
Target(
rust: 'x86_64-apple-darwin',
darwinPlatform: 'macosx',
darwinArch: 'x86_64',
),
Target(
rust: 'aarch64-apple-darwin',
darwinPlatform: 'macosx',
darwinArch: 'arm64',
),
Target(
rust: 'aarch64-apple-ios',
darwinPlatform: 'iphoneos',
darwinArch: 'arm64',
),
Target(
rust: 'aarch64-apple-ios-sim',
darwinPlatform: 'iphonesimulator',
darwinArch: 'arm64',
),
Target(
rust: 'x86_64-apple-ios',
darwinPlatform: 'iphonesimulator',
darwinArch: 'x86_64',
),
];
static Target? forFlutterName(String flutterName) {
return all.firstWhereOrNull((element) => element.flutter == flutterName);
}
static Target? forDarwin({
required String platformName,
required String darwinAarch,
}) {
return all.firstWhereOrNull((element) => //
element.darwinPlatform == platformName &&
element.darwinArch == darwinAarch);
}
static Target? forRustTriple(String triple) {
return all.firstWhereOrNull((element) => element.rust == triple);
}
static List<Target> androidTargets() {
return all
.where((element) => element.android != null)
.toList(growable: false);
}
/// Returns buildable targets on current host platform ignoring Android targets.
static List<Target> buildableTargets() {
if (Platform.isLinux) {
// Right now we don't support cross-compiling on Linux. So we just return
// the host target.
final arch = runCommand('arch', []).stdout as String;
if (arch.trim() == 'aarch64') {
return [Target.forRustTriple('aarch64-unknown-linux-gnu')!];
} else {
return [Target.forRustTriple('x86_64-unknown-linux-gnu')!];
}
}
return all.where((target) {
if (Platform.isWindows) {
return target.rust.contains('-windows-');
} else if (Platform.isMacOS) {
return target.darwinPlatform != null;
}
return false;
}).toList(growable: false);
}
@override
String toString() {
return rust;
}
final String? flutter;
final String rust;
final String? android;
final int? androidMinSdkVersion;
final String? darwinPlatform;
final String? darwinArch;
}

View File

@@ -0,0 +1,172 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:convert';
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'logging.dart';
import 'rustup.dart';
final log = Logger("process");
class CommandFailedException implements Exception {
final String executable;
final List<String> arguments;
final ProcessResult result;
CommandFailedException({
required this.executable,
required this.arguments,
required this.result,
});
@override
String toString() {
final stdout = result.stdout.toString().trim();
final stderr = result.stderr.toString().trim();
return [
"External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}",
"Returned Exit Code: ${result.exitCode}",
kSeparator,
"STDOUT:",
if (stdout.isNotEmpty) stdout,
kSeparator,
"STDERR:",
if (stderr.isNotEmpty) stderr,
].join('\n');
}
}
class TestRunCommandArgs {
final String executable;
final List<String> arguments;
final String? workingDirectory;
final Map<String, String>? environment;
final bool includeParentEnvironment;
final bool runInShell;
final Encoding? stdoutEncoding;
final Encoding? stderrEncoding;
TestRunCommandArgs({
required this.executable,
required this.arguments,
this.workingDirectory,
this.environment,
this.includeParentEnvironment = true,
this.runInShell = false,
this.stdoutEncoding,
this.stderrEncoding,
});
}
class TestRunCommandResult {
TestRunCommandResult({
this.pid = 1,
this.exitCode = 0,
this.stdout = '',
this.stderr = '',
});
final int pid;
final int exitCode;
final String stdout;
final String stderr;
}
TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride;
ProcessResult runCommand(
String executable,
List<String> arguments, {
String? workingDirectory,
Map<String, String>? environment,
bool includeParentEnvironment = true,
bool runInShell = false,
Encoding? stdoutEncoding = systemEncoding,
Encoding? stderrEncoding = systemEncoding,
}) {
if (testRunCommandOverride != null) {
final result = testRunCommandOverride!(TestRunCommandArgs(
executable: executable,
arguments: arguments,
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
stdoutEncoding: stdoutEncoding,
stderrEncoding: stderrEncoding,
));
return ProcessResult(
result.pid,
result.exitCode,
result.stdout,
result.stderr,
);
}
log.finer('Running command $executable ${arguments.join(' ')}');
final res = Process.runSync(
_resolveExecutable(executable),
arguments,
workingDirectory: workingDirectory,
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
stderrEncoding: stderrEncoding,
stdoutEncoding: stdoutEncoding,
);
if (res.exitCode != 0) {
throw CommandFailedException(
executable: executable,
arguments: arguments,
result: res,
);
} else {
return res;
}
}
class RustupNotFoundException implements Exception {
@override
String toString() {
return [
' ',
'rustup not found in PATH.',
' ',
'Maybe you need to install Rust? It only takes a minute:',
' ',
if (Platform.isWindows) 'https://www.rust-lang.org/tools/install',
if (hasHomebrewRustInPath()) ...[
'\$ brew unlink rust # Unlink homebrew Rust from PATH',
],
if (!Platform.isWindows)
"\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh",
' ',
].join('\n');
}
static bool hasHomebrewRustInPath() {
if (!Platform.isMacOS) {
return false;
}
final envPath = Platform.environment['PATH'] ?? '';
final paths = envPath.split(':');
return paths.any((p) {
return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync();
});
}
}
String _resolveExecutable(String executable) {
if (executable == 'rustup') {
final resolved = Rustup.executablePath();
if (resolved != null) {
return resolved;
}
throw RustupNotFoundException();
} else {
return executable;
}
}

View File

@@ -0,0 +1,84 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import 'dart:io';
import 'package:ed25519_edwards/ed25519_edwards.dart';
import 'package:http/http.dart';
import 'artifacts_provider.dart';
import 'cargo.dart';
import 'crate_hash.dart';
import 'options.dart';
import 'precompile_binaries.dart';
import 'target.dart';
class VerifyBinaries {
VerifyBinaries({
required this.manifestDir,
});
final String manifestDir;
Future<void> run() async {
final crateInfo = CrateInfo.load(manifestDir);
final config = CargokitCrateOptions.load(manifestDir: manifestDir);
final precompiledBinaries = config.precompiledBinaries;
if (precompiledBinaries == null) {
stdout.writeln('Crate does not support precompiled binaries.');
} else {
final crateHash = CrateHash.compute(manifestDir);
stdout.writeln('Crate hash: $crateHash');
for (final target in Target.all) {
final message = 'Checking ${target.rust}...';
stdout.write(message.padRight(40));
stdout.flush();
final artifacts = getArtifactNames(
target: target,
libraryName: crateInfo.packageName,
remote: true,
);
final prefix = precompiledBinaries.uriPrefix;
bool ok = true;
for (final artifact in artifacts) {
final fileName = PrecompileBinaries.fileName(target, artifact);
final signatureFileName =
PrecompileBinaries.signatureFileName(target, artifact);
final url = Uri.parse('$prefix$crateHash/$fileName');
final signatureUrl =
Uri.parse('$prefix$crateHash/$signatureFileName');
final signature = await get(signatureUrl);
if (signature.statusCode != 200) {
stdout.writeln('MISSING');
ok = false;
break;
}
final asset = await get(url);
if (asset.statusCode != 200) {
stdout.writeln('MISSING');
ok = false;
break;
}
if (!verify(precompiledBinaries.publicKey, asset.bodyBytes,
signature.bodyBytes)) {
stdout.writeln('INVALID SIGNATURE');
ok = false;
}
}
if (ok) {
stdout.writeln('OK');
}
}
}
}
}

View File

@@ -0,0 +1,453 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051
url: "https://pub.dev"
source: hosted
version: "64.0.0"
adaptive_number:
dependency: transitive
description:
name: adaptive_number
sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893"
url: "https://pub.dev"
source: hosted
version: "6.2.0"
args:
dependency: "direct main"
description:
name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.4.2"
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
collection:
dependency: "direct main"
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: "direct main"
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
coverage:
dependency: transitive
description:
name: coverage
sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097"
url: "https://pub.dev"
source: hosted
version: "1.6.3"
crypto:
dependency: "direct main"
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
ed25519_edwards:
dependency: "direct main"
description:
name: ed25519_edwards
sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
file:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
github:
dependency: "direct main"
description:
name: github
sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411"
url: "https://pub.dev"
source: hosted
version: "9.17.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
hex:
dependency: "direct main"
description:
name: hex
sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
http:
dependency: "direct main"
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
io:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
url: "https://pub.dev"
source: hosted
version: "4.8.1"
lints:
dependency: "direct dev"
description:
name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
logging:
dependency: "direct main"
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
url: "https://pub.dev"
source: hosted
version: "0.12.16"
meta:
dependency: transitive
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
mime:
dependency: transitive
description:
name: mime
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
url: "https://pub.dev"
source: hosted
version: "1.0.4"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: "direct main"
description:
name: path
sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.dev"
source: hosted
version: "5.4.0"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
shelf:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.1"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
url: "https://pub.dev"
source: hosted
version: "1.1.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
url: "https://pub.dev"
source: hosted
version: "0.10.12"
source_span:
dependency: "direct main"
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test:
dependency: "direct dev"
description:
name: test
sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9"
url: "https://pub.dev"
source: hosted
version: "1.24.6"
test_api:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
test_core:
dependency: transitive
description:
name: test_core
sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265"
url: "https://pub.dev"
source: hosted
version: "0.5.6"
toml:
dependency: "direct main"
description:
name: toml
sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44"
url: "https://pub.dev"
source: hosted
version: "0.14.0"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version:
dependency: "direct main"
description:
name: version
sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d"
url: "https://pub.dev"
source: hosted
version: "11.9.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
url: "https://pub.dev"
source: hosted
version: "2.4.0"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
yaml:
dependency: "direct main"
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.0.0 <4.0.0"

View File

@@ -0,0 +1,33 @@
# This is copied from Cargokit (which is the official way to use it currently)
# Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
name: build_tool
description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build.
publish_to: none
version: 1.0.0
environment:
sdk: ">=3.0.0 <4.0.0"
# Add regular dependencies here.
dependencies:
# these are pinned on purpose because the bundle_tool_runner doesn't have
# pubspec.lock. See run_build_tool.sh
logging: 1.2.0
path: 1.8.0
version: 3.0.0
collection: 1.18.0
ed25519_edwards: 0.3.1
hex: 0.2.0
yaml: 3.1.2
source_span: 1.10.0
github: 9.17.0
args: 2.4.2
crypto: 3.0.3
convert: 3.1.1
http: 1.1.0
toml: 0.14.0
dev_dependencies:
lints: ^2.1.0
test: ^1.24.0

View File

@@ -0,0 +1,99 @@
SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..")
# Workaround for https://github.com/dart-lang/pub/issues/4010
get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH)
if(WIN32)
# REALPATH does not properly resolve symlinks on windows :-/
execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
# Arguments
# - target: CMAKE target to which rust library is linked
# - manifest_dir: relative path from current folder to directory containing cargo manifest
# - lib_name: cargo package name
# - any_symbol_name: name of any exported symbol from the library.
# used on windows to force linking with library.
function(apply_cargokit target manifest_dir lib_name any_symbol_name)
set(CARGOKIT_LIB_NAME "${lib_name}")
set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}")
if (CMAKE_CONFIGURATION_TYPES)
set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/${CARGOKIT_LIB_FULL_NAME}")
else()
set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}")
set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}")
endif()
set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build")
if (FLUTTER_TARGET_PLATFORM)
set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}")
else()
set(CARGOKIT_TARGET_PLATFORM "windows-x64")
endif()
set(CARGOKIT_ENV
"CARGOKIT_CMAKE=${CMAKE_COMMAND}"
"CARGOKIT_CONFIGURATION=$<CONFIG>"
"CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}"
"CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}"
"CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}"
"CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}"
"CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool"
"CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}"
)
if (WIN32)
set(SCRIPT_EXTENSION ".cmd")
set(IMPORT_LIB_EXTENSION ".lib")
else()
set(SCRIPT_EXTENSION ".sh")
set(IMPORT_LIB_EXTENSION "")
execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}")
endif()
# Using generators in custom command is only supported in CMake 3.20+
if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0")
foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}"
"${CMAKE_CURRENT_BINARY_DIR}/_phony_"
COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV}
"${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake
VERBATIM
)
endforeach()
else()
add_custom_command(
OUTPUT
${OUTPUT_LIB}
"${CMAKE_CURRENT_BINARY_DIR}/_phony_"
COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV}
"${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake
VERBATIM
)
endif()
set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE)
if (TARGET ${target})
# If we have actual cmake target provided create target and make existing
# target depend on it
add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB})
add_dependencies("${target}" "${target}_cargokit")
target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}")
if(WIN32)
target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}")
endif()
else()
# Otherwise (FFI) just use ALL to force building always
add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB})
endif()
# Allow adding the output library to plugin bundled libraries
set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,27 @@
function Resolve-Symlinks {
[CmdletBinding()]
[OutputType([string])]
param(
[Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string] $Path
)
[string] $separator = '/'
[string[]] $parts = $Path.Split($separator)
[string] $realPath = ''
foreach ($part in $parts) {
if ($realPath -and !$realPath.EndsWith($separator)) {
$realPath += $separator
}
$realPath += $part
$item = Get-Item $realPath
if ($item.Target) {
$realPath = $item.Target.Replace('\', '/')
}
}
$realPath
}
$path=Resolve-Symlinks -Path $args[0]
Write-Host $path

View File

@@ -0,0 +1,179 @@
/// This is copied from Cargokit (which is the official way to use it currently)
/// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin
import java.nio.file.Paths
import org.apache.tools.ant.taskdefs.condition.Os
CargoKitPlugin.file = buildscript.sourceFile
apply plugin: CargoKitPlugin
class CargoKitExtension {
String manifestDir; // Relative path to folder containing Cargo.toml
String libname; // Library name within Cargo.toml. Must be a cdylib
}
abstract class CargoKitBuildTask extends DefaultTask {
@Input
String buildMode
@Input
String buildDir
@Input
String outputDir
@Input
String ndkVersion
@Input
String sdkDirectory
@Input
int compileSdkVersion;
@Input
int minSdkVersion;
@Input
String pluginFile
@Input
List<String> targetPlatforms
@TaskAction
def build() {
if (project.cargokit.manifestDir == null) {
throw new GradleException("Property 'manifestDir' must be set on cargokit extension");
}
if (project.cargokit.libname == null) {
throw new GradleException("Property 'libname' must be set on cargokit extension");
}
def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh"
def path = Paths.get(new File(pluginFile).parent, "..", executableName);
def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir)
def rootProjectDir = project.rootProject.projectDir
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
project.exec {
commandLine 'chmod', '+x', path
}
}
project.exec {
executable path
args "build-gradle"
environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir
environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool"
environment "CARGOKIT_MANIFEST_DIR", manifestDir
environment "CARGOKIT_CONFIGURATION", buildMode
environment "CARGOKIT_TARGET_TEMP_DIR", buildDir
environment "CARGOKIT_OUTPUT_DIR", outputDir
environment "CARGOKIT_NDK_VERSION", ndkVersion
environment "CARGOKIT_SDK_DIR", sdkDirectory
environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion
environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion
environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",")
environment "CARGOKIT_JAVA_HOME", System.properties['java.home']
}
}
}
class CargoKitPlugin implements Plugin<Project> {
static String file;
private Plugin findFlutterPlugin(Project rootProject) {
_findFlutterPlugin(rootProject.childProjects)
}
private Plugin _findFlutterPlugin(Map projects) {
for (project in projects) {
for (plugin in project.value.getPlugins()) {
if (plugin.class.name == "FlutterPlugin") {
return plugin;
}
}
def plugin = _findFlutterPlugin(project.value.childProjects);
if (plugin != null) {
return plugin;
}
}
return null;
}
@Override
void apply(Project project) {
def plugin = findFlutterPlugin(project.rootProject);
project.extensions.create("cargokit", CargoKitExtension)
if (plugin == null) {
print("Flutter plugin not found, CargoKit plugin will not be applied.")
return;
}
def cargoBuildDir = "${project.buildDir}/build"
// Determine if the project is an application or library
def isApplication = plugin.project.plugins.hasPlugin('com.android.application')
def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants
variants.all { variant ->
final buildType = variant.buildType.name
def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}";
def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs;
jniLibs.srcDir(new File(cargoOutputDir))
def platforms = plugin.getTargetPlatforms().collect()
// Same thing addFlutterDependencies does in flutter.gradle
if (buildType == "debug") {
platforms.add("android-x86")
platforms.add("android-x64")
}
// The task name depends on plugin properties, which are not available
// at this point
project.getGradle().afterProject {
def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}";
if (project.tasks.findByName(taskName)) {
return
}
if (plugin.project.android.ndkVersion == null) {
throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.")
}
def task = project.tasks.create(taskName, CargoKitBuildTask.class) {
buildMode = variant.buildType.name
buildDir = cargoBuildDir
outputDir = cargoOutputDir
ndkVersion = plugin.project.android.ndkVersion
sdkDirectory = plugin.project.android.sdkDirectory
minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int
compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int
targetPlatforms = platforms
pluginFile = CargoKitPlugin.file
}
def onTask = { newTask ->
if (newTask.name == "merge${buildType.capitalize()}NativeLibs") {
newTask.dependsOn task
// Fix gradle 7.4.2 not picking up JNI library changes
newTask.outputs.upToDateWhen { false }
}
}
project.tasks.each onTask
project.tasks.whenTaskAdded onTask
}
}
}
}

View File

@@ -0,0 +1,91 @@
@echo off
setlocal
setlocal ENABLEDELAYEDEXPANSION
SET BASEDIR=%~dp0
if not exist "%CARGOKIT_TOOL_TEMP_DIR%" (
mkdir "%CARGOKIT_TOOL_TEMP_DIR%"
)
cd /D "%CARGOKIT_TOOL_TEMP_DIR%"
SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool
SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart
set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/%
(
echo name: build_tool_runner
echo version: 1.0.0
echo publish_to: none
echo.
echo environment:
echo sdk: '^>=3.0.0 ^<4.0.0'
echo.
echo dependencies:
echo build_tool:
echo path: %BUILD_TOOL_PKG_DIR_POSIX%
) >pubspec.yaml
if not exist bin (
mkdir bin
)
(
echo import 'package:build_tool/build_tool.dart' as build_tool;
echo void main^(List^<String^> args^) ^{
echo build_tool.runMain^(args^);
echo ^}
) >bin\build_tool_runner.dart
SET PRECOMPILED=bin\build_tool_runner.dill
REM To detect changes in package we compare output of DIR /s (recursive)
set PREV_PACKAGE_INFO=.dart_tool\package_info.prev
set CUR_PACKAGE_INFO=.dart_tool\package_info.cur
DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig"
REM Last line in dir output is free space on harddrive. That is bound to
REM change between invocation so we need to remove it
(
Set "Line="
For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do (
SetLocal EnableDelayedExpansion
If Defined Line Echo !Line!
EndLocal
Set "Line=%%A")
) >"%CUR_PACKAGE_INFO%"
DEL "%CUR_PACKAGE_INFO%_orig"
REM Compare current directory listing with previous
FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1
If %ERRORLEVEL% neq 0 (
REM Changed - copy current to previous and remove precompiled kernel
if exist "%PREV_PACKAGE_INFO%" (
DEL "%PREV_PACKAGE_INFO%"
)
MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%"
if exist "%PRECOMPILED%" (
DEL "%PRECOMPILED%"
)
)
REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO%
REM which means we need to do pub get and precompile
if not exist "%PRECOMPILED%" (
echo Running pub get in "%cd%"
"%DART%" pub get --no-precompile
"%DART%" compile kernel bin/build_tool_runner.dart
)
"%DART%" "%PRECOMPILED%" %*
REM 253 means invalid snapshot version.
If %ERRORLEVEL% equ 253 (
"%DART%" pub get --no-precompile
"%DART%" compile kernel bin/build_tool_runner.dart
"%DART%" "%PRECOMPILED%" %*
)

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env bash
set -e
BASEDIR=$(dirname "$0")
mkdir -p "$CARGOKIT_TOOL_TEMP_DIR"
cd "$CARGOKIT_TOOL_TEMP_DIR"
# Write a very simple bin package in temp folder that depends on build_tool package
# from Cargokit. This is done to ensure that we don't pollute Cargokit folder
# with .dart_tool contents.
BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool"
if [[ -z $FLUTTER_ROOT ]]; then # not defined
DART=dart
else
DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart"
fi
cat << EOF > "pubspec.yaml"
name: build_tool_runner
version: 1.0.0
publish_to: none
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
build_tool:
path: "$BUILD_TOOL_PKG_DIR"
EOF
mkdir -p "bin"
cat << EOF > "bin/build_tool_runner.dart"
import 'package:build_tool/build_tool.dart' as build_tool;
void main(List<String> args) {
build_tool.runMain(args);
}
EOF
# Create alias for `shasum` if it does not exist and `sha1sum` exists
if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then
shopt -s expand_aliases
alias shasum="sha1sum"
fi
# Dart run will not cache any package that has a path dependency, which
# is the case for our build_tool_runner. So instead we precompile the package
# ourselves.
# To invalidate the cached kernel we use the hash of ls -LR of the build_tool
# package directory. This should be good enough, as the build_tool package
# itself is not meant to have any path dependencies.
if [[ "$OSTYPE" == "darwin"* ]]; then
PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum)
else
PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum)
fi
PACKAGE_HASH_FILE=".package_hash"
if [ -f "$PACKAGE_HASH_FILE" ]; then
EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE")
if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then
rm "$PACKAGE_HASH_FILE"
fi
fi
# Run pub get if needed.
if [ ! -f "$PACKAGE_HASH_FILE" ]; then
"$DART" pub get --no-precompile
"$DART" compile kernel bin/build_tool_runner.dart
echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE"
fi
set +e
"$DART" bin/build_tool_runner.dill "$@"
exit_code=$?
# 253 means invalid snapshot version.
if [ $exit_code == 253 ]; then
"$DART" pub get --no-precompile
"$DART" compile kernel bin/build_tool_runner.dart
"$DART" bin/build_tool_runner.dill "$@"
exit_code=$?
fi
exit $exit_code

View File

@@ -0,0 +1 @@
// This is an empty file to force CocoaPods to create a framework.

View File

@@ -0,0 +1,45 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'rust_lib_photos'
s.version = '0.0.1'
s.summary = 'A new Flutter FFI plugin project.'
s.description = <<-DESC
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
s.platform = :ios, '11.0'
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.swift_version = '5.0'
s.script_phase = {
:name => 'Build Rust library',
# First argument is relative path to the `rust` folder, second is name of rust library
:script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos',
:execution_position => :before_compile,
:input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'],
# Let XCode know that the static library referenced in -force_load below is
# created by this build step.
:output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"],
}
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
# Flutter.framework does not contain a i386 slice.
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a',
}
end

View File

@@ -0,0 +1,19 @@
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
# Project-level configuration.
set(PROJECT_NAME "rust_lib_photos")
project(${PROJECT_NAME} LANGUAGES CXX)
include("../cargokit/cmake/cargokit.cmake")
apply_cargokit(${PROJECT_NAME} ../../rust rust_lib_photos "")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(rust_lib_photos_bundled_libraries
"${${PROJECT_NAME}_cargokit_lib}"
PARENT_SCOPE
)

View File

@@ -0,0 +1 @@
// This is an empty file to force CocoaPods to create a framework.

View File

@@ -0,0 +1,44 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint rust_lib_photos.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'rust_lib_photos'
s.version = '0.0.1'
s.summary = 'A new Flutter FFI plugin project.'
s.description = <<-DESC
A new Flutter FFI plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'FlutterMacOS'
s.platform = :osx, '10.11'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.swift_version = '5.0'
s.script_phase = {
:name => 'Build Rust library',
# First argument is relative path to the `rust` folder, second is name of rust library
:script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../../rust rust_lib_photos',
:execution_position => :before_compile,
:input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'],
# Let XCode know that the static library referenced in -force_load below is
# created by this build step.
:output_files => ["${BUILT_PRODUCTS_DIR}/librust_lib_photos.a"],
}
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
# Flutter.framework does not contain a i386 slice.
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/librust_lib_photos.a',
}
end

View File

@@ -0,0 +1,34 @@
name: rust_lib_photos
description: "Utility to build Rust code"
version: 0.0.1
publish_to: none
environment:
sdk: '>=3.3.0 <4.0.0'
flutter: '>=3.3.0'
dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
dev_dependencies:
ffi: ^2.0.2
ffigen: ^11.0.0
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
plugin:
platforms:
android:
ffiPlugin: true
ios:
ffiPlugin: true
linux:
ffiPlugin: true
macos:
ffiPlugin: true
windows:
ffiPlugin: true

View File

@@ -0,0 +1,17 @@
flutter/
# Visual Studio user-specific files.
*.suo
*.user
*.userosscache
*.sln.docstates
# Visual Studio build-related files.
x64/
x86/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

View File

@@ -0,0 +1,20 @@
# The Flutter tooling requires that developers have a version of Visual Studio
# installed that includes CMake 3.14 or later. You should not increase this
# version, as doing so will cause the plugin to fail to compile for some
# customers of the plugin.
cmake_minimum_required(VERSION 3.14)
# Project-level configuration.
set(PROJECT_NAME "rust_lib_photos")
project(${PROJECT_NAME} LANGUAGES CXX)
include("../cargokit/cmake/cargokit.cmake")
apply_cargokit(${PROJECT_NAME} ../../../../../../rust rust_lib_photos "")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(rust_lib_photos_bundled_libraries
"${${PROJECT_NAME}_cargokit_lib}"
PARENT_SCOPE
)

Some files were not shown because too many files have changed in this diff Show More