diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79c8ffad72..3ca5671eba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: - name: Set up Android SDK uses: android-actions/setup-android@v3 - - name: Package android + - name: Run package run: ./gradlew sample:assembleRelease - name: Upload APK artifacts @@ -48,6 +48,38 @@ jobs: name: apk-files path: sample/build/outputs/apk/release/*.apk + package-ios: + name: Package ios + # needs: test + runs-on: macos-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Run package + run: xcodebuild -project sample/iosApp/iosApp.xcodeproj -scheme iosApp -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -derivedDataPath sample/build/ios/outputs/ + + - name: Read version name + id: version_name_step + run: | + versionName=$(grep '^versionName=' gradle.properties | awk -F'=' '{print $2}') + echo "version_name=$versionName" >> $GITHUB_OUTPUT + + - name: File name add version + run: mv "sample/build/ios/outputs/Build/Products/Debug-iphonesimulator/Sketch4 Sample.app" "sample/build/ios/outputs/Build/Products/Debug-iphonesimulator/Sketch4 Sample-${{ steps.version_name_step.outputs.version_name }}.app" + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: ios-files + path: sample/build/ios/outputs/Build/Products/Debug-iphonesimulator/*.app + package-linux: name: Package linux desktop # needs: test @@ -62,9 +94,15 @@ jobs: distribution: temurin java-version: 17 - - name: Package linux desktop + - name: Run package run: ./gradlew sample:packageReleaseDeb + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: deb-files + path: sample/build/compose/binaries/main-release/deb/*.deb + package-macos: name: Package macos desktop # needs: test @@ -79,9 +117,15 @@ jobs: distribution: temurin java-version: 17 - - name: Package macos desktop + - name: Run package run: ./gradlew sample:packageReleaseDmg + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: dmg-files + path: sample/build/compose/binaries/main-release/dmg/*.dmg + package-windows: name: Package windows desktop # needs: test @@ -96,9 +140,15 @@ jobs: distribution: temurin java-version: 17 - - name: Package windows desktop + - name: Run package run: ./gradlew sample:packageReleaseMsi + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: msi-files + path: sample/build/compose/binaries/main-release/msi/*.msi + package-js: name: Package js browser # needs: test @@ -113,9 +163,24 @@ jobs: distribution: temurin java-version: 17 - - name: Package js browser + - name: Run package run: ./gradlew sample:jsBrowserDistribution + - name: Read version name + id: version_name_step + run: | + versionName=$(grep '^versionName=' gradle.properties | awk -F'=' '{print $2}') + echo "version_name=$versionName" >> $GITHUB_OUTPUT + + - name: Compress + run: cd sample/build/dist/js/productionExecutable; zip -r "sketch-sample-js-${{ steps.version_name_step.outputs.version_name }}.zip" *; cd - + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: js-browser-files + path: sample/build/dist/js/productionExecutable/*.zip + package-wasm-js: name: Package wasm js browser # needs: test @@ -130,11 +195,27 @@ jobs: distribution: temurin java-version: 17 - - name: Package wasm js browser + - name: Run package run: ./gradlew sample:wasmJsBrowserDistribution + - name: Read version name + id: version_name_step + run: | + versionName=$(grep '^versionName=' gradle.properties | awk -F'=' '{print $2}') + echo "version_name=$versionName" >> $GITHUB_OUTPUT + + - name: Compress + run: cd sample/build/dist/wasmJs/productionExecutable; zip -r "sketch-sample-wasmJs-${{ steps.version_name_step.outputs.version_name }}.zip" *; cd - + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: wasm-js-browser-files + path: sample/build/dist/wasmJs/productionExecutable/*.zip + deploy-api-docs: name: Deploy API docs + needs: [ package-android, package-ios, package-linux, package-macos, package-windows, package-js, package-wasm-js ] runs-on: ubuntu-latest permissions: contents: write @@ -178,7 +259,7 @@ jobs: publish: name: Publish and release to maven central - needs: [ package-android, package-linux, package-macos, package-windows, package-js, package-wasm-js, deploy-api-docs ] + needs: deploy-api-docs runs-on: macos-latest steps: - name: Checkout @@ -194,14 +275,14 @@ jobs: - name: Set up Android SDK uses: android-actions/setup-android@v3 - - name: Run publish and release to maven central + - name: Run publish env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }} - # run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache - run: ./gradlew publishToMavenCentral --no-configuration-cache + run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache + # run: ./gradlew publishToMavenCentral --no-configuration-cache release: name: Release new version @@ -214,7 +295,43 @@ jobs: uses: actions/download-artifact@v4 with: name: apk-files - path: ./downloaded-apks + path: ./downloaded + + - name: Download ios artifacts + uses: actions/download-artifact@v4 + with: + name: ios-files + path: ./downloaded + + - name: Download deb artifacts + uses: actions/download-artifact@v4 + with: + name: deb-files + path: ./downloaded + + - name: Download dmg artifacts + uses: actions/download-artifact@v4 + with: + name: dmg-files + path: ./downloaded + + - name: Download msi artifacts + uses: actions/download-artifact@v4 + with: + name: msi-files + path: ./downloaded + + - name: Download js-browser artifacts + uses: actions/download-artifact@v4 + with: + name: js-browser-files + path: ./downloaded + + - name: Download wasm-js-browser artifacts + uses: actions/download-artifact@v4 + with: + name: wasm-js-browser-files + path: ./downloaded - name: Release uses: softprops/action-gh-release@v2 @@ -223,20 +340,10 @@ jobs: if: startsWith(github.ref, 'refs/tags/') # Only run on tag push with: body: | - Please check the [CHANGELOG.md](https://github.com/panpf/sketch/blob/main/CHANGELOG.md) file for updated log - - Sample App: - * Android:For the Android platform sample app, please see the attachment. - * Web:https://panpf.github.io/sketch/app - * For other platform sample apps, please refer to the document [Run Sample App](https://github.com/panpf/sketch?tab=readme-ov-file#run-sample-app) running source code experience - - \---------- - - 更新日志请查看 [CHANGELOG.md](https://github.com/panpf/sketch/blob/main/CHANGELOG_zh.md) 文件 - - 示例 App: - * Android:Android 平台示例 App 见附件 - * Web:https://panpf.github.io/sketch/app - * 其它平台示例 App 请参考文档 [运行示例 App](https://github.com/panpf/sketch/blob/main/README_zh.md#%E8%BF%90%E8%A1%8C%E7%A4%BA%E4%BE%8B-app) 运行源代码体验 - files: ./downloaded-apks/*.apk + Please check the [CHANGELOG.md](https://github.com/panpf/sketch/blob/main/CHANGELOG.md) file for updated log. + + Android, iOS, desktop and web packages, see the Assets attachment. + + Web example:https://panpf.github.io/sketch/app. + files: ./downloaded/* make_latest: true \ No newline at end of file diff --git a/.github/workflows/deploy_pages.yml b/.github/workflows/deploy_wen_app.yml similarity index 100% rename from .github/workflows/deploy_pages.yml rename to .github/workflows/deploy_wen_app.yml diff --git a/.github/workflows/deploy_wiki.yml b/.github/workflows/deploy_wiki.yml new file mode 100644 index 0000000000..9eabae9093 --- /dev/null +++ b/.github/workflows/deploy_wiki.yml @@ -0,0 +1,52 @@ +name: Deploy wiki + +on: + workflow_dispatch: + push: + branches: main + +jobs: + deploy-web-app: + name: Deploy wiki + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Build wiki + run: | + pip3 install --upgrade pip && pip3 install --upgrade mkdocs-material mkdocs-material-extensions mkdocs-minify-plugin mkdocs-static-i18n + ./build_docs.sh + + - name: Check out the gh-pages branch + run: | + git fetch origin gh-pages:gh-pages + git checkout gh-pages + + - name: Remove old app directory + run: | + rm -rf wiki/* + + - name: Create app directory + run: mkdir -p wiki + + - name: Move build artifacts to wiki directory + run: | + mv site/* wiki/ + + - name: Commit and push changes + uses: stefanzweifel/git-auto-commit-action@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit_message: "Update wiki" + branch: gh-pages + file_pattern: wiki/* \ No newline at end of file diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 0000000000..c4583609b5 --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,188 @@ +name: Package + +on: workflow_dispatch + +jobs: + package-android: + name: Package android + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Run package + run: ./gradlew sample:assembleRelease + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: apk-files + path: sample/build/outputs/apk/release/*.apk + + package-ios: + name: Package ios + runs-on: macos-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Run package + run: xcodebuild -project sample/iosApp/iosApp.xcodeproj -scheme iosApp -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -derivedDataPath sample/build/ios/outputs/ + + - name: Read version name + id: version_name_step + run: | + versionName=$(grep '^versionName=' gradle.properties | awk -F'=' '{print $2}') + echo "version_name=$versionName" >> $GITHUB_OUTPUT + + - name: File name add version + run: mv "sample/build/ios/outputs/Build/Products/Debug-iphonesimulator/Sketch4 Sample.app" "sample/build/ios/outputs/Build/Products/Debug-iphonesimulator/Sketch4 Sample-${{ steps.version_name_step.outputs.version_name }}.app" + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: ios-files + path: sample/build/ios/outputs/Build/Products/Debug-iphonesimulator/*.app + + package-linux: + name: Package linux desktop + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Run package + run: ./gradlew sample:packageReleaseDeb + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: deb-files + path: sample/build/compose/binaries/main-release/deb/*.deb + + package-macos: + name: Package macos desktop + runs-on: macos-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Run package + run: ./gradlew sample:packageReleaseDmg + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: dmg-files + path: sample/build/compose/binaries/main-release/dmg/*.dmg + + package-windows: + name: Package windows desktop + runs-on: windows-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Run package + run: ./gradlew sample:packageReleaseMsi + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: msi-files + path: sample/build/compose/binaries/main-release/msi/*.msi + + package-js: + name: Package js browser + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Run package + run: ./gradlew sample:jsBrowserDistribution + + - name: Read version name + id: version_name_step + run: | + versionName=$(grep '^versionName=' gradle.properties | awk -F'=' '{print $2}') + echo "version_name=$versionName" >> $GITHUB_OUTPUT + + - name: Compress + run: cd sample/build/dist/js/productionExecutable; zip -r "sketch-sample-js-${{ steps.version_name_step.outputs.version_name }}.zip" *; cd - + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: js-browser-files + path: sample/build/dist/js/productionExecutable/*.zip + + package-wasm-js: + name: Package wasm js browser + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Run package + run: ./gradlew sample:wasmJsBrowserDistribution + + - name: Read version name + id: version_name_step + run: | + versionName=$(grep '^versionName=' gradle.properties | awk -F'=' '{print $2}') + echo "version_name=$versionName" >> $GITHUB_OUTPUT + + - name: Compress + run: cd sample/build/dist/wasmJs/productionExecutable; zip -r "sketch-sample-wasmJs-${{ steps.version_name_step.outputs.version_name }}.zip" *; cd - + + - name: Upload APK artifacts + uses: actions/upload-artifact@v4 + with: + name: wasm-js-browser-files + path: sample/build/dist/wasmJs/productionExecutable/*.zip \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 95c4197b2f..9bf6f723ab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,7 +4,7 @@ on: workflow_dispatch jobs: publish: - name: Publish and release to maven central + name: Publish to maven central runs-on: macos-latest steps: - name: Checkout @@ -20,7 +20,7 @@ jobs: - name: Set up Android SDK uses: android-actions/setup-android@v3 - - name: Run publish and release to maven central + - name: Run publish env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} diff --git a/.gitignore b/.gitignore index d03e26be52..ef3fbca2f4 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,11 @@ webpack.config.d/ # Xcode *.xcuserstate -**/xcuserdata \ No newline at end of file +**/xcuserdata + +# docs +site/ +docs/index.md +docs/index.zh.md +docs/CHANGELOG.md +docs/CHANGELOG.zh.md \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b9627783c6..8f48c0b594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,56 @@ # Change Log -Translations: [简体中文](CHANGELOG_zh.md) +Translations: [简体中文](CHANGELOG.zh.md) > [!CAUTION] > 1. The 4.x version has undergone a lot of destructive reconstruction and simplification to be compatible with Compose Multiplatform, and is not compatible with the 3.x version. > 2. The maven groupId is upgraded to `io.github.panpf.sketch4`, so versions 2.\* and 3.\* will not prompt for upgrade. -> 3. Reference [《Migration Documentation》](docs/wiki/migrate.md) migrating from 3.x to 4.x +> 3. Reference [《Migration Documentation》](docs/migrate.md) migrating from 3.x to 4.x + +# 4.1.0 + +> [!CAUTION] +> The compose multiplatform version 1.8.0 must be jvm 11 to compile. Please modify the configuration +> of sourceCompatibility, targetCompatibility, jvmTarget, etc. to 11+ + +* fix: Fixed a bug where CrossfadePainter, ResizePainter, ImageBitmapPainter and DrawablePainter + cannot fill the canvas due to loss of floating point precision +* new: AsyncImage Adds the keepContentNoneStartOnDraw param +* depend: Update to kotlin 2.1.10 +* depend: Update to compose multiplatform 1.8.0 +* depend: Update to android-gif-drawable 1.2.29 +* depend: Update to ffmpegMediaMetadataRetriever 1.0.19 +* depend: Update to jetbrains-lifecycle 2.8.4 +* depend: Update to kotlinx-coroutines 1.10.2 +* depend: Update to ktor2 2.3.13 +* depend: Update to ktor3 3.0.3 +* depend: Update to okio 3.11.0 +* depend: Don't replace `kotlin-stdlib-jdk7` and `kotlin-stdlib-jdk8` with `kotlin-stdlib` + +# 4.0.6 + +compose: + +* improve: AsyncImage now no longer crashes when the window size is 0, but is a minimum of + 100. [#244](https://github.com/panpf/sketch/issues/244 + +# 4.0.5 + +fetch: + +* fix: Fixed a bug in FileUriFetcher that failed to load the windows file + path. [#239](https://github.com/panpf/sketch/issues/239) + +# 4.0.4 + +compose: + +* fix: Fixed a bug where AsyncImage and SubcomposeAsyncImage could not load images when component + size is 0. [#236](https://github.com/panpf/sketch/issues/236) +* improve: Restore AsyncImage and SubcomposeAsyncImage function to replace window container size if + width or height is 0 when setting size # 4.0.3 @@ -16,6 +59,8 @@ Translations: [简体中文](CHANGELOG_zh.md) # 4.0.2 +compose: + * fix: The AsyncImage component is reloaded when the current window size changes. [#231](https://github.com/panpf/sketch/issues/231) diff --git a/CHANGELOG_zh.md b/CHANGELOG.zh.md similarity index 92% rename from CHANGELOG_zh.md rename to CHANGELOG.zh.md index 17301e1145..de6c6561f0 100644 --- a/CHANGELOG_zh.md +++ b/CHANGELOG.zh.md @@ -5,7 +5,49 @@ > [!CAUTION] > 1. 4.x 版本为兼容 Compose Multiplatform 而进行了大量破坏性重构和简化,不兼容 3.x 版本 > 2. maven groupId 升级为 `io.github.panpf.sketch4`,因此 2.\*、3.\* 版本不会提示升级 -> 3. 参考 [《迁移文档》](docs/wiki/migrate_zh.md) 从 3.x 版本迁移 4.x 版本 +> 3. 参考 [《迁移文档》](docs/migrate.zh.md) 从 3.x 版本迁移 4.x 版本 + +# 4.1.0 + +> [!CAUTION] +> compose multiplatform 1.8.0 版本必须 jvm 11 才能编译,请修改 +> sourceCompatibility、targetCompatibility、jvmTarget 等配置为 11+ + +* fix: 修复 CrossfadePainter, ResizePainter, ImageBitmapPainter 和 DrawablePainter + 因丢失浮点精度导致内容无法充满画布的 bug +* new: AsyncImage 增加 keepContentNoneStartOnDraw 参数 +* depend: 更新 kotlin 2.1.10 +* depend: 更新 compose multiplatform 1.8.0 +* depend: 更新 android-gif-drawable 1.2.29 +* depend: 更新 ffmpegMediaMetadataRetriever 1.0.19 +* depend: 更新 jetbrains-lifecycle 2.8.4 +* depend: 更新 kotlinx-coroutines 1.10.2 +* depend: 更新 ktor2 2.3.13 +* depend: 更新 ktor3 3.0.3 +* depend: 更新 okio 3.11.0 +* depend: 不再替换 `kotlin-stdlib-jdk7` 和 `kotlin-stdlib-jdk8` 为 `kotlin-stdlib` + +# 4.0.6 + +compose: + +* improve: 现在 AsyncImage 在窗口大小为 0 时不再崩溃,而是最小为 + 100。 [#244](https://github.com/panpf/sketch/issues/244) + +# 4.0.5 + +fetch: + +* fix: 修复 FileUriFetcher 无法加载 windows 文件路径的 + bug。 [#239](https://github.com/panpf/sketch/issues/239) + +# 4.0.4 + +compose: + +* fix: 修复 AsyncImage 和 SubcomposeAsyncImage 在组件大小为 0 时无法加载图片的 + bug。 [#236](https://github.com/panpf/sketch/issues/236) +* improve: 恢复 AsyncImage 和 SubcomposeAsyncImage 在设置 size 时如果宽或高为 0 就替换为窗口容器大小的功能 # 4.0.3 @@ -14,6 +56,8 @@ # 4.0.2 +compose: + * fix: 当前窗口大小改变时 AsyncImage 组件会重新加载。 [#231](https://github.com/panpf/sketch/issues/231) diff --git a/README.md b/README.md index d629ad91d0..fc59c76795 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![version_icon]][version_link] ![QQ Group][qq_group_image] -Translations: [简体中文](README_zh.md) +Translations: [简体中文](README.zh.md) Sketch is an image loading library specially designed for Compose Multiplatform and Android View. It has the following features: @@ -24,13 +24,9 @@ has the following features: ## Sample App -* Android: Please go to the [Releases](https://github.com/panpf/sketch/releases) page to download - the latest version of the installation package -* Web: https://panpf.github.io/sketch/app -* Desktop: Use [kdoctor] to check the running environment, and follow the prompts to install the - required software, and then execute the `./package_desktop.sh` command in the project root - directory to package. The installation package location is included in the output. -* iOS: Please refer to the [Run Sample App](#Run-Sample-app) section to compile and run it yourself +* For Android, iOS, desktop version, and web deployable packages, please go to + the [Releases](https://github.com/panpf/sketch/releases) page to download. +* Web example:https://panpf.github.io/sketch/app ## Install @@ -140,8 +136,15 @@ registration: [《Register component》][register_component] ### R8 / Proguard -Sketch itself does not need to configure any obfuscation rules, but you may need to configure it for -the indirectly dependent [Kotlin Coroutines], [OkHttp], [Okio] Add obfuscation configuration +1. Android, iOS, Web and other platforms do not need to configure any obfuscation rules +2. The following obfuscation rules are required for desktop platforms: + ```proguard + # -------------------------- Sketch Privider ---------------------------- # + -keep class * implements com.github.panpf.sketch.util.DecoderProvider { *; } + -keep class * implements com.github.panpf.sketch.util.FetcherProvider { *; } + ``` +3. It may also be necessary to add obfuscation configurations to the indirect dependencies of + three-party libraries such as [Kotlin Coroutines], [OkHttp], [Okio] ## Quickly Started @@ -338,7 +341,7 @@ Apache 2.0. See the [LICENSE](LICENSE.txt) file for details. [license_image]: https://img.shields.io/badge/License-Apache%202-blue.svg -[logo_image]: docs/res/logo.png +[logo_image]: docs/images/logo.png [license_link]: https://www.apache.org/licenses/LICENSE-2.0 @@ -353,73 +356,73 @@ Apache 2.0. See the [LICENSE](LICENSE.txt) file for details. [comment]: <> (wiki) -[animated_image]: docs/wiki/animated_image.md +[animated_image]: docs/animated_image.md -[apk_app_icon]: docs/wiki/apk_app_icon.md +[apk_app_icon]: docs/apk_app_icon.md -[compose]: docs/wiki/compose.md +[compose]: docs/compose.md -[decoder]: docs/wiki/decoder.md +[decoder]: docs/decoder.md -[download_cache]: docs/wiki/download_cache.md +[download_cache]: docs/download_cache.md -[exif_orientation]: docs/wiki/exif_orientation.md +[exif_orientation]: docs/exif_orientation.md -[fetcher]: docs/wiki/fetcher.md +[fetcher]: docs/fetcher.md -[getting_started]: docs/wiki/getting_started.md +[getting_started]: docs/getting_started.md -[register_component]: docs/wiki/register_component.md +[register_component]: docs/register_component.md -[http]: docs/wiki/http.md +[http]: docs/http.md -[image_options]: docs/wiki/image_options.md +[image_options]: docs/image_options.md -[lifecycle]: docs/wiki/lifecycle.md +[lifecycle]: docs/lifecycle.md -[listener]: docs/wiki/listener.md +[listener]: docs/listener.md -[log]: docs/wiki/log.md +[log]: docs/log.md -[long_image_grid_thumbnails]: docs/wiki/long_image_grid_thumbnails.md +[long_image_grid_thumbnails]: docs/long_image_grid_thumbnails.md -[memory_cache]: docs/wiki/memory_cache.md +[memory_cache]: docs/memory_cache.md -[mime_type_logo]: docs/wiki/mime_type_logo.md +[mime_type_logo]: docs/mime_type_logo.md -[pause_load_when_scrolling]: docs/wiki/pause_load_when_scrolling.md +[pause_load_when_scrolling]: docs/pause_load_when_scrolling.md -[preload]: docs/wiki/preload.md +[preload]: docs/preload.md -[download]: docs/wiki/download_image.md +[download]: docs/download_image.md -[progress_indicator]: docs/wiki/progress_indicator.md +[progress_indicator]: docs/progress_indicator.md -[request_interceptor]: docs/wiki/request_interceptor.md +[request_interceptor]: docs/request_interceptor.md -[decode_interceptor]: docs/wiki/decode_interceptor.md +[decode_interceptor]: docs/decode_interceptor.md -[resize]: docs/wiki/resize.md +[resize]: docs/resize.md -[result_cache]: docs/wiki/result_cache.md +[result_cache]: docs/result_cache.md -[save_cellular_traffic]: docs/wiki/save_cellular_traffic.md +[save_cellular_traffic]: docs/save_cellular_traffic.md -[sketch_image_view]: docs/wiki/sketch_image_view.md +[sketch_image_view]: docs/sketch_image_view.md -[state_image]: docs/wiki/state_image.md +[state_image]: docs/state_image.md -[svg]: docs/wiki/svg.md +[svg]: docs/svg.md -[target]: docs/wiki/target.md +[target]: docs/target.md -[transformation]: docs/wiki/transformation.md +[transformation]: docs/transformation.md -[transition]: docs/wiki/transition.md +[transition]: docs/transition.md -[video_frame]: docs/wiki/video_frame.md +[video_frame]: docs/video_frame.md -[migrate]: docs/wiki/migrate.md +[migrate]: docs/migrate.md [comment]: <> (links) @@ -436,9 +439,9 @@ Apache 2.0. See the [LICENSE](LICENSE.txt) file for details. [Kotlin Coroutines]: https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro -[OkHttp]: https://github.com/square/okhttp/blob/master/okhttp/src/jvmMain/resources/META-INF/proguard/okhttp3.pro +[OkHttp]: https://square.github.io/okhttp/features/r8_proguard/ -[Okio]: https://github.com/square/okio/blob/master/okio/src/jvmMain/resources/META-INF/proguard/okio.pro +[Okio]: https://square.github.io/okio/ [compose_compiler_config.conf]: sketch-core/compose_compiler_config.conf diff --git a/README_zh.md b/README.zh.md similarity index 84% rename from README_zh.md rename to README.zh.md index c57ef612b9..d4bf4a56f8 100644 --- a/README_zh.md +++ b/README.zh.md @@ -18,11 +18,8 @@ Sketch 是专为 Compose Multiplatform 和 Android View 设计的图片加载库 ## 示例 App -* Android:请到 [Releases](https://github.com/panpf/sketch/releases) 页面下载最新版本的安装包 -* Web:https://panpf.github.io/sketch/app -* 桌面:使用 [kdoctor] 检查运行环境,并按照提示安装需要的软件,然后在项目根目录执行 - `./package_desktop.sh` 命令打包,安装包位置包含在输出中 -* iOS:请参考 [运行示例 App](#运行示例-app) 部分自行编译运行 +* Android、iOS、桌面版、Web 可部署包请到 [Releases](https://github.com/panpf/sketch/releases) 页面下载最新版本 +* Web 示例:https://panpf.github.io/sketch/app ## 安装 @@ -123,8 +120,14 @@ Sketch 支持自动发现并注册 Fetcher 和 Decoder 组件,在 jvm 平台 ### R8 / Proguard -Sketch 自己不需要配置任何混淆规则,但你可能需要为间接依赖的 [Kotlin Coroutines], [OkHttp], [Okio] -添加混淆配置 +1. Android、iOS、Web 等平台不需要配置任何混淆规则 +2. 桌面平台需要配置以下混淆规则: + ```proguard + # -------------------------- Sketch Privider ---------------------------- # + -keep class * implements com.github.panpf.sketch.util.DecoderProvider { *; } + -keep class * implements com.github.panpf.sketch.util.FetcherProvider { *; } + ``` +3. 可能还需要为间接依赖的 [Kotlin Coroutines], [OkHttp], [Okio] 等三方库添加混淆配置 ## 快速上手 @@ -311,7 +314,7 @@ Apache 2.0. 有关详细信息,请参阅 [LICENSE](LICENSE.txt) 文件. [license_image]: https://img.shields.io/badge/License-Apache%202-blue.svg -[logo_image]: docs/res/logo.png +[logo_image]: docs/images/logo.png [license_link]: https://www.apache.org/licenses/LICENSE-2.0 @@ -326,73 +329,73 @@ Apache 2.0. 有关详细信息,请参阅 [LICENSE](LICENSE.txt) 文件. [comment]: <> (wiki) -[animated_image]: docs/wiki/animated_image_zh.md +[animated_image]: docs/animated_image.zh.md -[apk_app_icon]: docs/wiki/apk_app_icon_zh.md +[apk_app_icon]: docs/apk_app_icon.zh.md -[compose]: docs/wiki/compose_zh.md +[compose]: docs/compose.zh.md -[decoder]: docs/wiki/decoder_zh.md +[decoder]: docs/decoder.zh.md -[download_cache]: docs/wiki/download_cache_zh.md +[download_cache]: docs/download_cache.zh.md -[exif_orientation]: docs/wiki/exif_orientation_zh.md +[exif_orientation]: docs/exif_orientation.zh.md -[fetcher]: docs/wiki/fetcher_zh.md +[fetcher]: docs/fetcher.zh.md -[getting_started]: docs/wiki/getting_started_zh.md +[getting_started]: docs/getting_started.zh.md -[register_component]: docs/wiki/register_component_zh.md +[register_component]: docs/register_component.zh.md -[http]: docs/wiki/http_zh.md +[http]: docs/http.zh.md -[image_options]: docs/wiki/image_options_zh.md +[image_options]: docs/image_options.zh.md -[lifecycle]: docs/wiki/lifecycle_zh.md +[lifecycle]: docs/lifecycle.zh.md -[listener]: docs/wiki/listener_zh.md +[listener]: docs/listener.zh.md -[log]: docs/wiki/log_zh.md +[log]: docs/log.zh.md -[long_image_grid_thumbnails]: docs/wiki/long_image_grid_thumbnails_zh.md +[long_image_grid_thumbnails]: docs/long_image_grid_thumbnails.zh.md -[memory_cache]: docs/wiki/memory_cache_zh.md +[memory_cache]: docs/memory_cache.zh.md -[mime_type_logo]: docs/wiki/mime_type_logo_zh.md +[mime_type_logo]: docs/mime_type_logo.zh.md -[pause_load_when_scrolling]: docs/wiki/pause_load_when_scrolling_zh.md +[pause_load_when_scrolling]: docs/pause_load_when_scrolling.zh.md -[preload]: docs/wiki/preload_zh.md +[preload]: docs/preload.zh.md -[download]: docs/wiki/download_image_zh.md +[download]: docs/download_image.zh.md -[progress_indicator]: docs/wiki/progress_indicator_zh.md +[progress_indicator]: docs/progress_indicator.zh.md -[request_interceptor]: docs/wiki/request_interceptor_zh.md +[request_interceptor]: docs/request_interceptor.zh.md -[decode_interceptor]: docs/wiki/decode_interceptor_zh.md +[decode_interceptor]: docs/decode_interceptor.zh.md -[resize]: docs/wiki/resize_zh.md +[resize]: docs/resize.zh.md -[result_cache]: docs/wiki/result_cache_zh.md +[result_cache]: docs/result_cache.zh.md -[save_cellular_traffic]: docs/wiki/save_cellular_traffic_zh.md +[save_cellular_traffic]: docs/save_cellular_traffic.zh.md -[sketch_image_view]: docs/wiki/sketch_image_view_zh.md +[sketch_image_view]: docs/sketch_image_view.zh.md -[state_image]: docs/wiki/state_image_zh.md +[state_image]: docs/state_image.zh.md -[svg]: docs/wiki/svg_zh.md +[svg]: docs/svg.zh.md -[target]: docs/wiki/target_zh.md +[target]: docs/target.zh.md -[transformation]: docs/wiki/transformation_zh.md +[transformation]: docs/transformation.zh.md -[transition]: docs/wiki/transition_zh.md +[transition]: docs/transition.zh.md -[video_frame]: docs/wiki/video_frame_zh.md +[video_frame]: docs/video_frame.zh.md -[migrate]: docs/wiki/migrate_zh.md +[migrate]: docs/migrate.zh.md [comment]: <> (links) @@ -409,9 +412,9 @@ Apache 2.0. 有关详细信息,请参阅 [LICENSE](LICENSE.txt) 文件. [Kotlin Coroutines]: https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro -[OkHttp]: https://github.com/square/okhttp/blob/master/okhttp/src/jvmMain/resources/META-INF/proguard/okhttp3.pro +[OkHttp]: https://square.github.io/okhttp/features/r8_proguard/ -[Okio]: https://github.com/square/okio/blob/master/okio/src/jvmMain/resources/META-INF/proguard/okio.pro +[Okio]: https://square.github.io/okio/ [compose_compiler_config.conf]: sketch-core/compose_compiler_config.conf @@ -421,6 +424,6 @@ Apache 2.0. 有关详细信息,请参阅 [LICENSE](LICENSE.txt) 文件. [comment]: <> (footer) -[CHANGELOG.md]: CHANGELOG_zh.md +[CHANGELOG.md]: CHANGELOG.zh.md [kdoctor]: https://github.com/Kotlin/kdoctor diff --git a/build.gradle.kts b/build.gradle.kts index ad2758014f..94abd49b48 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,12 +21,11 @@ buildscript { classpath(libs.gradlePlugin.buildkonfig) classpath(libs.gradlePlugin.jetbrainsCompose) classpath(libs.gradlePlugin.kotlin) - classpath(libs.gradlePlugin.kotlinSerialization) classpath(libs.gradlePlugin.kotlinComposeCompiler) + classpath(libs.gradlePlugin.kotlinSerialization) classpath(libs.gradlePlugin.kotlinxAtomicfu) classpath(libs.gradlePlugin.kotlinxCover) classpath(libs.gradlePlugin.mavenPublish) -// classpath(libs.gradlePlugin.dokka) } } @@ -34,6 +33,10 @@ plugins { alias(libs.plugins.dokka) } +tasks.register("cleanRootBuild", Delete::class) { + delete(rootProject.project.layout.buildDirectory.get().asFile.absolutePath) +} + allprojects { repositories { // maven { setUrl("https://maven.aliyun.com/repository/public") } // central、jcenter @@ -45,65 +48,35 @@ allprojects { // maven { setUrl("https://s01.oss.sonatype.org/content/repositories/snapshots") } // mavenLocal() } -} - -tasks.register("cleanRootBuild", Delete::class) { - delete(rootProject.project.layout.buildDirectory.get().asFile.absolutePath) -} - -allprojects { - kotlinDependenciesConfig() - jvmTargetConfig() - composeConfig() - publishConfig() - dokkaConfig() - applyOkioJsTestWorkaround() - androidTestConfig() -} -// TODO jetbrains-compose bug https://youtrack.jetbrains.com/issue/CMP-5831 -allprojects { - configurations.all { - resolutionStrategy.eachDependency { - if (requested.group == "org.jetbrains.kotlinx" && requested.name == "atomicfu") { - useVersion(libs.versions.kotlinx.atomicfu.get()) - } + // Minimum compatible Java version + afterEvaluate { + // Must be included in afterEvaluate to find the plugin + // Compose Multiplatform 1.8.0 must use JVM target 11+, and Android View also requires 1.8+ + val (version, target) = if (plugins.findPlugin("org.jetbrains.kotlin.plugin.compose") != null) { + JavaVersion.VERSION_11 to JvmTarget.JVM_11 + } else { + JavaVersion.VERSION_1_8 to JvmTarget.JVM_1_8 } - } -} - -fun Project.kotlinDependenciesConfig() { - dependencies { - modules { - module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") { - replacedBy("org.jetbrains.kotlin:kotlin-stdlib") - } - module("org.jetbrains.kotlin:kotlin-stdlib-jdk8") { - replacedBy("org.jetbrains.kotlin:kotlin-stdlib") - } + tasks.withType().configureEach { + sourceCompatibility = version.toString() + targetCompatibility = version.toString() + options.compilerArgs = options.compilerArgs + "-Xlint:-options" + } + tasks.withType().configureEach { + compilerOptions.jvmTarget = target } } -} - -fun Project.jvmTargetConfig() { - // Target JVM 8. - tasks.withType().configureEach { - sourceCompatibility = JavaVersion.VERSION_1_8.toString() - targetCompatibility = JavaVersion.VERSION_1_8.toString() - options.compilerArgs = options.compilerArgs + "-Xlint:-options" - } - tasks.withType().configureEach { - compilerOptions.jvmTarget = JvmTarget.JVM_1_8 - } -} -fun Project.composeConfig() { + // Add compilation configuration for Compose module plugins.withId("org.jetbrains.kotlin.plugin.compose") { extensions.configure { featureFlags.addAll( ComposeFeatureFlag.OptimizeNonSkippingGroups ) - stabilityConfigurationFile = rootDir.resolve("sketch-core/compose_compiler_config.conf") + stabilityConfigurationFiles.add { + rootDir.resolve("sketch-core/compose_compiler_config.conf") + } /** * Run the `./gradlew clean :sketch-compose:assembleRelease -PcomposeCompilerReports=true` command to generate a report, @@ -118,9 +91,24 @@ fun Project.composeConfig() { } } } -} -fun Project.publishConfig() { + // TODO jetbrains-compose bug https://youtrack.jetbrains.com/issue/CMP-5831 + configurations.all { + resolutionStrategy.eachDependency { + if (requested.group == "org.jetbrains.kotlinx" && requested.name == "atomicfu") { + useVersion(libs.versions.kotlinx.atomicfu.get()) + } + } + } + + // Uninstall test APKs after running instrumentation tests. + tasks.configureEach { + if (name == "connectedDebugAndroidTest") { + finalizedBy("uninstallDebugAndroidTest") + } + } + + // Configure publish plugin for all publishable library modules if ( // && hasProperty("mavenCentralUsername") // configured in the ~/.gradle/gradle.properties file // && hasProperty("mavenCentralPassword") // configured in the ~/.gradle/gradle.properties file @@ -147,16 +135,14 @@ fun Project.publishConfig() { } } } -} -fun Project.dokkaConfig() { - if ( - hasProperty("POM_ARTIFACT_ID") // configured in the module/gradle.properties file - ) { + // Configure Dokka plugin for all publishable library modules + if (hasProperty("POM_ARTIFACT_ID")) { // configured in the module/gradle.properties file apply { plugin("org.jetbrains.dokka") } } -} + applyOkioJsTestWorkaround() +} // https://github.com/square/okio/issues/1163 fun Project.applyOkioJsTestWorkaround() { @@ -202,13 +188,4 @@ fun Project.applyOkioJsTestWorkaround() { } } } -} - -fun Project.androidTestConfig() { - // Uninstall test APKs after running instrumentation tests. - tasks.configureEach { - if (name == "connectedDebugAndroidTest") { - finalizedBy("uninstallDebugAndroidTest") - } - } } \ No newline at end of file diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index cc182153be..02b96599d5 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -1,3 +1,5 @@ +rootProject.name = "buildSrc" // https://github.com/gradle/gradle/issues/30299 + dependencyResolutionManagement { versionCatalogs { create("libs") { diff --git a/buildSrc/src/main/kotlin/multiplatform.kt b/buildSrc/src/main/kotlin/multiplatform.kt index aa38bae250..7ed6a8ee7e 100644 --- a/buildSrc/src/main/kotlin/multiplatform.kt +++ b/buildSrc/src/main/kotlin/multiplatform.kt @@ -15,15 +15,13 @@ * limitations under the License. */ -@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) -@file:Suppress("unused") - import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Project import org.gradle.api.Task import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.invoke +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet @@ -76,6 +74,7 @@ fun Project.addAllMultiplatformTargets(vararg targets: MultiplatformTargets) { } if (targets.isEmpty() || targets.contains(MultiplatformTargets.WasmJs)) { + @OptIn(ExperimentalWasmDsl::class) wasmJs { // TODO: Fix wasm tests. browser { @@ -120,7 +119,7 @@ fun Project.addAllMultiplatformTargets(vararg targets: MultiplatformTargets) { // An error occurs when compiling js or wasmJs: // Resolving dependency configuration 'androidDebugAndroidTestCompilationApi' is not allowed as it is defined as 'canBeResolved=false'. //Instead, a resolvable ('canBeResolved=true') dependency configuration that extends 'androidDebugAndroidTestCompilationApi' should be resolved. -// if (targets.isEmpty() || targets.contains(MultiplatformTargets.Js) || targets.contains(MultiplatformTargets.WasmJs)) { +// if (targets.isEmpty() || targets.contains(MultiplatformTargets.WasmJs)) { // createSkikoWasmJsRuntimeDependency() // } } diff --git a/buildSrc/src/main/kotlin/projects.kt b/buildSrc/src/main/kotlin/projects.kt index 891cca7b2f..8afbe7f84c 100644 --- a/buildSrc/src/main/kotlin/projects.kt +++ b/buildSrc/src/main/kotlin/projects.kt @@ -18,7 +18,12 @@ import com.android.build.gradle.BaseExtension import com.android.build.gradle.LibraryExtension import com.android.build.gradle.TestExtension import com.android.build.gradle.internal.dsl.BaseAppModuleExtension +import org.gradle.api.JavaVersion import org.gradle.api.Project +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile fun Project.androidLibrary( nameSpace: String, @@ -30,9 +35,15 @@ fun Project.androidLibrary( enableAndroidTestCoverage = true } } - @Suppress("UnstableApiUsage") testOptions { - targetSdk = project.targetSdk + unitTests.all { test -> + test.testLogging { + exceptionFormat = TestExceptionFormat.FULL + showExceptions = true + showStackTraces = true + showCauses = false + } + } } action() } @@ -53,12 +64,8 @@ fun Project.androidApplication( fun Project.androidTest( name: String, - config: Boolean = false, action: TestExtension.() -> Unit = {}, ) = androidBase(name) { - buildFeatures { - buildConfig = config - } defaultConfig { vectorDrawables.useSupportLibrary = true } @@ -99,6 +106,19 @@ private fun Project.androidBase( // "VectorRaster", // ) // } + // Compose Multiplatform 1.8.0 must use JVM target 11+, and Android View also requires 1.8+ + val (version, target) = if (plugins.findPlugin("org.jetbrains.kotlin.plugin.compose") != null) { + JavaVersion.VERSION_11 to JvmTarget.JVM_11 + } else { + JavaVersion.VERSION_1_8 to JvmTarget.JVM_1_8 + } + compileOptions { + sourceCompatibility = version + targetCompatibility = version + } + tasks.withType().configureEach { + compilerOptions.jvmTarget.set(target) + } action() } // plugins.withId("org.jetbrains.kotlin.multiplatform") { diff --git a/buildSrc/src/main/kotlin/skikoWasm.kt b/buildSrc/src/main/kotlin/skikoWasm.kt index 945d6536be..b93efc456b 100644 --- a/buildSrc/src/main/kotlin/skikoWasm.kt +++ b/buildSrc/src/main/kotlin/skikoWasm.kt @@ -9,8 +9,8 @@ import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.artifacts.UnresolvedDependency import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.findByType +import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.support.uppercaseFirstChar -import org.gradle.kotlin.dsl.task import org.jetbrains.compose.web.tasks.UnpackSkikoWasmRuntimeTask import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget @@ -44,12 +44,13 @@ private fun Collection.configureExperimentalWebApplication(pro forEach { val mainCompilation = it.compilations.getByName("main") val testCompilation = it.compilations.getByName("test") - val unpackedRuntimeDir = project.layout.buildDirectory.dir("compose/skiko-wasm/${it.targetName}") + val unpackedRuntimeDir = + project.layout.buildDirectory.dir("compose/skiko-wasm/${it.targetName}") mainCompilation.defaultSourceSet.resources.srcDir(unpackedRuntimeDir) testCompilation.defaultSourceSet.resources.srcDir(unpackedRuntimeDir) val taskName = "unpackSkikoWasmRuntime${it.targetName.uppercaseFirstChar()}" - val unpackRuntime = project.task(taskName) { + val unpackRuntime = project.tasks.register(taskName) { skikoRuntimeFiles = skikoJsWasmRuntimeConfiguration outputDir.set(unpackedRuntimeDir) } diff --git a/build_docs.sh b/build_docs.sh new file mode 100755 index 0000000000..c38f1296e8 --- /dev/null +++ b/build_docs.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +set -e + +cp README.md docs/index.md +cp README.zh.md docs/index.zh.md +cp CHANGELOG.md docs/CHANGELOG.md +cp CHANGELOG.zh.md docs/CHANGELOG.zh.md + +perl -pi -e 's|\(README.zh.md\)|\(index.zh.md\)|g' docs/index.md +perl -pi -e 's|\(LICENSE.txt\)|\(../LICENSE.txt\)|g' docs/index.md +perl -pi -e 's|\(docs/|\(|g' docs/index.md +perl -pi -e 's|]: docs/|]: |g' docs/index.md +perl -pi -e 's|]: sketch-|]: ../sketch-|g' docs/index.md + +perl -pi -e 's|\(README.md\)|\(index.md\)|g' docs/index.zh.md +perl -pi -e 's|\(LICENSE.txt\)|\(../LICENSE.txt\)|g' docs/index.zh.md +perl -pi -e 's|\(docs/|\(|g' docs/index.zh.md +perl -pi -e 's|]: docs/|]: |g' docs/index.zh.md +perl -pi -e 's|]: sketch-|]: ../sketch-|g' docs/index.zh.md + +perl -pi -e 's|\(docs/|\(|g' docs/CHANGELOG.md +perl -pi -e 's|\(docs/|\(|g' docs/CHANGELOG.zh.md + +find docs -type f -name "*.md" -exec perl -pi -e 's|]: ../|]: https://github.com/panpf/sketch/blob/main/|g' {} + +find docs -type f -name "*.md" -exec perl -pi -e 's|\(../|\(https://github.com/panpf/sketch/blob/main/|g' {} + + +mkdocs build + +git checkout -- docs +git clean -f -d docs \ No newline at end of file diff --git a/docs/wiki/animated_image.md b/docs/animated_image.md similarity index 69% rename from docs/wiki/animated_image.md rename to docs/animated_image.md index ec2e3ad9c2..f0dfc53d24 100644 --- a/docs/wiki/animated_image.md +++ b/docs/animated_image.md @@ -1,6 +1,6 @@ # Animated Image -Translations: [简体中文](animated_image_zh.md) +Translations: [简体中文](animated_image.zh.md) Sketch provides the `sketch-animated-*` series of modules to support animated graphics. The supported platforms and differences are as follows: @@ -125,45 +125,45 @@ ImageRequest(context, "https://www.example.com/image.gif") { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[AnimatableDrawable]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/AnimatableDrawable.kt +[AnimatableDrawable]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/AnimatableDrawable.kt -[AnimatablePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/AnimatablePainter.kt +[AnimatablePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/AnimatablePainter.kt -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[GenericComposeTarget]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/GenericComposeTarget.kt +[GenericComposeTarget]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/GenericComposeTarget.kt -[GenericViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/GenericViewTarget.kt +[GenericViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/GenericViewTarget.kt -[ImageDecoderGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt +[ImageDecoderGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt -[KoralGifDecoder]: ../../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt +[KoralGifDecoder]: ../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt -[MovieGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt +[MovieGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt -[SkiaGifDecoder]: ../../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt +[SkiaGifDecoder]: ../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt -[ImageDecoderAnimatedHeifDecoder]: ../../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt +[ImageDecoderAnimatedHeifDecoder]: ../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt [Movie]: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/graphics/java/android/graphics/Movie.java -[ImageDecoderAnimatedWebpDecoder]: ../../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt +[ImageDecoderAnimatedWebpDecoder]: ../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt -[SkiaAnimatedWebpDecoder]: ../../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt +[SkiaAnimatedWebpDecoder]: ../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt -[GifDecoderProvider]: ../../sketch-animated-gif/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/GifDecoderProvider.common.kt +[GifDecoderProvider]: ../sketch-animated-gif/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/GifDecoderProvider.common.kt -[KoralGifDecoderProvider]: ../../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/internal/KoralGifDecoderProvider.kt +[KoralGifDecoderProvider]: ../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/internal/KoralGifDecoderProvider.kt -[AnimatedWebpDecoderProvider]: ../../sketch-animated-webp/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/AnimatedWebpDecoderProvider.common.kt +[AnimatedWebpDecoderProvider]: ../sketch-animated-webp/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/AnimatedWebpDecoderProvider.common.kt -[ImageDecoderAnimatedHeifDecoderProvider]: ../../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/internal/ImageDecoderAnimatedHeifDecoderProvider.kt +[ImageDecoderAnimatedHeifDecoderProvider]: ../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/internal/ImageDecoderAnimatedHeifDecoderProvider.kt -[PenfeizhouAnimatedWebpDecoder]: ../../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/PenfeizhouAnimatedWebpDecoder.kt +[PenfeizhouAnimatedWebpDecoder]: ../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/PenfeizhouAnimatedWebpDecoder.kt [comment]: <> (wiki) diff --git a/docs/wiki/animated_image_zh.md b/docs/animated_image.zh.md similarity index 65% rename from docs/wiki/animated_image_zh.md rename to docs/animated_image.zh.md index affb114b5e..46c0799f53 100644 --- a/docs/wiki/animated_image_zh.md +++ b/docs/animated_image.zh.md @@ -28,7 +28,7 @@ implementation("io.github.panpf.sketch4:sketch-animated-gif:${LAST_VERSION}") > [!IMPORTANT] > 上述组件都支持自动注册,你只需要导入即可,无需额外配置,如果你需要手动注册, -> 请阅读文档:[《注册组件》](register_component_zh.md) +> 请阅读文档:[《注册组件》](register_component.zh.md) ## 加载动图 @@ -114,46 +114,46 @@ ImageRequest(context, "https://www.example.com/image.gif") { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[AnimatableDrawable]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/AnimatableDrawable.kt +[AnimatableDrawable]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/AnimatableDrawable.kt -[AnimatablePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/AnimatablePainter.kt +[AnimatablePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/AnimatablePainter.kt -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[GenericComposeTarget]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/GenericComposeTarget.kt +[GenericComposeTarget]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/GenericComposeTarget.kt -[GenericViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/GenericViewTarget.kt +[GenericViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/GenericViewTarget.kt -[ImageDecoderGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt +[ImageDecoderGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt -[KoralGifDecoder]: ../../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt +[KoralGifDecoder]: ../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt -[MovieGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt +[MovieGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt -[SkiaGifDecoder]: ../../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt +[SkiaGifDecoder]: ../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt -[ImageDecoderAnimatedHeifDecoder]: ../../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt +[ImageDecoderAnimatedHeifDecoder]: ../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt [Movie]: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/graphics/java/android/graphics/Movie.java -[ImageDecoderAnimatedWebpDecoder]: ../../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt +[ImageDecoderAnimatedWebpDecoder]: ../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt -[SkiaAnimatedWebpDecoder]: ../../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt +[SkiaAnimatedWebpDecoder]: ../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt -[GifDecoderProvider]: ../../sketch-animated-gif/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/GifDecoderProvider.common.kt +[GifDecoderProvider]: ../sketch-animated-gif/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/GifDecoderProvider.common.kt -[KoralGifDecoderProvider]: ../../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/internal/KoralGifDecoderProvider.kt +[KoralGifDecoderProvider]: ../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/internal/KoralGifDecoderProvider.kt -[AnimatedWebpDecoderProvider]: ../../sketch-animated-webp/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/AnimatedWebpDecoderProvider.common.kt +[AnimatedWebpDecoderProvider]: ../sketch-animated-webp/src/commonMain/kotlin/com/github/panpf/sketch/decode/internal/AnimatedWebpDecoderProvider.common.kt -[ImageDecoderAnimatedHeifDecoderProvider]: ../../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/internal/ImageDecoderAnimatedHeifDecoderProvider.kt +[ImageDecoderAnimatedHeifDecoderProvider]: ../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/internal/ImageDecoderAnimatedHeifDecoderProvider.kt -[PenfeizhouAnimatedWebpDecoder]: ../../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/PenfeizhouAnimatedWebpDecoder.kt +[PenfeizhouAnimatedWebpDecoder]: ../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/PenfeizhouAnimatedWebpDecoder.kt [comment]: <> (wiki) -[getting_started_platform_different]: getting_started_zh.md#平台差异 \ No newline at end of file +[getting_started_platform_different]: getting_started.zh.md#平台差异 \ No newline at end of file diff --git a/docs/wiki/apk_app_icon.md b/docs/apk_app_icon.md similarity index 80% rename from docs/wiki/apk_app_icon.md rename to docs/apk_app_icon.md index 7f81c76595..acdfbc64c9 100644 --- a/docs/wiki/apk_app_icon.md +++ b/docs/apk_app_icon.md @@ -1,6 +1,6 @@ # Load Icon For Apk Or Installed App -Translations: [简体中文](apk_app_icon_zh.md) +Translations: [简体中文](apk_app_icon.zh.md) ## Load Apk Icon @@ -58,10 +58,10 @@ sketch.enqueue(ImageRequest(context, uri = appIconUri)) [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[ApkIconDecoder]: ../../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt +[ApkIconDecoder]: ../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt -[AppIconUriFetcher]: ../../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt +[AppIconUriFetcher]: ../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt \ No newline at end of file +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt \ No newline at end of file diff --git a/docs/wiki/apk_app_icon_zh.md b/docs/apk_app_icon.zh.md similarity index 80% rename from docs/wiki/apk_app_icon_zh.md rename to docs/apk_app_icon.zh.md index 311383e5ec..2a8e4b1e6a 100644 --- a/docs/wiki/apk_app_icon_zh.md +++ b/docs/apk_app_icon.zh.md @@ -20,7 +20,7 @@ sketch.enqueue(ImageRequest(context, uri = "/sdcard/sample.apk")) > [!IMPORTANT] > 1. `sketch-extensions-apkicon` - 模块支持自动注册组件,有关组件注册的详细内容请查看文档:[《注册组件》](register_component_zh.md) + 模块支持自动注册组件,有关组件注册的详细内容请查看文档:[《注册组件》](register_component.zh.md) > 2. 仅支持 Android 平台 ## 加载已安装 App 的图标 @@ -45,7 +45,7 @@ sketch.enqueue(ImageRequest(context, uri = appIconUri)) > 1. versionCode:App 的版本号。必须传入正确的版本号,因为对图标进行修改时就会将修改后的图标缓存在磁盘上,如果只用 packageName 作为缓存 key 那么 App 版本更新后图标即使改变了缓存也不会刷新 > 2. `sketch-extensions-appicon` - 模块支持自动注册组件,有关组件注册的详细内容请查看文档:[《注册组件》](register_component_zh.md) + 模块支持自动注册组件,有关组件注册的详细内容请查看文档:[《注册组件》](register_component.zh.md) > 3. 仅支持 Android 平台 [comment]: <> (classs) @@ -54,10 +54,10 @@ sketch.enqueue(ImageRequest(context, uri = appIconUri)) [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[ApkIconDecoder]: ../../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt +[ApkIconDecoder]: ../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt -[AppIconUriFetcher]: ../../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt +[AppIconUriFetcher]: ../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt diff --git a/docs/wiki/compose.md b/docs/compose.md similarity index 87% rename from docs/wiki/compose.md rename to docs/compose.md index c63ee918a4..37078b56ca 100644 --- a/docs/wiki/compose.md +++ b/docs/compose.md @@ -1,6 +1,6 @@ # Compose -Translations: [简体中文](compose_zh.md) +Translations: [简体中文](compose.zh.md) ## AsyncImage @@ -189,16 +189,16 @@ configured by [AsyncImageState] [comment]: <> (classs) -[AsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt +[AsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt -[AsyncImagePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt +[AsyncImagePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt -[SubcomposeAsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt +[SubcomposeAsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Listener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt +[Listener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt -[ProgressListener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt +[ProgressListener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt diff --git a/docs/wiki/compose_zh.md b/docs/compose.zh.md similarity index 87% rename from docs/wiki/compose_zh.md rename to docs/compose.zh.md index 53d881a8d8..40f61fd2c5 100644 --- a/docs/wiki/compose_zh.md +++ b/docs/compose.zh.md @@ -180,16 +180,16 @@ target() 方法,这会导致 App 崩溃,因为 Target 必须由 [AsyncImageS [comment]: <> (classs) -[AsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt +[AsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt -[AsyncImagePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt +[AsyncImagePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt -[SubcomposeAsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt +[SubcomposeAsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Listener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt +[Listener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt -[ProgressListener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt \ No newline at end of file +[ProgressListener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt \ No newline at end of file diff --git a/docs/wiki/decode_interceptor.md b/docs/decode_interceptor.md similarity index 87% rename from docs/wiki/decode_interceptor.md rename to docs/decode_interceptor.md index 70142e9e96..ac9d75e48c 100644 --- a/docs/wiki/decode_interceptor.md +++ b/docs/decode_interceptor.md @@ -1,6 +1,6 @@ # DecodeInterceptor -Translations: [简体中文](decode_interceptor_zh.md) +Translations: [简体中文](decode_interceptor.zh.md) The decoding process of Sketch supports changing the input and output before and after decoding through interceptors. @@ -52,6 +52,6 @@ ImageRequest(context, "https://example.com/image.jpg") { [comment]: <> (classs) -[DecodeInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeInterceptor.kt +[DecodeInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeInterceptor.kt -[DecodeResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeResult.kt \ No newline at end of file +[DecodeResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeResult.kt \ No newline at end of file diff --git a/docs/wiki/decode_interceptor_zh.md b/docs/decode_interceptor.zh.md similarity index 88% rename from docs/wiki/decode_interceptor_zh.md rename to docs/decode_interceptor.zh.md index 2abb7b1326..35ddc9952f 100644 --- a/docs/wiki/decode_interceptor_zh.md +++ b/docs/decode_interceptor.zh.md @@ -48,6 +48,6 @@ ImageRequest(context, "https://example.com/image.jpg") { [comment]: <> (classs) -[DecodeInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeInterceptor.kt +[DecodeInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeInterceptor.kt -[DecodeResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeResult.kt \ No newline at end of file +[DecodeResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/DecodeResult.kt \ No newline at end of file diff --git a/docs/wiki/decoder.md b/docs/decoder.md similarity index 81% rename from docs/wiki/decoder.md rename to docs/decoder.md index 13fa47fd95..3e38077ace 100644 --- a/docs/wiki/decoder.md +++ b/docs/decoder.md @@ -1,6 +1,6 @@ # Decoder -Translations: [简体中文](decoder_zh.md) +Translations: [简体中文](decoder.zh.md) [Decoder] is used to decode image files. Each supported image type has a corresponding [Decoder] implementation, as shown in the following table: @@ -142,35 +142,35 @@ ImageRequest(context, "https://example.com/image.jpg") { [comment]: <> (classs) -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[FetchResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt +[FetchResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt -[BitmapFactoryDecoder]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/BitmapFactoryDecoder.kt +[BitmapFactoryDecoder]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/BitmapFactoryDecoder.kt -[FFmpegVideoFrameDecoder]: ../../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt +[FFmpegVideoFrameDecoder]: ../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt -[ApkIconDecoder]: ../../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt +[ApkIconDecoder]: ../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt -[VideoFrameDecoder]: ../../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt +[VideoFrameDecoder]: ../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt -[SvgDecoder]: ../../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt +[SvgDecoder]: ../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt -[DrawableDecoder]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt +[DrawableDecoder]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt -[ImageDecoderGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt +[ImageDecoderGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt -[ImageDecoderAnimatedHeifDecoder]: ../../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt +[ImageDecoderAnimatedHeifDecoder]: ../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt -[ImageDecoderAnimatedWebpDecoder]: ../../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt +[ImageDecoderAnimatedWebpDecoder]: ../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt -[KoralGifDecoder]: ../../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt +[KoralGifDecoder]: ../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt -[MovieGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt +[MovieGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt [FFmpegMediaMetadataRetriever]: https://github.com/wseemann/FFmpegMediaMetadataRetriever @@ -186,10 +186,10 @@ ImageRequest(context, "https://example.com/image.jpg") { [MediaMetadataRetriever]: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/kotlin/android/media/MediaMetadataRetriever.java -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[SkiaDecoder]: ../../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaDecoder.kt +[SkiaDecoder]: ../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaDecoder.kt -[SkiaGifDecoder]: ../../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt +[SkiaGifDecoder]: ../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt -[SkiaAnimatedWebpDecoder]: ../../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt \ No newline at end of file +[SkiaAnimatedWebpDecoder]: ../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt \ No newline at end of file diff --git a/docs/wiki/decoder_zh.md b/docs/decoder.zh.md similarity index 73% rename from docs/wiki/decoder_zh.md rename to docs/decoder.zh.md index 142813ff61..c2fa8d4727 100644 --- a/docs/wiki/decoder_zh.md +++ b/docs/decoder.zh.md @@ -29,40 +29,40 @@ | Apk Icon | [ApkIconDecoder] | sketch-extensions-core | ✅ | ❌ | ❌ | ❌ | * [ApkIconDecoder] 在 Android 平台上解码 Apk - 文件的图标([了解更多](apk_app_icon_zh.md#加载-apk-图标)) + 文件的图标([了解更多](apk_app_icon.zh.md#加载-apk-图标)) * [BitmapFactoryDecoder] 在 Android 平台上使用 Android 内置的 [BitmapFactory] 解码图片,它是最后的解码器 * [DrawableDecoder] 在 Android 平台上解码 vector、shape 等 Android 支持的 xml drawable 图片 * [ImageDecoderGifDecoder] 在 Android 平台上使用 Android 内置的 [ImageDecoder] 解码 gif - 动图([了解更多](animated_image_zh.md)) + 动图([了解更多](animated_image.zh.md)) * [KoralGifDecoder] 在 Android 平台上使用 koral-- 的 [android-gif-drawable][android-gif-drawable] - 库解码 gif 动图([了解更多](animated_image_zh.md)) + 库解码 gif 动图([了解更多](animated_image.zh.md)) * [MovieGifDecoder] 在 Android 平台上使用 Android 内置的 [Movie] 解码 gif - 动图([了解更多](animated_image_zh.md)) + 动图([了解更多](animated_image.zh.md)) * [SkiaGifDecoder] 在非 Android 平台上使用 Skia 内置的 Codec 解码 gif - 动图([了解更多](animated_image_zh.md)) + 动图([了解更多](animated_image.zh.md)) * [ImageDecoderAnimatedHeifDecoder] 使用 Android 内置的 [ImageDecoder] 解码 heif - 动图([了解更多](animated_image_zh.md)) + 动图([了解更多](animated_image.zh.md)) * [SkiaDecoder] 在非 Android 平台上使用 Skia 内置的 Image 解码图片,它是最后的解码器 * [SvgDecoder] 在 Android 平台上使用 BigBadaboom 的 [androidsvg] 库,在非 Android 平台上使用 Skia 内置的 - SVGDOM 解码静态 svg 文件([了解更多](svg_zh.md)) + SVGDOM 解码静态 svg 文件([了解更多](svg.zh.md)) * [ImageDecoderAnimatedWebpDecoder] 在 Android 平台上使用 Android 内置的 [ImageDecoder] 解码 webp - 动图([了解更多](animated_image_zh.md)) + 动图([了解更多](animated_image.zh.md)) * [SkiaAnimatedWebpDecoder] 在非 Android 平台上使用 Skia 内置的 Codec 解码 webp - 动图([了解更多](animated_image_zh.md)) + 动图([了解更多](animated_image.zh.md)) * [VideoFrameDecoder] 在 Android 平台上使用 Android 内置的 [MediaMetadataRetriever] - 类解码视频文件的帧([了解更多](video_frame_zh.md)) + 类解码视频文件的帧([了解更多](video_frame.zh.md)) * [FFmpegVideoFrameDecoder] 在 Android 平台上使用 wseemann 的 [FFmpegMediaMetadataRetriever] - 库解码视频帧([了解更多](video_frame_zh.md)) + 库解码视频帧([了解更多](video_frame.zh.md)) > [!IMPORTANT] > 上述组件都支持自动注册,你只需要导入即可,无需额外配置,如果你需要手动注册, -> 请阅读文档:[《注册组件》](register_component_zh.md) +> 请阅读文档:[《注册组件》](register_component.zh.md) ### 扩展 Decoder 先实现 [Decoder] 接口定义你的 Decoder 和它的 Factory -然后参考文档 [《注册组件》](register_component_zh.md) 注册你的 Decoder 即可 +然后参考文档 [《注册组件》](register_component.zh.md) 注册你的 Decoder 即可 > [!CAUTION] > 1. 自定义 [Decoder] 需要应用 ImageRequest 中的很多与图片质量和尺寸相关的属性,例如 @@ -133,35 +133,35 @@ ImageRequest(context, "https://example.com/image.jpg") { [comment]: <> (classs) -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[FetchResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt +[FetchResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt -[BitmapFactoryDecoder]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/BitmapFactoryDecoder.kt +[BitmapFactoryDecoder]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/BitmapFactoryDecoder.kt -[FFmpegVideoFrameDecoder]: ../../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt +[FFmpegVideoFrameDecoder]: ../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt -[ApkIconDecoder]: ../../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt +[ApkIconDecoder]: ../sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/decode/ApkIconDecoder.kt -[VideoFrameDecoder]: ../../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt +[VideoFrameDecoder]: ../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt -[SvgDecoder]: ../../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt +[SvgDecoder]: ../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt -[DrawableDecoder]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt +[DrawableDecoder]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt -[ImageDecoderGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt +[ImageDecoderGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderGifDecoder.kt -[ImageDecoderAnimatedHeifDecoder]: ../../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt +[ImageDecoderAnimatedHeifDecoder]: ../sketch-animated-heif/src/main/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedHeifDecoder.kt -[ImageDecoderAnimatedWebpDecoder]: ../../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt +[ImageDecoderAnimatedWebpDecoder]: ../sketch-animated-webp/src/androidMain/kotlin/com/github/panpf/sketch/decode/ImageDecoderAnimatedWebpDecoder.kt -[KoralGifDecoder]: ../../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt +[KoralGifDecoder]: ../sketch-animated-gif-koral/src/main/kotlin/com/github/panpf/sketch/decode/KoralGifDecoder.kt -[MovieGifDecoder]: ../../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt +[MovieGifDecoder]: ../sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/decode/MovieGifDecoder.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt [FFmpegMediaMetadataRetriever]: https://github.com/wseemann/FFmpegMediaMetadataRetriever @@ -177,10 +177,10 @@ ImageRequest(context, "https://example.com/image.jpg") { [MediaMetadataRetriever]: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/kotlin/android/media/MediaMetadataRetriever.java -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[SkiaDecoder]: ../../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaDecoder.kt +[SkiaDecoder]: ../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaDecoder.kt -[SkiaGifDecoder]: ../../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt +[SkiaGifDecoder]: ../sketch-animated-gif/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaGifDecoder.kt -[SkiaAnimatedWebpDecoder]: ../../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt \ No newline at end of file +[SkiaAnimatedWebpDecoder]: ../sketch-animated-webp/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/SkiaAnimatedWebpDecoder.kt \ No newline at end of file diff --git a/docs/wiki/download_cache.md b/docs/download_cache.md similarity index 87% rename from docs/wiki/download_cache.md rename to docs/download_cache.md index 9f793f4739..3cf3a0a912 100644 --- a/docs/wiki/download_cache.md +++ b/docs/download_cache.md @@ -1,6 +1,6 @@ # Download Cache -Translations: [简体中文](download_cache_zh.md) +Translations: [简体中文](download_cache.zh.md) In order to avoid repeatedly downloading images from the Internet and improve the loading speed of images [Sketch] introduces download caching. [HttpUriFetcher] will first store images persistently @@ -172,16 +172,16 @@ The download cache is cleared under the following circumstances: 2. Actively call the `abort()` method of [DiskCache].Editor 3. Automatically clear older caches when maximum capacity is reached -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[DiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt +[DiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt -[LruDiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt +[LruDiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[HttpUriFetcher]: ../../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt +[HttpUriFetcher]: ../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt -[CachePolicy]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file +[CachePolicy]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file diff --git a/docs/wiki/download_cache_zh.md b/docs/download_cache.zh.md similarity index 87% rename from docs/wiki/download_cache_zh.md rename to docs/download_cache.zh.md index fafe5f11ed..af650874d7 100644 --- a/docs/wiki/download_cache_zh.md +++ b/docs/download_cache.zh.md @@ -165,16 +165,16 @@ scope.launch { 2. 主动调用 [DiskCache].Editor 的 `abort()` 方法 3. 达到最大容量时自动清除较旧的缓存 -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[DiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt +[DiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt -[LruDiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt +[LruDiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[HttpUriFetcher]: ../../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt +[HttpUriFetcher]: ../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt -[CachePolicy]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file +[CachePolicy]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file diff --git a/docs/wiki/download_image.md b/docs/download_image.md similarity index 81% rename from docs/wiki/download_image.md rename to docs/download_image.md index f5b822f880..3bbbc387b9 100644 --- a/docs/wiki/download_image.md +++ b/docs/download_image.md @@ -1,6 +1,6 @@ # Download Image -Translations: [简体中文](download_image_zh.md) +Translations: [简体中文](download_image.zh.md) Sometimes we need to download pictures to the disk cache in advance, or export pictures from the Internet to the album. In this case, we need to download the pictures first. @@ -52,8 +52,8 @@ scope.launch { > 2. When [Sketch].downloadCache is not available (JS) or [ImageRequest] .downloadCachePolicy.readEnabled is false, [DownloadData].Bytes is returned -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[DownloadData]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/DownloadData.kt \ No newline at end of file +[DownloadData]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/DownloadData.kt \ No newline at end of file diff --git a/docs/wiki/download_image_zh.md b/docs/download_image.zh.md similarity index 83% rename from docs/wiki/download_image_zh.md rename to docs/download_image.zh.md index 093a084153..a439605e62 100644 --- a/docs/wiki/download_image_zh.md +++ b/docs/download_image.zh.md @@ -50,8 +50,8 @@ scope.launch { > 2. [Sketch].downloadCache 不可用(JS)或 [ImageRequest].downloadCachePolicy.readEnabled 为 false 时返回的是 [DownloadData].Bytes -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[DownloadData]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/DownloadData.kt \ No newline at end of file +[DownloadData]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/DownloadData.kt \ No newline at end of file diff --git a/docs/wiki/exif_orientation.md b/docs/exif_orientation.md similarity index 87% rename from docs/wiki/exif_orientation.md rename to docs/exif_orientation.md index 4ae8e0cd74..568e1e3e82 100644 --- a/docs/wiki/exif_orientation.md +++ b/docs/exif_orientation.md @@ -1,6 +1,6 @@ # Exif Orientation -Translations: [简体中文](exif_orientation_zh.md) +Translations: [简体中文](exif_orientation.zh.md) Sketch supports restoring the orientation of images based on their Exif information. This feature is forcibly turned on and cannot be turned off. diff --git a/docs/wiki/exif_orientation_zh.md b/docs/exif_orientation.zh.md similarity index 100% rename from docs/wiki/exif_orientation_zh.md rename to docs/exif_orientation.zh.md diff --git a/docs/wiki/fetcher.md b/docs/fetcher.md similarity index 64% rename from docs/wiki/fetcher.md rename to docs/fetcher.md index 8e55430c96..701e436552 100644 --- a/docs/wiki/fetcher.md +++ b/docs/fetcher.md @@ -1,6 +1,6 @@ # Fetcher -Translations: [简体中文](fetcher_zh.md) +Translations: [简体中文](fetcher.zh.md) [Fetcher] is used to get data from uri, return [FetchResult], and hand it over to [Decoder] for use. @@ -12,7 +12,7 @@ following table: | http://, https:// | [HurlHttpUriFetcher] | - | sketch-http-hurl | ✅ | ❌ | ✅ | ❌ | | http://, https:// | [OkHttpHttpUriFetcher] | - | sketch-http-okhttp | ✅ | ❌ | ✅ | ❌ | | http://, https:// | [KtorHttpUriFetcher] | - | sketch-http-ktor3 | ✅ | ✅ | ✅ | ✅ | -| file://, / | [FileUriFetcher] | newFileUri() | - | ✅ | ✅ | ✅ | ✅ | +| file://, /, D:/ | [FileUriFetcher] | newFileUri() | - | ✅ | ✅ | ✅ | ✅ | | file:///compose_resource/ | [ComposeResourceUriFetcher] | newComposeResourceUri() | sketch-compose-resources | ✅ | ✅ | ✅ | ✅ | | data:image/, data:img/ | [Base64UriFetcher] | newBase64Uri() | - | ✅ | ✅ | ✅ | ✅ | | file:///android_asset/ | [AssetUriFetcher] | newAssetUri() | - | ✅ | ❌ | ❌ | ❌ | @@ -22,9 +22,9 @@ following table: | file:///kotlin_resource/ | [KotlinResourceUriFetcher] | newKotlinResourceUri() | - | ❌ | ✅ | ✅ | ❌ | * [HurlHttpUriFetcher]: Use the HttpURLConnection that comes with jvm to load images from the - network. [Learn more](http_zh.md) -* [OkHttpHttpUriFetcher]: Use OkHttp to load images from the network. [Learn more](http_zh.md) -* [KtorHttpUriFetcher]: Use Ktor to load images from the network. [Learn more](http_zh.md) + network. [Learn more](http.md) +* [OkHttpHttpUriFetcher]: Use OkHttp to load images from the network. [Learn more](http.md) +* [KtorHttpUriFetcher]: Use Ktor to load images from the network. [Learn more](http.md) * [AssetUriFetcher]: is used to load images from the Android assets directory * [ContentUriFetcher]: ContentResolver for Android to load images * [ResourceUriFetcher]: is used to load images from Android's resources directory @@ -49,34 +49,34 @@ Then refer to the document [《Register component》](register_component.md) to [comment]: <> (classs) -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Fetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt +[Fetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt -[FetchResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt +[FetchResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt -[AssetUriFetcher]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/AssetUriFetcher.kt +[AssetUriFetcher]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/AssetUriFetcher.kt -[Base64UriFetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Base64UriFetcher.kt +[Base64UriFetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Base64UriFetcher.kt -[ContentUriFetcher]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ContentUriFetcher.kt +[ContentUriFetcher]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ContentUriFetcher.kt -[FileUriFetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt +[FileUriFetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt -[HurlHttpUriFetcher]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt +[HurlHttpUriFetcher]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt -[OkHttpHttpUriFetcher]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt +[OkHttpHttpUriFetcher]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt -[KtorHttpUriFetcher]: ../../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt +[KtorHttpUriFetcher]: ../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt -[ResourceUriFetcher]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ResourceUriFetcher.kt +[ResourceUriFetcher]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ResourceUriFetcher.kt -[AppIconUriFetcher]: ../../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt +[AppIconUriFetcher]: ../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt -[KotlinResourceUriFetcher]: ../../sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/fetch/KotlinResourceUriFetcher.kt +[KotlinResourceUriFetcher]: ../sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/fetch/KotlinResourceUriFetcher.kt -[ComposeResourceUriFetcher]: ../../sketch-compose-resources/src/commonMain/kotlin/com/github/panpf/sketch/fetch/ComposeResourceUriFetcher.kt \ No newline at end of file +[ComposeResourceUriFetcher]: ../sketch-compose-resources/src/commonMain/kotlin/com/github/panpf/sketch/fetch/ComposeResourceUriFetcher.kt \ No newline at end of file diff --git a/docs/wiki/fetcher_zh.md b/docs/fetcher.zh.md similarity index 60% rename from docs/wiki/fetcher_zh.md rename to docs/fetcher.zh.md index fe743a1a08..5757b37210 100644 --- a/docs/wiki/fetcher_zh.md +++ b/docs/fetcher.zh.md @@ -11,7 +11,7 @@ | http://, https:// | [HurlHttpUriFetcher] | - | sketch-http-hurl | ✅ | ❌ | ✅ | ❌ | | http://, https:// | [OkHttpHttpUriFetcher] | - | sketch-http-okhttp | ✅ | ❌ | ✅ | ❌ | | http://, https:// | [KtorHttpUriFetcher] | - | sketch-http-ktor3 | ✅ | ✅ | ✅ | ✅ | -| file://, / | [FileUriFetcher] | newFileUri() | - | ✅ | ✅ | ✅ | ✅ | +| file://, /, D:/ | [FileUriFetcher] | newFileUri() | - | ✅ | ✅ | ✅ | ✅ | | file:///compose_resource/ | [ComposeResourceUriFetcher] | newComposeResourceUri() | sketch-compose-resources | ✅ | ✅ | ✅ | ✅ | | data:image/jpeg;base64 | [Base64UriFetcher] | newBase64Uri() | - | ✅ | ✅ | ✅ | ✅ | | file:///android_asset/ | [AssetUriFetcher] | newAssetUri() | - | ✅ | ❌ | ❌ | ❌ | @@ -20,14 +20,14 @@ | app.icon:// | [AppIconUriFetcher] | newAppIconUri() | sketch-extensions-appicon | ✅ | ❌ | ❌ | ❌ | | file:///kotlin_resource/ | [KotlinResourceUriFetcher] | newKotlinResourceUri() | - | ❌ | ✅ | ✅ | ❌ | -* [HurlHttpUriFetcher]:使用 jvm 自带的 HttpURLConnection 从网络加载图片。[了解更多](http_zh.md) -* [OkHttpHttpUriFetcher]:使用 OkHttp 从网络加载图片。[了解更多](http_zh.md) -* [KtorHttpUriFetcher]:使用 Ktor 从网络加载图片。[了解更多](http_zh.md) +* [HurlHttpUriFetcher]:使用 jvm 自带的 HttpURLConnection 从网络加载图片。[了解更多](http.zh.md) +* [OkHttpHttpUriFetcher]:使用 OkHttp 从网络加载图片。[了解更多](http.zh.md) +* [KtorHttpUriFetcher]:使用 Ktor 从网络加载图片。[了解更多](http.zh.md) * [AssetUriFetcher]:用于从 Android 的 assets 目录加载图片 * [ContentUriFetcher]:用于 Android 的 ContentResolver 加载图片 * [ResourceUriFetcher]:用于从 Android 的 resources 目录加载图片 * [AppIconUriFetcher]:用于加载已安装 App 的图标,它还需要依赖 `sketch-extensions-core` - 模块。[了解更多](apk_app_icon_zh.md#加载已安装-App-的图标) + 模块。[了解更多](apk_app_icon.zh.md#加载已安装-App-的图标) * [Base64UriFetcher]:用于从 uri 本身的 base64 数据块中加载图片 * [ComposeResourceUriFetcher]:用于从 Compose Multiplatform 的 composeResources 目录加载图片,它还需要依赖 `sketch-compose-resources` 模块。 @@ -35,44 +35,44 @@ > [!IMPORTANT] > 上述组件都支持自动注册,你只需要导入即可,无需额外配置,如果你需要手动注册, -> 请阅读文档:[《注册组件》](register_component_zh.md) +> 请阅读文档:[《注册组件》](register_component.zh.md) ### 扩展 Fetcher 先实现 [Fetcher] 接口定义你的 Fetcher 和它的 Factory -然后参考文档 [《注册组件》](register_component_zh.md) 注册你的 Fetcher 即可 +然后参考文档 [《注册组件》](register_component.zh.md) 注册你的 Fetcher 即可 [comment]: <> (classs) -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Fetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt +[Fetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt -[FetchResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt +[FetchResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FetchResult.kt -[AssetUriFetcher]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/AssetUriFetcher.kt +[AssetUriFetcher]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/AssetUriFetcher.kt -[Base64UriFetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Base64UriFetcher.kt +[Base64UriFetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Base64UriFetcher.kt -[ContentUriFetcher]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ContentUriFetcher.kt +[ContentUriFetcher]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ContentUriFetcher.kt -[FileUriFetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt +[FileUriFetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt -[HurlHttpUriFetcher]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt +[HurlHttpUriFetcher]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt -[OkHttpHttpUriFetcher]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt +[OkHttpHttpUriFetcher]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt -[KtorHttpUriFetcher]: ../../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt +[KtorHttpUriFetcher]: ../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt -[ResourceUriFetcher]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ResourceUriFetcher.kt +[ResourceUriFetcher]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/fetch/ResourceUriFetcher.kt -[AppIconUriFetcher]: ../../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt +[AppIconUriFetcher]: ../sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/fetch/AppIconUriFetcher.kt -[KotlinResourceUriFetcher]: ../../sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/fetch/KotlinResourceUriFetcher.kt +[KotlinResourceUriFetcher]: ../sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/fetch/KotlinResourceUriFetcher.kt -[ComposeResourceUriFetcher]: ../../sketch-compose-resources/src/commonMain/kotlin/com/github/panpf/sketch/fetch/ComposeResourceUriFetcher.kt \ No newline at end of file +[ComposeResourceUriFetcher]: ../sketch-compose-resources/src/commonMain/kotlin/com/github/panpf/sketch/fetch/ComposeResourceUriFetcher.kt \ No newline at end of file diff --git a/docs/wiki/getting_started.md b/docs/getting_started.md similarity index 92% rename from docs/wiki/getting_started.md rename to docs/getting_started.md index d880faba7f..9ee085d82b 100644 --- a/docs/wiki/getting_started.md +++ b/docs/getting_started.md @@ -1,6 +1,6 @@ # Getting Started -Translations: [简体中文](getting_started_zh.md) +Translations: [简体中文](getting_started.zh.md) ## Load Image @@ -123,7 +123,7 @@ resources, as follows: | URI | Describe | Create Function | Dependent Modules | |:--------------------------|:-------------------------|:------------------------|:---------------------------------------------------------------------------------| | http://, https:// | File in network | _ | sketch-http-hurl
sketch-http-okhttp
sketch-http-ktor2
sketch-http-ktor3 | -| file://, / | File in SDCard | newFileUri() | _ | +| file://, /, D:/ | File in SDCard | newFileUri() | _ | | content:// | Android Content Resolver | _ | _ | | file:///android_asset/ | Android Asset | newAssetUri() | _ | | android.resource:// | Android Resource | newResourceUri() | _ | @@ -478,35 +478,35 @@ Featured functions: [comment]: <> (classs) -[AsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt +[AsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt -[AsyncImagePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt +[AsyncImagePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt -[DiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt +[DiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt -[loadImage]: ../../sketch-view/src/main/kotlin/com/github/panpf/sketch/image_view_singleton_extensions.kt +[loadImage]: ../sketch-view/src/main/kotlin/com/github/panpf/sketch/image_view_singleton_extensions.kt -[Disposable]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Disposable.kt +[Disposable]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Disposable.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageRequest_SingletonExtensions]: ../../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.singleton.kt +[ImageRequest_SingletonExtensions]: ../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.singleton.kt -[ImageRequest_ViewExtensions]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/request/ImageRequest.view.kt +[ImageRequest_ViewExtensions]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/request/ImageRequest.view.kt -[ImageResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt +[ImageResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt -[SingletonSketch]: ../../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/SingletonSketch.common.kt +[SingletonSketch]: ../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/SingletonSketch.common.kt -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt +[ViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt [comment]: <> (wiki) diff --git a/docs/wiki/getting_started_zh.md b/docs/getting_started.zh.md similarity index 86% rename from docs/wiki/getting_started_zh.md rename to docs/getting_started.zh.md index c7dc3b99b6..d4ba709048 100644 --- a/docs/wiki/getting_started_zh.md +++ b/docs/getting_started.zh.md @@ -117,7 +117,7 @@ context.sketch.enqueue(request) | URI | 描述 | 创建函数 | 依赖模块 | |:--------------------------|:-------------------------|:------------------------|:---------------------------------------------------------------------------------| | http://, https:// | File in network | _ | sketch-http-hurl
sketch-http-okhttp
sketch-http-ktor2
sketch-http-ktor3 | -| file://, / | File in SDCard | newFileUri() | _ | +| file://, /, D:/ | File in SDCard | newFileUri() | _ | | content:// | Android Content Resolver | _ | _ | | file:///android_asset/ | Android Asset | newAssetUri() | _ | | android.resource:// | Android Resource | newResourceUri() | _ | @@ -456,103 +456,103 @@ val imageResult: ImageResult? = imageView.imageResult [comment]: <> (classs) -[AsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt +[AsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt -[AsyncImagePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt +[AsyncImagePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt -[DiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt +[DiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt -[loadImage]: ../../sketch-view/src/main/kotlin/com/github/panpf/sketch/image_view_singleton_extensions.kt +[loadImage]: ../sketch-view/src/main/kotlin/com/github/panpf/sketch/image_view_singleton_extensions.kt -[Disposable]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Disposable.kt +[Disposable]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Disposable.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageRequest_SingletonExtensions]: ../../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.singleton.kt +[ImageRequest_SingletonExtensions]: ../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.singleton.kt -[ImageRequest_ViewExtensions]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/request/ImageRequest.view.kt +[ImageRequest_ViewExtensions]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/request/ImageRequest.view.kt -[ImageResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt +[ImageResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt -[SingletonSketch]: ../../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/SingletonSketch.common.kt +[SingletonSketch]: ../sketch-singleton/src/commonMain/kotlin/com/github/panpf/sketch/SingletonSketch.common.kt -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt +[ViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt [comment]: <> (wiki) -[animated_image]: animated_image_zh.md +[animated_image]: animated_image.zh.md -[apk_app_icon]: apk_app_icon_zh.md +[apk_app_icon]: apk_app_icon.zh.md -[register_component]: register_component_zh.md +[register_component]: register_component.zh.md -[compose]: compose_zh.md +[compose]: compose.zh.md -[decoder]: decoder_zh.md +[decoder]: decoder.zh.md -[download_cache]: download_cache_zh.md +[download_cache]: download_cache.zh.md -[exif_orientation]: exif_orientation_zh.md +[exif_orientation]: exif_orientation.zh.md -[fetcher]: fetcher_zh.md +[fetcher]: fetcher.zh.md -[getting_started]: getting_started_zh.md +[getting_started]: getting_started.zh.md -[http]: http_zh.md +[http]: http.zh.md -[image_options]: image_options_zh.md +[image_options]: image_options.zh.md -[lifecycle]: lifecycle_zh.md +[lifecycle]: lifecycle.zh.md -[listener]: listener_zh.md +[listener]: listener.zh.md -[log]: log_zh.md +[log]: log.zh.md -[long_image_grid_thumbnails]: long_image_grid_thumbnails_zh.md +[long_image_grid_thumbnails]: long_image_grid_thumbnails.zh.md -[memory_cache]: memory_cache_zh.md +[memory_cache]: memory_cache.zh.md -[mime_type_logo]: mime_type_logo_zh.md +[mime_type_logo]: mime_type_logo.zh.md -[pause_load_when_scrolling]: pause_load_when_scrolling_zh.md +[pause_load_when_scrolling]: pause_load_when_scrolling.zh.md -[preload]: preload_zh.md +[preload]: preload.zh.md -[download]: download_image_zh.md +[download]: download_image.zh.md -[progress_indicator]: progress_indicator_zh.md +[progress_indicator]: progress_indicator.zh.md -[request_interceptor]: request_interceptor_zh.md +[request_interceptor]: request_interceptor.zh.md -[decode_interceptor]: decode_interceptor_zh.md +[decode_interceptor]: decode_interceptor.zh.md -[resize]: resize_zh.md +[resize]: resize.zh.md -[result_cache]: result_cache_zh.md +[result_cache]: result_cache.zh.md -[save_cellular_traffic]: save_cellular_traffic_zh.md +[save_cellular_traffic]: save_cellular_traffic.zh.md -[sketch_image_view]: sketch_image_view_zh.md +[sketch_image_view]: sketch_image_view.zh.md -[state_image]: state_image_zh.md +[state_image]: state_image.zh.md -[svg]: svg_zh.md +[svg]: svg.zh.md -[target]: target_zh.md +[target]: target.zh.md -[transformation]: transformation_zh.md +[transformation]: transformation.zh.md -[transition]: transition_zh.md +[transition]: transition.zh.md -[video_frame]: video_frame_zh.md +[video_frame]: video_frame.zh.md -[migrate]: migrate_zh.md \ No newline at end of file +[migrate]: migrate.zh.md \ No newline at end of file diff --git a/docs/wiki/http.md b/docs/http.md similarity index 79% rename from docs/wiki/http.md rename to docs/http.md index 284e07d181..df958a1602 100644 --- a/docs/wiki/http.md +++ b/docs/http.md @@ -1,6 +1,6 @@ # Http network image -Translations: [简体中文](http_zh.md) +Translations: [简体中文](http.zh.md) ## Components @@ -146,26 +146,26 @@ Sketch.Builder(context).apply { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[HttpStack]: ../../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/http/HttpStack.kt +[HttpStack]: ../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/http/HttpStack.kt -[HurlStack]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/http/HurlStack.kt +[HurlStack]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/http/HurlStack.kt -[OkHttpStack]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/http/OkHttpStack.kt +[OkHttpStack]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/http/OkHttpStack.kt -[HttpUriFetcher]: ../../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt +[HttpUriFetcher]: ../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt -[HurlHttpUriFetcher]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt +[HurlHttpUriFetcher]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt -[OkHttpHttpUriFetcher]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt +[OkHttpHttpUriFetcher]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt -[Ktor2HttpUriFetcher]: ../../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt +[Ktor2HttpUriFetcher]: ../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt -[Ktor3HttpUriFetcher]: ../../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt +[Ktor3HttpUriFetcher]: ../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt -[HurlHttpUriFetcherProvider]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/HurlHttpUriFetcherProvider.kt +[HurlHttpUriFetcherProvider]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/HurlHttpUriFetcherProvider.kt -[OkHttpHttpUriFetcherProvider]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/OkHttpHttpUriFetcherProvider.kt +[OkHttpHttpUriFetcherProvider]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/OkHttpHttpUriFetcherProvider.kt -[Ktor2HttpUriFetcherProvider]: ../../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt +[Ktor2HttpUriFetcherProvider]: ../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt -[Ktor3HttpUriFetcherProvider]: ../../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt +[Ktor3HttpUriFetcherProvider]: ../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt diff --git a/docs/wiki/http_zh.md b/docs/http.zh.md similarity index 77% rename from docs/wiki/http_zh.md rename to docs/http.zh.md index 17a6968559..8562937854 100644 --- a/docs/wiki/http_zh.md +++ b/docs/http.zh.md @@ -20,7 +20,7 @@ Sketch 提供了 `sketch-http-*` 系列模块以支持 Http 网络图片,所 core 版本,例如 `sketch-http-ktor2-core` 和 `sketch-http-ktor3-core`,然后配置自己所需的引擎的依赖 > * ktor2 不支持 wasmJs 平台,必须要支持 wasmJs 平台的请使用 ktor3 > * 上述组件都支持自动注册,你只需要导入即可,无需额外配置,如果你需要手动注册, - 请阅读文档:[《注册组件》](register_component_zh.md) + 请阅读文档:[《注册组件》](register_component.zh.md) ## 安装组件 @@ -137,28 +137,28 @@ Sketch.Builder(context).apply { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[HttpStack]: ../../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/http/HttpStack.kt +[HttpStack]: ../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/http/HttpStack.kt -[HurlStack]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/http/HurlStack.kt +[HurlStack]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/http/HurlStack.kt -[OkHttpStack]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/http/OkHttpStack.kt +[OkHttpStack]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/http/OkHttpStack.kt -[KtorStack]: ../../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/http/KtorStack.kt +[KtorStack]: ../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/http/KtorStack.kt -[HttpUriFetcher]: ../../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt +[HttpUriFetcher]: ../sketch-http-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HttpUriFetcher.kt -[HurlHttpUriFetcher]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt +[HurlHttpUriFetcher]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/HurlHttpUriFetcher.kt -[OkHttpHttpUriFetcher]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt +[OkHttpHttpUriFetcher]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/OkHttpHttpUriFetcher.kt -[Ktor2HttpUriFetcher]: ../../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt +[Ktor2HttpUriFetcher]: ../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt -[Ktor3HttpUriFetcher]: ../../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt +[Ktor3HttpUriFetcher]: ../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/KtorHttpUriFetcher.kt -[HurlHttpUriFetcherProvider]: ../../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/HurlHttpUriFetcherProvider.kt +[HurlHttpUriFetcherProvider]: ../sketch-http-hurl/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/HurlHttpUriFetcherProvider.kt -[OkHttpHttpUriFetcherProvider]: ../../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/OkHttpHttpUriFetcherProvider.kt +[OkHttpHttpUriFetcherProvider]: ../sketch-http-okhttp/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/OkHttpHttpUriFetcherProvider.kt -[Ktor2HttpUriFetcherProvider]: ../../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt +[Ktor2HttpUriFetcherProvider]: ../sketch-http-ktor2-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt -[Ktor3HttpUriFetcherProvider]: ../../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt +[Ktor3HttpUriFetcherProvider]: ../sketch-http-ktor3-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/internal/KtorHttpUriFetcherProvider.common.kt diff --git a/docs/wiki/image_options.md b/docs/image_options.md similarity index 71% rename from docs/wiki/image_options.md rename to docs/image_options.md index c36129d1b1..594269793f 100644 --- a/docs/wiki/image_options.md +++ b/docs/image_options.md @@ -1,6 +1,6 @@ # ImageOptions -Translations: [简体中文](image_options_zh.md) +Translations: [简体中文](image_options.zh.md) [ImageOptions] is used to define image request configurations in batches and supports all image-related attributes of [ImageRequest]. @@ -75,14 +75,14 @@ AsyncImage( ) ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file diff --git a/docs/wiki/image_options_zh.md b/docs/image_options.zh.md similarity index 72% rename from docs/wiki/image_options_zh.md rename to docs/image_options.zh.md index f0406434cb..3103fca340 100644 --- a/docs/wiki/image_options_zh.md +++ b/docs/image_options.zh.md @@ -74,14 +74,14 @@ AsyncImage( ) ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file diff --git a/docs/images/logo.ico b/docs/images/logo.ico new file mode 100644 index 0000000000..029f2771ab Binary files /dev/null and b/docs/images/logo.ico differ diff --git a/docs/res/logo.png b/docs/images/logo.png similarity index 100% rename from docs/res/logo.png rename to docs/images/logo.png diff --git a/docs/images/logo_source.jpg b/docs/images/logo_source.jpg new file mode 100644 index 0000000000..929b7ed8d2 Binary files /dev/null and b/docs/images/logo_source.jpg differ diff --git a/docs/res/sample_mime_type_logo.png b/docs/images/sample_mime_type_logo.png similarity index 100% rename from docs/res/sample_mime_type_logo.png rename to docs/images/sample_mime_type_logo.png diff --git a/docs/res/sample_progress_drawable.png b/docs/images/sample_progress_drawable.png similarity index 100% rename from docs/res/sample_progress_drawable.png rename to docs/images/sample_progress_drawable.png diff --git a/docs/res/sample_progress_indicator.png b/docs/images/sample_progress_indicator.png similarity index 100% rename from docs/res/sample_progress_indicator.png rename to docs/images/sample_progress_indicator.png diff --git a/docs/wiki/lifecycle.md b/docs/lifecycle.md similarity index 79% rename from docs/wiki/lifecycle.md rename to docs/lifecycle.md index 7b9f382268..7c8c14d171 100644 --- a/docs/wiki/lifecycle.md +++ b/docs/lifecycle.md @@ -1,6 +1,6 @@ # Lifecycle -Translations: [简体中文](lifecycle_zh.md) +Translations: [简体中文](lifecycle.zh.md) [Sketch] relies on [Lifecycle] to monitor the life cycle of the page for the following functions: @@ -40,10 +40,10 @@ ImageRequest(context, "https://www.example.com/image.gif") { } ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt [Lifecycle]: https://developer.android.com/reference/kotlin/androidx/lifecycle/Lifecycle -[GlobalLifecycle]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/GlobalLifecycle.kt \ No newline at end of file +[GlobalLifecycle]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/GlobalLifecycle.kt \ No newline at end of file diff --git a/docs/wiki/lifecycle_zh.md b/docs/lifecycle.zh.md similarity index 80% rename from docs/wiki/lifecycle_zh.md rename to docs/lifecycle.zh.md index bf6bdeef78..fd860cc2ea 100644 --- a/docs/wiki/lifecycle_zh.md +++ b/docs/lifecycle.zh.md @@ -34,10 +34,10 @@ ImageRequest(context, "https://www.example.com/image.gif") { } ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt [Lifecycle]: https://developer.android.com/reference/kotlin/androidx/lifecycle/Lifecycle -[GlobalLifecycle]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/GlobalLifecycle.kt \ No newline at end of file +[GlobalLifecycle]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/GlobalLifecycle.kt \ No newline at end of file diff --git a/docs/wiki/listener.md b/docs/listener.md similarity index 81% rename from docs/wiki/listener.md rename to docs/listener.md index f3ecf2bc8a..d7f323b2d2 100644 --- a/docs/wiki/listener.md +++ b/docs/listener.md @@ -1,6 +1,6 @@ # Listener -Translations: [简体中文](listener_zh.md) +Translations: [简体中文](listener.zh.md) ## Compose @@ -120,12 +120,12 @@ scope.launch { } ``` -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Listener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt +[Listener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt -[ProgressListener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt +[ProgressListener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file diff --git a/docs/wiki/listener_zh.md b/docs/listener.zh.md similarity index 80% rename from docs/wiki/listener_zh.md rename to docs/listener.zh.md index a01119fe50..c906a2a24b 100644 --- a/docs/wiki/listener_zh.md +++ b/docs/listener.zh.md @@ -5,7 +5,7 @@ ## Compose 在 Compose 中你必须通过 [AsyncImageState] 的 loadState 和 progress -属性来监听请求的状态和进度,具体原因请参考 [《Compose》](compose_zh.md#listenerprogresslistener),如下: +属性来监听请求的状态和进度,具体原因请参考 [《Compose》](compose.zh.md#listenerprogresslistener),如下: ```kotlin val state = rememberAsyncImageState() @@ -118,12 +118,12 @@ scope.launch { } ``` -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Listener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt +[Listener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Listener.kt -[ProgressListener]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt +[ProgressListener]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ProgressListener.kt -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[AsyncImageState]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file +[AsyncImageState]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt \ No newline at end of file diff --git a/docs/wiki/log.md b/docs/log.md similarity index 76% rename from docs/wiki/log.md rename to docs/log.md index 32563852e1..380489b5c5 100644 --- a/docs/wiki/log.md +++ b/docs/log.md @@ -1,6 +1,6 @@ # Log -Translations: [简体中文](log_zh.md) +Translations: [简体中文](log.zh.md) The log of [Sketch] is provided by the [Logger] component, and the tag is unified as `Sketch` @@ -58,10 +58,10 @@ Sketch.Builder(context).apply { }.build() ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Logger]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Logger.common.kt +[Logger]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Logger.common.kt -[AndroidLogPipeline]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/Logger.android.kt +[AndroidLogPipeline]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/Logger.android.kt -[PrintLogPipeline]: ../../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/Logger.nonAndroid.kt \ No newline at end of file +[PrintLogPipeline]: ../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/Logger.nonAndroid.kt \ No newline at end of file diff --git a/docs/wiki/log_zh.md b/docs/log.zh.md similarity index 77% rename from docs/wiki/log_zh.md rename to docs/log.zh.md index af2a1e0c76..b4620a43e7 100644 --- a/docs/wiki/log_zh.md +++ b/docs/log.zh.md @@ -54,10 +54,10 @@ Sketch.Builder(context).apply { }.build() ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Logger]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Logger.common.kt +[Logger]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Logger.common.kt -[AndroidLogPipeline]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/Logger.android.kt +[AndroidLogPipeline]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/util/Logger.android.kt -[PrintLogPipeline]: ../../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/Logger.nonAndroid.kt \ No newline at end of file +[PrintLogPipeline]: ../sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/util/Logger.nonAndroid.kt \ No newline at end of file diff --git a/docs/wiki/long_image_grid_thumbnails.md b/docs/long_image_grid_thumbnails.md similarity index 71% rename from docs/wiki/long_image_grid_thumbnails.md rename to docs/long_image_grid_thumbnails.md index 0e53da6a36..fbaf57e15f 100644 --- a/docs/wiki/long_image_grid_thumbnails.md +++ b/docs/long_image_grid_thumbnails.md @@ -1,6 +1,6 @@ # Improve the clarity of long images in grid lists -Translations: [简体中文](long_image_grid_thumbnails_zh.md) +Translations: [简体中文](long_image_grid_thumbnails.zh.md) Long images in the grid list will be displayed very blurry because the thumbnail size is too small. @@ -28,12 +28,12 @@ ImageRequest(context, "https://example.com/image.jpg") { } ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Resize]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt +[Resize]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt -[Precision]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt +[Precision]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt -[LongImagePrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[LongImagePrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[DefaultLongImageDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt \ No newline at end of file +[DefaultLongImageDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt \ No newline at end of file diff --git a/docs/wiki/long_image_grid_thumbnails_zh.md b/docs/long_image_grid_thumbnails.zh.md similarity index 71% rename from docs/wiki/long_image_grid_thumbnails_zh.md rename to docs/long_image_grid_thumbnails.zh.md index edf699632a..1f71675e86 100644 --- a/docs/wiki/long_image_grid_thumbnails_zh.md +++ b/docs/long_image_grid_thumbnails.zh.md @@ -24,12 +24,12 @@ ImageRequest(context, "https://example.com/image.jpg") { } ``` -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Resize]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt +[Resize]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt -[Precision]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt +[Precision]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt -[LongImagePrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[LongImagePrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[DefaultLongImageDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt \ No newline at end of file +[DefaultLongImageDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt \ No newline at end of file diff --git a/docs/wiki/memory_cache.md b/docs/memory_cache.md similarity index 79% rename from docs/wiki/memory_cache.md rename to docs/memory_cache.md index b5e6e60612..3ef725ade5 100644 --- a/docs/wiki/memory_cache.md +++ b/docs/memory_cache.md @@ -1,6 +1,6 @@ # Memory Cache -Translations: [简体中文](memory_cache_zh.md) +Translations: [简体中文](memory_cache.zh.md) In order to avoid repeated loading of images and improve the loading speed of images, Sketch introduces memory caching. [MemoryCacheRequestInterceptor] will cache the loaded Image in the memory @@ -104,16 +104,16 @@ The memory cache is cleared under the following circumstances: * The low available memory of the device triggers the `onLowMemory()` method of Application * System trimming memory triggers Application's `onTrimMemory(int)` method -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[MemoryCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/MemoryCache.common.kt +[MemoryCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/MemoryCache.common.kt -[LruMemoryCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruMemoryCache.kt +[LruMemoryCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruMemoryCache.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[MemoryCacheRequestInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/MemoryCacheRequestInterceptor.kt +[MemoryCacheRequestInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/MemoryCacheRequestInterceptor.kt -[CachePolicy]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file +[CachePolicy]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file diff --git a/docs/wiki/memory_cache_zh.md b/docs/memory_cache.zh.md similarity index 79% rename from docs/wiki/memory_cache_zh.md rename to docs/memory_cache.zh.md index 0c1180d7d4..c42b6a642e 100644 --- a/docs/wiki/memory_cache_zh.md +++ b/docs/memory_cache.zh.md @@ -95,16 +95,16 @@ scope.launch { * 设备可用内存较低触发了 Application 的 `onLowMemory()` 方法 * 系统整理内存触发了 Application 的 `onTrimMemory(int)` 方法 -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[MemoryCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/MemoryCache.common.kt +[MemoryCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/MemoryCache.common.kt -[LruMemoryCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruMemoryCache.kt +[LruMemoryCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruMemoryCache.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[MemoryCacheRequestInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/MemoryCacheRequestInterceptor.kt +[MemoryCacheRequestInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/MemoryCacheRequestInterceptor.kt -[CachePolicy]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file +[CachePolicy]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file diff --git a/docs/wiki/migrate.md b/docs/migrate.md similarity index 76% rename from docs/wiki/migrate.md rename to docs/migrate.md index 8d7f80af51..614d464052 100644 --- a/docs/wiki/migrate.md +++ b/docs/migrate.md @@ -1,6 +1,6 @@ # Migrate -Translations: [简体中文](migrate_zh.md) +Translations: [简体中文](migrate.zh.md) The [sketch3compat] directory provides some utility functions, utility classes, and alias classes that are compatible with the sketch3 API, which can help you adapt to sketch4 more easily. You can @@ -12,4 +12,4 @@ copy them directly into your project. Other migration details are being gradually improved. -[sketch3compat]: ../../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/sketch3compat \ No newline at end of file +[sketch3compat]: ../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/sketch3compat \ No newline at end of file diff --git a/docs/wiki/migrate_zh.md b/docs/migrate.zh.md similarity index 81% rename from docs/wiki/migrate_zh.md rename to docs/migrate.zh.md index 84434ed7b7..443182f155 100644 --- a/docs/wiki/migrate_zh.md +++ b/docs/migrate.zh.md @@ -11,4 +11,4 @@ sketch4,你可以直接拷贝他们到你的项目中。 其它迁移细节正在陆续完善中 -[sketch3compat]: ../../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/sketch3compat \ No newline at end of file +[sketch3compat]: ../sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/util/sketch3compat \ No newline at end of file diff --git a/docs/wiki/mime_type_logo.md b/docs/mime_type_logo.md similarity index 83% rename from docs/wiki/mime_type_logo.md rename to docs/mime_type_logo.md index 5d987fb761..ecc0417215 100644 --- a/docs/wiki/mime_type_logo.md +++ b/docs/mime_type_logo.md @@ -1,10 +1,10 @@ # Mime type logo -Translations: [简体中文](mime_type_logo_zh.md) +Translations: [简体中文](mime_type_logo.zh.md) Sketch provides extended functions for displaying image type logo for view and Compose, as follows: -![sample_mime_type_logo.png](../res/sample_mime_type_logo.png) +![sample_mime_type_logo.png](images/sample_mime_type_logo.png) ### Compose @@ -80,8 +80,8 @@ sketchImageView.showMimeTypeLogoWithRes( [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[MimeTypeLogoAbility]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt +[MimeTypeLogoAbility]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt -[MimeTypeLogoModifier]: ../../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoModifier.kt \ No newline at end of file +[MimeTypeLogoModifier]: ../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoModifier.kt \ No newline at end of file diff --git a/docs/wiki/mime_type_logo_zh.md b/docs/mime_type_logo.zh.md similarity index 84% rename from docs/wiki/mime_type_logo_zh.md rename to docs/mime_type_logo.zh.md index 0cdba0fe4d..81134adf4d 100644 --- a/docs/wiki/mime_type_logo_zh.md +++ b/docs/mime_type_logo.zh.md @@ -4,7 +4,7 @@ Sketch 为 view 和 Compose 提供了显示图片类型角标的扩展功能,如下: -![sample_mime_type_logo.png](../res/sample_mime_type_logo.png) +![sample_mime_type_logo.png](images/sample_mime_type_logo.png) ### Compose @@ -80,8 +80,8 @@ sketchImageView.showMimeTypeLogoWithRes( [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[MimeTypeLogoAbility]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt +[MimeTypeLogoAbility]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt -[MimeTypeLogoModifier]: ../../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoModifier.kt \ No newline at end of file +[MimeTypeLogoModifier]: ../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoModifier.kt \ No newline at end of file diff --git a/docs/wiki/pause_load_when_scrolling.md b/docs/pause_load_when_scrolling.md similarity index 81% rename from docs/wiki/pause_load_when_scrolling.md rename to docs/pause_load_when_scrolling.md index b789089b0e..509388ef3d 100644 --- a/docs/wiki/pause_load_when_scrolling.md +++ b/docs/pause_load_when_scrolling.md @@ -1,6 +1,6 @@ # Pause loading of images when list scrolling -Translations: [简体中文](pause_load_when_scrolling_zh.md) +Translations: [简体中文](pause_load_when_scrolling.zh.md) Loading a large number of images during list scrolling will reduce UI fluency. Therefore, pausing the loading of images during list scrolling on devices with poor performance can significantly @@ -67,8 +67,8 @@ ImageRequest(context, "https://example.com/image.jpg") { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[PauseLoadWhenScrollingDecodeInterceptor]: ../../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/PauseLoadWhenScrollingDecodeInterceptor.kt \ No newline at end of file +[PauseLoadWhenScrollingDecodeInterceptor]: ../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/PauseLoadWhenScrollingDecodeInterceptor.kt \ No newline at end of file diff --git a/docs/wiki/pause_load_when_scrolling_zh.md b/docs/pause_load_when_scrolling.zh.md similarity index 83% rename from docs/wiki/pause_load_when_scrolling_zh.md rename to docs/pause_load_when_scrolling.zh.md index 875ecc50f3..3c33a75eaf 100644 --- a/docs/wiki/pause_load_when_scrolling_zh.md +++ b/docs/pause_load_when_scrolling.zh.md @@ -66,8 +66,8 @@ ImageRequest(context, "https://example.com/image.jpg") { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[PauseLoadWhenScrollingDecodeInterceptor]: ../../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/PauseLoadWhenScrollingDecodeInterceptor.kt \ No newline at end of file +[PauseLoadWhenScrollingDecodeInterceptor]: ../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/PauseLoadWhenScrollingDecodeInterceptor.kt \ No newline at end of file diff --git a/docs/wiki/preload.md b/docs/preload.md similarity index 78% rename from docs/wiki/preload.md rename to docs/preload.md index 86937c2b99..a2cc617604 100644 --- a/docs/wiki/preload.md +++ b/docs/preload.md @@ -1,6 +1,6 @@ ## Preload -Translations: [简体中文](preload_zh.md) +Translations: [简体中文](preload.zh.md) Sometimes in order to improve the loading speed and prevent users from seeing the image loading process, it is necessary to load the image into memory in advance. @@ -28,6 +28,6 @@ scope.launch { > from [Target], which may cause inconsistencies in size, precision, and scale between preloading > and use, resulting in failure to hit the cache. -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt \ No newline at end of file +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt \ No newline at end of file diff --git a/docs/wiki/preload_zh.md b/docs/preload.zh.md similarity index 80% rename from docs/wiki/preload_zh.md rename to docs/preload.zh.md index e477e07012..38849c3f30 100644 --- a/docs/wiki/preload_zh.md +++ b/docs/preload.zh.md @@ -25,6 +25,6 @@ scope.launch { > size、precision、scale 就会从 [Target] 上获取,这样可能会造成预加载时和使用时的 size、precision、scale > 不一致导致无法命中缓存 -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt \ No newline at end of file +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt \ No newline at end of file diff --git a/docs/wiki/progress_indicator.md b/docs/progress_indicator.md similarity index 85% rename from docs/wiki/progress_indicator.md rename to docs/progress_indicator.md index e1a46fdb21..dcd482c732 100644 --- a/docs/wiki/progress_indicator.md +++ b/docs/progress_indicator.md @@ -1,14 +1,14 @@ # Download progress indicator -Translations: [简体中文](progress_indicator_zh.md) +Translations: [简体中文](progress_indicator.zh.md) Sketch provides extended functions for view and Compose to display download progress, as follows: -![sample_progress_indicator.png](../res/sample_progress_indicator.png) +![sample_progress_indicator.png](images/sample_progress_indicator.png) Three styles are provided to choose from, as follows: -![sample_progress_drawable.png](../res/sample_progress_drawable.png) +![sample_progress_drawable.png](images/sample_progress_drawable.png) > [!TIP] > They can also adjust color, size and behavior @@ -173,12 +173,12 @@ sketchImageView.showProgressIndicator(MyProgressDrawable()) [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[ProgressIndicatorAbility]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt +[ProgressIndicatorAbility]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt -[AbsProgressDrawable]: ../../sketch-extensions-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/internal/AbsProgressDrawable.kt +[AbsProgressDrawable]: ../sketch-extensions-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/internal/AbsProgressDrawable.kt -[ProgressIndicatorModifier]: ../../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/ProgressIndicatorModifier.kt +[ProgressIndicatorModifier]: ../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/ProgressIndicatorModifier.kt -[AbsProgressPainter]: ../../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/painter/internal/AbsProgressPainter.kt \ No newline at end of file +[AbsProgressPainter]: ../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/painter/internal/AbsProgressPainter.kt \ No newline at end of file diff --git a/docs/wiki/progress_indicator_zh.md b/docs/progress_indicator.zh.md similarity index 85% rename from docs/wiki/progress_indicator_zh.md rename to docs/progress_indicator.zh.md index 2746f4c09d..3296b9ccf8 100644 --- a/docs/wiki/progress_indicator_zh.md +++ b/docs/progress_indicator.zh.md @@ -4,11 +4,11 @@ Sketch 为 view 和 Compose 提供了显示下载进度的扩展功能,如下: -![sample_progress_indicator.png](../res/sample_progress_indicator.png) +![sample_progress_indicator.png](images/sample_progress_indicator.png) 提供了三种样式可供选择,如下: -![sample_progress_drawable.png](../res/sample_progress_drawable.png) +![sample_progress_drawable.png](images/sample_progress_drawable.png) > [!TIP] > 还可以调整它们的颜色、尺寸和行为 @@ -173,12 +173,12 @@ sketchImageView.showProgressIndicator(MyProgressDrawable()) [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[ProgressIndicatorAbility]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt +[ProgressIndicatorAbility]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/ability/MimeTypeLogoAbility.kt -[AbsProgressDrawable]: ../../sketch-extensions-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/internal/AbsProgressDrawable.kt +[AbsProgressDrawable]: ../sketch-extensions-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/internal/AbsProgressDrawable.kt -[ProgressIndicatorModifier]: ../../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/ProgressIndicatorModifier.kt +[ProgressIndicatorModifier]: ../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/ability/ProgressIndicatorModifier.kt -[AbsProgressPainter]: ../../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/painter/internal/AbsProgressPainter.kt \ No newline at end of file +[AbsProgressPainter]: ../sketch-extensions-compose/src/commonMain/kotlin/com/github/panpf/sketch/painter/internal/AbsProgressPainter.kt \ No newline at end of file diff --git a/docs/wiki/register_component.md b/docs/register_component.md similarity index 91% rename from docs/wiki/register_component.md rename to docs/register_component.md index 4613002425..eaccfc3222 100644 --- a/docs/wiki/register_component.md +++ b/docs/register_component.md @@ -1,6 +1,6 @@ # Register component -Translations: [简体中文](register_component_zh.md) +Translations: [简体中文](register_component.zh.md) Sketch supports extending the functions of Sketch through the [Fetcher] and [Decoder] interfaces, and the built-in `sketch-http-*` and this is how `sketch-animated-*` and extension components such @@ -116,10 +116,10 @@ ImageRequest(context, "http://sample.com/sample.jpeg").apply { }.build() ``` -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Fetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt +[Fetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt -[FetcherProvider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt +[FetcherProvider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt -[DecoderProvider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt \ No newline at end of file +[DecoderProvider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt \ No newline at end of file diff --git a/docs/wiki/register_component_zh.md b/docs/register_component.zh.md similarity index 91% rename from docs/wiki/register_component_zh.md rename to docs/register_component.zh.md index d635ded1ed..162811aaa8 100644 --- a/docs/wiki/register_component_zh.md +++ b/docs/register_component.zh.md @@ -102,10 +102,10 @@ ImageRequest(context, "http://sample.com/sample.jpeg") { } ``` -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Fetcher]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt +[Fetcher]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/Fetcher.kt -[FetcherProvider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt +[FetcherProvider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt -[DecoderProvider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt \ No newline at end of file +[DecoderProvider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/ComponentLoader.kt \ No newline at end of file diff --git a/docs/wiki/request_interceptor.md b/docs/request_interceptor.md similarity index 79% rename from docs/wiki/request_interceptor.md rename to docs/request_interceptor.md index c69dcd99d0..16e8b34735 100644 --- a/docs/wiki/request_interceptor.md +++ b/docs/request_interceptor.md @@ -1,6 +1,6 @@ # RequestInterceptor -Translations: [简体中文](request_interceptor_zh.md) +Translations: [简体中文](request_interceptor.zh.md) Sketch intercepts the execution process of [ImageRequest] through [RequestInterceptor], and you can use this to change the input and output of the execution process. @@ -51,10 +51,10 @@ ImageRequest(context, "https://example.com/image.jpg") { } ``` -[RequestInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/RequestInterceptor.kt +[RequestInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/RequestInterceptor.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt +[ImageResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt -[ImageData]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageData.kt \ No newline at end of file +[ImageData]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageData.kt \ No newline at end of file diff --git a/docs/wiki/request_interceptor_zh.md b/docs/request_interceptor.zh.md similarity index 80% rename from docs/wiki/request_interceptor_zh.md rename to docs/request_interceptor.zh.md index a8c1509214..00c0d0a648 100644 --- a/docs/wiki/request_interceptor_zh.md +++ b/docs/request_interceptor.zh.md @@ -48,10 +48,10 @@ ImageRequest(context, "https://example.com/image.jpg") { } ``` -[RequestInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/RequestInterceptor.kt +[RequestInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/RequestInterceptor.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt +[ImageResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt -[ImageData]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageData.kt \ No newline at end of file +[ImageData]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageData.kt \ No newline at end of file diff --git a/docs/res/logo_source.png b/docs/res/logo_source.png deleted file mode 100644 index 1037f85687..0000000000 Binary files a/docs/res/logo_source.png and /dev/null differ diff --git a/docs/wiki/resize.md b/docs/resize.md similarity index 75% rename from docs/wiki/resize.md rename to docs/resize.md index 7af68a567e..8ed1bbf080 100644 --- a/docs/wiki/resize.md +++ b/docs/resize.md @@ -1,6 +1,6 @@ # Resize -Translations: [简体中文](resize_zh.md) +Translations: [简体中文](resize.zh.md) [Sketch] will adjust the size of the image to avoid exceeding the target and causing memory waste. @@ -184,52 +184,52 @@ transition. [Understanding Perfect Transition](transition.md#perfect-transition) > 1. [ResizeOnDrawHelper] is provided by [Target], so if [Target] is not set, the resizeOnDraw property will have no effect -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Resize]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt +[Resize]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt -[Scale]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Scale.kt +[Scale]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Scale.kt -[ScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[ScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[FixedScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[FixedScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[LongImageScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[LongImageScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[FixedPrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[FixedPrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[LongImagePrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[LongImagePrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[PrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[PrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[Precision]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt +[Precision]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt -[ViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt +[ViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[CrossfadeTransition]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt +[CrossfadeTransition]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ResizeDrawable]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt +[ResizeDrawable]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt -[ResizeAnimatableDrawable]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeAnimatableDrawable.kt +[ResizeAnimatableDrawable]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeAnimatableDrawable.kt -[DefaultLongImageDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt +[DefaultLongImageDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Size]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Size.kt +[Size]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Size.kt -[SizeResolver]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt +[SizeResolver]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[ResizeOnDrawHelper]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt +[ResizeOnDrawHelper]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt -[ResizePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt +[ResizePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt [long_image_grid_thumbnails]: long_image_grid_thumbnails.md \ No newline at end of file diff --git a/docs/wiki/resize_zh.md b/docs/resize.zh.md similarity index 71% rename from docs/wiki/resize_zh.md rename to docs/resize.zh.md index 4eff7319a1..aa4100c49e 100644 --- a/docs/wiki/resize_zh.md +++ b/docs/resize.zh.md @@ -157,59 +157,59 @@ resizeOnDraw 依赖 [ResizeOnDrawHelper] 实现,[ResizeOnDrawHelper] 会用 [R 作为宽和高,内部用 [Resize] 的 [Scale] 对 [Image] 进行缩放 resizeOnDraw 搭配 [CrossfadeTransition] -可实现完美过渡。[了解完美过度](transition_zh.md#完美过渡) +可实现完美过渡。[了解完美过度](transition.zh.md#完美过渡) > [!IMPORTANT] > 1. [ResizeOnDrawHelper] 由 [Target] 提供,因此如果没有设置 [Target],resizeOnDraw 属性也将无效 -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Resize]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt +[Resize]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Resize.kt -[Scale]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Scale.kt +[Scale]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Scale.kt -[ScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[ScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[FixedScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[FixedScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[LongImageScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[LongImageScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[FixedPrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[FixedPrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[LongImagePrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[LongImagePrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[PrecisionDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt +[PrecisionDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/PrecisionDecider.kt -[Precision]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt +[Precision]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/Precision.kt -[ViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt +[ViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[CrossfadeTransition]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt +[CrossfadeTransition]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ResizeDrawable]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt +[ResizeDrawable]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt -[ResizeAnimatableDrawable]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeAnimatableDrawable.kt +[ResizeAnimatableDrawable]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeAnimatableDrawable.kt -[DefaultLongImageDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt +[DefaultLongImageDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/LongImageDecider.kt -[Decoder]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt +[Decoder]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/decode/Decoder.kt -[Size]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Size.kt +[Size]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Size.kt -[SizeResolver]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt +[SizeResolver]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[ResizeOnDrawHelper]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt +[ResizeOnDrawHelper]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt -[ResizePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt +[ResizePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt -[PlatformContext.screenSize()]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/platform_contexts.common.kt +[PlatformContext.screenSize()]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/platform_contexts.common.kt -[long_image_grid_thumbnails]: long_image_grid_thumbnails_zh.md \ No newline at end of file +[long_image_grid_thumbnails]: long_image_grid_thumbnails.zh.md \ No newline at end of file diff --git a/docs/wiki/result_cache.md b/docs/result_cache.md similarity index 87% rename from docs/wiki/result_cache.md rename to docs/result_cache.md index 7e2ef1bb29..d703d77118 100644 --- a/docs/wiki/result_cache.md +++ b/docs/result_cache.md @@ -1,6 +1,6 @@ # Result Cache -Translations: [简体中文](result_cache_zh.md) +Translations: [简体中文](result_cache.zh.md) In order to avoid repeated conversion of images and improve the loading speed of images, [Sketch] introduces result caching. [ResultCacheDecodeInterceptor] will persistently store the converted @@ -173,16 +173,16 @@ The results cache is cleared under the following circumstances: 2. Actively call the `abort()` method of [DiskCache].Editor 3. Automatically clear older caches when maximum capacity is reached -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[DiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt +[DiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt -[LruDiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt +[LruDiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[ResultCacheDecodeInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/ResultCacheDecodeInterceptor.kt +[ResultCacheDecodeInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/ResultCacheDecodeInterceptor.kt -[CachePolicy]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file +[CachePolicy]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file diff --git a/docs/wiki/result_cache_zh.md b/docs/result_cache.zh.md similarity index 86% rename from docs/wiki/result_cache_zh.md rename to docs/result_cache.zh.md index 821da1629e..6773cdb499 100644 --- a/docs/wiki/result_cache_zh.md +++ b/docs/result_cache.zh.md @@ -165,16 +165,16 @@ scope.launch { 2. 主动调用 [DiskCache].Editor 的 `abort()` 方法 3. 达到最大容量时自动清除较旧的缓存 -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[DiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt +[DiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/DiskCache.common.kt -[LruDiskCache]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt +[LruDiskCache]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/LruDiskCache.common.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[ResultCacheDecodeInterceptor]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/ResultCacheDecodeInterceptor.kt +[ResultCacheDecodeInterceptor]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/internal/ResultCacheDecodeInterceptor.kt -[CachePolicy]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file +[CachePolicy]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/cache/CachePolicy.kt \ No newline at end of file diff --git a/docs/wiki/save_cellular_traffic.md b/docs/save_cellular_traffic.md similarity index 79% rename from docs/wiki/save_cellular_traffic.md rename to docs/save_cellular_traffic.md index 975e9f0a14..5f313f9199 100644 --- a/docs/wiki/save_cellular_traffic.md +++ b/docs/save_cellular_traffic.md @@ -1,6 +1,6 @@ # Save cellular data -Translations: [简体中文](save_cellular_traffic_zh.md) +Translations: [简体中文](save_cellular_traffic.zh.md) > [!IMPORTANT] > Only available on Android platform @@ -92,12 +92,12 @@ sketchImageView.setClickIgnoreSaveCellularTrafficEnabled(true) [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[SaveCellularTrafficRequestInterceptor]: ../../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/SaveCellularTrafficRequestInterceptor.kt +[SaveCellularTrafficRequestInterceptor]: ../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/SaveCellularTrafficRequestInterceptor.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Depth]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Depth.kt \ No newline at end of file +[Depth]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Depth.kt \ No newline at end of file diff --git a/docs/wiki/save_cellular_traffic_zh.md b/docs/save_cellular_traffic.zh.md similarity index 80% rename from docs/wiki/save_cellular_traffic_zh.md rename to docs/save_cellular_traffic.zh.md index 6535b46e09..998ba845b6 100644 --- a/docs/wiki/save_cellular_traffic_zh.md +++ b/docs/save_cellular_traffic.zh.md @@ -90,12 +90,12 @@ sketchImageView.setClickIgnoreSaveCellularTrafficEnabled(true) [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[SaveCellularTrafficRequestInterceptor]: ../../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/SaveCellularTrafficRequestInterceptor.kt +[SaveCellularTrafficRequestInterceptor]: ../sketch-extensions-core/src/commonMain/kotlin/com/github/panpf/sketch/request/SaveCellularTrafficRequestInterceptor.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[Depth]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Depth.kt \ No newline at end of file +[Depth]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/Depth.kt \ No newline at end of file diff --git a/docs/wiki/sketch_image_view.md b/docs/sketch_image_view.md similarity index 89% rename from docs/wiki/sketch_image_view.md rename to docs/sketch_image_view.md index 87b7bd2a10..8aec32f859 100644 --- a/docs/wiki/sketch_image_view.md +++ b/docs/sketch_image_view.md @@ -1,6 +1,6 @@ # SketchImageView -Translations: [简体中文](sketch_image_view_zh.md) +Translations: [简体中文](sketch_image_view.zh.md) Sketch provides a [SketchImageView] component, which can be used with Sketch to load images more conveniently. It supports xml attributes to configure request attributes, supports flow methods to @@ -109,12 +109,12 @@ supports the following functions: [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[ViewAbilityContainer]: ../../sketch-extensions-viewability/src/main/kotlin/com/github/panpf/sketch/ability/ViewAbilityContainer.kt +[ViewAbilityContainer]: ../sketch-extensions-viewability/src/main/kotlin/com/github/panpf/sketch/ability/ViewAbilityContainer.kt -[attrs]: ../../sketch-extensions-view/src/main/res/values/attrs.xml +[attrs]: ../sketch-extensions-view/src/main/res/values/attrs.xml -[show_download_progress]: progress_indicator +[show_download_progress]: progress_indicator.md [show_image_type]: mime_type_logo.md \ No newline at end of file diff --git a/docs/wiki/sketch_image_view_zh.md b/docs/sketch_image_view.zh.md similarity index 89% rename from docs/wiki/sketch_image_view_zh.md rename to docs/sketch_image_view.zh.md index 6edb506359..a0f3860e8d 100644 --- a/docs/wiki/sketch_image_view_zh.md +++ b/docs/sketch_image_view.zh.md @@ -105,12 +105,12 @@ scope.launch { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SketchImageView]: ../../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt +[SketchImageView]: ../sketch-extensions-view/src/main/kotlin/com/github/panpf/sketch/SketchImageView.kt -[ViewAbilityContainer]: ../../sketch-extensions-viewability/src/main/kotlin/com/github/panpf/sketch/ability/ViewAbilityContainer.kt +[ViewAbilityContainer]: ../sketch-extensions-viewability/src/main/kotlin/com/github/panpf/sketch/ability/ViewAbilityContainer.kt -[attrs]: ../../sketch-extensions-view/src/main/res/values/attrs.xml +[attrs]: ../sketch-extensions-view/src/main/res/values/attrs.xml -[show_download_progress]: progress_indicator +[show_download_progress]: progress_indicator.zh.md -[show_image_type]: mime_type_logo.md \ No newline at end of file +[show_image_type]: mime_type_logo.zh.md \ No newline at end of file diff --git a/docs/wiki/state_image.md b/docs/state_image.md similarity index 77% rename from docs/wiki/state_image.md rename to docs/state_image.md index f28ec8b874..b85d3404c2 100644 --- a/docs/wiki/state_image.md +++ b/docs/state_image.md @@ -1,6 +1,6 @@ # StateImage -Translations: [简体中文](state_image_zh.md) +Translations: [简体中文](state_image.zh.md) [StateImage] is used to provide images for loading status and error status. There are several implementations: @@ -196,40 +196,40 @@ ImageRequest(context, "https://example.com/image.jpg") { > The standard for thumbnails is images with the same aspect ratio and without any Transformation > modification. -[StateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/StateImage.kt +[StateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/StateImage.kt -[ColorDrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/ColorDrawableStateImage.kt +[ColorDrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/ColorDrawableStateImage.kt -[ColorPainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ColorPainterStateImage.kt +[ColorPainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ColorPainterStateImage.kt -[ConditionStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ConditionStateImage.common.kt +[ConditionStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ConditionStateImage.common.kt -[DrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/DrawableStateImage.kt +[DrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/DrawableStateImage.kt -[IconDrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconDrawableStateImage.kt +[IconDrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconDrawableStateImage.kt -[IconAnimatableDrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconAnimatableDrawableStateImage.kt +[IconAnimatableDrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconAnimatableDrawableStateImage.kt -[IconPainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconPainterStateImage.common.kt +[IconPainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconPainterStateImage.common.kt -[IconAnimatablePainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconAnimatablePainterStateImage.common.kt +[IconAnimatablePainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconAnimatablePainterStateImage.common.kt -[MemoryCacheStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/MemoryCacheStateImage.kt +[MemoryCacheStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/MemoryCacheStateImage.kt -[ThumbnailMemoryCacheStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ThumbnailMemoryCacheStateImage.kt +[ThumbnailMemoryCacheStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ThumbnailMemoryCacheStateImage.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[CurrentStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/CurrentStateImage.kt +[CurrentStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/CurrentStateImage.kt -[PainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/PainterStateImage.kt +[PainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/PainterStateImage.kt -[IconPainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt +[IconPainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt -[IconAnimatablePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconAnimatablePainter.common.kt +[IconAnimatablePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconAnimatablePainter.common.kt -[IconAnimatableDrawable]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconAnimatableDrawable.kt +[IconAnimatableDrawable]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconAnimatableDrawable.kt -[IconDrawable]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt +[IconDrawable]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt diff --git a/docs/wiki/state_image_zh.md b/docs/state_image.zh.md similarity index 75% rename from docs/wiki/state_image_zh.md rename to docs/state_image.zh.md index 4255a0cf42..996941034d 100644 --- a/docs/wiki/state_image_zh.md +++ b/docs/state_image.zh.md @@ -168,40 +168,40 @@ ImageRequest(context, "https://example.com/image.jpg") { > [!TIP] > 缩略图的标准为宽高比一致并且没有用任何 Transformation 修改的图片 -[StateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/StateImage.kt +[StateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/StateImage.kt -[ColorDrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/ColorDrawableStateImage.kt +[ColorDrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/ColorDrawableStateImage.kt -[ColorPainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ColorPainterStateImage.kt +[ColorPainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ColorPainterStateImage.kt -[ConditionStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ConditionStateImage.common.kt +[ConditionStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ConditionStateImage.common.kt -[DrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/DrawableStateImage.kt +[DrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/DrawableStateImage.kt -[IconDrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconDrawableStateImage.kt +[IconDrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconDrawableStateImage.kt -[IconAnimatableDrawableStateImage]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconAnimatableDrawableStateImage.kt +[IconAnimatableDrawableStateImage]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/state/IconAnimatableDrawableStateImage.kt -[IconPainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconPainterStateImage.common.kt +[IconPainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconPainterStateImage.common.kt -[IconAnimatablePainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconAnimatablePainterStateImage.common.kt +[IconAnimatablePainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/IconAnimatablePainterStateImage.common.kt -[MemoryCacheStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/MemoryCacheStateImage.kt +[MemoryCacheStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/MemoryCacheStateImage.kt -[ThumbnailMemoryCacheStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ThumbnailMemoryCacheStateImage.kt +[ThumbnailMemoryCacheStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/ThumbnailMemoryCacheStateImage.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[CurrentStateImage]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/CurrentStateImage.kt +[CurrentStateImage]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/state/CurrentStateImage.kt -[PainterStateImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/PainterStateImage.kt +[PainterStateImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/state/PainterStateImage.kt -[IconPainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt +[IconPainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt -[IconAnimatablePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconAnimatablePainter.common.kt +[IconAnimatablePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconAnimatablePainter.common.kt -[IconAnimatableDrawable]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconAnimatableDrawable.kt +[IconAnimatableDrawable]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconAnimatableDrawable.kt -[IconDrawable]: ../../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt +[IconDrawable]: ../sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt diff --git a/docs/wiki/svg.md b/docs/svg.md similarity index 71% rename from docs/wiki/svg.md rename to docs/svg.md index bce187f8cb..bd3e74f850 100644 --- a/docs/wiki/svg.md +++ b/docs/svg.md @@ -1,6 +1,6 @@ # SVG -Translations: [简体中文](svg_zh.md) +Translations: [简体中文](svg.zh.md) Sketch supports decoding SVG static images, powered by [SvgDecoder] @@ -32,8 +32,8 @@ ImageRequest(context, "https://www.example.com/image.svg") { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SvgDecoder]: ../../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt +[SvgDecoder]: ../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file diff --git a/docs/wiki/svg_zh.md b/docs/svg.zh.md similarity index 67% rename from docs/wiki/svg_zh.md rename to docs/svg.zh.md index 34e9f7f743..78a14a39bf 100644 --- a/docs/wiki/svg_zh.md +++ b/docs/svg.zh.md @@ -14,7 +14,7 @@ implementation("io.github.panpf.sketch4:sketch-svg:${LAST_VERSION}") > [!IMPORTANT] > `sketch-svg` -> 模块支持自动注册组件,有关组件注册的详细内容请查看文档:[《注册组件》](register_component_zh.md) +> 模块支持自动注册组件,有关组件注册的详细内容请查看文档:[《注册组件》](register_component.zh.md) ### 配置 @@ -31,8 +31,8 @@ ImageRequest(context, "https://www.example.com/image.svg") { [version_link]: https://repo1.maven.org/maven2/io/github/panpf/sketch4/ -[SvgDecoder]: ../../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt +[SvgDecoder]: ../sketch-svg/src/commonMain/kotlin/com/github/panpf/sketch/decode/SvgDecoder.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file diff --git a/docs/wiki/target.md b/docs/target.md similarity index 62% rename from docs/wiki/target.md rename to docs/target.md index 18f7f7b2b2..3d2232fdda 100644 --- a/docs/wiki/target.md +++ b/docs/target.md @@ -1,6 +1,6 @@ # Target -Translations: [简体中文](target_zh.md) +Translations: [简体中文](target.zh.md) The main job of [Target] is to display [Image]. It is also responsible for providing [SizeResolver], [ScaleDecider], [ResizeOnDrawHelper], [LifecycleResolver] and other properties when building [ImageRequest]. These properties will be used as default values. @@ -72,32 +72,32 @@ ImageRequest(context, "https://www.example.com/image.jpg") { > 1. As shown above [RemoteViewsTarget] only converts the Drawable to Bitmap and calls the setImageViewBitmap method of [RemoteViews] to set the Bitmap > 2. So you still need to refresh the notification or AppWidget in the onUpdated function to display the Bitmap on the screen. -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt +[ViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt -[ImageViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ImageViewTarget.kt +[ImageViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ImageViewTarget.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt +[ImageResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt [RemoteViews]: https://developer.android.google.cn/reference/android/widget/RemoteViews -[RemoteViewsTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/RemoteViewsTarget.kt +[RemoteViewsTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/RemoteViewsTarget.kt -[SizeResolver]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt +[SizeResolver]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt -[ScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[ScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[ResizeOnDrawHelper]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt +[ResizeOnDrawHelper]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt -[LifecycleResolver]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/LifecycleResolver.kt +[LifecycleResolver]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/LifecycleResolver.kt -[AsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt +[AsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt -[AsyncImagePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt \ No newline at end of file +[AsyncImagePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt \ No newline at end of file diff --git a/docs/wiki/target_zh.md b/docs/target.zh.md similarity index 62% rename from docs/wiki/target_zh.md rename to docs/target.zh.md index b836e23180..d52c74b11f 100644 --- a/docs/wiki/target_zh.md +++ b/docs/target.zh.md @@ -74,32 +74,32 @@ ImageRequest(context, "https://www.example.com/image.jpg") { setImageViewBitmap 方法设置 Bitmap > 2. 所以还需要你在 onUpdated 函数中刷新通知或 AppWidget 才能将 Bitmap 显示到屏幕上 -[Sketch]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt +[Sketch]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Sketch.common.kt -[Image]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt +[Image]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/Image.kt -[Target]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt +[Target]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/target/Target.kt -[ViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt +[ViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ViewTarget.kt -[ImageViewTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ImageViewTarget.kt +[ImageViewTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/ImageViewTarget.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageResult]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt +[ImageResult]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageResult.kt [RemoteViews]: https://developer.android.google.cn/reference/android/widget/RemoteViews -[RemoteViewsTarget]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/RemoteViewsTarget.kt +[RemoteViewsTarget]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/target/RemoteViewsTarget.kt -[SizeResolver]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt +[SizeResolver]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/SizeResolver.kt -[ScaleDecider]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt +[ScaleDecider]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ScaleDecider.kt -[ResizeOnDrawHelper]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt +[ResizeOnDrawHelper]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/resize/ResizeOnDraw.kt -[LifecycleResolver]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/LifecycleResolver.kt +[LifecycleResolver]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/LifecycleResolver.kt -[AsyncImage]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt +[AsyncImage]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt -[AsyncImagePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt \ No newline at end of file +[AsyncImagePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt \ No newline at end of file diff --git a/docs/wiki/transformation.md b/docs/transformation.md similarity index 64% rename from docs/wiki/transformation.md rename to docs/transformation.md index a01b3d1d00..4708c76d62 100644 --- a/docs/wiki/transformation.md +++ b/docs/transformation.md @@ -1,6 +1,6 @@ # Transformation -Translations: [简体中文](transformation_zh.md) +Translations: [简体中文](transformation.zh.md) [Transformation] can convert the decoded Image. Sketch has the following built-in [Transformation] @@ -43,18 +43,18 @@ and certainty of the key attribute: > For custom [Transformation], do not perform recycle() on the input Image of the transform > method. This will cause unpredictable errors. -[Transformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/Transformation.kt +[Transformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/Transformation.kt -[CircleCropTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/CircleCropTransformation.kt +[CircleCropTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/CircleCropTransformation.kt -[RotateTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RotateTransformation.kt +[RotateTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RotateTransformation.kt -[RoundedCornersTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RoundedCornersTransformation.kt +[RoundedCornersTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RoundedCornersTransformation.kt -[MaskTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/MaskTransformation.kt +[MaskTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/MaskTransformation.kt -[BlurTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/BlurTransformation.kt +[BlurTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/BlurTransformation.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file diff --git a/docs/wiki/transformation_zh.md b/docs/transformation.zh.md similarity index 62% rename from docs/wiki/transformation_zh.md rename to docs/transformation.zh.md index aead41da3f..b1ce4f7555 100644 --- a/docs/wiki/transformation_zh.md +++ b/docs/transformation.zh.md @@ -36,18 +36,18 @@ ImageRequest(context, "https://example.com/image.jpg") { > [!TIP] > 自定义的 [Transformation] 不要对 transform 方法的 input Image 执行 recycle(),这会造成不可预知的错误 -[Transformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/Transformation.kt +[Transformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/Transformation.kt -[CircleCropTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/CircleCropTransformation.kt +[CircleCropTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/CircleCropTransformation.kt -[RotateTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RotateTransformation.kt +[RotateTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RotateTransformation.kt -[RoundedCornersTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RoundedCornersTransformation.kt +[RoundedCornersTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/RoundedCornersTransformation.kt -[MaskTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/MaskTransformation.kt +[MaskTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/MaskTransformation.kt -[BlurTransformation]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/BlurTransformation.kt +[BlurTransformation]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transform/BlurTransformation.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file diff --git a/docs/wiki/transition.md b/docs/transition.md similarity index 72% rename from docs/wiki/transition.md rename to docs/transition.md index 94537f6940..dc724be9b9 100644 --- a/docs/wiki/transition.md +++ b/docs/transition.md @@ -1,6 +1,6 @@ # Transition -Translations: [简体中文](transition_zh.md) +Translations: [简体中文](transition.zh.md) [Transition] is used to configure the transition method between the new picture and the old picture when it is displayed. [CrossfadeTransition] is provided by default to support the fade-in and fade-out effect. @@ -40,16 +40,16 @@ ImageRequest(context, "https://example.com/image.jpg") { } ``` -[Transition]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/Transition.kt +[Transition]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/Transition.kt -[CrossfadeTransition]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt +[CrossfadeTransition]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[ResizePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt +[ResizePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt -[ResizeDrawable]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt +[ResizeDrawable]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt [resizeOnDraw]: resize.md#resizeOnDraw \ No newline at end of file diff --git a/docs/wiki/transition_zh.md b/docs/transition.zh.md similarity index 69% rename from docs/wiki/transition_zh.md rename to docs/transition.zh.md index 99cf8f2293..e89c4a2123 100644 --- a/docs/wiki/transition_zh.md +++ b/docs/transition.zh.md @@ -43,16 +43,16 @@ ImageRequest(context, "https://example.com/image.jpg") { } ``` -[Transition]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/Transition.kt +[Transition]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/Transition.kt -[CrossfadeTransition]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt +[CrossfadeTransition]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/transition/CrossfadeTransition.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt -[ResizePainter]: ../../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt +[ResizePainter]: ../sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt -[ResizeDrawable]: ../../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt +[ResizeDrawable]: ../sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/ResizeDrawable.kt -[resizeOnDraw]: resize_zh.md#resizeOnDraw \ No newline at end of file +[resizeOnDraw]: resize.zh.md#resizeOnDraw \ No newline at end of file diff --git a/docs/wiki/video_frame.md b/docs/video_frame.md similarity index 77% rename from docs/wiki/video_frame.md rename to docs/video_frame.md index 48a7d40624..27b106af55 100644 --- a/docs/wiki/video_frame.md +++ b/docs/video_frame.md @@ -1,6 +1,6 @@ # Video Frame -Translations: [简体中文](video_frame_zh.md) +Translations: [简体中文](video_frame.zh.md) Sketch provides the `sketch-video-*` series of modules to support decoding video frames @@ -63,14 +63,14 @@ ImageRequest(context, "file:///sdcard/sample.mp4") { [FFmpegMediaMetadataRetriever]: https://github.com/wseemann/FFmpegMediaMetadataRetriever/blob/master/core/src/main/kotlin/wseemann/media/FFmpegMediaMetadataRetriever.java -[VideoFrameDecoder]: ../../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt +[VideoFrameDecoder]: ../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt -[FFmpegVideoFrameDecoder]: ../../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt +[FFmpegVideoFrameDecoder]: ../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt -[VideoFrameDecoderProvider]: ../../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/internal/VideoFrameDecoderProvider.kt +[VideoFrameDecoderProvider]: ../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/internal/VideoFrameDecoderProvider.kt -[FFmpegVideoFrameDecoderProvider]: ../../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/internal/FFmpegVideoFrameDecoderProvider.kt +[FFmpegVideoFrameDecoderProvider]: ../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/internal/FFmpegVideoFrameDecoderProvider.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file diff --git a/docs/wiki/video_frame_zh.md b/docs/video_frame.zh.md similarity index 75% rename from docs/wiki/video_frame_zh.md rename to docs/video_frame.zh.md index 4cc5d056e4..53b02752c0 100644 --- a/docs/wiki/video_frame_zh.md +++ b/docs/video_frame.zh.md @@ -30,7 +30,7 @@ implementation("io.github.panpf.sketch4:sketch-video-ffmpeg:${LAST_VERSION}") > [!IMPORTANT] > 上述组件都支持自动注册,你只需要导入即可,无需额外配置,如果你需要手动注册, -> 请阅读文档:[《注册组件》](register_component_zh.md) +> 请阅读文档:[《注册组件》](register_component.zh.md) ### 配置 @@ -60,14 +60,14 @@ ImageRequest(context, "file:///sdcard/sample.mp4") { [FFmpegMediaMetadataRetriever]: https://github.com/wseemann/FFmpegMediaMetadataRetriever/blob/master/core/src/main/kotlin/wseemann/media/FFmpegMediaMetadataRetriever.java -[VideoFrameDecoder]: ../../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt +[VideoFrameDecoder]: ../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/VideoFrameDecoder.kt -[FFmpegVideoFrameDecoder]: ../../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt +[FFmpegVideoFrameDecoder]: ../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/FFmpegVideoFrameDecoder.kt -[VideoFrameDecoderProvider]: ../../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/internal/VideoFrameDecoderProvider.kt +[VideoFrameDecoderProvider]: ../sketch-video/src/main/kotlin/com/github/panpf/sketch/decode/internal/VideoFrameDecoderProvider.kt -[FFmpegVideoFrameDecoderProvider]: ../../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/internal/FFmpegVideoFrameDecoderProvider.kt +[FFmpegVideoFrameDecoderProvider]: ../sketch-video-ffmpeg/src/main/kotlin/com/github/panpf/sketch/decode/internal/FFmpegVideoFrameDecoderProvider.kt -[ImageRequest]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt +[ImageRequest]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageRequest.common.kt -[ImageOptions]: ../../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file +[ImageOptions]: ../sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/request/ImageOptions.common.kt \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index b818519d2d..5fcf691c5f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,6 @@ android.useAndroidX=true android.nonTransitiveRClass=true # kotlin.daemon.jvmargs=-Xmx6g -kotlin.mpp.androidGradlePluginCompatibility.nowarn=true # w: A compileOnly dependency is used in targets: Kotlin/JS, Kotlin/Wasm. #Dependencies: # - org.jetbrains.kotlinx:atomicfu:0.25.0 (source sets: jsMain, wasmJsMain) @@ -35,9 +34,9 @@ org.jetbrains.compose.experimental.wasm.enabled=true # app minSdk=21 targetSdk=28 -compileSdk=34 -versionCode=4016 -versionName=4.0.3 +compileSdk=35 +versionCode=4100 +versionName=4.1.0 # #------------------------------------------ publish config ----------------------------------------# GROUP=io.github.panpf.sketch4 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6656b306bb..bb0a1c354d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -#distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip -distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.9-bin.zip \ No newline at end of file +#distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip \ No newline at end of file diff --git a/internal/images/files/sample_long_comic.jpg b/internal/images/files/sample_long_comic.jpg new file mode 100644 index 0000000000..3218f83bc2 Binary files /dev/null and b/internal/images/files/sample_long_comic.jpg differ diff --git a/internal/images/src/commonMain/kotlin/com/github/panpf/sketch/images/ResourceImages.kt b/internal/images/src/commonMain/kotlin/com/github/panpf/sketch/images/ResourceImages.kt index 7faf7867e4..6f7c2675b1 100644 --- a/internal/images/src/commonMain/kotlin/com/github/panpf/sketch/images/ResourceImages.kt +++ b/internal/images/src/commonMain/kotlin/com/github/panpf/sketch/images/ResourceImages.kt @@ -149,6 +149,12 @@ object ResourceImages { size = Size(30000, 926), mimeType = "image/jpeg" ) + val longCOMIC: ResourceImageFile = ResourceImageFile( + resourceName = "sample_long_comic.jpg", + name = "COMIC", + size = Size(690, 12176), + mimeType = "image/jpeg" + ) val clockExifFlipHorizontal: ResourceImageFile = ResourceImageFile( resourceName = "clock_exif_flip_horizontal.jpeg", @@ -303,7 +309,7 @@ object ResourceImages { ) val values: Array = arrayOf( - bmp, heic, jpeg, png, svg, webp, animGif, animHeif, animWebp, mp4, longQMSHT, + bmp, heic, jpeg, png, svg, webp, animGif, animHeif, animWebp, mp4, longQMSHT, longCOMIC, clockExifFlipHorizontal, clockExifFlipVertical, clockExifNormal, clockExifRotate90, clockExifRotate180, clockExifRotate270, clockExifTranspose, clockExifTransverse, clockExifUndefined, number1, number2, number3, number4, number5, number6, number7, number8, diff --git a/libs.versions.toml b/libs.versions.toml index 22c830c5a5..b6db452169 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -1,69 +1,69 @@ [versions] -android-plugin = "8.7.1" -androidx-activity = "1.9.0" +android-plugin = "8.10.0" +androidx-activity = "1.10.1" androidx-appcompat = "1.7.0" -androidx-compose = "1.7.1" # sync with jetbrains-compose -androidx-constraintlayout = "2.1.4" -androidx-constraintlayout-compose = "1.0.1" -androidx-core = "1.13.1" -androidx-exifinterface = "1.3.7" -androidx-fragment = "1.8.1" +androidx-compose = "1.8.0" # sync with jetbrains-compose +androidx-constraintlayout = "2.2.1" +androidx-constraintlayout-compose = "1.1.1" +androidx-core = "1.16.0" +androidx-exifinterface = "1.4.1" +androidx-fragment = "1.8.6" androidx-lifecycle = "2.8.5" # sync with jetbrains-lifecycle -androidx-navigation = "2.8.0" -androidx-recyclerview = "1.3.2" +androidx-navigation = "2.9.0" +androidx-recyclerview = "1.4.0" androidx-swiperefreshlayout = "1.1.0" androidx-test-ext-junit = "1.2.1" androidx-test-rules = "1.6.1" androidx-test-runner = "1.6.2" -androidgifdrawable = "1.2.15" +androidgifdrawable = "1.2.29" androidsvg = "1.4" -appdirs = "1.2.2" +appdirs = "1.4.0" -buildkonfig = "0.15.2" +buildkonfig = "0.17.1" -cashapp-paging = "3.3.0-alpha02-0.5.1" +dokka = "1.9.20" # 2.0.0 There is a problem with the version configuration, so I won't upgrade it yet -dokka = "1.9.20" +ffmpegMediaMetadataRetriever = "1.0.19" -ffmpegMediaMetadataRetriever = "1.0.16" - -google-material = "1.12.0" google-flexbox = "3.0.0" - -jetbrains-compose = "1.7.0" -jetbrains-lifecycle = "2.8.3" - -kotlin = "2.0.21" -kotlinx-atomicfu = "0.26.0" # sync with kotlin -kotlinx-coroutines = "1.9.0" -kotlinx-cover = "0.8.3" -kotlinx-datetime = "0.6.1" -kotlinx-serialization-json = "1.7.3" +google-material = "1.12.0" +gradle-maven-publish-plugin = "0.31.0" + +jetbrains-compose = "1.8.0" +jetbrains-lifecycle = "2.8.4" +jetbrains-compose-material-icons-core = "1.7.3" + +kotlin = "2.1.10" # 2.1.20 version will report an error when sync 'kotlin-project-structure-metadata.json (No such file or directory)', but it does not affect the compilation +kotlinx-atomicfu = "0.27.0" # sync with kotlin +kotlinx-coroutines = "1.10.2" +kotlinx-cover = "0.9.1" +kotlinx-datetime = "0.6.2" +kotlinx-serialization-json = "1.8.1" #noinspection GradleDependency -ktor2 = "2.3.12" -ktor3 = "3.0.0" +ktor2 = "2.3.13" +ktor3 = "3.0.3" # 3.1.0+ Error when publishing packaging obfuscation on desktop platform: 'https://youtrack.jetbrains.com/issue/KTOR-8325/Proguard-cant-find-enclosing-method-attachFor-error-since-3.1.0' leakcanary = "2.14" -mavenpublish = "0.29.0" -moko-permissions = "0.18.0" -multiplatformsettings = "1.2.0" +moko-permissions = "0.19.1" +multiplatform-paging = "3.3.0-alpha02-0.5.1" +multiplatform-settings = "1.3.0" okhttp3 = "4.12.0" -okio = "3.9.1" +okio = "3.11.0" panpf-assemblyadapter4 = "4.1.0" panpf-tools4a = "1.0.0" panpf-tools4j = "1.0.0" panpf-tools4k = "1.0.0" -panpf-zoomimage = "1.1.0-rc01" +panpf-zoomimage = "1.3.0" -penfeizhou-animation = "3.0.1" +penfeizhou-animation = "3.0.4" -skiko = "0.8.15" # sync with jetbrains-compose +skiko = "0.9.4" # sync with jetbrains-compose. 'decodes.nonAndroid#supportDecodeRegion()' must be modified -voyager = "1.1.0-alpha03" +voyager = "1.1.0-beta03" [libraries] gradlePlugin-android = { module = "com.android.tools.build:gradle", version.ref = "android-plugin" } @@ -75,8 +75,8 @@ gradlePlugin-kotlinComposeCompiler = { module = "org.jetbrains.kotlin:compose-co gradlePlugin-kotlinSerialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" } gradlePlugin-kotlinxAtomicfu = { module = "org.jetbrains.kotlinx:atomicfu-gradle-plugin", version.ref = "kotlinx-atomicfu" } gradlePlugin-kotlinxCover = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kotlinx-cover" } -gradlePlugin-mavenPublish = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "mavenpublish" } -gradlePlugin-dokka = { module = "org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin", version.ref = "dokka" } +gradlePlugin-mavenPublish = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "gradle-maven-publish-plugin" } +#gradlePlugin-dokka = { module = "org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin", version.ref = "dokka" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } @@ -104,7 +104,7 @@ androidgifdrawable = { module = "pl.droidsonroids.gif:android-gif-drawable", ver androidsvg = { module = "com.caverock:androidsvg-aar", version.ref = "androidsvg" } appdirs = { module = "net.harawata:appdirs", version.ref = "appdirs" } -cashapp-paging-compose-common = { module = "app.cash.paging:paging-compose-common", version.ref = "cashapp-paging" } +multiplatform-paging = { module = "app.cash.paging:paging-compose-common", version.ref = "multiplatform-paging" } ffmpegMediaMetadataRetriever-core = { module = "com.github.wseemann:FFmpegMediaMetadataRetriever-core", version.ref = "ffmpegMediaMetadataRetriever" } ffmpegMediaMetadataRetriever-native = { module = "com.github.wseemann:FFmpegMediaMetadataRetriever-native", version.ref = "ffmpegMediaMetadataRetriever" } @@ -114,6 +114,7 @@ google-flexbox = { module = "com.google.android.flexbox:flexbox", version.ref = jetbrains-lifecycle-common = { module = "org.jetbrains.androidx.lifecycle:lifecycle-common", version.ref = "jetbrains-lifecycle" } jetbrains-lifecycle-runtime-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "jetbrains-lifecycle" } +jetbrains-compose-material-icons-core = { module = "org.jetbrains.compose.material:material-icons-core", version.ref = "jetbrains-compose-material-icons-core" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } @@ -144,9 +145,10 @@ ktor3-serialization-kotlinxJson = { module = "io.ktor:ktor-serialization-kotlinx leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" } moko-permissions = { module = "dev.icerock.moko:permissions-compose", version.ref = "moko-permissions" } -multiplatformsettings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatformsettings" } -#multiplatformsettings-coroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatformsettings" } -#multiplatformsettings-observable = { module = "com.russhwolf:multiplatform-settings-make-observable", version.ref = "multiplatformsettings" } +moko-permissions-storage = { module = "dev.icerock.moko:permissions-storage", version.ref = "moko-permissions" } +multiplatformsettings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" } +#multiplatform-settings-coroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatform-settings" } +#multiplatform-settings-observable = { module = "com.russhwolf:multiplatform-settings-make-observable", version.ref = "multiplatform-settings" } okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3" } okio = { module = "com.squareup.okio:okio", version.ref = "okio" } diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..b092cd33da --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,144 @@ +# Built with https://github.com/squidfunk/mkdocs-material + +site_name: 'Sketch Image Loader' +site_url: 'https://github.com/panpf/sketch' +site_author: '2024 panpf' +site_description: 'An image loading library designed for Compose Multiplatform and Android View. It is powerful and rich in functions. In addition to basic functions, it also supports GIF, SVG, video thumbnails, Exif Orientation, etc.' + +repo_name: 'Sketch' +repo_url: 'https://github.com/panpf/sketch' + +copyright: 'Copyright © 2024 panpf' + +theme: + name: 'material' + language: 'en' + favicon: 'images/logo.ico' + logo: 'images/logo.png' + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: white + accent: white + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: black + accent: black + toggle: + icon: material/brightness-4 + name: Switch to light mode + font: + text: 'JetBrains' + code: 'JetBrains Mono' + features: + - content.tabs + - content.code.annotate + - content.code.copy + # - navigation.tabs + # - navigation.top + - navigation.indexes + - navigation.sections + - navigation.expand + - navigation.footer + - navigation.instant + - navigation.tracking + - search.highlight + - search.suggest + - search.share + +extra: + social: + - icon: 'fontawesome/brands/github' + link: 'https://github.com/panpf/sketch' + +nav: + - 'Overview': index.md + - 'Getting Started': getting_started.md + - 'Register Component': register_component.md + - 'Compose': compose.md + - 'Http': http.md + - 'Animated Image': animated_image.md + - 'Resize': resize.md + - 'Transformation': transformation.md + - 'Transition': transition.md + - 'StateImage': state_image.md + - 'Listener': listener.md + - 'DownloadCache': download_cache.md + - 'ResultCache': result_cache.md + - 'MemoryCache': memory_cache.md + - 'Fetcher': fetcher.md + - 'Decoder': decoder.md + - 'Target': target.md + - 'SVG': svg.md + - 'Video Frames': video_frame.md + - 'Exif Orientation': exif_orientation.md + - 'ImageOptions': image_options.md + - 'RequestInterceptor': request_interceptor.md + - 'DecodeInterceptor': decode_interceptor.md + - 'Preload': preload.md + - 'Download Image': download_image.md + - 'Lifecycle': lifecycle.md + - 'SketchImageView': sketch_image_view.md + - 'Clearer Thumbnail': long_image_grid_thumbnails.md + - 'Progress Indicator': progress_indicator.md + - 'Mime Type Logo': mime_type_logo.md + - 'Save Cellular Data': save_cellular_traffic.md + - 'Pause Load When Scrolling': pause_load_when_scrolling.md + - 'Apk/App Icon': apk_app_icon.md + - 'Log': log.md + - 'Migrate': migrate.md + - 'Change Log': CHANGELOG.md + +markdown_extensions: + - admonition + - toc: + permalink: true + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences # 支持代码块嵌套 + - pymdownx.tabbed # 支持选项卡 + - pymdownx.tasklist # 支持任务列表 + - pymdownx.emoji # 支持 Emoji + - pymdownx.details # 支持折叠内容 + - pymdownx.highlight # 支持代码高亮 + - tables # 支持表格 + - footnotes # 支持脚注 + - attr_list # 支持属性列表 + - md_in_html # 支持在 HTML 中嵌入 Markdown + +plugins: + - i18n: + docs_structure: suffix + default_language: en + languages: + - locale: en + default: true + name: English + build: true + - locale: zh + default: false + name: 中文 + build: true + - search + - minify: + minify_html: true + +validation: + # links: + # absolute_links: ignore + nav: + omitted_files: ignore \ No newline at end of file diff --git a/package_desktop.sh b/package_desktop.sh index 65d5fcbe24..5d9c47669c 100755 --- a/package_desktop.sh +++ b/package_desktop.sh @@ -7,6 +7,6 @@ if [ "$1" != "--skipClean" ]; then ./gradlew clean fi -./gradlew sample:packageDistributionForCurrentOS +./gradlew sample:packageReleaseDistributionForCurrentOS -echo "✅ Desktop package is created successfully. The distribution is written to $(pwd)/sample/build/compose/binaries/main/" \ No newline at end of file +echo "✅ Desktop package is created successfully. The distribution is written to $(pwd)/sample/build/compose/binaries/main-release/" \ No newline at end of file diff --git a/package_ios.sh b/package_ios.sh index 75d1508b54..1a43d958e0 100755 --- a/package_ios.sh +++ b/package_ios.sh @@ -7,6 +7,6 @@ if [ "$1" != "--skipClean" ]; then ./gradlew clean fi -#xcodebuild -project sample/iosApp/iosApp.xcodeproj -scheme iosApp -destination 'platform=iOS Simulator,name=iPhone 14,OS=latest' +xcodebuild -project sample/iosApp/iosApp.xcodeproj -scheme iosApp -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO -derivedDataPath sample/build/ios/outputs/ -#echo "✅ iOS package is created successfully." \ No newline at end of file +echo "✅ iOS package is created successfully. $(pwd)/sample/build/ios/outputs/Build/Products/Debug-iphonesimulator/" \ No newline at end of file diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 646a04365f..578bc43cf4 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -1,6 +1,5 @@ -@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) - import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl plugins { id("com.android.application") @@ -41,17 +40,12 @@ kotlin { binaries.executable() } + @OptIn(ExperimentalWasmDsl::class) wasmJs { moduleName = "composeApp" browser { commonWebpackConfig { outputFileName = "composeApp.js" -// devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { -// static = (static ?: mutableListOf()).apply { -// // Serve sources to debug inside browser -// add(project.projectDir.path) -// } -// } } } binaries.executable() @@ -71,6 +65,7 @@ kotlin { implementation(compose.components.resources) implementation(compose.material) // pull refresh implementation(compose.material3) + implementation(libs.jetbrains.compose.material.icons.core) implementation(libs.ktor3.client.contentNegotiation) implementation(libs.ktor3.serialization.kotlinxJson) implementation(libs.multiplatformsettings) @@ -105,6 +100,7 @@ kotlin { implementation(libs.google.flexbox) implementation(libs.kotlinx.serialization.json) implementation(libs.moko.permissions) + implementation(libs.moko.permissions.storage) implementation(libs.panpf.assemblyadapter4.pager2) implementation(libs.panpf.assemblyadapter4.recycler) implementation(libs.panpf.assemblyadapter4.recycler.paging) @@ -137,10 +133,11 @@ kotlin { resources.srcDirs("../internal/images/files") dependencies { implementation(libs.moko.permissions) + implementation(libs.moko.permissions.storage) } } nonJsCommonMain.dependencies { - implementation(libs.cashapp.paging.compose.common) + implementation(libs.multiplatform.paging) } commonTest.dependencies { diff --git a/sample/compose-desktop.pro b/sample/compose-desktop.pro index f0056ba717..d437febc8d 100644 --- a/sample/compose-desktop.pro +++ b/sample/compose-desktop.pro @@ -16,6 +16,7 @@ # ----------------------------------------- Okio ------------------------------------------------- # # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. -dontwarn org.codehaus.mojo.animal_sniffer.* +-keep class okio.** { *; } # ----------------------------------------- OkHttp ----------------------------------------------- # @@ -46,6 +47,9 @@ -keepclasseswithmembers class kotlinx.serialization.json.** { kotlinx.serialization.KSerializer serializer(...); } +-keepclassmembers public class **$$serializer { + private ** descriptor; +} # ----------------------------------------- ktor ------------------------------------------------- # @@ -75,4 +79,8 @@ -if @kotlinx.serialization.Serializable class com.github.panpf.sketch.sample.** -keepclassmembers class com.github.panpf.sketch.sample.<1>$Companion { kotlinx.serialization.KSerializer serializer(...); -} \ No newline at end of file +} + +# ----------------------------------------- Sketch Privider --------------------------------------------- # +-keep class * implements com.github.panpf.sketch.util.DecoderProvider { *; } +-keep class * implements com.github.panpf.sketch.util.FetcherProvider { *; } \ No newline at end of file diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro index e389b704b6..596160192b 100644 --- a/sample/proguard-rules.pro +++ b/sample/proguard-rules.pro @@ -28,6 +28,7 @@ # ----------------------------------------- Okio ------------------------------------------------- # # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. -dontwarn org.codehaus.mojo.animal_sniffer.* +-keep class okio.** { *; } # ----------------------------------------- kotlinx serialization -------------------------------- # @@ -41,6 +42,9 @@ -keepclasseswithmembers class kotlinx.serialization.json.** { kotlinx.serialization.KSerializer serializer(...); } +-keepclassmembers public class **$$serializer { + private ** descriptor; +} # ----------------------------------------- ktor ------------------------------------------------- # diff --git a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/MyApplication.kt b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/MyApplication.kt index e55b6b1315..51485ee48f 100644 --- a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/MyApplication.kt +++ b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/MyApplication.kt @@ -16,6 +16,7 @@ package com.github.panpf.sketch.sample +import android.annotation.SuppressLint import android.app.Application import android.content.Context import com.github.panpf.sketch.SingletonSketch @@ -23,11 +24,22 @@ import com.github.panpf.sketch.Sketch import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel +import java.security.SecureRandom +import java.security.cert.X509Certificate +import javax.net.ssl.HttpsURLConnection +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager class MyApplication : Application(), SingletonSketch.Factory { private val coroutineScope = CoroutineScope(Dispatchers.Main) + override fun onCreate() { + super.onCreate() + handleSSLHandshake() + } + override fun createSketch(context: Context): Sketch { return newSketch(context) } @@ -36,4 +48,40 @@ class MyApplication : Application(), SingletonSketch.Factory { super.onTerminate() coroutineScope.cancel() } + + /** + * for api.pexels.com on Android 5.0 + */ + private fun handleSSLHandshake() { + try { + val trustAllCerts = arrayOf( + @SuppressLint("CustomX509TrustManager") + object : X509TrustManager { + override fun getAcceptedIssuers(): Array { + return arrayOfNulls(0) + } + + @SuppressLint("TrustAllX509TrustManager") + override fun checkClientTrusted( + certs: Array?, + authType: String? + ) { + } + + @SuppressLint("TrustAllX509TrustManager") + override fun checkServerTrusted( + certs: Array?, + authType: String? + ) { + } + }) + val sc = SSLContext.getInstance("TLS") + // trustAllCerts trust all certificates + sc.init(null, trustAllCerts, SecureRandom()) + HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory) + HttpsURLConnection.setDefaultHostnameVerifier { _, _ -> true } + } catch (e: Exception) { + e.printStackTrace() + } + } } \ No newline at end of file diff --git a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.android.kt b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.android.kt index 353d94f2ba..3c611e6216 100644 --- a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.android.kt +++ b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.android.kt @@ -1,5 +1,6 @@ package com.github.panpf.sketch.sample.ui.gallery import dev.icerock.moko.permissions.Permission +import dev.icerock.moko.permissions.storage.STORAGE actual fun localPhotoListPermission(): Any? = Permission.STORAGE \ No newline at end of file diff --git a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewer.android.kt b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewer.android.kt index 091758d1fa..614a8c9cc5 100644 --- a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewer.android.kt +++ b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewer.android.kt @@ -16,6 +16,7 @@ import dev.icerock.moko.permissions.PermissionsController import dev.icerock.moko.permissions.compose.BindEffect import dev.icerock.moko.permissions.compose.PermissionsControllerFactory import dev.icerock.moko.permissions.compose.rememberPermissionsControllerFactory +import dev.icerock.moko.permissions.storage.WRITE_STORAGE import kotlinx.coroutines.launch @Composable diff --git a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewerFragment.kt b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewerFragment.kt index bf822ec7ae..57995a56e1 100644 --- a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewerFragment.kt +++ b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/PhotoViewerFragment.kt @@ -97,6 +97,7 @@ class PhotoViewerFragment : BaseBindingFragment() { .repeatCollectWithLifecycle(viewLifecycleOwner, State.CREATED) { alignmentState.value = AlignmentCompat.valueOf(it) } + keepTransformWhenSameAspectRatioContentSizeChangedState.value = true } subsampling.apply { appSettings.showTileBounds diff --git a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/DecoderTestFragment.kt b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/DecoderTestFragment.kt index ba80be88e2..f81a22fbc1 100644 --- a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/DecoderTestFragment.kt +++ b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/DecoderTestFragment.kt @@ -202,7 +202,7 @@ actual suspend fun buildDecoderTestItems( add( DecoderTestItem( name = "APK_ICON", - imageUri = headerUserPackageInfo.applicationInfo.publicSourceDir, + imageUri = headerUserPackageInfo.applicationInfo!!.publicSourceDir, imageDecoder = ApkIconDecoder.Factory() ) ) @@ -217,11 +217,11 @@ private suspend fun loadUserAppPackageInfo( context.packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS) (if (fromHeader) { packageList.find { - it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0 + it.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM == 0 } } else { packageList.findLast { - it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0 + it.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM == 0 } } ?: context.packageManager.getPackageInfo(context.packageName, 0)) } diff --git a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/FetcherTestScreen.android.kt b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/FetcherTestScreen.android.kt index a73804e0b3..497cdd2421 100644 --- a/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/FetcherTestScreen.android.kt +++ b/sample/src/androidMain/kotlin/com/github/panpf/sketch/sample/ui/test/FetcherTestScreen.android.kt @@ -75,11 +75,11 @@ private suspend fun loadUserAppPackageInfo( context.packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS) (if (fromHeader) { packageList.find { - it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0 + it.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM == 0 } } else { packageList.findLast { - it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0 + it.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM == 0 } } ?: context.packageManager.getPackageInfo(context.packageName, 0)) } diff --git a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/data/image_repo.common.kt b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/data/image_repo.common.kt index 3b17617f58..c4fef772fa 100644 --- a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/data/image_repo.common.kt +++ b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/data/image_repo.common.kt @@ -17,6 +17,7 @@ fun builtinImages(): List { .plus(ResourceImages.anims) .plus(ResourceImages.numbersGif) .plus(ResourceImages.longQMSHT) + .plus(ResourceImages.longCOMIC) .plus(ResourceImages.clockExifs) .plus(ResourceImages.mp4) .toList() diff --git a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/MyZoomAsyncImage.kt b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/MyZoomAsyncImage.kt index 8b0861f809..c15846b00a 100644 --- a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/MyZoomAsyncImage.kt +++ b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/MyZoomAsyncImage.kt @@ -41,18 +41,19 @@ fun MyZoomAsyncImage( val context = LocalPlatformContext.current val appSettings = context.appSettings - zoomState.apply { - LaunchedEffect(Unit) { - appSettings.showTileBounds.collect { - subsampling.showTileBounds = it - } + LaunchedEffect(zoomState) { + appSettings.showTileBounds.collect { + zoomState.subsampling.showTileBounds = it } - LaunchedEffect(Unit) { - appSettings.readModeEnabled.collect { - zoomable.readMode = if (it) ReadMode.Default else null - } + } + LaunchedEffect(zoomState) { + appSettings.readModeEnabled.collect { + zoomState.zoomable.readMode = if (it) ReadMode.Default else null } } + LaunchedEffect(zoomState) { + zoomState.zoomable.keepTransformWhenSameAspectRatioContentSizeChanged = true + } val request = ComposableImageRequest(uri) { val memoryCache by appSettings.memoryCache.collectAsState() diff --git a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/zoomimage/SketchZoomAsyncImage.kt b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/zoomimage/SketchZoomAsyncImage.kt index 0bd267b948..d5e47421e4 100644 --- a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/zoomimage/SketchZoomAsyncImage.kt +++ b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/components/zoomimage/SketchZoomAsyncImage.kt @@ -25,23 +25,21 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.isSpecified import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.unit.IntSize +import com.github.panpf.sketch.AsyncImage import com.github.panpf.sketch.AsyncImagePainter import com.github.panpf.sketch.AsyncImageState import com.github.panpf.sketch.LocalPlatformContext import com.github.panpf.sketch.PainterState import com.github.panpf.sketch.Sketch -import com.github.panpf.sketch.internal.AsyncImageContent +import com.github.panpf.sketch.internal.requestOf import com.github.panpf.sketch.name -import com.github.panpf.sketch.rememberAsyncImagePainter import com.github.panpf.sketch.rememberAsyncImageState import com.github.panpf.sketch.request.ImageRequest import com.github.panpf.zoomimage.compose.subsampling.subsampling @@ -109,7 +107,7 @@ fun SketchZoomAsyncImage( onLongPress: ((Offset) -> Unit)? = null, onTap: ((Offset) -> Unit)? = null, ) = SketchZoomAsyncImage( - request = ImageRequest(LocalPlatformContext.current, uri), + request = requestOf(LocalPlatformContext.current, uri), contentDescription = contentDescription, sketch = sketch, modifier = modifier, @@ -201,7 +199,7 @@ fun SketchZoomAsyncImage( // moseZoom directly acts on ZoomAsyncImage, causing the zoom center to be abnormal. Box(modifier = modifier.mouseZoom(zoomState.zoomable)) { - BaseZoomAsyncImage( + AsyncImage( request = request, contentDescription = contentDescription, sketch = sketch, @@ -210,6 +208,8 @@ fun SketchZoomAsyncImage( alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality, + clipToBounds = false, + keepContentNoneStartOnDraw = true, modifier = Modifier .matchParentSize() .zoom( @@ -244,8 +244,8 @@ private fun onPainterState( val painterSize = painterState?.painter ?.intrinsicSize ?.takeIf { it.isSpecified } - ?.roundToIntSize() - ?.takeIf { it.isNotEmpty() } + ?.let { IntSize(it.width.roundToInt(), it.height.roundToInt()) } + ?.takeIf { it.width > 0 && it.height > 0 } zoomState.zoomable.contentSize = painterSize ?: IntSize.Zero if (painterState is PainterState.Success) { @@ -267,49 +267,4 @@ private fun onPainterState( } else { zoomState.setSubsamplingImage(null as SubsamplingImage?) } -} - -private fun Size.roundToIntSize(): IntSize { - return IntSize(width.roundToInt(), height.roundToInt()) -} - -private fun IntSize.isNotEmpty(): Boolean = width > 0 && height > 0 - -/** - * 1. Disabled clipToBounds - * 2. alignment = Alignment.TopStart - * 3. contentScale = ContentScale.None - */ -@Composable -private fun BaseZoomAsyncImage( - request: ImageRequest, - contentDescription: String?, - sketch: Sketch, - modifier: Modifier = Modifier, - state: AsyncImageState = rememberAsyncImageState(), - contentScale: ContentScale = ContentScale.Fit, - alpha: Float = DefaultAlpha, - colorFilter: ColorFilter? = null, - filterQuality: FilterQuality = DrawScope.DefaultFilterQuality, -) { - val painter = rememberAsyncImagePainter( - request = request, - sketch = sketch, - state = state, - contentScale = contentScale, - filterQuality = filterQuality - ) - AsyncImageContent( - modifier = modifier.onSizeChanged { size -> - // Ensure images are prepared before content is drawn when in-memory cache exists - state.setSize(size) - }, - painter = painter, - contentDescription = contentDescription, - alignment = Alignment.TopStart, - contentScale = ContentScale.None, - alpha = alpha, - colorFilter = colorFilter, - clipToBounds = false, - ) } \ No newline at end of file diff --git a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/MainMenu.kt b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/MainMenu.kt index 1fb62ea42d..a48506c07d 100644 --- a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/MainMenu.kt +++ b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/MainMenu.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import com.github.panpf.sketch.LocalPlatformContext import com.github.panpf.sketch.sample.appSettings @@ -31,14 +32,12 @@ import org.jetbrains.compose.resources.painterResource fun MainMenu(modifier: Modifier = Modifier) { val colorScheme = MaterialTheme.colorScheme Row( - modifier = modifier.background( - color = colorScheme.tertiaryContainer, - shape = RoundedCornerShape(50) - ) + modifier = modifier + .clip(RoundedCornerShape(50)) + .background(color = colorScheme.tertiaryContainer) ) { val context = LocalPlatformContext.current val appSettings = context.appSettings - val modifier1 = Modifier.size(40.dp).padding(10.dp) val disallowAnimatedImageInList by appSettings.disallowAnimatedImageInList.collectAsState() val staggeredGridMode by appSettings.staggeredGridMode.collectAsState() val playIcon = if (disallowAnimatedImageInList) { @@ -54,26 +53,33 @@ fun MainMenu(modifier: Modifier = Modifier) { Icon( painter = playIcon, contentDescription = null, - modifier = modifier1.clickable { - appSettings.disallowAnimatedImageInList.value = !disallowAnimatedImageInList - }, + modifier = Modifier + .size(40.dp) + .clickable { + appSettings.disallowAnimatedImageInList.value = !disallowAnimatedImageInList + } + .padding(10.dp), tint = colorScheme.onTertiaryContainer ) Icon( painter = staggeredGridModeIcon, contentDescription = null, - modifier = modifier1.clickable { - appSettings.staggeredGridMode.value = !staggeredGridMode - }, + modifier = Modifier.size(40.dp) + .clickable { + appSettings.staggeredGridMode.value = !staggeredGridMode + } + .padding(10.dp), tint = colorScheme.onTertiaryContainer ) val settingsDialogState = rememberMyDialogState() Icon( painter = painterResource(drawable.ic_settings), contentDescription = null, - modifier = modifier1.clickable { - settingsDialogState.show() - }, + modifier = Modifier.size(40.dp) + .clickable { + settingsDialogState.show() + } + .padding(10.dp), tint = colorScheme.onTertiaryContainer ) MyDialog(settingsDialogState) { diff --git a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/setting/AppSettingsList.common.kt b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/setting/AppSettingsList.common.kt index 84a6efdae1..c74ebd0f40 100644 --- a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/setting/AppSettingsList.common.kt +++ b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/setting/AppSettingsList.common.kt @@ -512,37 +512,41 @@ fun DropdownSetting(settingItem: DropdownSettingItem) { } Spacer(modifier = Modifier.width(10.dp)) - val value by settingItem.state.collectAsState() - Text(text = value.toString(), fontSize = 10.sp) - Icon( - painter = painterResource(drawable.ic_expand_more), - contentDescription = "more" - ) - } - DropdownMenu( - expanded = expanded, - modifier = Modifier.align(Alignment.CenterEnd), - onDismissRequest = { expanded = false }, - ) { - settingItem.values.forEachIndexed { index, value -> - if (index > 0) { - HorizontalDivider( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 14.dp) + Box { + Row { + val value by settingItem.state.collectAsState() + Text(text = value.toString(), fontSize = 10.sp) + Icon( + painter = painterResource(drawable.ic_expand_more), + contentDescription = "more" ) } - DropdownMenuItem( - text = { Text(text = value.toString()) }, - onClick = { - settingItem.state.value = value - expanded = false - coroutineScope.launch { - settingItem.onItemClick?.invoke(value) + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + settingItem.values.forEachIndexed { index, value -> + if (index > 0) { + HorizontalDivider( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 14.dp) + ) } + DropdownMenuItem( + text = { Text(text = value.toString()) }, + onClick = { + settingItem.state.value = value + expanded = false + coroutineScope.launch { + settingItem.onItemClick?.invoke(value) + } + } + ) } - ) + } } } } diff --git a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/test/TestPage.common.kt b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/test/TestPage.common.kt index ab699b32df..6d350d98e1 100644 --- a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/test/TestPage.common.kt +++ b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/test/TestPage.common.kt @@ -68,6 +68,7 @@ fun TestPage() { add(TestGroup("Other")) add(TestItem("DisplayInsanity", DisplayInsanityTestScreen())) + add(TestItem("UserZoom", UserZoomTestScreen())) add(TestItem("Temp", TempTestScreen())) add(ProjectInfo) diff --git a/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/test/UserZoomTestScreen.kt b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/test/UserZoomTestScreen.kt new file mode 100644 index 0000000000..619f9f3d80 --- /dev/null +++ b/sample/src/commonMain/kotlin/com/github/panpf/sketch/sample/ui/test/UserZoomTestScreen.kt @@ -0,0 +1,141 @@ +package com.github.panpf.sketch.sample.ui.test + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.NavigationBarDefaults +import androidx.compose.material3.ScrollableTabRow +import androidx.compose.material3.Tab +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.geometry.isSpecified +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.roundToIntSize +import com.github.panpf.sketch.AsyncImage +import com.github.panpf.sketch.cache.CachePolicy +import com.github.panpf.sketch.images.ResourceImages +import com.github.panpf.sketch.rememberAsyncImageState +import com.github.panpf.sketch.request.ComposableImageRequest +import com.github.panpf.sketch.sample.ui.base.BaseScreen +import com.github.panpf.sketch.sample.ui.base.ToolbarScaffold +import com.github.panpf.sketch.sample.ui.util.isNotEmpty +import com.github.panpf.sketch.state.ThumbnailMemoryCacheStateImage +import com.github.panpf.zoomimage.compose.ZoomState +import com.github.panpf.zoomimage.compose.rememberZoomState +import com.github.panpf.zoomimage.compose.zoom.ZoomableState +import com.github.panpf.zoomimage.compose.zoom.zoomable +import kotlinx.coroutines.launch + +class UserZoomTestScreen : BaseScreen() { + + @Composable + override fun DrawContent() { + ToolbarScaffold(title = "UserZoomTest") { + val tabs = remember { + listOf("HOR", "VER") + } + val pagerState = rememberPagerState(0) { tabs.size } + val coroutineScope = rememberCoroutineScope() + Column( + Modifier.fillMaxWidth() + .windowInsetsPadding(NavigationBarDefaults.windowInsets) + ) { + ScrollableTabRow(selectedTabIndex = pagerState.currentPage, edgePadding = 20.dp) { + tabs.forEachIndexed { index, title -> + Tab( + selected = index == pagerState.currentPage, + onClick = { + coroutineScope.launch { + pagerState.scrollToPage(index) + } + }, + content = { + Text( + text = title, + modifier = Modifier.padding(vertical = 8.dp, horizontal = 10.dp) + ) + } + ) + } + } + HorizontalPager(state = pagerState) { + when (tabs[it]) { + "HOR" -> UserZoomContent(ResourceImages.longQMSHT.uri) + "VER" -> UserZoomContent(ResourceImages.longCOMIC.uri) + } + } + } + } + } + + @Composable + private fun UserZoomContent(uri: String) { + Box(Modifier.fillMaxSize()) { + val imageState = rememberAsyncImageState() + val zoomState: ZoomState = rememberZoomState() + imageState.onPainterState = remember { + { + val painterSize = it.painter + ?.intrinsicSize + ?.takeIf { it.isSpecified } + ?.roundToIntSize() + ?.takeIf { it.isNotEmpty() } + zoomState.zoomable.contentSize = painterSize ?: IntSize.Zero + } + } + AsyncImage( + request = ComposableImageRequest(uri) { + memoryCachePolicy(CachePolicy.DISABLED) + resultCachePolicy(CachePolicy.DISABLED) + placeholder(ThumbnailMemoryCacheStateImage(uri)) + crossfade(fadeStart = false) + }, + state = imageState, + contentDescription = "", + modifier = Modifier.matchParentSize() + .background(Color.Cyan) + .zoomable( + zoomable = zoomState.zoomable, + userSetupContentSize = true, + ) + .zoomingWithUser(zoomState.zoomable) + ) + } + } + + /** + * A Modifier that applies changes in [ZoomableState].transform to the component. It can be used on any composable component. + */ + private fun Modifier.zoomingWithUser( + zoomable: ZoomableState + ): Modifier = this + .clipToBounds() + .graphicsLayer { + val transform = zoomable.userTransform + zoomable.logger.v { "ZoomableState. graphicsLayer. transform=$transform" } + scaleX = transform.scaleX + scaleY = transform.scaleY + translationX = transform.offsetX + translationY = transform.offsetY + transformOrigin = transform.scaleOrigin + } + // Because rotationOrigin and rotationOrigin are different, they must be set separately. + .graphicsLayer { + val transform = zoomable.transform + rotationZ = transform.rotation + transformOrigin = transform.rotationOrigin + } +} \ No newline at end of file diff --git a/sample/src/iosMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.ios.kt b/sample/src/iosMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.ios.kt index 353d94f2ba..3c611e6216 100644 --- a/sample/src/iosMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.ios.kt +++ b/sample/src/iosMain/kotlin/com/github/panpf/sketch/sample/ui/gallery/LocalPhotoListPage.ios.kt @@ -1,5 +1,6 @@ package com.github.panpf.sketch.sample.ui.gallery import dev.icerock.moko.permissions.Permission +import dev.icerock.moko.permissions.storage.STORAGE actual fun localPhotoListPermission(): Any? = Permission.STORAGE \ No newline at end of file diff --git a/sketch-animated-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/ScaledAnimatableDrawable.kt b/sketch-animated-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/ScaledAnimatableDrawable.kt index a43a334c80..e0f0d4a179 100644 --- a/sketch-animated-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/ScaledAnimatableDrawable.kt +++ b/sketch-animated-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/ScaledAnimatableDrawable.kt @@ -32,7 +32,7 @@ import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.withSave import androidx.vectordrawable.graphics.drawable.Animatable2Compat import com.github.panpf.sketch.drawable.internal.AnimatableCallbackHelper -import com.github.panpf.sketch.util.computeScaleMultiplierWithFit +import com.github.panpf.sketch.util.calculateScaleMultiplierWithFit import com.github.panpf.sketch.util.toLogString import kotlin.math.roundToInt @@ -97,18 +97,23 @@ class ScaledAnimatableDrawable @JvmOverloads constructor( val targetWidth = bounds.width() val targetHeight = bounds.height() - val multiplier = - computeScaleMultiplierWithFit(width, height, targetWidth, targetHeight, fitScale) - - val left = ((targetWidth - multiplier * width) / 2).roundToInt() - val top = ((targetHeight - multiplier * height) / 2).roundToInt() + val multiplier = calculateScaleMultiplierWithFit( + srcWidth = width.toFloat(), + srcHeight = height.toFloat(), + dstWidth = targetWidth.toFloat(), + dstHeight = targetHeight.toFloat(), + fitScale = fitScale + ) + + val left = ((targetWidth - multiplier * width) / 2f).roundToInt() + val top = ((targetHeight - multiplier * height) / 2f).roundToInt() val right = left + width val bottom = top + height drawable.setBounds(left, top, right, bottom) childDx = bounds.left.toFloat() childDy = bounds.top.toFloat() - childScale = multiplier.toFloat() + childScale = multiplier } override fun onLevelChange(level: Int) = drawable.setLevel(level) diff --git a/sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/drawable/MovieDrawable.kt b/sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/drawable/MovieDrawable.kt index b207f6919d..f7b4d934d8 100644 --- a/sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/drawable/MovieDrawable.kt +++ b/sketch-animated-gif/src/androidMain/kotlin/com/github/panpf/sketch/drawable/MovieDrawable.kt @@ -40,7 +40,7 @@ import com.github.panpf.sketch.request.ANIMATION_REPEAT_INFINITE import com.github.panpf.sketch.transform.AnimatedTransformation import com.github.panpf.sketch.transform.PixelOpacity.OPAQUE import com.github.panpf.sketch.transform.PixelOpacity.UNCHANGED -import com.github.panpf.sketch.util.computeScaleMultiplierWithFit +import com.github.panpf.sketch.util.calculateScaleMultiplierWithFit /** * A [Drawable] that supports rendering [Movie]s (i.e. GIFs). @@ -236,13 +236,16 @@ class MovieDrawable( val movieHeight = movie.height() if (movieWidth <= 0 || movieHeight <= 0) return - softwareScale = - computeScaleMultiplierWithFit(movieWidth, movieHeight, boundsWidth, boundsHeight, true) - .run { + softwareScale = calculateScaleMultiplierWithFit( + srcWidth = movieWidth.toFloat(), + srcHeight = movieHeight.toFloat(), + dstWidth = boundsWidth.toFloat(), + dstHeight = boundsHeight.toFloat(), + fitScale = true + ).run { // if (isSoftwareScalingEnabled) this else - coerceAtMost(1.0) - } - .toFloat() + coerceAtMost(1f) + } val bitmapWidth = (softwareScale * movieWidth).toInt() val bitmapHeight = (softwareScale * movieHeight).toInt() @@ -256,17 +259,15 @@ class MovieDrawable( // hardwareDx = 0f // hardwareDy = 0f // } else { - hardwareScale = - computeScaleMultiplierWithFit( - bitmapWidth, - bitmapHeight, - boundsWidth, - boundsHeight, - true - ) - .toFloat() - hardwareDx = bounds.left + (boundsWidth - hardwareScale * bitmapWidth) / 2 - hardwareDy = bounds.top + (boundsHeight - hardwareScale * bitmapHeight) / 2 + hardwareScale = calculateScaleMultiplierWithFit( + srcWidth = bitmapWidth.toFloat(), + srcHeight = bitmapHeight.toFloat(), + dstWidth = boundsWidth.toFloat(), + dstHeight = boundsHeight.toFloat(), + fitScale = true + ) + hardwareDx = bounds.left + (boundsWidth - hardwareScale * bitmapWidth) / 2f + hardwareDy = bounds.top + (boundsHeight - hardwareScale * bitmapHeight) / 2f // } } diff --git a/sketch-compose-core/src/androidMain/kotlin/com/github/panpf/sketch/painter/DrawablePainter.kt b/sketch-compose-core/src/androidMain/kotlin/com/github/panpf/sketch/painter/DrawablePainter.kt index 35c73a16c0..acc6e79e55 100644 --- a/sketch-compose-core/src/androidMain/kotlin/com/github/panpf/sketch/painter/DrawablePainter.kt +++ b/sketch-compose-core/src/androidMain/kotlin/com/github/panpf/sketch/painter/DrawablePainter.kt @@ -51,6 +51,7 @@ import androidx.compose.ui.unit.LayoutDirection.Rtl import com.github.panpf.sketch.drawable.EquitableDrawable import com.github.panpf.sketch.util.RememberedCounter import com.github.panpf.sketch.util.toLogString +import kotlin.math.ceil import kotlin.math.roundToInt /** @@ -179,7 +180,7 @@ open class DrawablePainter( drawInvalidateTick // Update the Drawable's bounds - drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt()) + drawable.setBounds(0, 0, ceil(size.width).toInt(), ceil(size.height).toInt()) canvas.withSave { drawable.draw(canvas.nativeCanvas) diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt index 20e0929ccd..61970cde86 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImage.kt @@ -18,6 +18,7 @@ package com.github.panpf.sketch import androidx.compose.runtime.Composable import androidx.compose.runtime.NonRestartableComposable +import androidx.compose.runtime.Stable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter @@ -26,7 +27,10 @@ import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQuality import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.LayoutDirection import com.github.panpf.sketch.internal.AsyncImageContent +import com.github.panpf.sketch.internal.requestOf import com.github.panpf.sketch.request.ImageRequest /** @@ -49,6 +53,8 @@ import com.github.panpf.sketch.request.ImageRequest * rendered onscreen. * @param filterQuality Sampling algorithm applied to a bitmap when it is scaled and drawn into the * destination. + * @param clipToBounds Whether to clip the content to the bounds of this layout. Defaults to true. + * @param keepContentNoneStartOnDraw Whether to always draw the content as none on the left on drawing, even if LayoutDirection is Rtl. * * @see com.github.panpf.sketch.compose.core.common.test.AsyncImageTest.testAsyncImage1 */ @@ -66,8 +72,9 @@ fun AsyncImage( colorFilter: ColorFilter? = null, filterQuality: FilterQuality = DefaultFilterQuality, clipToBounds: Boolean = true, + keepContentNoneStartOnDraw: Boolean = false, ) = AsyncImage( - request = ImageRequest(LocalPlatformContext.current, uri), + request = requestOf(LocalPlatformContext.current, uri), contentDescription = contentDescription, sketch = sketch, modifier = modifier, @@ -78,6 +85,7 @@ fun AsyncImage( colorFilter = colorFilter, filterQuality = filterQuality, clipToBounds = clipToBounds, + keepContentNoneStartOnDraw = keepContentNoneStartOnDraw, ) /** @@ -100,6 +108,8 @@ fun AsyncImage( * rendered onscreen. * @param filterQuality Sampling algorithm applied to a bitmap when it is scaled and drawn into the * destination. + * @param clipToBounds Whether to clip the content to the bounds of this layout. Defaults to true. + * @param keepContentNoneStartOnDraw Whether to always draw the content as none on the left on drawing, even if LayoutDirection is Rtl. * * @see com.github.panpf.sketch.compose.core.common.test.AsyncImageTest.testAsyncImage2 */ @@ -116,6 +126,7 @@ fun AsyncImage( colorFilter: ColorFilter? = null, filterQuality: FilterQuality = DefaultFilterQuality, clipToBounds: Boolean = true, + keepContentNoneStartOnDraw: Boolean = false, ) { val painter = rememberAsyncImagePainter( request = request, @@ -124,6 +135,13 @@ fun AsyncImage( contentScale = contentScale, filterQuality = filterQuality ) + val drawAlignment = if (keepContentNoneStartOnDraw) { + Alignment.TopStart.rtlFlipped(LocalLayoutDirection.current) + } else { + alignment + } + val drawContentScale = + if (keepContentNoneStartOnDraw) ContentScale.None else contentScale AsyncImageContent( modifier = modifier.onSizeChanged { size -> // Ensure images are prepared before content is drawn when in-memory cache exists @@ -131,10 +149,30 @@ fun AsyncImage( }, painter = painter, contentDescription = contentDescription, - alignment = alignment, - contentScale = contentScale, + alignment = drawAlignment, + contentScale = drawContentScale, alpha = alpha, colorFilter = colorFilter, clipToBounds = clipToBounds, ) +} + +/** + * If [layoutDirection] is [LayoutDirection.Rtl], returns the horizontally flipped [Alignment], otherwise returns itself + */ +@Stable +private fun Alignment.rtlFlipped(layoutDirection: LayoutDirection): Alignment { + if (layoutDirection != LayoutDirection.Rtl) return this + return when (this) { + Alignment.TopStart -> Alignment.TopEnd + Alignment.TopCenter -> Alignment.TopCenter + Alignment.TopEnd -> Alignment.TopStart + Alignment.CenterStart -> Alignment.CenterEnd + Alignment.Center -> Alignment.Center + Alignment.CenterEnd -> Alignment.CenterStart + Alignment.BottomStart -> Alignment.BottomEnd + Alignment.BottomCenter -> Alignment.BottomCenter + Alignment.BottomEnd -> Alignment.BottomStart + else -> this + } } \ No newline at end of file diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt index 638fb8dca0..7dfab32009 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImagePainter.kt @@ -28,7 +28,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.geometry.Size -import androidx.compose.ui.geometry.isUnspecified import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.FilterQuality @@ -37,10 +36,10 @@ import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.toIntSize +import com.github.panpf.sketch.internal.requestOf import com.github.panpf.sketch.painter.SketchPainter import com.github.panpf.sketch.request.ImageRequest -import com.github.panpf.sketch.util.isEmpty -import com.github.panpf.sketch.util.toIntSizeOrNull /** * Return an [AsyncImagePainter] that executes an [ImageRequest] asynchronously and renders the result. @@ -75,7 +74,7 @@ fun rememberAsyncImagePainter( contentScale: ContentScale = ContentScale.Fit, filterQuality: FilterQuality = DefaultFilterQuality, ): AsyncImagePainter = rememberAsyncImagePainter( - request = ImageRequest(LocalPlatformContext.current, uri), + request = requestOf(LocalPlatformContext.current, uri), sketch = sketch, state = state, contentScale = contentScale, @@ -140,22 +139,20 @@ class AsyncImagePainter internal constructor( get() = state.painter?.intrinsicSize ?: Size.Unspecified override fun DrawScope.onDraw() { - // Use draw size as component size - // It plays a decisive role when using AsyncImagePainter without AsyncImage - // When intrinsicSize is empty or Unspecified, drawSize represents the size of the component. - val componentSizeEmpty = state.size?.takeIf { !it.isEmpty() } == null - val painterSizeEmpty = intrinsicSize.let { it.isUnspecified || it.isEmpty() } - if (componentSizeEmpty && painterSizeEmpty) { - val drawSize = this@onDraw.size.toIntSizeOrNull() - if (drawSize != null) { - state.setSize(drawSize) - } - } + setupRequestSize(this@onDraw.size) // Draw the current painter. state.painter?.apply { draw(size, alpha, colorFilter) } } + private fun setupRequestSize(drawSize: Size) { + // When using AsyncImage or SubcomposeAsyncImage, it will not be executed here because they will actively call setSize + // So this will only be executed when AsyncImagePainter is used as a Painter in the Image component + if (state.size == null) { + state.setSize(drawSize.toIntSize()) + } + } + override fun applyAlpha(alpha: Float): Boolean { this.alpha = alpha return true diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt index bdaff2da5d..05e81812fb 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/AsyncImageState.kt @@ -41,6 +41,7 @@ import com.github.panpf.sketch.target.AsyncImageTarget import com.github.panpf.sketch.util.RememberedCounter import com.github.panpf.sketch.util.difference import com.github.panpf.sketch.util.toHexString +import com.github.panpf.sketch.util.windowContainerSize import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -58,10 +59,14 @@ import kotlinx.coroutines.launch */ @Composable fun rememberAsyncImageState(options: ImageOptions? = null): AsyncImageState { + val context = LocalPlatformContext.current val inspectionMode = LocalInspectionMode.current val lifecycle = if (inspectionMode) GlobalLifecycle else LocalLifecycleOwner.current.lifecycle - return remember(inspectionMode, lifecycle, options) { - AsyncImageState(inspectionMode, lifecycle, options) + val windowContainerSize = windowContainerSize() + return remember(context, inspectionMode, lifecycle, options) { + AsyncImageState(context, inspectionMode, lifecycle, options) + }.apply { + this@apply.target.windowContainerSize = windowContainerSize } } @@ -72,11 +77,15 @@ fun rememberAsyncImageState(options: ImageOptions? = null): AsyncImageState { */ @Composable fun rememberAsyncImageState(optionsLazy: () -> ImageOptions): AsyncImageState { + val context = LocalPlatformContext.current val inspectionMode = LocalInspectionMode.current val lifecycle = if (inspectionMode) GlobalLifecycle else LocalLifecycleOwner.current.lifecycle - return remember(inspectionMode, lifecycle) { + val windowContainerSize = windowContainerSize() + return remember(context, inspectionMode, lifecycle) { val options = optionsLazy.invoke() - AsyncImageState(inspectionMode, lifecycle, options) + AsyncImageState(context, inspectionMode, lifecycle, options) + }.apply { + this@apply.target.windowContainerSize = windowContainerSize } } @@ -87,12 +96,13 @@ fun rememberAsyncImageState(optionsLazy: () -> ImageOptions): AsyncImageState { */ @Stable class AsyncImageState internal constructor( + val context: PlatformContext, val inspectionMode: Boolean, val lifecycle: Lifecycle, val imageOptions: ImageOptions?, ) : RememberObserver { - internal val target = AsyncImageTarget(lifecycle, imageOptions) + internal val target = AsyncImageTarget(context, lifecycle, imageOptions) internal var lastRequest: ImageRequest? = null internal var loadImageJob: Job? = null internal var coroutineScope: CoroutineScope? = null diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt index 01b7f8170d..f715f76ef8 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/SubcomposeAsyncImage.kt @@ -37,8 +37,9 @@ import com.github.panpf.sketch.PainterState.Error import com.github.panpf.sketch.PainterState.Loading import com.github.panpf.sketch.PainterState.Success import com.github.panpf.sketch.internal.AsyncImageContent +import com.github.panpf.sketch.internal.requestOf import com.github.panpf.sketch.request.ImageRequest -import com.github.panpf.sketch.util.toIntSizeOrNull +import com.github.panpf.sketch.util.toRequestSize /** * A composable that executes an [ImageRequest] asynchronously and renders the result. @@ -84,7 +85,7 @@ fun SubcomposeAsyncImage( filterQuality: FilterQuality = DefaultFilterQuality, clipToBounds: Boolean = true, ) = SubcomposeAsyncImage( - request = ImageRequest(LocalPlatformContext.current, uri), + request = requestOf(LocalPlatformContext.current, uri), contentDescription = contentDescription, sketch = sketch, modifier = modifier, @@ -136,7 +137,7 @@ fun SubcomposeAsyncImage( filterQuality: FilterQuality = DefaultFilterQuality, content: @Composable SubcomposeAsyncImageScope.() -> Unit, ) = SubcomposeAsyncImage( - request = ImageRequest(LocalPlatformContext.current, uri), + request = requestOf(LocalPlatformContext.current, uri), contentDescription = contentDescription, sketch = sketch, modifier = modifier, @@ -260,7 +261,7 @@ fun SubcomposeAsyncImage( propagateMinConstraints = true ) { // Ensure images are prepared before content is drawn when in-memory cache exists - constraints.toIntSizeOrNull()?.let { state.setSize(it) } + state.setSize(constraints.toRequestSize()) RealSubcomposeAsyncImageScope( parentScope = this, diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/internal/utils.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/internal/utils.kt new file mode 100644 index 0000000000..6d33a4a097 --- /dev/null +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/internal/utils.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 panpf + * Copyright 2023 Coil Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.panpf.sketch.internal + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import com.github.panpf.sketch.LocalPlatformContext +import com.github.panpf.sketch.PlatformContext +import com.github.panpf.sketch.request.ImageRequest + +/** + * Create a new [ImageRequest] with the given [uri] and [context], then remember it. + * + * @see com.github.panpf.sketch.compose.core.common.test.internal.UtilsTest.testRequestOf + */ +@Composable +fun requestOf(context: PlatformContext, uri: String?): ImageRequest { + return remember(context, uri) { ImageRequest(context, uri) } +} + +/** + * Create a new [ImageRequest] with the given [uri], then remember it. + * + * @see com.github.panpf.sketch.compose.core.common.test.internal.UtilsTest.testRequestOf2 + */ +@Composable +fun requestOf(uri: String?): ImageRequest { + val context = LocalPlatformContext.current + return remember(uri) { ImageRequest(context, uri) } +} \ No newline at end of file diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/CrossfadePainter.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/CrossfadePainter.kt index ad7f328f17..7d072ff63b 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/CrossfadePainter.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/CrossfadePainter.kt @@ -32,14 +32,11 @@ import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.inset import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.layout.ScaleFactor -import androidx.compose.ui.layout.times import com.github.panpf.sketch.transition.CrossfadeTransition import com.github.panpf.sketch.transition.TransitionPainter -import com.github.panpf.sketch.util.computeScaleMultiplierWithFit +import com.github.panpf.sketch.util.calculateScaleMultiplierWithFit import kotlin.js.JsName import kotlin.math.max -import kotlin.math.roundToInt import kotlin.time.TimeSource /** @@ -169,16 +166,16 @@ class CrossfadePainter constructor( if (painter == null || alpha <= 0) return with(painter) { - val drawSize = this@drawPainter.size - val painterScaledSize = computeScaledSize(this@with.intrinsicSize, drawSize) - if (drawSize.isUnspecified || drawSize.isEmpty()) { - draw(painterScaledSize, alpha, colorFilter) + val dstSize: Size = this@drawPainter.size + val srcSize: Size = this@with.intrinsicSize + val scaledSrcSize = computeScaledSize(srcSize, dstSize) + if (dstSize.isUnspecified || dstSize.isEmpty()) { + draw(scaledSrcSize, alpha, colorFilter) } else { - inset( - horizontal = (drawSize.width - painterScaledSize.width) / 2, - vertical = (drawSize.height - painterScaledSize.height) / 2 - ) { - draw(painterScaledSize, alpha, colorFilter) + val horizontal = (dstSize.width - scaledSrcSize.width) / 2f + val vertical = (dstSize.height - scaledSrcSize.height) / 2f + inset(horizontal = horizontal, vertical = vertical) { + draw(scaledSrcSize, alpha, colorFilter) } } } @@ -187,14 +184,17 @@ class CrossfadePainter constructor( private fun computeScaledSize(srcSize: Size, dstSize: Size): Size { if (srcSize.isUnspecified || srcSize.isEmpty()) return dstSize if (dstSize.isUnspecified || dstSize.isEmpty()) return dstSize - val sizeMultiplier = computeScaleMultiplierWithFit( - srcWidth = srcSize.width.roundToInt(), - srcHeight = srcSize.height.roundToInt(), - dstWidth = dstSize.width.roundToInt(), - dstHeight = dstSize.height.roundToInt(), + val sizeMultiplier = calculateScaleMultiplierWithFit( + srcWidth = srcSize.width, + srcHeight = srcSize.height, + dstWidth = dstSize.width, + dstHeight = dstSize.height, fitScale = fitScale ) - return srcSize * ScaleFactor(sizeMultiplier.toFloat(), sizeMultiplier.toFloat()) + return Size( + width = srcSize.width * sizeMultiplier, + height = srcSize.height * sizeMultiplier + ) } override fun onRemembered() { diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt index 62f496e78a..b9e7752874 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/IconPainter.common.kt @@ -32,14 +32,9 @@ import androidx.compose.ui.graphics.painter.ColorPainter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.toArgb import com.github.panpf.sketch.util.Key -import com.github.panpf.sketch.util.Rect -import com.github.panpf.sketch.util.calculateCropBounds -import com.github.panpf.sketch.util.calculateInsideBounds -import com.github.panpf.sketch.util.size -import com.github.panpf.sketch.util.toIntSizeOrNull +import com.github.panpf.sketch.util.calculateScaleMultiplierWithCrop +import com.github.panpf.sketch.util.calculateScaleMultiplierWithInside import com.github.panpf.sketch.util.toLogString -import com.github.panpf.sketch.util.toSize -import com.github.panpf.sketch.util.toSketchSize /** * Create a [IconPainter] and remember it. @@ -250,43 +245,45 @@ open class IconPainter constructor( } override fun DrawScope.onDraw() { - val containerSize = this@onDraw.size - val containerBounds = Rect(0, 0, containerSize.width.toInt(), containerSize.height.toInt()) + val dstSize = this@onDraw.size if (background != null) { val backgroundSize = background.intrinsicSize - val backgroundBounds = if (backgroundSize.isSpecified) { - calculateCropBounds( - contentSize = backgroundSize.toIntSizeOrNull()!!.toSketchSize(), - containerBounds = containerBounds + if (backgroundSize.isSpecified) { + val backgroundScaleFactor = calculateScaleMultiplierWithCrop( + srcWidth = backgroundSize.width, + srcHeight = backgroundSize.height, + dstWidth = dstSize.width, + dstHeight = dstSize.height ) + val scaledBackgroundSize = backgroundSize * backgroundScaleFactor + val backgroundLeft = (dstSize.width - scaledBackgroundSize.width) / 2f + val backgroundTop = (dstSize.height - scaledBackgroundSize.height) / 2f + translate(left = backgroundLeft, top = backgroundTop) { + with(background) { + draw(size = scaledBackgroundSize, colorFilter = colorFilter) + } + } } else { - containerBounds - } - translate( - left = backgroundBounds.left.toFloat(), - top = backgroundBounds.top.toFloat() - ) { with(background) { - draw( - size = backgroundBounds.size.toSize(), - colorFilter = colorFilter - ) + draw(size = dstSize, colorFilter = colorFilter) } } } val realIconSize = iconSize ?: icon.intrinsicSize - val iconBounds = calculateInsideBounds( - contentSize = realIconSize.toIntSizeOrNull()!!.toSketchSize(), - containerBounds = containerBounds + val iconScaleFactor = calculateScaleMultiplierWithInside( + srcWidth = realIconSize.width, + srcHeight = realIconSize.height, + dstWidth = dstSize.width, + dstHeight = dstSize.height ) - translate(left = iconBounds.left.toFloat(), top = iconBounds.top.toFloat()) { + val scaledRealIconSize = realIconSize * iconScaleFactor + val iconLeft = (dstSize.width - scaledRealIconSize.width) / 2f + val iconTop = (dstSize.height - scaledRealIconSize.height) / 2f + translate(left = iconLeft, top = iconTop) { with(icon) { val filter = iconTint?.let { ColorFilter.tint(it) } ?: colorFilter - draw( - size = iconBounds.size.toSize(), - colorFilter = filter - ) + draw(size = scaledRealIconSize, colorFilter = filter) } } } diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ImageBitmapPainter.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ImageBitmapPainter.kt index d037e51430..7b477500f0 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ImageBitmapPainter.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ImageBitmapPainter.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.IntSize import com.github.panpf.sketch.util.toLogString +import kotlin.math.ceil /** * [ImageBitmap] converted to [ImageBitmapPainter] @@ -47,8 +48,12 @@ class ImageBitmapPainter( override val intrinsicSize = Size(bitmap.width.toFloat(), bitmap.height.toFloat()) override fun DrawScope.onDraw() { - val intSize = IntSize(size.width.toInt(), size.height.toInt()) - drawImage(bitmap, dstSize = intSize, filterQuality = filterQuality) + // ceil must be used here instead of round, because when size is a decimal and downward round, the image cannot completely overwrite dst + val dstSize = IntSize( + width = ceil(size.width).toInt(), + height = ceil(size.height).toInt() + ) + drawImage(bitmap, dstSize = dstSize, filterQuality = filterQuality) } override fun equals(other: Any?): Boolean { diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt index 8504f7369d..b54dd8108f 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/painter/ResizePainter.kt @@ -26,12 +26,9 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.inset import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.layout.ScaleFactor -import androidx.compose.ui.layout.times import com.github.panpf.sketch.resize.Scale import com.github.panpf.sketch.resize.Scale.CENTER_CROP -import com.github.panpf.sketch.util.computeScaleMultiplierWithFit -import kotlin.math.roundToInt +import com.github.panpf.sketch.util.calculateScaleMultiplierWithFit /** * Create a [ResizePainter] and remember it @@ -99,21 +96,19 @@ open class ResizePainter( override fun DrawScope.onDraw() { with(painter) { - val drawSize = this@onDraw.size - val painterScaledSize = computeScaledSize(this@with.intrinsicSize, drawSize) - if (drawSize.isUnspecified || drawSize.isEmpty()) { - draw(painterScaledSize, alpha, colorFilter) + val dstSize: Size = this@onDraw.size + val srcSize: Size = this@with.intrinsicSize + val scaledSrcSize = computeScaledSize(srcSize, dstSize) + if (dstSize.isUnspecified || dstSize.isEmpty()) { + draw(scaledSrcSize, alpha, colorFilter) } else { val (horizontal, vertical) = when (scale) { Scale.START_CROP -> 0f to 0f - Scale.END_CROP -> drawSize.width - painterScaledSize.width to drawSize.height - painterScaledSize.height - else -> (drawSize.width - painterScaledSize.width) / 2 to (drawSize.height - painterScaledSize.height) / 2 + Scale.END_CROP -> dstSize.width - scaledSrcSize.width to dstSize.height - scaledSrcSize.height + else -> (dstSize.width - scaledSrcSize.width) / 2f to (dstSize.height - scaledSrcSize.height) / 2f } - inset( - horizontal = horizontal, - vertical = vertical - ) { - draw(painterScaledSize, alpha, colorFilter) + inset(horizontal = horizontal, vertical = vertical) { + draw(scaledSrcSize, alpha, colorFilter) } } } @@ -122,14 +117,17 @@ open class ResizePainter( private fun computeScaledSize(srcSize: Size, dstSize: Size): Size { if (srcSize.isUnspecified || srcSize.isEmpty()) return dstSize if (dstSize.isUnspecified || dstSize.isEmpty()) return dstSize - val sizeMultiplier = computeScaleMultiplierWithFit( - srcWidth = srcSize.width.roundToInt(), - srcHeight = srcSize.height.roundToInt(), - dstWidth = dstSize.width.roundToInt(), - dstHeight = dstSize.height.roundToInt(), + val sizeMultiplier = calculateScaleMultiplierWithFit( + srcWidth = srcSize.width, + srcHeight = srcSize.height, + dstWidth = dstSize.width, + dstHeight = dstSize.height, fitScale = false ) - return srcSize * ScaleFactor(sizeMultiplier.toFloat(), sizeMultiplier.toFloat()) + return Size( + width = srcSize.width * sizeMultiplier, + height = srcSize.height * sizeMultiplier + ) } override fun equals(other: Any?): Boolean { diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/AsyncImageTarget.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/AsyncImageTarget.kt index 9a933d8499..8b0944b8c7 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/AsyncImageTarget.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/target/AsyncImageTarget.kt @@ -29,6 +29,7 @@ import com.github.panpf.sketch.AsyncImage import com.github.panpf.sketch.Image import com.github.panpf.sketch.PainterState import com.github.panpf.sketch.PainterState.Loading +import com.github.panpf.sketch.PlatformContext import com.github.panpf.sketch.Sketch import com.github.panpf.sketch.request.ImageOptions import com.github.panpf.sketch.request.ImageRequest @@ -41,7 +42,8 @@ import com.github.panpf.sketch.resize.AsyncImageSizeResolver import com.github.panpf.sketch.resize.ScaleDecider import com.github.panpf.sketch.target.internal.AsyncImageListener import com.github.panpf.sketch.util.fitScale -import com.github.panpf.sketch.util.isEmpty +import com.github.panpf.sketch.util.screenSize +import com.github.panpf.sketch.util.toIntSize import com.github.panpf.sketch.util.toScale /** @@ -49,7 +51,8 @@ import com.github.panpf.sketch.util.toScale * * @see com.github.panpf.sketch.compose.core.common.test.target.AsyncImageTargetTest */ -class AsyncImageTarget( +class AsyncImageTarget constructor( + private val context: PlatformContext, private val lifecycle: Lifecycle, private val imageOptions: ImageOptions?, ) : GenericComposeTarget() { @@ -79,6 +82,16 @@ class AsyncImageTarget( listener.onLoadState = value } + var windowContainerSize: IntSize = context.screenSize().toIntSize() + set(value) { + val finalValue = if (value.width < 100 || value.height < 100) { + IntSize(value.width.coerceAtLeast(100), value.height.coerceAtLeast(100)) + } else { + value + } + field = finalValue + } + override val painter: Painter? get() = painterMutableState.value @@ -103,10 +116,14 @@ class AsyncImageTarget( } fun setSize(size: IntSize) { - if (!size.isEmpty()) { - this.sizeMutableState.value = size - this.sizeResolver.sizeState.value = size - } + // If the width or height is 0, it means that the constraint of the component is to wrap content. + // In this case, the size of the window container can be used instead. + val limitedSize = IntSize( + width = if (size.width > 0) size.width else windowContainerSize.width, + height = if (size.height > 0) size.height else windowContainerSize.height + ) + this.sizeMutableState.value = limitedSize + this.sizeResolver.sizeState.value = limitedSize } fun onRemembered() { @@ -172,18 +189,20 @@ class AsyncImageTarget( if (this === other) return true if (other == null || this::class != other::class) return false other as AsyncImageTarget + if (context != other.context) return false if (lifecycle != other.lifecycle) return false if (imageOptions != other.imageOptions) return false return true } override fun hashCode(): Int { - var result = lifecycle.hashCode() + var result = context.hashCode() + result = 31 * result + (lifecycle.hashCode()) result = 31 * result + (imageOptions?.hashCode() ?: 0) return result } override fun toString(): String { - return "AsyncImageTarget(lifecycle=$lifecycle, options=$imageOptions)" + return "AsyncImageTarget(context=$context, lifecycle=$lifecycle, options=$imageOptions)" } } \ No newline at end of file diff --git a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/util/compose_core_utils.kt b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/util/compose_core_utils.kt index 35713fff22..8abdf42605 100644 --- a/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/util/compose_core_utils.kt +++ b/sketch-compose-core/src/commonMain/kotlin/com/github/panpf/sketch/util/compose_core_utils.kt @@ -17,6 +17,7 @@ package com.github.panpf.sketch.util +import com.github.panpf.sketch.util.Size as SketchSize import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.geometry.Size @@ -34,7 +35,6 @@ import com.github.panpf.sketch.painter.CrossfadePainter import com.github.panpf.sketch.painter.PainterWrapper import com.github.panpf.sketch.resize.Scale import kotlin.math.roundToInt -import com.github.panpf.sketch.util.Size as SketchSize /** * Get window container size @@ -173,6 +173,19 @@ internal fun Constraints.toIntSizeOrNull(): IntSize? = when { else -> null } +/** + * Convert [Constraints] to Resize + * + * @see com.github.panpf.sketch.compose.core.common.test.util.ComposeCoreUtilsTest.testConstraintsToRequestSize + */ +@Stable +internal fun Constraints.toRequestSize(): IntSize { + return IntSize( + width = if (hasBoundedWidth) maxWidth else 0, + height = if (hasBoundedHeight) maxHeight else 0 + ) +} + /** * Find the leaf [Painter] of the [Painter] * diff --git a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImagePainterTest.kt b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImagePainterTest.kt index d75746ff11..002e7a55da 100644 --- a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImagePainterTest.kt +++ b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImagePainterTest.kt @@ -1,5 +1,10 @@ package com.github.panpf.sketch.compose.core.common.test +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.FilterQuality @@ -7,22 +12,29 @@ import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.runComposeUiTest +import androidx.compose.ui.unit.IntSize import com.github.panpf.sketch.AsyncImagePainter import com.github.panpf.sketch.AsyncImageState import com.github.panpf.sketch.asImage +import com.github.panpf.sketch.images.ResourceImages import com.github.panpf.sketch.rememberAsyncImagePainter import com.github.panpf.sketch.rememberAsyncImageState import com.github.panpf.sketch.request.ComposableImageRequest import com.github.panpf.sketch.request.ImageRequest +import com.github.panpf.sketch.request.ImageResult import com.github.panpf.sketch.test.singleton.getTestContextAndSketch import com.github.panpf.sketch.test.utils.LifecycleContainer import com.github.panpf.sketch.test.utils.SizeColorPainter import com.github.panpf.sketch.test.utils.TestLifecycle import com.github.panpf.sketch.test.utils.fakeSuccessImageResult +import com.github.panpf.sketch.test.utils.getTestContext +import com.github.panpf.sketch.util.toIntSize +import com.github.panpf.sketch.util.windowContainerSize import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals import kotlin.test.assertSame +import kotlin.test.assertTrue @OptIn(ExperimentalTestApi::class) class AsyncImagePainterTest { @@ -140,6 +152,7 @@ class AsyncImagePainterTest { val (context, sketch) = getTestContextAndSketch() val request = ImageRequest(context, "http://sample.com/sample.jpeg") val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = TestLifecycle(), imageOptions = null @@ -166,12 +179,15 @@ class AsyncImagePainterTest { @Test fun testEqualsAndHashCode() { + val context = getTestContext() val asyncImageState1 = AsyncImageState( + context = context, inspectionMode = false, lifecycle = TestLifecycle(), imageOptions = null ) val asyncImageState2 = AsyncImageState( + context = context, inspectionMode = false, lifecycle = TestLifecycle(), imageOptions = null @@ -191,7 +207,9 @@ class AsyncImagePainterTest { @Test fun testToString() { + val context = getTestContext() val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = TestLifecycle(), imageOptions = null @@ -202,4 +220,312 @@ class AsyncImagePainterTest { actual = element.toString() ) } + + @Test + fun testImageNoBounded() { + val (_, sketch) = getTestContextAndSketch() + + // has bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize() + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + Image( + painter = rememberAsyncImagePainter( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state + ), + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + /* + * AsyncImagePainter requires the drawing size to load the image as the request size when drawing, + * but the image here is 0 when width or height is 0, and it will not go to the drawing stage, so the request will never be executed. + */ +// // width is not bounded +// runComposeUiTest { +// var stateHolder: AsyncImageState? = null +// var windowContainerSizeHolder: IntSize? = null +// setContent { +// windowContainerSizeHolder = windowContainerSize() +// LifecycleContainer { +// Box( +// modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) +// ) { +// val state = rememberAsyncImageState().apply { +// stateHolder = this +// } +// Image( +// painter = rememberAsyncImagePainter( +// uri = ResourceImages.jpeg.uri, +// sketch = sketch, +// state = state +// ), +// contentDescription = "test image", +// modifier = Modifier.fillMaxSize() +// ) +// } +// } +// } +// waitUntil(timeoutMillis = 5000) { +// stateHolder?.result is ImageResult.Success +// } +// assertTrue(actual = stateHolder?.result is ImageResult.Success) +// assertEquals( +// expected = IntSize(0, windowContainerSizeHolder!!.height), +// actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() +// ) +// } +// +// // height is not bounded +// runComposeUiTest { +// var stateHolder: AsyncImageState? = null +// var windowContainerSizeHolder: IntSize? = null +// setContent { +// windowContainerSizeHolder = windowContainerSize() +// LifecycleContainer { +// Box( +// modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) +// ) { +// val state = rememberAsyncImageState().apply { +// stateHolder = this +// } +// Image( +// painter = rememberAsyncImagePainter( +// uri = ResourceImages.jpeg.uri, +// sketch = sketch, +// state = state +// ), +// contentDescription = "test image", +// modifier = Modifier.fillMaxSize() +// ) +// } +// } +// } +// waitUntil(timeoutMillis = 5000) { +// stateHolder?.result is ImageResult.Success +// } +// assertTrue(actual = stateHolder?.result is ImageResult.Success) +// assertEquals( +// expected = IntSize(windowContainerSizeHolder!!.width, 0), +// actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() +// ) +// } +// +// // width height is not bounded +// runComposeUiTest { +// var stateHolder: AsyncImageState? = null +// var windowContainerSizeHolder: IntSize? = null +// setContent { +// windowContainerSizeHolder = windowContainerSize() +// LifecycleContainer { +// Box( +// modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()).verticalScroll( +// rememberScrollState() +// ) +// ) { +// val state = rememberAsyncImageState().apply { +// stateHolder = this +// } +// Image( +// painter = rememberAsyncImagePainter( +// uri = ResourceImages.jpeg.uri, +// sketch = sketch, +// state = state +// ), +// contentDescription = "test image", +// modifier = Modifier.fillMaxSize() +// ) +// } +// } +// } +// waitUntil(timeoutMillis = 5000) { +// stateHolder?.result is ImageResult.Success +// } +// assertTrue(actual = stateHolder?.result is ImageResult.Success) +// assertEquals( +// expected = IntSize(0, 0), +// actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() +// ) +// } + } + + @Test + fun testImageNoBounded2() { + val (_, sketch) = getTestContextAndSketch() + + // has bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize() + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + Image( + painter = rememberAsyncImagePainter( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state + ), + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + /* + * AsyncImagePainter requires the drawing size to load the image as the request size when drawing, + * but the image here is 0 when width or height is 0, and it will not go to the drawing stage, so the request will never be executed. + */ +// // width is not bounded +// runComposeUiTest { +// var stateHolder: AsyncImageState? = null +// var windowContainerSizeHolder: IntSize? = null +// setContent { +// windowContainerSizeHolder = windowContainerSize() +// LifecycleContainer { +// Box( +// modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) +// ) { +// val state = rememberAsyncImageState().apply { +// stateHolder = this +// } +// Image( +// painter = rememberAsyncImagePainter( +// uri = ResourceImages.jpeg.uri, +// sketch = sketch, +// state = state +// ), +// contentDescription = "test image", +// modifier = Modifier.wrapContentSize() +// ) +// } +// } +// } +// waitUntil(timeoutMillis = 5000) { +// stateHolder?.result is ImageResult.Success +// } +// assertTrue(actual = stateHolder?.result is ImageResult.Success) +// assertEquals( +// expected = IntSize(0, 0), +// actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() +// ) +// } +// +// // height is not bounded +// runComposeUiTest { +// var stateHolder: AsyncImageState? = null +// var windowContainerSizeHolder: IntSize? = null +// setContent { +// windowContainerSizeHolder = windowContainerSize() +// LifecycleContainer { +// Box( +// modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) +// ) { +// val state = rememberAsyncImageState().apply { +// stateHolder = this +// } +// Image( +// painter = rememberAsyncImagePainter( +// uri = ResourceImages.jpeg.uri, +// sketch = sketch, +// state = state +// ), +// contentDescription = "test image", +// modifier = Modifier.wrapContentSize() +// ) +// } +// } +// } +// waitUntil(timeoutMillis = 5000) { +// stateHolder?.result is ImageResult.Success +// } +// assertTrue(actual = stateHolder?.result is ImageResult.Success) +// assertEquals( +// expected = IntSize(0, 0), +// actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() +// ) +// } +// +// // width height is not bounded +// runComposeUiTest { +// var stateHolder: AsyncImageState? = null +// var windowContainerSizeHolder: IntSize? = null +// setContent { +// windowContainerSizeHolder = windowContainerSize() +// LifecycleContainer { +// Box( +// modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()).verticalScroll( +// rememberScrollState() +// ) +// ) { +// val state = rememberAsyncImageState().apply { +// stateHolder = this +// } +// Image( +// painter = rememberAsyncImagePainter( +// uri = ResourceImages.jpeg.uri, +// sketch = sketch, +// state = state +// ), +// contentDescription = "test image", +// modifier = Modifier.wrapContentSize() +// ) +// } +// } +// } +// waitUntil(timeoutMillis = 5000) { +// stateHolder?.result is ImageResult.Success +// } +// assertTrue(actual = stateHolder?.result is ImageResult.Success) +// assertEquals( +// expected = IntSize(0, 0), +// actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() +// ) +// } + } } \ No newline at end of file diff --git a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageStateTest.kt b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageStateTest.kt index bd850fbc15..631bd499ac 100644 --- a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageStateTest.kt +++ b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageStateTest.kt @@ -53,13 +53,17 @@ import com.github.panpf.sketch.test.utils.block import com.github.panpf.sketch.test.utils.createBitmapImage import com.github.panpf.sketch.test.utils.current import com.github.panpf.sketch.test.utils.fakeSuccessImageResult +import com.github.panpf.sketch.test.utils.getTestContext import com.github.panpf.sketch.test.utils.runInNewSketchWithUse import com.github.panpf.sketch.test.utils.similarity import com.github.panpf.sketch.test.utils.toPreviewBitmap import com.github.panpf.sketch.util.Size import com.github.panpf.sketch.util.div +import com.github.panpf.sketch.util.screenSize import com.github.panpf.sketch.util.toHexString +import com.github.panpf.sketch.util.toIntSize import com.github.panpf.sketch.util.toSize +import com.github.panpf.sketch.util.windowContainerSize import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.test.runTest import kotlin.test.Test @@ -80,6 +84,10 @@ class AsyncImageStateTest { rememberAsyncImageState().apply { assertEquals(expected = testLifecycle, actual = lifecycle) assertEquals(expected = false, actual = inspectionMode) + assertEquals( + expected = windowContainerSize(), + actual = target.windowContainerSize + ) assertEquals(expected = null, actual = imageOptions) } @@ -87,6 +95,10 @@ class AsyncImageStateTest { rememberAsyncImageState().apply { assertEquals(expected = GlobalLifecycle, actual = lifecycle) assertEquals(expected = true, actual = inspectionMode) + assertEquals( + expected = windowContainerSize(), + actual = target.windowContainerSize + ) assertEquals(expected = null, actual = imageOptions) } } @@ -94,6 +106,10 @@ class AsyncImageStateTest { rememberAsyncImageState(ImageOptions { size(101, 202) }).apply { assertEquals(expected = testLifecycle, actual = lifecycle) assertEquals(expected = false, actual = inspectionMode) + assertEquals( + expected = windowContainerSize(), + actual = target.windowContainerSize + ) assertEquals( expected = ImageOptions { size(101, 202) }, actual = imageOptions @@ -105,6 +121,10 @@ class AsyncImageStateTest { }.apply { assertEquals(expected = testLifecycle, actual = lifecycle) assertEquals(expected = false, actual = inspectionMode) + assertEquals( + expected = windowContainerSize(), + actual = target.windowContainerSize + ) assertEquals( expected = ImageOptions { size(202, 101) }, actual = imageOptions @@ -117,19 +137,36 @@ class AsyncImageStateTest { @Test fun testConstructor() { - AsyncImageState(true, GlobalLifecycle, ImageOptions()) - - AsyncImageState( - inspectionMode = false, - lifecycle = GlobalLifecycle, - imageOptions = ImageOptions() - ) + val context = getTestContext() + runComposeUiTest { + setContent { + AsyncImageState( + context = context, + inspectionMode = false, + lifecycle = GlobalLifecycle, + imageOptions = ImageOptions() + ).apply { + assertEquals(expected = GlobalLifecycle, actual = lifecycle) + assertEquals(expected = false, actual = inspectionMode) + assertEquals( + expected = context.screenSize().toIntSize(), + actual = target.windowContainerSize + ) + assertEquals( + expected = ImageOptions(), + actual = imageOptions + ) + } + } + } } @Test fun testLifecycle() { + val context = getTestContext() val lifecycle = GlobalLifecycle val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = lifecycle, imageOptions = ImageOptions() @@ -142,7 +179,9 @@ class AsyncImageStateTest { @Test fun testImageOptions() { + val context = getTestContext() val asyncImageState1 = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = null @@ -153,6 +192,7 @@ class AsyncImageStateTest { ) val asyncImageState2 = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions() @@ -165,7 +205,9 @@ class AsyncImageStateTest { @Test fun testContentScale() { + val context = getTestContext() val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions() @@ -301,6 +343,7 @@ class AsyncImageStateTest { val (context, sketch) = getTestContextAndSketch() val request = ImageRequest(context, "http://sample.com/sample.jpeg") val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions() @@ -340,7 +383,10 @@ class AsyncImageStateTest { @Test fun testSize() { + val context = getTestContext() + val windowContainerSize = context.screenSize() val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions() @@ -354,43 +400,43 @@ class AsyncImageStateTest { asyncImageState.setSize(IntSize(0, 1000)) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, 1000), actual = asyncImageState.size ) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, 1000), actual = asyncImageState.target.sizeState.value ) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, 1000), actual = asyncImageState.target.getSizeResolver().sizeState.value ) asyncImageState.setSize(IntSize(1000, 0)) assertEquals( - expected = null, + expected = IntSize(1000, windowContainerSize.height), actual = asyncImageState.size ) assertEquals( - expected = null, + expected = IntSize(1000, windowContainerSize.height), actual = asyncImageState.target.sizeState.value ) assertEquals( - expected = null, + expected = IntSize(1000, windowContainerSize.height), actual = asyncImageState.target.getSizeResolver().sizeState.value ) asyncImageState.setSize(IntSize(0, 0)) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, windowContainerSize.height), actual = asyncImageState.size ) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, windowContainerSize.height), actual = asyncImageState.target.sizeState.value ) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, windowContainerSize.height), actual = asyncImageState.target.getSizeResolver().sizeState.value ) @@ -405,7 +451,9 @@ class AsyncImageStateTest { @Test fun testRemembered() { + val context = getTestContext() val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions() @@ -454,6 +502,7 @@ class AsyncImageStateTest { val (context, sketch) = getTestContextAndSketch() var throwable: Throwable? val asyncImageState = AsyncImageState( + context = context, inspectionMode = true, lifecycle = GlobalLifecycle, imageOptions = ImageOptions() @@ -587,6 +636,7 @@ class AsyncImageStateTest { } val (context, sketch) = getTestContextAndSketch() val asyncImageState = AsyncImageState( + context = context, inspectionMode = true, lifecycle = GlobalLifecycle, imageOptions = ImageOptions() @@ -640,6 +690,7 @@ class AsyncImageStateTest { val resourceImage = ResourceImages.jpeg val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions { @@ -790,6 +841,7 @@ class AsyncImageStateTest { val resourceImage = ResourceImages.jpeg val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions { @@ -852,6 +904,7 @@ class AsyncImageStateTest { // success runComposeUiTest { val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions { @@ -989,6 +1042,7 @@ class AsyncImageStateTest { // error runComposeUiTest { val asyncImageState = AsyncImageState( + context = context, inspectionMode = false, lifecycle = GlobalLifecycle, imageOptions = ImageOptions { @@ -1118,12 +1172,11 @@ class AsyncImageStateTest { @Test fun testEqualsAndHashCode() = runTest { + val context = getTestContext() val lifecycle = GlobalLifecycle val imageOptions = ImageOptions() - val element1 = - AsyncImageState(true, lifecycle, imageOptions) - val element11 = - AsyncImageState(true, lifecycle, imageOptions) + val element1 = AsyncImageState(context, true, lifecycle, imageOptions) + val element11 = AsyncImageState(context, true, lifecycle, imageOptions) assertNotEquals(illegal = element1, actual = element11) assertNotEquals(illegal = element1, actual = null as Any?) @@ -1134,8 +1187,8 @@ class AsyncImageStateTest { @Test fun testToString() = runTest { - val asyncImageState = - AsyncImageState(true, GlobalLifecycle, ImageOptions()) + val context = getTestContext() + val asyncImageState = AsyncImageState(context, true, GlobalLifecycle, ImageOptions()) assertEquals( expected = "AsyncImageState@${asyncImageState.toHexString()}", actual = asyncImageState.toString() diff --git a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageTest.kt b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageTest.kt index c70ecc1cfa..0b3d92184d 100644 --- a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageTest.kt +++ b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/AsyncImageTest.kt @@ -1,6 +1,11 @@ package com.github.panpf.sketch.compose.core.common.test +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter @@ -182,4 +187,304 @@ class AsyncImageTest { ) } } + + @Test + fun testAsyncImageNoBounded() { + val (_, sketch) = getTestContextAndSketch() + + // has bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize() + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + .verticalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + } + + @Test + fun testAsyncImageNoBounded2() { + val (_, sketch) = getTestContextAndSketch() + + // has bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize() + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + .verticalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + AsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + } } \ No newline at end of file diff --git a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/SubcomposeAsyncImageTest.kt b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/SubcomposeAsyncImageTest.kt index 0a331797a5..d89af357ec 100644 --- a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/SubcomposeAsyncImageTest.kt +++ b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/SubcomposeAsyncImageTest.kt @@ -1,5 +1,11 @@ package com.github.panpf.sketch.compose.core.common.test +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter @@ -7,13 +13,20 @@ import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.runComposeUiTest +import androidx.compose.ui.unit.IntSize +import com.github.panpf.sketch.AsyncImageState import com.github.panpf.sketch.SubcomposeAsyncImage import com.github.panpf.sketch.images.ResourceImages import com.github.panpf.sketch.rememberAsyncImageState import com.github.panpf.sketch.request.ComposableImageRequest +import com.github.panpf.sketch.request.ImageResult import com.github.panpf.sketch.test.singleton.getTestContextAndSketch import com.github.panpf.sketch.test.utils.LifecycleContainer +import com.github.panpf.sketch.util.toIntSize +import com.github.panpf.sketch.util.windowContainerSize import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalTestApi::class) class SubcomposeAsyncImageTest { @@ -201,4 +214,306 @@ class SubcomposeAsyncImageTest { // TODO test: Screenshot test or draw to Bitmap, then compare Bitmap } } + + @Test + fun testAsyncImageNoBounded() { + val (_, sketch) = getTestContextAndSketch() + + // has bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize() + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + .verticalScroll( + rememberScrollState() + ) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.fillMaxSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + } + + @Test + fun testAsyncImageNoBounded2() { + val (_, sketch) = getTestContextAndSketch() + + // has bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize() + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + + // width height is not bounded + runComposeUiTest { + var stateHolder: AsyncImageState? = null + var windowContainerSizeHolder: IntSize? = null + setContent { + windowContainerSizeHolder = windowContainerSize() + LifecycleContainer { + Box( + modifier = Modifier.fillMaxSize().horizontalScroll(rememberScrollState()) + .verticalScroll(rememberScrollState()) + ) { + val state = rememberAsyncImageState().apply { + stateHolder = this + } + SubcomposeAsyncImage( + uri = ResourceImages.jpeg.uri, + sketch = sketch, + state = state, + contentDescription = "test image", + modifier = Modifier.wrapContentSize() + ) + } + } + } + waitUntil(timeoutMillis = 5000) { + stateHolder?.result is ImageResult.Success + } + assertTrue(actual = stateHolder?.result is ImageResult.Success) + assertEquals( + expected = IntSize( + windowContainerSizeHolder!!.width, + windowContainerSizeHolder!!.height + ), + actual = (stateHolder?.result as ImageResult.Success).resize.size.toIntSize() + ) + } + } } \ No newline at end of file diff --git a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/internal/UtilsTest.kt b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/internal/UtilsTest.kt new file mode 100644 index 0000000000..75920e0a66 --- /dev/null +++ b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/internal/UtilsTest.kt @@ -0,0 +1,76 @@ +package com.github.panpf.sketch.compose.core.common.test.internal + +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.runComposeUiTest +import com.github.panpf.sketch.LocalPlatformContext +import com.github.panpf.sketch.internal.requestOf +import com.github.panpf.sketch.request.ImageRequest +import kotlin.test.Test +import kotlin.test.assertEquals + +class UtilsTest { + + @OptIn(ExperimentalTestApi::class) + @Test + fun testRequestOf() { + runComposeUiTest { + val requests1 = mutableListOf() + val requests2 = mutableListOf() + setContent { + var index by remember { mutableIntStateOf(0) } + val context = LocalPlatformContext.current + val request1 = requestOf(context, "testUri") + requests1.add(request1) + val request2 = requestOf(context, "testUri${index}") + requests2.add(request2) + LaunchedEffect(index) { + if (index < 9) { + index++ + } + } + } + waitForIdle() + assertEquals(expected = 10, actual = requests1.size) + assertEquals(expected = 10, actual = requests2.size) + + requests1.takeLast(9).all { it == requests1.first() } + requests2.takeLast(9).all { it != requests2.first() } + requests1.takeLast(9).all { it === requests1.first() } + requests2.takeLast(9).all { it !== requests2.first() } + } + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun testRequestOf2() { + runComposeUiTest { + val requests1 = mutableListOf() + val requests2 = mutableListOf() + setContent { + var index by remember { mutableIntStateOf(0) } + val request1 = requestOf("testUri") + requests1.add(request1) + val request2 = requestOf("testUri${index}") + requests2.add(request2) + LaunchedEffect(index) { + if (index < 9) { + index++ + } + } + } + waitForIdle() + assertEquals(expected = 10, actual = requests1.size) + assertEquals(expected = 10, actual = requests2.size) + + requests1.takeLast(9).all { it == requests1.first() } + requests2.takeLast(9).all { it != requests2.first() } + requests1.takeLast(9).all { it === requests1.first() } + requests2.takeLast(9).all { it !== requests2.first() } + } + } +} \ No newline at end of file diff --git a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/target/AsyncImageTargetTest.kt b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/target/AsyncImageTargetTest.kt index 07b89ff79d..4477b1b657 100644 --- a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/target/AsyncImageTargetTest.kt +++ b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/target/AsyncImageTargetTest.kt @@ -22,6 +22,9 @@ import com.github.panpf.sketch.test.utils.asOrThrow import com.github.panpf.sketch.test.utils.createBitmapImage import com.github.panpf.sketch.test.utils.fakeErrorImageResult import com.github.panpf.sketch.test.utils.fakeSuccessImageResult +import com.github.panpf.sketch.test.utils.getTestContext +import com.github.panpf.sketch.util.screenSize +import com.github.panpf.sketch.util.toIntSize import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -31,17 +34,78 @@ class AsyncImageTargetTest { @Test fun testConstructor() { + val context = getTestContext() AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) } + @Test + fun testWindowContainerSize() { + val context = getTestContext() + AsyncImageTarget( + context = context, + lifecycle = TestLifecycle(), + imageOptions = ImageOptions(), + ).apply { + assertEquals(expected = context.screenSize().toIntSize(), actual = windowContainerSize) + } + + AsyncImageTarget( + context = context, + lifecycle = TestLifecycle(), + imageOptions = ImageOptions(), + ).apply { + windowContainerSize = IntSize(0, 1000) + assertEquals(expected = IntSize(100, 1000), actual = windowContainerSize) + } + + AsyncImageTarget( + context = context, + lifecycle = TestLifecycle(), + imageOptions = ImageOptions(), + ).apply { + windowContainerSize = IntSize(-1, 1000) + assertEquals(expected = IntSize(100, 1000), actual = windowContainerSize) + } + + AsyncImageTarget( + context = context, + lifecycle = TestLifecycle(), + imageOptions = ImageOptions(), + ).apply { + windowContainerSize = IntSize(1000, 0) + assertEquals(expected = IntSize(1000, 100), actual = windowContainerSize) + } + + AsyncImageTarget( + context = context, + lifecycle = TestLifecycle(), + imageOptions = ImageOptions(), + ).apply { + windowContainerSize = IntSize(1000, -1) + assertEquals(expected = IntSize(1000, 100), actual = windowContainerSize) + } + + AsyncImageTarget( + context = context, + lifecycle = TestLifecycle(), + imageOptions = ImageOptions(), + ).apply { + windowContainerSize = IntSize(3000, 2000) + }.apply { + assertEquals(expected = IntSize(3000, 2000), actual = windowContainerSize) + } + } + @Test fun testPainterAndRemembered() { val (context, sketch) = getTestContextAndSketch() val request = ImageRequest(context, "http://sample.com/sample.jpeg") val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -101,6 +165,7 @@ class AsyncImageTargetTest { val (context, sketch) = getTestContextAndSketch() val request = ImageRequest(context, "http://sample.com/sample.jpeg") val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -126,7 +191,9 @@ class AsyncImageTargetTest { @Test fun testContentScale() { + val context = getTestContext() val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -176,6 +243,7 @@ class AsyncImageTargetTest { val (context, sketch) = getTestContextAndSketch() val request = ImageRequest(context, "http://sample.com/sample.jpeg") val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -211,7 +279,10 @@ class AsyncImageTargetTest { @Test fun testSize() { + val context = getTestContext() + val windowContainerSize = context.screenSize() val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -220,31 +291,31 @@ class AsyncImageTargetTest { target.setSize(IntSize(0, 1000)) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, 1000), actual = target.sizeState.value ) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, 1000), actual = target.getSizeResolver().sizeState.value ) target.setSize(IntSize(1000, 0)) assertEquals( - expected = null, + expected = IntSize(1000, windowContainerSize.height), actual = target.sizeState.value ) assertEquals( - expected = null, + expected = IntSize(1000, windowContainerSize.height), actual = target.getSizeResolver().sizeState.value ) target.setSize(IntSize(0, 0)) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, windowContainerSize.height), actual = target.sizeState.value ) assertEquals( - expected = null, + expected = IntSize(windowContainerSize.width, windowContainerSize.height), actual = target.getSizeResolver().sizeState.value ) @@ -258,7 +329,9 @@ class AsyncImageTargetTest { @Test fun testRequestManagerAndRemembered() { + val context = getTestContext() val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -283,7 +356,9 @@ class AsyncImageTargetTest { @Test fun testListenerAndProgressListener() { + val context = getTestContext() val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -293,8 +368,10 @@ class AsyncImageTargetTest { @Test fun testLifecycleResolver() { + val context = getTestContext() val lifecycle = TestLifecycle() val target = AsyncImageTarget( + context = context, lifecycle = lifecycle, imageOptions = ImageOptions(), ) @@ -306,8 +383,10 @@ class AsyncImageTargetTest { @Test fun testSizeResolver() { + val context = getTestContext() val lifecycle = TestLifecycle() val target = AsyncImageTarget( + context = context, lifecycle = lifecycle, imageOptions = ImageOptions(), ) @@ -319,8 +398,10 @@ class AsyncImageTargetTest { @Test fun testScaleDecider() { + val context = getTestContext() val lifecycle = TestLifecycle() val target = AsyncImageTarget( + context = context, lifecycle = lifecycle, imageOptions = ImageOptions(), ) @@ -350,7 +431,9 @@ class AsyncImageTargetTest { @Test fun testImageOptions() { + val context = getTestContext() val target1 = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = null, ) @@ -360,6 +443,7 @@ class AsyncImageTargetTest { ) val target2 = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -374,6 +458,7 @@ class AsyncImageTargetTest { val (context, sketch) = getTestContextAndSketch() val request = ImageRequest(context, "http://sample.com/sample.jpeg") val target = AsyncImageTarget( + context = context, lifecycle = TestLifecycle(), imageOptions = ImageOptions(), ) @@ -413,21 +498,26 @@ class AsyncImageTargetTest { @Test fun testEqualsAndHashCode() { + val context = getTestContext() val lifecycle1 = TestLifecycle() val lifecycle2 = TestLifecycle() val element1 = AsyncImageTarget( + context = context, lifecycle = lifecycle1, imageOptions = ImageOptions(), ) val element11 = AsyncImageTarget( + context = context, lifecycle = lifecycle1, imageOptions = ImageOptions(), ) val element2 = AsyncImageTarget( + context = context, lifecycle = lifecycle2, imageOptions = ImageOptions(), ) val element3 = AsyncImageTarget( + context = context, lifecycle = lifecycle1, imageOptions = ImageOptions { size(101, 202) }, ) @@ -447,14 +537,16 @@ class AsyncImageTargetTest { @Test fun testToString() { + val context = getTestContext() val lifecycle = TestLifecycle() val options = ImageOptions() val target = AsyncImageTarget( + context = context, lifecycle = lifecycle, imageOptions = options, ) assertEquals( - expected = "AsyncImageTarget(lifecycle=$lifecycle, options=$options)", + expected = "AsyncImageTarget(context=$context, lifecycle=$lifecycle, options=$options)", actual = target.toString() ) } diff --git a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/util/ComposeCoreUtilsTest.kt b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/util/ComposeCoreUtilsTest.kt index 3e770a60ce..a0759e47dd 100644 --- a/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/util/ComposeCoreUtilsTest.kt +++ b/sketch-compose-core/src/commonTest/kotlin/com/github/panpf/sketch/compose/core/common/test/util/ComposeCoreUtilsTest.kt @@ -26,6 +26,7 @@ import com.github.panpf.sketch.util.toHexString import com.github.panpf.sketch.util.toIntSize import com.github.panpf.sketch.util.toIntSizeOrNull import com.github.panpf.sketch.util.toLogString +import com.github.panpf.sketch.util.toRequestSize import com.github.panpf.sketch.util.toScale import com.github.panpf.sketch.util.toSize import com.github.panpf.sketch.util.toSketchSize @@ -204,6 +205,38 @@ class ComposeCoreUtilsTest { ) } + @Test + fun testConstraintsToRequestSize() { + assertEquals( + expected = IntSize(0, 0), + actual = Constraints(maxWidth = 0, maxHeight = 0).toRequestSize() + ) + assertEquals( + expected = IntSize(1, 0), + actual = Constraints(maxWidth = 1, maxHeight = 0).toRequestSize() + ) + assertEquals( + expected = IntSize(0, 1), + actual = Constraints(maxWidth = 0, maxHeight = 1).toRequestSize() + ) + assertEquals( + expected = IntSize(1, 0), + actual = Constraints(maxWidth = 1, maxHeight = Constraints.Infinity).toRequestSize() + ) + assertEquals( + expected = IntSize(0, 1), + actual = Constraints(maxWidth = Constraints.Infinity, maxHeight = 1).toRequestSize() + ) + assertEquals( + expected = IntSize(1, 1), + actual = Constraints(maxWidth = 1, maxHeight = 1).toRequestSize() + ) + assertEquals( + expected = IntSize(2, 2), + actual = Constraints(maxWidth = 2, maxHeight = 2).toRequestSize() + ) + } + @Test fun testPainterFindLeafPainter() { val painter1 = ColorPainter(Color.White) diff --git a/sketch-compose-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/compose/core/nonandroid/test/painter/AnimatedImagePainterTest.kt b/sketch-compose-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/compose/core/nonandroid/test/painter/AnimatedImagePainterTest.kt index 35b6d50259..50d8061d5a 100644 --- a/sketch-compose-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/compose/core/nonandroid/test/painter/AnimatedImagePainterTest.kt +++ b/sketch-compose-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/compose/core/nonandroid/test/painter/AnimatedImagePainterTest.kt @@ -15,7 +15,6 @@ import com.github.panpf.sketch.AnimatedImage import com.github.panpf.sketch.images.ResourceImages import com.github.panpf.sketch.images.toDataSource import com.github.panpf.sketch.painter.AnimatedImagePainter -import com.github.panpf.sketch.test.utils.block import com.github.panpf.sketch.test.utils.getTestContext import kotlinx.coroutines.isActive import okio.buffer @@ -148,10 +147,11 @@ class AnimatedImagePainterTest { remember { animatedImagePainter } Image(painter = animatedImagePainter, contentDescription = "") } - waitForIdle() - block(1000) - assertTrue(animatedTransformationCalled) + waitUntil { + animatedTransformationCalled + } } + assertTrue(animatedTransformationCalled) } @Test diff --git a/sketch-compose/src/commonMain/kotlin/com/github/panpf/sketch/SingletonAsyncImage.kt b/sketch-compose/src/commonMain/kotlin/com/github/panpf/sketch/SingletonAsyncImage.kt index ab58de97e6..e7dec63f19 100644 --- a/sketch-compose/src/commonMain/kotlin/com/github/panpf/sketch/SingletonAsyncImage.kt +++ b/sketch-compose/src/commonMain/kotlin/com/github/panpf/sketch/SingletonAsyncImage.kt @@ -46,6 +46,8 @@ import com.github.panpf.sketch.request.ImageRequest * rendered onscreen. * @param filterQuality Sampling algorithm applied to a bitmap when it is scaled and drawn into the * destination. + * @param clipToBounds Whether to clip the content to the bounds of this layout. Defaults to true. + * @param keepContentNoneStartOnDraw Whether to always draw the content as none on the left on drawing, even if LayoutDirection is Rtl. * * @see com.github.panpf.sketch.compose.common.test.SingletonAsyncImageTest.testAsyncImage1 */ @@ -62,6 +64,7 @@ fun AsyncImage( colorFilter: ColorFilter? = null, filterQuality: FilterQuality = DefaultFilterQuality, clipToBounds: Boolean = true, + keepContentNoneStartOnDraw: Boolean = false, ) = AsyncImage( uri = uri, contentDescription = contentDescription, @@ -74,6 +77,7 @@ fun AsyncImage( colorFilter = colorFilter, filterQuality = filterQuality, clipToBounds = clipToBounds, + keepContentNoneStartOnDraw = keepContentNoneStartOnDraw, ) /** @@ -95,6 +99,8 @@ fun AsyncImage( * rendered onscreen. * @param filterQuality Sampling algorithm applied to a bitmap when it is scaled and drawn into the * destination. + * @param clipToBounds Whether to clip the content to the bounds of this layout. Defaults to true. + * @param keepContentNoneStartOnDraw Whether to always draw the content as none on the left on drawing, even if LayoutDirection is Rtl. * * @see com.github.panpf.sketch.compose.common.test.SingletonAsyncImageTest.testAsyncImage2 */ @@ -111,6 +117,7 @@ fun AsyncImage( colorFilter: ColorFilter? = null, filterQuality: FilterQuality = DefaultFilterQuality, clipToBounds: Boolean = true, + keepContentNoneStartOnDraw: Boolean = false, ) = AsyncImage( request = request, contentDescription = contentDescription, @@ -123,4 +130,5 @@ fun AsyncImage( colorFilter = colorFilter, filterQuality = filterQuality, clipToBounds = clipToBounds, + keepContentNoneStartOnDraw = keepContentNoneStartOnDraw, ) \ No newline at end of file diff --git a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DecodesAndroidTest.kt b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DecodesAndroidTest.kt index 320e9b6964..45b6acfb3e 100644 --- a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DecodesAndroidTest.kt +++ b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DecodesAndroidTest.kt @@ -29,6 +29,7 @@ import com.github.panpf.sketch.decode.DecodeResult import com.github.panpf.sketch.decode.ImageInfo import com.github.panpf.sketch.decode.ImageInvalidException import com.github.panpf.sketch.decode.internal.ExifOrientationHelper +import com.github.panpf.sketch.decode.internal.ImageFormat import com.github.panpf.sketch.decode.internal.calculateSampleSize import com.github.panpf.sketch.decode.internal.calculateSampleSizeForRegion import com.github.panpf.sketch.decode.internal.calculateSampledBitmapSize @@ -1817,11 +1818,30 @@ class DecodesAndroidTest { val result = runCatching { dataSource.decodeRegion(srcRect = (imageFile.size / 2f).toRect()) } - assertEquals( - expected = supportBitmapRegionDecoder(imageFile.mimeType, imageFile.animated), - actual = result.isSuccess, - message = "imageFile=${imageFile.uri}, failure: '${result.exceptionOrNull()}'" - ) + if (ImageFormat.HEIC.mimeType == imageFile.mimeType || ImageFormat.HEIF.mimeType == imageFile.mimeType) { + // Images in heif format will report an error on the 33 simulator, but the real machine is OK. + val expected = + supportBitmapRegionDecoder(imageFile.mimeType, imageFile.animated) == true + if (expected) { + assertEquals( + expected = true, + actual = result.isSuccess || result.isFailure, + message = "imageFile=${imageFile.uri}, failure: '${result.exceptionOrNull()}'" + ) + } else { + assertEquals( + expected = false, + actual = result.isSuccess, + message = "imageFile=${imageFile.uri}, failure: '${result.exceptionOrNull()}'" + ) + } + } else { + assertEquals( + expected = supportBitmapRegionDecoder(imageFile.mimeType, imageFile.animated), + actual = result.isSuccess, + message = "imageFile=${imageFile.uri}, failure: '${result.exceptionOrNull()}'" + ) + } } assertEquals( diff --git a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DrawableDecoderTest.kt b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DrawableDecoderTest.kt index 71fc02821d..48671a0e43 100644 --- a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DrawableDecoderTest.kt +++ b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/decode/internal/DrawableDecoderTest.kt @@ -49,7 +49,7 @@ import com.github.panpf.sketch.test.utils.decode import com.github.panpf.sketch.test.utils.getBitmapOrThrow import com.github.panpf.sketch.test.utils.toRequestContext import com.github.panpf.sketch.util.Size -import com.github.panpf.sketch.util.computeScaleMultiplierWithOneSide +import com.github.panpf.sketch.util.calculateScaleMultiplierWithOneSide import com.github.panpf.tools4a.dimen.ktx.dp2px import kotlinx.coroutines.test.runTest import org.junit.runner.RunWith @@ -157,7 +157,7 @@ class DrawableDecoderTest { // resize: scale, EXACTLY, START_CROP val targetSize = Size(100, 100) - val targetScale = computeScaleMultiplierWithOneSide( + val targetScale = calculateScaleMultiplierWithOneSide( sourceSize = imageSize, targetSize = targetSize ) diff --git a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/drawable/IconDrawableTest.kt b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/drawable/IconDrawableTest.kt index 9042819acf..66f7722b73 100644 --- a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/drawable/IconDrawableTest.kt +++ b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/drawable/IconDrawableTest.kt @@ -43,11 +43,13 @@ import com.github.panpf.sketch.test.utils.getTestContext import com.github.panpf.sketch.test.utils.intrinsicSize import com.github.panpf.sketch.util.Size import com.github.panpf.sketch.util.asOrThrow -import com.github.panpf.sketch.util.calculateInsideBounds import com.github.panpf.sketch.util.getDrawableCompat import com.github.panpf.sketch.util.toSketchRect import com.github.panpf.tools4a.dimen.ktx.dp2px import org.junit.runner.RunWith +import kotlin.math.ceil +import kotlin.math.floor +import kotlin.math.min import kotlin.math.roundToInt import kotlin.test.Test import kotlin.test.assertEquals @@ -876,4 +878,47 @@ class IconDrawableTest { ).toString() ) } + + /** + * Calculate the bounds of the Drawable to inside the container. + * + * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testCalculateInsideBounds + */ + private fun calculateInsideBounds( + contentSize: Size, + containerBounds: com.github.panpf.sketch.util.Rect + ): com.github.panpf.sketch.util.Rect { + val containerWidth = containerBounds.width() + val containerHeight = containerBounds.height() + if (contentSize.width <= containerWidth && contentSize.height <= containerHeight) { + // center + val left = containerBounds.left + (containerWidth - contentSize.width) / 2f + val top = containerBounds.top + (containerHeight - contentSize.height) / 2f + val right = left + contentSize.width + val bottom = top + contentSize.height + return com.github.panpf.sketch.util.Rect( + /* left = */ floor(left).toInt(), + /* top = */ floor(top).toInt(), + /* right = */ ceil(right).toInt(), + /* bottom = */ ceil(bottom).toInt() + ) + } else { + // fit + val widthScale = containerWidth.toFloat() / contentSize.width + val heightScale = containerHeight.toFloat() / contentSize.height + val scale = min(widthScale, heightScale) + val scaledWidth = contentSize.width * scale + val scaledHeight = contentSize.height * scale + val left = containerBounds.left + (containerWidth - scaledWidth) / 2f + val top = containerBounds.top + (containerHeight - scaledHeight) / 2f + val right = left + scaledWidth + val bottom = top + scaledHeight + return com.github.panpf.sketch.util.Rect( + /* left = */ floor(left).toInt(), + /* top = */ floor(top).toInt(), + /* right = */ ceil(right).toInt(), + /* bottom = */ ceil(bottom).toInt() + ) + } + } } \ No newline at end of file diff --git a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt index 2c4b2f7c64..ab9b1a3048 100644 --- a/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt +++ b/sketch-core/src/androidInstrumentedTest/kotlin/com/github/panpf/sketch/core/android/test/util/BitmapsAndroidTest.kt @@ -663,7 +663,7 @@ class BitmapsAndroidTest { .forEach { colorType -> val jpegBitmap = ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap val newJpegBitmap = - createBitmap(jpegBitmap.width, jpegBitmap.height, jpegBitmap.config) + createBitmap(jpegBitmap.width, jpegBitmap.height, jpegBitmap.config!!) if (jpegBitmap.produceFingerPrint() == "ffffffffffffffff") { assertTrue( actual = jpegBitmap.similarity(newJpegBitmap) == 0, @@ -690,7 +690,7 @@ class BitmapsAndroidTest { val pngBitmap = ResourceImages.png.decode(BitmapColorType(colorType)).bitmap val newPngBitmap = - createBitmap(pngBitmap.width, pngBitmap.height, pngBitmap.config) + createBitmap(pngBitmap.width, pngBitmap.height, pngBitmap.config!!) if (pngBitmap.produceFingerPrint() == "ffffffffffffffff") { assertTrue( actual = pngBitmap.similarity(newPngBitmap) == 0, @@ -865,7 +865,7 @@ class BitmapsAndroidTest { assertEquals(expected = jpegIntPixels.toList(), actual = piecedIntPexels.toList()) val newJpegBitmap = - createBitmap(jpegBitmap.width, jpegBitmap.height, jpegBitmap.config) + createBitmap(jpegBitmap.width, jpegBitmap.height, jpegBitmap.config!!) newJpegBitmap.installIntPixels(piecedIntPexels) assertTrue(actual = jpegBitmap.similarity(newJpegBitmap) == 0) } @@ -879,7 +879,7 @@ class BitmapsAndroidTest { .forEach { colorType -> val jpegBitmap = ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap val newJpegBitmap = - createBitmap(jpegBitmap.width, jpegBitmap.height, jpegBitmap.config) + createBitmap(jpegBitmap.width, jpegBitmap.height, jpegBitmap.config!!) if (jpegBitmap.produceFingerPrint() == "ffffffffffffffff") { assertTrue( actual = jpegBitmap.similarity(newJpegBitmap) == 0, @@ -906,7 +906,7 @@ class BitmapsAndroidTest { val pngBitmap = ResourceImages.png.decode(BitmapColorType(colorType)).bitmap val newPngBitmap = - createBitmap(pngBitmap.width, pngBitmap.height, pngBitmap.config) + createBitmap(pngBitmap.width, pngBitmap.height, pngBitmap.config!!) if (pngBitmap.produceFingerPrint() == "ffffffffffffffff") { assertTrue( actual = pngBitmap.similarity(newPngBitmap) == 0, diff --git a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt index 08e437761a..d14d32c12d 100644 --- a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt +++ b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/DrawableDecoder.kt @@ -28,7 +28,7 @@ import com.github.panpf.sketch.request.RequestContext import com.github.panpf.sketch.source.DataFrom.LOCAL import com.github.panpf.sketch.source.DrawableDataSource import com.github.panpf.sketch.util.Size -import com.github.panpf.sketch.util.computeScaleMultiplierWithOneSide +import com.github.panpf.sketch.util.calculateScaleMultiplierWithOneSide import com.github.panpf.sketch.util.safeToSoftware import com.github.panpf.sketch.util.toBitmap import kotlinx.atomicfu.locks.SynchronizedObject @@ -73,7 +73,7 @@ open class DrawableDecoder( val imageInfo = imageInfo val resize = requestContext.computeResize(imageInfo.size) var transformeds: List? = null - val scale: Float = computeScaleMultiplierWithOneSide( + val scale: Float = calculateScaleMultiplierWithOneSide( sourceSize = imageInfo.size, targetSize = resize.size ) diff --git a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt index 200bc3e998..a98e78a5c1 100644 --- a/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt +++ b/sketch-core/src/androidMain/kotlin/com/github/panpf/sketch/drawable/IconDrawable.kt @@ -32,12 +32,12 @@ import android.os.Build.VERSION_CODES import androidx.annotation.ColorInt import androidx.core.graphics.drawable.DrawableCompat import com.github.panpf.sketch.util.Size -import com.github.panpf.sketch.util.calculateCropBounds -import com.github.panpf.sketch.util.calculateInsideBounds +import com.github.panpf.sketch.util.calculateScaleMultiplierWithCrop +import com.github.panpf.sketch.util.calculateScaleMultiplierWithInside import com.github.panpf.sketch.util.isNotEmpty -import com.github.panpf.sketch.util.toAndroidRect import com.github.panpf.sketch.util.toLogString -import com.github.panpf.sketch.util.toSketchRect +import kotlin.math.ceil +import kotlin.math.floor /** * It consists of two parts: icon and bg. bg is scaled to fill bounds, the icon size is unchanged always centered. @@ -75,22 +75,55 @@ open class IconDrawable constructor( override fun onBoundsChange(bounds: Rect) { super.onBoundsChange(bounds) + val containerWidth = bounds.width() + val containerHeight = bounds.height() val background = background if (background != null) { val backgroundSize = Size(background.intrinsicWidth, background.intrinsicHeight) val backgroundBounds = if (backgroundSize.isNotEmpty) { - calculateCropBounds( - contentSize = backgroundSize, - containerBounds = bounds.toSketchRect() - ).toAndroidRect() + val backgroundScaleFactor = calculateScaleMultiplierWithCrop( + srcWidth = backgroundSize.width.toFloat(), + srcHeight = backgroundSize.height.toFloat(), + dstWidth = containerWidth.toFloat(), + dstHeight = containerHeight.toFloat() + ) + val scaledWidth = backgroundSize.width * backgroundScaleFactor + val scaledHeight = backgroundSize.height * backgroundScaleFactor + val left = bounds.left + (containerWidth - scaledWidth) / 2f + val top = bounds.top + (containerHeight - scaledHeight) / 2f + val right = left + scaledWidth + val bottom = top + scaledHeight + Rect( + /* left = */ floor(left).toInt(), + /* top = */ floor(top).toInt(), + /* right = */ ceil(right).toInt(), + /* bottom = */ ceil(bottom).toInt() + ) } else { bounds } background.bounds = backgroundBounds } - val iconSize = iconSize ?: Size(icon.intrinsicWidth, icon.intrinsicHeight) - val iconBounds = calculateInsideBounds(iconSize, bounds.toSketchRect()).toAndroidRect() + val realIconSize = iconSize ?: Size(icon.intrinsicWidth, icon.intrinsicHeight) + val backgroundScaleFactor = calculateScaleMultiplierWithInside( + srcWidth = realIconSize.width.toFloat(), + srcHeight = realIconSize.height.toFloat(), + dstWidth = containerWidth.toFloat(), + dstHeight = containerHeight.toFloat() + ) + val scaledWidth = realIconSize.width * backgroundScaleFactor + val scaledHeight = realIconSize.height * backgroundScaleFactor + val left = bounds.left + (containerWidth - scaledWidth) / 2f + val top = bounds.top + (containerHeight - scaledHeight) / 2f + val right = left + scaledWidth + val bottom = top + scaledHeight + val iconBounds = Rect( + /* left = */ floor(left).toInt(), + /* top = */ floor(top).toInt(), + /* right = */ ceil(right).toInt(), + /* bottom = */ ceil(bottom).toInt() + ) icon.bounds = iconBounds } diff --git a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/ComponentRegistry.kt b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/ComponentRegistry.kt index 5f10dcd25b..1f9886c139 100644 --- a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/ComponentRegistry.kt +++ b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/ComponentRegistry.kt @@ -110,7 +110,7 @@ open class ComponentRegistry private constructor( ?: throw IllegalArgumentException( "No Fetcher can handle this uri '${requestContext.request.uri}', " + "Please add a new Fetcher to support it, " + - "refer to the documentation: https://github.com/panpf/sketch/blob/main/docs/wiki/fetcher.md" + "refer to the documentation: https://github.com/panpf/sketch/blob/main/docs/fetcher.md" ) } @@ -138,7 +138,7 @@ open class ComponentRegistry private constructor( ): Decoder { return newDecoderOrNull(requestContext, fetchResult) ?: throw IllegalArgumentException( - "No Decoder can handle this uri '${requestContext.request.uri}', Please add a new Decoder to support it, refer to the documentation: https://github.com/panpf/sketch/blob/main/docs/wiki/decoder.md" + "No Decoder can handle this uri '${requestContext.request.uri}', Please add a new Decoder to support it, refer to the documentation: https://github.com/panpf/sketch/blob/main/docs/decoder.md" ) } diff --git a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt index 2ef35c7f5d..0ed6bf27c1 100644 --- a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt +++ b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.kt @@ -30,23 +30,28 @@ import okio.Path import okio.Path.Companion.toPath /** - * Sample: 'file:///sdcard/sample.jpg' + * Return sample: 'file:///sdcard/sample.jpg', 'D:\test\relative\image.jpg' * * @see com.github.panpf.sketch.core.common.test.fetch.FileUriFetcherTest.testNewFileUri */ -fun newFileUri(path: String): String = "${FileUriFetcher.SCHEME}://$path" +fun newFileUri(path: String): String = + if (Path.DIRECTORY_SEPARATOR == "/" && !path.startsWith("${FileUriFetcher.SCHEME}://")) { + "${FileUriFetcher.SCHEME}://$path" + } else { + path + } /** - * Sample: 'file:///sdcard/sample.jpg' + * Return sample: 'file:///sdcard/sample.jpg', 'D:\test\relative\image.jpg' * - * @see com.github.panpf.sketch.core.common.test.fetch.FileUriFetcherTest.testNewFileUri + * @see com.github.panpf.sketch.core.common.test.fetch.FileUriFetcherTest.testNewFileUri2 */ -fun newFileUri(path: Path): String = "${FileUriFetcher.SCHEME}://$path" +fun newFileUri(path: Path): String = newFileUri(path.toString()) /** * Check if the uri is a file uri * - * Support 'file:///sdcard/sample.jpg', '/sdcard/sample.jpg' uri + * Support 'file:///sdcard/sample.jpg', '/sdcard/sample.jpg', 'D:\test\relative\image.jpg' uri * * @see com.github.panpf.sketch.core.common.test.fetch.FileUriFetcherTest.testIsFileUri */ @@ -56,7 +61,7 @@ fun isFileUri(uri: Uri): Boolean = && uri.path?.takeIf { it.isNotEmpty() } != null /** - * Support 'file:///sdcard/sample.jpg', '/sdcard/sample.jpg' uri + * Support 'file:///sdcard/sample.jpg', '/sdcard/sample.jpg', 'D:\test\relative\image.jpg' uri * * @see com.github.panpf.sketch.core.common.test.fetch.FileUriFetcherTest */ diff --git a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Uri.kt b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Uri.kt index a106d2efe1..e0bba0bf77 100644 --- a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Uri.kt +++ b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/Uri.kt @@ -17,23 +17,78 @@ package com.github.panpf.sketch.util +import okio.Path + /** * Parse this [String] into a [Uri]. * * This method will not throw if the URI is malformed. * + * @param separator The path separator used to separate URI path elements. By default, this + * will be '/' on UNIX systems and '\' on Windows systems. * @see com.github.panpf.sketch.core.common.test.util.UriTest.testToUri */ -fun String.toUri(): Uri = Uri(this) +fun String.toUri(separator: String = Path.DIRECTORY_SEPARATOR): Uri { + var data = this + if (separator != "/") { + data = data.replace(separator, "/") + } + val original = this + return Uri( + data = original, + separator = separator, + elementsLazy = lazyOf(parseUriElements(data = data, original = original)) + ) +} + +/** + * Create a [Uri] from parts without parsing. + * + * @see toUri + */ +fun buildUri( + scheme: String? = null, + authority: String? = null, + path: String? = null, + query: String? = null, + fragment: String? = null, + separator: String = Path.DIRECTORY_SEPARATOR, +): Uri { + require(scheme != null || authority != null || path != null || query != null || fragment != null) { + "At least one of scheme, authority, path, query, or fragment must be non-null." + } + + return Uri( + data = buildData(scheme, authority, path, query, fragment), + separator = separator, + elements = Uri.Elements( + scheme = scheme, + authority = authority, + path = path, + query = query, + fragment = fragment, + ) + ) +} /** * A uniform resource locator. * * @see com.github.panpf.sketch.core.common.test.util.UriTest */ -class Uri internal constructor(private val data: String) { +class Uri internal constructor( + private val data: String, + val separator: String, + elementsLazy: Lazy +) { - private val elements: Elements by lazy { parseUri(data) } + private val elements: Elements by elementsLazy + + constructor(data: String, separator: String, elements: Elements) : this( + data = data, + separator = separator, + elementsLazy = lazyOf(elements) + ) val scheme: String? get() = elements.scheme @@ -53,7 +108,7 @@ class Uri internal constructor(private val data: String) { val path = path if (path != null) { val segments = mutableListOf() - var index = 0 + var index = -1 while (index < path.length) { val startIndex = index + 1 index = path.indexOf('/', startIndex) @@ -72,6 +127,19 @@ class Uri internal constructor(private val data: String) { } } + /** + * Returns the URI's [Uri.path] formatted according to the URI's native [Uri.separator]. + */ + val filePath: String? by lazy { + val pathSegments = pathSegments + if (pathSegments.isEmpty()) { + null + } else { + val prefix = if (path!!.startsWith(separator)) separator else "" + pathSegments.joinToString(prefix = prefix, separator = separator) + } + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false @@ -97,7 +165,40 @@ class Uri internal constructor(private val data: String) { ) } -private fun parseUri(data: String): Uri.Elements { +private fun buildData( + scheme: String?, + authority: String?, + path: String?, + query: String?, + fragment: String?, +) = buildString { + if (scheme != null) { + append(scheme) + append(':') + } + if (authority != null) { + append("//") + append(authority) + } + if (path != null) { + append(path) + } + if (query != null) { + append('?') + append(query) + } + if (fragment != null) { + append('#') + append(fragment) + } +} + +private fun parseUriElements( + data: String, + original: String, +): Uri.Elements { + var openScheme = true + var schemeEndIndex = -1 var authorityStartIndex = -1 var pathStartIndex = -1 var queryStartIndex = -1 @@ -107,31 +208,42 @@ private fun parseUri(data: String): Uri.Elements { while (index < data.length) { when (data[index]) { ':' -> { - if (queryStartIndex == -1 && - fragmentStartIndex == -1 && - pathStartIndex == -1 && - authorityStartIndex == -1 && - index + 2 < data.length && - data[index + 1] == '/' && - data[index + 2] == '/' + if (openScheme && + queryStartIndex == -1 && + fragmentStartIndex == -1 ) { - authorityStartIndex = index + 3 - index += 2 + if (index + 2 < original.length && + original[index + 1] == '/' && + original[index + 2] == '/' + ) { + // Standard URI with an authority (e.g. "file:///path/image.jpg"). + openScheme = false + schemeEndIndex = index + authorityStartIndex = index + 3 + index += 2 + } else if (data == original) { + // Special URI that has no authority (e.g. "file:/path/image.jpg"). + schemeEndIndex = index + authorityStartIndex = index + 1 + pathStartIndex = index + 1 + index += 1 + } } } '/' -> { - if (queryStartIndex == -1 && - fragmentStartIndex == -1 && - pathStartIndex == -1 + if (pathStartIndex == -1 && + queryStartIndex == -1 && + fragmentStartIndex == -1 ) { - pathStartIndex = index + openScheme = false + pathStartIndex = if (authorityStartIndex == -1) 0 else index } } '?' -> { - if (fragmentStartIndex == -1 && - queryStartIndex == -1 + if (queryStartIndex == -1 && + fragmentStartIndex == -1 ) { queryStartIndex = index + 1 } @@ -162,7 +274,7 @@ private fun parseUri(data: String): Uri.Elements { ) if (authorityStartIndex != -1) { - scheme = data.substring(0, authorityStartIndex - 3) + scheme = data.substring(0, schemeEndIndex) val authorityEndIndex = minOf( if (pathStartIndex == -1) Int.MAX_VALUE else pathStartIndex, @@ -181,16 +293,19 @@ private fun parseUri(data: String): Uri.Elements { fragment = data.substring(fragmentStartIndex, data.length) } - val size = maxOf( - scheme?.length ?: 0, - authority?.length ?: 0, + val maxLength = maxOf( + 0, maxOf( - path?.length ?: 0, - query?.length ?: 0, - fragment?.length ?: 0, - ), + scheme.length, + authority.length, + maxOf( + path.length, + query.length, + fragment.length, + ), + ) - 2, ) - val bytes = ByteArray(size) + val bytes = ByteArray(maxLength) return Uri.Elements( scheme = scheme?.percentDecode(bytes), authority = authority?.percentDecode(bytes), @@ -203,9 +318,19 @@ private fun parseUri(data: String): Uri.Elements { private fun String.percentDecode(bytes: ByteArray): String { var size = 0 var index = 0 + val length = length + val searchLength = maxOf(0, length - 2) - while (index < length) { - if (get(index) == '%' && index + 2 < length) { + while (true) { + if (index >= searchLength) { + if (index == size) { + // Fast path: the string doesn't have any encoded characters. + return this + } else if (index >= length) { + // Slow path: decode the byte array. + return bytes.decodeToString(endIndex = size) + } + } else if (get(index) == '%') { try { val hex = substring(index + 1, index + 3) bytes[size] = hex.toInt(16).toByte() @@ -220,12 +345,7 @@ private fun String.percentDecode(bytes: ByteArray): String { size++ index++ } +} - if (size == length) { - // Fast path: the string doesn't have any encoded characters. - return this - } else { - // Slow path: decode the byte array. - return bytes.decodeToString(endIndex = size) - } -} \ No newline at end of file +private val String?.length: Int + get() = this?.length ?: 0 \ No newline at end of file diff --git a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/core_utils.common.kt b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/core_utils.common.kt index 47d591d5b7..d3b1a259f3 100644 --- a/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/core_utils.common.kt +++ b/sketch-core/src/commonMain/kotlin/com/github/panpf/sketch/util/core_utils.common.kt @@ -250,30 +250,89 @@ internal fun ceilRoundPow2(number: Int): Int { /** * Calculate the scale multiplier according to the fit scale * - * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testComputeScaleMultiplierWithFit + * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testCalculateScaleMultiplierWithFit */ +fun calculateScaleMultiplierWithFit( + srcWidth: Float, + srcHeight: Float, + dstWidth: Float, + dstHeight: Float, + fitScale: Boolean +): Float { + val widthScaleFactor: Float = dstWidth / srcWidth + val heightScaleFactor: Float = dstHeight / srcHeight + return if (fitScale) { + min(widthScaleFactor, heightScaleFactor) + } else { + max(widthScaleFactor, heightScaleFactor) + } +} + +/** + * Calculate the scale multiplier according to the fit scale + */ +@Deprecated( + message = "Use calculateScaleMultiplierWithFit(Float, Float, Float, Float, Boolean) instead", + replaceWith = ReplaceWith("calculateScaleMultiplierWithFit(srcWidth, srcHeight, dstWidth, dstHeight, fitScale)") +) fun computeScaleMultiplierWithFit( srcWidth: Int, srcHeight: Int, dstWidth: Int, dstHeight: Int, fitScale: Boolean -): Double { - val widthPercent = dstWidth / srcWidth.toDouble() - val heightPercent = dstHeight / srcHeight.toDouble() - return if (fitScale) { - min(widthPercent, heightPercent) +): Float = calculateScaleMultiplierWithFit( + srcWidth = srcWidth.toFloat(), + srcHeight = srcHeight.toFloat(), + dstWidth = dstWidth.toFloat(), + dstHeight = dstHeight.toFloat(), + fitScale = fitScale +) + +/** + * Calculate the scale multiplier according to the inside scale + * + * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testCalculateScaleMultiplierWithInside + */ +fun calculateScaleMultiplierWithInside( + srcWidth: Float, + srcHeight: Float, + dstWidth: Float, + dstHeight: Float +): Float { + if (srcWidth <= dstWidth && srcHeight <= dstHeight) { + // center + return 1f } else { - max(widthPercent, heightPercent) + // fit + val widthScale = dstWidth / srcWidth + val heightScale = dstHeight / srcHeight + return min(widthScale, heightScale) } } +/** + * Calculate the scale multiplier according to the inside scale + * + * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testCalculateScaleMultiplierWithCrop + */ +fun calculateScaleMultiplierWithCrop( + srcWidth: Float, + srcHeight: Float, + dstWidth: Float, + dstHeight: Float +): Float { + val widthScale = dstWidth / srcWidth + val heightScale = dstHeight / srcHeight + return max(widthScale, heightScale) +} + /** * Calculate the scale multiplier according to the one side scale * - * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testComputeScaleMultiplierWithOneSide + * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testCalculateScaleMultiplierWithOneSide */ -fun computeScaleMultiplierWithOneSide(sourceSize: SketchSize, targetSize: SketchSize): Float { +fun calculateScaleMultiplierWithOneSide(sourceSize: SketchSize, targetSize: SketchSize): Float { if (targetSize.isNotEmpty) { val widthScaleFactor = targetSize.width.toFloat() / sourceSize.width val heightScaleFactor = targetSize.height.toFloat() / sourceSize.height @@ -291,12 +350,26 @@ fun computeScaleMultiplierWithOneSide(sourceSize: SketchSize, targetSize: Sketch return 1f } +/** + * Calculate the scale multiplier according to the one side scale + */ +@Deprecated( + message = "Use calculateScaleMultiplierWithOneSide(SketchSize, SketchSize) instead", + replaceWith = ReplaceWith("calculateScaleMultiplierWithOneSide(sourceSize, targetSize)") +) +fun computeScaleMultiplierWithOneSide(sourceSize: SketchSize, targetSize: SketchSize): Float = + calculateScaleMultiplierWithOneSide( + sourceSize = sourceSize, + targetSize = targetSize + ) + /** * Calculate the bounds of the Drawable to inside the container. * * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testCalculateInsideBounds */ +@Deprecated("No longer used") fun calculateInsideBounds(contentSize: Size, containerBounds: Rect): Rect { val containerWidth = containerBounds.width() val containerHeight = containerBounds.height() @@ -327,6 +400,7 @@ fun calculateInsideBounds(contentSize: Size, containerBounds: Rect): Rect { * * @see com.github.panpf.sketch.core.common.test.util.CoreUtilsTest.testCalculateCropBounds */ +@Deprecated("No longer used") fun calculateCropBounds(contentSize: Size, containerBounds: Rect): Rect { val containerWidth = containerBounds.width() val containerHeight = containerBounds.height() diff --git a/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/fetch/FileUriFetcherTest.kt b/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/fetch/FileUriFetcherTest.kt index 894b1e814c..9c15a1548d 100644 --- a/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/fetch/FileUriFetcherTest.kt +++ b/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/fetch/FileUriFetcherTest.kt @@ -27,6 +27,7 @@ import com.github.panpf.sketch.util.Size import com.github.panpf.sketch.util.defaultFileSystem import com.github.panpf.sketch.util.toUri import kotlinx.coroutines.test.runTest +import okio.Path import okio.Path.Companion.toPath import kotlin.test.Test import kotlin.test.assertEquals @@ -39,14 +40,36 @@ class FileUriFetcherTest { @Test fun testNewFileUri() { - assertEquals( - expected = "file:///sdcard/sample.jpg", - actual = newFileUri("/sdcard/sample.jpg") - ) - assertEquals( - expected = "file:///sdcard1/sample1.jpg", - actual = newFileUri("/sdcard1/sample1.jpg".toPath()) - ) + if (Path.DIRECTORY_SEPARATOR == "/") { + assertEquals( + expected = "file:///sdcard/sample.jpg", + actual = newFileUri("/sdcard/sample.jpg") + ) + assertEquals( + expected = "file:///sdcard1/sample1.jpg", + actual = newFileUri("file:///sdcard1/sample1.jpg") + ) + } else { + assertEquals( + expected = "D:\\test\\relative\\image.jpg", + actual = newFileUri("D:\\test\\relative\\image.jpg") + ) + } + } + + @Test + fun testNewFileUri2() { + if (Path.DIRECTORY_SEPARATOR == "/") { + assertEquals( + expected = "file:///sdcard1/sample1.jpg", + actual = newFileUri("/sdcard1/sample1.jpg".toPath()) + ) + } else { + assertEquals( + expected = "D:\\test\\relative\\image.jpg", + actual = newFileUri("D:\\test\\relative\\image.jpg".toPath()) + ) + } } @Test diff --git a/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/CoreUtilsTest.kt b/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/CoreUtilsTest.kt index 66846904a5..7f975f0bc4 100644 --- a/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/CoreUtilsTest.kt +++ b/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/CoreUtilsTest.kt @@ -29,10 +29,12 @@ import com.github.panpf.sketch.util.asOrNull import com.github.panpf.sketch.util.asOrThrow import com.github.panpf.sketch.util.calculateCropBounds import com.github.panpf.sketch.util.calculateInsideBounds +import com.github.panpf.sketch.util.calculateScaleMultiplierWithCrop +import com.github.panpf.sketch.util.calculateScaleMultiplierWithFit +import com.github.panpf.sketch.util.calculateScaleMultiplierWithInside +import com.github.panpf.sketch.util.calculateScaleMultiplierWithOneSide import com.github.panpf.sketch.util.ceilRoundPow2 import com.github.panpf.sketch.util.compareVersions -import com.github.panpf.sketch.util.computeScaleMultiplierWithFit -import com.github.panpf.sketch.util.computeScaleMultiplierWithOneSide import com.github.panpf.sketch.util.difference import com.github.panpf.sketch.util.floorRoundPow2 import com.github.panpf.sketch.util.format @@ -283,56 +285,100 @@ class CoreUtilsTest { } @Test - fun testComputeScaleMultiplierWithFit() { - assertEquals(0.2, computeScaleMultiplierWithFit(1000, 600, 200, 400, true), 0.1) - assertEquals(0.6, computeScaleMultiplierWithFit(1000, 600, 200, 400, false), 0.1) - assertEquals(0.3, computeScaleMultiplierWithFit(1000, 600, 400, 200, true), 0.1) - assertEquals(0.4, computeScaleMultiplierWithFit(1000, 600, 400, 200, false), 0.1) - - assertEquals(0.6, computeScaleMultiplierWithFit(1000, 600, 2000, 400, true), 0.1) - assertEquals(2.0, computeScaleMultiplierWithFit(1000, 600, 2000, 400, false), 0.1) - assertEquals(0.4, computeScaleMultiplierWithFit(1000, 600, 400, 2000, true), 0.1) - assertEquals(3.3, computeScaleMultiplierWithFit(1000, 600, 400, 2000, false), 0.1) - - assertEquals(2.0, computeScaleMultiplierWithFit(1000, 600, 2000, 4000, true), 0.1) - assertEquals(6.6, computeScaleMultiplierWithFit(1000, 600, 2000, 4000, false), 0.1) - assertEquals(3.3, computeScaleMultiplierWithFit(1000, 600, 4000, 2000, true), 0.1) - assertEquals(4.0, computeScaleMultiplierWithFit(1000, 600, 4000, 2000, false), 0.1) + fun testCalculateScaleMultiplierWithFit() { + assertEquals(0.2f, calculateScaleMultiplierWithFit(1000f, 600f, 200f, 400f, true), 0f) + assertEquals( + 0.666f, + calculateScaleMultiplierWithFit(1000f, 600f, 200f, 400f, false), + 0.001f + ) + assertEquals(0.333f, calculateScaleMultiplierWithFit(1000f, 600f, 400f, 200f, true), 0.001f) + assertEquals(0.4f, calculateScaleMultiplierWithFit(1000f, 600f, 400f, 200f, false), 0f) + + assertEquals( + 0.666f, + calculateScaleMultiplierWithFit(1000f, 600f, 2000f, 400f, true), + 0.001f + ) + assertEquals(2.0f, calculateScaleMultiplierWithFit(1000f, 600f, 2000f, 400f, false), 0f) + assertEquals(0.4f, calculateScaleMultiplierWithFit(1000f, 600f, 400f, 2000f, true), 0f) + assertEquals( + 3.333f, + calculateScaleMultiplierWithFit(1000f, 600f, 400f, 2000f, false), + 0.001f + ) + + assertEquals(2.0f, calculateScaleMultiplierWithFit(1000f, 600f, 2000f, 4000f, true), 0f) + assertEquals( + 6.666f, + calculateScaleMultiplierWithFit(1000f, 600f, 2000f, 4000f, false), + 0.001f + ) + assertEquals( + 3.333f, + calculateScaleMultiplierWithFit(1000f, 600f, 4000f, 2000f, true), + 0.001f + ) + assertEquals(4.0f, calculateScaleMultiplierWithFit(1000f, 600f, 4000f, 2000f, false), 0f) + } + + @Test + fun testCalculateScaleMultiplierWithInside() { + assertEquals(0.2f, calculateScaleMultiplierWithInside(1000f, 600f, 200f, 400f), 0f) + assertEquals(0.333f, calculateScaleMultiplierWithInside(1000f, 600f, 400f, 200f), 0.001f) + + assertEquals(0.666f, calculateScaleMultiplierWithInside(1000f, 600f, 2000f, 400f), 0.001f) + assertEquals(0.4f, calculateScaleMultiplierWithInside(1000f, 600f, 400f, 2000f), 0f) + + assertEquals(1f, calculateScaleMultiplierWithInside(1000f, 600f, 2000f, 4000f), 0f) + assertEquals(1f, calculateScaleMultiplierWithInside(1000f, 600f, 4000f, 2000f), 0f) + } + + @Test + fun testCalculateScaleMultiplierWithCrop() { + assertEquals(0.666f, calculateScaleMultiplierWithCrop(1000f, 600f, 200f, 400f), 0.001f) + assertEquals(0.4f, calculateScaleMultiplierWithCrop(1000f, 600f, 400f, 200f), 0f) + + assertEquals(2.0f, calculateScaleMultiplierWithCrop(1000f, 600f, 2000f, 400f), 0f) + assertEquals(3.333f, calculateScaleMultiplierWithCrop(1000f, 600f, 400f, 2000f), 0.001f) + + assertEquals(6.666f, calculateScaleMultiplierWithCrop(1000f, 600f, 2000f, 4000f), 0.001f) + assertEquals(4.0f, calculateScaleMultiplierWithCrop(1000f, 600f, 4000f, 2000f), 0f) } @Test - fun testComputeScaleMultiplierWithOneSide() { + fun testCalculateScaleMultiplierWithOneSide() { assertEquals( expected = 1.5f, - actual = computeScaleMultiplierWithOneSide( + actual = calculateScaleMultiplierWithOneSide( sourceSize = Size(100, 100), targetSize = Size(150, 200) ) ) assertEquals( expected = 0.2f, - actual = computeScaleMultiplierWithOneSide( + actual = calculateScaleMultiplierWithOneSide( sourceSize = Size(100, 100), targetSize = Size(50, 20) ) ) assertEquals( expected = 2.0f, - actual = computeScaleMultiplierWithOneSide( + actual = calculateScaleMultiplierWithOneSide( sourceSize = Size(100, 100), targetSize = Size(0, 200) ) ) assertEquals( expected = 0.5f, - actual = computeScaleMultiplierWithOneSide( + actual = calculateScaleMultiplierWithOneSide( sourceSize = Size(100, 100), targetSize = Size(50, 0) ) ) assertEquals( expected = 1.0f, - actual = computeScaleMultiplierWithOneSide( + actual = calculateScaleMultiplierWithOneSide( sourceSize = Size(100, 100), targetSize = Size(0, 0) ) diff --git a/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/UriTest.kt b/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/UriTest.kt index 0954a68915..e235f62645 100644 --- a/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/UriTest.kt +++ b/sketch-core/src/commonTest/kotlin/com/github/panpf/sketch/core/common/test/util/UriTest.kt @@ -1,9 +1,10 @@ package com.github.panpf.sketch.core.common.test.util -import com.github.panpf.sketch.util.Uri +import com.github.panpf.sketch.util.buildUri import com.github.panpf.sketch.util.toUri import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import kotlin.test.assertNull class UriTest { @@ -11,8 +12,65 @@ class UriTest { @Test fun testToUri() { assertEquals( - expected = Uri("https://example.com/image.jpg?q=jpg#fragment"), - actual = "https://example.com/image.jpg?q=jpg#fragment".toUri() + expected = "https://example.com/image.jpg?q=jpg#fragment", + actual = "https://example.com/image.jpg?q=jpg#fragment".toUri(separator = "/") + .toString() + ) + assertEquals( + expected = "https://example.com/image.jpg?q=jpg#fragment", + actual = "https://example.com/image.jpg?q=jpg#fragment".toUri(separator = "\\") + .toString() + ) + + assertEquals( + expected = "D:\\test\\relative\\image.jpg", + actual = "D:\\test\\relative\\image.jpg".toUri(separator = "/").toString() + ) + assertEquals( + expected = "D:\\test\\relative\\image.jpg", + actual = "D:\\test\\relative\\image.jpg".toUri(separator = "\\").toString() + ) + } + + @Test + fun testBuildUri() { + assertFailsWith(IllegalArgumentException::class) { + buildUri() + } + assertEquals( + expected = "http:", + actual = buildUri(scheme = "http").toString() + ) + assertEquals( + expected = "http://sample.com", + actual = buildUri(scheme = "http", authority = "sample.com").toString() + ) + assertEquals( + expected = "http://sample.com/avatar/my.png", + actual = buildUri( + scheme = "http", + authority = "sample.com", + path = "/avatar/my.png" + ).toString() + ) + assertEquals( + expected = "http://sample.com/avatar/my.png?key1=value1&key2=value2", + actual = buildUri( + scheme = "http", + authority = "sample.com", + path = "/avatar/my.png", + query = "key1=value1&key2=value2" + ).toString() + ) + assertEquals( + expected = "http://sample.com/avatar/my.png?key1=value1&key2=value2#fragment1", + actual = buildUri( + scheme = "http", + authority = "sample.com", + path = "/avatar/my.png", + query = "key1=value1&key2=value2", + fragment = "fragment1" + ).toString() ) } @@ -32,34 +90,110 @@ class UriTest { ) } + @Test + fun testToString() { + val uriString = "C:\\Users\\tiger\\Pictures\\P.jpg" + + val uri = uriString.toUri("/") + assertEquals(expected = uriString, actual = uri.toString()) + + val uri2 = uriString.toUri("\\") + assertEquals(expected = uriString, actual = uri2.toString()) + } + @Test fun network() { val uri = "https://www.example.com/image.jpg?q=jpg#fragment".toUri() assertEquals("https", uri.scheme) assertEquals("www.example.com", uri.authority) assertEquals("/image.jpg", uri.path) + assertEquals("/image.jpg", uri.filePath) assertEquals(listOf("image.jpg"), uri.pathSegments) assertEquals("q=jpg", uri.query) assertEquals("fragment", uri.fragment) } + @Test + fun absolute() { + val uri = "/test/absolute/image.jpg#something".toUri() + assertNull(uri.scheme) + assertNull(uri.authority) + assertEquals("/test/absolute/image.jpg", uri.path) + assertEquals("/test/absolute/image.jpg", uri.filePath) + assertEquals(listOf("test", "absolute", "image.jpg"), uri.pathSegments) + assertNull(uri.query) + assertEquals("something", uri.fragment) + } + @Test fun relative() { - val uri = "/test/relative/image.jpg#something".toUri() + val uri = "test/relative/image.jpg#something".toUri() assertNull(uri.scheme) assertNull(uri.authority) - assertEquals("/test/relative/image.jpg", uri.path) + assertEquals("test/relative/image.jpg", uri.path) + assertEquals("test/relative/image.jpg", uri.filePath) assertEquals(listOf("test", "relative", "image.jpg"), uri.pathSegments) assertNull(uri.query) assertEquals("something", uri.fragment) } + @Test + fun absoluteWithFileScheme() { + val uri = "file:///test/absolute/image.jpg#something".toUri() + assertEquals("file", uri.scheme) + assertEquals("", uri.authority) + assertEquals("/test/absolute/image.jpg", uri.path) + assertEquals("/test/absolute/image.jpg", uri.filePath) + assertEquals(listOf("test", "absolute", "image.jpg"), uri.pathSegments) + assertNull(uri.query) + assertEquals("something", uri.fragment) + } + + @Test + fun relativeWithFileScheme() { + val uri = "file://test/relative/image.jpg#something".toUri() + assertEquals("file", uri.scheme) + // This looks wrong, but is consistent with the URI specification. + // The authority is always the first path after the scheme and "://". + assertEquals("test", uri.authority) + assertEquals("/relative/image.jpg", uri.path) + assertEquals("/relative/image.jpg", uri.filePath) + assertEquals(listOf("relative", "image.jpg"), uri.pathSegments) + assertNull(uri.query) + assertEquals("something", uri.fragment) + } + + @Test + fun absoluteFileUriWithoutAuthority() { + val uri = "file:/test/image.jpg".toUri() + assertEquals("file", uri.scheme) + assertEquals("", uri.authority) + assertEquals("/test/image.jpg", uri.path) + assertEquals("/test/image.jpg", uri.filePath) + assertEquals(listOf("test", "image.jpg"), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } + + @Test + fun relativeFileUriWithoutAuthority() { + val uri = "file:test/image.jpg".toUri() + assertEquals("file", uri.scheme) + assertEquals("", uri.authority) + assertEquals("test/image.jpg", uri.path) + assertEquals("test/image.jpg", uri.filePath) + assertEquals(listOf("test", "image.jpg"), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } + @Test fun malformed() { val uri = "#something:/test/relative/image.jpg".toUri() assertNull(uri.scheme) assertNull(uri.authority) assertNull(uri.path) + assertNull(uri.filePath) assertEquals(listOf(), uri.pathSegments) assertNull(uri.query) assertEquals("something:/test/relative/image.jpg", uri.fragment) @@ -71,6 +205,7 @@ class UriTest { assertNull(uri.scheme) assertNull(uri.authority) assertEquals("/", uri.path) + assertNull(uri.filePath) assertEquals(listOf(), uri.pathSegments) assertNull(uri.query) assertEquals("02dkfj;anc%%2", uri.fragment) @@ -83,6 +218,7 @@ class UriTest { assertEquals("https", uri.scheme) assertEquals("images.unsplash.com", uri.authority) assertEquals("/photo-1550939810-cb345b2f4ad7", uri.path) + assertEquals("/photo-1550939810-cb345b2f4ad7", uri.filePath) assertEquals(listOf("photo-1550939810-cb345b2f4ad7"), uri.pathSegments) assertEquals( "ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjU4MjM5fQ", @@ -98,6 +234,7 @@ class UriTest { assertEquals("https", uri.scheme) assertEquals("example.com", uri.authority) assertEquals("/上海+中國", uri.path) + assertEquals("/上海+中國", uri.filePath) assertEquals(listOf("上海+中國"), uri.pathSegments) assertNull(uri.query) assertNull(uri.fragment) @@ -111,6 +248,7 @@ class UriTest { assertEquals("https", uri.scheme) assertEquals("example.com", uri.authority) assertEquals("/something ", uri.path) + assertEquals("/something ", uri.filePath) assertEquals(listOf("something "), uri.pathSegments) assertNull(uri.query) assertNull(uri.fragment) @@ -124,6 +262,7 @@ class UriTest { assertEquals("https", uri.scheme) assertEquals("example.com", uri.authority) assertEquals("/上海+中國%", uri.path) + assertEquals("/上海+中國%", uri.filePath) assertEquals(listOf("上海+中國%"), uri.pathSegments) assertNull(uri.query) assertNull(uri.fragment) @@ -136,6 +275,7 @@ class UriTest { assertEquals("file", uri.scheme) assertEquals("", uri.authority) assertEquals("/test///image.jpg", uri.path) + assertEquals("/test/image.jpg", uri.filePath) assertEquals(listOf("test", "image.jpg"), uri.pathSegments) assertNull(uri.query) assertNull(uri.fragment) @@ -146,9 +286,98 @@ class UriTest { val uri = "https://example.com?a=b#c".toUri() assertEquals("https", uri.scheme) assertEquals("example.com", uri.authority) - assertEquals(null, uri.path) + assertNull(uri.path) + assertNull(uri.filePath) assertEquals(listOf(), uri.pathSegments) assertEquals("a=b", uri.query) assertEquals("c", uri.fragment) } + + @Test + fun windowsPath() { + val uri = "D:\\test\\relative\\image.jpg".toUri(separator = "\\") + assertNull(uri.scheme) + assertNull(uri.authority) + assertEquals("D:/test/relative/image.jpg", uri.path) + assertEquals("D:\\test\\relative\\image.jpg", uri.filePath) + assertEquals(listOf("D:", "test", "relative", "image.jpg"), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } + + @Test + fun windowsPath2() { + val uri = "file://D:\\test\\relative\\image.jpg".toUri(separator = "\\") + assertEquals("file", uri.scheme) + assertEquals("D:", uri.authority) + assertEquals("/test/relative/image.jpg", uri.path) + assertEquals("test\\relative\\image.jpg", uri.filePath) + assertEquals(listOf("test", "relative", "image.jpg"), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } + + @Test + fun windowsPath3() { + val uri = "file://D:\\test\\relative\\image.jpg".toUri(separator = "/") + assertEquals("file", uri.scheme) + assertEquals("D:\\test\\relative\\image.jpg", uri.authority) + assertNull(uri.path) + assertNull(uri.filePath) + assertEquals(emptyList(), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } + + @Test + fun windowsPathFormattedAsUri() { + val uri = "file:///H:/1.png".toUri(separator = "\\") + assertEquals("file", uri.scheme) + assertEquals("", uri.authority) + assertEquals("/H:/1.png", uri.path) + assertEquals("H:\\1.png", uri.filePath) + assertEquals(listOf("H:", "1.png"), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } + + @Test + fun multipleSchemeSegments() { + // This format is used for Compose multiplatform resources on Android/JVM. + val uri = "jar:file:/outer/path/test.apk!/internal/path/1.png".toUri() + assertEquals("jar:file", uri.scheme) + assertEquals("", uri.authority) + assertEquals("/outer/path/test.apk!/internal/path/1.png", uri.path) + assertEquals("/outer/path/test.apk!/internal/path/1.png", uri.filePath) + assertEquals( + listOf("outer", "path", "test.apk!", "internal", "path", "1.png"), + uri.pathSegments + ) + assertNull(uri.query) + assertNull(uri.fragment) + } + + @Test + fun ipv6() { + val uri = "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/".toUri() + assertEquals("http", uri.scheme) + assertEquals("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", uri.authority) + assertEquals("/", uri.path) + assertNull(uri.filePath) + assertEquals(listOf(), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } + + @Test + fun data() { + val uri = "data:image/png;base64,FAKE_DATA".toUri() + assertEquals("data", uri.scheme) + assertEquals("", uri.authority) + assertEquals("image/png;base64,FAKE_DATA", uri.path) + assertEquals("image/png;base64,FAKE_DATA", uri.filePath) + assertEquals(listOf("image", "png;base64,FAKE_DATA"), uri.pathSegments) + assertNull(uri.query) + assertNull(uri.fragment) + } } diff --git a/sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/util/platform_contexts.desktop.kt b/sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/util/platform_contexts.desktop.kt index 1da4701ee1..89501f2a25 100644 --- a/sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/util/platform_contexts.desktop.kt +++ b/sketch-core/src/desktopMain/kotlin/com/github/panpf/sketch/util/platform_contexts.desktop.kt @@ -42,7 +42,7 @@ actual fun PlatformContext.appCacheDirectory(): Path? { ?.md5() ?: throw UnsupportedOperationException( "Unable to generate application aliases to automatically initialize downloadCache and resultCache, " + - "please configure them manually. Documentation address 'https://github.com/panpf/sketch/blob/main/docs/wiki/getting_started.md'" + "please configure them manually. Documentation address 'https://github.com/panpf/sketch/blob/main/docs/getting_started.md'" ) val fakeAppName = "SketchImageLoader${File.separator}${appFlag}" val cacheDir = AppDirsFactory.getInstance() diff --git a/sketch-core/src/jvmCommonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.jvm.kt b/sketch-core/src/jvmCommonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.jvm.kt index 14b2ef4cce..b9ea63a8f6 100644 --- a/sketch-core/src/jvmCommonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.jvm.kt +++ b/sketch-core/src/jvmCommonMain/kotlin/com/github/panpf/sketch/fetch/FileUriFetcher.jvm.kt @@ -16,12 +16,11 @@ package com.github.panpf.sketch.fetch -import com.github.panpf.sketch.fetch.FileUriFetcher.Companion.SCHEME import java.io.File /** - * Sample: 'file:///sdcard/sample.jpg' + * Return sample: 'file:///sdcard/sample.jpg', 'D:\test\relative\image.jpg' * * @see com.github.panpf.sketch.core.jvmcommon.test.fetch.FileUriFetcherJvmTest.testNewFileUri */ -fun newFileUri(file: File): String = "$SCHEME://${file.path}" \ No newline at end of file +fun newFileUri(file: File): String = newFileUri(file.path) \ No newline at end of file diff --git a/sketch-core/src/jvmCommonTest/kotlin/com/github/panpf/sketch/core/jvmcommon/test/fetch/FileUriFetcherJvmTest.kt b/sketch-core/src/jvmCommonTest/kotlin/com/github/panpf/sketch/core/jvmcommon/test/fetch/FileUriFetcherJvmTest.kt index 9077395b95..439cec0d13 100644 --- a/sketch-core/src/jvmCommonTest/kotlin/com/github/panpf/sketch/core/jvmcommon/test/fetch/FileUriFetcherJvmTest.kt +++ b/sketch-core/src/jvmCommonTest/kotlin/com/github/panpf/sketch/core/jvmcommon/test/fetch/FileUriFetcherJvmTest.kt @@ -1,6 +1,7 @@ package com.github.panpf.sketch.core.jvmcommon.test.fetch import com.github.panpf.sketch.fetch.newFileUri +import okio.Path import java.io.File import kotlin.test.Test import kotlin.test.assertEquals @@ -9,13 +10,16 @@ class FileUriFetcherJvmTest { @Test fun testNewFileUri() { - assertEquals( - expected = "file:///sdcard/sample.jpg", - actual = newFileUri(File("/sdcard/sample.jpg")) - ) - assertEquals( - expected = "file:///sdcard1/sample1.jpg", - actual = newFileUri(File("/sdcard1/sample1.jpg")) - ) + if (Path.DIRECTORY_SEPARATOR == "/") { + assertEquals( + expected = "file:///sdcard1/sample1.jpg", + actual = newFileUri(File("/sdcard1/sample1.jpg")) + ) + } else { + assertEquals( + expected = "D:\\test\\relative\\image.jpg", + actual = newFileUri(File("D:\\test\\relative\\image.jpg")) + ) + } } } \ No newline at end of file diff --git a/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/decodes.nonAndroid.kt b/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/decodes.nonAndroid.kt index 05620ddd39..43484bc37d 100644 --- a/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/decodes.nonAndroid.kt +++ b/sketch-core/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/decodes.nonAndroid.kt @@ -207,7 +207,7 @@ fun supportDecodeRegion(mimeType: String): Boolean? { "image/jpeg", "image/png", "image/webp", "image/bmp", "image/gif" -> true "image/svg+xml" -> false "image/heic", "image/heif", "image/avif" -> - if (compareVersions(BuildKonfig.SKIKO_VERSION_NAME, "0.8.15") <= 0) false else null + if (compareVersions(BuildKonfig.SKIKO_VERSION_NAME, "0.9.4") <= 0) false else null else -> null } diff --git a/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt b/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt index c253aa0e8e..7f546c4e03 100644 --- a/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt +++ b/sketch-core/src/nonAndroidTest/kotlin/com/github/panpf/sketch/core/nonandroid/test/util/BitmapsNonAndroidTest.kt @@ -272,271 +272,283 @@ class BitmapsNonAndroidTest { @Test fun testReadIntPixels() { @Suppress("EnumValuesSoftDeprecate") - ColorType.values().filter { it != ColorType.UNKNOWN && it != ColorType.BGRA_10101010_XR } + ColorType.values() + .filter { it != ColorType.UNKNOWN } + .filter { it != ColorType.BGRA_10101010_XR } + .filter { it != ColorType.R8G8_UNORM } + .filter { it != ColorType.A16_UNORM } .forEach { colorType -> - val jpegBitmap = ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap - val newJpegBitmap = createBitmap(jpegBitmap.imageInfo) - if (jpegBitmap.produceFingerPrint() == "ffffffffffffffff") { + val jpegBitmap = ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap + val newJpegBitmap = createBitmap(jpegBitmap.imageInfo) + if (jpegBitmap.produceFingerPrint() == "ffffffffffffffff") { + assertTrue( + actual = jpegBitmap.similarity(newJpegBitmap) == 0, + message = "colorType=$colorType" + ) + } else { + assertTrue( + actual = jpegBitmap.similarity(newJpegBitmap) >= 10, + message = "colorType=$colorType" + ) + } + val jpegIntPixels = jpegBitmap.readIntPixels().apply { + assertEquals( + expected = jpegBitmap.width * jpegBitmap.height, + actual = size, + message = "colorType=$colorType" + ) + } + newJpegBitmap.installIntPixels(jpegIntPixels) assertTrue( actual = jpegBitmap.similarity(newJpegBitmap) == 0, message = "colorType=$colorType" ) - } else { - assertTrue( - actual = jpegBitmap.similarity(newJpegBitmap) >= 10, - message = "colorType=$colorType" - ) - } - val jpegIntPixels = jpegBitmap.readIntPixels().apply { - assertEquals( - expected = jpegBitmap.width * jpegBitmap.height, - actual = size, - message = "colorType=$colorType" - ) - } - newJpegBitmap.installIntPixels(jpegIntPixels) - assertTrue( - actual = jpegBitmap.similarity(newJpegBitmap) == 0, - message = "colorType=$colorType" - ) - val pngBitmap = ResourceImages.png.decode(BitmapColorType(colorType)).bitmap - val newPngBitmap = createBitmap(pngBitmap.imageInfo) - if (pngBitmap.produceFingerPrint() == "ffffffffffffffff") { + val pngBitmap = ResourceImages.png.decode(BitmapColorType(colorType)).bitmap + val newPngBitmap = createBitmap(pngBitmap.imageInfo) + if (pngBitmap.produceFingerPrint() == "ffffffffffffffff") { + assertTrue( + actual = pngBitmap.similarity(newPngBitmap) == 0, + message = "colorType=$colorType" + ) + } else { + assertTrue( + actual = pngBitmap.similarity(newPngBitmap) >= 10, + message = "colorType=$colorType" + ) + } + val pngIntPixels = pngBitmap.readIntPixels().apply { + assertEquals( + expected = pngBitmap.width * pngBitmap.height, + actual = size, + message = "colorType=$colorType" + ) + } + newPngBitmap.installIntPixels(pngIntPixels) assertTrue( actual = pngBitmap.similarity(newPngBitmap) == 0, message = "colorType=$colorType" ) - } else { - assertTrue( - actual = pngBitmap.similarity(newPngBitmap) >= 10, - message = "colorType=$colorType" - ) } - val pngIntPixels = pngBitmap.readIntPixels().apply { - assertEquals( - expected = pngBitmap.width * pngBitmap.height, - actual = size, - message = "colorType=$colorType" - ) - } - newPngBitmap.installIntPixels(pngIntPixels) - assertTrue( - actual = pngBitmap.similarity(newPngBitmap) == 0, - message = "colorType=$colorType" - ) - } @Suppress("EnumValuesSoftDeprecate") - ColorType.values().filter { it != ColorType.UNKNOWN && it != ColorType.BGRA_10101010_XR } + ColorType.values() + .filter { it != ColorType.UNKNOWN } + .filter { it != ColorType.BGRA_10101010_XR } + .filter { it != ColorType.R8G8_UNORM } + .filter { it != ColorType.A16_UNORM } .forEach { colorType -> - val jpegBitmap = - ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap.apply { - assertEquals(expected = Size(1291, 1936), actual = size) - assertEquals(expected = colorType, actual = colorType) + val jpegBitmap = + ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap.apply { + assertEquals(expected = Size(1291, 1936), actual = size) + assertEquals(expected = colorType, actual = colorType) + } + val leftTopRect = Rect( + left = 0, + top = 0, + right = jpegBitmap.width / 2, + bottom = jpegBitmap.height / 2 + ).apply { + assertTrue(right > left) + assertTrue(bottom > top) + assertEquals(0, actual = left) + assertEquals(0, actual = top) + } + val rightTopRect = Rect( + left = leftTopRect.right, + top = leftTopRect.top, + right = jpegBitmap.width, + bottom = leftTopRect.bottom + ).apply { + assertTrue(right > left) + assertTrue(bottom > top) + assertEquals(leftTopRect.right, actual = left) + assertEquals(leftTopRect.top, actual = top) + assertEquals(jpegBitmap.width, actual = right) + assertEquals(leftTopRect.bottom, actual = bottom) + } + val leftBottomRect = Rect( + left = leftTopRect.left, + top = leftTopRect.bottom, + right = leftTopRect.right, + bottom = jpegBitmap.height + ).apply { + assertTrue(right > left) + assertTrue(bottom > top) + assertEquals(leftTopRect.left, actual = left) + assertEquals(leftTopRect.bottom, actual = top) + assertEquals(leftTopRect.right, actual = right) + assertEquals(jpegBitmap.height, actual = bottom) + } + val rightBottomRect = Rect( + left = leftTopRect.right, + top = leftTopRect.bottom, + right = jpegBitmap.width, + bottom = jpegBitmap.height + ).apply { + assertTrue(right > left) + assertTrue(bottom > top) + assertEquals(leftTopRect.right, actual = left) + assertEquals(leftTopRect.bottom, actual = top) + assertEquals(jpegBitmap.width, actual = right) + assertEquals(jpegBitmap.height, actual = bottom) } - val leftTopRect = Rect( - left = 0, - top = 0, - right = jpegBitmap.width / 2, - bottom = jpegBitmap.height / 2 - ).apply { - assertTrue(right > left) - assertTrue(bottom > top) - assertEquals(0, actual = left) - assertEquals(0, actual = top) - } - val rightTopRect = Rect( - left = leftTopRect.right, - top = leftTopRect.top, - right = jpegBitmap.width, - bottom = leftTopRect.bottom - ).apply { - assertTrue(right > left) - assertTrue(bottom > top) - assertEquals(leftTopRect.right, actual = left) - assertEquals(leftTopRect.top, actual = top) - assertEquals(jpegBitmap.width, actual = right) - assertEquals(leftTopRect.bottom, actual = bottom) - } - val leftBottomRect = Rect( - left = leftTopRect.left, - top = leftTopRect.bottom, - right = leftTopRect.right, - bottom = jpegBitmap.height - ).apply { - assertTrue(right > left) - assertTrue(bottom > top) - assertEquals(leftTopRect.left, actual = left) - assertEquals(leftTopRect.bottom, actual = top) - assertEquals(leftTopRect.right, actual = right) - assertEquals(jpegBitmap.height, actual = bottom) - } - val rightBottomRect = Rect( - left = leftTopRect.right, - top = leftTopRect.bottom, - right = jpegBitmap.width, - bottom = jpegBitmap.height - ).apply { - assertTrue(right > left) - assertTrue(bottom > top) - assertEquals(leftTopRect.right, actual = left) - assertEquals(leftTopRect.bottom, actual = top) - assertEquals(jpegBitmap.width, actual = right) - assertEquals(jpegBitmap.height, actual = bottom) - } - assertEquals( - expected = jpegBitmap.width, - actual = leftTopRect.width() + rightTopRect.width(), - message = "leftTopRect=$leftTopRect, rightTopRect=$rightTopRect" - ) - assertEquals( - expected = jpegBitmap.width, - actual = leftBottomRect.width() + rightBottomRect.width(), - message = "leftBottomRect=$leftBottomRect, rightBottomRect=$rightBottomRect" - ) - assertEquals( - expected = jpegBitmap.height, - actual = leftTopRect.height() + leftBottomRect.height(), - message = "leftTopRect=$leftTopRect, leftBottomRect=$leftBottomRect" - ) - assertEquals( - expected = jpegBitmap.height, - actual = rightTopRect.height() + rightBottomRect.height(), - message = "rightTopRect=$rightTopRect, rightBottomRect=$rightBottomRect" - ) - val leftTopIntPexels = jpegBitmap.readIntPixels( - x = leftTopRect.left, - y = leftTopRect.top, - width = leftTopRect.width(), - height = leftTopRect.height() - ).apply { assertEquals( - expected = leftTopRect.width() * leftTopRect.height(), - actual = size + expected = jpegBitmap.width, + actual = leftTopRect.width() + rightTopRect.width(), + message = "leftTopRect=$leftTopRect, rightTopRect=$rightTopRect" ) - } - val rightTopIntPexels = jpegBitmap.readIntPixels( - x = rightTopRect.left, - y = rightTopRect.top, - width = rightTopRect.width(), - height = rightTopRect.height() - ).apply { assertEquals( - expected = rightTopRect.width() * rightTopRect.height(), - actual = size + expected = jpegBitmap.width, + actual = leftBottomRect.width() + rightBottomRect.width(), + message = "leftBottomRect=$leftBottomRect, rightBottomRect=$rightBottomRect" ) - } - val leftBottomIntPexels = jpegBitmap.readIntPixels( - x = leftBottomRect.left, - y = leftBottomRect.top, - width = leftBottomRect.width(), - height = leftBottomRect.height() - ).apply { assertEquals( - expected = leftBottomRect.width() * leftBottomRect.height(), - actual = size + expected = jpegBitmap.height, + actual = leftTopRect.height() + leftBottomRect.height(), + message = "leftTopRect=$leftTopRect, leftBottomRect=$leftBottomRect" ) - } - val rightBottomIntPexels = jpegBitmap.readIntPixels( - x = rightBottomRect.left, - y = rightBottomRect.top, - width = rightBottomRect.width(), - height = rightBottomRect.height() - ).apply { assertEquals( - expected = rightBottomRect.width() * rightBottomRect.height(), - actual = size + expected = jpegBitmap.height, + actual = rightTopRect.height() + rightBottomRect.height(), + message = "rightTopRect=$rightTopRect, rightBottomRect=$rightBottomRect" ) - } - val piecedIntPexels = IntArray(jpegBitmap.width * jpegBitmap.height).apply { - indices.forEach { index -> - val x = index % jpegBitmap.width - val y = index / jpegBitmap.width - val pixel = if (leftTopRect.contains(x, y)) { - leftTopIntPexels[(y - leftTopRect.top) * leftTopRect.width() + (x - leftTopRect.left)] - } else if (rightTopRect.contains(x, y)) { - rightTopIntPexels[(y - rightTopRect.top) * rightTopRect.width() + (x - rightTopRect.left)] - } else if (leftBottomRect.contains(x, y)) { - leftBottomIntPexels[(y - leftBottomRect.top) * leftBottomRect.width() + (x - leftBottomRect.left)] - } else if (rightBottomRect.contains(x, y)) { - rightBottomIntPexels[(y - rightBottomRect.top) * rightBottomRect.width() + (x - rightBottomRect.left)] - } else { - throw IllegalArgumentException("Unknown rect, x=$x, y=$y") + val leftTopIntPexels = jpegBitmap.readIntPixels( + x = leftTopRect.left, + y = leftTopRect.top, + width = leftTopRect.width(), + height = leftTopRect.height() + ).apply { + assertEquals( + expected = leftTopRect.width() * leftTopRect.height(), + actual = size + ) + } + val rightTopIntPexels = jpegBitmap.readIntPixels( + x = rightTopRect.left, + y = rightTopRect.top, + width = rightTopRect.width(), + height = rightTopRect.height() + ).apply { + assertEquals( + expected = rightTopRect.width() * rightTopRect.height(), + actual = size + ) + } + val leftBottomIntPexels = jpegBitmap.readIntPixels( + x = leftBottomRect.left, + y = leftBottomRect.top, + width = leftBottomRect.width(), + height = leftBottomRect.height() + ).apply { + assertEquals( + expected = leftBottomRect.width() * leftBottomRect.height(), + actual = size + ) + } + val rightBottomIntPexels = jpegBitmap.readIntPixels( + x = rightBottomRect.left, + y = rightBottomRect.top, + width = rightBottomRect.width(), + height = rightBottomRect.height() + ).apply { + assertEquals( + expected = rightBottomRect.width() * rightBottomRect.height(), + actual = size + ) + } + val piecedIntPexels = IntArray(jpegBitmap.width * jpegBitmap.height).apply { + indices.forEach { index -> + val x = index % jpegBitmap.width + val y = index / jpegBitmap.width + val pixel = if (leftTopRect.contains(x, y)) { + leftTopIntPexels[(y - leftTopRect.top) * leftTopRect.width() + (x - leftTopRect.left)] + } else if (rightTopRect.contains(x, y)) { + rightTopIntPexels[(y - rightTopRect.top) * rightTopRect.width() + (x - rightTopRect.left)] + } else if (leftBottomRect.contains(x, y)) { + leftBottomIntPexels[(y - leftBottomRect.top) * leftBottomRect.width() + (x - leftBottomRect.left)] + } else if (rightBottomRect.contains(x, y)) { + rightBottomIntPexels[(y - rightBottomRect.top) * rightBottomRect.width() + (x - rightBottomRect.left)] + } else { + throw IllegalArgumentException("Unknown rect, x=$x, y=$y") + } + this@apply[index] = pixel } - this@apply[index] = pixel + }.apply { + assertEquals(expected = 1291 * 1936, actual = size) } - }.apply { - assertEquals(expected = 1291 * 1936, actual = size) - } - val jpegIntPixels = jpegBitmap.readIntPixels().apply { - assertEquals(expected = 1291 * 1936, actual = size) - } - assertEquals(expected = jpegIntPixels.toList(), actual = piecedIntPexels.toList()) + val jpegIntPixels = jpegBitmap.readIntPixels().apply { + assertEquals(expected = 1291 * 1936, actual = size) + } + assertEquals(expected = jpegIntPixels.toList(), actual = piecedIntPexels.toList()) - val newJpegBitmap = createBitmap(jpegBitmap.imageInfo) - newJpegBitmap.installIntPixels(piecedIntPexels) - assertTrue(actual = jpegBitmap.similarity(newJpegBitmap) == 0) - } + val newJpegBitmap = createBitmap(jpegBitmap.imageInfo) + newJpegBitmap.installIntPixels(piecedIntPexels) + assertTrue(actual = jpegBitmap.similarity(newJpegBitmap) == 0) + } } @Test fun testInstallIntPixels() { @Suppress("EnumValuesSoftDeprecate") - ColorType.values().filter { it != ColorType.UNKNOWN && it != ColorType.BGRA_10101010_XR } + ColorType.values() + .filter { it != ColorType.UNKNOWN } + .filter { it != ColorType.BGRA_10101010_XR } + .filter { it != ColorType.R8G8_UNORM } + .filter { it != ColorType.A16_UNORM } .forEach { colorType -> - val jpegBitmap = ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap - val newJpegBitmap = createBitmap(jpegBitmap.imageInfo) - if (jpegBitmap.produceFingerPrint() == "ffffffffffffffff") { + val jpegBitmap = ResourceImages.jpeg.decode(BitmapColorType(colorType)).bitmap + val newJpegBitmap = createBitmap(jpegBitmap.imageInfo) + if (jpegBitmap.produceFingerPrint() == "ffffffffffffffff") { + assertTrue( + actual = jpegBitmap.similarity(newJpegBitmap) == 0, + message = "colorType=$colorType" + ) + } else { + assertTrue( + actual = jpegBitmap.similarity(newJpegBitmap) >= 10, + message = "colorType=$colorType" + ) + } + val jpegIntPixels = jpegBitmap.readIntPixels().apply { + assertEquals( + expected = jpegBitmap.width * jpegBitmap.height, + actual = size, + message = "colorType=$colorType" + ) + } + newJpegBitmap.installIntPixels(jpegIntPixels) assertTrue( actual = jpegBitmap.similarity(newJpegBitmap) == 0, message = "colorType=$colorType" ) - } else { - assertTrue( - actual = jpegBitmap.similarity(newJpegBitmap) >= 10, - message = "colorType=$colorType" - ) - } - val jpegIntPixels = jpegBitmap.readIntPixels().apply { - assertEquals( - expected = jpegBitmap.width * jpegBitmap.height, - actual = size, - message = "colorType=$colorType" - ) - } - newJpegBitmap.installIntPixels(jpegIntPixels) - assertTrue( - actual = jpegBitmap.similarity(newJpegBitmap) == 0, - message = "colorType=$colorType" - ) - val pngBitmap = ResourceImages.png.decode(BitmapColorType(colorType)).bitmap - val newPngBitmap = createBitmap(pngBitmap.imageInfo) - if (pngBitmap.produceFingerPrint() == "ffffffffffffffff") { + val pngBitmap = ResourceImages.png.decode(BitmapColorType(colorType)).bitmap + val newPngBitmap = createBitmap(pngBitmap.imageInfo) + if (pngBitmap.produceFingerPrint() == "ffffffffffffffff") { + assertTrue( + actual = pngBitmap.similarity(newPngBitmap) == 0, + message = "colorType=$colorType" + ) + } else { + assertTrue( + actual = pngBitmap.similarity(newPngBitmap) >= 10, + message = "colorType=$colorType" + ) + } + val pngIntPixels = pngBitmap.readIntPixels().apply { + assertEquals( + expected = pngBitmap.width * pngBitmap.height, + actual = size, + message = "colorType=$colorType" + ) + } + newPngBitmap.installIntPixels(pngIntPixels) assertTrue( actual = pngBitmap.similarity(newPngBitmap) == 0, message = "colorType=$colorType" ) - } else { - assertTrue( - actual = pngBitmap.similarity(newPngBitmap) >= 10, - message = "colorType=$colorType" - ) - } - val pngIntPixels = pngBitmap.readIntPixels().apply { - assertEquals( - expected = pngBitmap.width * pngBitmap.height, - actual = size, - message = "colorType=$colorType" - ) } - newPngBitmap.installIntPixels(pngIntPixels) - assertTrue( - actual = pngBitmap.similarity(newPngBitmap) == 0, - message = "colorType=$colorType" - ) - } } @Test diff --git a/sketch-extensions-apkicon/src/androidTest/kotlin/com/github/panpf/sketch/extensions/apkicon/test/decode/ApkIconDecoderTest.kt b/sketch-extensions-apkicon/src/androidTest/kotlin/com/github/panpf/sketch/extensions/apkicon/test/decode/ApkIconDecoderTest.kt index 73261b2aa1..7e5321fc57 100644 --- a/sketch-extensions-apkicon/src/androidTest/kotlin/com/github/panpf/sketch/extensions/apkicon/test/decode/ApkIconDecoderTest.kt +++ b/sketch-extensions-apkicon/src/androidTest/kotlin/com/github/panpf/sketch/extensions/apkicon/test/decode/ApkIconDecoderTest.kt @@ -39,7 +39,7 @@ import com.github.panpf.sketch.test.utils.intrinsicSize import com.github.panpf.sketch.test.utils.shortInfoColorSpace import com.github.panpf.sketch.test.utils.toRequestContext import com.github.panpf.sketch.util.Size -import com.github.panpf.sketch.util.computeScaleMultiplierWithOneSide +import com.github.panpf.sketch.util.calculateScaleMultiplierWithOneSide import com.github.panpf.sketch.util.times import com.github.panpf.sketch.util.toShortInfoString import kotlinx.coroutines.test.runTest @@ -127,7 +127,7 @@ class ApkIconDecoderTest { ImageRequest(context, apkFilePath) .decode(sketch, factory) .apply { - val sizeMultiplier = computeScaleMultiplierWithOneSide(imageInfo.size, screenSize) + val sizeMultiplier = calculateScaleMultiplierWithOneSide(imageInfo.size, screenSize) val bitmapSize = imageInfo.size.times(sizeMultiplier) assertEquals( expected = "Bitmap(${bitmapSize},ARGB_8888${shortInfoColorSpace("SRGB")})", @@ -147,7 +147,7 @@ class ApkIconDecoderTest { ImageRequest(context, apkFilePath) { colorType(Bitmap.Config.RGB_565) }.decode(sketch, factory).apply { - val sizeMultiplier = computeScaleMultiplierWithOneSide(imageInfo.size, screenSize) + val sizeMultiplier = calculateScaleMultiplierWithOneSide(imageInfo.size, screenSize) val bitmapSize = imageInfo.size.times(sizeMultiplier) assertEquals( expected = "Bitmap(${bitmapSize},RGB_565${shortInfoColorSpace("SRGB")})", @@ -185,7 +185,7 @@ class ApkIconDecoderTest { }.decode(sketch, factory) .apply { val sizeMultiplier = - computeScaleMultiplierWithOneSide(imageInfo.size, Size(100, 100)) + calculateScaleMultiplierWithOneSide(imageInfo.size, Size(100, 100)) val bitmapSize = imageInfo.size.times(sizeMultiplier) assertEquals( expected = "Bitmap(${bitmapSize},ARGB_8888${shortInfoColorSpace("SRGB")})", diff --git a/sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/drawable/ApkIconDrawableFetcher.kt b/sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/drawable/ApkIconDrawableFetcher.kt index d3c4e49930..c88cce933e 100644 --- a/sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/drawable/ApkIconDrawableFetcher.kt +++ b/sketch-extensions-apkicon/src/main/kotlin/com/github/panpf/sketch/drawable/ApkIconDrawableFetcher.kt @@ -36,9 +36,11 @@ class ApkIconDrawableFetcher(private val file: File) : DrawableFetcher { val packageInfo = packageManager.getPackageArchiveInfo(file.path, PackageManager.GET_ACTIVITIES) ?: throw IOException("getPackageArchiveInfo return null. ${file.path}") - packageInfo.applicationInfo.sourceDir = file.path - packageInfo.applicationInfo.publicSourceDir = file.path - return packageManager.getApplicationIcon(packageInfo.applicationInfo) + val applicationInfo = packageInfo.applicationInfo + ?: throw IOException("applicationInfo is null. ${file.path}") + applicationInfo.sourceDir = file.path + applicationInfo.publicSourceDir = file.path + return packageManager.getApplicationIcon(applicationInfo) } override fun equals(other: Any?): Boolean { diff --git a/sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/drawable/AppIconDrawableFetcher.kt b/sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/drawable/AppIconDrawableFetcher.kt index 50c82784e8..27d25161f1 100644 --- a/sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/drawable/AppIconDrawableFetcher.kt +++ b/sketch-extensions-appicon/src/main/kotlin/com/github/panpf/sketch/drawable/AppIconDrawableFetcher.kt @@ -44,7 +44,9 @@ class AppIconDrawableFetcher( if (appVersionCode != versionCode) { throw Exception("App versionCode mismatch, $appVersionCode != $versionCode") } - return packageInfo.applicationInfo.loadIcon(packageManager) + val applicationInfo = packageInfo.applicationInfo + ?: throw Exception("applicationInfo is null '$packageName'") + return applicationInfo.loadIcon(packageManager) ?: throw Exception("loadIcon return null '$packageName'") } diff --git a/sketch-svg/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.android.kt b/sketch-svg/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.android.kt index a3018189a0..09f587c677 100644 --- a/sketch-svg/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.android.kt +++ b/sketch-svg/src/androidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.android.kt @@ -35,7 +35,7 @@ import com.github.panpf.sketch.request.svgBackgroundColor import com.github.panpf.sketch.request.svgCss import com.github.panpf.sketch.source.DataSource import com.github.panpf.sketch.util.Size -import com.github.panpf.sketch.util.computeScaleMultiplierWithOneSide +import com.github.panpf.sketch.util.calculateScaleMultiplierWithOneSide import com.github.panpf.sketch.util.isNotEmpty import com.github.panpf.sketch.util.safeToSoftware import com.github.panpf.sketch.util.times @@ -100,7 +100,7 @@ internal actual fun DataSource.decodeSvg( val imageInfo = ImageInfo(size = imageSize, mimeType = SvgDecoder.MIME_TYPE) val resize = requestContext.computeResize(imageInfo.size) - val targetScale = computeScaleMultiplierWithOneSide( + val targetScale = calculateScaleMultiplierWithOneSide( sourceSize = imageSize, targetSize = resize.size ) diff --git a/sketch-svg/src/commonTest/kotlin/com/github/panpf/sketch/svg/common/test/decode/SvgDecoderTest.kt b/sketch-svg/src/commonTest/kotlin/com/github/panpf/sketch/svg/common/test/decode/SvgDecoderTest.kt index 8a1accd165..5f397be6fc 100644 --- a/sketch-svg/src/commonTest/kotlin/com/github/panpf/sketch/svg/common/test/decode/SvgDecoderTest.kt +++ b/sketch-svg/src/commonTest/kotlin/com/github/panpf/sketch/svg/common/test/decode/SvgDecoderTest.kt @@ -37,7 +37,7 @@ import com.github.panpf.sketch.test.utils.fetch import com.github.panpf.sketch.test.utils.toRequestContext import com.github.panpf.sketch.util.Size import com.github.panpf.sketch.util.SketchSize -import com.github.panpf.sketch.util.computeScaleMultiplierWithOneSide +import com.github.panpf.sketch.util.calculateScaleMultiplierWithOneSide import com.github.panpf.sketch.util.screenSize import com.github.panpf.sketch.util.times import kotlinx.coroutines.test.runTest @@ -144,7 +144,7 @@ class SvgDecoderTest { actual = imageInfo.toShortString() ) val size = context.screenSize() - val sizeMultiplier = computeScaleMultiplierWithOneSide(imageInfo.size, size) + val sizeMultiplier = calculateScaleMultiplierWithOneSide(imageInfo.size, size) val bitmapSize = imageInfo.size.times(sizeMultiplier) assertEquals(expected = bitmapSize, actual = image.size) assertEquals(expected = LOCAL, actual = dataFrom) diff --git a/sketch-svg/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.nonAndroid.kt b/sketch-svg/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.nonAndroid.kt index 764396c79b..609e8ee601 100644 --- a/sketch-svg/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.nonAndroid.kt +++ b/sketch-svg/src/nonAndroidMain/kotlin/com/github/panpf/sketch/decode/internal/svgs.nonAndroid.kt @@ -27,7 +27,7 @@ import com.github.panpf.sketch.request.RequestContext import com.github.panpf.sketch.request.svgBackgroundColor import com.github.panpf.sketch.source.DataSource import com.github.panpf.sketch.util.Size -import com.github.panpf.sketch.util.computeScaleMultiplierWithOneSide +import com.github.panpf.sketch.util.calculateScaleMultiplierWithOneSide import com.github.panpf.sketch.util.isNotEmpty import com.github.panpf.sketch.util.times import okio.buffer @@ -114,7 +114,7 @@ internal actual fun DataSource.decodeSvg( val imageInfo = ImageInfo(size = imageSize, mimeType = MIME_TYPE) val resize = requestContext.computeResize(imageInfo.size) - val targetScale = computeScaleMultiplierWithOneSide( + val targetScale = calculateScaleMultiplierWithOneSide( sourceSize = imageSize, targetSize = resize.size ) diff --git a/sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/CrossfadeDrawable.kt b/sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/CrossfadeDrawable.kt index b1cae7cc74..cfc74dc44a 100644 --- a/sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/CrossfadeDrawable.kt +++ b/sketch-view-core/src/main/kotlin/com/github/panpf/sketch/drawable/CrossfadeDrawable.kt @@ -39,11 +39,12 @@ import androidx.core.graphics.withSave import androidx.vectordrawable.graphics.drawable.Animatable2Compat import com.github.panpf.sketch.transition.CrossfadeTransition import com.github.panpf.sketch.transition.TransitionDrawable -import com.github.panpf.sketch.util.computeScaleMultiplierWithFit +import com.github.panpf.sketch.util.calculateScaleMultiplierWithFit import com.github.panpf.sketch.util.requiredMainThread import com.github.panpf.sketch.util.toLogString +import kotlin.math.ceil +import kotlin.math.floor import kotlin.math.max -import kotlin.math.roundToInt /** * A [Drawable] that crossfades from [start] to [end]. @@ -323,21 +324,26 @@ class CrossfadeDrawable @JvmOverloads constructor( val targetWidth = targetBounds.width() val targetHeight = targetBounds.height() - val multiplier = computeScaleMultiplierWithFit( - srcWidth = width, - srcHeight = height, - dstWidth = targetWidth, - dstHeight = targetHeight, + val multiplier = calculateScaleMultiplierWithFit( + srcWidth = width.toFloat(), + srcHeight = height.toFloat(), + dstWidth = targetWidth.toFloat(), + dstHeight = targetHeight.toFloat(), fitScale = fitScale ) - val dx = ((targetWidth - multiplier * width) / 2).roundToInt() - val dy = ((targetHeight - multiplier * height) / 2).roundToInt() + val dx = (targetWidth - multiplier * width) / 2f + val dy = (targetHeight - multiplier * height) / 2f val left = targetBounds.left + dx val top = targetBounds.top + dy val right = targetBounds.right - dx val bottom = targetBounds.bottom - dy - drawable.setBounds(left, top, right, bottom) + drawable.setBounds( + /* left = */ floor(left).toInt(), + /* top = */ floor(top).toInt(), + /* right = */ ceil(right).toInt(), + /* bottom = */ ceil(bottom).toInt() + ) } private fun computeIntrinsicDimension(startSize: Int?, endSize: Int?): Int {