diff --git a/README.md b/README.md index 83bc2203c..2831612c7 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Инструкция по сборке: Требуется: - 1) Android Studio Hedgehog (2023.1.1) или выше. Kotlin 1.9.* - 2) Android SDK 34 - 3) Android NDK 26.0.10404224 + 1) Android Studio Ladybug Feature Drop 2024.2.2 или выше. Kotlin 2.1.* + 2) Android SDK 35 + 3) Android NDK 28.0.12674087 Если не работает музыка в Fenrir Kate, обновите kate_receipt_gms_token в app.build_config. Взять токен можно из Kate Mobile Extra Mod diff --git a/app_fenrir/src/main/AndroidManifest.xml b/app_fenrir/src/main/AndroidManifest.xml index c20ca6b02..bfcef0fe1 100644 --- a/app_fenrir/src/main/AndroidManifest.xml +++ b/app_fenrir/src/main/AndroidManifest.xml @@ -618,7 +618,9 @@ + android:screenOrientation="locked" + android:theme="@style/App.DayNight.Swipes" + tools:ignore="DiscouragedApi" /> + android:screenOrientation="locked" + android:theme="@style/App.DayNight.Swipes" + tools:ignore="DiscouragedApi" /> , V : IMvpVie AbsMvpBottomSheetDialogFragment(), IMvpView, IErrorView, IToastView, IToolbarView { override fun showError(errorText: String?) { - customToast?.showToastError(errorText) + if (isAdded) { + customToast?.showToastError(errorText) + } } override val customToast: AbsCustomToast? diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/settings/backup/SettingsBackup.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/settings/backup/SettingsBackup.kt index 5ed7562b1..7f677c0ae 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/settings/backup/SettingsBackup.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/settings/backup/SettingsBackup.kt @@ -32,6 +32,7 @@ class SettingsBackup { var download_photo_tap: Boolean? = null var show_photos_line: Boolean? = null var instant_photo_display: Boolean? = null + var validate_tls: Boolean? = null var picasso_dispatcher: String? = null var audio_round_icon: Boolean? = null var use_long_click_download: Boolean? = null diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/json/internal/JsonExceptions.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/json/internal/JsonExceptions.kt index ff14dbd75..979624cc7 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/json/internal/JsonExceptions.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/util/serializeble/json/internal/JsonExceptions.kt @@ -56,7 +56,7 @@ internal fun AbstractJsonLexer.invalidTrailingComma(entity: String = "object"): fail( "Trailing comma before the end of JSON $entity", position = currentPosition - 1, - hint = "Trailing commas are non-complaint JSON and not allowed by default. Use 'allowTrailingCommas = true' in 'Json {}' builder to support them." + hint = "Trailing commas are non-complaint JSON and not allowed by default. Use 'allowTrailingComma = true' in 'Json {}' builder to support them." ) } diff --git a/build.gradle b/build.gradle index 9a6261394..ce57bb856 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ buildscript { ext.graphicsShapesVersion = "1.0.1" ext.lifecycleVersion = "2.8.7" ext.mediaVersion = "1.7.0" - ext.media3Version = "1.5.0-rc02" + ext.media3Version = "1.5.0" ext.resourceInspectionAnnotation = "1.0.1" ext.savedStateVersion = "1.3.0-alpha05" ext.swiperefreshlayoutVersion = "1.2.0-alpha01" @@ -58,7 +58,7 @@ buildscript { ext.autoValueVersion = "1.11.0" //common libraries - ext.kotlin_version = "2.1.0-RC2" + ext.kotlin_version = "2.1.0" ext.kotlin_coroutines = "1.9.0" ext.kotlin_serializer = "1.7.3" ext.okhttpLibraryVersion = "5.0.0-SNAPSHOT" @@ -92,7 +92,7 @@ buildscript { //maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } } dependencies { - classpath "com.android.tools.build:gradle:8.7.2" + classpath "com.android.tools.build:gradle:8.8.0-rc01" classpath "com.google.gms:google-services:4.4.2" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e52cec12e..0108df974 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Mar 20 16:00:00 MSK 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/libfenrir/build.gradle b/libfenrir/build.gradle index c05556242..af4f0df7b 100644 --- a/libfenrir/build.gradle +++ b/libfenrir/build.gradle @@ -49,7 +49,7 @@ android { externalNativeBuild { cmake { - version = "3.31.0" + version = "3.31.1" path = file("src/main/jni/CMakeLists.txt") } } diff --git a/libfenrir/src/main/java/com/github/luben/zstd/Zstd.java b/libfenrir/src/main/java/com/github/luben/zstd/Zstd.java index a40b8ac71..56d538f3c 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/Zstd.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/Zstd.java @@ -531,7 +531,7 @@ public static long decompressDirectByteBufferFastDict(ByteBuffer dst, int dstOff public static native int loadDictCompress(long stream, byte[] dict, int dict_size); public static native int loadFastDictCompress(long stream, ZstdDictCompress dict); // TODO: Fix native compilation - //public static native void registerSequenceProducer(long stream, long seqProdState, long seqProdFunction); + public static native void registerSequenceProducer(long stream, long seqProdState, long seqProdFunction); // static native long getBuiltinSequenceProducer(); // Used in tests static native long getStubSequenceProducer(); // Used in tests public static native int setCompressionChecksums(long stream, boolean useChecksums); diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java index 06539e097..6a3f9c1f3 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdCompressCtx.java @@ -274,7 +274,6 @@ public ZstdCompressCtx setLong(int windowLog) { * Register an external sequence producer * @param producer the user-defined {@link SequenceProducer} to register. */ - /* TODO: fix compilation public ZstdCompressCtx registerSequenceProducer(SequenceProducer producer) { ensureOpen(); acquireSharedLock(); @@ -300,7 +299,6 @@ public ZstdCompressCtx registerSequenceProducer(SequenceProducer producer) { } return this; } - */ /** * Enable or disable sequence producer fallback @@ -361,7 +359,7 @@ public ZstdCompressCtx setValidateSequences(Zstd.ParamSwitch validateSequences) /** * Enable or disable long-distance matching. - * @param enableLDM whether to enable long-distance matching. + * @param ldm whether to enable long-distance matching. */ public ZstdCompressCtx setEnableLongDistanceMatching(Zstd.ParamSwitch enableLDM) { ensureOpen(); @@ -433,7 +431,12 @@ public ZstdCompressCtx loadDict(byte[] dict) { */ public ZstdFrameProgression getFrameProgression() { ensureOpen(); - return getFrameProgression0(nativePtr); + acquireSharedLock(); + try { + return getFrameProgression0(nativePtr); + } finally { + releaseSharedLock(); + } } private static native ZstdFrameProgression getFrameProgression0(long ptr); @@ -443,10 +446,16 @@ public ZstdFrameProgression getFrameProgression() { */ public void reset() { ensureOpen(); - long result = reset0(nativePtr); - if (Zstd.isError(result)) { - throw new ZstdException(result); + acquireSharedLock(); + try { + long result = reset0(nativePtr); + if (Zstd.isError(result)) { + throw new ZstdException(result); + } + } finally { + releaseSharedLock(); } + } private static native long reset0(long ptr); @@ -460,9 +469,14 @@ public void reset() { */ public void setPledgedSrcSize(long srcSize) { ensureOpen(); - long result = setPledgedSrcSize0(nativePtr, srcSize); - if (Zstd.isError(result)) { - throw new ZstdException(result); + acquireSharedLock(); + try { + long result = setPledgedSrcSize0(nativePtr, srcSize); + if (Zstd.isError(result)) { + throw new ZstdException(result); + } + } finally { + releaseSharedLock(); } } private static native long setPledgedSrcSize0(long ptr, long srcSize); @@ -478,14 +492,19 @@ public void setPledgedSrcSize(long srcSize) { */ public boolean compressDirectByteBufferStream(ByteBuffer dst, ByteBuffer src, EndDirective endOp) { ensureOpen(); - long result = compressDirectByteBufferStream0(nativePtr, dst, dst.position(), dst.limit(), src, src.position(), src.limit(), endOp.value()); - if ((result & 0x80000000L) != 0) { - long code = result & 0xFF; - throw new ZstdException(code, Zstd.getErrorName(code)); + acquireSharedLock(); + try { + long result = compressDirectByteBufferStream0(nativePtr, dst, dst.position(), dst.limit(), src, src.position(), src.limit(), endOp.value()); + if ((result & 0x80000000L) != 0) { + long code = result & 0xFF; + throw new ZstdException(code, Zstd.getErrorName(code)); + } + src.position((int)(result & 0x7FFFFFFF)); + dst.position((int)(result >>> 32) & 0x7FFFFFFF); + return (result >>> 63) == 1; + } finally { + releaseSharedLock(); } - src.position((int)(result & 0x7FFFFFFF)); - dst.position((int)(result >>> 32) & 0x7FFFFFFF); - return (result >>> 63) == 1; } /** @@ -600,7 +619,6 @@ public int compressByteArray(byte[] dstBuff, int dstOffset, int dstSize, byte[] * @return the size of the compressed data */ public int compress(ByteBuffer dstBuf, ByteBuffer srcBuf) { - int size = compressDirectByteBuffer(dstBuf, // compress into dstBuf dstBuf.position(), // write compressed data starting at offset position() dstBuf.limit() - dstBuf.position(), // write no more than limit() - position() bytes diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdDecompressCtx.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdDecompressCtx.java index 7c68540ce..d61d9a1df 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/ZstdDecompressCtx.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdDecompressCtx.java @@ -95,9 +95,18 @@ public ZstdDecompressCtx loadDict(byte[] dict) { */ public void reset() { ensureOpen(); - reset0(nativePtr); + acquireSharedLock(); + try { + long result = reset0(nativePtr); + if (Zstd.isError(result)) { + throw new ZstdException(result); + } + } finally { + releaseSharedLock(); + } + } - private static native void reset0(long nativePtr); + private static native long reset0(long nativePtr); private void ensureOpen() { if (nativePtr == 0) { @@ -115,14 +124,19 @@ private void ensureOpen() { */ public boolean decompressDirectByteBufferStream(ByteBuffer dst, ByteBuffer src) { ensureOpen(); - long result = decompressDirectByteBufferStream0(nativePtr, dst, dst.position(), dst.limit(), src, src.position(), src.limit()); - if ((result & 0x80000000L) != 0) { - long code = result & 0xFF; - throw new ZstdException(code, Zstd.getErrorName(code)); + acquireSharedLock(); + try { + long result = decompressDirectByteBufferStream0(nativePtr, dst, dst.position(), dst.limit(), src, src.position(), src.limit()); + if ((result & 0x80000000L) != 0) { + long code = result & 0xFF; + throw new ZstdException(code, Zstd.getErrorName(code)); + } + src.position((int)(result & 0x7FFFFFFF)); + dst.position((int)(result >>> 32) & 0x7FFFFFFF); + return (result >>> 63) == 1; + } finally { + releaseSharedLock(); } - src.position((int)(result & 0x7FFFFFFF)); - dst.position((int)(result >>> 32) & 0x7FFFFFFF); - return (result >>> 63) == 1; } /** @@ -236,7 +250,6 @@ public int decompressByteArray(byte[] dstBuff, int dstOffset, int dstSize, byte[ * @return the size of the decompressed data. */ public int decompress(ByteBuffer dstBuf, ByteBuffer srcBuf) throws ZstdException { - int size = decompressDirectByteBuffer(dstBuf, // decompress into dstBuf dstBuf.position(), // write decompressed data at offset position() dstBuf.limit() - dstBuf.position(), // write no more than limit() - position() diff --git a/libfenrir/src/main/jni/CMakeLists.txt b/libfenrir/src/main/jni/CMakeLists.txt index d83d5f6a3..fb8281ff5 100644 --- a/libfenrir/src/main/jni/CMakeLists.txt +++ b/libfenrir/src/main/jni/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.31.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.31.1 FATAL_ERROR) project(fenrir_jni C CXX ASM) if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") diff --git a/libfenrir/src/main/jni/animation/libyuv/include/libyuv/row.h b/libfenrir/src/main/jni/animation/libyuv/include/libyuv/row.h index 70f89134c..02fa935ca 100644 --- a/libfenrir/src/main/jni/animation/libyuv/include/libyuv/row.h +++ b/libfenrir/src/main/jni/animation/libyuv/include/libyuv/row.h @@ -553,8 +553,15 @@ extern "C" { #define HAS_BGRATOUVROW_SVE2 #define HAS_DIVIDEROW_16_SVE2 #define HAS_HALFFLOATROW_SVE2 +#define HAS_I210ALPHATOARGBROW_SVE2 +#define HAS_I210TOAR30ROW_SVE2 #define HAS_I210TOARGBROW_SVE2 +#define HAS_I212TOAR30ROW_SVE2 +#define HAS_I212TOARGBROW_SVE2 #define HAS_I400TOARGBROW_SVE2 +#define HAS_I410ALPHATOARGBROW_SVE2 +#define HAS_I410TOAR30ROW_SVE2 +#define HAS_I410TOARGBROW_SVE2 #define HAS_I422ALPHATOARGBROW_SVE2 #define HAS_I422TOARGB1555ROW_SVE2 #define HAS_I422TOARGB4444ROW_SVE2 @@ -565,7 +572,9 @@ extern "C" { #define HAS_I444ALPHATOARGBROW_SVE2 #define HAS_I444TOARGBROW_SVE2 #define HAS_NV12TOARGBROW_SVE2 +#define HAS_NV12TORGB24ROW_SVE2 #define HAS_NV21TOARGBROW_SVE2 +#define HAS_NV21TORGB24ROW_SVE2 #define HAS_P210TOAR30ROW_SVE2 #define HAS_P210TOARGBROW_SVE2 #define HAS_P410TOAR30ROW_SVE2 @@ -582,8 +591,10 @@ extern "C" { // The following are available on AArch64 SME platforms: #if !defined(LIBYUV_DISABLE_SME) && defined(CLANG_HAS_SME) && \ defined(__aarch64__) +#define HAS_ARGBMULTIPLYROW_SME #define HAS_I422TOARGBROW_SME #define HAS_I444TOARGBROW_SME +#define HAS_MULTIPLYROW_16_SME #endif // The following are available on AArch64 platforms: @@ -1064,6 +1075,13 @@ void I210AlphaToARGBRow_NEON(const uint16_t* src_y, uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width); +void I210AlphaToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + const uint16_t* src_a, + uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, + int width); void I410AlphaToARGBRow_NEON(const uint16_t* src_y, const uint16_t* src_u, const uint16_t* src_v, @@ -1071,6 +1089,13 @@ void I410AlphaToARGBRow_NEON(const uint16_t* src_y, uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width); +void I410AlphaToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + const uint16_t* src_a, + uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, + int width); void I444ToARGBRow_NEON(const uint8_t* src_y, const uint8_t* src_u, const uint8_t* src_v, @@ -1113,30 +1138,60 @@ void I410ToARGBRow_NEON(const uint16_t* src_y, uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width); +void I410ToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, + int width); void I210ToAR30Row_NEON(const uint16_t* src_y, const uint16_t* src_u, const uint16_t* src_v, uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width); +void I210ToAR30Row_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, + int width); void I410ToAR30Row_NEON(const uint16_t* src_y, const uint16_t* src_u, const uint16_t* src_v, uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width); +void I410ToAR30Row_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, + int width); void I212ToARGBRow_NEON(const uint16_t* src_y, const uint16_t* src_u, const uint16_t* src_v, uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width); +void I212ToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, + int width); void I212ToAR30Row_NEON(const uint16_t* src_y, const uint16_t* src_u, const uint16_t* src_v, uint8_t* rgb_buf, const struct YuvConstants* yuvconstants, int width); +void I212ToAR30Row_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* rgb_buf, + const struct YuvConstants* yuvconstants, + int width); void I422ToARGBRow_NEON(const uint8_t* src_y, const uint8_t* src_u, const uint8_t* src_v, @@ -1279,11 +1334,21 @@ void NV12ToRGB24Row_NEON(const uint8_t* src_y, uint8_t* dst_rgb24, const struct YuvConstants* yuvconstants, int width); +void NV12ToRGB24Row_SVE2(const uint8_t* src_y, + const uint8_t* src_uv, + uint8_t* dst_rgb24, + const struct YuvConstants* yuvconstants, + int width); void NV21ToRGB24Row_NEON(const uint8_t* src_y, const uint8_t* src_vu, uint8_t* dst_rgb24, const struct YuvConstants* yuvconstants, int width); +void NV21ToRGB24Row_SVE2(const uint8_t* src_y, + const uint8_t* src_vu, + uint8_t* dst_rgb24, + const struct YuvConstants* yuvconstants, + int width); void NV21ToYUV24Row_NEON(const uint8_t* src_y, const uint8_t* src_vu, uint8_t* dst_yuv24, @@ -3321,6 +3386,10 @@ void MultiplyRow_16_Any_NEON(const uint16_t* src_ptr, uint16_t* dst_ptr, int scale, int width); +void MultiplyRow_16_SME(const uint16_t* src_y, + uint16_t* dst_y, + int scale, + int width); void DivideRow_16_C(const uint16_t* src_y, uint16_t* dst_y, @@ -4988,6 +5057,10 @@ void ARGBMultiplyRow_Any_NEON(const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* dst_ptr, int width); +void ARGBMultiplyRow_SME(const uint8_t* src_argb, + const uint8_t* src_argb1, + uint8_t* dst_argb, + int width); void ARGBMultiplyRow_MSA(const uint8_t* src_argb0, const uint8_t* src_argb1, uint8_t* dst_argb, @@ -6670,14 +6743,6 @@ void HalfFloatRow_SVE2(const uint16_t* src, uint16_t* dst, float scale, int width); -void HalfFloat1Row_NEON(const uint16_t* src, - uint16_t* dst, - float scale, - int width); -void HalfFloat1Row_Any_NEON(const uint16_t* src_ptr, - uint16_t* dst_ptr, - float param, - int width); void HalfFloat1Row_SVE2(const uint16_t* src, uint16_t* dst, float scale, diff --git a/libfenrir/src/main/jni/animation/libyuv/include/libyuv/scale_row.h b/libfenrir/src/main/jni/animation/libyuv/include/libyuv/scale_row.h index 101ccbf84..3747c318e 100644 --- a/libfenrir/src/main/jni/animation/libyuv/include/libyuv/scale_row.h +++ b/libfenrir/src/main/jni/animation/libyuv/include/libyuv/scale_row.h @@ -116,6 +116,11 @@ extern "C" { #define HAS_SCALEUVROWUP2_BILINEAR_16_NEON #endif +// The following are available on AArch64 Neon platforms: +#if !defined(LIBYUV_DISABLE_NEON) && defined(__aarch64__) +#define HAS_SCALEROWDOWN2_16_NEON +#endif + // The following are available on AArch64 SME platforms: #if !defined(LIBYUV_DISABLE_SME) && defined(CLANG_HAS_SME) && \ defined(__aarch64__) @@ -1423,6 +1428,10 @@ void ScaleRowDown2_NEON(const uint8_t* src_ptr, ptrdiff_t src_stride, uint8_t* dst, int dst_width); +void ScaleRowDown2_16_NEON(const uint16_t* src_ptr, + ptrdiff_t src_stride, + uint16_t* dst, + int dst_width); void ScaleRowDown2_SME(const uint8_t* src_ptr, ptrdiff_t src_stride, uint8_t* dst, @@ -1431,6 +1440,10 @@ void ScaleRowDown2Linear_NEON(const uint8_t* src_ptr, ptrdiff_t src_stride, uint8_t* dst, int dst_width); +void ScaleRowDown2Linear_16_NEON(const uint16_t* src_ptr, + ptrdiff_t src_stride, + uint16_t* dst, + int dst_width); void ScaleRowDown2Linear_SME(const uint8_t* src_ptr, ptrdiff_t src_stride, uint8_t* dst, diff --git a/libfenrir/src/main/jni/animation/libyuv/source/convert_argb.cc b/libfenrir/src/main/jni/animation/libyuv/source/convert_argb.cc index 7a2f7813f..1405d2bea 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/convert_argb.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/convert_argb.cc @@ -977,6 +977,11 @@ int I010ToAR30Matrix(const uint16_t* src_y, } } #endif +#if defined(HAS_I210TOAR30ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I210ToAR30Row = I210ToAR30Row_SVE2; + } +#endif #if defined(HAS_I210TOAR30ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I210ToAR30Row = I210ToAR30Row_Any_SSSE3; @@ -1160,6 +1165,11 @@ int I012ToAR30Matrix(const uint16_t* src_y, I212ToAR30Row = I212ToAR30Row_NEON; } } +#endif +#if defined(HAS_I212TOAR30ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I212ToAR30Row = I212ToAR30Row_SVE2; + } #endif for (y = 0; y < height; ++y) { I212ToAR30Row(src_y, src_u, src_v, dst_ar30, yuvconstants, width); @@ -1211,6 +1221,11 @@ int I210ToAR30Matrix(const uint16_t* src_y, } } #endif +#if defined(HAS_I210TOAR30ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I210ToAR30Row = I210ToAR30Row_SVE2; + } +#endif #if defined(HAS_I210TOAR30ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I210ToAR30Row = I210ToAR30Row_Any_SSSE3; @@ -1374,6 +1389,11 @@ int I410ToAR30Matrix(const uint16_t* src_y, } } #endif +#if defined(HAS_I410TOAR30ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410ToAR30Row = I410ToAR30Row_SVE2; + } +#endif #if defined(HAS_I410TOAR30ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I410ToAR30Row = I410ToAR30Row_Any_SSSE3; @@ -1628,6 +1648,11 @@ int I012ToARGBMatrix(const uint16_t* src_y, I212ToARGBRow = I212ToARGBRow_NEON; } } +#endif +#if defined(HAS_I212TOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I212ToARGBRow = I212ToARGBRow_SVE2; + } #endif for (y = 0; y < height; ++y) { I212ToARGBRow(src_y, src_u, src_v, dst_argb, yuvconstants, width); @@ -1859,6 +1884,11 @@ int I410ToARGBMatrix(const uint16_t* src_y, } } #endif +#if defined(HAS_I410TOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410ToARGBRow = I410ToARGBRow_SVE2; + } +#endif #if defined(HAS_I410TOARGBROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) { I410ToARGBRow = I410ToARGBRow_Any_AVX2; @@ -2259,6 +2289,22 @@ int I420AlphaToARGBMatrix(const uint8_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif for (y = 0; y < height; ++y) { I422AlphaToARGBRow(src_y, src_u, src_v, src_a, dst_argb, yuvconstants, @@ -2407,6 +2453,22 @@ int I422AlphaToARGBMatrix(const uint8_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif for (y = 0; y < height; ++y) { I422AlphaToARGBRow(src_y, src_u, src_v, src_a, dst_argb, yuvconstants, @@ -2537,6 +2599,22 @@ int I444AlphaToARGBMatrix(const uint8_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif for (y = 0; y < height; ++y) { I444AlphaToARGBRow(src_y, src_u, src_v, src_a, dst_argb, yuvconstants, @@ -2725,6 +2803,11 @@ int I010AlphaToARGBMatrix(const uint16_t* src_y, } } #endif +#if defined(HAS_I210ALPHATOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I210AlphaToARGBRow = I210AlphaToARGBRow_SVE2; + } +#endif #if defined(HAS_I210ALPHATOARGBROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I210AlphaToARGBRow = I210AlphaToARGBRow_Any_SSSE3; @@ -2778,6 +2861,22 @@ int I010AlphaToARGBMatrix(const uint16_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif for (y = 0; y < height; ++y) { I210AlphaToARGBRow(src_y, src_u, src_v, src_a, dst_argb, yuvconstants, @@ -2839,6 +2938,11 @@ int I210AlphaToARGBMatrix(const uint16_t* src_y, } } #endif +#if defined(HAS_I210ALPHATOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I210AlphaToARGBRow = I210AlphaToARGBRow_SVE2; + } +#endif #if defined(HAS_I210ALPHATOARGBROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I210AlphaToARGBRow = I210AlphaToARGBRow_Any_SSSE3; @@ -2892,6 +2996,22 @@ int I210AlphaToARGBMatrix(const uint16_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif for (y = 0; y < height; ++y) { I210AlphaToARGBRow(src_y, src_u, src_v, src_a, dst_argb, yuvconstants, @@ -2951,6 +3071,11 @@ int I410AlphaToARGBMatrix(const uint16_t* src_y, } } #endif +#if defined(HAS_I410ALPHATOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410AlphaToARGBRow = I410AlphaToARGBRow_SVE2; + } +#endif #if defined(HAS_I410ALPHATOARGBROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I410AlphaToARGBRow = I410AlphaToARGBRow_Any_SSSE3; @@ -3004,6 +3129,22 @@ int I410AlphaToARGBMatrix(const uint16_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif for (y = 0; y < height; ++y) { I410AlphaToARGBRow(src_y, src_u, src_v, src_a, dst_argb, yuvconstants, @@ -4469,6 +4610,11 @@ int NV12ToRGB24Matrix(const uint8_t* src_y, } } #endif +#if defined(HAS_NV12TORGB24ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + NV12ToRGB24Row = NV12ToRGB24Row_SVE2; + } +#endif #if defined(HAS_NV12TORGB24ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { NV12ToRGB24Row = NV12ToRGB24Row_Any_SSSE3; @@ -4535,6 +4681,11 @@ int NV21ToRGB24Matrix(const uint8_t* src_y, } } #endif +#if defined(HAS_NV21TORGB24ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + NV21ToRGB24Row = NV21ToRGB24Row_SVE2; + } +#endif #if defined(HAS_NV21TORGB24ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { NV21ToRGB24Row = NV21ToRGB24Row_Any_SSSE3; @@ -6937,6 +7088,11 @@ static int I010ToAR30MatrixBilinear(const uint16_t* src_y, } } #endif +#if defined(HAS_I410TOAR30ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410ToAR30Row = I410ToAR30Row_SVE2; + } +#endif #if defined(HAS_I410TOAR30ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I410ToAR30Row = I410ToAR30Row_Any_SSSE3; @@ -7051,6 +7207,11 @@ static int I210ToAR30MatrixLinear(const uint16_t* src_y, } } #endif +#if defined(HAS_I410TOAR30ROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410ToAR30Row = I410ToAR30Row_SVE2; + } +#endif #if defined(HAS_I410TOAR30ROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I410ToAR30Row = I410ToAR30Row_Any_SSSE3; @@ -7152,6 +7313,11 @@ static int I010ToARGBMatrixBilinear(const uint16_t* src_y, } } #endif +#if defined(HAS_I410TOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410ToARGBRow = I410ToARGBRow_SVE2; + } +#endif #if defined(HAS_I410TOARGBROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) { I410ToARGBRow = I410ToARGBRow_Any_AVX2; @@ -7265,6 +7431,11 @@ static int I210ToARGBMatrixLinear(const uint16_t* src_y, } } #endif +#if defined(HAS_I410TOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410ToARGBRow = I410ToARGBRow_SVE2; + } +#endif #if defined(HAS_I410TOARGBROW_AVX2) if (TestCpuFlag(kCpuHasAVX2)) { I410ToARGBRow = I410ToARGBRow_Any_AVX2; @@ -7438,6 +7609,22 @@ static int I420AlphaToARGBMatrixBilinear( ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif #if defined(HAS_SCALEROWUP2_BILINEAR_SSE2) if (TestCpuFlag(kCpuHasSSE2)) { @@ -7653,6 +7840,22 @@ static int I422AlphaToARGBMatrixLinear(const uint8_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif #if defined(HAS_SCALEROWUP2_LINEAR_SSE2) if (TestCpuFlag(kCpuHasSSE2)) { @@ -7754,6 +7957,11 @@ static int I010AlphaToARGBMatrixBilinear( } } #endif +#if defined(HAS_I410ALPHATOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410AlphaToARGBRow = I410AlphaToARGBRow_SVE2; + } +#endif #if defined(HAS_I410ALPHATOARGBROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I410AlphaToARGBRow = I410AlphaToARGBRow_Any_SSSE3; @@ -7807,6 +8015,22 @@ static int I010AlphaToARGBMatrixBilinear( ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif #if defined(HAS_SCALEROWUP2_BILINEAR_12_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { @@ -7930,6 +8154,11 @@ static int I210AlphaToARGBMatrixLinear(const uint16_t* src_y, } } #endif +#if defined(HAS_I410ALPHATOARGBROW_SVE2) + if (TestCpuFlag(kCpuHasSVE2)) { + I410AlphaToARGBRow = I410AlphaToARGBRow_SVE2; + } +#endif #if defined(HAS_I410ALPHATOARGBROW_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { I410AlphaToARGBRow = I410AlphaToARGBRow_Any_SSSE3; @@ -7983,6 +8212,22 @@ static int I210AlphaToARGBMatrixLinear(const uint16_t* src_y, ARGBAttenuateRow = ARGBAttenuateRow_RVV; } #endif +#if defined(HAS_ARGBATTENUATEROW_LSX) + if (TestCpuFlag(kCpuHasLSX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX; + if (IS_ALIGNED(width, 8)) { + ARGBAttenuateRow = ARGBAttenuateRow_LSX; + } + } +#endif +#if defined(HAS_ARGBATTENUATEROW_LASX) + if (TestCpuFlag(kCpuHasLASX)) { + ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX; + if (IS_ALIGNED(width, 16)) { + ARGBAttenuateRow = ARGBAttenuateRow_LASX; + } + } +#endif #if defined(HAS_SCALEROWUP2_LINEAR_12_SSSE3) if (TestCpuFlag(kCpuHasSSSE3)) { diff --git a/libfenrir/src/main/jni/animation/libyuv/source/planar_functions.cc b/libfenrir/src/main/jni/animation/libyuv/source/planar_functions.cc index be67a1ded..cc909eb73 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/planar_functions.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/planar_functions.cc @@ -829,6 +829,11 @@ void ConvertToMSBPlane_16(const uint16_t* src_y, } } #endif +#if defined(HAS_MULTIPLYROW_16_SME) + if (TestCpuFlag(kCpuHasSME)) { + MultiplyRow_16 = MultiplyRow_16_SME; + } +#endif for (y = 0; y < height; ++y) { MultiplyRow_16(src_y, dst_y, scale, width); @@ -3134,6 +3139,11 @@ int ARGBMultiply(const uint8_t* src_argb0, } } #endif +#if defined(HAS_ARGBMULTIPLYROW_SME) + if (TestCpuFlag(kCpuHasSME)) { + ARGBMultiplyRow = ARGBMultiplyRow_SME; + } +#endif #if defined(HAS_ARGBMULTIPLYROW_MSA) if (TestCpuFlag(kCpuHasMSA)) { ARGBMultiplyRow = ARGBMultiplyRow_Any_MSA; @@ -5208,11 +5218,18 @@ int HalfFloatPlane(const uint16_t* src_y, } #endif #if defined(HAS_HALFFLOATROW_NEON) - if (TestCpuFlag(kCpuHasNEON)) { - HalfFloatRow = - scale == 1.0f ? HalfFloat1Row_Any_NEON : HalfFloatRow_Any_NEON; + if (TestCpuFlag(kCpuHasNEON) +#if defined(__arm__) + // When scale is 1/65535 the scale * 2^-112 used to convert is a denormal. + // But when Neon vmul is asked to multiply a normal float by that + // denormal scale, even though the result would have been normal, it + // flushes to zero. The scalar version of vmul supports denormals. + && scale >= 1.0f / 4096.0f +#endif + ) { + HalfFloatRow = HalfFloatRow_Any_NEON; if (IS_ALIGNED(width, 16)) { - HalfFloatRow = scale == 1.0f ? HalfFloat1Row_NEON : HalfFloatRow_NEON; + HalfFloatRow = HalfFloatRow_NEON; } } #endif diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_any.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_any.cc index a61ab817c..70ab046ec 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_any.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_any.cc @@ -1813,25 +1813,7 @@ ANY11P16(HalfFloat1Row_Any_F16C, 15) #endif #ifdef HAS_HALFFLOATROW_NEON -#ifdef __aarch64__ ANY11P16(HalfFloatRow_Any_NEON, HalfFloatRow_NEON, uint16_t, uint16_t, 2, 2, 15) -ANY11P16(HalfFloat1Row_Any_NEON, - HalfFloat1Row_NEON, - uint16_t, - uint16_t, - 2, - 2, - 15) -#else -ANY11P16(HalfFloatRow_Any_NEON, HalfFloatRow_NEON, uint16_t, uint16_t, 2, 2, 7) -ANY11P16(HalfFloat1Row_Any_NEON, - HalfFloat1Row_NEON, - uint16_t, - uint16_t, - 2, - 2, - 7) -#endif #endif #ifdef HAS_HALFFLOATROW_MSA ANY11P16(HalfFloatRow_Any_MSA, HalfFloatRow_MSA, uint16_t, uint16_t, 2, 2, 31) diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_lasx.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_lasx.cc index 6d49aa5e8..734d7ee29 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_lasx.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_lasx.cc @@ -1148,24 +1148,26 @@ void ARGBAttenuateRow_LASX(const uint8_t* src_argb, __m256i b, g, r, a, dst0, dst1; __m256i control = {0x0005000100040000, 0x0007000300060002, 0x0005000100040000, 0x0007000300060002}; + __m256i zero = __lasx_xvldi(0); + __m256i const_add = __lasx_xvldi(0x8ff); for (x = 0; x < len; x++) { DUP2_ARG2(__lasx_xvld, src_argb, 0, src_argb, 32, src0, src1); tmp0 = __lasx_xvpickev_b(src1, src0); tmp1 = __lasx_xvpickod_b(src1, src0); - b = __lasx_xvpackev_b(tmp0, tmp0); - r = __lasx_xvpackod_b(tmp0, tmp0); - g = __lasx_xvpackev_b(tmp1, tmp1); - a = __lasx_xvpackod_b(tmp1, tmp1); - reg0 = __lasx_xvmulwev_w_hu(b, a); - reg1 = __lasx_xvmulwod_w_hu(b, a); - reg2 = __lasx_xvmulwev_w_hu(r, a); - reg3 = __lasx_xvmulwod_w_hu(r, a); - reg4 = __lasx_xvmulwev_w_hu(g, a); - reg5 = __lasx_xvmulwod_w_hu(g, a); - reg0 = __lasx_xvssrani_h_w(reg1, reg0, 24); - reg2 = __lasx_xvssrani_h_w(reg3, reg2, 24); - reg4 = __lasx_xvssrani_h_w(reg5, reg4, 24); + b = __lasx_xvpackev_b(zero, tmp0); + r = __lasx_xvpackod_b(zero, tmp0); + g = __lasx_xvpackev_b(zero, tmp1); + a = __lasx_xvpackod_b(zero, tmp1); + reg0 = __lasx_xvmaddwev_w_hu(const_add, b, a); + reg1 = __lasx_xvmaddwod_w_hu(const_add, b, a); + reg2 = __lasx_xvmaddwev_w_hu(const_add, r, a); + reg3 = __lasx_xvmaddwod_w_hu(const_add, r, a); + reg4 = __lasx_xvmaddwev_w_hu(const_add, g, a); + reg5 = __lasx_xvmaddwod_w_hu(const_add, g, a); + reg0 = __lasx_xvssrani_h_w(reg1, reg0, 8); + reg2 = __lasx_xvssrani_h_w(reg3, reg2, 8); + reg4 = __lasx_xvssrani_h_w(reg5, reg4, 8); reg0 = __lasx_xvshuf_h(control, reg0, reg0); reg2 = __lasx_xvshuf_h(control, reg2, reg2); reg4 = __lasx_xvshuf_h(control, reg4, reg4); diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_lsx.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_lsx.cc index ee74cad9f..50d5ba6a0 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_lsx.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_lsx.cc @@ -1102,24 +1102,26 @@ void ARGBAttenuateRow_LSX(const uint8_t* src_argb, __m128i reg0, reg1, reg2, reg3, reg4, reg5; __m128i b, g, r, a, dst0, dst1; __m128i control = {0x0005000100040000, 0x0007000300060002}; + __m128i zero = __lsx_vldi(0); + __m128i const_add = __lsx_vldi(0x8ff); for (x = 0; x < len; x++) { DUP2_ARG2(__lsx_vld, src_argb, 0, src_argb, 16, src0, src1); tmp0 = __lsx_vpickev_b(src1, src0); tmp1 = __lsx_vpickod_b(src1, src0); - b = __lsx_vpackev_b(tmp0, tmp0); - r = __lsx_vpackod_b(tmp0, tmp0); - g = __lsx_vpackev_b(tmp1, tmp1); - a = __lsx_vpackod_b(tmp1, tmp1); - reg0 = __lsx_vmulwev_w_hu(b, a); - reg1 = __lsx_vmulwod_w_hu(b, a); - reg2 = __lsx_vmulwev_w_hu(r, a); - reg3 = __lsx_vmulwod_w_hu(r, a); - reg4 = __lsx_vmulwev_w_hu(g, a); - reg5 = __lsx_vmulwod_w_hu(g, a); - reg0 = __lsx_vssrani_h_w(reg1, reg0, 24); - reg2 = __lsx_vssrani_h_w(reg3, reg2, 24); - reg4 = __lsx_vssrani_h_w(reg5, reg4, 24); + b = __lsx_vpackev_b(zero, tmp0); + r = __lsx_vpackod_b(zero, tmp0); + g = __lsx_vpackev_b(zero, tmp1); + a = __lsx_vpackod_b(zero, tmp1); + reg0 = __lsx_vmaddwev_w_hu(const_add, b, a); + reg1 = __lsx_vmaddwod_w_hu(const_add, b, a); + reg2 = __lsx_vmaddwev_w_hu(const_add, r, a); + reg3 = __lsx_vmaddwod_w_hu(const_add, r, a); + reg4 = __lsx_vmaddwev_w_hu(const_add, g, a); + reg5 = __lsx_vmaddwod_w_hu(const_add, g, a); + reg0 = __lsx_vssrani_h_w(reg1, reg0, 8); + reg2 = __lsx_vssrani_h_w(reg3, reg2, 8); + reg4 = __lsx_vssrani_h_w(reg5, reg4, 8); reg0 = __lsx_vshuf_h(control, reg0, reg0); reg2 = __lsx_vshuf_h(control, reg2, reg2); reg4 = __lsx_vshuf_h(control, reg4, reg4); diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc index 1211a3727..cfbb364d1 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc @@ -3536,59 +3536,41 @@ void SobelYRow_NEON(const uint8_t* src_y0, } // %y passes a float as a scalar vector for vector * scalar multiply. -// the regoster must be d0 to d15 and indexed with [0] or [1] to access +// the register must be d0 to d15 and indexed with [0] or [1] to access // the float in the first or second float of the d-reg -void HalfFloat1Row_NEON(const uint16_t* src, - uint16_t* dst, - float /*unused*/, - int width) { - asm volatile ( - - "1: \n" - "vld1.8 {q1}, [%0]! \n" // load 8 shorts - "subs %2, %2, #8 \n" // 8 pixels per loop - "vmovl.u16 q2, d2 \n" // 8 int's - "vmovl.u16 q3, d3 \n" - "vcvt.f32.u32 q2, q2 \n" // 8 floats - "vcvt.f32.u32 q3, q3 \n" - "vmul.f32 q2, q2, %y3 \n" // adjust exponent - "vmul.f32 q3, q3, %y3 \n" - "vqshrn.u32 d2, q2, #13 \n" // isolate halffloat - "vqshrn.u32 d3, q3, #13 \n" - "vst1.8 {q1}, [%1]! \n" - "bgt 1b \n" - : "+r"(src), // %0 - "+r"(dst), // %1 - "+r"(width) // %2 - : "w"(1.9259299444e-34f) // %3 - : "cc", "memory", "q1", "q2", "q3"); -} - void HalfFloatRow_NEON(const uint16_t* src, uint16_t* dst, float scale, int width) { - asm volatile ( + asm volatile ( "1: \n" - "vld1.8 {q1}, [%0]! \n" // load 8 shorts - "subs %2, %2, #8 \n" // 8 pixels per loop - "vmovl.u16 q2, d2 \n" // 8 int's - "vmovl.u16 q3, d3 \n" - "vcvt.f32.u32 q2, q2 \n" // 8 floats - "vcvt.f32.u32 q3, q3 \n" - "vmul.f32 q2, q2, %y3 \n" // adjust exponent - "vmul.f32 q3, q3, %y3 \n" - "vqshrn.u32 d2, q2, #13 \n" // isolate halffloat - "vqshrn.u32 d3, q3, #13 \n" - "vst1.8 {q1}, [%1]! \n" + "vld1.16 {q0, q1}, [%0]! \n" // load 16 shorts + "subs %2, %2, #16 \n" // 16 pixels per loop + "vmovl.u16 q8, d0 \n" + "vmovl.u16 q9, d1 \n" + "vmovl.u16 q10, d2 \n" + "vmovl.u16 q11, d3 \n" + "vcvt.f32.u32 q8, q8 \n" + "vcvt.f32.u32 q9, q9 \n" + "vcvt.f32.u32 q10, q10 \n" + "vcvt.f32.u32 q11, q11 \n" + "vmul.f32 q8, q8, %y3 \n" // adjust exponent + "vmul.f32 q9, q9, %y3 \n" + "vmul.f32 q10, q10, %y3 \n" + "vmul.f32 q11, q11, %y3 \n" + "vqshrn.u32 d0, q8, #13 \n" // isolate halffloat + "vqshrn.u32 d1, q9, #13 \n" + "vqshrn.u32 d2, q10, #13 \n" + "vqshrn.u32 d3, q11, #13 \n" + "vst1.16 {q0, q1}, [%1]! \n" // store 16 fp16 "bgt 1b \n" - : "+r"(src), // %0 - "+r"(dst), // %1 - "+r"(width) // %2 + : "+r"(src), // %0 + "+r"(dst), // %1 + "+r"(width) // %2 : "w"(scale * 1.9259299444e-34f) // %3 - : "cc", "memory", "q1", "q2", "q3"); + : "cc", "memory", "q0", "q1", "q8", "q9", "q10", "q11"); } void ByteToFloatRow_NEON(const uint8_t* src, diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_neon64.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_neon64.cc index 4b1ed2c0c..55f686766 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_neon64.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_neon64.cc @@ -4664,37 +4664,6 @@ void SobelYRow_NEON(const uint8_t* src_y0, ); } -// Caveat - rounds float to half float whereas scaling version truncates. -void HalfFloat1Row_NEON(const uint16_t* src, - uint16_t* dst, - float /*unused*/, - int width) { - asm volatile( - "1: \n" - "ldp q0, q1, [%0], #32 \n" // load 16 shorts - "subs %w2, %w2, #16 \n" // 16 pixels per loop - "uxtl v2.4s, v0.4h \n" - "uxtl v4.4s, v1.4h \n" - "uxtl2 v3.4s, v0.8h \n" - "uxtl2 v5.4s, v1.8h \n" - "prfm pldl1keep, [%0, 448] \n" - "scvtf v2.4s, v2.4s \n" - "scvtf v4.4s, v4.4s \n" - "scvtf v3.4s, v3.4s \n" - "scvtf v5.4s, v5.4s \n" - "fcvtn v0.4h, v2.4s \n" - "fcvtn v1.4h, v4.4s \n" - "fcvtn2 v0.8h, v3.4s \n" - "fcvtn2 v1.8h, v5.4s \n" - "stp q0, q1, [%1], #32 \n" // store 16 shorts - "b.gt 1b \n" - : "+r"(src), // %0 - "+r"(dst), // %1 - "+r"(width) // %2 - : - : "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5"); -} - void HalfFloatRow_NEON(const uint16_t* src, uint16_t* dst, float scale, @@ -4717,10 +4686,10 @@ void HalfFloatRow_NEON(const uint16_t* src, "fmul v3.4s, v3.4s, %3.s[0] \n" "fmul v5.4s, v5.4s, %3.s[0] \n" "uqshrn v0.4h, v2.4s, #13 \n" // isolate halffloat - "uqshrn v1.4h, v4.4s, #13 \n" // isolate halffloat + "uqshrn v1.4h, v4.4s, #13 \n" "uqshrn2 v0.8h, v3.4s, #13 \n" "uqshrn2 v1.8h, v5.4s, #13 \n" - "stp q0, q1, [%1], #32 \n" // store 16 shorts + "stp q0, q1, [%1], #32 \n" // store 16 fp16 "b.gt 1b \n" : "+r"(src), // %0 "+r"(dst), // %1 diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_sme.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_sme.cc index 7676d9e64..da94cd7be 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_sme.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_sme.cc @@ -216,6 +216,102 @@ __arm_locally_streaming void I422ToARGBRow_SME( : "cc", "memory", YUVTORGB_SVE_REGS); } +__arm_locally_streaming void MultiplyRow_16_SME(const uint16_t* src_y, + uint16_t* dst_y, + int scale, + int width) { + // Streaming-SVE only, no use of ZA tile. + int vl; + asm volatile( + "cnth %x[vl] \n" + "mov z0.h, %w[scale] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p0.h \n" + "1: \n" + "ld1h {z1.h}, p0/z, [%[src_y]] \n" + "incb %[src_y] \n" + "mul z1.h, z0.h, z1.h \n" + "subs %w[width], %w[width], %w[vl] \n" + "st1h {z1.h}, p0, [%[dst_y]] \n" + "incb %[dst_y] \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p0.h, wzr, %w[width] \n" + "ld1h {z1.h}, p0/z, [%[src_y]] \n" + "mul z1.h, z0.h, z1.h \n" + "st1h {z1.h}, p0, [%[dst_y]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [dst_y] "+r"(dst_y), // %[dst_y] + [width] "+r"(width), // %[width] + [vl] "=&r"(vl) // %[vl] + : [scale] "r"(scale) // %[scale] + : "memory", "cc", "z0", "z1", "p0"); +} + +__arm_locally_streaming void ARGBMultiplyRow_SME(const uint8_t* src_argb, + const uint8_t* src_argb1, + uint8_t* dst_argb, + int width) { + // Streaming-SVE only, no use of ZA tile. + width *= 4; + int vl; + asm volatile( + "cntb %x[vl] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p0.b \n" + "1: \n" + "ld1b {z0.b}, p0/z, [%[src_argb]] \n" + "ld1b {z1.b}, p0/z, [%[src_argb1]] \n" + "incb %[src_argb] \n" + "incb %[src_argb1] \n" + "umullb z2.h, z0.b, z1.b \n" + "umullt z1.h, z0.b, z1.b \n" + "rshrnb z0.b, z2.h, #8 \n" + "rshrnt z0.b, z1.h, #8 \n" + "subs %w[width], %w[width], %w[vl] \n" + "st1b {z0.b}, p0, [%[dst_argb]] \n" + "incb %[dst_argb] \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p0.b, wzr, %w[width] \n" + "ld1b {z0.b}, p0/z, [%[src_argb]] \n" + "ld1b {z1.b}, p0/z, [%[src_argb1]] \n" + "umullb z2.h, z0.b, z1.b \n" + "umullt z1.h, z0.b, z1.b \n" + "rshrnb z0.b, z2.h, #8 \n" + "rshrnt z0.b, z1.h, #8 \n" + "st1b {z0.b}, p0, [%[dst_argb]] \n" + + "99: \n" + : [src_argb] "+r"(src_argb), // %[src_argb] + [src_argb1] "+r"(src_argb1), // %[src_argb1] + [dst_argb] "+r"(dst_argb), // %[dst_argb] + [width] "+r"(width), // %[width] + [vl] "=&r"(vl) // %[vl] + : + : "memory", "cc", "z0", "z1", "z2", "p0", "p1"); +} + #endif // !defined(LIBYUV_DISABLE_SME) && defined(CLANG_HAS_SME) && // defined(__aarch64__) diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_sve.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_sve.cc index bfa49d9c2..51b22ddd7 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_sve.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_sve.cc @@ -68,33 +68,50 @@ extern "C" { // We need a different predicate for the UV component to handle the tail. // If there is a single element remaining then we want to load one Y element // but two UV elements. -#define READNV_SVE \ - "ld1b {z0.h}, p1/z, [%[src_y]] \n" /* Y0Y0 */ \ - "ld1b {z1.h}, p2/z, [%[src_uv]] \n" /* U0V0 or V0U0 */ \ - "inch %[src_y] \n" \ - "inch %[src_uv] \n" \ +#define READNV_SVE_2X \ + "ld1b {z0.b}, p1/z, [%[src_y]] \n" /* Y0Y0 */ \ + "ld1b {z2.b}, p2/z, [%[src_uv]] \n" /* U0V0 or V0U0 */ \ + "incb %[src_y] \n" \ + "incb %[src_uv] \n" \ "prfm pldl1keep, [%[src_y], 448] \n" \ "prfm pldl1keep, [%[src_uv], 256] \n" \ - "trn1 z0.b, z0.b, z0.b \n" /* YYYY */ \ - "tbl z1.b, {z1.b}, z22.b \n" /* UVUV */ + "trn2 z1.b, z0.b, z0.b \n" /* YYYY */ \ + "trn1 z0.b, z0.b, z0.b \n" /* YYYY */ #define READI210_SVE \ "ld1h {z3.h}, p1/z, [%[src_y]] \n" \ - "lsl z0.h, z3.h, #6 \n" \ - "usra z0.h, z3.h, #4 \n" \ "ld1h {z1.s}, p1/z, [%[src_u]] \n" \ "ld1h {z2.s}, p1/z, [%[src_v]] \n" \ "incb %[src_y] \n" \ "inch %[src_u] \n" \ "inch %[src_v] \n" \ + "lsl z0.h, z3.h, #6 \n" \ + "trn1 z1.h, z1.h, z1.h \n" \ + "trn1 z2.h, z2.h, z2.h \n" \ "prfm pldl1keep, [%[src_y], 448] \n" \ "prfm pldl1keep, [%[src_u], 128] \n" \ "prfm pldl1keep, [%[src_v], 128] \n" \ - "trn1 z1.h, z1.h, z1.h \n" \ - "trn1 z2.h, z2.h, z2.h \n" \ + "usra z0.h, z3.h, #4 \n" \ "uqshrnb z1.b, z1.h, #2 \n" \ "uqshrnb z2.b, z2.h, #2 \n" +#define READI212_SVE \ + "ld1h {z3.h}, p1/z, [%[src_y]] \n" \ + "ld1h {z1.s}, p1/z, [%[src_u]] \n" \ + "ld1h {z2.s}, p1/z, [%[src_v]] \n" \ + "incb %[src_y] \n" \ + "inch %[src_u] \n" \ + "inch %[src_v] \n" \ + "lsl z0.h, z3.h, #4 \n" \ + "trn1 z1.h, z1.h, z1.h \n" \ + "trn1 z2.h, z2.h, z2.h \n" \ + "prfm pldl1keep, [%[src_y], 448] \n" \ + "prfm pldl1keep, [%[src_u], 128] \n" \ + "prfm pldl1keep, [%[src_v], 128] \n" \ + "usra z0.h, z3.h, #8 \n" \ + "uqshrnb z1.b, z1.h, #4 \n" \ + "uqshrnb z2.b, z2.h, #4 \n" + #define READP210_SVE \ "ld1h {z0.h}, p1/z, [%[src_y]] \n" \ "ld1h {z1.h}, p2/z, [%[src_uv]] \n" \ @@ -104,6 +121,21 @@ extern "C" { "prfm pldl1keep, [%[src_uv], 256] \n" \ "tbl z1.b, {z1.b}, z22.b \n" +#define READI410_SVE \ + "ld1h {z3.h}, p1/z, [%[src_y]] \n" \ + "lsl z0.h, z3.h, #6 \n" \ + "usra z0.h, z3.h, #4 \n" \ + "ld1h {z1.h}, p1/z, [%[src_u]] \n" \ + "ld1h {z2.h}, p1/z, [%[src_v]] \n" \ + "incb %[src_y] \n" \ + "incb %[src_u] \n" \ + "incb %[src_v] \n" \ + "prfm pldl1keep, [%[src_y], 448] \n" \ + "prfm pldl1keep, [%[src_u], 128] \n" \ + "prfm pldl1keep, [%[src_v], 128] \n" \ + "uqshrnb z1.b, z1.h, #2 \n" \ + "uqshrnb z2.b, z2.h, #2 \n" + // We need different predicates for the UV components since we are reading // 32-bit (pairs of UV) elements rather than 16-bit Y elements. #define READP410_SVE \ @@ -193,6 +225,27 @@ extern "C" { "uqsub z18.h, z18.h, z27.h \n" /* R0 */ \ "uqsub z22.h, z22.h, z27.h \n" /* R1 */ +#define NVTORGB_SVE_2X(bt_u, bt_v) \ + "umulh z0.h, z24.h, z0.h \n" /* Y0 */ \ + "umulh z1.h, z24.h, z1.h \n" /* Y1 */ \ + "umull" #bt_u " z6.h, z30.b, z2.b \n" \ + "umull" #bt_u " z4.h, z28.b, z2.b \n" /* DB */ \ + "umull" #bt_v " z5.h, z29.b, z2.b \n" /* DR */ \ + "umlal" #bt_v " z6.h, z31.b, z2.b \n" /* DG */ \ + \ + "add z17.h, z0.h, z26.h \n" /* G0 */ \ + "add z21.h, z1.h, z26.h \n" /* G1 */ \ + "add z16.h, z0.h, z4.h \n" /* B0 */ \ + "add z20.h, z1.h, z4.h \n" /* B1 */ \ + "add z18.h, z0.h, z5.h \n" /* R0 */ \ + "add z22.h, z1.h, z5.h \n" /* R1 */ \ + "uqsub z17.h, z17.h, z6.h \n" /* G0 */ \ + "uqsub z21.h, z21.h, z6.h \n" /* G1 */ \ + "uqsub z16.h, z16.h, z25.h \n" /* B0 */ \ + "uqsub z20.h, z20.h, z25.h \n" /* B1 */ \ + "uqsub z18.h, z18.h, z27.h \n" /* R0 */ \ + "uqsub z22.h, z22.h, z27.h \n" /* R1 */ + #define I400TORGB_SVE \ "umulh z18.h, z24.h, z0.h \n" /* Y */ \ "movprfx z16, z18 \n" \ @@ -210,6 +263,13 @@ extern "C" { "uqshrnt z16.b, z17.h, #6 \n" /* BG */ \ "trn1 z17.b, z18.b, z19.b \n" /* RA */ +#define RGBATOARGB8_SVE \ + /* Inputs: B: z16.h, G: z17.h, R: z18.h, A: z19.h */ \ + "uqshrnb z16.b, z16.h, #6 \n" /* B0 */ \ + "uqshrnt z16.b, z17.h, #6 \n" /* BG */ \ + "uqshrnb z17.b, z18.h, #6 \n" /* R0 */ \ + "uqshrnt z17.b, z19.h, #2 \n" /* RA */ + #define RGBTOARGB8_SVE_2X \ /* Inputs: B: z16.h, G: z17.h, R: z18.h, A: z19.b */ \ "uqshrnb z16.b, z16.h, #6 \n" /* B0 */ \ @@ -745,33 +805,36 @@ void I422AlphaToARGBRow_SVE2(const uint8_t* src_y, : "cc", "memory", YUVTORGB_SVE_REGS); } -static inline void NVToARGBRow_SVE2(const uint8_t* src_y, - const uint8_t* src_uv, - uint8_t* dst_argb, - const struct YuvConstants* yuvconstants, - int width, - uint32_t nv_uv_start, - uint32_t nv_uv_step) { +void NV12ToARGBRow_SVE2(const uint8_t* src_y, + const uint8_t* src_uv, + uint8_t* dst_argb, + const struct YuvConstants* yuvconstants, + int width) { + uint32_t nv_u_start = 0xff00U; + uint32_t nv_u_step = 0x0002U; + uint32_t nv_v_start = 0xff01U; + uint32_t nv_v_step = 0x0002U; uint64_t vl; - asm("cnth %0" : "=r"(vl)); + asm("cntb %0" : "=r"(vl)); int width_last_y = width & (vl - 1); int width_last_uv = width_last_y + (width_last_y & 1); asm volatile( "ptrue p0.b \n" // YUVTORGB_SVE_SETUP - "index z22.s, %w[nv_uv_start], %w[nv_uv_step] \n" "dup z19.b, #255 \n" // A + "index z7.h, %w[nv_u_start], %w[nv_u_step] \n" + "index z23.h, %w[nv_v_start], %w[nv_v_step] \n" "subs %w[width], %w[width], %w[vl] \n" "b.lt 2f \n" // Run bulk of computation with an all-true predicate to avoid predicate // generation overhead. - "ptrue p1.h \n" - "ptrue p2.h \n" + "ptrue p1.b \n" + "ptrue p2.b \n" "1: \n" // - READNV_SVE NVTORGB_SVE RGBTOARGB8_SVE + READNV_SVE_2X NVTORGB_SVE_2X(b, t) RGBTOARGB8_SVE_2X "subs %w[width], %w[width], %w[vl] \n" - "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + "st4b {z16.b, z17.b, z18.b, z19.b}, p1, [%[dst_argb]] \n" "add %[dst_argb], %[dst_argb], %[vl], lsl #2 \n" "b.ge 1b \n" @@ -780,11 +843,10 @@ static inline void NVToARGBRow_SVE2(const uint8_t* src_y, "b.eq 99f \n" // Calculate a predicate for the final iteration to deal with the tail. - "3: \n" - "whilelt p1.h, wzr, %w[width_last_y] \n" - "whilelt p2.h, wzr, %w[width_last_uv] \n" // - READNV_SVE NVTORGB_SVE RGBTOARGB8_SVE - "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + "whilelt p1.b, wzr, %w[width_last_y] \n" + "whilelt p2.b, wzr, %w[width_last_uv] \n" // + READNV_SVE_2X NVTORGB_SVE_2X(b, t) RGBTOARGB8_SVE_2X + "st4b {z16.b, z17.b, z18.b, z19.b}, p1, [%[dst_argb]] \n" "99: \n" : [src_y] "+r"(src_y), // %[src_y] @@ -794,33 +856,193 @@ static inline void NVToARGBRow_SVE2(const uint8_t* src_y, : [vl] "r"(vl), // %[vl] [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] - [nv_uv_start] "r"(nv_uv_start), // %[nv_uv_start] - [nv_uv_step] "r"(nv_uv_step), // %[nv_uv_step] + [nv_u_start] "r"(nv_u_start), // %[nv_u_start] + [nv_u_step] "r"(nv_u_step), // %[nv_u_step] + [nv_v_start] "r"(nv_v_start), // %[nv_v_start] + [nv_v_step] "r"(nv_v_step), // %[nv_v_step] [width_last_y] "r"(width_last_y), // %[width_last_y] [width_last_uv] "r"(width_last_uv) // %[width_last_uv] : "cc", "memory", YUVTORGB_SVE_REGS, "p2"); } -void NV12ToARGBRow_SVE2(const uint8_t* src_y, - const uint8_t* src_uv, - uint8_t* dst_argb, - const struct YuvConstants* yuvconstants, - int width) { - uint32_t nv_uv_start = 0x02000200U; - uint32_t nv_uv_step = 0x04040404U; - NVToARGBRow_SVE2(src_y, src_uv, dst_argb, yuvconstants, width, nv_uv_start, - nv_uv_step); -} - void NV21ToARGBRow_SVE2(const uint8_t* src_y, const uint8_t* src_vu, uint8_t* dst_argb, const struct YuvConstants* yuvconstants, int width) { - uint32_t nv_uv_start = 0x00020002U; - uint32_t nv_uv_step = 0x04040404U; - NVToARGBRow_SVE2(src_y, src_vu, dst_argb, yuvconstants, width, nv_uv_start, - nv_uv_step); + uint32_t nv_u_start = 0xff01U; + uint32_t nv_u_step = 0x0002U; + uint32_t nv_v_start = 0xff00U; + uint32_t nv_v_step = 0x0002U; + uint64_t vl; + asm("cntb %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + int width_last_uv = width_last_y + (width_last_y & 1); + asm volatile( + "ptrue p0.b \n" // + YUVTORGB_SVE_SETUP + "dup z19.b, #255 \n" // A + "index z7.h, %w[nv_u_start], %w[nv_u_step] \n" + "index z23.h, %w[nv_v_start], %w[nv_v_step] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.b \n" + "ptrue p2.b \n" + "1: \n" // + READNV_SVE_2X NVTORGB_SVE_2X(t, b) RGBTOARGB8_SVE_2X + "subs %w[width], %w[width], %w[vl] \n" + "st4b {z16.b, z17.b, z18.b, z19.b}, p1, [%[dst_argb]] \n" + "add %[dst_argb], %[dst_argb], %[vl], lsl #2 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.b, wzr, %w[width_last_y] \n" + "whilelt p2.b, wzr, %w[width_last_uv] \n" // + READNV_SVE_2X NVTORGB_SVE_2X(t, b) RGBTOARGB8_SVE_2X + "st4b {z16.b, z17.b, z18.b, z19.b}, p1, [%[dst_argb]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_uv] "+r"(src_vu), // %[src_vu] + [dst_argb] "+r"(dst_argb), // %[dst_argb] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [nv_u_start] "r"(nv_u_start), // %[nv_u_start] + [nv_u_step] "r"(nv_u_step), // %[nv_u_step] + [nv_v_start] "r"(nv_v_start), // %[nv_v_start] + [nv_v_step] "r"(nv_v_step), // %[nv_v_step] + [width_last_y] "r"(width_last_y), // %[width_last_y] + [width_last_uv] "r"(width_last_uv) // %[width_last_uv] + : "cc", "memory", YUVTORGB_SVE_REGS, "p2"); +} + +void NV12ToRGB24Row_SVE2(const uint8_t* src_y, + const uint8_t* src_uv, + uint8_t* dst_rgb24, + const struct YuvConstants* yuvconstants, + int width) { + uint32_t nv_u_start = 0xff00U; + uint32_t nv_u_step = 0x0002U; + uint32_t nv_v_start = 0xff01U; + uint32_t nv_v_step = 0x0002U; + uint64_t vl; + asm("cntb %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + int width_last_uv = width_last_y + (width_last_y & 1); + asm volatile( + "ptrue p0.b \n" // + YUVTORGB_SVE_SETUP + "dup z19.b, #255 \n" // A + "index z7.h, %w[nv_u_start], %w[nv_u_step] \n" + "index z23.h, %w[nv_v_start], %w[nv_v_step] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.b \n" + "ptrue p2.b \n" + "1: \n" // + READNV_SVE_2X NVTORGB_SVE_2X(b, t) RGBTOARGB8_SVE_2X + "subs %w[width], %w[width], %w[vl] \n" + "st3b {z16.b, z17.b, z18.b}, p1, [%[dst_rgb24]] \n" + "incb %[dst_rgb24], all, mul #3 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.b, wzr, %w[width_last_y] \n" + "whilelt p2.b, wzr, %w[width_last_uv] \n" // + READNV_SVE_2X NVTORGB_SVE_2X(b, t) RGBTOARGB8_SVE_2X + "st3b {z16.b, z17.b, z18.b}, p1, [%[dst_rgb24]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_uv] "+r"(src_uv), // %[src_uv] + [dst_rgb24] "+r"(dst_rgb24), // %[dst_rgb24] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [nv_u_start] "r"(nv_u_start), // %[nv_u_start] + [nv_u_step] "r"(nv_u_step), // %[nv_u_step] + [nv_v_start] "r"(nv_v_start), // %[nv_v_start] + [nv_v_step] "r"(nv_v_step), // %[nv_v_step] + [width_last_y] "r"(width_last_y), // %[width_last_y] + [width_last_uv] "r"(width_last_uv) // %[width_last_uv] + : "cc", "memory", YUVTORGB_SVE_REGS, "p2"); +} + +void NV21ToRGB24Row_SVE2(const uint8_t* src_y, + const uint8_t* src_vu, + uint8_t* dst_rgb24, + const struct YuvConstants* yuvconstants, + int width) { + uint32_t nv_u_start = 0xff01U; + uint32_t nv_u_step = 0x0002U; + uint32_t nv_v_start = 0xff00U; + uint32_t nv_v_step = 0x0002U; + uint64_t vl; + asm("cntb %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + int width_last_uv = width_last_y + (width_last_y & 1); + asm volatile( + "ptrue p0.b \n" // + YUVTORGB_SVE_SETUP + "dup z19.b, #255 \n" // A + "index z7.h, %w[nv_u_start], %w[nv_u_step] \n" + "index z23.h, %w[nv_v_start], %w[nv_v_step] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.b \n" + "ptrue p2.b \n" + "1: \n" // + READNV_SVE_2X NVTORGB_SVE_2X(t, b) RGBTOARGB8_SVE_2X + "subs %w[width], %w[width], %w[vl] \n" + "st3b {z16.b, z17.b, z18.b}, p1, [%[dst_rgb24]] \n" + "incb %[dst_rgb24], all, mul #3 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.b, wzr, %w[width_last_y] \n" + "whilelt p2.b, wzr, %w[width_last_uv] \n" // + READNV_SVE_2X NVTORGB_SVE_2X(t, b) RGBTOARGB8_SVE_2X + "st3b {z16.b, z17.b, z18.b}, p1, [%[dst_rgb24]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_uv] "+r"(src_vu), // %[src_vu] + [dst_rgb24] "+r"(dst_rgb24), // %[dst_rgb24] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [nv_u_start] "r"(nv_u_start), // %[nv_u_start] + [nv_u_step] "r"(nv_u_step), // %[nv_u_step] + [nv_v_start] "r"(nv_v_start), // %[nv_v_start] + [nv_v_step] "r"(nv_v_step), // %[nv_v_step] + [width_last_y] "r"(width_last_y), // %[width_last_y] + [width_last_uv] "r"(width_last_uv) // %[width_last_uv] + : "cc", "memory", YUVTORGB_SVE_REGS, "p2"); } // Dot-product constants are stored as four-tuples with the two innermost @@ -1848,7 +2070,6 @@ void I210ToARGBRow_SVE2(const uint16_t* src_y, uint64_t vl; asm("cnth %0" : "=r"(vl)); int width_last_y = width & (vl - 1); - width_last_y = width_last_y == 0 ? vl : width_last_y; asm volatile( "ptrue p0.b \n" YUVTORGB_SVE_SETUP "dup z19.b, #255 \n" // A @@ -1887,6 +2108,102 @@ void I210ToARGBRow_SVE2(const uint16_t* src_y, : "cc", "memory", YUVTORGB_SVE_REGS); } +void I210AlphaToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + const uint16_t* src_a, + uint8_t* dst_argb, + const struct YuvConstants* yuvconstants, + int width) { + uint64_t vl; + asm("cnth %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + asm volatile( + "ptrue p0.b \n" YUVTORGB_SVE_SETUP + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.h \n" + "1: \n" READI210_SVE + "ld1h {z19.h}, p1/z, [%[src_a]] \n" I4XXTORGB_SVE + "incb %[src_a] \n" RGBATOARGB8_SVE + "subs %w[width], %w[width], %w[vl] \n" + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + "add %[dst_argb], %[dst_argb], %[vl], lsl #2 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.h, wzr, %w[width_last_y] \n" READI210_SVE + "ld1h {z19.h}, p1/z, [%[src_a]] \n" // + I4XXTORGB_SVE RGBATOARGB8_SVE + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_u] "+r"(src_u), // %[src_u] + [src_v] "+r"(src_v), // %[src_v] + [src_a] "+r"(src_a), // %[src_a] + [dst_argb] "+r"(dst_argb), // %[dst_argb] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [width_last_y] "r"(width_last_y) // %[width_last_y] + : "cc", "memory", YUVTORGB_SVE_REGS); +} + +void I210ToAR30Row_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* dst_ar30, + const struct YuvConstants* yuvconstants, + int width) { + uint64_t vl; + asm("cnth %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + uint16_t limit = 0x3ff0; + asm volatile( + "ptrue p0.b \n" YUVTORGB_SVE_SETUP + "dup z23.h, %w[limit] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.h \n" + "1: \n" // + READI210_SVE I4XXTORGB_SVE STOREAR30_SVE + "subs %w[width], %w[width], %w[vl] \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.h, wzr, %w[width_last_y] \n" // + READI210_SVE I4XXTORGB_SVE STOREAR30_SVE + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_u] "+r"(src_u), // %[src_u] + [src_v] "+r"(src_v), // %[src_v] + [dst_ar30] "+r"(dst_ar30), // %[dst_ar30] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [width_last_y] "r"(width_last_y), // %[width_last_y] + [limit] "r"(limit) // %[limit] + : "cc", "memory", YUVTORGB_SVE_REGS); +} + // P210 has 10 bits in msb of 16 bit NV12 style layout. void P210ToARGBRow_SVE2(const uint16_t* src_y, const uint16_t* src_uv, @@ -1896,7 +2213,6 @@ void P210ToARGBRow_SVE2(const uint16_t* src_y, uint64_t vl; asm("cnth %0" : "=r"(vl)); int width_last_y = width & (vl - 1); - width_last_y = width_last_y == 0 ? vl : width_last_y; int width_last_uv = width_last_y + (width_last_y & 1); uint32_t nv_uv_start = 0x03010301U; uint32_t nv_uv_step = 0x04040404U; @@ -1951,7 +2267,6 @@ void P210ToAR30Row_SVE2(const uint16_t* src_y, uint64_t vl; asm("cnth %0" : "=r"(vl)); int width_last_y = width & (vl - 1); - width_last_y = width_last_y == 0 ? vl : width_last_y; int width_last_uv = width_last_y + (width_last_y & 1); uint32_t nv_uv_start = 0x03010301U; uint32_t nv_uv_step = 0x04040404U; @@ -1998,6 +2313,150 @@ void P210ToAR30Row_SVE2(const uint16_t* src_y, : "cc", "memory", YUVTORGB_SVE_REGS); } +void I410ToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* dst_argb, + const struct YuvConstants* yuvconstants, + int width) { + uint64_t vl; + asm("cnth %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + asm volatile( + "ptrue p0.b \n" YUVTORGB_SVE_SETUP + "dup z19.b, #255 \n" // A + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.h \n" + "1: \n" // + READI410_SVE I4XXTORGB_SVE RGBTOARGB8_SVE + "subs %w[width], %w[width], %w[vl] \n" + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + "add %[dst_argb], %[dst_argb], %[vl], lsl #2 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.h, wzr, %w[width_last_y] \n" // + READI410_SVE I4XXTORGB_SVE RGBTOARGB8_SVE + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_u] "+r"(src_u), // %[src_u] + [src_v] "+r"(src_v), // %[src_v] + [dst_argb] "+r"(dst_argb), // %[dst_argb] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [width_last_y] "r"(width_last_y) // %[width_last_y] + : "cc", "memory", YUVTORGB_SVE_REGS); +} + +void I410AlphaToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + const uint16_t* src_a, + uint8_t* dst_argb, + const struct YuvConstants* yuvconstants, + int width) { + uint64_t vl; + asm("cnth %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + asm volatile( + "ptrue p0.b \n" YUVTORGB_SVE_SETUP + "cmp %w[width], %w[vl] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.h \n" + "1: \n" READI410_SVE + "ld1h {z19.h}, p1/z, [%[src_a]] \n" I4XXTORGB_SVE + "incb %[src_a] \n" RGBATOARGB8_SVE + "subs %w[width], %w[width], %w[vl] \n" + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + "add %[dst_argb], %[dst_argb], %[vl], lsl #2 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.h, wzr, %w[width_last_y] \n" READI410_SVE + "ld1h {z19.h}, p1/z, [%[src_a]] \n" // + I4XXTORGB_SVE RGBATOARGB8_SVE + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_u] "+r"(src_u), // %[src_u] + [src_v] "+r"(src_v), // %[src_v] + [src_a] "+r"(src_a), // %[src_a] + [dst_argb] "+r"(dst_argb), // %[dst_argb] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [width_last_y] "r"(width_last_y) // %[width_last_y] + : "cc", "memory", YUVTORGB_SVE_REGS); +} + +void I410ToAR30Row_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* dst_ar30, + const struct YuvConstants* yuvconstants, + int width) { + uint64_t vl; + asm("cnth %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + uint16_t limit = 0x3ff0; + asm volatile( + "ptrue p0.b \n" YUVTORGB_SVE_SETUP + "dup z23.h, %w[limit] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.h \n" + "1: \n" // + READI410_SVE I4XXTORGB_SVE STOREAR30_SVE + "subs %w[width], %w[width], %w[vl] \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.h, wzr, %w[width_last_y] \n" // + READI410_SVE I4XXTORGB_SVE STOREAR30_SVE + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_u] "+r"(src_u), // %[src_u] + [src_v] "+r"(src_v), // %[src_v] + [dst_ar30] "+r"(dst_ar30), // %[dst_argb] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [width_last_y] "r"(width_last_y), // %[width_last_y] + [limit] "r"(limit) // %[limit] + : "cc", "memory", YUVTORGB_SVE_REGS); +} + void P410ToARGBRow_SVE2(const uint16_t* src_y, const uint16_t* src_uv, uint8_t* dst_argb, @@ -2006,7 +2465,6 @@ void P410ToARGBRow_SVE2(const uint16_t* src_y, uint64_t vl; asm("cnth %0" : "=r"(vl)); int width_last_y = width & (vl - 1); - width_last_y = width_last_y == 0 ? vl : width_last_y; asm volatile( "ptrue p0.b \n" YUVTORGB_SVE_SETUP "dup z19.b, #255 \n" // A @@ -2057,7 +2515,6 @@ void P410ToAR30Row_SVE2(const uint16_t* src_y, uint64_t vl; asm("cnth %0" : "=r"(vl)); int width_last_y = width & (vl - 1); - width_last_y = width_last_y == 0 ? vl : width_last_y; uint16_t limit = 0x3ff0; asm volatile( "ptrue p0.b \n" YUVTORGB_SVE_SETUP @@ -2100,6 +2557,99 @@ void P410ToAR30Row_SVE2(const uint16_t* src_y, : "cc", "memory", YUVTORGB_SVE_REGS); } +void I212ToAR30Row_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* dst_ar30, + const struct YuvConstants* yuvconstants, + int width) { + uint64_t vl; + asm("cnth %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + uint16_t limit = 0x3ff0; + asm volatile( + "ptrue p0.b \n" YUVTORGB_SVE_SETUP + "dup z23.h, %w[limit] \n" + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.h \n" + "1: \n" // + READI212_SVE I4XXTORGB_SVE STOREAR30_SVE + "subs %w[width], %w[width], %w[vl] \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.h, wzr, %w[width_last_y] \n" // + READI212_SVE I4XXTORGB_SVE STOREAR30_SVE + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_u] "+r"(src_u), // %[src_u] + [src_v] "+r"(src_v), // %[src_v] + [dst_ar30] "+r"(dst_ar30), // %[dst_ar30] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [width_last_y] "r"(width_last_y), // %[width_last_y] + [limit] "r"(limit) // %[limit] + : "cc", "memory", YUVTORGB_SVE_REGS); +} + +void I212ToARGBRow_SVE2(const uint16_t* src_y, + const uint16_t* src_u, + const uint16_t* src_v, + uint8_t* dst_argb, + const struct YuvConstants* yuvconstants, + int width) { + uint64_t vl; + asm("cnth %0" : "=r"(vl)); + int width_last_y = width & (vl - 1); + asm volatile( + "ptrue p0.b \n" YUVTORGB_SVE_SETUP + "dup z19.b, #255 \n" // A + "subs %w[width], %w[width], %w[vl] \n" + "b.lt 2f \n" + + // Run bulk of computation with an all-true predicate to avoid predicate + // generation overhead. + "ptrue p1.h \n" + "1: \n" // + READI212_SVE I4XXTORGB_SVE RGBTOARGB8_SVE + "subs %w[width], %w[width], %w[vl] \n" + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + "add %[dst_argb], %[dst_argb], %[vl], lsl #2 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[width], %w[width], %w[vl] \n" + "b.eq 99f \n" + + // Calculate a predicate for the final iteration to deal with the tail. + "whilelt p1.h, wzr, %w[width_last_y] \n" // + READI212_SVE I4XXTORGB_SVE RGBTOARGB8_SVE + "st2h {z16.h, z17.h}, p1, [%[dst_argb]] \n" + + "99: \n" + : [src_y] "+r"(src_y), // %[src_y] + [src_u] "+r"(src_u), // %[src_u] + [src_v] "+r"(src_v), // %[src_v] + [dst_argb] "+r"(dst_argb), // %[dst_argb] + [width] "+r"(width) // %[width] + : [vl] "r"(vl), // %[vl] + [kUVCoeff] "r"(&yuvconstants->kUVCoeff), // %[kUVCoeff] + [kRGBCoeffBias] "r"(&yuvconstants->kRGBCoeffBias), // %[kRGBCoeffBias] + [width_last_y] "r"(width_last_y) // %[width_last_y] + : "cc", "memory", YUVTORGB_SVE_REGS); +} + #endif // !defined(LIBYUV_DISABLE_SVE) && defined(__aarch64__) #ifdef __cplusplus diff --git a/libfenrir/src/main/jni/animation/libyuv/source/scale.cc b/libfenrir/src/main/jni/animation/libyuv/source/scale.cc index 661224166..8b8315043 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/scale.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/scale.cc @@ -188,8 +188,9 @@ static void ScalePlaneDown2_16(int src_width, #if defined(HAS_SCALEROWDOWN2_16_NEON) if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(dst_width, 16)) { - ScaleRowDown2 = - filtering ? ScaleRowDown2Box_16_NEON : ScaleRowDown2_16_NEON; + ScaleRowDown2 = filtering == kFilterNone ? ScaleRowDown2_16_NEON + : filtering == kFilterLinear ? ScaleRowDown2Linear_16_NEON + : ScaleRowDown2Box_16_NEON; } #endif #if defined(HAS_SCALEROWDOWN2_16_SSE2) diff --git a/libfenrir/src/main/jni/animation/libyuv/source/scale_neon64.cc b/libfenrir/src/main/jni/animation/libyuv/source/scale_neon64.cc index c125c6c09..69c51b1bb 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/scale_neon64.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/scale_neon64.cc @@ -1354,6 +1354,71 @@ void ScaleARGBFilterCols_NEON(uint8_t* dst_argb, #undef SCALE_ARGB_FILTER_COLS_STEP_ADDR +void ScaleRowDown2_16_NEON(const uint16_t* src_ptr, + ptrdiff_t src_stride, + uint16_t* dst, + int dst_width) { + (void)src_stride; + asm volatile( + "subs %w[dst_width], %w[dst_width], #32 \n" + "b.lt 2f \n" + + "1: \n" + "ldp q0, q1, [%[src_ptr]] \n" + "ldp q2, q3, [%[src_ptr], #32] \n" + "ldp q4, q5, [%[src_ptr], #64] \n" + "ldp q6, q7, [%[src_ptr], #96] \n" + "add %[src_ptr], %[src_ptr], #128 \n" + "uzp2 v0.8h, v0.8h, v1.8h \n" + "uzp2 v1.8h, v2.8h, v3.8h \n" + "uzp2 v2.8h, v4.8h, v5.8h \n" + "uzp2 v3.8h, v6.8h, v7.8h \n" + "subs %w[dst_width], %w[dst_width], #32 \n" // 32 elems per iteration. + "stp q0, q1, [%[dst_ptr]] \n" + "stp q2, q3, [%[dst_ptr], #32] \n" + "add %[dst_ptr], %[dst_ptr], #64 \n" + "b.ge 1b \n" + + "2: \n" + "adds %w[dst_width], %w[dst_width], #32 \n" + "b.eq 99f \n" + + "ldp q0, q1, [%[src_ptr]] \n" + "ldp q2, q3, [%[src_ptr], #32] \n" + "uzp2 v0.8h, v0.8h, v1.8h \n" + "uzp2 v1.8h, v2.8h, v3.8h \n" + "stp q0, q1, [%[dst_ptr]] \n" + + "99: \n" + : [src_ptr] "+r"(src_ptr), // %[src_ptr] + [dst_ptr] "+r"(dst), // %[dst_ptr] + [dst_width] "+r"(dst_width) // %[dst_width] + : + : "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"); +} + +void ScaleRowDown2Linear_16_NEON(const uint16_t* src_ptr, + ptrdiff_t src_stride, + uint16_t* dst, + int dst_width) { + (void)src_stride; + asm volatile( + "1: \n" + "ld2 {v0.8h, v1.8h}, [%[src_ptr]], #32 \n" + "ld2 {v2.8h, v3.8h}, [%[src_ptr]], #32 \n" + "subs %w[dst_width], %w[dst_width], #16 \n" + "urhadd v0.8h, v0.8h, v1.8h \n" + "urhadd v1.8h, v2.8h, v3.8h \n" + "prfm pldl1keep, [%[src_ptr], 448] \n" + "stp q0, q1, [%[dst_ptr]], #32 \n" + "b.gt 1b \n" + : [src_ptr] "+r"(src_ptr), // %[src_ptr] + [dst_ptr] "+r"(dst), // %[dst_ptr] + [dst_width] "+r"(dst_width) // %[dst_width] + : + : "memory", "cc", "v0", "v1", "v2", "v3"); +} + // Read 16x2 average down and write 8x1. void ScaleRowDown2Box_16_NEON(const uint16_t* src_ptr, ptrdiff_t src_stride, diff --git a/libfenrir/src/main/jni/animation/thorvg/inc/colorreplace.h b/libfenrir/src/main/jni/animation/thorvg/inc/colorreplace.h index 4033898f0..a9727801a 100644 --- a/libfenrir/src/main/jni/animation/thorvg/inc/colorreplace.h +++ b/libfenrir/src/main/jni/animation/thorvg/inc/colorreplace.h @@ -1,5 +1,6 @@ #ifndef _ColorReplace_H_ #define _ColorReplace_H_ + #include #include @@ -15,6 +16,7 @@ namespace tvg { s = 0.f; v = 0.f; } + float h; float s; float v; @@ -27,7 +29,8 @@ namespace tvg { g = 0; b = 0; } - rgb_model(int32_t color) { + + explicit rgb_model(int32_t color) { #ifdef BIG_ENDIAN_COLORS r = ((color >> 16) & 0xff); g = ((color >> 8) & 0xff); @@ -38,88 +41,90 @@ namespace tvg { b = ((color >> 16) & 0xff); #endif } + rgb_model(uint8_t iR, uint8_t iG, uint8_t iB) { r = iR; g = iG; b = iB; } + rgb_model(float fR, float fG, float fB) { - r = (uint8_t)(fR * 255) & 0xff; - g = (uint8_t)(fG * 255) & 0xff; - b = (uint8_t)(fB * 255) & 0xff; + r = (uint8_t) (fR * 255) & 0xff; + g = (uint8_t) (fG * 255) & 0xff; + b = (uint8_t) (fB * 255) & 0xff; } - void toFloat(float& fR, float& fG, float& fB) const { - fR = (float)r / 255.0f; - fG = (float)g / 255.0f; - fB = (float)b / 255.0f; + + void toFloat(float &fR, float &fG, float &fB) const { + fR = (float) r / 255.0f; + fG = (float) g / 255.0f; + fB = (float) b / 255.0f; } - void toColors(uint8_t& iR, uint8_t& iG, uint8_t& iB) const { + + void toColors(uint8_t &iR, uint8_t &iG, uint8_t &iB) const { iR = r; iG = g; iB = b; } - int32_t toInt32() const { + + [[nodiscard]] int32_t toInt32() const { #ifdef BIG_ENDIAN_COLORS return (int32_t)((r << 16) | (g << 8) | b); #else - return (int32_t)(r | (g << 8) | (b << 16)); + return (int32_t) (r | (g << 8) | (b << 16)); #endif } + uint8_t r; uint8_t g; uint8_t b; }; - static inline uint8_t clamp(float v) - { + + static inline uint8_t clamp(float v) { if (v < 0.f) return 0; if (v > 255.f) return 255; - return (uint8_t)v; + return (uint8_t) v; } - static inline float clampf(float v) - { + static inline float clampf(float v) { if (v < 0.f) return 0.f; if (v > 1.f) return 1.f; return v; } - static hsv_model rgb2hsv(const rgb_model& color) - { - hsv_model out; - float min, max, delta; + + static hsv_model rgb2hsv(const rgb_model &color) { + hsv_model out; + float min, max, delta; min = color.r < color.g ? color.r : color.g; - min = min < color.b ? min : color.b; + min = min < (float) color.b ? min : (float) color.b; max = color.r > color.g ? color.r : color.g; - max = max > color.b ? max : color.b; + max = max > (float) color.b ? max : (float) color.b; out.v = max / 255.0f; delta = max - min; - if (delta < 0.00001f) - { + if (delta < 0.00001f) { out.s = 0; out.h = 0; return out; } if (max > 0.0f) { out.s = (delta / max); - } - else { + } else { out.s = 0.0f; out.h = NAN; return out; } - if (color.r >= max) - out.h = (color.g - color.b) / delta; + if ((float) color.r >= max) + out.h = ((float) color.g - (float) color.b) / delta; + else if ((float) color.g >= max) + out.h = 2.0f + ((float) color.b - (float) color.r) / delta; else - if (color.g >= max) - out.h = 2.0f + (color.b - color.r) / delta; - else - out.h = 4.0f + (color.r - color.g) / delta; + out.h = 4.0f + ((float) color.r - (float) color.g) / delta; out.h *= 60.0f; @@ -129,8 +134,8 @@ namespace tvg { return out; } - static rgb_model change_hsv_c(const rgb_model& color, const float fHue, const float fSat, const float fVal) - { + static rgb_model + change_hsv_c(const rgb_model &color, const float fHue, const float fSat, const float fVal) { rgb_model out; const float cosA = fSat * cos(fHue * 3.14159265f / 180); const float sinA = fSat * sin(fHue * 3.14159265f / 180); @@ -144,13 +149,17 @@ namespace tvg { const float minus = aThirdOfOneMinusCosA - rootThirdTimesSinA; float matrix[3][3] = { - { cosA + oneMinusCosA / 3.0f , minus , plus }, - { plus , cosA + aThirdOfOneMinusCosA , minus }, - { minus , plus , cosA + aThirdOfOneMinusCosA } + {cosA + oneMinusCosA / 3.0f, minus, plus}, + {plus, cosA + aThirdOfOneMinusCosA, minus}, + {minus, plus, cosA + + aThirdOfOneMinusCosA} }; - out.r = clamp((color.r * matrix[0][0] + color.g * matrix[0][1] + color.b * matrix[0][2]) * fVal); - out.g = clamp((color.r * matrix[1][0] + color.g * matrix[1][1] + color.b * matrix[1][2]) * fVal); - out.b = clamp((color.r * matrix[2][0] + color.g * matrix[2][1] + color.b * matrix[2][2]) * fVal); + out.r = clamp(((float) color.r * matrix[0][0] + (float) color.g * matrix[0][1] + + (float) color.b * matrix[0][2]) * fVal); + out.g = clamp(((float) color.r * matrix[1][0] + (float) color.g * matrix[1][1] + + (float) color.b * matrix[1][2]) * fVal); + out.b = clamp(((float) color.r * matrix[2][0] + (float) color.g * matrix[2][1] + + (float) color.b * matrix[2][2]) * fVal); return out; } @@ -159,26 +168,28 @@ namespace tvg { ColorReplace() { useCustomColorsLottieOffset = false; } - ColorReplace& registerCustomColorLottie(uint32_t color, uint32_t replace) { + + ColorReplace ®isterCustomColorLottie(int32_t color, int32_t replace) { customColorsTableLottie[color] = replace; return *this; } - ColorReplace& setUseCustomColorsLottieOffset() { + + ColorReplace &setUseCustomColorsLottieOffset() { useCustomColorsLottieOffset = true; return *this; } - void getCustomColorLottie32(int32_t& r, int32_t& g, int32_t& b) { - uint8_t sr = (uint8_t)r; - uint8_t sg = (uint8_t)g; - uint8_t sb = (uint8_t)b; + void getCustomColorLottie32(int32_t &r, int32_t &g, int32_t &b) { + auto sr = (uint8_t) r; + auto sg = (uint8_t) g; + auto sb = (uint8_t) b; getCustomColorLottie(sr, sg, sb); r = sr; g = sg; b = sb; } - void getCustomColorLottie(uint8_t& r, uint8_t& g, uint8_t& b) { + void getCustomColorLottie(uint8_t &r, uint8_t &g, uint8_t &b) { if (!customColorsTableLottie.empty()) { if (useCustomColorsLottieOffset) { switchColorModel(r, g, b); @@ -192,21 +203,18 @@ namespace tvg { } } - std::unique_ptr _ptr() { - return std::make_unique(*this); - } - - std::mapcustomColorsTableLottie; + std::map customColorsTableLottie; bool useCustomColorsLottieOffset; private: - inline void switchColorModel(uint8_t& r, uint8_t& g, uint8_t& b) { + inline void switchColorModel(uint8_t &r, uint8_t &g, uint8_t &b) { int32_t from = customColorsTableLottie.begin()->first; int32_t to = customColorsTableLottie.begin()->second; hsv_model frm = rgb2hsv(rgb_model(from)); hsv_model tom = rgb2hsv(rgb_model(to)); hsv_model clrr = rgb2hsv(rgb_model(r, g, b)); - change_hsv_c(rgb_model(r, g, b), tom.h - frm.h, clampf(clrr.s + (tom.s - frm.s)), clampf(clrr.v + (tom.v - frm.v))).toColors(r, g, b); + change_hsv_c(rgb_model(r, g, b), tom.h - frm.h, clampf(clrr.s + (tom.s - frm.s)), + clampf(clrr.v + (tom.v - frm.v))).toColors(r, g, b); } }; } diff --git a/libfenrir/src/main/jni/animation/thorvg/inc/thorvg.h b/libfenrir/src/main/jni/animation/thorvg/inc/thorvg.h index 7bdd9f72d..7cb0ec4e8 100644 --- a/libfenrir/src/main/jni/animation/thorvg/inc/thorvg.h +++ b/libfenrir/src/main/jni/animation/thorvg/inc/thorvg.h @@ -1,8 +1,8 @@ #ifndef _THORVG_H_ #define _THORVG_H_ +#include #include -#include #include #ifdef TVG_API @@ -377,7 +377,7 @@ class TVG_API Paint * @param[in] target The paint of the target object. * @param[in] method The method used to mask the source object with the target. */ - Result mask(std::unique_ptr target, MaskMethod method) noexcept; + Result mask(Paint* target, MaskMethod method) noexcept; /** * @brief Clip the drawing region of the paint object. @@ -391,7 +391,7 @@ class TVG_API Paint * @note @p clipper only supports the Shape type. * @note Experimental API */ - Result clip(std::unique_ptr clipper) noexcept; + Result clip(Paint* clipper) noexcept; /** * @brief Sets the blending method for the paint object. @@ -449,6 +449,53 @@ class TVG_API Paint */ MaskMethod mask(const Paint** target) const noexcept; + /** + * @brief Increment the reference count for the Paint instance. + * + * This method increases the reference count of the Paint object, allowing shared ownership and control over its lifetime. + * + * @return The updated reference count after the increment by 1. + * + * @warning Please ensure that each call to ref() is paired with a corresponding call to unref() to prevent a dangling instance. + * + * @see Paint::unref() + * @see Paint::refCnt() + * + * @since 1.0 + */ + uint8_t ref() noexcept; + + /** + * @brief Decrement the reference count for the Paint instance. + * + * This method decreases the reference count of the Paint object by 1. + * If the reference count reaches zero and the @p free flag is set to true, the Paint instance is automatically deleted. + * + * @param[in] free Flag indicating whether to delete the Paint instance when the reference count reaches zero. + * + * @return The updated reference count after the decrement. + * + * @see Paint::ref() + * @see Paint::refCnt() + * + * @since 1.0 + */ + uint8_t unref(bool free = true) noexcept; + + /** + * @brief Retrieve the current reference count of the Paint instance. + * + * This method provides the current reference count, allowing the user to check the shared ownership state of the Paint object. + * + * @return The current reference count of the Paint instance. + * + * @see Paint::ref() + * @see Paint::unref() + * + * @since 1.0 + */ + uint8_t refCnt() const noexcept; + /** * @brief Returns the ID value of this class. * @@ -591,32 +638,40 @@ class TVG_API Canvas virtual ~Canvas(); /** - * @brief Returns the list of the paints that currently held by the Canvas. + * @brief Returns the list of paints currently held by the Canvas. * - * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. + * This function provides a list of paint nodes, allowing users to access scene-graph information. * * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). - * @see Canvas::sync() + * @see Canvas::push() + * @see Canvas::clear() * - * @note Experimental API + * @warning This is read-only. Do not modify the list. + * @note 1.0 */ - std::list& paints() noexcept; + const std::list& paints() const noexcept; /** - * @brief Passes drawing elements to the Canvas using Paint objects. + * @brief Adds a paint object to the root scene. * - * Only pushed paints in the canvas will be drawing targets. - * They are retained by the canvas until you call Canvas::clear(). + * This function appends a paint object to root scene of the canvas. If the optional @p at + * is provided, the new paint object will be inserted immediately before the specified + * paint object in the root scene. If @p at is @c nullptr, the paint object will be added + * to the end of the root scene. * - * @param[in] paint A Paint object to be drawn. + * @param[in] target A pointer to the Paint object to be added into the root scene. + * This parameter must not be @c nullptr. + * @param[in] at A pointer to an existing Paint object in the root scene before which + * the new paint object will be added. If @c nullptr, the new + * paint object is added to the end of the root scene. The default is @c nullptr. * - * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument. + * @note The ownership of the @p paint object is transferred to the canvas upon addition. + * @note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering. * - * @note The rendering order of the paints is the same as the order as they were pushed into the canvas. Consider sorting the paints before pushing them if you intend to use layering. * @see Canvas::paints() * @see Canvas::clear() */ - virtual Result push(std::unique_ptr paint) noexcept; + Result push(Paint* target, Paint* at = nullptr) noexcept; /** * @brief Clear the internal canvas resources that used for the drawing. @@ -631,7 +686,24 @@ class TVG_API Canvas * @see Canvas::push() * @see Canvas::paints() */ - virtual Result clear(bool paints = true, bool buffer = true) noexcept; + Result clear(bool paints = true, bool buffer = true) noexcept; + + /** + * @brief Removes a paint object or all paint objects from the root scene. + * + * This function removes a specified paint object from the root scene. If no paint + * object is specified (i.e., the default @c nullptr is used), the function + * performs to clear all paints from the root scene. + * + * @param[in] paint A pointer to the Paint object to be removed from the root scene. + * If @c nullptr, remove all the paints from the root scene. + * + * @see Canvas::push() + * @see Canvas::paints() + * + * @since 1.0 + */ + Result remove(Paint* paint = nullptr) noexcept; /** * @brief Request the canvas to update the paint objects. @@ -643,7 +715,7 @@ class TVG_API Canvas * * @note The Update behavior can be asynchronous if the assigned thread number is greater than zero. */ - virtual Result update(Paint* paint = nullptr) noexcept; + Result update(Paint* paint = nullptr) noexcept; /** * @brief Requests the canvas to draw the Paint objects. @@ -651,7 +723,7 @@ class TVG_API Canvas * @note Drawing can be asynchronous if the assigned thread number is greater than zero. To guarantee the drawing is done, call sync() afterwards. * @see Canvas::sync() */ - virtual Result draw() noexcept; + Result draw() noexcept; /** * @brief Sets the drawing region in the canvas. @@ -673,7 +745,7 @@ class TVG_API Canvas * @note When resetting the target, the viewport will also be reset to the target size. * @since 0.15 */ - virtual Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept; + Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept; /** * @brief Guarantees that drawing task is finished. @@ -685,7 +757,7 @@ class TVG_API Canvas * * @see Canvas::draw() */ - virtual Result sync() noexcept; + Result sync() noexcept; _TVG_DECLARE_PRIVATE(Canvas); }; @@ -740,7 +812,7 @@ class TVG_API LinearGradient final : public Fill * * @return A new LinearGradient object. */ - static std::unique_ptr gen() noexcept; + static LinearGradient* gen() noexcept; /** * @brief Returns the ID value of this class. @@ -812,7 +884,7 @@ class TVG_API RadialGradient final : public Fill * * @return A new RadialGradient object. */ - static std::unique_ptr gen() noexcept; + static RadialGradient* gen() noexcept; /** * @brief Returns the ID value of this class. @@ -986,7 +1058,7 @@ class TVG_API Shape final : public Paint * * @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument. */ - Result strokeFill(std::unique_ptr f) noexcept; + Result strokeFill(Fill* f) noexcept; /** * @brief Sets the dash pattern of the stroke. @@ -1070,7 +1142,7 @@ class TVG_API Shape final : public Paint * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. */ - Result fill(std::unique_ptr f) noexcept; + Result fill(Fill* f) noexcept; /** * @brief Sets the fill rule for the Shape object. @@ -1196,7 +1268,7 @@ class TVG_API Shape final : public Paint * * @return A new Shape object. */ - static std::unique_ptr gen() noexcept; + static Shape* gen() noexcept; /** * @brief Returns the ID value of this class. @@ -1242,7 +1314,7 @@ class TVG_API Picture final : public Paint * @note The Load behavior can be asynchronous if the assigned thread number is greater than zero. * @see Initializer::init() */ - Result load(const char* filename, std::unique_ptr colorReplacement = nullptr) noexcept; + Result load(const char* filename, ColorReplace *colorReplacement = nullptr) noexcept; /** * @brief Loads a picture data from a memory block of a given size. @@ -1262,10 +1334,10 @@ class TVG_API Picture final : public Paint * * @warning It's the user responsibility to release the @p data memory. * - * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out. + * @note If you are unsure about the MIME type, you can provide an empty value like @c nullptr, and thorvg will attempt to figure it out. * @since 0.5 */ - Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath = "", bool copy = false, std::unique_ptr colorReplacement = nullptr) noexcept; + Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath = nullptr, bool copy = false, ColorReplace *colorReplacement = nullptr) noexcept; /** * @brief Resizes the picture content to the given width and height. @@ -1326,7 +1398,7 @@ class TVG_API Picture final : public Paint * * @return A new Picture object. */ - static std::unique_ptr gen() noexcept; + static Picture* gen() noexcept; /** * @brief Returns the ID value of this class. @@ -1361,44 +1433,55 @@ class TVG_API Scene final : public Paint ~Scene(); /** - * @brief Passes drawing elements to the Scene using Paint objects. + * @brief Inserts a paint object to the scene. * - * Only the paints pushed into the scene will be the drawn targets. - * The paints are retained by the scene until Scene::clear() is called. + * This function appends a paint object to the scene. If the optional @p at + * is provided, the new paint object will be inserted immediately before the specified + * paint object in the scene. If @p at is @c nullptr, the paint object will be added + * to the end of the scene. * - * @param[in] paint A Paint object to be drawn. + * @param[in] target A pointer to the Paint object to be added into the scene. + * This parameter must not be @c nullptr. + * @param[in] at A pointer to an existing Paint object in the scene before which + * the new paint object will be added. If @c nullptr, the new + * paint object is added to the end of the scene. The default is @c nullptr. * + * @note The ownership of the @p paint object is transferred to the scene upon addition. * @note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering. * @see Scene::paints() - * @see Scene::clear() + * @see Scene:remove() */ - Result push(std::unique_ptr paint) noexcept; + Result push(Paint* target, Paint* at = nullptr) noexcept; /** - * @brief Returns the list of the paints that currently held by the Scene. + * @brief Returns the list of paints currently held by the Scene. * - * This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree. + * This function provides a list of paint nodes, allowing users to access scene-graph information. * - * @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync(). - * @see Canvas::sync() - * @see Scene::push(std::unique_ptr paint) - * @see Scene::clear() + * @see Scene::push() + * @see Scene:remove() * - * @note Experimental API + * @warning This is read-only. Do not modify the list. + * @since 1.0 */ - std::list& paints() noexcept; + const std::list& paints() const noexcept; /** - * @brief Sets the total number of the paints pushed into the scene to be zero. - * Depending on the value of the @p free argument, the paints are freed or not. + * @brief Removes a paint object or all paint objects from the scene. * - * @param[in] free If @c true, the memory occupied by paints is deallocated, otherwise it is not. + * This function removes a specified paint object from the scene. If no paint + * object is specified (i.e., the default @c nullptr is used), the function + * performs to clear all paints from the scene. * - * @warning If you don't free the paints they become dangled. They are supposed to be reused, otherwise you are responsible for their lives. Thus please use the @p free argument only when you know how it works, otherwise it's not recommended. + * @param[in] paint A pointer to the Paint object to be removed from the scene. + * If @c nullptr, remove all the paints from the scene. * - * @since 0.2 + * @see Scene::push() + * @see Scene::paints() + * + * @since 1.0 */ - Result clear(bool free = true) noexcept; + Result remove(Paint* paint = nullptr) noexcept; /** * @brief Apply a post-processing effect to the scene. @@ -1419,7 +1502,7 @@ class TVG_API Scene final : public Paint * * @return A new Scene object. */ - static std::unique_ptr gen() noexcept; + static Scene* gen() noexcept; /** * @brief Returns the ID value of this class. @@ -1502,7 +1585,7 @@ class TVG_API Text final : public Paint * * @since 0.15 */ - Result fill(std::unique_ptr f) noexcept; + Result fill(Fill* f) noexcept; /** * @brief Loads a scalable font data (ttf) from a file. @@ -1542,7 +1625,7 @@ class TVG_API Text final : public Paint * @warning It's the user responsibility to release the @p data memory. * * @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data. - * @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out. + * @note If you are unsure about the MIME type, you can provide an empty value like @c nullptr, and thorvg will attempt to figure it out. * @see Text::font(const char* name, float size, const char* style) * * @note 0.15 @@ -1572,7 +1655,7 @@ class TVG_API Text final : public Paint * * @since 0.15 */ - static std::unique_ptr gen() noexcept; + static Text* gen() noexcept; /** * @brief Returns the ID value of this class. @@ -1660,7 +1743,7 @@ class TVG_API SwCanvas final : public Canvas * @brief Creates a new SwCanvas object. * @return A new SwCanvas object. */ - static std::unique_ptr gen() noexcept; + static SwCanvas* gen() noexcept; _TVG_DECLARE_PRIVATE(SwCanvas); }; @@ -1687,6 +1770,7 @@ class TVG_API GlCanvas final : public Canvas * @param[in] id The GL target ID, usually indicating the FBO ID. A value of @c 0 specifies the main surface. * @param[in] w The width (in pixels) of the raster image. * @param[in] h The height (in pixels) of the raster image. + * @param[in] cs Specifies how the pixel values should be interpreted. Currently, it only allows @c ColorSpace::ABGR8888S as @c GL_RGBA8. * * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced. * @retval Result::NonSupport In case the gl engine is not supported. @@ -1694,10 +1778,9 @@ class TVG_API GlCanvas final : public Canvas * @see Canvas::viewport() * @see Canvas::sync() * - * @note Currently, this only allows the GL_RGBA8 color space format. * @note Experimental API */ - Result target(int32_t id, uint32_t w, uint32_t h) noexcept; + Result target(int32_t id, uint32_t w, uint32_t h, ColorSpace cs) noexcept; /** * @brief Creates a new GlCanvas object. @@ -1706,7 +1789,7 @@ class TVG_API GlCanvas final : public Canvas * * @since 0.14 */ - static std::unique_ptr gen() noexcept; + static GlCanvas* gen() noexcept; _TVG_DECLARE_PRIVATE(GlCanvas); }; @@ -1729,11 +1812,13 @@ class TVG_API WgCanvas final : public Canvas /** * @brief Sets the drawing target for the rasterization. * - * @param[in] instance WGPUInstance, context for all other wgpu objects. - * @param[in] surface WGPUSurface, handle to a presentable surface. - * @param[in] w The width of the surface. - * @param[in] h The height of the surface. * @param[in] device WGPUDevice, a desired handle for the wgpu device. If it is @c nullptr, ThorVG will assign an appropriate device internally. + * @param[in] instance WGPUInstance, context for all other wgpu objects. + * @param[in] target Either WGPUSurface or WGPUTexture, serving as handles to a presentable surface or texture. + * @param[in] w The width of the target. + * @param[in] h The height of the target. + * @param[in] cs Specifies how the pixel values should be interpreted. Currently, it only allows @c ColorSpace::ABGR8888S as @c WGPUTextureFormat_RGBA8Unorm. + * @param[in] type @c 0: surface, @c 1: texture are used as pesentable target. * * @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced. * @retval Result::NonSupport In case the wg engine is not supported. @@ -1743,7 +1828,7 @@ class TVG_API WgCanvas final : public Canvas * @see Canvas::viewport() * @see Canvas::sync() */ - Result target(void* instance, void* surface, uint32_t w, uint32_t h, void* device = nullptr) noexcept; + Result target(void* device, void* instance, void* target, uint32_t w, uint32_t h, ColorSpace cs, int type = 0) noexcept; /** * @brief Creates a new WgCanvas object. @@ -1752,7 +1837,7 @@ class TVG_API WgCanvas final : public Canvas * * @since 0.15 */ - static std::unique_ptr gen() noexcept; + static WgCanvas* gen() noexcept; _TVG_DECLARE_PRIVATE(WgCanvas); }; @@ -1935,7 +2020,7 @@ class TVG_API Animation * @return A new Animation object. * */ - static std::unique_ptr gen() noexcept; + static Animation* gen() noexcept; _TVG_DECLARE_PRIVATE(Animation); }; @@ -1970,7 +2055,7 @@ class TVG_API Saver final * * @note Experimental API */ - Result background(std::unique_ptr paint) noexcept; + Result background(Paint* paint) noexcept; /** * @brief Exports the given @p paint data to the given @p path @@ -1992,7 +2077,7 @@ class TVG_API Saver final * * @since 0.5 */ - Result save(std::unique_ptr paint, const char* filename, uint32_t quality = 100) noexcept; + Result save(Paint* paint, const char* filename, uint32_t quality = 100) noexcept; /** * @brief Export the provided animation data to the specified file path. @@ -2015,7 +2100,7 @@ class TVG_API Saver final * * @note Experimental API */ - Result save(std::unique_ptr animation, const char* filename, uint32_t quality = 100, uint32_t fps = 0) noexcept; + Result save(Animation* animation, const char* filename, uint32_t quality = 100, uint32_t fps = 0) noexcept; /** * @brief Guarantees that the saving task is finished. @@ -2038,7 +2123,7 @@ class TVG_API Saver final * * @since 0.5 */ - static std::unique_ptr gen() noexcept; + static Saver* gen() noexcept; _TVG_DECLARE_PRIVATE(Saver); }; @@ -2071,7 +2156,7 @@ class TVG_API Accessor final * * @note Experimental API */ - Result set(const Picture* picture, std::function func, void* data) noexcept; + Result set(Picture* picture, std::function func, void* data) noexcept; /** * @brief Generate a unique ID (hash key) from a given name. @@ -2094,34 +2179,11 @@ class TVG_API Accessor final * * @return A new Accessor object. */ - static std::unique_ptr gen() noexcept; + static Accessor* gen() noexcept; _TVG_DECLARE_PRIVATE(Accessor); }; - -/** - * @brief The cast() function is a utility function used to cast a 'Paint' to type 'T'. - * @since 0.11 - */ -template -std::unique_ptr cast(Paint* paint) -{ - return std::unique_ptr(static_cast(paint)); -} - - -/** - * @brief The cast() function is a utility function used to cast a 'Fill' to type 'T'. - * @since 0.11 - */ -template -std::unique_ptr cast(Fill* fill) -{ - return std::unique_ptr(static_cast(fill)); -} - - /** @}*/ } //namespace diff --git a/libfenrir/src/main/jni/animation/thorvg/src/common/tvgInlist.h b/libfenrir/src/main/jni/animation/thorvg/src/common/tvgInlist.h index fc99ae3d1..98a7415c7 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/common/tvgInlist.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/common/tvgInlist.h @@ -36,6 +36,11 @@ struct Inlist T* head = nullptr; T* tail = nullptr; + ~Inlist() + { + free(); + } + void free() { while (head) { @@ -106,6 +111,14 @@ struct Inlist } }; +#define INLIST_FOREACH(inlist, cur) \ + for (auto cur = inlist.head; cur; cur = cur->next) + +#define INLIST_SAFE_FOREACH(inlist, cur) \ + auto cur = inlist.head; \ + auto next = cur ? cur->next : nullptr; \ + for (; cur; cur = next, next = (cur ? cur->next : nullptr)) + } #endif // _TVG_INLIST_H_ diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/thorvg_lottie.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/thorvg_lottie.h index 2fe88f012..1518bc391 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/thorvg_lottie.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/thorvg_lottie.h @@ -86,7 +86,7 @@ class TVG_API LottieAnimation final : public Animation * * @since 0.15 */ - static std::unique_ptr gen() noexcept; + static LottieAnimation* gen() noexcept; }; } //namespace diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieAnimation.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieAnimation.cpp index 4ae6396e5..a73b52cc6 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieAnimation.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieAnimation.cpp @@ -82,7 +82,7 @@ const char* LottieAnimation::marker(uint32_t idx) noexcept } -unique_ptr LottieAnimation::gen() noexcept +LottieAnimation* LottieAnimation::gen() noexcept { - return unique_ptr(new LottieAnimation); + return new LottieAnimation; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.cpp index e56bea292..7211b2008 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.cpp @@ -217,8 +217,6 @@ void LottieBuilder::updateGroup(LottieGroup* parent, LottieObject** child, float contexts.back(new RenderContext(*ctx, propagator, group->mergeable())); updateChildren(group, frameNo, contexts); - - contexts.free(); } @@ -274,7 +272,7 @@ void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** chi auto stroke = static_cast(*child); ctx->merging = nullptr; - ctx->propagator->strokeFill(unique_ptr(stroke->fill(frameNo, exps))); + ctx->propagator->strokeFill(stroke->fill(frameNo, exps)); _updateStroke(static_cast(stroke), frameNo, ctx, exps); } @@ -302,7 +300,7 @@ void LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child ctx->merging = nullptr; //TODO: reuse the fill instance? - ctx->propagator->fill(unique_ptr(fill->fill(frameNo, exps))); + ctx->propagator->fill(fill->fill(frameNo, exps)); ctx->propagator->fill(fill->rule); if (ctx->propagator->strokeWidth() > 0) ctx->propagator->order(true); @@ -320,7 +318,7 @@ static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx) ctx->merging = static_cast(ctx->propagator->duplicate()); } - parent->scene->push(cast(ctx->merging)); + parent->scene->push(ctx->merging); return true; } @@ -366,12 +364,12 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx) //push repeat shapes in order. if (repeater->inorder) { for (auto shape = shapes.begin(); shape < shapes.end(); ++shape) { - parent->scene->push(cast(*shape)); + parent->scene->push(*shape); propagators.push(*shape); } } else if (!shapes.empty()) { for (auto shape = shapes.end() - 1; shape >= shapes.begin(); --shape) { - parent->scene->push(cast(*shape)); + parent->scene->push(*shape); propagators.push(*shape); } } @@ -691,6 +689,7 @@ static void _updateStar(TVG_UNUSED LottieGroup* parent, LottiePolyStar* star, Ma auto intermediate = Shape::gen(); roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, outerRoundness, hasRoundness); offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); + delete(intermediate); } else { roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, outerRoundness, hasRoundness); } @@ -777,6 +776,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr auto intermediate = Shape::gen(); roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, 0.0f, false); offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); + delete(intermediate); } else { roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, 0.0f, false); } @@ -963,7 +963,7 @@ void LottieBuilder::updatePrecomp(LottieComposition* comp, LottieLayer* precomp, //clip the layer viewport auto clipper = precomp->statical.pooling(true); clipper->transform(precomp->cache.matrix); - precomp->scene->clip(cast(clipper)); + precomp->scene->clip(clipper); } @@ -971,14 +971,14 @@ void LottieBuilder::updateSolid(LottieLayer* layer) { auto solidFill = layer->statical.pooling(true); solidFill->opacity(layer->cache.opacity); - layer->scene->push(cast(solidFill)); + layer->scene->push(solidFill); } void LottieBuilder::updateImage(LottieGroup* layer) { auto image = static_cast(layer->children.first()); - layer->scene->push(tvg::cast(image->pooling(true))); + layer->scene->push(image->pooling(true)); } @@ -992,9 +992,10 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) if (!p || !text->font) return; auto scale = doc.size; - Point cursor = {0.0f, 0.0f}; + Point cursor{}; + //TODO: Need to revise to alloc scene / textgroup when they are really necessary auto scene = Scene::gen(); - auto textGroup = Scene::gen(); + Scene* textGroup = Scene::gen(); int line = 0; int space = 0; auto lineSpacing = 0.0f; @@ -1017,14 +1018,15 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) else if (doc.justify == 2) layout.x += (doc.bbox.size.x * 0.5f) - (cursor.x * 0.5f * scale); //center aligned //new text group, single scene based on text-grouping - scene->push(std::move(textGroup)); + scene->push(textGroup); textGroup = Scene::gen(); textGroup->translate(cursor.x, cursor.y); scene->translate(layout.x, layout.y); scene->scale(scale); - layer->scene->push(std::move(scene)); + layer->scene->push(scene); + scene = nullptr; if (*p == '\0') break; ++p; @@ -1043,21 +1045,34 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) ++space; if (textGrouping == LottieText::AlignOption::Group::Word) { //new text group, single scene for each word - scene->push(std::move(textGroup)); + scene->push(textGroup); textGroup = Scene::gen(); textGroup->translate(cursor.x, cursor.y); } } + /* all lowercase letters are converted to uppercase in the "t" text field, making the "ca" value irrelevant, thus AllCaps is nothing to do. + So only convert lowercase letters to uppercase (for 'SmallCaps' an extra scaling factor applied) */ + auto code = p; + auto capScale = 1.0f; + char capCode; + if ((unsigned char)(p[0]) < 0x80 && doc.caps) { + if (*p >= 'a' && *p <= 'z') { + capCode = *p + 'A' - 'a'; + code = &capCode; + if (doc.caps == 2) capScale = 0.7f; + } + } + //find the glyph bool found = false; for (auto g = text->font->chars.begin(); g < text->font->chars.end(); ++g) { auto glyph = *g; //draw matched glyphs - if (!strncmp(glyph->code, p, glyph->len)) { + if (!strncmp(glyph->code, code, glyph->len)) { if (textGrouping == LottieText::AlignOption::Group::Chars || textGrouping == LottieText::AlignOption::Group::All) { //new text group, single scene for each characters - scene->push(std::move(textGroup)); + scene->push(textGroup); textGroup = Scene::gen(); textGroup->translate(cursor.x, cursor.y); } @@ -1083,42 +1098,61 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) shape->strokeFill(doc.stroke.color.rgb[0], doc.stroke.color.rgb[1], doc.stroke.color.rgb[2]); } - bool needGroup = false; + auto needGroup = false; + //text range process if (!text->ranges.empty()) { Point scaling = {1.0f, 1.0f}; auto rotation = 0.0f; Point translation = {0.0f, 0.0f}; + auto color = doc.color; + auto strokeColor = doc.stroke.color; + uint8_t opacity = 255; + uint8_t fillOpacity = 255; + uint8_t strokeOpacity = 255; - //text range process for (auto s = text->ranges.begin(); s < text->ranges.end(); ++s) { - float start, end; - (*s)->range(frameNo, float(totalChars), start, end); - auto basedIdx = idx; if ((*s)->based == LottieTextRange::Based::CharsExcludingSpaces) basedIdx = idx - space; else if ((*s)->based == LottieTextRange::Based::Words) basedIdx = line + space; else if ((*s)->based == LottieTextRange::Based::Lines) basedIdx = line; - if (basedIdx < start || basedIdx >= end) continue; + auto f = (*s)->factor(frameNo, float(totalChars), (float)basedIdx); + if (tvg::zero(f)) continue; needGroup = true; - translation = translation + (*s)->style.position(frameNo); - scaling = scaling * (*s)->style.scale(frameNo) * 0.01f; - rotation += (*s)->style.rotation(frameNo); + translation = translation + f * (*s)->style.position(frameNo); + scaling = scaling * (f * ((*s)->style.scale(frameNo) * 0.01f - Point{1.0f, 1.0f}) + Point{1.0f, 1.0f}); + rotation += f * (*s)->style.rotation(frameNo); - shape->opacity((*s)->style.opacity(frameNo)); + opacity = (uint8_t)(opacity - f * (opacity - (*s)->style.opacity(frameNo))); + shape->opacity(opacity); - auto color = (*s)->style.fillColor(frameNo); - shape->fill(color.rgb[0], color.rgb[1], color.rgb[2], (*s)->style.fillOpacity(frameNo)); + auto rangeColor = (*s)->style.fillColor(frameNo); //TODO: use flag to check whether it was really set + if (tvg::equal(f, 1.0f)) color = rangeColor; + else { + color.rgb[0] = lerp(color.rgb[0], rangeColor.rgb[0], f); + color.rgb[1] = lerp(color.rgb[1], rangeColor.rgb[1], f); + color.rgb[2] = lerp(color.rgb[2], rangeColor.rgb[2], f); + } + fillOpacity = (uint8_t)(fillOpacity - f * (fillOpacity - (*s)->style.fillOpacity(frameNo))); + shape->fill(color.rgb[0], color.rgb[1], color.rgb[2], fillOpacity); if (doc.stroke.render) { - auto strokeColor = (*s)->style.strokeColor(frameNo); - shape->strokeWidth((*s)->style.strokeWidth(frameNo) / scale); - shape->strokeFill(strokeColor.rgb[0], strokeColor.rgb[1], strokeColor.rgb[2], (*s)->style.strokeOpacity(frameNo)); + shape->strokeWidth(f * (*s)->style.strokeWidth(frameNo) / scale); + auto rangeColor = (*s)->style.strokeColor(frameNo); //TODO: use flag to check whether it was really set + if (tvg::equal(f, 1.0f)) strokeColor = rangeColor; + else { + strokeColor.rgb[0] = lerp(strokeColor.rgb[0], rangeColor.rgb[0], f); + strokeColor.rgb[1] = lerp(strokeColor.rgb[1], rangeColor.rgb[1], f); + strokeColor.rgb[2] = lerp(strokeColor.rgb[2], rangeColor.rgb[2], f); + } + strokeOpacity = (uint8_t)(strokeOpacity - f * (strokeOpacity - (*s)->style.strokeOpacity(frameNo))); + shape->strokeFill(strokeColor.rgb[0], strokeColor.rgb[1], strokeColor.rgb[2], strokeOpacity); } - cursor.x += (*s)->style.letterSpacing(frameNo); - auto spacing = (*s)->style.lineSpacing(frameNo); + cursor.x += f * (*s)->style.letterSpacing(frameNo); + + auto spacing = f * (*s)->style.lineSpacing(frameNo); if (spacing > lineSpacing) lineSpacing = spacing; } @@ -1141,33 +1175,34 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) //center pivoting textGroupMatrix.e13 += (pivotX * textGroupMatrix.e11 + pivotX * textGroupMatrix.e12); textGroupMatrix.e23 += (pivotY * textGroupMatrix.e21 + pivotY * textGroupMatrix.e22); - + textGroup->transform(textGroupMatrix); } auto& matrix = shape->transform(); tvg::identity(&matrix); - translate(&matrix, (translation.x / scale + cursor.x) - textGroupMatrix.e13, (translation.y / scale + cursor.y) - textGroupMatrix.e23); - tvg::scale(&matrix, scaling.x, scaling.y); + translate(&matrix, translation.x / scale + cursor.x - textGroupMatrix.e13, translation.y / scale + cursor.y - textGroupMatrix.e23); + tvg::scale(&matrix, scaling.x * capScale, scaling.y * capScale); shape->transform(matrix); } if (needGroup) { - textGroup->push(cast(shape)); + textGroup->push(shape); } else { // When text isn't selected, exclude the shape from the text group auto& matrix = shape->transform(); matrix.e13 = cursor.x; matrix.e23 = cursor.y; + matrix.e11 = matrix.e22 = capScale; //cases with matrix scaling factors =! 1 handled in the 'needGroup' scenario shape->transform(matrix); - scene->push(cast(shape)); + scene->push(shape); } p += glyph->len; idx += glyph->len; //advance the cursor position horizontally - cursor.x += glyph->width + doc.tracking; + cursor.x += (glyph->width + doc.tracking) * capScale; found = true; break; @@ -1179,6 +1214,9 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) ++idx; } } + + delete(scene); + delete(textGroup); } @@ -1210,18 +1248,18 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo) //Cheaper. Replace the masking with a clipper if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha && opacity == 255) { - layer->scene->clip(tvg::cast(pShape)); + layer->scene->clip(pShape); return; } //Introduce an intermediate scene for embracing the matte + masking if (layer->matteTarget) { - auto scene = Scene::gen().release(); - scene->push(cast(layer->scene)); + auto scene = Scene::gen(); + scene->push(layer->scene); layer->scene = scene; } - layer->scene->mask(tvg::cast(pShape), compMethod); + layer->scene->mask(pShape, compMethod); //Apply the subsquent masks for (auto m = layer->masks.begin() + 1; m < layer->masks.end(); ++m) { @@ -1239,7 +1277,7 @@ void LottieBuilder::updateMaskings(LottieLayer* layer, float frameNo) shape->fill(255, 255, 255, mask->opacity(frameNo)); shape->transform(layer->cache.matrix); mask->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps); - pShape->mask(tvg::cast(shape), method); + pShape->mask(shape, method); pShape = shape; pMethod = method; } @@ -1255,7 +1293,7 @@ bool LottieBuilder::updateMatte(LottieComposition* comp, float frameNo, Scene* s updateLayer(comp, scene, target, frameNo); if (target->scene) { - layer->scene->mask(cast(target->scene), layer->matteType); + layer->scene->mask(target->scene, layer->matteType); } else if (layer->matteType == MaskMethod::Alpha || layer->matteType == MaskMethod::Luma) { //matte target is not exist. alpha blending definitely bring an invisible result delete(layer->scene); @@ -1306,7 +1344,7 @@ void LottieBuilder::updateLayer(LottieComposition* comp, Scene* scene, LottieLay if (layer->type != LottieLayer::Null && layer->cache.opacity == 0) return; //Prepare render data - layer->scene = Scene::gen().release(); + layer->scene = Scene::gen(); layer->scene->id = layer->id; //ignore opacity when Null layer? @@ -1351,7 +1389,7 @@ void LottieBuilder::updateLayer(LottieComposition* comp, Scene* scene, LottieLay updateEffect(layer, frameNo); //the given matte source was composited by the target earlier. - if (!layer->matteSrc) scene->push(cast(layer->scene)); + if (!layer->matteSrc) scene->push(layer->scene); } @@ -1468,7 +1506,7 @@ bool LottieBuilder::update(LottieComposition* comp, float frameNo) //update children layers auto root = comp->root; - root->scene->clear(); + root->scene->remove(); if (exps && comp->expressions) exps->update(comp->timeAtFrame(frameNo)); @@ -1485,7 +1523,7 @@ void LottieBuilder::build(LottieComposition* comp) { if (!comp) return; - comp->root->scene = Scene::gen().release(); + comp->root->scene = Scene::gen(); _buildComposition(comp, comp->root); @@ -1494,5 +1532,5 @@ void LottieBuilder::build(LottieComposition* comp) //viewport clip auto clip = Shape::gen(); clip->appendRect(0, 0, comp->w, comp->h); - comp->root->scene->clip(std::move(clip)); + comp->root->scene->clip(clip); } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.h index cb628e866..17278c22d 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieBuilder.h @@ -25,7 +25,6 @@ #include "tvgCommon.h" #include "tvgInlist.h" -#include "tvgPaint.h" #include "tvgShape.h" #include "tvgLottieExpressions.h" #include "tvgLottieModifier.h" @@ -64,13 +63,13 @@ struct RenderContext RenderContext(Shape* propagator) { P(propagator)->reset(); - PP(propagator)->ref(); + propagator->ref(); this->propagator = propagator; } ~RenderContext() { - PP(propagator)->unref(); + propagator->unref(false); free(transform); delete(roundness); delete(offsetPath); @@ -79,7 +78,7 @@ struct RenderContext RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false) { if (mergeable) merging = rhs.merging; - PP(propagator)->ref(); + propagator->ref(); this->propagator = propagator; this->repeaters = rhs.repeaters; if (rhs.roundness) this->roundness = new LottieRoundnessModifier(rhs.roundness->r); diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieCommon.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieCommon.h index 5cdbdc3e6..80b0718aa 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieCommon.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieCommon.h @@ -67,7 +67,8 @@ struct TextDocument char* name = nullptr; float size; float tracking = 0.0f; - uint8_t justify; + uint8_t justify = 0; + uint8_t caps = 0; //0: Regular, 1: AllCaps, 2: SmallCaps }; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieExpressions.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieExpressions.cpp index cb752c452..bae380d74 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieExpressions.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieExpressions.cpp @@ -238,7 +238,7 @@ static jerry_value_t _buildTrimpath(LottieTrimpath* trimpath, float frameNo) jerry_object_set_sz(obj, "end", end); jerry_value_free(end); auto offset = jerry_number(trimpath->offset(frameNo)); - jerry_object_set_sz(obj, "offset", end); + jerry_object_set_sz(obj, "offset", offset); jerry_value_free(offset); return obj; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieInterpolator.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieInterpolator.cpp index 19ad502e0..b32523cf5 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieInterpolator.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieInterpolator.cpp @@ -127,7 +127,7 @@ float LottieInterpolator::progress(float t) void LottieInterpolator::set(const char* key, Point& inTangent, Point& outTangent) { - this->key = strdup(key); + if (key) this->key = strdup(key); this->inTangent = inTangent; this->outTangent = outTangent; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.cpp index aa05b9695..116dd24d7 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.cpp @@ -43,6 +43,10 @@ void LottieLoader::run(unsigned tid) ScopedLock lock(key); comp = parser.comp; } + if (parser.slots) { + override(parser.slots, true); + parser.slots = nullptr; + } builder->build(comp); release(); @@ -57,8 +61,6 @@ void LottieLoader::release() free((char*)content); content = nullptr; } - free(dirName); - dirName = nullptr; } @@ -81,6 +83,8 @@ LottieLoader::~LottieLoader() //TODO: correct position? delete(comp); delete(builder); + + free(dirName); } @@ -195,7 +199,7 @@ bool LottieLoader::header() } -bool LottieLoader::open(const char* data, uint32_t size, const std::string& rpath, bool copy, const ColorReplace& colorReplacement) +bool LottieLoader::open(const char* data, uint32_t size, const char* rpath, bool copy, const ColorReplace& colorReplacement) { colorReplaceInternal = colorReplacement; if (copy) { @@ -208,17 +212,18 @@ bool LottieLoader::open(const char* data, uint32_t size, const std::string& rpat this->size = size; this->copy = copy; - if (rpath.empty()) this->dirName = strdup("."); - else this->dirName = strdup(rpath.c_str()); + if (!rpath) this->dirName = strdup("."); + else this->dirName = strdup(rpath); return header(); } -bool LottieLoader::open(const string& path, const ColorReplace& colorReplacement) +bool LottieLoader::open(const char* path, const ColorReplace& colorReplacement) { +#ifdef THORVG_FILE_IO_SUPPORT colorReplaceInternal = colorReplacement; - auto f = fopen(path.c_str(), "r"); + auto f = fopen(path, "r"); if (!f) return false; fseek(f, 0, SEEK_END); @@ -240,11 +245,14 @@ bool LottieLoader::open(const string& path, const ColorReplace& colorReplacement fclose(f); - this->dirName = strDirname(path.c_str()); + this->dirName = strDirname(path); this->content = content; this->copy = true; return header(); +#else + return false; +#endif } @@ -289,34 +297,35 @@ Paint* LottieLoader::paint() } -bool LottieLoader::override(const char* slot) +bool LottieLoader::override(const char* slots, bool byDefault) { if (!ready() || comp->slots.count == 0) return false; - auto success = true; - //override slots - if (slot) { + if (slots) { //Copy the input data because the JSON parser will encode the data immediately. - auto temp = strdup(slot); + auto temp = byDefault ? slots : strdup(slots); //parsing slot json LottieParser parser(temp, dirName, colorReplaceInternal); parser.comp = comp; auto idx = 0; + auto succeed = false; while (auto sid = parser.sid(idx == 0)) { + auto applied = false; for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) { if (strcmp((*s)->sid, sid)) continue; - if (!parser.apply(*s)) success = false; + if (parser.apply(*s, byDefault)) succeed = applied = true; break; } + if (!applied) parser.skip(sid); ++idx; } - - if (idx < 1) success = false; - free(temp); - rebuild = overridden = success; + free((char*)temp); + rebuild = succeed; + overridden |= succeed; + return rebuild; //reset slots } else if (overridden) { for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) { @@ -325,7 +334,7 @@ bool LottieLoader::override(const char* slot) overridden = false; rebuild = true; } - return success; + return true; } @@ -422,4 +431,4 @@ bool LottieLoader::ready() done(); if (comp) return true; return false; -} \ No newline at end of file +} diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.h index 70988b1b8..4519c7681 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieLoader.h @@ -53,12 +53,12 @@ class LottieLoader : public FrameModule, public Task LottieLoader(); ~LottieLoader(); - bool open(const string& path, const ColorReplace& colorReplacement) override; - bool open(const char* data, uint32_t size, const std::string& rpath, bool copy, const ColorReplace& colorReplacement) override; + bool open(const char* path, const ColorReplace& colorReplacement) override; + bool open(const char* data, uint32_t size, const char* rpath, bool copy, const ColorReplace& colorReplacement) override; bool resize(Paint* paint, float w, float h) override; bool read() override; Paint* paint() override; - bool override(const char* slot); + bool override(const char* slot, bool byDefault = false); //Frame Controls bool frame(float no) override; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.cpp index 4f97802aa..3f6134095 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.cpp @@ -21,8 +21,6 @@ */ #include "tvgMath.h" -#include "tvgPaint.h" -#include "tvgFill.h" #include "tvgTaskScheduler.h" #include "tvgLottieModel.h" @@ -41,28 +39,10 @@ void LottieSlot::reset() { if (!overridden) return; + auto shallow = pairs.count == 1 ? true : false; + for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) { - switch (type) { - case LottieProperty::Type::ColorStop: { - static_cast(pair->obj)->colorStops.release(); - static_cast(pair->obj)->colorStops = *static_cast(pair->prop); - static_cast(pair->prop)->frames = nullptr; - break; - } - case LottieProperty::Type::Color: { - static_cast(pair->obj)->color.release(); - static_cast(pair->obj)->color = *static_cast(pair->prop); - static_cast(pair->prop)->frames = nullptr; - break; - } - case LottieProperty::Type::TextDoc: { - static_cast(pair->obj)->doc.release(); - static_cast(pair->obj)->doc = *static_cast(pair->prop); - static_cast(pair->prop)->frames = nullptr; - break; - } - default: break; - } + pair->obj->override(pair->prop, shallow, true); delete(pair->prop); pair->prop = nullptr; } @@ -70,70 +50,132 @@ void LottieSlot::reset() } -void LottieSlot::assign(LottieObject* target, ColorReplace* colorReplacement) +void LottieSlot::assign(LottieObject* target, bool byDefault, ColorReplace* colorReplacement) { + auto copy = !overridden && !byDefault; + auto shallow = pairs.count == 1 ? true : false; + //apply slot object to all targets for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) { //backup the original properties before overwriting switch (type) { - case LottieProperty::Type::ColorStop: { - if (!overridden) { - pair->prop = new LottieColorStop; - *static_cast(pair->prop) = static_cast(pair->obj)->colorStops; - } - - pair->obj->override(&static_cast(target)->colorStops); + case LottieProperty::Type::Opacity: { + if (copy) pair->prop = new LottieOpacity(static_cast(pair->obj)->opacity); + pair->obj->override(&static_cast(target)->opacity, shallow, byDefault); break; } case LottieProperty::Type::Color: { auto color = static_cast(pair->obj)->color; colorReplacement->getCustomColorLottie32(color.value.rgb[0], color.value.rgb[1], color.value.rgb[2]); - if (!overridden) { - pair->prop = new LottieColor; - *static_cast(pair->prop) = color; - } - - pair->obj->override(&color); + if (copy) pair->prop = new LottieColor(color); + pair->obj->override(&color, shallow, byDefault); + break; + } + case LottieProperty::Type::ColorStop: { + if (copy) pair->prop = new LottieColorStop(static_cast(pair->obj)->colorStops); + pair->obj->override(&static_cast(target)->colorStops, shallow, byDefault); break; } case LottieProperty::Type::TextDoc: { - if (!overridden) { - pair->prop = new LottieTextDoc; - *static_cast(pair->prop) = static_cast(pair->obj)->doc; - } - - pair->obj->override(&static_cast(target)->doc); + if (copy) pair->prop = new LottieTextDoc(static_cast(pair->obj)->doc); + pair->obj->override(&static_cast(target)->doc, shallow, byDefault); + break; + } + case LottieProperty::Type::Image: { + if (copy) pair->prop = new LottieBitmap(static_cast(pair->obj)->data); + pair->obj->override(&static_cast(target)->data, shallow, byDefault); break; } default: break; } } - overridden = true; + if (!byDefault) overridden = true; } -void LottieTextRange::range(float frameNo, float totalLen, float& start, float& end) +float LottieTextRange::factor(float frameNo, float totalLen, float idx) { - auto divisor = (rangeUnit == Unit::Percent) ? (100.0f / totalLen) : 1.0f; - auto offset = this->offset(frameNo) / divisor; - start = nearbyintf(this->start(frameNo) / divisor) + offset; - end = nearbyintf(this->end(frameNo) / divisor) + offset; - - if (start > end) std::swap(start, end); - - if (random == 0) return; - - auto range = end - start; - auto len = (rangeUnit == Unit::Percent) ? 100.0f : totalLen; - start = static_cast(random % int(len - range)); - end = start + range; -} + auto offset = this->offset(frameNo); + auto start = this->start(frameNo) + offset; + auto end = this->end(frameNo) + offset; + + if (random > 0) { + auto range = end - start; + auto len = (rangeUnit == Unit::Percent) ? 100.0f : totalLen; + start = static_cast(random % int(len - range)); + end = start + range; + } + auto divisor = (rangeUnit == Unit::Percent) ? (100.0f / totalLen) : 1.0f; + start /= divisor; + end /= divisor; + + auto f = 0.0f; + + switch (this->shape) { + case Square: { + auto smoothness = this->smoothness(frameNo); + if (tvg::zero(smoothness)) f = idx >= nearbyintf(start) && idx < nearbyintf(end) ? 1.0f : 0.0f; + else { + if (idx >= std::floor(start)) { + auto diff = idx - start; + f = diff < 0.0f ? std::min(end, 1.0f) + diff : end - idx; + } + smoothness *= 0.01f; + f = (f - (1.0f - smoothness) * 0.5f) / smoothness; + } + break; + } + case RampUp: { + f = tvg::equal(start, end) ? (idx >= end ? 1.0f : 0.0f) : (0.5f + idx - start) / (end - start); + break; + } + case RampDown: { + f = tvg::equal(start, end) ? (idx >= end ? 0.0f : 1.0f) : 1.0f - (0.5f + idx - start) / (end - start); + break; + } + case Triangle: { + f = tvg::equal(start, end) ? 0.0f : 2.0f * (0.5f + idx - start) / (end - start); + f = f < 1.0f ? f : 2.0f - f; + break; + } + case Round: { + idx += 0.5f - start; + clamp(idx, 0.0f, end - start); + auto range = 0.5f * (end - start); + auto t = idx - range; + f = tvg::equal(start, end) ? 0.0f : sqrtf(1.0f - t * t / (range * range)); + break; + } + case Smooth: { + idx += 0.5f - start; + clamp(idx, 0.0f, end - start); + f = tvg::equal(start, end) ? 0.0f : 0.5f * (1.0f + cosf(MATH_PI * (1.0f + 2.0f * idx / (end - start)))); + break; + } + } + clamp(f, 0.0f, 1.0f); + + //apply easing + auto minEase = this->minEase(frameNo); + clamp(minEase, -100.0f, 100.0f); + auto maxEase = this->maxEase(frameNo); + clamp(maxEase, -100.0f, 100.0f); + if (!tvg::zero(minEase) || !tvg::zero(maxEase)) { + Point in{1.0f, 1.0f}; + Point out{0.0f, 0.0f}; + + if (maxEase > 0.0f) in.x = 1.0f - maxEase * 0.01f; + else in.y = 1.0f + maxEase * 0.01f; + if (minEase > 0.0f) out.x = minEase * 0.01f; + else out.y = -minEase * 0.01f; + + interpolator->set(nullptr, in, out); + f = interpolator->progress(f); + } + clamp(f, 0.0f, 1.0f); -LottieImage::~LottieImage() -{ - free(b64Data); - free(mimeType); + return f * this->maxAmount(frameNo) * 0.01f; } @@ -141,23 +183,36 @@ void LottieImage::prepare() { LottieObject::type = LottieObject::Image; - auto picture = Picture::gen().release(); + auto picture = Picture::gen(); //force to load a picture on the same thread TaskScheduler::async(false); - if (size > 0) picture->load((const char*)b64Data, size, mimeType); - else picture->load(path); + if (data.size > 0) picture->load((const char*)data.b64Data, data.size, data.mimeType); + else picture->load(data.path); TaskScheduler::async(true); - picture->size(width, height); - PP(picture)->ref(); + picture->size(data.width, data.height); + picture->ref(); pooler.push(picture); } +void LottieImage::update() +{ + //Update the picture data + TaskScheduler::async(false); + for (auto p = pooler.begin(); p < pooler.end(); ++p) { + if (data.size > 0) (*p)->load((const char*)data.b64Data, data.size, data.mimeType); + else (*p)->load(data.path); + (*p)->size(data.width, data.height); + } + TaskScheduler::async(true); +} + + void LottieTrimpath::segment(float frameNo, float& start, float& end, LottieExpressions* exps) { start = this->start(frameNo, exps) * 0.01f; @@ -286,12 +341,12 @@ Fill* LottieGradient::fill(float frameNo, LottieExpressions* exps) //Linear Graident if (id == 1) { - fill = LinearGradient::gen().release(); + fill = LinearGradient::gen(); static_cast(fill)->linear(s.x, s.y, e.x, e.y); } //Radial Gradient if (id == 2) { - fill = RadialGradient::gen().release(); + fill = RadialGradient::gen(); auto w = fabsf(e.x - s.x); auto h = fabsf(e.y - s.y); @@ -442,16 +497,16 @@ void LottieLayer::prepare(RGB24* color) //prepare the viewport clipper if (type == LottieLayer::Precomp) { - auto clipper = Shape::gen().release(); + auto clipper = Shape::gen(); clipper->appendRect(0.0f, 0.0f, w, h); - PP(clipper)->ref(); + clipper->ref(); statical.pooler.push(clipper); //prepare solid fill in advance if it is a layer type. } else if (color && type == LottieLayer::Solid) { - auto solidFill = Shape::gen().release(); + auto solidFill = Shape::gen(); solidFill->appendRect(0, 0, static_cast(w), static_cast(h)); solidFill->fill(color->rgb[0], color->rgb[1], color->rgb[2]); - PP(solidFill)->ref(); + solidFill->ref(); statical.pooler.push(solidFill); } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.h index e924d5c5e..130323ffb 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieModel.h @@ -158,7 +158,7 @@ struct LottieObject { } - virtual void override(LottieProperty* prop) + virtual void override(LottieProperty* prop, bool shallow, bool byDefault) { TVGERR("LOTTIE", "Unsupported slot type"); } @@ -201,6 +201,11 @@ struct LottieTextRange enum Shape : uint8_t { Square = 1, RampUp, RampDown, Triangle, Round, Smooth }; enum Unit : uint8_t { Percent = 1, Index }; + ~LottieTextRange() + { + free(interpolator); + } + struct { LottieColor fillColor = RGB24{255, 255, 255}; LottieColor strokeColor = RGB24{255, 255, 255}; @@ -222,13 +227,14 @@ struct LottieTextRange LottieFloat smoothness = 0.0f; LottieFloat start = 0.0f; LottieFloat end = FLT_MAX; + LottieInterpolator* interpolator = nullptr; Based based = Chars; Shape shape = Square; Unit rangeUnit = Percent; uint8_t random = 0; bool expressible = false; - void range(float frameNo, float totalLen, float& start, float& end); + float factor(float frameNo, float totalLen, float idx); }; @@ -273,15 +279,15 @@ struct LottieText : LottieObject, LottieRenderPooler LottiePoint anchor{}; } alignOption; - void prepare() + LottieText() { LottieObject::type = LottieObject::Text; } - void override(LottieProperty* prop) override + void override(LottieProperty* prop, bool shallow, bool byDefault = false) override { - this->doc = *static_cast(prop); - this->prepare(); + if (byDefault) doc.release(); + doc.copy(*static_cast(prop), shallow); } LottieProperty* property(uint16_t ix) override @@ -305,7 +311,7 @@ struct LottieTrimpath : LottieObject { enum Type : uint8_t { Simultaneous = 1, Individual = 2 }; - void prepare() + LottieTrimpath() { LottieObject::type = LottieObject::Trimpath; } @@ -344,7 +350,7 @@ struct LottieShape : LottieObject, LottieRenderPooler return true; } - void prepare(LottieObject::Type type) + LottieShape(LottieObject::Type type) { LottieObject::type = type; } @@ -353,7 +359,7 @@ struct LottieShape : LottieObject, LottieRenderPooler struct LottieRoundedCorner : LottieObject { - void prepare() + LottieRoundedCorner() { LottieObject::type = LottieObject::RoundedCorner; } @@ -370,10 +376,7 @@ struct LottieRoundedCorner : LottieObject struct LottiePath : LottieShape { - void prepare() - { - LottieShape::prepare(LottieObject::Path); - } + LottiePath() : LottieShape(LottieObject::Path) {} LottieProperty* property(uint16_t ix) override { @@ -387,10 +390,7 @@ struct LottiePath : LottieShape struct LottieRect : LottieShape { - void prepare() - { - LottieShape::prepare(LottieObject::Rect); - } + LottieRect() : LottieShape(LottieObject::Rect) {} LottieProperty* property(uint16_t ix) override { @@ -410,10 +410,7 @@ struct LottiePolyStar : LottieShape { enum Type : uint8_t {Star = 1, Polygon}; - void prepare() - { - LottieShape::prepare(LottieObject::Polystar); - } + LottiePolyStar() : LottieShape(LottieObject::Polystar) {} LottieProperty* property(uint16_t ix) override { @@ -440,10 +437,7 @@ struct LottiePolyStar : LottieShape struct LottieEllipse : LottieShape { - void prepare() - { - LottieShape::prepare(LottieObject::Ellipse); - } + LottieEllipse() : LottieShape(LottieObject::Ellipse) {} LottieProperty* property(uint16_t ix) override { @@ -477,7 +471,7 @@ struct LottieTransform : LottieObject delete(rotationEx); } - void prepare() + LottieTransform() { LottieObject::type = LottieObject::Transform; } @@ -533,7 +527,7 @@ struct LottieSolid : LottieObject struct LottieSolidStroke : LottieSolid, LottieStroke { - void prepare() + LottieSolidStroke() { LottieObject::type = LottieObject::SolidStroke; } @@ -549,25 +543,30 @@ struct LottieSolidStroke : LottieSolid, LottieStroke return LottieSolid::property(ix); } - void override(LottieProperty* prop) override + void override(LottieProperty* prop, bool shallow, bool byDefault) override { - this->color = *static_cast(prop); - this->prepare(); + if (byDefault) color.release(); + color.copy(*static_cast(prop), shallow); } }; struct LottieSolidFill : LottieSolid { - void prepare() + LottieSolidFill() { LottieObject::type = LottieObject::SolidFill; } - void override(LottieProperty* prop) override + void override(LottieProperty* prop, bool shallow, bool byDefault) override { - this->color = *static_cast(prop); - this->prepare(); + if (prop->type == LottieProperty::Type::Opacity) { + if (byDefault) opacity.release(); + opacity.copy(*static_cast(prop), shallow); + } else if (prop->type == LottieProperty::Type::Color) { + if (byDefault) color.release(); + color.copy(*static_cast(prop), shallow); + } } FillRule rule = FillRule::Winding; @@ -607,6 +606,12 @@ struct LottieGradient : LottieObject return nullptr; } + void override(LottieProperty* prop, bool shallow, bool byDefault = false) override + { + if (byDefault) colorStops.release(); + colorStops.copy(*static_cast(prop), shallow); + prepare(); + } uint32_t populate(ColorStop& color, size_t count); Fill* fill(float frameNo, LottieExpressions* exps); @@ -624,17 +629,9 @@ struct LottieGradient : LottieObject struct LottieGradientFill : LottieGradient { - LottieGradientFill(ColorReplace* colorReplacement): LottieGradient(colorReplacement){} - void prepare() + LottieGradientFill(ColorReplace* colorReplacement): LottieGradient(colorReplacement) { LottieObject::type = LottieObject::GradientFill; - LottieGradient::prepare(); - } - - void override(LottieProperty* prop) override - { - this->colorStops = *static_cast(prop); - this->prepare(); } FillRule rule = FillRule::Winding; @@ -643,11 +640,9 @@ struct LottieGradientFill : LottieGradient struct LottieGradientStroke : LottieGradient, LottieStroke { - LottieGradientStroke(ColorReplace* colorReplacement) : LottieGradient(colorReplacement) {} - void prepare() + LottieGradientStroke(ColorReplace* colorReplacement) : LottieGradient(colorReplacement) { LottieObject::type = LottieObject::GradientStroke; - LottieGradient::prepare(); } LottieProperty* property(uint16_t ix) override @@ -660,34 +655,28 @@ struct LottieGradientStroke : LottieGradient, LottieStroke } return LottieGradient::property(ix); } - - void override(LottieProperty* prop) override - { - this->colorStops = *static_cast(prop); - this->prepare(); - } }; struct LottieImage : LottieObject, LottieRenderPooler { - union { - char* b64Data = nullptr; - char* path; - }; - char* mimeType = nullptr; - uint32_t size = 0; - float width = 0.0f; - float height = 0.0f; + LottieBitmap data; + + void override(LottieProperty* prop, bool shallow, bool byDefault = false) override + { + if (byDefault) data.release(); + data.copy(*static_cast(prop), shallow); + update(); + } - ~LottieImage(); void prepare(); + void update(); }; struct LottieRepeater : LottieObject { - void prepare() + LottieRepeater() { LottieObject::type = LottieObject::Repeater; } @@ -721,7 +710,7 @@ struct LottieRepeater : LottieObject struct LottieOffsetPath : LottieObject { - void prepare() + LottieOffsetPath() { LottieObject::type = LottieObject::OffsetPath; } @@ -849,7 +838,7 @@ struct LottieSlot LottieProperty* prop; }; - void assign(LottieObject* target, ColorReplace* colorReplacement); + void assign(LottieObject* target, bool byDefault, ColorReplace* colorReplacement); void reset(); LottieSlot(char* sid, LottieObject* obj, LottieProperty::Type type) : sid(sid), type(type) diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.cpp index dbff50c4f..dd5421228 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.cpp @@ -158,7 +158,7 @@ StrokeJoin LottieParser::getStrokeJoin() } -void LottieParser::getValue(TextDocument& doc) +bool LottieParser::getValue(TextDocument& doc) { enterObject(); while (auto key = nextObjectKey()) { @@ -166,6 +166,7 @@ void LottieParser::getValue(TextDocument& doc) else if (KEY_AS("f")) doc.name = getStringCopy(); else if (KEY_AS("t")) doc.text = getStringCopy(); else if (KEY_AS("j")) doc.justify = getInt(); + else if (KEY_AS("ca")) doc.caps = getInt(); else if (KEY_AS("tr")) doc.tracking = getFloat() * 0.1f; else if (KEY_AS("lh")) doc.height = getFloat(); else if (KEY_AS("ls")) doc.shift = getFloat(); @@ -177,10 +178,11 @@ void LottieParser::getValue(TextDocument& doc) else if (KEY_AS("of")) doc.stroke.render = getBool(); else skip(key); } + return false; } -void LottieParser::getValue(PathSet& path) +bool LottieParser::getValue(PathSet& path) { Array outs, ins, pts; bool closed = false; @@ -203,8 +205,8 @@ void LottieParser::getValue(PathSet& path) if (arrayWrapper) nextArrayValue(); //valid path data? - if (ins.empty() || outs.empty() || pts.empty()) return; - if (ins.count != outs.count || outs.count != pts.count) return; + if (ins.empty() || outs.empty() || pts.empty()) return false; + if (ins.count != outs.count || outs.count != pts.count) return false; //convert path auto out = outs.begin(); @@ -250,21 +252,29 @@ void LottieParser::getValue(PathSet& path) outPts.data = nullptr; outCmds.data = nullptr; + + return false; } -void LottieParser::getValue(ColorStop& color) +bool LottieParser::getValue(ColorStop& color) { - if (peekType() == kArrayType) enterArray(); + if (peekType() == kArrayType) { + enterArray(); + if (!nextArrayValue()) return true; + } if (!color.input) color.input = new Array(static_cast(context.parent)->colorStops.count * 6); else color.input->clear(); - while (nextArrayValue()) color.input->push(getFloat()); + do color.input->push(getFloat()); + while (nextArrayValue()); + + return true; } -void LottieParser::getValue(Array& pts) +bool LottieParser::getValue(Array& pts) { enterArray(); while (nextArrayValue()) { @@ -273,10 +283,11 @@ void LottieParser::getValue(Array& pts) getValue(pt); pts.push(pt); } + return false; } -void LottieParser::getValue(int8_t& val) +bool LottieParser::getValue(int8_t& val) { if (peekType() == kArrayType) { enterArray(); @@ -284,12 +295,13 @@ void LottieParser::getValue(int8_t& val) //discard rest while (nextArrayValue()) getInt(); } else { - val = getFloat(); + val = (int8_t) getFloat(); } + return false; } -void LottieParser::getValue(uint8_t& val) +bool LottieParser::getValue(uint8_t& val) { if (peekType() == kArrayType) { enterArray(); @@ -299,10 +311,11 @@ void LottieParser::getValue(uint8_t& val) } else { val = (uint8_t)(getFloat() * 2.55f); } + return false; } -void LottieParser::getValue(float& val) +bool LottieParser::getValue(float& val) { if (peekType() == kArrayType) { enterArray(); @@ -312,41 +325,44 @@ void LottieParser::getValue(float& val) } else { val = getFloat(); } + return false; } bool LottieParser::getValue(Point& pt) { - auto type = peekType(); - if (type == kNullType) return false; - - int i = 0; - auto ptr = (float*)(&pt); + if (peekType() == kNullType) return false; + if (peekType() == kArrayType) { + enterArray(); + if (!nextArrayValue()) return false; + } - if (type == kArrayType) enterArray(); + pt.x = getFloat(); + pt.y = getFloat(); - while (nextArrayValue()) { - auto val = getFloat(); - if (i < 2) ptr[i++] = val; - } + while (nextArrayValue()) getFloat(); //drop return true; } -void LottieParser::getValue(RGB24& color) +bool LottieParser::getValue(RGB24& color) { - int i = 0; + if (peekType() == kArrayType) { + enterArray(); + if (!nextArrayValue()) return false; + } - if (peekType() == kArrayType) enterArray(); + color.rgb[0] = REMAP255(getFloat()); + color.rgb[1] = REMAP255(getFloat()); + color.rgb[2] = REMAP255(getFloat()); - while (nextArrayValue()) { - auto val = getFloat(); - if (i < 3) color.rgb[i++] = REMAP255(val); - } + while (nextArrayValue()) getFloat(); //drop colorReplaceInternal.getCustomColorLottie32(color.rgb[0], color.rgb[1], color.rgb[2]); //TODO: color filter? + + return true; } @@ -471,18 +487,10 @@ void LottieParser::parsePropertyInternal(T& prop) getValue(prop.value); //multi value property } else { - //TODO: Here might be a single frame. - //Can we figure out the frame number in advance? enterArray(); while (nextArrayValue()) { - //keyframes value - if (peekType() == kObjectType) { - parseKeyFrame(prop); - //multi value property with no keyframes - } else { - getValue(prop.value); - break; - } + if (peekType() == kObjectType) parseKeyFrame(prop); //keyframes value + else if (getValue(prop.value)) break; //multi value property with no keyframes } prop.prepare(); } @@ -490,17 +498,15 @@ void LottieParser::parsePropertyInternal(T& prop) template -void LottieParser::registerSlot(LottieObject* obj) +void LottieParser::registerSlot(LottieObject* obj, const char* sid) { - auto sid = getStringCopy(); - //append object if the slot already exists. for (auto slot = comp->slots.begin(); slot < comp->slots.end(); ++slot) { if (strcmp((*slot)->sid, sid)) continue; (*slot)->pairs.push({obj}); return; } - comp->slots.push(new LottieSlot(sid, obj, type)); + comp->slots.push(new LottieSlot(strdup(sid), obj, type)); } @@ -510,7 +516,7 @@ void LottieParser::parseProperty(T& prop, LottieObject* obj) enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("k")) parsePropertyInternal(prop); - else if (obj && KEY_AS("sid")) registerSlot(obj); + else if (obj && KEY_AS("sid")) registerSlot(obj, getString()); else if (KEY_AS("x")) prop.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &prop); else if (KEY_AS("ix")) prop.ix = getInt(); else skip(key); @@ -557,7 +563,6 @@ LottieRect* LottieParser::parseRect() else if (parseDirection(rect, key)) continue; else skip(key); } - rect->prepare(); return rect; } @@ -575,7 +580,6 @@ LottieEllipse* LottieParser::parseEllipse() else if (parseDirection(ellipse, key)) continue; else skip(key); } - ellipse->prepare(); return ellipse; } @@ -618,7 +622,6 @@ LottieTransform* LottieParser::parseTransform(bool ddd) else if (KEY_AS("sa")) parseProperty(transform->skewAxis); else skip(key); } - transform->prepare(); return transform; } @@ -637,7 +640,6 @@ LottieSolidFill* LottieParser::parseSolidFill() else if (KEY_AS("r")) fill->rule = getFillRule(); else skip(key); } - fill->prepare(); return fill; } @@ -680,7 +682,6 @@ LottieSolidStroke* LottieParser::parseSolidStroke() else if (KEY_AS("d")) parseStrokeDash(stroke); else skip(key); } - stroke->prepare(); return stroke; } @@ -714,7 +715,6 @@ LottiePath* LottieParser::parsePath() else if (parseDirection(path, key)) continue; else skip(key); } - path->prepare(); return path; } @@ -738,7 +738,6 @@ LottiePolyStar* LottieParser::parsePolyStar() else if (parseDirection(star, key)) continue; else skip(key); } - star->prepare(); return star; } @@ -754,7 +753,6 @@ LottieRoundedCorner* LottieParser::parseRoundedCorner() else if (KEY_AS("r")) parseProperty(corner->radius); else skip(key); } - corner->prepare(); return corner; } @@ -765,7 +763,7 @@ void LottieParser::parseColorStop(LottieGradient* gradient) while (auto key = nextObjectKey()) { if (KEY_AS("p")) gradient->colorStops.count = getInt(); else if (KEY_AS("k")) parseProperty(gradient->colorStops, gradient); - else if (KEY_AS("sid")) registerSlot(gradient); + else if (KEY_AS("sid")) registerSlot(gradient, getString()); else skip(key); } } @@ -837,8 +835,6 @@ LottieTrimpath* LottieParser::parseTrimpath() else if (KEY_AS("m")) trim->type = static_cast(getInt()); else skip(key); } - trim->prepare(); - return trim; } @@ -869,8 +865,6 @@ LottieRepeater* LottieParser::parseRepeater() } else skip(key); } - repeater->prepare(); - return repeater; } @@ -888,8 +882,6 @@ LottieOffsetPath* LottieParser::parseOffsetPath() else if (KEY_AS("ml")) parseProperty(offsetPath->miterLimit); else skip(key); } - offsetPath->prepare(); - return offsetPath; } @@ -935,34 +927,29 @@ void LottieParser::parseObject(Array& parent) } -LottieImage* LottieParser::parseImage(const char* data, const char* subPath, bool embedded, float width, float height) +void LottieParser::parseImage(LottieImage* image, const char* data, const char* subPath, bool embedded, float width, float height) { - //Used for Image Asset - auto image = new LottieImage; - //embedded image resource. should start with "data:" //header look like "data:image/png;base64," so need to skip till ','. if (embedded && !strncmp(data, "data:", 5)) { //figure out the mimetype auto mimeType = data + 11; auto needle = strstr(mimeType, ";"); - image->mimeType = strDuplicate(mimeType, needle - mimeType); + image->data.mimeType = strDuplicate(mimeType, needle - mimeType); //b64 data auto b64Data = strstr(data, ",") + 1; size_t length = strlen(data) - (b64Data - data); - image->size = b64Decode(b64Data, length, &image->b64Data); + image->data.size = b64Decode(b64Data, length, &image->data.b64Data); //external image resource } else { auto len = strlen(dirName) + strlen(subPath) + strlen(data) + 2; - image->path = static_cast(malloc(len)); - snprintf(image->path, len, "%s/%s%s", dirName, subPath, data); + image->data.path = static_cast(malloc(len)); + snprintf(image->data.path, len, "%s/%s%s", dirName, subPath, data); } - image->width = width; - image->height = height; + image->data.width = width; + image->data.height = height; image->prepare(); - - return image; } @@ -974,6 +961,7 @@ LottieObject* LottieParser::parseAsset() unsigned long id = 0; //Used for Image Asset + const char* sid = nullptr; const char* data = nullptr; const char* subPath = nullptr; float width = 0.0f; @@ -995,9 +983,14 @@ LottieObject* LottieParser::parseAsset() else if (KEY_AS("w")) width = getFloat(); else if (KEY_AS("h")) height = getFloat(); else if (KEY_AS("e")) embedded = getInt(); + else if (KEY_AS("sid")) sid = getString(); else skip(key); } - if (data) obj = parseImage(data, subPath, embedded, width, height); + if (data) { + obj = new LottieImage; + parseImage(static_cast(obj), data, subPath, embedded, width, height); + if (sid) registerSlot(obj, sid); + } if (obj) obj->id = id; return obj; } @@ -1166,7 +1159,10 @@ void LottieParser::parseTextRange(LottieText* text) enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("t")) selector->expressible = (bool) getInt(); - else if (KEY_AS("xe")) parseProperty(selector->maxEase); + else if (KEY_AS("xe")) { + parseProperty(selector->maxEase); + selector->interpolator = static_cast(malloc(sizeof(LottieInterpolator))); + } else if (KEY_AS("ne")) parseProperty(selector->minEase); else if (KEY_AS("a")) parseProperty(selector->maxAmount); else if (KEY_AS("b")) selector->based = (LottieTextRange::Based) getInt(); @@ -1220,8 +1216,6 @@ void LottieParser::parseText(Array& parent) } else skip(key); } - - text->prepare(); parent.push(text); } @@ -1477,7 +1471,7 @@ const char* LottieParser::sid(bool first) } -bool LottieParser::apply(LottieSlot* slot) +bool LottieParser::apply(LottieSlot* slot, bool byDefault) { enterObject(); @@ -1485,13 +1479,10 @@ bool LottieParser::apply(LottieSlot* slot) LottieObject* obj = nullptr; //slot object switch (slot->type) { - case LottieProperty::Type::ColorStop: { - obj = new LottieGradient(&colorReplaceInternal); + case LottieProperty::Type::Opacity: { + obj = new LottieSolid; context.parent = obj; - while (auto key = nextObjectKey()) { - if (KEY_AS("p")) parseColorStop(static_cast(obj)); - else skip(key); - } + parseSlotProperty(static_cast(obj)->opacity); break; } case LottieProperty::Type::Color: { @@ -1500,18 +1491,38 @@ bool LottieParser::apply(LottieSlot* slot) parseSlotProperty(static_cast(obj)->color); break; } + case LottieProperty::Type::ColorStop: { + obj = new LottieGradient(&colorReplaceInternal); + context.parent = obj; + while (auto key = nextObjectKey()) { + if (KEY_AS("p")) parseColorStop(static_cast(obj)); + else skip(key); + } + break; + } case LottieProperty::Type::TextDoc: { obj = new LottieText; context.parent = obj; parseSlotProperty(static_cast(obj)->doc); break; } + case LottieProperty::Type::Image: { + while (auto key = nextObjectKey()) { + if (KEY_AS("p")) obj = parseAsset(); + else skip(key); + } + context.parent = obj; + break; + } default: break; } - if (!obj || Invalid()) return false; + if (!obj || Invalid()) { + delete(obj); + return false; + } - slot->assign(obj, &colorReplaceInternal); + slot->assign(obj, byDefault, &colorReplaceInternal); delete(obj); @@ -1519,6 +1530,47 @@ bool LottieParser::apply(LottieSlot* slot) } +void LottieParser::captureSlots(const char* key) +{ + free(slots); + + // TODO: Replace with immediate parsing, once the slot spec is confirmed by the LAC + + auto begin = getPos(); + auto end = getPos(); + auto depth = 1; + auto invalid = true; + + //get slots string + while (++end) { + if (*end == '}') { + --depth; + if (depth == 0) { + invalid = false; + break; + } + } else if (*end == '{') { + ++depth; + } + } + + if (invalid) { + TVGERR("LOTTIE", "Invalid Slots!"); + skip(key); + return; + } + + //composite '{' + slots + '}' + auto len = (end - begin + 2); + slots = (char*)malloc(sizeof(char) * len + 1); + slots[0] = '{'; + memcpy(slots + 1, begin, len); + slots[len] = '\0'; + + skip(key); +} + + bool LottieParser::parse() { //verify json. @@ -1547,6 +1599,7 @@ bool LottieParser::parse() else if (KEY_AS("fonts")) parseFonts(); else if (KEY_AS("chars")) parseChars(glyphs); else if (KEY_AS("markers")) parseMarkers(); + else if (KEY_AS("slots")) captureSlots(key); else skip(key); } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.h index c5c99b11c..d8474396a 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParser.h @@ -37,12 +37,14 @@ struct LottieParser : LookaheadParserHandler } bool parse(); - bool apply(LottieSlot* slot); + bool apply(LottieSlot* slot, bool byDefault); const char* sid(bool first = false); - template void registerSlot(LottieObject* obj); + void captureSlots(const char* key); + template void registerSlot(LottieObject* obj, const char* sid); LottieComposition* comp = nullptr; const char* dirName = nullptr; //base resource directory + char* slots = nullptr; private: ColorReplace colorReplaceInternal; @@ -58,14 +60,14 @@ struct LottieParser : LookaheadParserHandler void getInterpolatorPoint(Point& pt); void getPathSet(LottiePathSet& path); void getLayerSize(float& val); - void getValue(TextDocument& doc); - void getValue(PathSet& path); - void getValue(Array& pts); - void getValue(ColorStop& color); - void getValue(float& val); - void getValue(uint8_t& val); - void getValue(int8_t& val); - void getValue(RGB24& color); + bool getValue(TextDocument& doc); + bool getValue(PathSet& path); + bool getValue(Array& pts); + bool getValue(ColorStop& color); + bool getValue(float& val); + bool getValue(uint8_t& val); + bool getValue(int8_t& val); + bool getValue(RGB24& color); bool getValue(Point& pt); template bool parseTangent(const char *key, LottieVectorFrame& value); @@ -77,7 +79,7 @@ struct LottieParser : LookaheadParserHandler LottieObject* parseObject(); LottieObject* parseAsset(); - LottieImage* parseImage(const char* data, const char* subPath, bool embedded, float width, float height); + void parseImage(LottieImage* image, const char* data, const char* subPath, bool embedded, float width, float height); LottieLayer* parseLayer(LottieLayer* precomp); LottieObject* parseGroup(); LottieRect* parseRect(); diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.cpp index 4c7b355ac..51673fd82 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.cpp @@ -230,4 +230,10 @@ void LookaheadParserHandler::skip(const char* key) } else { skipOut(0); } +} + + +char* LookaheadParserHandler::getPos() +{ + return iss.src_; } \ No newline at end of file diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.h index 2a2d77931..150912e93 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieParserHandler.h @@ -195,6 +195,7 @@ struct LookaheadParserHandler void skip(const char* key); void skipOut(int depth); int peekType(); + char* getPos(); }; #endif //_TVG_LOTTIE_PARSER_HANDLER_H_ \ No newline at end of file diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieProperty.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieProperty.h index 66acac8ff..932b5e79e 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieProperty.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieProperty.h @@ -112,7 +112,7 @@ struct LottieVectorFrame //Property would have an either keyframes or single value. struct LottieProperty { - enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid }; + enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Image, Invalid }; LottieExpression* exp = nullptr; Type type; @@ -261,6 +261,12 @@ struct LottieGenericProperty : LottieProperty LottieGenericProperty(T v) : value(v) {} LottieGenericProperty() {} + LottieGenericProperty(const LottieGenericProperty& rhs) + { + copy(rhs); + type = rhs.type; + } + ~LottieGenericProperty() { release(); @@ -329,14 +335,17 @@ struct LottieGenericProperty : LottieProperty return operator()(frameNo); } - LottieGenericProperty& operator=(const LottieGenericProperty& other) + void copy(const LottieGenericProperty& rhs, bool shallow = true) { - //shallow copy, used for slot overriding - if (other.frames) { - frames = other.frames; - const_cast&>(other).frames = nullptr; - } else value = other.value; - return *this; + if (rhs.frames) { + if (shallow) { + frames = rhs.frames; + const_cast&>(rhs).frames = nullptr; + } else { + frames = new Array>; + *frames = *rhs.frames; + } + } else value = rhs.value; } float angle(float frameNo) { return 0; } @@ -504,6 +513,14 @@ struct LottieColorStop : LottieProperty uint16_t count = 0; //colorstop count bool populated = false; + LottieColorStop() {} + + LottieColorStop(const LottieColorStop& rhs) + { + copy(rhs); + type = rhs.type; + } + ~LottieColorStop() { release(); @@ -608,20 +625,22 @@ struct LottieColorStop : LottieProperty return fill->colorStops(result.data, count); } - LottieColorStop& operator=(const LottieColorStop& other) + void copy(const LottieColorStop& rhs, bool shallow = true) { - //shallow copy, used for slot overriding - if (other.frames) { - frames = other.frames; - const_cast(other).frames = nullptr; + if (rhs.frames) { + if (shallow) { + frames = rhs.frames; + const_cast(rhs).frames = nullptr; + } else { + frames = new Array>; + *frames = *rhs.frames; + } } else { - value = other.value; - const_cast(other).value = {nullptr, nullptr}; + value = rhs.value; + const_cast(rhs).value = ColorStop(); } - populated = other.populated; - count = other.count; - - return *this; + populated = rhs.populated; + count = rhs.count; } void prepare() {} @@ -735,6 +754,14 @@ struct LottieTextDoc : LottieProperty Array>* frames = nullptr; TextDocument value; + LottieTextDoc() {} + + LottieTextDoc(const LottieTextDoc& rhs) + { + copy(rhs); + type = rhs.type; + } + ~LottieTextDoc() { release(); @@ -808,24 +835,85 @@ struct LottieTextDoc : LottieProperty return frame->value; } - LottieTextDoc& operator=(const LottieTextDoc& other) + void copy(const LottieTextDoc& rhs, bool shallow = true) { - //shallow copy, used for slot overriding - if (other.frames) { - frames = other.frames; - const_cast(other).frames = nullptr; + if (rhs.frames) { + if (shallow) { + frames = rhs.frames; + const_cast(rhs).frames = nullptr; + } else { + frames = new Array>; + *frames = *rhs.frames; + } } else { - value = other.value; - const_cast(other).value.text = nullptr; - const_cast(other).value.name = nullptr; + value = rhs.value; + const_cast(rhs).value.text = nullptr; + const_cast(rhs).value.name = nullptr; } - return *this; } void prepare() {} }; +struct LottieBitmap : LottieProperty +{ + union { + char* b64Data = nullptr; + char* path; + }; + char* mimeType = nullptr; + uint32_t size = 0; + float width = 0.0f; + float height = 0.0f; + + LottieBitmap() {} + + LottieBitmap(const LottieBitmap& rhs) + { + copy(rhs); + type = rhs.type; + } + + ~LottieBitmap() + { + release(); + } + + void release() + { + free(b64Data); + free(mimeType); + + b64Data = nullptr; + mimeType = nullptr; + } + + uint32_t frameCnt() override { return 0; } + uint32_t nearest(float time) override { return 0; } + float frameNo(int32_t key) override { return 0; } + + void copy(const LottieBitmap& rhs, bool shallow = true) + { + if (shallow) { + b64Data = rhs.b64Data; + mimeType = rhs.mimeType; + } else { + //TODO: optimize here by avoiding data copy + TVGLOG("LOTTIE", "Shallow copy of the image data!"); + b64Data = strdup(rhs.b64Data); + mimeType = strdup(rhs.mimeType); + } + size = rhs.size; + width = rhs.width; + height = rhs.height; + + const_cast(rhs).b64Data = nullptr; + const_cast(rhs).mimeType = nullptr; + } +}; + + using LottiePoint = LottieGenericProperty; using LottieFloat = LottieGenericProperty; using LottieOpacity = LottieGenericProperty; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieRenderPooler.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieRenderPooler.h index 90216a2ba..036631e19 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieRenderPooler.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/lottie/tvgLottieRenderPooler.h @@ -36,7 +36,7 @@ struct LottieRenderPooler ~LottieRenderPooler() { for (auto p = pooler.begin(); p < pooler.end(); ++p) { - if (PP(*p)->unref() == 0) delete(*p); + (*p)->unref(); } } @@ -44,15 +44,16 @@ struct LottieRenderPooler { //return available one. for (auto p = pooler.begin(); p < pooler.end(); ++p) { - if (PP(*p)->refCnt == 1) return *p; + if ((*p)->refCnt() == 1) return *p; } //no empty, generate a new one. - auto p = copy ? static_cast(pooler[0]->duplicate()) : T::gen().release(); - PP(p)->ref(); + auto p = copy ? static_cast(pooler[0]->duplicate()) : T::gen(); + p->ref(); pooler.push(p); return p; } }; + #endif //_TVG_LOTTIE_RENDER_POOLER_H_ \ No newline at end of file diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.cpp index 52e3185e8..270c68bd1 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -2499,6 +2499,18 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char } +static SvgColor* _findLatestColor(const SvgLoaderData* loader) +{ + auto parent = loader->stack.count > 0 ? loader->stack.last() : loader->doc; + + while (parent != nullptr) { + if (parent->style->curColorSet) return &parent->style->color; + parent = parent->parent; + } + return nullptr; +} + + static bool _attrParseStopsStyle(void* data, const char* key, const char* value) { SvgLoaderData* loader = (SvgLoaderData*)data; @@ -2508,7 +2520,13 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value) stop->a = _toOpacity(value); loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity); } else if (!strcmp(key, "stop-color")) { - if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) { + if (!strcmp(value, "currentColor")) { + if (auto latestColor = _findLatestColor(loader)) { + stop->r = latestColor->r; + stop->g = latestColor->g; + stop->b = latestColor->b; + } + } else if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) { loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); } } else { @@ -2531,7 +2549,13 @@ static bool _attrParseStops(void* data, const char* key, const char* value) stop->a = _toOpacity(value); } } else if (!strcmp(key, "stop-color")) { - if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { + if (!strcmp(value, "currentColor")) { + if (auto latestColor = _findLatestColor(loader)) { + stop->r = latestColor->r; + stop->g = latestColor->g; + stop->b = latestColor->b; + } + } else if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); } } else if (!strcmp(key, "style")) { @@ -3756,7 +3780,7 @@ void SvgLoader::run(unsigned tid) //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLOAT_EPSILON || fabsf(vh) <= FLOAT_EPSILON)) { TVGLOG("SVG", "The width and/or height set to 0 - rendering disabled."); - root = Scene::gen().release(); + root = Scene::gen(); return; } @@ -3873,7 +3897,7 @@ bool SvgLoader::header() } -bool SvgLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& rpath, bool copy, const ColorReplace& colorReplacement) +bool SvgLoader::open(const char* data, uint32_t size, TVG_UNUSED const char* rpath, bool copy, const ColorReplace& colorReplacement) { clear(); @@ -3891,8 +3915,9 @@ bool SvgLoader::open(const char* data, uint32_t size, TVG_UNUSED const string& r } -bool SvgLoader::open(const string& path, const ColorReplace& colorReplacement) +bool SvgLoader::open(const char* path, const ColorReplace& colorReplacement) { +#ifdef THORVG_FILE_IO_SUPPORT clear(); ifstream f; @@ -3910,6 +3935,9 @@ bool SvgLoader::open(const string& path, const ColorReplace& colorReplacement) size = filePath.size(); return header(); +#else + return false; +#endif } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.h b/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.h index adcdbd514..8cca42891 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgLoader.h @@ -42,8 +42,8 @@ class SvgLoader : public ImageLoader, public Task SvgLoader(); ~SvgLoader(); - bool open(const string& path, const ColorReplace& colorReplacement) override; - bool open(const char* data, uint32_t size, const string& rpath, bool copy, const ColorReplace& colorReplacement) override; + bool open(const char* path, const ColorReplace& colorReplacement) override; + bool open(const char* data, uint32_t size, const char* rpath, bool copy, const ColorReplace& colorReplacement) override; bool resize(Paint* paint, float w, float h) override; bool read() override; bool close() override; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp index 9a193da9d..7aa2d34a4 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -36,7 +36,7 @@ /************************************************************************/ static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform); -static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth); +static Scene* _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth); static inline bool _isGroupType(SvgNodeType type) @@ -84,7 +84,7 @@ static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf) } -static unique_ptr _applyLinearGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) +static LinearGradient* _applyLinearGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop* stops; auto fillGrad = LinearGradient::gen(); @@ -130,7 +130,7 @@ static unique_ptr _applyLinearGradientProperty(SvgStyleGradient* } -static unique_ptr _applyRadialGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) +static RadialGradient* _applyRadialGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop *stops; auto fillGrad = RadialGradient::gen(); @@ -239,8 +239,8 @@ static Paint* _applyComposition(SvgLoaderData& loaderData, Paint* paint, const S if (!validClip && !validMask) return paint; - Scene* scene = Scene::gen().release(); - scene->push(tvg::cast(paint)); + auto scene = Scene::gen(); + scene->push(paint); if (validClip) { node->style->clipPath.applying = true; @@ -250,13 +250,13 @@ static Paint* _applyComposition(SvgLoaderData& loaderData, Paint* paint, const S auto valid = false; //Composite only when valid shapes exist for (uint32_t i = 0; i < clipNode->child.count; ++i, ++child) { - if (_appendClipChild(loaderData, *child, clipper.get(), vBox, svgPath, clipNode->child.count > 1)) valid = true; + if (_appendClipChild(loaderData, *child, clipper, vBox, svgPath, clipNode->child.count > 1)) valid = true; } if (valid) { Matrix finalTransform = _compositionTransform(paint, node, clipNode, SvgNodeType::ClipPath); clipper->transform(finalTransform); - scene->clip(std::move(clipper)); + scene->clip(clipper); } node->style->clipPath.applying = false; @@ -273,7 +273,7 @@ static Paint* _applyComposition(SvgLoaderData& loaderData, Paint* paint, const S } else if (node->transform) { mask->transform(*node->transform); } - scene->mask(std::move(mask), maskNode->node.mask.type == SvgMaskType::Luminance ? MaskMethod::Luma: MaskMethod::Alpha); + scene->mask(mask, maskNode->node.mask.type == SvgMaskType::Luminance ? MaskMethod::Luma: MaskMethod::Alpha); } node->style->mask.applying = false; @@ -298,11 +298,9 @@ static Paint* _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg auto bBox = vBox; if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg); if (style->fill.paint.gradient->type == SvgGradientType::Linear) { - auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); - vg->fill(std::move(linear)); + vg->fill(_applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { - auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); - vg->fill(std::move(radial)); + vg->fill(_applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } } else if (style->fill.paint.url) { TVGLOG("SVG", "The fill's url not supported."); @@ -334,11 +332,9 @@ static Paint* _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg auto bBox = vBox; if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg); if (style->stroke.paint.gradient->type == SvgGradientType::Linear) { - auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity); - vg->strokeFill(std::move(linear)); + vg->strokeFill(_applyLinearGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity)); } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) { - auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity); - vg->strokeFill(std::move(radial)); + vg->strokeFill(_applyRadialGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity)); } } else if (style->stroke.paint.url) { //TODO: Apply the color pointed by url @@ -414,8 +410,8 @@ static bool _recognizeShape(SvgNode* node, Shape* shape) static Paint* _shapeBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath) { auto shape = Shape::gen(); - if (!_recognizeShape(node, shape.get())) return nullptr; - return _applyProperty(loaderData, node, shape.release(), vBox, svgPath, false); + if (!_recognizeShape(node, shape)) return nullptr; + return _applyProperty(loaderData, node, shape, vBox, svgPath, false); } @@ -584,7 +580,7 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const if (node->transform) m = *node->transform * m; picture->transform(m); - return _applyComposition(loaderData, picture.release(), node, vBox, svgPath); + return _applyComposition(loaderData, picture, node, vBox, svgPath); } @@ -661,7 +657,7 @@ static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMee } -static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth) +static Scene* _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth) { auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1); @@ -709,7 +705,7 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod } viewBoxClip->transform(mClipTransform); - scene->clip(std::move(viewBoxClip)); + scene->clip(viewBoxClip); } } else { scene->transform(mUseTransform); @@ -729,11 +725,9 @@ static void _applyTextFill(SvgStyleProperty* style, Text* text, const Box& vBox) if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(text); if (style->fill.paint.gradient->type == SvgGradientType::Linear) { - auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); - text->fill(std::move(linear)); + text->fill(_applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { - auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity); - text->fill(std::move(radial)); + text->fill(_applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity)); } } else if (style->fill.paint.url) { //TODO: Apply the color pointed by url @@ -755,7 +749,7 @@ static Paint* _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, c auto textNode = &node->node.text; if (!textNode->text) return nullptr; - auto text = Text::gen().release(); + auto text = Text::gen(); Matrix textTransform; if (node->transform) textTransform = *node->transform; @@ -776,7 +770,7 @@ static Paint* _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, c } -static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth) +static Scene* _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth) { /* Exception handling: Prevent invalid SVG data input. The size is the arbitrary value, we need an experimental size. */ @@ -808,12 +802,12 @@ static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgN else if ((*child)->type != SvgNodeType::Mask) paint = _shapeBuildHelper(loaderData, *child, vBox, svgPath); if (paint) { if ((*child)->id) paint->id = djb2Encode((*child)->id); - scene->push(tvg::cast(paint)); + scene->push(paint); } } } scene->opacity(node->style->opacity); - return tvg::cast(_applyComposition(loaderData, scene.release(), node, vBox, svgPath)); + return static_cast(_applyComposition(loaderData, scene, node, vBox, svgPath)); } @@ -849,7 +843,7 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe auto docNode = _sceneBuildHelper(loaderData, loaderData.doc, vBox, svgPath, false, 0); - if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag); + if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode, vBox, w, h, viewFlag); if (!tvg::equal(w, vBox.w) || !tvg::equal(h, vBox.h)) { Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox); @@ -862,11 +856,8 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe viewBoxClip->appendRect(0, 0, w, h); auto clippingLayer = Scene::gen(); - clippingLayer->clip(std::move(viewBoxClip)); - clippingLayer->push(std::move(docNode)); - - auto root = Scene::gen(); - root->push(std::move(clippingLayer)); + clippingLayer->clip(viewBoxClip); + clippingLayer->push(docNode); loaderData.doc->node.doc.vx = vBox.x; loaderData.doc->node.doc.vy = vBox.y; @@ -875,5 +866,8 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe loaderData.doc->node.doc.w = w; loaderData.doc->node.doc.h = h; - return root.release(); + auto root = Scene::gen(); + root->push(clippingLayer); + + return root; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/config.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/config.h index 036311a3f..35495a872 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/config.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/config.h @@ -5,6 +5,7 @@ //#define THORVG_JPG_LOADER_SUPPORT #define THORVG_LOTTIE_LOADER_SUPPORT #define THORVG_THREAD_SUPPORT +#define THORVG_FILE_IO_SUPPORT #if defined(__ARM_NEON__) || defined(__aarch64__) #define THORVG_NEON_VECTOR_SUPPORT #else diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwCommon.h index 825bf3cf4..4ced7d5ed 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwCommon.h @@ -558,9 +558,9 @@ void mpoolRetDashOutline(SwMpool* mpool, unsigned idx); bool rasterCompositor(SwSurface* surface); bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); -bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c); bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity); -bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0); void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); @@ -572,9 +572,9 @@ void rasterUnpremultiply(RenderSurface* surface); void rasterPremultiply(RenderSurface* surface); bool rasterConvertCS(RenderSurface* surface, ColorSpace to); -bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params, bool direct); +bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params); bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect); -bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, bool direct); +bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct); bool effectDropShadowPrepare(RenderEffectDropShadow* effect); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp index 01e9e66d0..4168917b7 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp @@ -163,7 +163,7 @@ bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* params) } -bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params, TVG_UNUSED bool direct) +bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params) { if (cmp->image.channelSize != sizeof(uint32_t)) { TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!"); @@ -237,7 +237,7 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i } auto iarr = 1.0f / (dimension + dimension + 1); - //#pragma omp parallel for + #pragma omp parallel for for (int y = 0; y < h; ++y) { auto p = y * stride; auto i = p; //current index @@ -335,7 +335,7 @@ bool effectDropShadowPrepare(RenderEffectDropShadow* params) //A quite same integration with effectGaussianBlur(). See it for detailed comments. //surface[0]: the original image, to overlay it into the filtered image. //surface[1]: temporary buffer for generating the filtered image. -bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, bool direct) +bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct) { if (cmp->image.channelSize != sizeof(uint32_t)) { TVGERR("SW_ENGINE", "Not supported grayscale Drop Shadow!"); @@ -357,7 +357,7 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe auto stride = cmp->image.stride; auto front = cmp->image.buf32; auto back = buffer[1]->buf32; - auto opacity = params->color[3]; + opacity = MULTIPLY(params->color[3], opacity); TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level); diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp index 944a361cc..b556bb8ef 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp @@ -324,7 +324,7 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t /* Rect */ /************************************************************************/ -static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t a) { auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); @@ -343,7 +343,7 @@ static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region, } -static bool _rasterDirectMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterDirectMaskedRect(SwSurface* surface, const SwBBox& region, SwMask maskOp, uint8_t a) { auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); @@ -364,7 +364,7 @@ static bool _rasterDirectMaskedRect(SwSurface* surface, const SwBBox& region, Sw } -static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { //8bit masking channels composition if (surface->channelSize != sizeof(uint8_t)) return false; @@ -372,13 +372,13 @@ static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y); auto maskOp = _getMaskOp(surface->compositor->method); - if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, region, maskOp, r, g, b, a); - else return _rasterCompositeMaskedRect(surface, region, maskOp, r, g, b, a); + if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, region, maskOp, c.a); + else return _rasterCompositeMaskedRect(surface, region, maskOp, c.a); return false; } -static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); @@ -390,7 +390,7 @@ static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; @@ -407,7 +407,7 @@ static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t auto dst = &buffer[y * surface->stride]; auto cmp = &cbuffer[y * surface->compositor->image.stride * csize]; for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) { - *dst = INTERPOLATE8(a, *dst, alpha(cmp)); + *dst = INTERPOLATE8(c.a, *dst, alpha(cmp)); } } } @@ -415,13 +415,13 @@ static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t } -static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { if (surface->channelSize != sizeof(uint32_t)) return false; auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; for (uint32_t y = 0; y < h; ++y) { @@ -434,26 +434,26 @@ static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, uint8_ } -static bool _rasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { #if defined(THORVG_AVX_VECTOR_SUPPORT) - return avxRasterTranslucentRect(surface, region, r, g, b, a); + return avxRasterTranslucentRect(surface, region, c); #elif defined(THORVG_NEON_VECTOR_SUPPORT) - return neonRasterTranslucentRect(surface, region, r, g, b, a); + return neonRasterTranslucentRect(surface, region, c); #else - return cRasterTranslucentRect(surface, region, r, g, b, a); + return cRasterTranslucentRect(surface, region, c); #endif } -static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b) +static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, 255); + auto color = surface->join(c.r, c.g, c.b, 255); auto buffer = surface->buf32 + (region.min.y * surface->stride); for (uint32_t y = 0; y < h; ++y) { rasterPixel32(buffer + y * surface->stride, color, region.min.x, w); @@ -471,16 +471,16 @@ static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r } -static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { if (_compositing(surface)) { - if (_matting(surface)) return _rasterMattedRect(surface, region, r, g, b, a); - else return _rasterMaskedRect(surface, region, r, g, b, a); + if (_matting(surface)) return _rasterMattedRect(surface, region, c); + else return _rasterMaskedRect(surface, region, c); } else if (_blending(surface)) { - return _rasterBlendingRect(surface, region, r, g, b, a); + return _rasterBlendingRect(surface, region, c); } else { - if (a == 255) return _rasterSolidRect(surface, region, r, g, b); - else return _rasterTranslucentRect(surface, region, r, g, b, a); + if (c.a == 255) return _rasterSolidRect(surface, region, c); + else return _rasterTranslucentRect(surface, region, c); } return false; } @@ -490,7 +490,7 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin /* Rle */ /************************************************************************/ -static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t a) { auto span = rle->spans; auto cbuffer = surface->compositor->image.buf8; @@ -510,7 +510,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask mas } -static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t a) { auto span = rle->spans; auto cbuffer = surface->compositor->image.buf8; @@ -531,7 +531,7 @@ static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp } -static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, const RenderColor& c) { TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method); @@ -539,13 +539,13 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t if (surface->channelSize != sizeof(uint8_t)) return false; auto maskOp = _getMaskOp(surface->compositor->method); - if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, r, g, b, a); - else return _rasterCompositeMaskedRle(surface, rle, maskOp, r, g, b, a); + if (_direct(surface->compositor->method)) return _rasterDirectMaskedRle(surface, rle, maskOp, c.a); + else return _rasterCompositeMaskedRle(surface, rle, maskOp, c.a); return false; } -static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, const RenderColor& c) { TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method); @@ -557,7 +557,7 @@ static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t //32bit channels if (surface->channelSize == sizeof(uint32_t)) { uint32_t src; - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; @@ -574,8 +574,8 @@ static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; - if (span->coverage == 255) src = a; - else src = MULTIPLY(a, span->coverage); + if (span->coverage == 255) src = c.a; + else src = MULTIPLY(c.a, span->coverage); for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { *dst = INTERPOLATE8(src, *dst, alpha(cmp)); } @@ -585,12 +585,12 @@ static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t } -static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) { if (surface->channelSize != sizeof(uint32_t)) return false; auto span = rle->spans; - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; @@ -609,25 +609,25 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, uint8_t r, } -static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) { #if defined(THORVG_AVX_VECTOR_SUPPORT) - return avxRasterTranslucentRle(surface, rle, r, g, b, a); + return avxRasterTranslucentRle(surface, rle, c); #elif defined(THORVG_NEON_VECTOR_SUPPORT) - return neonRasterTranslucentRle(surface, rle, r, g, b, a); + return neonRasterTranslucentRle(surface, rle, c); #else - return cRasterTranslucentRle(surface, rle, r, g, b, a); + return cRasterTranslucentRle(surface, rle, c); #endif } -static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b) +static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) { auto span = rle->spans; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, 255); + auto color = surface->join(c.r, c.g, c.b, 255); for (uint32_t i = 0; i < rle->size; ++i, ++span) { if (span->coverage == 255) { rasterPixel32(surface->buf32 + span->y * surface->stride, color, span->x, span->len); @@ -658,18 +658,18 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, uint8_t r, uin } -static bool _rasterRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool _rasterRle(SwSurface* surface, SwRle* rle, const RenderColor& c) { if (!rle) return false; if (_compositing(surface)) { - if (_matting(surface)) return _rasterMattedRle(surface, rle, r, g, b, a); - else return _rasterMaskedRle(surface, rle, r, g, b, a); + if (_matting(surface)) return _rasterMattedRle(surface, rle, c); + else return _rasterMaskedRle(surface, rle, c); } else if (_blending(surface)) { - return _rasterBlendingRle(surface, rle, r, g, b, a); + return _rasterBlendingRle(surface, rle, c); } else { - if (a == 255) return _rasterSolidRle(surface, rle, r, g, b); - else return _rasterTranslucentRle(surface, rle, r, g, b, a); + if (c.a == 255) return _rasterSolidRle(surface, rle, c); + else return _rasterTranslucentRle(surface, rle, c); } return false; } @@ -1724,7 +1724,8 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, if (auto color = fillFetchSolid(shape->fill, fdata)) { auto a = MULTIPLY(color->a, opacity); - return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true; + RenderColor c = {color->r, color->g, color->b, a}; + return a > 0 ? rasterShape(surface, shape, c) : true; } auto type = fdata->type(); @@ -1744,8 +1745,9 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false; if (auto color = fillFetchSolid(shape->stroke->fill, fdata)) { - auto a = MULTIPLY(color->a, opacity); - return a > 0 ? rasterStroke(surface, shape, color->r, color->g, color->b, a) : true; + RenderColor c = {color->r, color->g, color->b, color->a}; + c.a = MULTIPLY(c.a, opacity); + return c.a > 0 ? rasterStroke(surface, shape, c) : true; } auto type = fdata->type(); @@ -1756,27 +1758,27 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, } -bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c) { - if (a < 255) { - r = MULTIPLY(r, a); - g = MULTIPLY(g, a); - b = MULTIPLY(b, a); + if (c.a < 255) { + c.r = MULTIPLY(c.r, c.a); + c.g = MULTIPLY(c.g, c.a); + c.b = MULTIPLY(c.b, c.a); } - if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a); - else return _rasterRle(surface, shape->rle, r, g, b, a); + if (shape->fastTrack) return _rasterRect(surface, shape->bbox, c); + else return _rasterRle(surface, shape->rle, c); } -bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c) { - if (a < 255) { - r = MULTIPLY(r, a); - g = MULTIPLY(g, a); - b = MULTIPLY(b, a); + if (c.a < 255) { + c.r = MULTIPLY(c.r, c.a); + c.g = MULTIPLY(c.g, c.a); + c.b = MULTIPLY(c.b, c.a); } - return _rasterRle(surface, shape->strokeRle, r, g, b, a); + return _rasterRle(surface, shape->strokeRle, c); } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h index 79cab043f..bade3a558 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h @@ -99,17 +99,17 @@ static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32 } -static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - uint32_t ialpha = 255 - a; + uint32_t ialpha = 255 - c.a; auto avxColor = _mm_set1_epi32(color); auto avxIalpha = _mm_set1_epi8(ialpha); @@ -146,11 +146,11 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u } else if (surface->channelSize == sizeof(uint8_t)) { TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize); auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; - auto ialpha = ~a; + auto ialpha = ~c.a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { - *dst = a + MULTIPLY(*dst, ialpha); + *dst = c.a + MULTIPLY(*dst, ialpha); } } } @@ -158,13 +158,13 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u } -static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) { auto span = rle->spans; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); uint32_t src; for (uint32_t i = 0; i < rle->size; ++i) { @@ -215,9 +215,9 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_ uint8_t src; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; - if (span->coverage < 255) src = MULTIPLY(span->coverage, a); - else src = a; - auto ialpha = ~a; + if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); + else src = c.a; + auto ialpha = ~c.a; for (uint32_t x = 0; x < span->len; ++x, ++dst) { *dst = src + MULTIPLY(*dst, ialpha); } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterC.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterC.h index 8bea1b137..260f7ce77 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterC.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterC.h @@ -92,13 +92,13 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int } -static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) { auto span = rle->spans; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); uint32_t src; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; @@ -114,9 +114,9 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, u uint8_t src; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; - if (span->coverage < 255) src = MULTIPLY(span->coverage, a); - else src = a; - auto ialpha = ~a; + if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); + else src = c.a; + auto ialpha = ~c.a; for (uint32_t x = 0; x < span->len; ++x, ++dst) { *dst = src + MULTIPLY(*dst, ialpha); } @@ -126,16 +126,16 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, u } -static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto ialpha = 255 - a; + auto ialpha = 255 - c.a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { @@ -145,11 +145,11 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; - auto ialpha = ~a; + auto ialpha = ~c.a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { - *dst = a + MULTIPLY(*dst, ialpha); + *dst = c.a + MULTIPLY(*dst, ialpha); } } } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h index fe693b7f3..5dbdd6f78 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h @@ -89,13 +89,13 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3 } -static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) { auto span = rle->spans; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); uint32_t src; uint8x8_t *vDst = nullptr; uint16_t align; @@ -134,9 +134,9 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8 uint8_t src; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; - if (span->coverage < 255) src = MULTIPLY(span->coverage, a); - else src = a; - auto ialpha = ~a; + if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); + else src = c.a; + auto ialpha = ~c.a; for (uint32_t x = 0; x < span->len; ++x, ++dst) { *dst = src + MULTIPLY(*dst, ialpha); } @@ -146,16 +146,16 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8 } -static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c) { auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->join(r, g, b, a); + auto color = surface->join(c.r, c.g, c.b, c.a); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto ialpha = 255 - a; + auto ialpha = 255 - c.a; auto vColor = vdup_n_u32(color); auto vIalpha = vdup_n_u8((uint8_t) ialpha); @@ -186,11 +186,11 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, } else if (surface->channelSize == sizeof(uint8_t)) { TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize); auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; - auto ialpha = ~a; + auto ialpha = ~c.a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { - *dst = a + MULTIPLY(*dst, ialpha); + *dst = c.a + MULTIPLY(*dst, ialpha); } } } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index a3d87ef64..707978ea2 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -85,7 +85,7 @@ struct SwShapeTask : SwTask Additionally, the stroke style should not be dashed. */ bool antialiasing(float strokeWidth) { - return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst || rshape->strokeTrim() || rshape->stroke->color[3] < 255;; + return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst || rshape->strokeTrim() || rshape->stroke->color.a < 255; } float validStrokeWidth() @@ -95,7 +95,7 @@ struct SwShapeTask : SwTask auto width = rshape->stroke->width; if (tvg::zero(width)) return 0.0f; - if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f; + if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color.a, opacity) == 0)) return 0.0f; if (tvg::zero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12)); @@ -128,8 +128,7 @@ struct SwShapeTask : SwTask //Shape if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { - uint8_t alpha = 0; - rshape->fillColor(nullptr, nullptr, nullptr, &alpha); + auto alpha = rshape->color.a; alpha = MULTIPLY(alpha, opacity); visibleFill = (alpha > 0 || rshape->fill); shapeReset(&shape); @@ -270,25 +269,25 @@ static void _termEngine() static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity) { - uint8_t r, g, b, a; if (auto fill = task->rshape->fill) { rasterGradientShape(surface, &task->shape, fill, opacity); } else { - task->rshape->fillColor(&r, &g, &b, &a); - a = MULTIPLY(opacity, a); - if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); + RenderColor c; + task->rshape->fillColor(&c.r, &c.g, &c.b, &c.a); + c.a = MULTIPLY(opacity, c.a); + if (c.a > 0) rasterShape(surface, &task->shape, c); } } static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity) { - uint8_t r, g, b, a; if (auto strokeFill = task->rshape->strokeFill()) { rasterGradientStroke(surface, &task->shape, strokeFill, opacity); } else { - if (task->rshape->strokeFill(&r, &g, &b, &a)) { - a = MULTIPLY(opacity, a); - if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); + RenderColor c; + if (task->rshape->strokeFill(&c.r, &c.g, &c.b, &c.a)) { + c.a = MULTIPLY(opacity, c.a); + if (c.a > 0) rasterStroke(surface, &task->shape, c); } } } @@ -541,15 +540,27 @@ const RenderSurface* SwRenderer::mainSurface() } -SwSurface* SwRenderer::request(int channelSize) +SwSurface* SwRenderer::request(int channelSize, bool square) { SwSurface* cmp = nullptr; + uint32_t w, h; + + if (square) { + //Same Dimensional Size is demanded for the Post Processing Fast Flipping + w = h = std::max(surface->w, surface->h); + } else { + w = surface->w; + h = surface->h; + } //Use cached data for (auto p = compositors.begin(); p < compositors.end(); ++p) { - if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == channelSize) { - cmp = *p; - break; + auto cur = *p; + if (cur->compositor->valid && cur->compositor->image.channelSize == channelSize) { + if (w == cur->w && h == cur->h) { + cmp = *p; + break; + } } } @@ -558,15 +569,13 @@ SwSurface* SwRenderer::request(int channelSize) //Inherits attributes from main surface cmp = new SwSurface(surface); cmp->compositor = new SwCompositor; - cmp->compositor->image.data = (pixel_t*)malloc(channelSize * surface->stride * surface->h); - cmp->compositor->image.w = surface->w; - cmp->compositor->image.h = surface->h; - cmp->compositor->image.stride = surface->stride; + cmp->compositor->image.data = (pixel_t*)malloc(channelSize * w * h); + cmp->w = cmp->compositor->image.w = w; + cmp->h = cmp->compositor->image.h = h; + cmp->compositor->image.stride = w; cmp->compositor->image.direct = true; cmp->compositor->valid = true; cmp->channelSize = cmp->compositor->image.channelSize = channelSize; - cmp->w = cmp->compositor->image.w; - cmp->h = cmp->compositor->image.h; compositors.push(cmp); } @@ -578,7 +587,7 @@ SwSurface* SwRenderer::request(int channelSize) } -RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) +RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) { auto x = region.x; auto y = region.y; @@ -590,7 +599,7 @@ RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) //Out of boundary if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr; - auto cmp = request(CHANNEL_SIZE(cs)); + auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing)); //Boundary Check if (x < 0) x = 0; @@ -598,6 +607,8 @@ RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) if (x + w > sw) w = (sw - x); if (y + h > sh) h = (sh - y); + if (w == 0 || h == 0) return nullptr; + cmp->compositor->recoverSfc = surface; cmp->compositor->recoverCmp = surface->compositor; cmp->compositor->valid = false; @@ -649,7 +660,7 @@ bool SwRenderer::prepare(RenderEffect* effect) } -bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct) +bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) { if (effect->invalid) return false; @@ -657,14 +668,14 @@ bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, bool switch (effect->type) { case SceneEffect::GaussianBlur: { - return effectGaussianBlur(p, request(surface->channelSize), static_cast(effect), direct); + return effectGaussianBlur(p, request(surface->channelSize, true), static_cast(effect)); } case SceneEffect::DropShadow: { - auto cmp1 = request(surface->channelSize); + auto cmp1 = request(surface->channelSize, true); cmp1->compositor->valid = false; - auto cmp2 = request(surface->channelSize); + auto cmp2 = request(surface->channelSize, true); SwSurface* surfaces[] = {cmp1, cmp2}; - auto ret = effectDropShadow(p, surfaces, static_cast(effect), direct); + auto ret = effectDropShadow(p, surfaces, static_cast(effect), opacity, direct); cmp1->compositor->valid = true; return ret; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.h index 489a024bb..ca96da880 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRenderer.h @@ -55,13 +55,13 @@ class SwRenderer : public RenderMethod bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); bool mempool(bool shared); - RenderCompositor* target(const RenderRegion& region, ColorSpace cs) override; + RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override; bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override; bool endComposite(RenderCompositor* cmp) override; void clearCompositors(); bool prepare(RenderEffect* effect) override; - bool effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override; + bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) override; static SwRenderer* gen(); static bool init(uint32_t threads); @@ -79,7 +79,7 @@ class SwRenderer : public RenderMethod SwRenderer(); ~SwRenderer(); - SwSurface* request(int channelSize); + SwSurface* request(int channelSize, bool square); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags); }; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRle.cpp index e4563cbd6..c57e98bc9 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRle.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/sw_engine/tvgSwRle.cpp @@ -188,7 +188,6 @@ * http://www.freetype.org */ -#include #include #include #include "tvgSwCommon.h" @@ -243,8 +242,6 @@ struct RleWorker int bandSize; int bandShoot; - jmp_buf jmpBuf; - void* buffer; long bufferSize; @@ -359,7 +356,7 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor rle->spans = static_cast(realloc(rle->spans, rle->alloc * sizeof(SwSpan))); } } - + //Clip x range SwCoord xOver = 0; if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); @@ -418,7 +415,7 @@ static Cell* _findCell(RleWorker& rw) pcell = &cell->next; } - if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1); + if (rw.cellsCnt >= rw.maxCells) return nullptr; auto cell = rw.cells + rw.cellsCnt++; cell->x = x; @@ -431,17 +428,22 @@ static Cell* _findCell(RleWorker& rw) } -static void _recordCell(RleWorker& rw) +static bool _recordCell(RleWorker& rw) { if (rw.area | rw.cover) { auto cell = _findCell(rw); + + if (cell == nullptr) return false; + cell->area += rw.area; cell->cover += rw.cover; } + + return true; } -static void _setCell(RleWorker& rw, SwPoint pos) +static bool _setCell(RleWorker& rw, SwPoint pos) { /* Move the cell pointer to a new position. We set the `invalid' */ /* flag to indicate that the cell isn't part of those we're interested */ @@ -463,17 +465,21 @@ static void _setCell(RleWorker& rw, SwPoint pos) //Are we moving to a different cell? if (pos != rw.cellPos) { //Record the current one if it is valid - if (!rw.invalid) _recordCell(rw); + if (!rw.invalid) { + if (!_recordCell(rw)) return false; + } } rw.area = 0; rw.cover = 0; rw.cellPos = pos; rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt); + + return true; } -static void _startCell(RleWorker& rw, SwPoint pos) +static bool _startCell(RleWorker& rw, SwPoint pos) { if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x; if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x; @@ -483,23 +489,27 @@ static void _startCell(RleWorker& rw, SwPoint pos) rw.cellPos = pos - rw.cellMin; rw.invalid = false; - _setCell(rw, pos); + return _setCell(rw, pos); } -static void _moveTo(RleWorker& rw, const SwPoint& to) +static bool _moveTo(RleWorker& rw, const SwPoint& to) { //record current cell, if any */ - if (!rw.invalid) _recordCell(rw); + if (!rw.invalid) { + if (!_recordCell(rw)) return false; + } //start to a new position - _startCell(rw, TRUNC(to)); + if (!_startCell(rw, TRUNC(to))) return false; rw.pos = to; + + return true; } -static void _lineTo(RleWorker& rw, const SwPoint& to) +static bool _lineTo(RleWorker& rw, const SwPoint& to) { #define SW_UDIV(a, b) \ static_cast(((unsigned long)(a) * (unsigned long)(b)) >> \ @@ -511,7 +521,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) //vertical clipping if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) { rw.pos = to; - return; + return true; } auto line = rw.lineStack; @@ -539,7 +549,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) //any horizontal line } else if (diff.y == 0) { e1.x = e2.x; - _setCell(rw, e1); + if (!_setCell(rw, e1)) return false; } else if (diff.x == 0) { //vertical line up if (diff.y > 0) { @@ -549,7 +559,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) rw.area += (f2.y - f1.y) * f1.x * 2; f1.y = 0; ++e1.y; - _setCell(rw, e1); + if (!_setCell(rw, e1)) return false; } while(e1.y != e2.y); //vertical line down } else { @@ -559,7 +569,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) rw.area += (f2.y - f1.y) * f1.x * 2; f1.y = ONE_PIXEL; --e1.y; - _setCell(rw, e1); + if (!_setCell(rw, e1)) return false; } while(e1.y != e2.y); } //any other line @@ -612,7 +622,7 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) --e1.y; } - _setCell(rw, e1); + if (!_setCell(rw, e1)) return false; } while(e1 != e2); } @@ -622,12 +632,12 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) rw.area += (f2.y - f1.y) * (f1.x + f2.x); rw.pos = line[0]; - if (line-- == rw.lineStack) return; + if (line-- == rw.lineStack) return true; } } -static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) +static bool _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) { auto arc = rw.bezStack; arc[0] = to; @@ -691,14 +701,14 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, continue; draw: - _lineTo(rw, arc[0]); - if (arc == rw.bezStack) return; + if (!_lineTo(rw, arc[0])) return false; + if (arc == rw.bezStack) return true; arc -= 3; } } -static void _decomposeOutline(RleWorker& rw) +static bool _decomposeOutline(RleWorker& rw) { auto outline = rw.outline; auto first = 0; //index of first point in contour @@ -711,38 +721,43 @@ static void _decomposeOutline(RleWorker& rw) auto types = outline->types.data + first; ++types; - _moveTo(rw, UPSCALE(outline->pts[first])); + if (!_moveTo(rw, UPSCALE(outline->pts[first]))) return false; while (pt < limit) { //emit a single line_to if (types[0] == SW_CURVE_TYPE_POINT) { ++pt; ++types; - _lineTo(rw, UPSCALE(*pt)); + if (!_lineTo(rw, UPSCALE(*pt))) return false; //types cubic } else { pt += 3; types += 3; - if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); - else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); + if (pt <= limit) { + if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]))) return false; + } + else if (pt - 1 == limit) { + if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start)) return false; + } else goto close; } } close: - _lineTo(rw, start); + if (!_lineTo(rw, start)) return false; first = last + 1; } + + return true; } static int _genRle(RleWorker& rw) { - if (setjmp(rw.jmpBuf) == 0) { - _decomposeOutline(rw); - if (!rw.invalid) _recordCell(rw); - return 0; + if (!_decomposeOutline(rw)) return -1; + if (!rw.invalid) { + if (!_recordCell(rw)) return -1; } - return -1; //lack of cell memory + return 0; } @@ -1027,4 +1042,5 @@ void rleClip(SwRle *rle, const SwBBox* clip) _replaceClipSpan(rle, spans, spansEnd - spans); TVGLOG("SW_ENGINE", "Using Box Clipping!"); -} \ No newline at end of file +} + diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAccessor.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAccessor.cpp index 076ffab33..94b5dc3a5 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAccessor.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAccessor.cpp @@ -50,20 +50,28 @@ static bool accessChildren(Iterator* it, function func, void* data) noexcept +Result Accessor::set(Picture* picture, function func, void* data) noexcept { if (!picture || !func) return Result::InvalidArguments; //Use the Preorder Tree-Search + picture->ref(); + //Root - if (!func(picture, data)) return Result::Success; + if (!func(picture, data)) { + picture->unref(); + return Result::Success; + } //Children if (auto it = IteratorAccessor::iterator(picture)) { accessChildren(it, func, data); delete(it); } + + picture->unref(false); + return Result::Success; } @@ -86,7 +94,7 @@ Accessor::Accessor() : pImpl(nullptr) } -unique_ptr Accessor::gen() noexcept +Accessor* Accessor::gen() noexcept { - return unique_ptr(new Accessor); + return new Accessor; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.cpp index 6c4711e8c..4e7c8c4c0 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.cpp @@ -120,7 +120,7 @@ Result Animation::segment(float *begin, float *end) noexcept } -unique_ptr Animation::gen() noexcept +Animation* Animation::gen() noexcept { - return unique_ptr(new Animation); + return new Animation; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.h index 14212eb67..6eb5d4950 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgAnimation.h @@ -24,7 +24,6 @@ #define _TVG_ANIMATION_H_ #include "tvgCommon.h" -#include "tvgPaint.h" #include "tvgPicture.h" struct Animation::Impl @@ -33,15 +32,13 @@ struct Animation::Impl Impl() { - picture = Picture::gen().release(); - PP(picture)->ref(); + picture = Picture::gen(); + picture->ref(); } ~Impl() { - if (PP(picture)->unref() == 0) { - delete(picture); - } + picture->unref(); } }; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.cpp index a87b8ce6a..27ae181d2 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.cpp @@ -37,15 +37,15 @@ Canvas::~Canvas() } -list& Canvas::paints() noexcept +const list& Canvas::paints() const noexcept { - return pImpl->paints; + return pImpl->scene->paints(); } -Result Canvas::push(unique_ptr paint) noexcept +Result Canvas::push(Paint* target, Paint* at) noexcept { - return pImpl->push(std::move(paint)); + return pImpl->push(target, at); } @@ -68,6 +68,8 @@ Result Canvas::draw() noexcept Result Canvas::update(Paint* paint) noexcept { TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this); + + if (pImpl->scene->paints().empty() || pImpl->status == Status::Drawing) return Result::InsufficientCondition; auto ret = pImpl->update(paint, false); TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this); @@ -75,6 +77,12 @@ Result Canvas::update(Paint* paint) noexcept } +Result Canvas::remove(Paint* paint) noexcept +{ + return pImpl->remove(paint); +} + + Result Canvas::viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept { return pImpl->viewport(x, y, w, h); diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.h index 11519019a..c521cf26d 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCanvas.h @@ -25,18 +25,18 @@ #include "tvgPaint.h" - enum Status : uint8_t {Synced = 0, Updating, Drawing, Damaged}; struct Canvas::Impl { - list paints; + Scene* scene; RenderMethod* renderer; RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; Status status = Status::Synced; - Impl(RenderMethod* pRenderer) : renderer(pRenderer) + Impl(RenderMethod* pRenderer) : scene(Scene::gen()), renderer(pRenderer) { + scene->ref(); renderer->ref(); } @@ -45,78 +45,71 @@ struct Canvas::Impl //make it sure any deferred jobs renderer->sync(); - clearPaints(); - + scene->unref(); if (renderer->unref() == 0) delete(renderer); } - void clearPaints() - { - for (auto paint : paints) { - if (P(paint)->unref() == 0) delete(paint); - } - paints.clear(); - } - - Result push(unique_ptr paint) + Result push(Paint* target, Paint* at) { //You cannot push paints during rendering. - if (status == Status::Drawing) return Result::InsufficientCondition; + if (status == Status::Drawing) { + TVG_DELETE(target); + return Result::InsufficientCondition; + } - auto p = paint.release(); - if (!p) return Result::MemoryCorruption; - PP(p)->ref(); - paints.push_back(p); + auto ret = scene->push(target, at); + if (ret != Result::Success) return ret; - return update(p, true); + return update(target, true); } Result clear(bool paints, bool buffer) { + auto ret = Result::Success; + if (status == Status::Drawing) return Result::InsufficientCondition; //Clear render target - if (buffer) { - if (!renderer->clear()) return Result::InsufficientCondition; + if (buffer && !renderer->clear()) { + ret = Result::InsufficientCondition; } - if (paints) clearPaints(); + if (paints) scene->remove(); - return Result::Success; + return ret; } - Result update(Paint* paint, bool force) + Result remove(Paint* paint) { - if (paints.empty() || status == Status::Drawing) return Result::InsufficientCondition; + if (status == Status::Drawing) return Result::InsufficientCondition; + return scene->remove(paint); + } + + Result update(Paint* paint, bool force) + { Array clips; auto flag = RenderUpdateFlag::None; if (status == Status::Damaged || force) flag = RenderUpdateFlag::All; auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1}; - if (paint) { - paint->pImpl->update(renderer, m, clips, 255, flag); - } else { - for (auto paint : paints) { - paint->pImpl->update(renderer, m, clips, 255, flag); - } - } + if (paint) P(paint)->update(renderer, m, clips, 255, flag); + else PP(scene)->update(renderer, m, clips, 255, flag); + status = Status::Updating; return Result::Success; } Result draw() { + if (status == Status::Drawing || scene->paints().empty()) return Result::InsufficientCondition; + if (status == Status::Damaged) update(nullptr, false); - if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition; - bool rendered = false; - for (auto paint : paints) { - if (paint->pImpl->render(renderer)) rendered = true; - } + if (!renderer->preRender()) return Result::InsufficientCondition; - if (!rendered || !renderer->postRender()) return Result::InsufficientCondition; + if (!PP(scene)->render(renderer) || !renderer->postRender()) return Result::InsufficientCondition; status = Status::Drawing; return Result::Success; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCommon.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCommon.h index 75e26b3a7..8e56784cb 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCommon.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgCommon.h @@ -80,6 +80,9 @@ uint16_t THORVG_VERSION_NUMBER(); #define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl. +#define TVG_DELETE(PAINT) \ + if (PAINT->refCnt() == 0) delete(PAINT) + //for debugging #if 0 #include diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgFill.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgFill.cpp index 394f52413..ce3f0ff6d 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgFill.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgFill.cpp @@ -38,7 +38,7 @@ Fill* RadialGradient::Impl::duplicate() ret->pImpl->fy = fy; ret->pImpl->fr = fr; - return ret.release(); + return ret; } @@ -67,7 +67,7 @@ Fill* LinearGradient::Impl::duplicate() ret->pImpl->x2 = x2; ret->pImpl->y2 = y2; - return ret.release(); + return ret; }; @@ -182,9 +182,9 @@ Result RadialGradient::radial(float* cx, float* cy, float* r, float* fx, float* } -unique_ptr RadialGradient::gen() noexcept +RadialGradient* RadialGradient::gen() noexcept { - return unique_ptr(new RadialGradient); + return new RadialGradient; } @@ -228,9 +228,9 @@ Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const } -unique_ptr LinearGradient::gen() noexcept +LinearGradient* LinearGradient::gen() noexcept { - return unique_ptr(new LinearGradient); + return new LinearGradient; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgGlCanvas.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgGlCanvas.cpp index 24e2fb8b1..08f41f6b0 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgGlCanvas.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgGlCanvas.cpp @@ -59,9 +59,11 @@ GlCanvas::~GlCanvas() } -Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept +Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h, ColorSpace cs) noexcept { #ifdef THORVG_GL_RASTER_SUPPORT + if (cs != ColorSpace::ABGR8888S) return Result::NonSupport; + if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { return Result::InsufficientCondition; } @@ -83,11 +85,11 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept } -unique_ptr GlCanvas::gen() noexcept +GlCanvas* GlCanvas::gen() noexcept { #ifdef THORVG_GL_RASTER_SUPPORT if (GlRenderer::init() <= 0) return nullptr; - return unique_ptr(new GlCanvas); + return new GlCanvas; #endif return nullptr; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoadModule.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoadModule.h index 9e34b5e09..9e6fff767 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoadModule.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoadModule.h @@ -48,8 +48,8 @@ struct LoadModule if (pathcache) free(hashpath); } - virtual bool open(const string& path, const ColorReplace &colorReplacement) { return false; } - virtual bool open(const char* data, uint32_t size, const string& rpath, bool copy, const ColorReplace &colorReplacement) { return false; } + virtual bool open(const char* path, const ColorReplace &colorReplacement) { return false; } + virtual bool open(const char* data, uint32_t size, const char* rpath, bool copy, const ColorReplace &colorReplacement) { return false; } virtual bool resize(Paint* paint, float w, float h) { return false; } virtual void sync() {}; //finish immediately if any async update jobs. diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.cpp index 1e4ce454c..c809444e3 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.cpp @@ -159,6 +159,7 @@ static LoadModule* _find(FileType type) } +#ifdef THORVG_FILE_IO_SUPPORT static LoadModule* _findByPath(const char* filename) { auto ext = strExtension(filename); @@ -173,6 +174,7 @@ static LoadModule* _findByPath(const char* filename) if (!strcmp(ext, "otf") || !strcmp(ext, "otc")) return _find(FileType::Ttf); return nullptr; } +#endif static FileType _convert(const char* mimeType) @@ -204,14 +206,11 @@ static LoadModule* _findFromCache(const char* filename) { ScopedLock lock(key); - auto loader = _activeLoaders.head; - - while (loader) { + INLIST_FOREACH(_activeLoaders, loader) { if (loader->pathcache && !strcmp(loader->hashpath, filename)) { ++loader->sharing; return loader; } - loader = loader->next; } return nullptr; } @@ -223,16 +222,13 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const char* m if (type == FileType::Unknown) return nullptr; ScopedLock lock(key); - auto loader = _activeLoaders.head; - auto key = HASH_KEY(data); - while (loader) { + INLIST_FOREACH(_activeLoaders, loader) { if (loader->type == type && loader->hashkey == key) { ++loader->sharing; return loader; } - loader = loader->next; } return nullptr; } @@ -251,15 +247,12 @@ bool LoaderMgr::init() bool LoaderMgr::term() { - auto loader = _activeLoaders.head; - //clean up the remained font loaders which is globally used. - while (loader && loader->type == FileType::Ttf) { + INLIST_SAFE_FOREACH(_activeLoaders, loader) { + if (loader->type != FileType::Ttf) break; auto ret = loader->close(); - auto tmp = loader; - loader = loader->next; - _activeLoaders.remove(tmp); - if (ret) delete(tmp); + _activeLoaders.remove(loader); + if (ret) delete(loader); } return true; } @@ -279,8 +272,9 @@ bool LoaderMgr::retrieve(LoadModule* loader) } -LoadModule* LoaderMgr::loader(const char* filename, bool* invalid, std::unique_ptr colorReplacement) +LoadModule* LoaderMgr::loader(const char* filename, bool* invalid, ColorReplace *colorReplacement) { +#ifdef THORVG_FILE_IO_SUPPORT *invalid = false; //TODO: svg & lottie is not sharable. @@ -326,6 +320,7 @@ LoadModule* LoaderMgr::loader(const char* filename, bool* invalid, std::unique_p } } *invalid = true; +#endif return nullptr; } @@ -336,22 +331,19 @@ bool LoaderMgr::retrieve(const char* filename) } -LoadModule* LoaderMgr::loader(const char* key, std::unique_ptr colorReplacement) +LoadModule* LoaderMgr::loader(const char* key, ColorReplace *colorReplacement) { - auto loader = _activeLoaders.head; - - while (loader) { + INLIST_FOREACH(_activeLoaders, loader) { if (loader->pathcache && strstr(loader->hashpath, key)) { ++loader->sharing; return loader; } - loader = loader->next; } return nullptr; } -LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, std::unique_ptr colorReplacement) +LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, ColorReplace *colorReplacement) { //Note that users could use the same data pointer with the different content. //Thus caching is only valid for shareable. @@ -429,7 +421,7 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, Colo //loads fonts from memory - loader is cached (regardless of copy value) in order to access it while setting font -LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size, TVG_UNUSED const char* mimeType, bool copy, std::unique_ptr colorReplacement) +LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size, TVG_UNUSED const char* mimeType, bool copy, ColorReplace *colorReplacement) { #ifdef THORVG_TTF_LOADER_SUPPORT //TODO: add check for mimetype ? @@ -449,4 +441,4 @@ LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size, delete(loader); #endif return nullptr; -} \ No newline at end of file +} diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.h index fd70b0d08..ca94b9712 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgLoader.h @@ -29,11 +29,11 @@ struct LoaderMgr { static bool init(); static bool term(); - static LoadModule* loader(const char* filename, bool* invalid, std::unique_ptr colorReplacement); - static LoadModule* loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, std::unique_ptr colorReplacement); + static LoadModule* loader(const char* filename, bool* invalid, ColorReplace *colorReplacement); + static LoadModule* loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, ColorReplace *colorReplacement); static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy); - static LoadModule* loader(const char* name, const char* data, uint32_t size, const char* mimeType, bool copy, std::unique_ptr colorReplacement); - static LoadModule* loader(const char* key, std::unique_ptr colorReplacement); + static LoadModule* loader(const char* name, const char* data, uint32_t size, const char* mimeType, bool copy, ColorReplace *colorReplacement); + static LoadModule* loader(const char* key, ColorReplace *colorReplacement); static bool retrieve(const char* filename); static bool retrieve(LoadModule* loader); }; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.cpp index 94838f300..208f164e5 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.cpp @@ -163,8 +163,8 @@ Paint* Paint::Impl::duplicate(Paint* ret) ret->pImpl->opacity = opacity; - if (maskData) ret->pImpl->mask(ret, maskData->target->duplicate(), maskData->method); - if (clipper) ret->pImpl->clip(clipper->duplicate()); + if (maskData) ret->mask(maskData->target->duplicate(), maskData->method); + if (clipper) ret->clip(clipper->duplicate()); return ret; } @@ -216,7 +216,7 @@ bool Paint::Impl::render(RenderMethod* renderer) if (MASK_REGION_MERGING(maskData->method)) region.add(P(maskData->target)->bounds(renderer)); if (region.w == 0 || region.h == 0) return true; - cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method)); + cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking); if (renderer->beginComposite(cmp, MaskMethod::None, 255)) { maskData->target->pImpl->render(renderer); } @@ -357,12 +357,12 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme void Paint::Impl::reset() { if (clipper) { - delete(clipper); + clipper->unref(); clipper = nullptr; } if (maskData) { - if (P(maskData->target)->unref() == 0) delete(maskData->target); + maskData->target->unref(); free(maskData); maskData = nullptr; } @@ -374,7 +374,7 @@ void Paint::Impl::reset() blendMethod = BlendMethod::Normal; renderFlag = RenderUpdateFlag::None; - ctxFlag = ContextFlag::Invalid; + ctxFlag = ContextFlag::Default; opacity = 255; paint->id = 0; } @@ -442,25 +442,22 @@ Paint* Paint::duplicate() const noexcept } -Result Paint::clip(std::unique_ptr clipper) noexcept +Result Paint::clip(Paint* clipper) noexcept { - auto p = clipper.release(); - - if (p && p->type() != Type::Shape) { + if (clipper && clipper->type() != Type::Shape) { TVGERR("RENDERER", "Clipping only supports the Shape!"); + TVG_DELETE(clipper); return Result::NonSupport; } - pImpl->clip(p); + pImpl->clip(clipper); return Result::Success; } -Result Paint::mask(std::unique_ptr target, MaskMethod method) noexcept +Result Paint::mask(Paint* target, MaskMethod method) noexcept { - auto p = target.release(); - if (pImpl->mask(this, p, method)) return Result::Success; - - delete(p); + if (pImpl->mask(target, method)) return Result::Success; + if (target) TVG_DELETE(target); return Result::InvalidArguments; } @@ -505,4 +502,34 @@ Result Paint::blend(BlendMethod method) noexcept } return Result::Success; +} + + +uint8_t Paint::ref() noexcept +{ + if (pImpl->refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!"); + else ++pImpl->refCnt; + + return pImpl->refCnt; +} + + +uint8_t Paint::unref(bool free) noexcept +{ + if (pImpl->refCnt > 0) --pImpl->refCnt; + else TVGERR("RENDERER", "Corrupted Reference Count!"); + + if (free && pImpl->refCnt == 0) { + //TODO: use the global dismiss function? + delete(this); + return 0; + } + + return pImpl->refCnt; +} + + +uint8_t Paint::refCnt() const noexcept +{ + return pImpl->refCnt; } \ No newline at end of file diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.h index 95124fb98..001a0017b 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPaint.h @@ -28,7 +28,7 @@ namespace tvg { - enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1}; + enum ContextFlag : uint8_t {Default = 0, FastTrack = 1}; struct Iterator { @@ -76,7 +76,7 @@ namespace tvg uint8_t renderFlag; uint8_t ctxFlag; uint8_t opacity; - uint8_t refCnt = 0; //reference count + uint8_t refCnt = 0; //reference count Impl(Paint* pnt) : paint(pnt) { @@ -86,25 +86,13 @@ namespace tvg ~Impl() { if (maskData) { - if (P(maskData->target)->unref() == 0) delete(maskData->target); + maskData->target->unref(); free(maskData); } - if (clipper && P(clipper)->unref() == 0) delete(clipper); + if (clipper) clipper->unref(); if (renderer && (renderer->unref() == 0)) delete(renderer); } - uint8_t ref() - { - if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!"); - return ++refCnt; - } - - uint8_t unref() - { - if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!"); - return --refCnt; - } - bool transform(const Matrix& m) { if (&tr.m != &m) tr.m = m; @@ -124,28 +112,20 @@ namespace tvg void clip(Paint* clp) { - if (this->clipper) { - P(this->clipper)->unref(); - if (this->clipper != clp && P(this->clipper)->refCnt == 0) { - delete(this->clipper); - } - } - this->clipper = clp; + if (clipper) clipper->unref(clipper != clp); + clipper = clp; if (!clp) return; - P(clipper)->ref(); + clipper->ref(); } - bool mask(Paint* source, Paint* target, MaskMethod method) + bool mask(Paint* target, MaskMethod method) { //Invalid case if ((!target && method != MaskMethod::None) || (target && method == MaskMethod::None)) return false; if (maskData) { - P(maskData->target)->unref(); - if ((maskData->target != target) && P(maskData->target)->refCnt == 0) { - delete(maskData->target); - } + maskData->target->unref(maskData->target != target); //Reset scenario if (!target && method == MaskMethod::None) { free(maskData); @@ -156,9 +136,9 @@ namespace tvg if (!target && method == MaskMethod::None) return true; maskData = static_cast(malloc(sizeof(Mask))); } - P(target)->ref(); + target->ref(); maskData->target = target; - maskData->source = source; + maskData->source = paint; maskData->method = method; return true; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.cpp index fdceb66f4..48a09faa8 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.cpp @@ -56,16 +56,18 @@ RenderUpdateFlag Picture::Impl::load() } -bool Picture::Impl::needComposition(uint8_t opacity) +void Picture::Impl::queryComposition(uint8_t opacity) { + cFlag = CompositionFlag::Invalid; + //In this case, paint(scene) would try composition itself. - if (opacity < 255) return false; + if (opacity < 255) return; //Composition test const Paint* target; picture->mask(&target); - if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; - return true; + if (!target || target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return; + cFlag = CompositionFlag::Opacity; } @@ -77,8 +79,8 @@ bool Picture::Impl::render(RenderMethod* renderer) if (surface) return renderer->renderImage(rd); else if (paint) { RenderCompositor* cmp = nullptr; - if (needComp) { - cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + if (cFlag) { + cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast(cFlag)); renderer->beginComposite(cmp, MaskMethod::None, 255); } ret = paint->pImpl->render(renderer); @@ -142,9 +144,9 @@ Picture::~Picture() } -unique_ptr Picture::gen() noexcept +Picture* Picture::gen() noexcept { - return unique_ptr(new Picture); + return new Picture; } @@ -154,19 +156,24 @@ Type Picture::type() const noexcept } -Result Picture::load(const char* filename, std::unique_ptr colorReplacement) noexcept +Result Picture::load(const char* filename, ColorReplace *colorReplacement) noexcept { +#ifdef THORVG_FILE_IO_SUPPORT if (!filename) return Result::InvalidArguments; - return pImpl->load(filename, std::move(colorReplacement)); + return pImpl->load(filename, colorReplacement); +#else + TVGLOG("RENDERER", "FILE IO is disabled!"); + return Result::NonSupport; +#endif } -Result Picture::load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, std::unique_ptr colorReplacement) noexcept +Result Picture::load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, ColorReplace *colorReplacement) noexcept { if (!data || size <= 0) return Result::InvalidArguments; - return pImpl->load(data, size, mimeType, rpath, copy, std::move(colorReplacement)); + return pImpl->load(data, size, mimeType, rpath, copy, colorReplacement); } @@ -212,6 +219,9 @@ const Paint* Picture::paint(uint32_t id) noexcept return true; }; - tvg::Accessor::gen()->set(this, cb, &value); + auto accessor = tvg::Accessor::gen(); + accessor->set(this, cb, &value); + delete(accessor); + return value.ret; -} \ No newline at end of file +} diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.h index 6a450576f..ef8ac2197 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgPicture.h @@ -64,10 +64,10 @@ struct Picture::Impl RenderData rd = nullptr; //engine data float w = 0, h = 0; Picture* picture = nullptr; + uint8_t cFlag = CompositionFlag::Invalid; bool resizing = false; - bool needComp = false; //need composition - bool needComposition(uint8_t opacity); + void queryComposition(uint8_t opacity); bool render(RenderMethod* renderer); bool size(float w, float h); RenderRegion bounds(RenderMethod* renderer); @@ -107,7 +107,7 @@ struct Picture::Impl loader->resize(paint, w, h); resizing = false; } - needComp = needComposition(opacity) ? true : false; + queryComposition(opacity); rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false); } return rd; @@ -122,12 +122,12 @@ struct Picture::Impl return true; } - Result load(const char* filename, std::unique_ptr colorReplacement) + Result load(const char* filename, ColorReplace *colorReplacement) { if (paint || surface) return Result::InsufficientCondition; bool invalid; //Invalid Path - auto loader = static_cast(LoaderMgr::loader(filename, &invalid, std::move(colorReplacement))); + auto loader = static_cast(LoaderMgr::loader(filename, &invalid, colorReplacement)); if (!loader) { if (invalid) return Result::InvalidArguments; return Result::NonSupport; @@ -135,10 +135,10 @@ struct Picture::Impl return load(loader); } - Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, std::unique_ptr colorReplacement) + Result load(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy, ColorReplace *colorReplacement) { if (paint || surface) return Result::InsufficientCondition; - auto loader = static_cast(LoaderMgr::loader(data, size, mimeType, rpath, copy, std::move(colorReplacement))); + auto loader = static_cast(LoaderMgr::loader(data, size, mimeType, rpath, copy, colorReplacement)); if (!loader) return Result::NonSupport; return load(loader); } @@ -159,7 +159,7 @@ struct Picture::Impl load(); - auto picture = Picture::gen().release(); + auto picture = Picture::gen(); auto dup = picture->pImpl; if (paint) dup->paint = paint->duplicate(); diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgRender.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgRender.h index bc0e56fb8..ba735bcb3 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgRender.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgRender.h @@ -36,6 +36,7 @@ using RenderData = void*; using pixel_t = uint32_t; enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255}; +enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose struct RenderSurface { @@ -67,6 +68,11 @@ struct RenderSurface } }; +struct RenderColor +{ + uint8_t r, g, b, a; +}; + struct RenderCompositor { MaskMethod method; @@ -90,7 +96,7 @@ struct RenderRegion struct RenderStroke { float width = 0.0f; - uint8_t color[4] = {0, 0, 0, 0}; + RenderColor color{}; Fill *fill = nullptr; float* dashPattern = nullptr; uint32_t dashCnt = 0; @@ -109,8 +115,7 @@ struct RenderStroke void operator=(const RenderStroke& rhs) { width = rhs.width; - - memcpy(color, rhs.color, sizeof(color)); + color = rhs.color; delete(fill); if (rhs.fill) fill = rhs.fill->duplicate(); @@ -173,7 +178,7 @@ struct RenderShape } path; Fill *fill = nullptr; - uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a + RenderColor color{}; RenderStroke *stroke = nullptr; FillRule rule = FillRule::Winding; @@ -185,10 +190,10 @@ struct RenderShape void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const { - if (r) *r = color[0]; - if (g) *g = color[1]; - if (b) *b = color[2]; - if (a) *a = color[3]; + if (r) *r = color.r; + if (g) *g = color.g; + if (b) *b = color.b; + if (a) *a = color.a; } float strokeWidth() const @@ -209,10 +214,10 @@ struct RenderShape { if (!stroke) return false; - if (r) *r = stroke->color[0]; - if (g) *g = stroke->color[1]; - if (b) *b = stroke->color[2]; - if (a) *a = stroke->color[3]; + if (r) *r = stroke->color.r; + if (g) *g = stroke->color.g; + if (b) *b = stroke->color.b; + if (a) *a = stroke->color.a; return true; } @@ -334,12 +339,12 @@ class RenderMethod virtual bool clear() = 0; virtual bool sync() = 0; - virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs) = 0; + virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) = 0; virtual bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) = 0; virtual bool endComposite(RenderCompositor* cmp) = 0; virtual bool prepare(RenderEffect* effect) = 0; - virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, bool direct) = 0; + virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) = 0; }; static inline bool MASK_REGION_MERGING(MaskMethod method) diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaveModule.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaveModule.h index e610dca1b..6ea7e0b8c 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaveModule.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaveModule.h @@ -33,8 +33,8 @@ class SaveModule public: virtual ~SaveModule() {} - virtual bool save(Paint* paint, Paint* bg, const string& path, uint32_t quality) = 0; - virtual bool save(Animation* animation, Paint* bg, const string& path, uint32_t quality, uint32_t fps) = 0; + virtual bool save(Paint* paint, Paint* bg, const char* filename, uint32_t quality) = 0; + virtual bool save(Animation* animation, Paint* bg, const char* filename, uint32_t quality, uint32_t fps) = 0; virtual bool close() = 0; }; diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaver.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaver.cpp index 6bf9fca10..a5c8495b7 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaver.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSaver.cpp @@ -20,10 +20,10 @@ * SOFTWARE. */ +#include #include "tvgCommon.h" #include "tvgStr.h" #include "tvgSaveModule.h" -#include "tvgPaint.h" #ifdef THORVG_GIF_SAVER_SUPPORT #include "tvgGifSaver.h" @@ -41,7 +41,7 @@ struct Saver::Impl ~Impl() { delete(saveModule); - delete(bg); + if (bg) bg->unref(); } }; @@ -101,71 +101,72 @@ Saver::~Saver() } -Result Saver::save(unique_ptr paint, const char* filename, uint32_t quality) noexcept +Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcept { - auto p = paint.release(); - if (!p) return Result::MemoryCorruption; + if (!paint) return Result::InvalidArguments; //Already on saving another resource. if (pImpl->saveModule) { - if (P(p)->refCnt == 0) delete(p); + TVG_DELETE(paint); return Result::InsufficientCondition; } if (auto saveModule = _find(filename)) { - if (saveModule->save(p, pImpl->bg, filename, quality)) { + if (saveModule->save(paint, pImpl->bg, filename, quality)) { pImpl->saveModule = saveModule; return Result::Success; } else { - if (P(p)->refCnt == 0) delete(p); + TVG_DELETE(paint); delete(saveModule); return Result::Unknown; } } - if (P(p)->refCnt == 0) delete(p); + TVG_DELETE(paint); return Result::NonSupport; } -Result Saver::background(unique_ptr paint) noexcept +Result Saver::background(Paint* paint) noexcept { - delete(pImpl->bg); - pImpl->bg = paint.release(); + if (!paint) return Result::InvalidArguments; + + if (pImpl->bg) TVG_DELETE(pImpl->bg); + paint->ref(); + pImpl->bg = paint; return Result::Success; } -Result Saver::save(unique_ptr animation, const char* filename, uint32_t quality, uint32_t fps) noexcept +Result Saver::save(Animation* animation, const char* filename, uint32_t quality, uint32_t fps) noexcept { - auto a = animation.release(); - if (!a) return Result::MemoryCorruption; + if (!animation) return Result::InvalidArguments; //animation holds the picture, it must be 1 at the bottom. - auto remove = PP(a->picture())->refCnt <= 1 ? true : false; + auto remove = animation->picture()->refCnt() <= 1 ? true : false; - if (tvg::zero(a->totalFrame())) { - if (remove) delete(a); + if (tvg::zero(animation->totalFrame())) { + if (remove) delete(animation); return Result::InsufficientCondition; } //Already on saving another resource. if (pImpl->saveModule) { - if (remove) delete(a); + if (remove) delete(animation); return Result::InsufficientCondition; } if (auto saveModule = _find(filename)) { - if (saveModule->save(a, pImpl->bg, filename, quality, fps)) { + if (saveModule->save(animation, pImpl->bg, filename, quality, fps)) { pImpl->saveModule = saveModule; return Result::Success; } else { - if (remove) delete(a); + if (remove) delete(animation); delete(saveModule); return Result::Unknown; } } - if (remove) delete(a); + if (remove) delete(animation); return Result::NonSupport; } @@ -181,7 +182,7 @@ Result Saver::sync() noexcept } -unique_ptr Saver::gen() noexcept +Saver* Saver::gen() noexcept { - return unique_ptr(new Saver); + return new Saver; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.cpp index b86fb762f..b846c75e9 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.cpp @@ -55,9 +55,9 @@ Scene::~Scene() } -unique_ptr Scene::gen() noexcept +Scene* Scene::gen() noexcept { - return unique_ptr(new Scene); + return new Scene; } @@ -67,26 +67,23 @@ Type Scene::type() const noexcept } -Result Scene::push(unique_ptr paint) noexcept +Result Scene::push(Paint* target, Paint* at) noexcept { - auto p = paint.release(); - if (!p) return Result::MemoryCorruption; - PP(p)->ref(); - pImpl->paints.push_back(p); + if (!target) return Result::InvalidArguments; + target->ref(); - return Result::Success; + return pImpl->insert(target, at); } -Result Scene::clear(bool free) noexcept +Result Scene::remove(Paint* paint) noexcept { - pImpl->clear(free); - - return Result::Success; + if (paint) return pImpl->remove(paint); + else return pImpl->clearPaints(); } -list& Scene::paints() noexcept +const list& Scene::paints() const noexcept { return pImpl->paints; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.h index 1ded536da..c320978d0 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgScene.h @@ -23,6 +23,7 @@ #ifndef _TVG_SCENE_H_ #define _TVG_SCENE_H_ +#include #include "tvgMath.h" #include "tvgPaint.h" @@ -62,8 +63,8 @@ struct Scene::Impl Scene* scene = nullptr; RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; Array* effects = nullptr; + uint8_t compFlag = CompositionFlag::Invalid; uint8_t opacity; //for composition - bool needComp = false; //composite or not Impl(Scene* s) : scene(s) { @@ -73,42 +74,42 @@ struct Scene::Impl { resetEffects(); - for (auto paint : paints) { - if (P(paint)->unref() == 0) delete(paint); - } + clearPaints(); if (auto renderer = PP(scene)->renderer) { renderer->dispose(rd); } } - bool needComposition(uint8_t opacity) + uint8_t needComposition(uint8_t opacity) { - if (opacity == 0 || paints.empty()) return false; + compFlag = CompositionFlag::Invalid; - //post effects requires composition - if (effects) return true; + if (opacity == 0 || paints.empty()) return 0; - //Masking / Blending may require composition (even if opacity == 255) - if (scene->mask(nullptr) != MaskMethod::None) return true; - if (PP(scene)->blendMethod != BlendMethod::Normal) return true; + //post effects, masking, blending may require composition + if (effects) compFlag |= CompositionFlag::PostProcessing; + if (scene->mask(nullptr) != MaskMethod::None) compFlag |= CompositionFlag::Masking; + if (PP(scene)->blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending; //Half translucent requires intermediate composition. - if (opacity == 255) return false; + if (opacity == 255) return compFlag; //If scene has several children or only scene, it may require composition. //OPTIMIZE: the bitmap type of the picture would not need the composition. //OPTIMIZE: a single paint of a scene would not need the composition. - if (paints.size() == 1 && paints.front()->type() == Type::Shape) return false; + if (paints.size() == 1 && paints.front()->type() == Type::Shape) return compFlag; - return true; + compFlag |= CompositionFlag::Opacity; + + return 1; } RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) { this->vport = renderer->viewport(); - if ((needComp = needComposition(opacity))) { + if (needComposition(opacity)) { /* Overriding opacity value. If this scene is half-translucent, It must do intermediate composition with that opacity value. */ this->opacity = opacity; @@ -128,8 +129,8 @@ struct Scene::Impl renderer->blend(PP(scene)->blendMethod); - if (needComp) { - cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + if (compFlag) { + cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast(compFlag)); renderer->beginComposite(cmp, MaskMethod::None, opacity); } @@ -142,7 +143,7 @@ struct Scene::Impl if (effects) { auto direct = effects->count == 1 ? true : false; for (auto e = effects->begin(); e < effects->end(); ++e) { - renderer->effect(cmp, *e, direct); + renderer->effect(cmp, *e, opacity, direct); } } renderer->endComposite(cmp); @@ -225,12 +226,12 @@ struct Scene::Impl { if (ret) TVGERR("RENDERER", "TODO: duplicate()"); - auto scene = Scene::gen().release(); + auto scene = Scene::gen(); auto dup = scene->pImpl; for (auto paint : paints) { auto cdup = paint->duplicate(); - P(cdup)->ref(); + cdup->ref(); dup->paints.push_back(cdup); } @@ -239,12 +240,48 @@ struct Scene::Impl return scene; } - void clear(bool free) + Result clearPaints() { - for (auto paint : paints) { - if (P(paint)->unref() == 0 && free) delete(paint); + auto itr = paints.begin(); + while (itr != paints.end()) { + (*itr)->unref(); + paints.erase(itr++); + } + return Result::Success; + } + + Result remove(Paint* paint) + { + owned(paint); + paint->unref(); + paints.remove(paint); + return Result::Success; + } + + void owned(Paint* paint) + { +#ifdef THORVG_LOG_ENABLED + for (auto p : paints) { + if (p == paint) return; + } + TVGERR("RENDERER", "The paint(%p) is not existed from the scene(%p)", paint, scene); +#endif + } + + Result insert(Paint* target, Paint* at) + { + //Relocated the paint to the current scene space + P(target)->renderFlag |= RenderUpdateFlag::Transform; + + if (at == nullptr) { + paints.push_back(target); + } else { + //OPTIMIZE: Remove searching? + auto itr = find_if(paints.begin(), paints.end(),[&at](const Paint* paint){ return at == paint; }); + if (itr == paints.end()) return Result::InvalidArguments; + paints.insert(itr, target); } - paints.clear(); + return Result::Success; } Iterator* iterator() diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.cpp index ada7323b3..a1d4c34d4 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.cpp @@ -43,9 +43,9 @@ Shape :: ~Shape() } -unique_ptr Shape::gen() noexcept +Shape* Shape::gen() noexcept { - return unique_ptr(new Shape); + return new Shape; } @@ -60,7 +60,7 @@ Result Shape::reset() noexcept pImpl->rs.path.cmds.clear(); pImpl->rs.path.pts.clear(); - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->rFlag |= RenderUpdateFlag::Path; return Result::Success; } @@ -87,7 +87,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pImpl->grow(cmdCnt, ptsCnt); pImpl->append(cmds, cmdCnt, pts, ptsCnt); - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->rFlag |= RenderUpdateFlag::Path; return Result::Success; } @@ -105,7 +105,7 @@ Result Shape::lineTo(float x, float y) noexcept { pImpl->lineTo(x, y); - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->rFlag |= RenderUpdateFlag::Path; return Result::Success; } @@ -115,7 +115,7 @@ Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float { pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y); - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->rFlag |= RenderUpdateFlag::Path; return Result::Success; } @@ -125,7 +125,7 @@ Result Shape::close() noexcept { pImpl->close(); - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->rFlag |= RenderUpdateFlag::Path; return Result::Success; } @@ -144,7 +144,7 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy); pImpl->close(); - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->rFlag |= RenderUpdateFlag::Path; return Result::Success; } @@ -184,7 +184,7 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) pImpl->close(); } - pImpl->flag |= RenderUpdateFlag::Path; + pImpl->rFlag |= RenderUpdateFlag::Path; return Result::Success; } @@ -195,29 +195,26 @@ Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept if (pImpl->rs.fill) { delete(pImpl->rs.fill); pImpl->rs.fill = nullptr; - pImpl->flag |= RenderUpdateFlag::Gradient; + pImpl->rFlag |= RenderUpdateFlag::Gradient; } - if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success; + if (r == pImpl->rs.color.r && g == pImpl->rs.color.g && b == pImpl->rs.color.b && a == pImpl->rs.color.a) return Result::Success; - pImpl->rs.color[0] = r; - pImpl->rs.color[1] = g; - pImpl->rs.color[2] = b; - pImpl->rs.color[3] = a; - pImpl->flag |= RenderUpdateFlag::Color; + pImpl->rs.color = {r, g, b, a}; + + pImpl->rFlag |= RenderUpdateFlag::Color; return Result::Success; } -Result Shape::fill(unique_ptr f) noexcept +Result Shape::fill(Fill* f) noexcept { - auto p = f.release(); - if (!p) return Result::MemoryCorruption; + if (!f) return Result::InvalidArguments; - if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill); - pImpl->rs.fill = p; - pImpl->flag |= RenderUpdateFlag::Gradient; + if (pImpl->rs.fill && pImpl->rs.fill != f) delete(pImpl->rs.fill); + pImpl->rs.fill = f; + pImpl->rFlag |= RenderUpdateFlag::Gradient; return Result::Success; } @@ -272,9 +269,9 @@ Result Shape::strokeFill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const n } -Result Shape::strokeFill(unique_ptr f) noexcept +Result Shape::strokeFill(Fill* f) noexcept { - return pImpl->strokeFill(std::move(f)); + return pImpl->strokeFill(f); } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.h index ad89616eb..72e5f7c81 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgShape.h @@ -33,10 +33,9 @@ struct Shape::Impl RenderShape rs; //shape data RenderData rd = nullptr; //engine data Shape* shape; - uint8_t flag = RenderUpdateFlag::None; - + uint8_t rFlag = RenderUpdateFlag::None; + uint8_t cFlag = CompositionFlag::Invalid; uint8_t opacity; //for composition - bool needComp = false; //composite or not Impl(Shape* s) : shape(s) { @@ -57,8 +56,8 @@ struct Shape::Impl renderer->blend(PP(shape)->blendMethod); - if (needComp) { - cmp = renderer->target(bounds(renderer), renderer->colorSpace()); + if (cFlag) { + cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast(cFlag)); renderer->beginComposite(cmp, MaskMethod::None, opacity); } @@ -69,14 +68,19 @@ struct Shape::Impl bool needComposition(uint8_t opacity) { + cFlag = CompositionFlag::Invalid; + if (opacity == 0) return false; //Shape composition is only necessary when stroking & fill are valid. - if (!rs.stroke || rs.stroke->width < FLOAT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false; - if (!rs.fill && rs.color[3] == 0) return false; + if (!rs.stroke || rs.stroke->width < FLOAT_EPSILON || (!rs.stroke->fill && rs.stroke->color.a == 0)) return false; + if (!rs.fill && rs.color.a == 0) return false; //translucent fill & stroke - if (opacity < 255) return true; + if (opacity < 255) { + cFlag = CompositionFlag::Opacity; + return true; + } //Composition test const Paint* target; @@ -96,22 +100,23 @@ struct Shape::Impl } } + cFlag = CompositionFlag::Masking; return true; } RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { - if (static_cast(pFlag | flag) == RenderUpdateFlag::None) return rd; + if (static_cast(pFlag | rFlag) == RenderUpdateFlag::None) return rd; - if ((needComp = needComposition(opacity))) { + if (needComposition(opacity)) { /* Overriding opacity value. If this scene is half-translucent, It must do intermediate composition with that opacity value. */ this->opacity = opacity; opacity = 255; } - rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast(pFlag | flag), clipper); - flag = RenderUpdateFlag::None; + rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast(pFlag | rFlag), clipper); + rFlag = RenderUpdateFlag::None; return rd; } @@ -208,7 +213,7 @@ struct Shape::Impl { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->width = width; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; } void strokeTrim(float begin, float end, bool simultaneous) @@ -224,7 +229,7 @@ struct Shape::Impl rs.stroke->trim.begin = begin; rs.stroke->trim.end = end; rs.stroke->trim.simultaneous = simultaneous; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; } bool strokeTrim(float* begin, float* end) @@ -244,21 +249,21 @@ struct Shape::Impl { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->cap = cap; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; } void strokeJoin(StrokeJoin join) { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->join = join; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; } void strokeMiterlimit(float miterlimit) { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->miterlimit = miterlimit; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; } void strokeFill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) @@ -267,29 +272,25 @@ struct Shape::Impl if (rs.stroke->fill) { delete(rs.stroke->fill); rs.stroke->fill = nullptr; - flag |= RenderUpdateFlag::GradientStroke; + rFlag |= RenderUpdateFlag::GradientStroke; } - rs.stroke->color[0] = r; - rs.stroke->color[1] = g; - rs.stroke->color[2] = b; - rs.stroke->color[3] = a; + rs.stroke->color = {r, g, b, a}; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; } - Result strokeFill(unique_ptr f) + Result strokeFill(Fill* f) { - auto p = f.release(); - if (!p) return Result::MemoryCorruption; + if (!f) return Result::InvalidArguments; if (!rs.stroke) rs.stroke = new RenderStroke(); - if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill); - rs.stroke->fill = p; - rs.stroke->color[3] = 0; + if (rs.stroke->fill && rs.stroke->fill != f) delete(rs.stroke->fill); + rs.stroke->fill = f; + rs.stroke->color.a = 0; - flag |= RenderUpdateFlag::Stroke; - flag |= RenderUpdateFlag::GradientStroke; + rFlag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::GradientStroke; return Result::Success; } @@ -324,7 +325,7 @@ struct Shape::Impl } rs.stroke->dashCnt = cnt; rs.stroke->dashOffset = offset; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; return Result::Success; } @@ -339,29 +340,27 @@ struct Shape::Impl { if (!rs.stroke) rs.stroke = new RenderStroke(); rs.stroke->strokeFirst = strokeFirst; - flag |= RenderUpdateFlag::Stroke; + rFlag |= RenderUpdateFlag::Stroke; } void update(RenderUpdateFlag flag) { - this->flag |= flag; + rFlag |= flag; } Paint* duplicate(Paint* ret) { auto shape = static_cast(ret); if (shape) shape->reset(); - else shape = Shape::gen().release(); + else shape = Shape::gen(); auto dup = shape->pImpl; delete(dup->rs.fill); //Default Properties - dup->flag = RenderUpdateFlag::All; + dup->rFlag = RenderUpdateFlag::All; dup->rs.rule = rs.rule; - - //Color - memcpy(dup->rs.color, rs.color, sizeof(rs.color)); + dup->rs.color = rs.color; //Path dup->rs.path.cmds.push(rs.path.cmds); @@ -389,7 +388,7 @@ struct Shape::Impl rs.path.cmds.clear(); rs.path.pts.clear(); - rs.color[3] = 0; + rs.color.a = 0; rs.rule = FillRule::Winding; delete(rs.stroke); diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSwCanvas.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSwCanvas.cpp index f3ff2207f..334fc4d99 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSwCanvas.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgSwCanvas.cpp @@ -68,7 +68,7 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept if (!renderer) return Result::MemoryCorruption; //It can't change the policy during the running. - if (!Canvas::pImpl->paints.empty()) return Result::InsufficientCondition; + if (!Canvas::pImpl->scene->paints().empty()) return Result::InsufficientCondition; if (policy == MempoolPolicy::Individual) renderer->mempool(false); else renderer->mempool(true); @@ -109,11 +109,11 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t } -unique_ptr SwCanvas::gen() noexcept +SwCanvas* SwCanvas::gen() noexcept { #ifdef THORVG_SW_RASTER_SUPPORT if (SwRenderer::init() <= 0) return nullptr; - return unique_ptr(new SwCanvas); + return new SwCanvas; #endif return nullptr; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.cpp index be94ba24d..37137815e 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.cpp @@ -58,15 +58,19 @@ Result Text::font(const char* name, float size, const char* style) noexcept } -Result Text::load(const char* path) noexcept +Result Text::load(const char* filename) noexcept { +#ifdef THORVG_FILE_IO_SUPPORT bool invalid; //invalid path - if (!LoaderMgr::loader(path, &invalid, nullptr)) { + if (!LoaderMgr::loader(filename, &invalid, nullptr)) { if (invalid) return Result::InvalidArguments; else return Result::NonSupport; } - return Result::Success; +#else + TVGLOG("RENDERER", "FILE IO is disabled!"); + return Result::NonSupport; +#endif } @@ -87,8 +91,13 @@ Result Text::load(const char* name, const char* data, uint32_t size, const char* Result Text::unload(const char* filename) noexcept { +#ifdef THORVG_FILE_IO_SUPPORT if (LoaderMgr::retrieve(filename)) return Result::Success; return Result::InsufficientCondition; +#else + TVGLOG("RENDERER", "FILE IO is disabled!"); + return Result::NonSupport; +#endif } @@ -98,15 +107,15 @@ Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept } -Result Text::fill(unique_ptr f) noexcept +Result Text::fill(Fill* f) noexcept { - return pImpl->shape->fill(std::move(f)); + return pImpl->shape->fill(f); } -unique_ptr Text::gen() noexcept +Text* Text::gen() noexcept { - return unique_ptr(new Text); + return new Text; } diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.h b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.h index 5770a17dc..272edfe9d 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.h +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgText.h @@ -38,7 +38,7 @@ struct Text::Impl bool italic = false; bool changed = false; - Impl(Text* p) : paint(p), shape(Shape::gen().release()) + Impl(Text* p) : paint(p), shape(Shape::gen()) { } @@ -113,7 +113,7 @@ struct Text::Impl //transform the gradient coordinates based on the final scaled font. auto fill = P(shape)->rs.fill; - if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) { + if (fill && P(shape)->rFlag & RenderUpdateFlag::Gradient) { auto scale = 1.0f / loader->scale; if (fill->type() == Type::LinearGradient) { P(static_cast(fill))->x1 *= scale; @@ -145,7 +145,7 @@ struct Text::Impl load(); - auto text = Text::gen().release(); + auto text = Text::gen(); auto dup = text->pImpl; P(shape)->duplicate(dup->shape); diff --git a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgWgCanvas.cpp b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgWgCanvas.cpp index 991f73fc5..d9f8e69aa 100644 --- a/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgWgCanvas.cpp +++ b/libfenrir/src/main/jni/animation/thorvg/src/renderer/tvgWgCanvas.cpp @@ -52,25 +52,25 @@ WgCanvas::~WgCanvas() { #ifdef THORVG_WG_RASTER_SUPPORT auto renderer = static_cast(Canvas::pImpl->renderer); - renderer->target(nullptr, 0, 0); + renderer->target(nullptr, nullptr, nullptr, 0, 0); #endif } -Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h, void* device) noexcept +Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w, uint32_t h, ColorSpace cs, int type) noexcept { #ifdef THORVG_WG_RASTER_SUPPORT + if (cs != ColorSpace::ABGR8888S) return Result::NonSupport; + if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) { return Result::InsufficientCondition; } - if (!instance || !surface || (w == 0) || (h == 0)) return Result::InvalidArguments; - //We know renderer type, avoid dynamic_cast for performance. auto renderer = static_cast(Canvas::pImpl->renderer); if (!renderer) return Result::MemoryCorruption; - if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h, (WGPUDevice)device)) return Result::Unknown; + if (!renderer->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown; Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; renderer->viewport(Canvas::pImpl->vport); @@ -83,10 +83,10 @@ Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h, v } -unique_ptr WgCanvas::gen() noexcept +WgCanvas* WgCanvas::gen() noexcept { #ifdef THORVG_WG_RASTER_SUPPORT - return unique_ptr(new WgCanvas); + return new WgCanvas; #endif return nullptr; } diff --git a/libfenrir/src/main/jni/animation/thorvg/thorvg-1.0-pre5.tar.gz b/libfenrir/src/main/jni/animation/thorvg/thorvg-1.0-pre5.tar.gz deleted file mode 100644 index 4c7d8f70e..000000000 Binary files a/libfenrir/src/main/jni/animation/thorvg/thorvg-1.0-pre5.tar.gz and /dev/null differ diff --git a/libfenrir/src/main/jni/animation/thorvg/thorvg-1.0-pre7.tar.gz b/libfenrir/src/main/jni/animation/thorvg/thorvg-1.0-pre7.tar.gz new file mode 100644 index 000000000..fbc156b85 Binary files /dev/null and b/libfenrir/src/main/jni/animation/thorvg/thorvg-1.0-pre7.tar.gz differ diff --git a/libfenrir/src/main/jni/animation/thorvg_jni.cpp b/libfenrir/src/main/jni/animation/thorvg_jni.cpp index cec7da076..60da3cb34 100644 --- a/libfenrir/src/main/jni/animation/thorvg_jni.cpp +++ b/libfenrir/src/main/jni/animation/thorvg_jni.cpp @@ -15,10 +15,15 @@ #include "fenrir_native.h" #include "tvgGifEncoder.h" -class LottieInfo { +class LottieAnimation { public: - std::unique_ptr animation; - std::unique_ptr canvas; + ~LottieAnimation() { + delete animation; + delete canvas; + } + + tvg::Animation *animation = nullptr; + tvg::SwCanvas *canvas = nullptr; bool isCanvasPushed = false; }; @@ -83,9 +88,10 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGSVGRender_createBitmapNat auto picture = tvg::Picture::gen(); bool orig; std::string jsonString = doDecompressResource(u->size(), u->data(), orig); - tvg::Result result = orig ? picture->load((const char *) u->data(), u->size(), "svg", "", true) + tvg::Result result = orig ? picture->load((const char *) u->data(), u->size(), "svg", nullptr, + true) : picture->load((const char *) jsonString.data(), jsonString.size(), - "svg", "", true); + "svg", nullptr, true); if (result != tvg::Result::Success) { return; } @@ -181,7 +187,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nLoadFromF jintArray data, jintArray colorReplacement, jboolean useMoveColor) { - auto *info = new LottieInfo(); + auto *info = new LottieAnimation(); tvg::ColorReplace colors; if (useMoveColor) { colors.setUseCustomColorsLottieOffset(); @@ -226,7 +232,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nLoadFromF std::string jsonString = doDecompressResource(length, arr, orig); if (orig) { info->animation = tvg::Animation::gen(); - info->animation->picture()->load(arr, length, "lottie", "", true, colors._ptr()); + info->animation->picture()->load(arr, length, "lottie", nullptr, true, &colors); } delete[] arr; if (!orig) { @@ -235,8 +241,8 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nLoadFromF return 0; } info->animation = tvg::Animation::gen(); - info->animation->picture()->load(jsonString.data(), jsonString.size(), "lottie", "", true, - colors._ptr()); + info->animation->picture()->load(jsonString.data(), jsonString.size(), "lottie", nullptr, + true, &colors); } info->canvas = tvg::SwCanvas::gen(); float tmpWidth = 0; @@ -283,17 +289,18 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nLoadFromM } } - auto *info = new LottieInfo(); + auto *info = new LottieAnimation(); auto u = reinterpret_cast *>(json); bool orig; std::string jsonString = doDecompressResource(u->size(), u->data(), orig); if (orig) { info->animation = tvg::Animation::gen(); - info->animation->picture()->load(u->data(), u->size(), "lottie", "", true, colors._ptr()); + info->animation->picture()->load(u->data(), u->size(), "lottie", nullptr, true, &colors); } else { info->animation = tvg::Animation::gen(); - info->animation->picture()->load(jsonString.data(), jsonString.size(), "lottie", "", true, - colors._ptr()); + info->animation->picture()->load(jsonString.data(), jsonString.size(), "lottie", nullptr, + true, + &colors); } info->canvas = tvg::SwCanvas::gen(); float tmpWidth = 0; @@ -321,7 +328,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nDestroy(J if (!ptr) { return; } - auto *info = reinterpret_cast(ptr); + auto *info = reinterpret_cast(ptr); delete info; } @@ -333,7 +340,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nSetBuffer return; } - auto *info = reinterpret_cast(ptr); + auto *info = reinterpret_cast(ptr); void *pixels; if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) { info->canvas->sync(); @@ -381,7 +388,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nGetFrame( if (!ptr || !bitmap) { return; } - auto *info = reinterpret_cast(ptr); + auto *info = reinterpret_cast(ptr); if (!info->canvas) { return; } @@ -392,7 +399,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottieDrawable_nGetFrame( info->animation->frame((float) frame); if (!info->isCanvasPushed) { info->isCanvasPushed = true; - info->canvas->push(tvg::cast(info->animation->picture())); + info->canvas->push(info->animation->picture()); } else { info->canvas->update(info->animation->picture()); } @@ -413,7 +420,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottie2Gif_lottie2gif(JNI jint fps, jstring gifName, jobject listener) { - auto info = LottieInfo(); + auto info = LottieAnimation(); char const *srcString = SafeGetStringUTFChars(env, srcPath, nullptr); std::string path = srcString; if (srcString != nullptr) { @@ -439,7 +446,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottie2Gif_lottie2gif(JNI std::string jsonString = doDecompressResource(length, arr, orig); if (orig) { info.animation = tvg::Animation::gen(); - info.animation->picture()->load(arr, length, "lottie", "", true); + info.animation->picture()->load(arr, length, "lottie", nullptr, true); } delete[] arr; if (!orig) { @@ -447,7 +454,8 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottie2Gif_lottie2gif(JNI return 0; } info.animation = tvg::Animation::gen(); - info.animation->picture()->load(jsonString.data(), jsonString.size(), "lottie", "", true); + info.animation->picture()->load(jsonString.data(), jsonString.size(), "lottie", nullptr, + true); } if (!info.animation) { return false; @@ -469,7 +477,7 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottie2Gif_lottie2gif(JNI info.canvas->push(std::move(bg)); } - info.canvas->push(tvg::cast(info.animation->picture())); + info.canvas->push(info.animation->picture()); char const *gifNameString = SafeGetStringUTFChars(env, gifName, nullptr); std::string gifNameStr = gifNameString; @@ -564,4 +572,4 @@ Java_dev_ragnarok_fenrir_module_animation_thorvg_ThorVGLottie2Gif_lottie2gif(JNI } delete[] pixels; return true; -} \ No newline at end of file +} diff --git a/material/build.gradle b/material/build.gradle index bdc0d284c..567d6c1ec 100644 --- a/material/build.gradle +++ b/material/build.gradle @@ -3,7 +3,7 @@ plugins { id("kotlin-android") } -//1.13.0-alpha07 +//1.13.0-alpha08 def srcDirs = [ 'com/google/android/material/animation', diff --git a/material/java/com/google/android/material/appbar/CollapsingToolbarLayout.java b/material/java/com/google/android/material/appbar/CollapsingToolbarLayout.java index 6e5b95bb6..d58d3e2af 100644 --- a/material/java/com/google/android/material/appbar/CollapsingToolbarLayout.java +++ b/material/java/com/google/android/material/appbar/CollapsingToolbarLayout.java @@ -165,9 +165,11 @@ public class CollapsingToolbarLayout extends FrameLayout { private int expandedMarginTop; private int expandedMarginEnd; private int expandedMarginBottom; + private int expandedTitleSpacing; private final Rect tmpRect = new Rect(); - @NonNull final CollapsingTextHelper collapsingTextHelper; + @NonNull final CollapsingTextHelper collapsingTitleHelper; + @NonNull final CollapsingTextHelper collapsingSubtitleHelper; @NonNull final ElevationOverlayProvider elevationOverlayProvider; private boolean collapsingTitleEnabled; private boolean drawCollapsingTitle; @@ -205,32 +207,29 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet this(context, attrs, R.attr.collapsingToolbarLayoutStyle); } - public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + public CollapsingToolbarLayout( + @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr); // Ensure we are using the correctly themed context rather than the context that was passed in. context = getContext(); screenOrientation = getResources().getConfiguration().orientation; - collapsingTextHelper = new CollapsingTextHelper(this); - collapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); - collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(false); + collapsingTitleHelper = new CollapsingTextHelper(this); + collapsingTitleHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); + collapsingTitleHelper.setRtlTextDirectionHeuristicsEnabled(false); elevationOverlayProvider = new ElevationOverlayProvider(context); TypedArray a = ThemeEnforcement.obtainStyledAttributes( - context, - attrs, - R.styleable.CollapsingToolbarLayout, - defStyleAttr, - DEF_STYLE_RES); + context, attrs, R.styleable.CollapsingToolbarLayout, defStyleAttr, DEF_STYLE_RES); - collapsingTextHelper.setExpandedTextGravity( + collapsingTitleHelper.setExpandedTextGravity( a.getInt( R.styleable.CollapsingToolbarLayout_expandedTitleGravity, Gravity.START | Gravity.BOTTOM)); - collapsingTextHelper.setCollapsedTextGravity( + collapsingTitleHelper.setCollapsedTextGravity( a.getInt( R.styleable.CollapsingToolbarLayout_collapsedTitleGravity, Gravity.START | Gravity.CENTER_VERTICAL)); @@ -258,23 +257,27 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet expandedMarginBottom = a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom, 0); } + if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleSpacing)) { + expandedTitleSpacing = + a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleSpacing, 0); + } collapsingTitleEnabled = a.getBoolean(R.styleable.CollapsingToolbarLayout_titleEnabled, true); setTitle(a.getText(R.styleable.CollapsingToolbarLayout_title)); // First load the default text appearances - collapsingTextHelper.setExpandedTextAppearance( + collapsingTitleHelper.setExpandedTextAppearance( R.style.TextAppearance_Design_CollapsingToolbar_Expanded); - collapsingTextHelper.setCollapsedTextAppearance( + collapsingTitleHelper.setCollapsedTextAppearance( androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Title); // Now overlay any custom text appearances if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance)) { - collapsingTextHelper.setExpandedTextAppearance( + collapsingTitleHelper.setExpandedTextAppearance( a.getResourceId(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance, 0)); } if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance)) { - collapsingTextHelper.setCollapsedTextAppearance( + collapsingTitleHelper.setCollapsedTextAppearance( a.getResourceId(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance, 0)); } @@ -286,12 +289,12 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet } if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextColor)) { - collapsingTextHelper.setExpandedTextColor( + collapsingTitleHelper.setExpandedTextColor( MaterialResources.getColorStateList( context, a, R.styleable.CollapsingToolbarLayout_expandedTitleTextColor)); } if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextColor)) { - collapsingTextHelper.setCollapsedTextColor( + collapsingTitleHelper.setCollapsedTextColor( MaterialResources.getColorStateList( context, a, R.styleable.CollapsingToolbarLayout_collapsedTitleTextColor)); } @@ -300,11 +303,57 @@ public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_scrimVisibleHeightTrigger, -1); if (a.hasValue(R.styleable.CollapsingToolbarLayout_maxLines)) { - collapsingTextHelper.setExpandedMaxLines(a.getInt(R.styleable.CollapsingToolbarLayout_maxLines, 1)); + collapsingTitleHelper.setExpandedMaxLines( + a.getInt(R.styleable.CollapsingToolbarLayout_maxLines, 1)); + } + + if (a.hasValue(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator)) { + collapsingTitleHelper.setPositionInterpolator( + android.view.animation.AnimationUtils.loadInterpolator( + context, + a.getResourceId(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator, 0))); + } + + collapsingSubtitleHelper = new CollapsingTextHelper(this); + collapsingSubtitleHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR); + collapsingSubtitleHelper.setRtlTextDirectionHeuristicsEnabled(false); + + if (a.hasValue(R.styleable.CollapsingToolbarLayout_subtitle)) { + setSubtitle(a.getText(R.styleable.CollapsingToolbarLayout_subtitle)); } + collapsingSubtitleHelper.setExpandedTextGravity( + a.getInt( + R.styleable.CollapsingToolbarLayout_expandedSubtitleGravity, + Gravity.START | Gravity.BOTTOM)); + collapsingSubtitleHelper.setCollapsedTextGravity( + a.getInt( + R.styleable.CollapsingToolbarLayout_collapsedSubtitleGravity, + Gravity.START | Gravity.CENTER_VERTICAL)); + collapsingSubtitleHelper.setExpandedTextAppearance( + androidx.appcompat.R.style.TextAppearance_AppCompat_Headline); + collapsingSubtitleHelper.setCollapsedTextAppearance( + androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Subtitle); + if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextAppearance)) { + collapsingSubtitleHelper.setExpandedTextAppearance( + a.getResourceId(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextAppearance, 0)); + } + if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextAppearance)) { + collapsingSubtitleHelper.setCollapsedTextAppearance( + a.getResourceId(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextAppearance, 0)); + } + if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextColor)) { + collapsingSubtitleHelper.setExpandedTextColor( + MaterialResources.getColorStateList( + context, a, R.styleable.CollapsingToolbarLayout_expandedSubtitleTextColor)); + } + if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextColor)) { + collapsingSubtitleHelper.setCollapsedTextColor( + MaterialResources.getColorStateList( + context, a, R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextColor)); + } if (a.hasValue(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator)) { - collapsingTextHelper.setPositionInterpolator( + collapsingSubtitleHelper.setPositionInterpolator( android.view.animation.AnimationUtils.loadInterpolator( context, a.getResourceId(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator, 0))); @@ -426,15 +475,17 @@ public void draw(@NonNull Canvas canvas) { && contentScrim != null && scrimAlpha > 0 && isTitleCollapseFadeMode() - && collapsingTextHelper.getExpansionFraction() - < collapsingTextHelper.getFadeModeThresholdFraction()) { + && collapsingTitleHelper.getExpansionFraction() + < collapsingTitleHelper.getFadeModeThresholdFraction()) { // Mask the expanded text with the contentScrim int save = canvas.save(); canvas.clipRect(contentScrim.getBounds(), Op.DIFFERENCE); - collapsingTextHelper.draw(canvas); + collapsingTitleHelper.draw(canvas); + collapsingSubtitleHelper.draw(canvas); canvas.restoreToCount(save); } else { - collapsingTextHelper.draw(canvas); + collapsingTitleHelper.draw(canvas); + collapsingSubtitleHelper.draw(canvas); } } @@ -452,7 +503,7 @@ && isTitleCollapseFadeMode() @Override protected void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - collapsingTextHelper.maybeUpdateFontWeightAdjustment(newConfig); + collapsingTitleHelper.maybeUpdateFontWeightAdjustment(newConfig); // When the orientation changes with extra multiline height enabled and when collapsed, there // can be an issue where the offset/scroll state is invalid due to the number of lines of text @@ -461,7 +512,7 @@ protected void onConfigurationChanged(@NonNull Configuration newConfig) { // fully collapsed prior to screen rotation. if (screenOrientation != newConfig.orientation && extraMultilineHeightEnabled - && collapsingTextHelper.getExpansionFraction() == 1f) { + && collapsingTitleHelper.getExpansionFraction() == 1f) { ViewParent parent = getParent(); if (parent instanceof AppBarLayout) { AppBarLayout appBarLayout = (AppBarLayout) parent; @@ -616,16 +667,16 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } - if (extraMultilineHeightEnabled && collapsingTextHelper.getExpandedMaxLines() > 1) { + if (extraMultilineHeightEnabled && collapsingTitleHelper.getExpandedMaxLines() > 1) { // Need to update title and bounds in order to calculate line count and text height. updateTitleFromToolbarIfNeeded(); updateTextBounds(0, 0, getMeasuredWidth(), getMeasuredHeight(), /* forceRecalculate= */ true); - int lineCount = collapsingTextHelper.getExpandedLineCount(); + int lineCount = collapsingTitleHelper.getExpandedLineCount(); if (lineCount > 1) { // Add extra height based on the amount of height beyond the first line of title text. int expandedTextHeight = - Math.round(collapsingTextHelper.getExpandedTextFullSingleLineHeight()); + Math.round(collapsingTitleHelper.getExpandedTextFullSingleLineHeight()); extraMultilineHeight = expandedTextHeight * (lineCount - 1); int newHeight = getMeasuredHeight() + extraMultilineHeight; heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY); @@ -669,7 +720,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto getViewOffsetHelper(getChildAt(i)).onViewLayout(); } - updateTextBounds(left, top, right, bottom, /* forceRecalculate= */false); + updateTextBounds(left, top, right, bottom, /* forceRecalculate= */ false); updateTitleFromToolbarIfNeeded(); @@ -696,23 +747,51 @@ private void updateTextBounds( updateCollapsedBounds(isRtl); // Update the expanded bounds - collapsingTextHelper.setExpandedBounds( - isRtl ? expandedMarginEnd : expandedMarginStart, - tmpRect.top + expandedMarginTop, - right - left - (isRtl ? expandedMarginStart : expandedMarginEnd), - bottom - top - expandedMarginBottom); - - // Now recalculate using the new bounds - collapsingTextHelper.recalculate(forceRecalculate); + if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) { + collapsingTitleHelper.setExpandedBounds( + isRtl ? expandedMarginEnd : expandedMarginStart, + tmpRect.top + expandedMarginTop, + right - left - (isRtl ? expandedMarginStart : expandedMarginEnd), + bottom - top - expandedMarginBottom); + + // Now recalculate using the new bounds + collapsingTitleHelper.recalculate(forceRecalculate); + } else { + collapsingTitleHelper.setExpandedBounds( + isRtl ? expandedMarginEnd : expandedMarginStart, + tmpRect.top + expandedMarginTop, + right - left - (isRtl ? expandedMarginStart : expandedMarginEnd), + (int) + (bottom + - top + - expandedMarginBottom + - collapsingSubtitleHelper.getExpandedTextFullSingleLineHeight() + - expandedTitleSpacing)); + collapsingSubtitleHelper.setExpandedBounds( + isRtl ? expandedMarginEnd : expandedMarginStart, + (int) + (tmpRect.top + + expandedMarginTop + + collapsingTitleHelper.getExpandedTextFullSingleLineHeight() + + expandedTitleSpacing), + right - left - (isRtl ? expandedMarginStart : expandedMarginEnd), + bottom - top - expandedMarginBottom); + + // Now recalculate using the new bounds + collapsingTitleHelper.recalculate(forceRecalculate); + collapsingSubtitleHelper.recalculate(forceRecalculate); + } } } } private void updateTitleFromToolbarIfNeeded() { if (toolbar != null) { - if (collapsingTitleEnabled && TextUtils.isEmpty(collapsingTextHelper.getText())) { - // If we do not currently have a title, try and grab it from the Toolbar - setTitle(getToolbarTitle(toolbar)); + if (collapsingTitleEnabled) { + if (TextUtils.isEmpty(collapsingTitleHelper.getText())) { + // If we do not currently have a title, try and grab it from the Toolbar + setTitle(getToolbarTitle(toolbar)); + } } } } @@ -743,13 +822,35 @@ private void updateCollapsedBounds(boolean isRtl) { titleMarginTop = 0; titleMarginBottom = 0; } - collapsingTextHelper.setCollapsedBounds( - tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart), - tmpRect.top + maxOffset + titleMarginTop, - tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd), - tmpRect.bottom + maxOffset - titleMarginBottom); + if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) { + collapsingTitleHelper.setCollapsedBounds( + tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart), + tmpRect.top + maxOffset + titleMarginTop, + tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd), + tmpRect.bottom + maxOffset - titleMarginBottom); + } else { + collapsingTitleHelper.setCollapsedBounds( + tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart), + tmpRect.top + maxOffset + titleMarginTop, + tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd), + (int) + (tmpRect.bottom + + maxOffset + - titleMarginBottom + - collapsingSubtitleHelper.getCollapsedFullSingleLineHeight())); + collapsingSubtitleHelper.setCollapsedBounds( + tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart), + (int) + (tmpRect.top + + maxOffset + + titleMarginTop + + collapsingTitleHelper.getCollapsedFullSingleLineHeight()), + tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd), + tmpRect.bottom + maxOffset - titleMarginBottom); + } } + @Nullable private static CharSequence getToolbarTitle(View view) { if (view instanceof androidx.appcompat.widget.Toolbar) { return ((androidx.appcompat.widget.Toolbar) view).getTitle(); @@ -787,7 +888,7 @@ static ViewOffsetHelper getViewOffsetHelper(@NonNull View view) { * @attr ref R.styleable#CollapsingToolbarLayout_title */ public void setTitle(@Nullable CharSequence title) { - collapsingTextHelper.setText(title); + collapsingTitleHelper.setText(title); updateContentDescriptionFromTitle(); } @@ -799,7 +900,29 @@ public void setTitle(@Nullable CharSequence title) { */ @Nullable public CharSequence getTitle() { - return collapsingTitleEnabled ? collapsingTextHelper.getText() : null; + return collapsingTitleEnabled ? collapsingTitleHelper.getText() : null; + } + + /** + * Sets the subtitle to be displayed by this view, if enabled. + * + * @see #setTitleEnabled(boolean) + * @see #getSubtitle() + * @attr ref R.styleable#CollapsingToolbarLayout_subtitle + */ + public void setSubtitle(@Nullable CharSequence subtitle) { + collapsingSubtitleHelper.setText(subtitle); + } + + /** + * Returns the subtitle currently being displayed by this view. If the subtitle is not enabled, + * then this will return {@code null}. + * + * @attr ref R.styleable#CollapsingToolbarLayout_subtitle + */ + @Nullable + public CharSequence getSubtitle() { + return collapsingTitleEnabled ? collapsingSubtitleHelper.getText() : null; } /** @@ -812,7 +935,8 @@ public void setTitleCollapseMode(@TitleCollapseMode int titleCollapseMode) { this.titleCollapseMode = titleCollapseMode; boolean fadeModeEnabled = isTitleCollapseFadeMode(); - collapsingTextHelper.setFadeModeEnabled(fadeModeEnabled); + collapsingTitleHelper.setFadeModeEnabled(fadeModeEnabled); + collapsingSubtitleHelper.setFadeModeEnabled(fadeModeEnabled); ViewParent parent = getParent(); if (parent instanceof AppBarLayout) { @@ -876,7 +1000,6 @@ public boolean isTitleEnabled() { return collapsingTitleEnabled; } - /** * Set ellipsizing on the title text. * @@ -884,15 +1007,13 @@ public boolean isTitleEnabled() { * @attr ref R.styleable#CollapsingToolbarLayout_titleTextEllipsize */ public void setTitleEllipsize(@NonNull TruncateAt ellipsize) { - collapsingTextHelper.setTitleTextEllipsize(ellipsize); + collapsingTitleHelper.setTitleTextEllipsize(ellipsize); } - /** - * Get ellipsizing currently applied on the title text. - */ + /** Get ellipsizing currently applied on the title text. */ @NonNull public TruncateAt getTitleTextEllipsize() { - return collapsingTextHelper.getTitleTextEllipsize(); + return collapsingTitleHelper.getTitleTextEllipsize(); } // Convert to supported TruncateAt values @@ -1082,8 +1203,8 @@ protected void drawableStateChanged() { if (d != null && d.isStateful()) { changed |= d.setState(state); } - if (collapsingTextHelper != null) { - changed |= collapsingTextHelper.setState(state); + if (collapsingTitleHelper != null) { + changed |= collapsingTitleHelper.setState(state); } if (changed) { @@ -1152,7 +1273,18 @@ public Drawable getStatusBarScrim() { * com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance */ public void setCollapsedTitleTextAppearance(@StyleRes int resId) { - collapsingTextHelper.setCollapsedTextAppearance(resId); + collapsingTitleHelper.setCollapsedTextAppearance(resId); + } + + /** + * Sets the text color and size for the collapsed subtitle from the specified TextAppearance + * resource. + * + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedSubtitleTextAppearance + */ + public void setCollapsedSubtitleTextAppearance(@StyleRes int resId) { + collapsingSubtitleHelper.setCollapsedTextAppearance(resId); } /** @@ -1170,7 +1302,25 @@ public void setCollapsedTitleTextColor(@ColorInt int color) { * @param colors ColorStateList containing the new text colors */ public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) { - collapsingTextHelper.setCollapsedTextColor(colors); + collapsingTitleHelper.setCollapsedTextColor(colors); + } + + /** + * Sets the text color of the collapsed subtitle. + * + * @param color The new text color in ARGB format + */ + public void setCollapsedSubtitleTextColor(@ColorInt int color) { + setCollapsedSubtitleTextColor(ColorStateList.valueOf(color)); + } + + /** + * Sets the text colors of the collapsed subtitle. + * + * @param colors ColorStateList containing the new text colors + */ + public void setCollapsedSubtitleTextColor(@NonNull ColorStateList colors) { + collapsingSubtitleHelper.setCollapsedTextColor(colors); } /** @@ -1180,7 +1330,19 @@ public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) { * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity */ public void setCollapsedTitleGravity(int gravity) { - collapsingTextHelper.setCollapsedTextGravity(gravity); + collapsingTitleHelper.setCollapsedTextGravity(gravity); + } + + /** + * Sets the horizontal alignment of the collapsed subtitle and the vertical gravity that will be + * used when there is extra space in the collapsed bounds beyond what is required for the subtitle + * itself. + * + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedSubtitleGravity + */ + public void setCollapsedSubitleGravity(int gravity) { + collapsingSubtitleHelper.setCollapsedTextGravity(gravity); } /** @@ -1189,7 +1351,17 @@ public void setCollapsedTitleGravity(int gravity) { * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity */ public int getCollapsedTitleGravity() { - return collapsingTextHelper.getCollapsedTextGravity(); + return collapsingTitleHelper.getCollapsedTextGravity(); + } + + /** + * Returns the horizontal and vertical alignment for subtitle when collapsed. + * + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedSubtitleGravity + */ + public int getCollapsedSubtitleGravity() { + return collapsingSubtitleHelper.getCollapsedTextGravity(); } /** @@ -1199,7 +1371,18 @@ public int getCollapsedTitleGravity() { * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance */ public void setExpandedTitleTextAppearance(@StyleRes int resId) { - collapsingTextHelper.setExpandedTextAppearance(resId); + collapsingTitleHelper.setExpandedTextAppearance(resId); + } + + /** + * Sets the text color and size for the expanded subtitle from the specified TextAppearance + * resource. + * + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedSubtitleTextAppearance + */ + public void setExpandedSubtitleTextAppearance(@StyleRes int resId) { + collapsingSubtitleHelper.setExpandedTextAppearance(resId); } /** @@ -1217,7 +1400,25 @@ public void setExpandedTitleColor(@ColorInt int color) { * @param colors ColorStateList containing the new text colors */ public void setExpandedTitleTextColor(@NonNull ColorStateList colors) { - collapsingTextHelper.setExpandedTextColor(colors); + collapsingTitleHelper.setExpandedTextColor(colors); + } + + /** + * Sets the text color of the expanded subtitle. + * + * @param color The new text color in ARGB format + */ + public void setExpandedSubtitleColor(@ColorInt int color) { + setExpandedSubtitleTextColor(ColorStateList.valueOf(color)); + } + + /** + * Sets the text colors of the expanded subtitle. + * + * @param colors ColorStateList containing the new text colors + */ + public void setExpandedSubtitleTextColor(@NonNull ColorStateList colors) { + collapsingSubtitleHelper.setExpandedTextColor(colors); } /** @@ -1227,7 +1428,19 @@ public void setExpandedTitleTextColor(@NonNull ColorStateList colors) { * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleGravity */ public void setExpandedTitleGravity(int gravity) { - collapsingTextHelper.setExpandedTextGravity(gravity); + collapsingTitleHelper.setExpandedTextGravity(gravity); + } + + /** + * Sets the horizontal alignment of the expanded subtitle and the vertical gravity that will be + * used when there is extra space in the expanded bounds beyond what is required for the subtitle + * itself. + * + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedSubtitleGravity + */ + public void setExpandedSubtitleGravity(int gravity) { + collapsingSubtitleHelper.setExpandedTextGravity(gravity); } /** @@ -1236,7 +1449,17 @@ public void setExpandedTitleGravity(int gravity) { * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleGravity */ public int getExpandedTitleGravity() { - return collapsingTextHelper.getExpandedTextGravity(); + return collapsingTitleHelper.getExpandedTextGravity(); + } + + /** + * Returns the horizontal and vertical alignment for subtitle when expanded. + * + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedSubtitleGravity + */ + public int getExpandedSubtitleGravity() { + return collapsingSubtitleHelper.getExpandedTextGravity(); } /** @@ -1245,12 +1468,26 @@ public int getExpandedTitleGravity() { * @param textSize The text size of the expanded title. */ public void setExpandedTitleTextSize(float textSize) { - collapsingTextHelper.setExpandedTextSize(textSize); + collapsingTitleHelper.setExpandedTextSize(textSize); + } + + /** + * Sets the text size of the expanded subtitle. + * + * @param textSize The text size of the expanded subtitle. + */ + public void setExpandedSubtitleTextSize(float textSize) { + collapsingSubtitleHelper.setExpandedTextSize(textSize); } /** Returns the text size of the expanded title. */ public float getExpandedTitleTextSize() { - return collapsingTextHelper.getExpandedTextSize(); + return collapsingTitleHelper.getExpandedTextSize(); + } + + /** Returns the text size of the expanded subtitle. */ + public float getExpandedSubtitleTextSize() { + return collapsingSubtitleHelper.getExpandedTextSize(); } /** @@ -1259,12 +1496,26 @@ public float getExpandedTitleTextSize() { * @param textSize The text size of the collapsed title. */ public void setCollapsedTitleTextSize(float textSize) { - collapsingTextHelper.setCollapsedTextSize(textSize); + collapsingTitleHelper.setCollapsedTextSize(textSize); + } + + /** + * Sets the text size of the collapsed subtitle. + * + * @param textSize The text size of the collapsed subtitle. + */ + public void setCollapsedSubtitleTextSize(float textSize) { + collapsingSubtitleHelper.setCollapsedTextSize(textSize); } /** Returns the text size of the collapsed title. */ public float getCollapsedTitleTextSize() { - return collapsingTextHelper.getCollapsedTextSize(); + return collapsingTitleHelper.getCollapsedTextSize(); + } + + /** Returns the text size of the collapsed subtitle. */ + public float getCollapsedSubtitleTextSize() { + return collapsingSubtitleHelper.getCollapsedTextSize(); } /** @@ -1273,13 +1524,28 @@ public float getCollapsedTitleTextSize() { * @param typeface typeface to use, or {@code null} to use the default. */ public void setCollapsedTitleTypeface(@Nullable Typeface typeface) { - collapsingTextHelper.setCollapsedTypeface(typeface); + collapsingTitleHelper.setCollapsedTypeface(typeface); + } + + /** + * Set the typeface to use for the collapsed subtitle. + * + * @param typeface typeface to use, or {@code null} to use the default. + */ + public void setCollapsedSubtitleTypeface(@Nullable Typeface typeface) { + collapsingSubtitleHelper.setCollapsedTypeface(typeface); } /** Returns the typeface used for the collapsed title. */ @NonNull public Typeface getCollapsedTitleTypeface() { - return collapsingTextHelper.getCollapsedTypeface(); + return collapsingTitleHelper.getCollapsedTypeface(); + } + + /** Returns the typeface used for the collapsed subtitle. */ + @NonNull + public Typeface getCollapsedSubtitleTypeface() { + return collapsingSubtitleHelper.getCollapsedTypeface(); } /** @@ -1288,13 +1554,28 @@ public Typeface getCollapsedTitleTypeface() { * @param typeface typeface to use, or {@code null} to use the default. */ public void setExpandedTitleTypeface(@Nullable Typeface typeface) { - collapsingTextHelper.setExpandedTypeface(typeface); + collapsingTitleHelper.setExpandedTypeface(typeface); + } + + /** + * Set the typeface to use for the expanded subtitle. + * + * @param typeface typeface to use, or {@code null} to use the default. + */ + public void setExpandedSubtitleTypeface(@Nullable Typeface typeface) { + collapsingSubtitleHelper.setExpandedTypeface(typeface); } /** Returns the typeface used for the expanded title. */ @NonNull public Typeface getExpandedTitleTypeface() { - return collapsingTextHelper.getExpandedTypeface(); + return collapsingTitleHelper.getExpandedTypeface(); + } + + /** Returns the typeface used for the expanded subtitle. */ + @NonNull + public Typeface getExpandedSubtitleTypeface() { + return collapsingSubtitleHelper.getExpandedTypeface(); } /** @@ -1321,7 +1602,8 @@ public void setExpandedTitleMargin(int start, int top, int end, int bottom) { /** * @return the starting expanded title margin in pixels * @see #setExpandedTitleMarginStart(int) - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart */ public int getExpandedTitleMarginStart() { return expandedMarginStart; @@ -1332,7 +1614,8 @@ public int getExpandedTitleMarginStart() { * * @param margin the starting title margin in pixels * @see #getExpandedTitleMarginStart() - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart */ public void setExpandedTitleMarginStart(int margin) { expandedMarginStart = margin; @@ -1342,7 +1625,8 @@ public void setExpandedTitleMarginStart(int margin) { /** * @return the top expanded title margin in pixels * @see #setExpandedTitleMarginTop(int) - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop */ public int getExpandedTitleMarginTop() { return expandedMarginTop; @@ -1353,7 +1637,8 @@ public int getExpandedTitleMarginTop() { * * @param margin the top title margin in pixels * @see #getExpandedTitleMarginTop() - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop */ public void setExpandedTitleMarginTop(int margin) { expandedMarginTop = margin; @@ -1363,7 +1648,8 @@ public void setExpandedTitleMarginTop(int margin) { /** * @return the ending expanded title margin in pixels * @see #setExpandedTitleMarginEnd(int) - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd */ public int getExpandedTitleMarginEnd() { return expandedMarginEnd; @@ -1374,7 +1660,8 @@ public int getExpandedTitleMarginEnd() { * * @param margin the ending title margin in pixels * @see #getExpandedTitleMarginEnd() - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd */ public void setExpandedTitleMarginEnd(int margin) { expandedMarginEnd = margin; @@ -1384,7 +1671,8 @@ public void setExpandedTitleMarginEnd(int margin) { /** * @return the bottom expanded title margin in pixels * @see #setExpandedTitleMarginBottom(int) - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom */ public int getExpandedTitleMarginBottom() { return expandedMarginBottom; @@ -1395,7 +1683,8 @@ public int getExpandedTitleMarginBottom() { * * @param margin the bottom title margin in pixels * @see #getExpandedTitleMarginBottom() - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom */ public void setExpandedTitleMarginBottom(int margin) { expandedMarginBottom = margin; @@ -1403,30 +1692,43 @@ public void setExpandedTitleMarginBottom(int margin) { } /** - * Sets the maximum number of lines to display in the expanded state. - * Experimental Feature. + * Returns the spacing between the expanded title and subtitle in pixels + * + * @see #setExpandedTitleSpacing(int) + * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleSpacing */ - @RestrictTo(LIBRARY_GROUP) - public void setMaxLines(int maxLines) { - collapsingTextHelper.setExpandedMaxLines(maxLines); + public int getExpandedTitleSpacing() { + return expandedTitleSpacing; } /** - * Gets the maximum number of lines to display in the expanded state. - * Experimental Feature. + * Sets the spacing between the expanded title and subtitle. + * + * @param titleSpacing the spacing between the expanded title and subtitle in pixels + * @see #getExpandedTitleSpacing() + * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleSpacing */ + public void setExpandedTitleSpacing(int titleSpacing) { + expandedTitleSpacing = titleSpacing; + requestLayout(); + } + + /** Sets the maximum number of lines to display in the expanded state. Experimental Feature. */ + @RestrictTo(LIBRARY_GROUP) + public void setMaxLines(int maxLines) { + collapsingTitleHelper.setExpandedMaxLines(maxLines); + } + + /** Gets the maximum number of lines to display in the expanded state. Experimental Feature. */ @RestrictTo(LIBRARY_GROUP) public int getMaxLines() { - return collapsingTextHelper.getExpandedMaxLines(); + return collapsingTitleHelper.getExpandedMaxLines(); } - /** - * Gets the current number of lines of the title text. - * Experimental Feature. - */ + /** Gets the current number of lines of the title text. Experimental Feature. */ @RestrictTo(LIBRARY_GROUP) public int getLineCount() { - return collapsingTextHelper.getLineCount(); + return collapsingTitleHelper.getLineCount(); } /** @@ -1436,14 +1738,14 @@ public int getLineCount() { @RestrictTo(LIBRARY_GROUP) @RequiresApi(VERSION_CODES.M) public void setLineSpacingAdd(float spacingAdd) { - collapsingTextHelper.setLineSpacingAdd(spacingAdd); + collapsingTitleHelper.setLineSpacingAdd(spacingAdd); } /** Gets the line spacing addition of the title text, or -1 if not set. Experimental Feature. */ @RestrictTo(LIBRARY_GROUP) @RequiresApi(VERSION_CODES.M) public float getLineSpacingAdd() { - return collapsingTextHelper.getLineSpacingAdd(); + return collapsingTitleHelper.getLineSpacingAdd(); } /** @@ -1453,14 +1755,14 @@ public float getLineSpacingAdd() { @RestrictTo(LIBRARY_GROUP) @RequiresApi(VERSION_CODES.M) public void setLineSpacingMultiplier(@FloatRange(from = 0.0) float spacingMultiplier) { - collapsingTextHelper.setLineSpacingMultiplier(spacingMultiplier); + collapsingTitleHelper.setLineSpacingMultiplier(spacingMultiplier); } /** Gets the line spacing multiplier of the title text, or -1 if not set. Experimental Feature. */ @RestrictTo(LIBRARY_GROUP) @RequiresApi(VERSION_CODES.M) public float getLineSpacingMultiplier() { - return collapsingTextHelper.getLineSpacingMultiplier(); + return collapsingTitleHelper.getLineSpacingMultiplier(); } /** @@ -1470,14 +1772,14 @@ public float getLineSpacingMultiplier() { @RestrictTo(LIBRARY_GROUP) @RequiresApi(VERSION_CODES.M) public void setHyphenationFrequency(int hyphenationFrequency) { - collapsingTextHelper.setHyphenationFrequency(hyphenationFrequency); + collapsingTitleHelper.setHyphenationFrequency(hyphenationFrequency); } /** Gets the hyphenation frequency of the title text, or -1 if not set. Experimental Feature. */ @RestrictTo(LIBRARY_GROUP) @RequiresApi(VERSION_CODES.M) public int getHyphenationFrequency() { - return collapsingTextHelper.getHyphenationFrequency(); + return collapsingTitleHelper.getHyphenationFrequency(); } /** @@ -1495,7 +1797,7 @@ public int getHyphenationFrequency() { @RequiresApi(VERSION_CODES.M) public void setStaticLayoutBuilderConfigurer( @Nullable StaticLayoutBuilderConfigurer staticLayoutBuilderConfigurer) { - collapsingTextHelper.setStaticLayoutBuilderConfigurer(staticLayoutBuilderConfigurer); + collapsingTitleHelper.setStaticLayoutBuilderConfigurer(staticLayoutBuilderConfigurer); } /** @@ -1504,7 +1806,7 @@ public void setStaticLayoutBuilderConfigurer( */ @RestrictTo(LIBRARY_GROUP) public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeuristicsEnabled) { - collapsingTextHelper.setRtlTextDirectionHeuristicsEnabled(rtlTextDirectionHeuristicsEnabled); + collapsingTitleHelper.setRtlTextDirectionHeuristicsEnabled(rtlTextDirectionHeuristicsEnabled); } /** @@ -1513,12 +1815,12 @@ public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeurist */ @RestrictTo(LIBRARY_GROUP) public boolean isRtlTextDirectionHeuristicsEnabled() { - return collapsingTextHelper.isRtlTextDirectionHeuristicsEnabled(); + return collapsingTitleHelper.isRtlTextDirectionHeuristicsEnabled(); } /** - * Sets whether the top system window inset should be respected regardless of what the - * {@code layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature. + * Sets whether the top system window inset should be respected regardless of what the {@code + * layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature. */ @RestrictTo(LIBRARY_GROUP) public void setForceApplySystemWindowInsetTop(boolean forceApplySystemWindowInsetTop) { @@ -1526,8 +1828,8 @@ public void setForceApplySystemWindowInsetTop(boolean forceApplySystemWindowInse } /** - * Gets whether the top system window inset should be respected regardless of what the - * {@code layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature. + * Gets whether the top system window inset should be respected regardless of what the {@code + * layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature. */ @RestrictTo(LIBRARY_GROUP) public boolean isForceApplySystemWindowInsetTop() { @@ -1606,7 +1908,7 @@ public int getScrimVisibleHeightTrigger() { * com.google.android.material.R.styleable#CollapsingToolbarLayout_titlePositionInterpolator */ public void setTitlePositionInterpolator(@Nullable TimeInterpolator interpolator) { - collapsingTextHelper.setPositionInterpolator(interpolator); + collapsingTitleHelper.setPositionInterpolator(interpolator); } /** @@ -1615,14 +1917,15 @@ public void setTitlePositionInterpolator(@Nullable TimeInterpolator interpolator */ @Nullable public TimeInterpolator getTitlePositionInterpolator() { - return collapsingTextHelper.getPositionInterpolator(); + return collapsingTitleHelper.getPositionInterpolator(); } /** * Set the duration used for scrim visibility animations. * * @param duration the duration to use in milliseconds - * @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_scrimAnimationDuration + * @attr ref + * com.google.android.material.R.styleable#CollapsingToolbarLayout_scrimAnimationDuration */ public void setScrimAnimationDuration(@IntRange(from = 0) final long duration) { scrimAnimationDuration = duration; @@ -1823,10 +2126,14 @@ public void onOffsetChanged(AppBarLayout layout, int verticalOffset) { int height = getHeight(); final int expandRange = height - getMinimumHeight() - insetTop; final int scrimRange = height - getScrimVisibleHeightTrigger(); - collapsingTextHelper.setFadeModeStartFraction( + collapsingTitleHelper.setFadeModeStartFraction( + Math.min(1, (float) scrimRange / (float) expandRange)); + collapsingTitleHelper.setCurrentOffsetY(currentOffset + expandRange); + collapsingTitleHelper.setExpansionFraction(Math.abs(verticalOffset) / (float) expandRange); + collapsingSubtitleHelper.setFadeModeStartFraction( Math.min(1, (float) scrimRange / (float) expandRange)); - collapsingTextHelper.setCurrentOffsetY(currentOffset + expandRange); - collapsingTextHelper.setExpansionFraction(Math.abs(verticalOffset) / (float) expandRange); + collapsingSubtitleHelper.setCurrentOffsetY(currentOffset + expandRange); + collapsingSubtitleHelper.setExpansionFraction(Math.abs(verticalOffset) / (float) expandRange); } } diff --git a/material/java/com/google/android/material/appbar/res-public/values/public.xml b/material/java/com/google/android/material/appbar/res-public/values/public.xml index b350134b0..ac407b7aa 100644 --- a/material/java/com/google/android/material/appbar/res-public/values/public.xml +++ b/material/java/com/google/android/material/appbar/res-public/values/public.xml @@ -20,7 +20,9 @@ + + @@ -30,10 +32,15 @@ + + + + + @@ -75,6 +82,8 @@ + + diff --git a/material/java/com/google/android/material/appbar/res/values/attrs.xml b/material/java/com/google/android/material/appbar/res/values/attrs.xml index 87dc89a95..ecddc2713 100644 --- a/material/java/com/google/android/material/appbar/res/values/attrs.xml +++ b/material/java/com/google/android/material/appbar/res/values/attrs.xml @@ -31,9 +31,15 @@ + + + + + + @@ -134,27 +140,35 @@ - - - - - + + + + + + @@ -168,6 +182,12 @@ + + + + @@ -233,10 +253,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/material/java/com/google/android/material/appbar/res/values/dimens.xml b/material/java/com/google/android/material/appbar/res/values/dimens.xml index 4184e1d68..e08d84692 100644 --- a/material/java/com/google/android/material/appbar/res/values/dimens.xml +++ b/material/java/com/google/android/material/appbar/res/values/dimens.xml @@ -22,7 +22,9 @@ @dimen/m3_comp_top_app_bar_small_container_height @dimen/m3_comp_top_app_bar_medium_container_height + 136dp @dimen/m3_comp_top_app_bar_large_container_height + 152dp 16dp 16dp diff --git a/material/java/com/google/android/material/appbar/res/values/styles.xml b/material/java/com/google/android/material/appbar/res/values/styles.xml index 542011cc5..2755efdb3 100644 --- a/material/java/com/google/android/material/appbar/res/values/styles.xml +++ b/material/java/com/google/android/material/appbar/res/values/styles.xml @@ -19,11 +19,9 @@ @@ -205,8 +207,8 @@ 0dp - @android:color/transparent - @android:color/transparent + @android:color/transparent + @android:color/transparent - + @@ -135,7 +135,11 @@ @@ -279,9 +283,7 @@ @@ -188,7 +184,7 @@ @dimen/m3_comp_input_chip_container_height @dimen/m3_comp_input_chip_with_avatar_avatar_size @dimen/m3_comp_input_chip_unselected_outline_width - @dimen/m3_comp_input_chip_container_elevation + @dimen/m3_comp_input_chip_container_elevation 4dp 0dp @@ -204,12 +200,8 @@ @@ -224,12 +216,8 @@ @@ -257,7 +245,7 @@ @dimen/m3_comp_suggestion_chip_container_height @dimen/m3_comp_suggestion_chip_with_leading_icon_leading_icon_size @dimen/m3_comp_suggestion_chip_flat_outline_width - @dimen/m3_comp_suggestion_chip_flat_container_elevation + @dimen/m3_comp_suggestion_chip_flat_container_elevation 8dp 0dp @@ -274,12 +262,10 @@ @@ -309,7 +295,7 @@ @dimen/m3_comp_assist_chip_container_height @dimen/m3_comp_assist_chip_with_icon_icon_size @dimen/m3_comp_assist_chip_flat_outline_width - @dimen/m3_comp_assist_chip_flat_container_elevation + @dimen/m3_comp_assist_chip_flat_container_elevation 8dp 0dp @@ -326,12 +312,8 @@ @@ -348,7 +330,7 @@ @dimen/m3_comp_filter_chip_container_height @dimen/m3_comp_filter_chip_with_icon_icon_size @dimen/m3_comp_filter_chip_flat_unselected_outline_width - @dimen/m3_comp_filter_chip_flat_container_elevation + @dimen/m3_comp_filter_chip_flat_container_elevation 8dp 0dp @@ -365,11 +347,8 @@ diff --git a/material/java/com/google/android/material/color/res/values/theme_overlay.xml b/material/java/com/google/android/material/color/res/values/theme_overlay.xml index d9a962736..04e765346 100644 --- a/material/java/com/google/android/material/color/res/values/theme_overlay.xml +++ b/material/java/com/google/android/material/color/res/values/theme_overlay.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + @@ -91,13 +91,13 @@ @@ -142,7 +142,7 @@ @@ -229,9 +219,7 @@ @color/m3_efab_ripple_color_selector @dimen/m3_comp_extended_fab_primary_container_height @dimen/m3_comp_extended_fab_primary_container_elevation - - @animator/m3_extended_fab_state_list_animator - + @animator/m3_extended_fab_state_list_animator @dimen/m3_comp_extended_fab_primary_icon_size @macro/m3_comp_extended_fab_primary_label_text_type @macro/m3_comp_extended_fab_primary_container_shape @@ -262,14 +250,10 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/material/java/com/google/android/material/floatingtoolbar/res/values/tokens.xml b/material/java/com/google/android/material/floatingtoolbar/res/values/tokens.xml new file mode 100644 index 000000000..05a20d426 --- /dev/null +++ b/material/java/com/google/android/material/floatingtoolbar/res/values/tokens.xml @@ -0,0 +1,34 @@ + + + + + + + + + + ?attr/colorSurfaceContainer + ?attr/colorPrimaryContainer + + 64dp + 8dp + 8dp + 16dp + + diff --git a/material/java/com/google/android/material/navigation/NavigationBarItemView.java b/material/java/com/google/android/material/navigation/NavigationBarItemView.java index b76a8e9c5..e7cc3e91a 100644 --- a/material/java/com/google/android/material/navigation/NavigationBarItemView.java +++ b/material/java/com/google/android/material/navigation/NavigationBarItemView.java @@ -166,6 +166,7 @@ public abstract class NavigationBarItemView extends FrameLayout private boolean expanded = false; private boolean onlyShowWhenExpanded = false; private boolean measurePaddingFromBaseline = false; + private boolean scaleLabelSizeWithFont = false; public NavigationBarItemView(@NonNull Context context) { super(context); @@ -208,20 +209,37 @@ public NavigationBarItemView(@NonNull Context context) { if (icon.getVisibility() == VISIBLE) { tryUpdateBadgeBounds(icon); } - // If item icon gravity is start, we want to update the active indicator width in a layout - // change listener to keep the active indicator size up to date with the content width. LayoutParams lp = (LayoutParams) innerContentContainer.getLayoutParams(); int newWidth = right - left + lp.rightMargin + lp.leftMargin; + int newHeight = bottom - top + lp.topMargin + lp.bottomMargin; + // If item icon gravity is start, we want to update the active indicator width in a layout + // change listener to keep the active indicator size up to date with the content width. if (itemIconGravity == ITEM_ICON_GRAVITY_START - && activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT - && newWidth != activeIndicatorView.getMeasuredWidth()) { + && activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT) { + LayoutParams indicatorParams = (LayoutParams) activeIndicatorView.getLayoutParams(); - int minWidth = - min( - activeIndicatorDesiredWidth, - getMeasuredWidth() - (activeIndicatorMarginHorizontal * 2)); - indicatorParams.width = max(newWidth, minWidth); - activeIndicatorView.setLayoutParams(indicatorParams); + boolean layoutParamsChanged = false; + if (activeIndicatorExpandedDesiredWidth == ACTIVE_INDICATOR_WIDTH_WRAP_CONTENT + && activeIndicatorView.getMeasuredWidth() != newWidth) { + int minWidth = + min( + activeIndicatorDesiredWidth, + getMeasuredWidth() - (activeIndicatorMarginHorizontal * 2)); + indicatorParams.width = max(newWidth, minWidth); + layoutParamsChanged = true; + } + + // We expect the active indicator height to be larger than the height of the + // inner content due to having a min height, but if it is smaller (for example due to + // the text content changing to be multi-line) it should encompass that + if (activeIndicatorView.getMeasuredHeight() < newHeight) { + indicatorParams.height = newHeight; + layoutParamsChanged = true; + } + + if (layoutParamsChanged) { + activeIndicatorView.setLayoutParams(indicatorParams); + } } }); } @@ -819,12 +837,28 @@ public void setMeasureBottomPaddingFromLabelBaseline(boolean measurePaddingFromB requestLayout(); } + public void setLabelFontScalingEnabled(boolean scaleLabelSizeWithFont) { + this.scaleLabelSizeWithFont = scaleLabelSizeWithFont; + setTextAppearanceActive(textAppearanceActive); + setTextAppearanceInactive(textAppearanceInactive); + setHorizontalTextAppearanceActive(horizontalTextAppearanceActive); + setHorizontalTextAppearanceInactive(horizontalTextAppearanceInactive); + } + + private void setTextAppearanceForLabel(TextView label, int textAppearance) { + if (scaleLabelSizeWithFont) { + TextViewCompat.setTextAppearance(label, textAppearance); + } else { + setTextAppearanceWithoutFontScaling(label, textAppearance); + } + } + private void updateInactiveLabelTextAppearance( @Nullable TextView smallLabel, @StyleRes int textAppearanceInactive) { if (smallLabel == null) { return; } - setTextAppearanceWithoutFontScaling(smallLabel, textAppearanceInactive); + setTextAppearanceForLabel(smallLabel, textAppearanceInactive); calculateTextScaleFactors(); smallLabel.setMinimumHeight( MaterialResources.getUnscaledLineHeight( @@ -841,7 +875,7 @@ private void updateActiveLabelTextAppearance( if (largeLabel == null) { return; } - setTextAppearanceWithoutFontScaling(largeLabel, textAppearanceActive); + setTextAppearanceForLabel(largeLabel, textAppearanceActive); calculateTextScaleFactors(); largeLabel.setMinimumHeight( MaterialResources.getUnscaledLineHeight( @@ -911,6 +945,37 @@ private static void setTextAppearanceWithoutFontScaling( } } + public void setLabelMaxLines(int labelMaxLines) { + smallLabel.setMaxLines(labelMaxLines); + largeLabel.setMaxLines(labelMaxLines); + expandedSmallLabel.setMaxLines(labelMaxLines); + expandedLargeLabel.setMaxLines(labelMaxLines); + + // Due to b/316260445 that was fixed in V+, text with ellipses may be cut off when centered + // due to letter spacing being miscalculated for the ellipses character. We only center the text + // in the following scenarios: + // 1. API level is greater than 34, OR + // 2. The text is not cut off by an ellipses + if (VERSION.SDK_INT > VERSION_CODES.UPSIDE_DOWN_CAKE) { + smallLabel.setGravity(Gravity.CENTER); + largeLabel.setGravity(Gravity.CENTER); + } else if (labelMaxLines > 1) { + // If not single-line, remove the ellipses and center. Removing the ellipses is an unfortunate + // tradeoff due to this bug. We do not want to remove the ellipses for single-line text + // because centering text is not useful (since the textview is centered already) and we + // would rather keep the ellipses. + smallLabel.setEllipsize(null); + largeLabel.setEllipsize(null); + smallLabel.setGravity(Gravity.CENTER); + largeLabel.setGravity(Gravity.CENTER); + } else { + smallLabel.setGravity(Gravity.CENTER_VERTICAL); + largeLabel.setGravity(Gravity.CENTER_VERTICAL); + } + + requestLayout(); + } + public void setTextColor(@Nullable ColorStateList color) { textColor = color; if (color != null) { @@ -1133,7 +1198,8 @@ private void updateActiveIndicatorLayoutParams(int availableWidth) { } else { newWidth = min(activeIndicatorExpandedDesiredWidth, adjustedAvailableWidth); } - newHeight = activeIndicatorExpandedDesiredHeight; + newHeight = + max(activeIndicatorExpandedDesiredHeight, innerContentContainer.getMeasuredHeight()); } LayoutParams indicatorParams = (LayoutParams) activeIndicatorView.getLayoutParams(); // If the label visibility is unlabeled, make the active indicator's height equal to its diff --git a/material/java/com/google/android/material/navigation/NavigationBarMenuView.java b/material/java/com/google/android/material/navigation/NavigationBarMenuView.java index aa8bf770d..a78d9c8c4 100644 --- a/material/java/com/google/android/material/navigation/NavigationBarMenuView.java +++ b/material/java/com/google/android/material/navigation/NavigationBarMenuView.java @@ -36,6 +36,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.TextView; import androidx.annotation.Dimension; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -121,6 +122,8 @@ public abstract class NavigationBarMenuView extends ViewGroup implements MenuVie private NavigationBarPresenter presenter; private NavigationBarMenuBuilder menu; private boolean measurePaddingFromLabelBaseline; + private boolean scaleLabelWithFont; + private int labelMaxLines = 1; private int itemPoolSize = 0; private boolean expanded; @@ -139,6 +142,7 @@ public NavigationBarMenuView(@NonNull Context context) { } else { set = new AutoTransition(); set.setOrdering(TransitionSet.ORDERING_TOGETHER); + set.excludeTarget(TextView.class, true); set.setDuration( MotionUtils.resolveThemeDuration( getContext(), @@ -180,7 +184,7 @@ public void setCheckedItem(@NonNull MenuItem checkedItem) { return; } // Unset the previous checked item - if (this.checkedItem != null) { + if (this.checkedItem != null && this.checkedItem.isChecked()) { this.checkedItem.setChecked(false); } checkedItem.setChecked(true); @@ -500,6 +504,38 @@ public void setMeasurePaddingFromLabelBaseline(boolean measurePaddingFromLabelBa } } + public void setLabelFontScalingEnabled(boolean scaleLabelWithFont) { + this.scaleLabelWithFont = scaleLabelWithFont; + if (buttons != null) { + for (NavigationBarMenuItemView item : buttons) { + if (item instanceof NavigationBarItemView) { + ((NavigationBarItemView) item) + .setLabelFontScalingEnabled(scaleLabelWithFont); + } + } + } + } + + public boolean getScaleLabelTextWithFont() { + return scaleLabelWithFont; + } + + public void setLabelMaxLines(int labelMaxLines) { + this.labelMaxLines = labelMaxLines; + if (buttons != null) { + for (NavigationBarMenuItemView item : buttons) { + if (item instanceof NavigationBarItemView) { + ((NavigationBarItemView) item) + .setLabelMaxLines(labelMaxLines); + } + } + } + } + + public int getLabelMaxLines() { + return labelMaxLines; + } + /** * Get the distance between the item's active indicator container and the label. */ @@ -1060,6 +1096,7 @@ private NavigationBarItemView createMenuItem( presenter.setUpdateSuspended(false); NavigationBarItemView child = getNewItem(); child.setShifting(shifting); + child.setLabelMaxLines(labelMaxLines); child.setIconTintList(itemIconTint); child.setIconSize(itemIconSize); // Set the text color the default, then look for another text color in order of precedence. @@ -1077,6 +1114,7 @@ private NavigationBarItemView createMenuItem( child.setItemPaddingBottom(itemPaddingBottom); } child.setMeasureBottomPaddingFromLabelBaseline(measurePaddingFromLabelBaseline); + child.setLabelFontScalingEnabled(scaleLabelWithFont); if (itemActiveIndicatorLabelPadding != NO_PADDING) { child.setActiveIndicatorLabelPadding(itemActiveIndicatorLabelPadding); } diff --git a/material/java/com/google/android/material/navigation/NavigationBarView.java b/material/java/com/google/android/material/navigation/NavigationBarView.java index d63ad2675..3376ab68c 100644 --- a/material/java/com/google/android/material/navigation/NavigationBarView.java +++ b/material/java/com/google/android/material/navigation/NavigationBarView.java @@ -327,6 +327,12 @@ public NavigationBarView( setMeasureBottomPaddingFromLabelBaseline(attributes.getBoolean( R.styleable.NavigationBarView_measureBottomPaddingFromLabelBaseline, true)); + setLabelFontScalingEnabled( + attributes.getBoolean(R.styleable.NavigationBarView_labelFontScalingEnabled, false)); + + setLabelMaxLines( + attributes.getInteger(R.styleable.NavigationBarView_labelMaxLines, 1)); + int activeIndicatorStyleResId = attributes.getResourceId(R.styleable.NavigationBarView_itemActiveIndicatorStyle, 0); @@ -708,6 +714,34 @@ private void setMeasureBottomPaddingFromLabelBaseline(boolean measurePaddingFrom menuView.setMeasurePaddingFromLabelBaseline(measurePaddingFromBaseline); } + /** + * Sets whether or not the label text should scale with the system font size. + */ + public void setLabelFontScalingEnabled(boolean labelFontScalingEnabled) { + menuView.setLabelFontScalingEnabled(labelFontScalingEnabled); + } + + /** + * Returns whether or not the label text should scale with the system font size. + */ + public boolean getScaleLabelTextWithFont() { + return menuView.getScaleLabelTextWithFont(); + } + + /** + * Set the max lines limit for the label text. + */ + public void setLabelMaxLines(int labelMaxLines) { + menuView.setLabelMaxLines(labelMaxLines); + } + + /** + * Returns the max lines limit for the label text. + */ + public int getLabelMaxLines(int labelMaxLines) { + return menuView.getLabelMaxLines(); + } + /** * Set the distance between the active indicator container and the item's label. */ diff --git a/material/java/com/google/android/material/navigation/res-public/values/public.xml b/material/java/com/google/android/material/navigation/res-public/values/public.xml index 26219a998..3012ac6d2 100644 --- a/material/java/com/google/android/material/navigation/res-public/values/public.xml +++ b/material/java/com/google/android/material/navigation/res-public/values/public.xml @@ -66,5 +66,9 @@ + + + + diff --git a/material/java/com/google/android/material/navigation/res/values/attrs.xml b/material/java/com/google/android/material/navigation/res/values/attrs.xml index 1080bd0c9..d98863fe9 100644 --- a/material/java/com/google/android/material/navigation/res/values/attrs.xml +++ b/material/java/com/google/android/material/navigation/res/values/attrs.xml @@ -118,6 +118,12 @@ + + + + + + diff --git a/material/java/com/google/android/material/navigationrail/NavigationRailMenuView.java b/material/java/com/google/android/material/navigationrail/NavigationRailMenuView.java index f7d2df73e..d6942dd8f 100644 --- a/material/java/com/google/android/material/navigationrail/NavigationRailMenuView.java +++ b/material/java/com/google/android/material/navigationrail/NavigationRailMenuView.java @@ -175,8 +175,8 @@ private int measureSharedChildHeights( } private int measureChildHeight(View child, int widthMeasureSpec, int heightMeasureSpec) { + child.measure(widthMeasureSpec, heightMeasureSpec); if (child.getVisibility() != GONE) { - child.measure(widthMeasureSpec, heightMeasureSpec); return child.getMeasuredHeight(); } diff --git a/material/java/com/google/android/material/navigationrail/NavigationRailView.java b/material/java/com/google/android/material/navigationrail/NavigationRailView.java index 7107eec6d..bf9c327de 100644 --- a/material/java/com/google/android/material/navigationrail/NavigationRailView.java +++ b/material/java/com/google/android/material/navigationrail/NavigationRailView.java @@ -169,22 +169,10 @@ public NavigationRailView( // Ensure we are using the correctly themed context rather than the context that was passed in. context = getContext(); - minExpandedWidth = - getContext() - .getResources() - .getDimensionPixelSize(R.dimen.m3_navigation_rail_min_expanded_width); - maxExpandedWidth = - getContext() - .getResources() - .getDimensionPixelSize(R.dimen.m3_navigation_rail_max_expanded_width); expandedItemSpacing = getContext() .getResources() .getDimensionPixelSize(R.dimen.m3_navigation_rail_expanded_item_spacing); - expandedItemMinHeight = - getContext() - .getResources() - .getDimensionPixelSize(R.dimen.m3_navigation_rail_expanded_item_min_height); expandedItemGravity = ITEM_GRAVITY_START_CENTER; expandedIconGravity = ITEM_ICON_GRAVITY_START; @@ -213,11 +201,31 @@ public NavigationRailView( setMenuGravity( attributes.getInt(R.styleable.NavigationRailView_menuGravity, DEFAULT_MENU_GRAVITY)); - if (attributes.hasValue(R.styleable.NavigationRailView_itemMinHeight)) { - setCollapsedItemMinimumHeight( - attributes.getDimensionPixelSize( - R.styleable.NavigationRailView_itemMinHeight, NO_ITEM_MINIMUM_HEIGHT)); + int collapsedItemMinHeight = attributes.getDimensionPixelSize( + R.styleable.NavigationRailView_itemMinHeight, NO_ITEM_MINIMUM_HEIGHT); + int expandedItemMinHeight = attributes.getDimensionPixelSize( + R.styleable.NavigationRailView_itemMinHeight, NO_ITEM_MINIMUM_HEIGHT); + + if (attributes.hasValue(R.styleable.NavigationRailView_collapsedItemMinHeight)) { + collapsedItemMinHeight = attributes.getDimensionPixelSize( + R.styleable.NavigationRailView_collapsedItemMinHeight, NO_ITEM_MINIMUM_HEIGHT); } + if (attributes.hasValue(R.styleable.NavigationRailView_expandedItemMinHeight)) { + expandedItemMinHeight = attributes.getDimensionPixelSize( + R.styleable.NavigationRailView_expandedItemMinHeight, NO_ITEM_MINIMUM_HEIGHT); + } + setCollapsedItemMinimumHeight(collapsedItemMinHeight); + setExpandedItemMinimumHeight(expandedItemMinHeight); + minExpandedWidth = attributes.getDimensionPixelSize( + R.styleable.NavigationRailView_expandedMinWidth, + context + .getResources() + .getDimensionPixelSize(R.dimen.m3_navigation_rail_min_expanded_width)); + maxExpandedWidth = attributes.getDimensionPixelSize( + R.styleable.NavigationRailView_expandedMaxWidth, + context + .getResources() + .getDimensionPixelSize(R.dimen.m3_navigation_rail_max_expanded_width)); if (attributes.hasValue(R.styleable.NavigationRailView_paddingTopSystemWindowInsets)) { paddingTopSystemWindowInsets = @@ -568,6 +576,32 @@ public void setCollapsedItemMinimumHeight(@Px int minHeight) { } } + /** + * Gets the minimum height of a navigation rail menu item when the navigation rail is collapsed. + */ + public int getCollapsedItemMinimumHeight() { + return collapsedItemMinHeight; + } + + /** + * Sets the minimum height of a navigation rail menu item when the navigation rail is expanded. + * + * @param minHeight the min height of the item when the nav rail is collapsed + */ + public void setExpandedItemMinimumHeight(@Px int minHeight) { + expandedItemMinHeight = minHeight; + if (expanded) { + ((NavigationRailMenuView) getMenuView()).setItemMinimumHeight(minHeight); + } + } + + /** + * Gets the minimum height of a navigation rail menu item when the navigation rail is expanded. + */ + public int getExpandedItemMinimumHeight() { + return expandedItemMinHeight; + } + /** * Set the padding in between the navigation rail menu items. */ diff --git a/material/java/com/google/android/material/navigationrail/res-public/values/public.xml b/material/java/com/google/android/material/navigationrail/res-public/values/public.xml index e592eb6b9..6de483528 100644 --- a/material/java/com/google/android/material/navigationrail/res-public/values/public.xml +++ b/material/java/com/google/android/material/navigationrail/res-public/values/public.xml @@ -18,6 +18,9 @@ + + + diff --git a/material/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml b/material/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml index a407643f3..30a44a2c8 100644 --- a/material/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml +++ b/material/java/com/google/android/material/navigationrail/res/layout/mtrl_navigation_rail_item.xml @@ -72,7 +72,7 @@ android:layout_height="wrap_content" android:duplicateParentState="true" android:ellipsize="end" - android:maxLines="1" + android:gravity="center_vertical" android:paddingStart="@dimen/m3_navigation_rail_label_padding_horizontal" android:paddingEnd="@dimen/m3_navigation_rail_label_padding_horizontal" android:textSize="@dimen/mtrl_navigation_rail_text_size" /> @@ -81,8 +81,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:duplicateParentState="true" + android:gravity="center_vertical" android:ellipsize="end" - android:maxLines="1" android:textSize="@dimen/mtrl_navigation_rail_active_text_size" android:paddingStart="@dimen/m3_navigation_rail_label_padding_horizontal" android:paddingEnd="@dimen/m3_navigation_rail_label_padding_horizontal" diff --git a/material/java/com/google/android/material/navigationrail/res/values/attrs.xml b/material/java/com/google/android/material/navigationrail/res/values/attrs.xml index ef396190a..d1558b967 100644 --- a/material/java/com/google/android/material/navigationrail/res/values/attrs.xml +++ b/material/java/com/google/android/material/navigationrail/res/values/attrs.xml @@ -19,9 +19,19 @@ - + + + + + + + + + diff --git a/material/java/com/google/android/material/navigationrail/res/values/dimens.xml b/material/java/com/google/android/material/navigationrail/res/values/dimens.xml index 8e158f81e..3f578ea0e 100644 --- a/material/java/com/google/android/material/navigationrail/res/values/dimens.xml +++ b/material/java/com/google/android/material/navigationrail/res/values/dimens.xml @@ -47,6 +47,5 @@ 220dp 360dp 0dp - 56dp diff --git a/material/java/com/google/android/material/progressindicator/BaseProgressIndicator.java b/material/java/com/google/android/material/progressindicator/BaseProgressIndicator.java index b569f9cea..413aca64d 100644 --- a/material/java/com/google/android/material/progressindicator/BaseProgressIndicator.java +++ b/material/java/com/google/android/material/progressindicator/BaseProgressIndicator.java @@ -29,10 +29,12 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup; import android.view.ViewParent; import android.widget.ProgressBar; import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -470,10 +472,10 @@ && getWindowVisibility() == View.VISIBLE *

This is necessary as before API 24, it is not guaranteed that Views will ever be notified * about their parent changing. Thus, we don't have a proper point to hook in and re-check {@link * #isShown()} on parent changes that result from {@link - * android.view.ViewGroup#attachViewToParent(View, int, LayoutParams)}, which *can* change our - * effective visibility. So this method errs on the side of assuming visibility unless we can - * conclusively prove otherwise (but may result in some false positives, if this view ends up - * being attached to a non-visible hierarchy after being detached in a visible state). + * android.view.ViewGroup#attachViewToParent(View, int, ViewGroup.LayoutParams)}, which *can* + * change our effective visibility. So this method errs on the side of assuming visibility unless + * we can conclusively prove otherwise (but may result in some false positives, if this view + * ends up being attached to a non-visible hierarchy after being detached in a visible state). */ boolean isEffectivelyVisible() { View current = this; @@ -906,6 +908,22 @@ public void setVisibilityAfterHide(int visibility) { visibilityAfterHide = visibility; } + /** + * Sets the scale of the animation duration in indeterminate mode. + * + * @param indeterminateAnimatorDurationScale The new scale of the animation duration in + * indeterminate mode. + * @attr ref + * com.google.android.material.progressindicator.R.styleable#BaseProgressIndicator_indeterminateAnimatorDurationScale + */ + public void setIndeterminateAnimatorDurationScale( + @FloatRange(from = 0.1f, to = 10f) float indeterminateAnimatorDurationScale) { + if (spec.indeterminateAnimatorDurationScale != indeterminateAnimatorDurationScale) { + spec.indeterminateAnimatorDurationScale = indeterminateAnimatorDurationScale; + getIndeterminateDrawable().getAnimatorDelegate().invalidateSpecValues(); + } + } + /** @hide */ @RestrictTo(Scope.LIBRARY_GROUP) @VisibleForTesting diff --git a/material/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java b/material/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java index 1f2795d72..3965bb652 100644 --- a/material/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java +++ b/material/java/com/google/android/material/progressindicator/BaseProgressIndicatorSpec.java @@ -29,6 +29,7 @@ import androidx.annotation.AttrRes; import androidx.annotation.CallSuper; import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.Px; @@ -85,6 +86,10 @@ public abstract class BaseProgressIndicatorSpec { /** The speed of the waveform, if a wave effect is configured. */ @Px public int waveSpeed; + /** The scale of the animation duration in indeterminate mode. */ + @FloatRange(from = 0.1f, to = 10f) + public float indeterminateAnimatorDurationScale; + /** * Instantiates BaseProgressIndicatorSpec. * @@ -135,6 +140,8 @@ protected BaseProgressIndicatorSpec( waveAmplitude = abs(a.getDimensionPixelSize(R.styleable.BaseProgressIndicator_waveAmplitude, 0)); waveSpeed = a.getDimensionPixelSize(R.styleable.BaseProgressIndicator_waveSpeed, 0); + indeterminateAnimatorDurationScale = + a.getFloat(R.styleable.BaseProgressIndicator_indeterminateAnimatorDurationScale, 1); loadIndicatorColors(context, a); loadTrackColor(context, a); diff --git a/material/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java b/material/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java index 5888f1a1a..737fe4e3b 100644 --- a/material/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java +++ b/material/java/com/google/android/material/progressindicator/CircularDrawingDelegate.java @@ -24,7 +24,6 @@ import static java.lang.Math.toDegrees; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; @@ -68,6 +67,10 @@ final class CircularDrawingDelegate extends DrawingDelegate endPoints = new Pair<>(new PathPoint(), new PathPoint()); + /** Instantiates CircularDrawingDelegate with the current spec. */ CircularDrawingDelegate(@NonNull CircularProgressIndicatorSpec spec) { super(spec); @@ -349,24 +352,24 @@ private void drawArc( // Draws the arc without rounded corners. float startDegreeWithoutCorners = startDegree + displayedCornerRadiusInDegree; float arcDegreeWithoutCorners = arcDegree - displayedCornerRadiusInDegree * 2; - Pair endPoints = new Pair<>(new PathPoint(), new PathPoint()); + endPoints.first.reset(); + endPoints.second.reset(); if (!shouldDrawWavyPath) { endPoints.first.rotate(startDegreeWithoutCorners + 90); endPoints.first.moveAcross(-adjustedRadius); endPoints.second.rotate(startDegreeWithoutCorners + arcDegreeWithoutCorners + 90); endPoints.second.moveAcross(-adjustedRadius); - RectF arcBound = - new RectF(-adjustedRadius, -adjustedRadius, adjustedRadius, adjustedRadius); - canvas.drawArc(arcBound, startDegreeWithoutCorners, arcDegreeWithoutCorners, false, paint); + arcBounds.set(-adjustedRadius, -adjustedRadius, adjustedRadius, adjustedRadius); + canvas.drawArc(arcBounds, startDegreeWithoutCorners, arcDegreeWithoutCorners, false, paint); } else { - endPoints = - getDisplayedPath( - activePathMeasure, - displayedActivePath, - startDegreeWithoutCorners / 360, - arcDegreeWithoutCorners / 360, - amplitudeFraction, - phaseFraction); + calculateDisplayedPath( + activePathMeasure, + displayedActivePath, + endPoints, + startDegreeWithoutCorners / 360, + arcDegreeWithoutCorners / 360, + amplitudeFraction, + phaseFraction); canvas.drawPath(displayedActivePath, paint); } @@ -451,7 +454,7 @@ void invalidateCachedPaths() { QUARTER_CIRCLE_CONTROL_HANDLE_LENGTH, -1, 1, -QUARTER_CIRCLE_CONTROL_HANDLE_LENGTH, 1, 0); } // Scales the circle to its radius. - Matrix transform = new Matrix(); + transform.reset(); transform.setScale(adjustedRadius, adjustedRadius); cachedActivePath.transform(transform); if (spec.hasWavyEffect(drawingDeterminateIndicator)) { @@ -515,10 +518,10 @@ private void appendCubicPerHalfCycle( anchor2.posVec[1]); } - @NonNull - private Pair getDisplayedPath( + private void calculateDisplayedPath( @NonNull PathMeasure pathMeasure, @NonNull Path displayedPath, + @NonNull Pair endPoints, float start, float span, float amplitudeFraction, @@ -548,16 +551,17 @@ private Pair getDisplayedPath( float endDistance = (start + span) * pathMeasure.getLength() / 2; pathMeasure.getSegment(startDistance, endDistance, displayedPath, true); // Gathers the position and tangent of the start and end. - PathPoint startPoint = new PathPoint(); + PathPoint startPoint = endPoints.first; + startPoint.reset(); pathMeasure.getPosTan(startDistance, startPoint.posVec, startPoint.tanVec); - PathPoint endPoint = new PathPoint(); + PathPoint endPoint = endPoints.second; + endPoint.reset(); pathMeasure.getPosTan(endDistance, endPoint.posVec, endPoint.tanVec); // Transforms the result path to match the canvas. - Matrix transform = new Matrix(); + transform.reset(); transform.setRotate(resultRotation); startPoint.rotate(resultRotation); endPoint.rotate(resultRotation); displayedPath.transform(transform); - return new Pair<>(startPoint, endPoint); } } diff --git a/material/java/com/google/android/material/progressindicator/CircularIndeterminateAdvanceAnimatorDelegate.java b/material/java/com/google/android/material/progressindicator/CircularIndeterminateAdvanceAnimatorDelegate.java index 09d9e4681..784cbb9a1 100644 --- a/material/java/com/google/android/material/progressindicator/CircularIndeterminateAdvanceAnimatorDelegate.java +++ b/material/java/com/google/android/material/progressindicator/CircularIndeterminateAdvanceAnimatorDelegate.java @@ -86,7 +86,8 @@ private void maybeInitializeAnimators() { if (animator == null) { // Instantiates an animator with the linear interpolator to control the animation progress. animator = ObjectAnimator.ofFloat(this, ANIMATION_FRACTION, 0, 1); - animator.setDuration(TOTAL_DURATION_IN_MS); + animator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); animator.setInterpolator(null); animator.setRepeatCount(ValueAnimator.INFINITE); animator.addListener( @@ -102,7 +103,8 @@ public void onAnimationRepeat(Animator animation) { if (completeEndAnimator == null) { completeEndAnimator = ObjectAnimator.ofFloat(this, COMPLETE_END_FRACTION, 0, 1); - completeEndAnimator.setDuration(DURATION_TO_COMPLETE_END_IN_MS); + completeEndAnimator.setDuration( + (long) (DURATION_TO_COMPLETE_END_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); completeEndAnimator.setInterpolator(interpolator); completeEndAnimator.addListener( new AnimatorListenerAdapter() { @@ -118,6 +120,14 @@ public void onAnimationEnd(Animator animation) { } } + private void updateAnimatorsDuration() { + maybeInitializeAnimators(); + animator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); + completeEndAnimator.setDuration( + (long) (DURATION_TO_COMPLETE_END_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); + } + @Override void cancelAnimatorImmediately() { if (animator != null) { @@ -141,6 +151,7 @@ void requestCancelAnimatorAfterCurrentCycle() { @Override public void invalidateSpecValues() { + updateAnimatorsDuration(); resetPropertiesForNewStart(); } diff --git a/material/java/com/google/android/material/progressindicator/CircularIndeterminateRetreatAnimatorDelegate.java b/material/java/com/google/android/material/progressindicator/CircularIndeterminateRetreatAnimatorDelegate.java index 07563b57d..fdde4e803 100644 --- a/material/java/com/google/android/material/progressindicator/CircularIndeterminateRetreatAnimatorDelegate.java +++ b/material/java/com/google/android/material/progressindicator/CircularIndeterminateRetreatAnimatorDelegate.java @@ -101,7 +101,8 @@ private void maybeInitializeAnimators() { if (animator == null) { // Instantiates an animator with the linear interpolator to control the animation progress. animator = ObjectAnimator.ofFloat(this, ANIMATION_FRACTION, 0, 1); - animator.setDuration(TOTAL_DURATION_IN_MS); + animator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); animator.setInterpolator(null); animator.setRepeatCount(ValueAnimator.INFINITE); animator.addListener( @@ -118,7 +119,8 @@ public void onAnimationRepeat(Animator animation) { if (completeEndAnimator == null) { completeEndAnimator = ObjectAnimator.ofFloat(this, COMPLETE_END_FRACTION, 0, 1); - completeEndAnimator.setDuration(DURATION_TO_COMPLETE_END_IN_MS); + completeEndAnimator.setDuration( + (long) (DURATION_TO_COMPLETE_END_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); completeEndAnimator.addListener( new AnimatorListenerAdapter() { @Override @@ -133,6 +135,14 @@ public void onAnimationEnd(Animator animation) { } } + private void updateAnimatorsDuration() { + maybeInitializeAnimators(); + animator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); + completeEndAnimator.setDuration( + (long) (DURATION_TO_COMPLETE_END_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); + } + @Override void cancelAnimatorImmediately() { if (animator != null) { @@ -156,6 +166,7 @@ void requestCancelAnimatorAfterCurrentCycle() { @Override public void invalidateSpecValues() { + updateAnimatorsDuration(); resetPropertiesForNewStart(); } diff --git a/material/java/com/google/android/material/progressindicator/DeterminateDrawable.java b/material/java/com/google/android/material/progressindicator/DeterminateDrawable.java index e60e6f651..d7845dc5a 100644 --- a/material/java/com/google/android/material/progressindicator/DeterminateDrawable.java +++ b/material/java/com/google/android/material/progressindicator/DeterminateDrawable.java @@ -23,7 +23,6 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint.Style; -import android.graphics.Rect; import android.widget.ProgressBar; import androidx.annotation.NonNull; import androidx.core.math.MathUtils; @@ -322,8 +321,6 @@ private void maybeStartAmplitudeAnimator(int level) { @Override public void draw(@NonNull Canvas canvas) { - Rect clipBounds = new Rect(); - if (getBounds().isEmpty() || !isVisible() || !canvas.getClipBounds(clipBounds)) { // Escape if bounds are empty, clip bounds are empty, or currently hidden. return; diff --git a/material/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java b/material/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java index c73d6036e..70f06bc68 100644 --- a/material/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java +++ b/material/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java @@ -23,6 +23,7 @@ import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.Property; import androidx.annotation.FloatRange; @@ -83,12 +84,16 @@ abstract class DrawableWithAnimatedVisibilityChange extends Drawable implements @IntRange(from = 0, to = 255) private int totalAlpha; + // Pre-allocates objects used in draw(). + @NonNull Rect clipBounds; + // ******************* Constructor ******************* DrawableWithAnimatedVisibilityChange( @NonNull Context context, @NonNull BaseProgressIndicatorSpec baseSpec) { this.context = context; this.baseSpec = baseSpec; + this.clipBounds = new Rect(); animatorDurationScaleProvider = new AnimatorDurationScaleProvider(); setAlpha(255); @@ -236,7 +241,6 @@ public boolean setVisible(boolean visible, boolean restart) { * @param animate Whether to change the visibility with animation. * @return {@code true}, if the visibility changes or will change after the animation; {@code * false}, otherwise. - * @see #setVisible(boolean, boolean, boolean) */ public boolean setVisible(boolean visible, boolean restart, boolean animate) { float systemAnimatorDurationScale = diff --git a/material/java/com/google/android/material/progressindicator/DrawingDelegate.java b/material/java/com/google/android/material/progressindicator/DrawingDelegate.java index 9d8559715..f65b36fa3 100644 --- a/material/java/com/google/android/material/progressindicator/DrawingDelegate.java +++ b/material/java/com/google/android/material/progressindicator/DrawingDelegate.java @@ -34,6 +34,7 @@ import androidx.annotation.IntRange; import androidx.annotation.NonNull; import androidx.annotation.Px; +import java.util.Arrays; /** A delegate abstract class for drawing the graphics in different drawable classes. */ abstract class DrawingDelegate { @@ -48,8 +49,12 @@ abstract class DrawingDelegate { final PathMeasure activePathMeasure = new PathMeasure(cachedActivePath, /* forceClosed= */ false); + // Pre-allocates a Matrix to transform this point. + final Matrix transform; + public DrawingDelegate(S spec) { this.spec = spec; + this.transform = new Matrix(); } /** @@ -186,9 +191,12 @@ protected class PathPoint { float[] posVec = new float[2]; // The tangent vector of this point on a path. The length is not guaranteed. float[] tanVec = new float[2]; + // Pre-allocates a Matrix for transform this point. + final Matrix transform; public PathPoint() { tanVec[0] = 1; + transform = new Matrix(); } public PathPoint(PathPoint other) { @@ -198,6 +206,7 @@ public PathPoint(PathPoint other) { public PathPoint(float[] pos, float[] tan) { arraycopy(pos, 0, this.posVec, 0, 2); arraycopy(tan, 0, this.tanVec, 0, 2); + transform = new Matrix(); } /** Moves this point by (x, y). */ @@ -239,10 +248,17 @@ void moveAcross(float distance) { /** Rotates the coordinates by the given degrees around (0, 0). */ public void rotate(float rotationDegrees) { - Matrix transform = new Matrix(); + transform.reset(); transform.setRotate(rotationDegrees); transform.mapPoints(posVec); transform.mapPoints(tanVec); } + + public void reset() { + Arrays.fill(posVec, 0); + Arrays.fill(tanVec, 0); + tanVec[0] = 1; + transform.reset(); + } } } diff --git a/material/java/com/google/android/material/progressindicator/IndeterminateDrawable.java b/material/java/com/google/android/material/progressindicator/IndeterminateDrawable.java index e53751cb9..827f47e78 100644 --- a/material/java/com/google/android/material/progressindicator/IndeterminateDrawable.java +++ b/material/java/com/google/android/material/progressindicator/IndeterminateDrawable.java @@ -23,7 +23,6 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; @@ -183,8 +182,6 @@ public int getIntrinsicHeight() { /** Draws the graphics based on the progress indicator's properties and the animation states. */ @Override public void draw(@NonNull Canvas canvas) { - Rect clipBounds = new Rect(); - if (getBounds().isEmpty() || !isVisible() || !canvas.getClipBounds(clipBounds)) { // Escape if bounds are empty, clip bounds are empty, or currently hidden. return; diff --git a/material/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java b/material/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java index a5d98a634..b03a5091b 100644 --- a/material/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java +++ b/material/java/com/google/android/material/progressindicator/LinearDrawingDelegate.java @@ -26,7 +26,6 @@ import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; @@ -63,6 +62,9 @@ final class LinearDrawingDelegate extends DrawingDelegate endPoints = new Pair<>(new PathPoint(), new PathPoint()); + /** Instantiates LinearDrawingDelegate with the current spec. */ LinearDrawingDelegate(@NonNull LinearProgressIndicatorSpec spec) { super(spec); @@ -255,7 +257,8 @@ private void drawLine( paint.setAntiAlias(true); paint.setStrokeWidth(displayedTrackThickness); - Pair endPoints = new Pair<>(new PathPoint(), new PathPoint()); + endPoints.first.reset(); + endPoints.second.reset(); endPoints.first.translate(startBlockCenterX + originX, 0); endPoints.second.translate(endBlockCenterX + originX, 0); if (startBlockCenterX >= endBlockCenterX) { @@ -280,14 +283,14 @@ private void drawLine( paint); } else { // Draws a portion of the cached wavy path. - endPoints = - getDisplayedPath( - activePathMeasure, - displayedActivePath, - startBlockCenterX / trackLength, - endBlockCenterX / trackLength, - amplitudeFraction, - phaseFraction); + calculateDisplayedPath( + activePathMeasure, + displayedActivePath, + endPoints, + startBlockCenterX / trackLength, + endBlockCenterX / trackLength, + amplitudeFraction, + phaseFraction); canvas.drawPath(displayedActivePath, paint); } if (!useStrokeCap && displayedCornerRadius > 0) { @@ -381,20 +384,20 @@ void invalidateCachedPaths() { } // Transforms the wavy path from y = -1/2 * cos(PI * x) + 1/2, as calculated above, // to y = cos(2 * PI * x / wavelength), as required in spec. - Matrix transformMatrix = new Matrix(); - transformMatrix.setScale(adjustedWavelength / 2, -2); - transformMatrix.postTranslate(0, 1); - cachedActivePath.transform(transformMatrix); + transform.reset(); + transform.setScale(adjustedWavelength / 2, -2); + transform.postTranslate(0, 1); + cachedActivePath.transform(transform); } else { cachedActivePath.lineTo(trackLength, 0); } activePathMeasure.setPath(cachedActivePath, /* forceNewPath= */ false); } - @NonNull - private Pair getDisplayedPath( + private void calculateDisplayedPath( @NonNull PathMeasure pathMeasure, @NonNull Path displayedPath, + @NonNull Pair endPoints, float start, float end, float amplitudeFraction, @@ -420,12 +423,14 @@ private Pair getDisplayedPath( float endDistance = end * pathMeasure.getLength(); pathMeasure.getSegment(startDistance, endDistance, displayedPath, true); // Gathers the position and tangent of the start and end. - PathPoint startPoint = new PathPoint(); + PathPoint startPoint = endPoints.first; + startPoint.reset(); pathMeasure.getPosTan(startDistance, startPoint.posVec, startPoint.tanVec); - PathPoint endPoint = new PathPoint(); + PathPoint endPoint = endPoints.second; + endPoint.reset(); pathMeasure.getPosTan(endDistance, endPoint.posVec, endPoint.tanVec); // Transforms the result path to match the canvas. - Matrix transform = new Matrix(); + transform.reset(); transform.setTranslate(resultTranslationX, 0); startPoint.translate(resultTranslationX, 0); endPoint.translate(resultTranslationX, 0); @@ -436,6 +441,5 @@ private Pair getDisplayedPath( endPoint.scale(1, scaleY); } displayedPath.transform(transform); - return new Pair<>(startPoint, endPoint); } } diff --git a/material/java/com/google/android/material/progressindicator/LinearIndeterminateContiguousAnimatorDelegate.java b/material/java/com/google/android/material/progressindicator/LinearIndeterminateContiguousAnimatorDelegate.java index 46f8e62e5..553e5dd56 100644 --- a/material/java/com/google/android/material/progressindicator/LinearIndeterminateContiguousAnimatorDelegate.java +++ b/material/java/com/google/android/material/progressindicator/LinearIndeterminateContiguousAnimatorDelegate.java @@ -72,7 +72,8 @@ private void maybeInitializeAnimators() { if (animator == null) { // Instantiates an animator with the linear interpolator to control the animation progress. animator = ObjectAnimator.ofFloat(this, ANIMATION_FRACTION, 0, 1); - animator.setDuration(DURATION_PER_CYCLE_IN_MS); + animator.setDuration( + (long) (DURATION_PER_CYCLE_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); animator.setInterpolator(null); animator.setRepeatCount(ValueAnimator.INFINITE); animator.addListener( @@ -88,6 +89,12 @@ public void onAnimationRepeat(Animator animation) { } } + private void updateAnimatorsDuration() { + maybeInitializeAnimators(); + animator.setDuration( + (long) (DURATION_PER_CYCLE_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); + } + @Override public void cancelAnimatorImmediately() { if (animator != null) { @@ -103,6 +110,7 @@ public void requestCancelAnimatorAfterCurrentCycle() { @Override public void invalidateSpecValues() { + updateAnimatorsDuration(); resetPropertiesForNewStart(); } diff --git a/material/java/com/google/android/material/progressindicator/LinearIndeterminateDisjointAnimatorDelegate.java b/material/java/com/google/android/material/progressindicator/LinearIndeterminateDisjointAnimatorDelegate.java index 7084d469f..b3ac98fb8 100644 --- a/material/java/com/google/android/material/progressindicator/LinearIndeterminateDisjointAnimatorDelegate.java +++ b/material/java/com/google/android/material/progressindicator/LinearIndeterminateDisjointAnimatorDelegate.java @@ -92,7 +92,8 @@ private void maybeInitializeAnimators() { if (animator == null) { // Instantiates an animator with the linear interpolator to control the animation progress. animator = ObjectAnimator.ofFloat(this, ANIMATION_FRACTION, 0, 1); - animator.setDuration(TOTAL_DURATION_IN_MS); + animator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); animator.setInterpolator(null); animator.setRepeatCount(ValueAnimator.INFINITE); animator.addListener( @@ -107,7 +108,8 @@ public void onAnimationRepeat(Animator animation) { } if (completeEndAnimator == null) { completeEndAnimator = ObjectAnimator.ofFloat(this, ANIMATION_FRACTION, 1); - completeEndAnimator.setDuration(TOTAL_DURATION_IN_MS); + completeEndAnimator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); completeEndAnimator.setInterpolator(null); completeEndAnimator.addListener( new AnimatorListenerAdapter() { @@ -123,6 +125,14 @@ public void onAnimationEnd(Animator animation) { } } + private void updateAnimatorsDuration() { + maybeInitializeAnimators(); + animator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); + completeEndAnimator.setDuration( + (long) (TOTAL_DURATION_IN_MS * baseSpec.indeterminateAnimatorDurationScale)); + } + @Override public void cancelAnimatorImmediately() { if (animator != null) { @@ -147,6 +157,7 @@ public void requestCancelAnimatorAfterCurrentCycle() { @Override public void invalidateSpecValues() { + updateAnimatorsDuration(); resetPropertiesForNewStart(); } diff --git a/material/java/com/google/android/material/progressindicator/res-public/values/public.xml b/material/java/com/google/android/material/progressindicator/res-public/values/public.xml index 0b2833987..7deb9e55c 100644 --- a/material/java/com/google/android/material/progressindicator/res-public/values/public.xml +++ b/material/java/com/google/android/material/progressindicator/res-public/values/public.xml @@ -31,6 +31,7 @@ + diff --git a/material/java/com/google/android/material/progressindicator/res/values/attrs.xml b/material/java/com/google/android/material/progressindicator/res/values/attrs.xml index 74bfb5eb6..69e1e364a 100644 --- a/material/java/com/google/android/material/progressindicator/res/values/attrs.xml +++ b/material/java/com/google/android/material/progressindicator/res/values/attrs.xml @@ -112,6 +112,13 @@ If positive, wave moves towards 100%; if negative, wave moves towards 0%. --> + + diff --git a/material/java/com/google/android/material/ripple/RippleDrawableCompat.java b/material/java/com/google/android/material/ripple/RippleDrawableCompat.java index 2c66b02c2..7a1e6bc19 100644 --- a/material/java/com/google/android/material/ripple/RippleDrawableCompat.java +++ b/material/java/com/google/android/material/ripple/RippleDrawableCompat.java @@ -185,8 +185,7 @@ public int getOpacity() { } /** - * A {@link ConstantState} for {@link Ripple} - * + * A {@link ConstantState} for {@link RippleDrawableCompat} */ static final class RippleDrawableCompatState extends ConstantState { diff --git a/material/java/com/google/android/material/search/res/values/styles.xml b/material/java/com/google/android/material/search/res/values/styles.xml index 72d92800e..3c6c85ec8 100644 --- a/material/java/com/google/android/material/search/res/values/styles.xml +++ b/material/java/com/google/android/material/search/res/values/styles.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + @@ -45,7 +45,7 @@ explicitly on the modal side sheet View. --> @dimen/m3_side_sheet_width match_parent - @dimen/m3_side_sheet_modal_elevation + @dimen/m3_side_sheet_modal_elevation @macro/m3_comp_sheet_side_docked_modal_container_color @macro/m3_comp_sheet_side_docked_modal_container_shape diff --git a/material/java/com/google/android/material/slider/BaseSlider.java b/material/java/com/google/android/material/slider/BaseSlider.java index c37a271d9..b89c37438 100644 --- a/material/java/com/google/android/material/slider/BaseSlider.java +++ b/material/java/com/google/android/material/slider/BaseSlider.java @@ -27,6 +27,8 @@ import static com.google.android.material.slider.LabelFormatter.LABEL_GONE; import static com.google.android.material.slider.LabelFormatter.LABEL_VISIBLE; import static com.google.android.material.slider.LabelFormatter.LABEL_WITHIN_BOUNDS; +import static com.google.android.material.slider.SliderOrientation.HORIZONTAL; +import static com.google.android.material.slider.SliderOrientation.VERTICAL; import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap; import static java.lang.Float.compare; import static java.lang.Math.abs; @@ -38,12 +40,12 @@ import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; @@ -101,7 +103,6 @@ import com.google.android.material.shape.MaterialShapeDrawable; import com.google.android.material.shape.ShapeAppearanceModel; import com.google.android.material.tooltip.TooltipDrawable; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.math.BigDecimal; @@ -191,6 +192,7 @@ * * * @attr ref com.google.android.material.R.styleable#Slider_android_enabled + * @attr ref com.google.android.material.R.styleable#Slider_android_orientation * @attr ref com.google.android.material.R.styleable#Slider_android_stepSize * @attr ref com.google.android.material.R.styleable#Slider_android_valueFrom * @attr ref com.google.android.material.R.styleable#Slider_android_valueTo @@ -212,12 +214,13 @@ * @attr ref com.google.android.material.R.styleable#Slider_trackColorActive * @attr ref com.google.android.material.R.styleable#Slider_trackColorInactive * @attr ref com.google.android.material.R.styleable#Slider_trackHeight - * @attr ref com.google.android.material.R.styleable#Slider_trackIconActive - * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveSize + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveStart + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveEnd * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveColor - * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactive - * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveSize + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveStart + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveEnd * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveColor + * @attr ref com.google.android.material.R.styleable#Slider_trackIconSize * @attr ref com.google.android.material.R.styleable#Slider_trackCornerSize * @attr ref com.google.android.material.R.styleable#Slider_trackInsideCornerSize * @attr ref com.google.android.material.R.styleable#Slider_trackStopIndicatorSize @@ -304,17 +307,18 @@ abstract class BaseSlider< private int minTrackSidePadding; private int defaultThumbRadius; - private int defaultTrackHeight; + private int defaultTrackThickness; private int defaultTickActiveRadius; private int defaultTickInactiveRadius; private int minTickSpacing; @Px private int minTouchTargetSize; - private int minWidgetHeight; - private int widgetHeight; + @Orientation private int widgetOrientation; + private int minWidgetThickness; + private int widgetThickness; private int labelBehavior; - private int trackHeight; + private int trackThickness; private int trackSidePadding; private int thumbWidth; private int thumbHeight; @@ -325,12 +329,13 @@ abstract class BaseSlider< private int trackStopIndicatorSize; private int trackCornerSize; private int trackInsideCornerSize; - private Drawable trackIconActive; - @Px private int trackIconActiveSize; - private ColorStateList trackIconActiveColor; - private Drawable trackIconInactive; - @Px private int trackIconInactiveSize; - private ColorStateList trackIconInactiveColor; + @Nullable private Drawable trackIconActiveStart; + @Nullable private Drawable trackIconActiveEnd; + @Nullable private ColorStateList trackIconActiveColor; + @Nullable private Drawable trackIconInactiveStart; + @Nullable private Drawable trackIconInactiveEnd; + @Nullable private ColorStateList trackIconInactiveColor; + @Px private int trackIconSize; private int labelPadding; private float touchDownX; private MotionEvent lastEvent; @@ -365,6 +370,10 @@ abstract class BaseSlider< @NonNull private final RectF activeTrackRect = new RectF(); @NonNull private final RectF inactiveTrackRect = new RectF(); @NonNull private final RectF cornerRect = new RectF(); + @NonNull private final Rect labelRect = new Rect(); + @NonNull private final RectF iconRectF = new RectF(); + @NonNull private final Rect iconRect = new Rect(); + @NonNull private final Matrix rotationMatrix = new Matrix(); @NonNull private final MaterialShapeDrawable defaultThumbDrawable = new MaterialShapeDrawable(); @Nullable private Drawable customThumbDrawable; @NonNull private List customThumbDrawablesForValues = Collections.emptyList(); @@ -412,6 +421,10 @@ abstract class BaseSlider< @Retention(RetentionPolicy.SOURCE) @interface SeparationUnit {} + @IntDef({HORIZONTAL, VERTICAL}) + @Retention(RetentionPolicy.SOURCE) + public @interface Orientation {} + public BaseSlider(@NonNull Context context) { this(context, null); } @@ -478,13 +491,13 @@ public BaseSlider( } private void loadResources(@NonNull Resources resources) { - minWidgetHeight = resources.getDimensionPixelSize(R.dimen.mtrl_slider_widget_height); + minWidgetThickness = resources.getDimensionPixelSize(R.dimen.mtrl_slider_widget_height); minTrackSidePadding = resources.getDimensionPixelOffset(R.dimen.mtrl_slider_track_side_padding); trackSidePadding = minTrackSidePadding; defaultThumbRadius = resources.getDimensionPixelSize(R.dimen.mtrl_slider_thumb_radius); - defaultTrackHeight = resources.getDimensionPixelSize(R.dimen.mtrl_slider_track_height); + defaultTrackThickness = resources.getDimensionPixelSize(R.dimen.mtrl_slider_track_height); defaultTickActiveRadius = resources.getDimensionPixelSize(R.dimen.mtrl_slider_tick_radius); defaultTickInactiveRadius = resources.getDimensionPixelSize(R.dimen.mtrl_slider_tick_radius); @@ -498,6 +511,8 @@ private void processAttributes(Context context, AttributeSet attrs, int defStyle ThemeEnforcement.obtainStyledAttributes( context, attrs, R.styleable.Slider, defStyleAttr, DEF_STYLE_RES); + setOrientation(a.getInt(R.styleable.Slider_android_orientation, HORIZONTAL)); + labelStyle = a.getResourceId(R.styleable.Slider_labelStyle, R.style.Widget_MaterialComponents_Tooltip); @@ -578,16 +593,19 @@ private void processAttributes(Context context, AttributeSet attrs, int defStyle setTrackCornerSize( a.getDimensionPixelSize(R.styleable.Slider_trackCornerSize, TRACK_CORNER_SIZE_UNSET)); setTrackInsideCornerSize(a.getDimensionPixelSize(R.styleable.Slider_trackInsideCornerSize, 0)); - setTrackIconActive( - MaterialResources.getDrawable(context, a, R.styleable.Slider_trackIconActive)); - setTrackIconActiveSize(a.getDimensionPixelSize(R.styleable.Slider_trackIconActiveSize, 0)); + setTrackIconActiveStart( + MaterialResources.getDrawable(context, a, R.styleable.Slider_trackIconActiveStart)); + setTrackIconActiveEnd( + MaterialResources.getDrawable(context, a, R.styleable.Slider_trackIconActiveEnd)); setTrackIconActiveColor( MaterialResources.getColorStateList(context, a, R.styleable.Slider_trackIconActiveColor)); - setTrackIconInactive( - MaterialResources.getDrawable(context, a, R.styleable.Slider_trackIconInactive)); - setTrackIconInactiveSize(a.getDimensionPixelSize(R.styleable.Slider_trackIconInactiveSize, 0)); + setTrackIconInactiveStart( + MaterialResources.getDrawable(context, a, R.styleable.Slider_trackIconInactiveStart)); + setTrackIconInactiveEnd( + MaterialResources.getDrawable(context, a, R.styleable.Slider_trackIconInactiveEnd)); setTrackIconInactiveColor( MaterialResources.getColorStateList(context, a, R.styleable.Slider_trackIconInactiveColor)); + setTrackIconSize(a.getDimensionPixelSize(R.styleable.Slider_trackIconSize, 0)); int radius = a.getDimensionPixelSize(R.styleable.Slider_thumbRadius, 0); int thumbWidth = a.getDimensionPixelSize(R.styleable.Slider_thumbWidth, radius * 2); @@ -616,7 +634,7 @@ private void processAttributes(Context context, AttributeSet attrs, int defStyle private boolean maybeIncreaseTrackSidePadding() { int increasedSidePaddingByThumb = max(thumbWidth / 2 - defaultThumbRadius, 0); - int increasedSidePaddingByTrack = max((trackHeight - defaultTrackHeight) / 2, 0); + int increasedSidePaddingByTrack = max((trackThickness - defaultTrackThickness) / 2, 0); int increasedSidePaddingByActiveTick = max(tickActiveRadius - defaultTickActiveRadius, 0); int increasedSidePaddingByInactiveTick = max(tickInactiveRadius - defaultTickInactiveRadius, 0); int newTrackSidePadding = @@ -630,7 +648,7 @@ private boolean maybeIncreaseTrackSidePadding() { } trackSidePadding = newTrackSidePadding; if (isLaidOut()) { - updateTrackWidth(getWidth()); + updateTrackWidth(isVertical() ? getHeight() : getWidth()); } return true; } @@ -1236,7 +1254,7 @@ public void setThumbWidth(@IntRange(from = 0) @Px int width) { adjustCustomThumbDrawableBounds(customDrawable); } - updateWidgetLayout(); + updateWidgetLayout(false); } /** @@ -1292,7 +1310,7 @@ public void setThumbHeight(@IntRange(from = 0) @Px int height) { adjustCustomThumbDrawableBounds(customDrawable); } - updateWidgetLayout(); + updateWidgetLayout(false); } /** @@ -1455,7 +1473,7 @@ public int getLabelBehavior() { public void setLabelBehavior(@LabelBehavior int labelBehavior) { if (this.labelBehavior != labelBehavior) { this.labelBehavior = labelBehavior; - requestLayout(); + updateWidgetLayout(true); } } @@ -1489,7 +1507,7 @@ public int getTrackWidth() { */ @Px public int getTrackHeight() { - return trackHeight; + return trackThickness; } /** @@ -1499,10 +1517,10 @@ public int getTrackHeight() { * @attr ref com.google.android.material.R.styleable#Slider_trackHeight */ public void setTrackHeight(@IntRange(from = 0) @Px int trackHeight) { - if (this.trackHeight != trackHeight) { - this.trackHeight = trackHeight; + if (this.trackThickness != trackHeight) { + this.trackThickness = trackHeight; invalidateTrack(); - updateWidgetLayout(); + updateWidgetLayout(false); } } @@ -1527,7 +1545,7 @@ public void setTickActiveRadius(@IntRange(from = 0) @Px int tickActiveRadius) { if (this.tickActiveRadius != tickActiveRadius) { this.tickActiveRadius = tickActiveRadius; activeTicksPaint.setStrokeWidth(tickActiveRadius * 2); - updateWidgetLayout(); + updateWidgetLayout(false); } } @@ -1552,34 +1570,48 @@ public void setTickInactiveRadius(@IntRange(from = 0) @Px int tickInactiveRadius if (this.tickInactiveRadius != tickInactiveRadius) { this.tickInactiveRadius = tickInactiveRadius; inactiveTicksPaint.setStrokeWidth(tickInactiveRadius * 2); - updateWidgetLayout(); + updateWidgetLayout(false); } } - private void updateWidgetLayout() { - boolean sizeChanged = maybeIncreaseWidgetHeight(); + private void updateWidgetLayout(boolean forceRefresh) { + boolean sizeChanged = maybeIncreaseWidgetThickness(); boolean sidePaddingChanged = maybeIncreaseTrackSidePadding(); - if (sizeChanged) { + if (isVertical()) { + updateRotationMatrix(); + } + if (sizeChanged || forceRefresh) { requestLayout(); } else if (sidePaddingChanged) { postInvalidate(); } } - private boolean maybeIncreaseWidgetHeight() { - int topAndBottomPaddings = getPaddingTop() + getPaddingBottom(); - int minHeightRequiredByTrack = trackHeight + topAndBottomPaddings; - int minHeightRequiredByThumb = thumbHeight + getPaddingTop() + getPaddingBottom(); + private boolean maybeIncreaseWidgetThickness() { + int paddings; + if (isVertical()) { + paddings = getPaddingLeft() + getPaddingRight(); + } else { + paddings = getPaddingTop() + getPaddingBottom(); + } + int minHeightRequiredByTrack = trackThickness + paddings; + int minHeightRequiredByThumb = thumbHeight + paddings; int newWidgetHeight = - max(minWidgetHeight, max(minHeightRequiredByTrack, minHeightRequiredByThumb)); - if (newWidgetHeight == widgetHeight) { + max(minWidgetThickness, max(minHeightRequiredByTrack, minHeightRequiredByThumb)); + if (newWidgetHeight == widgetThickness) { return false; } - widgetHeight = newWidgetHeight; + widgetThickness = newWidgetHeight; return true; } + private void updateRotationMatrix() { + float pivot = calculateTrackCenter(); + rotationMatrix.reset(); + rotationMatrix.setRotate(90, pivot, pivot); + } + /** * Returns the color of the halo. * @@ -1907,7 +1939,7 @@ public void setTrackStopIndicatorSize(@Px int trackStopIndicatorSize) { @Px public int getTrackCornerSize() { if (trackCornerSize == TRACK_CORNER_SIZE_UNSET) { - return trackHeight / 2; // full rounded corners by default when unset + return trackThickness / 2; // full rounded corners by default when unset } return trackCornerSize; } @@ -1951,73 +1983,119 @@ public void setTrackInsideCornerSize(@Px int cornerSize) { } /** - * Sets the active track icon. + * Sets the active track start icon. + * + * @param icon Drawable to use for the active track's start icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveStart + * @see #setTrackIconActiveStart(int) + * @see #getTrackIconActiveStart() + */ + public void setTrackIconActiveStart(@Nullable Drawable icon) { + if (this.trackIconActiveStart == icon) { + return; + } + this.trackIconActiveStart = icon; + invalidate(); + } + + /** + * Sets the active track start icon. + * + * @param iconResourceId Drawable resource ID to use for the active track's start icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveStart + * @see #setTrackIconActiveStart(Drawable) + * @see #getTrackIconActiveStart() + */ + public void setTrackIconActiveStart(@DrawableRes int iconResourceId) { + Drawable icon = null; + if (iconResourceId != 0) { + icon = AppCompatResources.getDrawable(getContext(), iconResourceId); + } + setTrackIconActiveStart(icon); + } + + /** + * Gets the active track start icon shown, if present. + * + * @return Start icon shown for this active track, if present. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveStart + * @see #setTrackIconActiveStart(Drawable) + * @see #setTrackIconActiveStart(int) + */ + @Nullable + public Drawable getTrackIconActiveStart() { + return trackIconActiveStart; + } + + /** + * Sets the active track end icon. * - * @param icon Drawable to use for the active track's icon. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconActive - * @see #setTrackIconActiveResource(int) - * @see #getTrackIconActive() + * @param icon Drawable to use for the active track's end icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveEnd + * @see #setTrackIconActiveEnd(int) + * @see #getTrackIconActiveEnd() */ - public void setTrackIconActive(@Nullable Drawable icon) { - if (this.trackIconActive == icon) { + public void setTrackIconActiveEnd(@Nullable Drawable icon) { + if (this.trackIconActiveEnd == icon) { return; } - this.trackIconActive = icon; + this.trackIconActiveEnd = icon; invalidate(); } /** - * Sets the active track icon. + * Sets the active track end icon. * - * @param iconResourceId Drawable resource ID to use for the active track's icon. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconActive - * @see #setTrackIconActive(Drawable) - * @see #getTrackIconActive() + * @param iconResourceId Drawable resource ID to use for the active track's end icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveEnd + * @see #setTrackIconActiveEnd(Drawable) + * @see #getTrackIconActiveEnd() */ - public void setTrackIconActiveResource(@DrawableRes int iconResourceId) { + public void setTrackIconActiveEnd(@DrawableRes int iconResourceId) { Drawable icon = null; if (iconResourceId != 0) { icon = AppCompatResources.getDrawable(getContext(), iconResourceId); } - setTrackIconActive(icon); + setTrackIconActiveEnd(icon); } /** - * Gets the active track icon shown, if present. + * Gets the active track end icon shown, if present. * - * @return Icon shown for this active track, if present. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconActive - * @see #setTrackIconActive(Drawable) - * @see #setTrackIconActiveResource(int) + * @return End icon shown for this active track, if present. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveEnd + * @see #setTrackIconActiveEnd(Drawable) + * @see #setTrackIconActiveEnd(int) */ - public Drawable getTrackIconActive() { - return trackIconActive; + @Nullable + public Drawable getTrackIconActiveEnd() { + return trackIconActiveEnd; } /** - * Sets the active track icon size. + * Sets the track icons size. * - * @param size size to use for the active track's icon. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveSize - * @see #getTrackIconActiveSize() + * @param size size to use for the track icons. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconSize + * @see #getTrackIconSize() */ - public void setTrackIconActiveSize(@Px int size) { - if (this.trackIconActiveSize == size) { + public void setTrackIconSize(@Px int size) { + if (this.trackIconSize == size) { return; } - this.trackIconActiveSize = size; + this.trackIconSize = size; invalidate(); } /** - * Gets the active track icon size shown, if present. + * Gets the track icons size shown, if present. * - * @return Size of the icon shown for this active track, if present. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveSize - * @see #setTrackIconActiveSize(int) + * @return Size of the icons shown for this track, if present. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconSize + * @see #setTrackIconSize(int) */ - public int getTrackIconActiveSize() { - return trackIconActiveSize; + public int getTrackIconSize() { + return trackIconSize; } /** @@ -2027,7 +2105,7 @@ public int getTrackIconActiveSize() { * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveColor * @see #getTrackIconActiveColor() */ - public void setTrackIconActiveColor(ColorStateList color) { + public void setTrackIconActiveColor(@Nullable ColorStateList color) { if (this.trackIconActiveColor == color) { return; } @@ -2042,78 +2120,99 @@ public void setTrackIconActiveColor(ColorStateList color) { * @attr ref com.google.android.material.R.styleable#Slider_trackIconActiveColor * @see #setTrackIconActiveColor(ColorStateList) */ + @Nullable public ColorStateList getTrackIconActiveColor() { return trackIconActiveColor; } /** - * Sets the inactive track icon. + * Sets the inactive track start icon. * - * @param icon Drawable to use for the inactive track's icon. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactive - * @see #setTrackIconInactiveResource(int) - * @see #getTrackIconInactive() + * @param icon Drawable to use for the inactive track's start icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveStart + * @see #setTrackIconInactiveStart(int) + * @see #getTrackIconInactiveStart() */ - public void setTrackIconInactive(@Nullable Drawable icon) { - if (this.trackIconInactive == icon) { + public void setTrackIconInactiveStart(@Nullable Drawable icon) { + if (this.trackIconInactiveStart == icon) { return; } - this.trackIconInactive = icon; + this.trackIconInactiveStart = icon; invalidate(); } /** - * Sets the inactive track icon. + * Sets the inactive track start icon. * - * @param iconResourceId Drawable resource ID to use for the inactive track's icon. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactive - * @see #setTrackIconInactive(Drawable) - * @see #getTrackIconInactive() + * @param iconResourceId Drawable resource ID to use for the inactive track's start icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveStart + * @see #setTrackIconInactiveStart(Drawable) + * @see #getTrackIconInactiveStart() */ - public void setTrackIconInactiveResource(@DrawableRes int iconResourceId) { + public void setTrackIconInactiveStart(@DrawableRes int iconResourceId) { Drawable icon = null; if (iconResourceId != 0) { icon = AppCompatResources.getDrawable(getContext(), iconResourceId); } - setTrackIconInactive(icon); + setTrackIconInactiveStart(icon); } /** - * Gets the inactive track icon shown, if present. + * Gets the inactive track start icon shown, if present. * - * @return Icon shown for this inactive track, if present. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactive - * @see #setTrackIconInactive(Drawable) - * @see #setTrackIconInactiveResource(int) + * @return Start icon shown for this inactive track, if present. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveStart + * @see #setTrackIconInactiveStart(Drawable) + * @see #setTrackIconInactiveStart(int) */ - public Drawable getTrackIconInactive() { - return trackIconInactive; + @Nullable + public Drawable getTrackIconInactiveStart() { + return trackIconInactiveStart; } /** - * Sets the inactive track icon size. + * Sets the inactive track end icon. * - * @param size size to use for the inactive track's icon. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveSize - * @see #getTrackIconInactiveSize() + * @param icon Drawable to use for the inactive track's end icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveEnd + * @see #setTrackIconInactiveEnd(int) + * @see #getTrackIconInactiveEnd() */ - public void setTrackIconInactiveSize(@Px int size) { - if (this.trackIconInactiveSize == size) { + public void setTrackIconInactiveEnd(@Nullable Drawable icon) { + if (this.trackIconInactiveEnd == icon) { return; } - this.trackIconInactiveSize = size; + this.trackIconInactiveEnd = icon; invalidate(); } /** - * Gets the inactive track icon size shown, if present. + * Sets the inactive track end icon. + * + * @param iconResourceId Drawable resource ID to use for the inactive track's end icon. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveEnd + * @see #setTrackIconInactiveEnd(Drawable) + * @see #getTrackIconInactiveEnd() + */ + public void setTrackIconInactiveEnd(@DrawableRes int iconResourceId) { + Drawable icon = null; + if (iconResourceId != 0) { + icon = AppCompatResources.getDrawable(getContext(), iconResourceId); + } + setTrackIconInactiveEnd(icon); + } + + /** + * Gets the inactive track end icon shown, if present. * - * @return Size of the icon shown for this inactive track, if present. - * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveSize - * @see #setTrackIconInactiveSize(int) + * @return End icon shown for this inactive track, if present. + * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveEnd + * @see #setTrackIconInactiveEnd(Drawable) + * @see #setTrackIconInactiveEnd(int) */ - public int getTrackIconInactiveSize() { - return trackIconInactiveSize; + @Nullable + public Drawable getTrackIconInactiveEnd() { + return trackIconInactiveEnd; } /** @@ -2123,7 +2222,7 @@ public int getTrackIconInactiveSize() { * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveColor * @see #getTrackIconInactiveColor() */ - public void setTrackIconInactiveColor(ColorStateList color) { + public void setTrackIconInactiveColor(@Nullable ColorStateList color) { if (this.trackIconInactiveColor == color) { return; } @@ -2138,6 +2237,7 @@ public void setTrackIconInactiveColor(ColorStateList color) { * @attr ref com.google.android.material.R.styleable#Slider_trackIconInactiveColor * @see #setTrackIconInactiveColor(ColorStateList) */ + @Nullable public ColorStateList getTrackIconInactiveColor() { return trackIconInactiveColor; } @@ -2166,6 +2266,14 @@ public void setEnabled(boolean enabled) { setLayerType(enabled ? LAYER_TYPE_NONE : LAYER_TYPE_HARDWARE, null); } + public void setOrientation(@Orientation int orientation) { + if (this.widgetOrientation == orientation) { + return; + } + this.widgetOrientation = orientation; + updateWidgetLayout(true); + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -2210,19 +2318,21 @@ private void detachLabelFromContentView(TooltipDrawable label) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure( - widthMeasureSpec, - MeasureSpec.makeMeasureSpec( - widgetHeight - + (labelBehavior == LABEL_WITHIN_BOUNDS || shouldAlwaysShowLabel() - ? labels.get(0).getIntrinsicHeight() - : 0), - MeasureSpec.EXACTLY)); + int labelSize = 0; + if (labelBehavior == LABEL_WITHIN_BOUNDS || shouldAlwaysShowLabel()) { + labelSize = labels.get(0).getIntrinsicHeight(); + } + int spec = MeasureSpec.makeMeasureSpec(widgetThickness + labelSize, MeasureSpec.EXACTLY); + if (isVertical()) { + super.onMeasure(spec, heightMeasureSpec); + } else { + super.onMeasure(widthMeasureSpec, spec); + } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { - updateTrackWidth(w); + updateTrackWidth(isVertical() ? h : w); updateHaloHotspot(); } @@ -2245,6 +2355,10 @@ private void maybeCalculateTicksCoordinates() { ticksCoordinates[i] = trackSidePadding + i / 2f * interval; ticksCoordinates[i + 1] = calculateTrackCenter(); } + + if (isVertical()) { + rotationMatrix.mapPoints(ticksCoordinates); + } } private void updateTrackWidth(int width) { @@ -2260,16 +2374,24 @@ private void updateHaloHotspot() { if (!shouldDrawCompatHalo() && getMeasuredWidth() > 0) { final Drawable background = getBackground(); if (background instanceof RippleDrawable) { - int x = (int) (normalizeValue(values.get(focusedThumbIdx)) * trackWidth + trackSidePadding); + float x = normalizeValue(values.get(focusedThumbIdx)) * trackWidth + trackSidePadding; int y = calculateTrackCenter(); + float[] haloBounds = {x - haloRadius, y - haloRadius, x + haloRadius, y + haloRadius}; + if (isVertical()) { + rotationMatrix.mapPoints(haloBounds); + } DrawableCompat.setHotspotBounds( - background, x - haloRadius, y - haloRadius, x + haloRadius, y + haloRadius); + background, + (int) haloBounds[0], + (int) haloBounds[1], + (int) haloBounds[2], + (int) haloBounds[3]); } } } private int calculateTrackCenter() { - return widgetHeight / 2 + return widgetThickness / 2 + (labelBehavior == LABEL_WITHIN_BOUNDS || shouldAlwaysShowLabel() ? labels.get(0).getIntrinsicHeight() : 0); @@ -2321,30 +2443,37 @@ private float[] getActiveRange() { float right = normalizeValue(max); // In RTL we draw things in reverse, so swap the left and right range values - return isRtl() ? new float[] {right, left} : new float[] {left, right}; + return isRtl() || isVertical() ? new float[] {right, left} : new float[] {left, right}; } private void drawInactiveTrack(@NonNull Canvas canvas, int width, int yCenter) { + int trackCornerSize = getTrackCornerSize(); float[] activeRange = getActiveRange(); float right = trackSidePadding + activeRange[1] * width; if (right < trackSidePadding + width) { inactiveTrackRect.set( right + thumbTrackGapSize, - yCenter - trackHeight / 2f, - trackSidePadding + width + getTrackCornerSize(), - yCenter + trackHeight / 2f); - updateTrack(canvas, inactiveTrackPaint, inactiveTrackRect, FullCornerDirection.RIGHT); + yCenter - trackThickness / 2f, + trackSidePadding + width + trackCornerSize, + yCenter + trackThickness / 2f); + updateTrack( + canvas, + inactiveTrackPaint, + inactiveTrackRect, + trackCornerSize, + FullCornerDirection.RIGHT); } // Also draw inactive track to the left if there is any float left = trackSidePadding + activeRange[0] * width; if (left > trackSidePadding) { inactiveTrackRect.set( - trackSidePadding - getTrackCornerSize(), - yCenter - trackHeight / 2f, + trackSidePadding - trackCornerSize, + yCenter - trackThickness / 2f, left - thumbTrackGapSize, - yCenter + trackHeight / 2f); - updateTrack(canvas, inactiveTrackPaint, inactiveTrackRect, FullCornerDirection.LEFT); + yCenter + trackThickness / 2f); + updateTrack( + canvas, inactiveTrackPaint, inactiveTrackRect, trackCornerSize, FullCornerDirection.LEFT); } } @@ -2354,7 +2483,7 @@ private void drawInactiveTrack(@NonNull Canvas canvas, int width, int yCenter) { */ private float normalizeValue(float value) { float normalized = (value - valueFrom) / (valueTo - valueFrom); - if (isRtl()) { + if (isRtl() || isVertical()) { return 1 - normalized; } return normalized; @@ -2367,7 +2496,7 @@ private void drawActiveTrack(@NonNull Canvas canvas, int width, int yCenter) { FullCornerDirection direction = FullCornerDirection.NONE; if (values.size() == 1) { // Only 1 thumb - direction = isRtl() ? FullCornerDirection.RIGHT : FullCornerDirection.LEFT; + direction = isRtl() || isVertical() ? FullCornerDirection.RIGHT : FullCornerDirection.LEFT; } for (int i = 0; i < values.size(); i++) { @@ -2376,25 +2505,26 @@ private void drawActiveTrack(@NonNull Canvas canvas, int width, int yCenter) { left = valueToX(values.get(i - 1)); } right = valueToX(values.get(i)); - if (isRtl()) { // Swap left right + if (isRtl() || isVertical()) { // Swap left right float temp = left; left = right; right = temp; } } + int trackCornerSize = getTrackCornerSize(); switch (direction) { case NONE: left += thumbTrackGapSize; right -= thumbTrackGapSize; break; case LEFT: - left -= getTrackCornerSize(); + left -= trackCornerSize; right -= thumbTrackGapSize; break; case RIGHT: left += thumbTrackGapSize; - right += getTrackCornerSize(); + right += trackCornerSize; break; default: // fall through @@ -2405,9 +2535,34 @@ private void drawActiveTrack(@NonNull Canvas canvas, int width, int yCenter) { continue; } - activeTrackRect.set(left, yCenter - trackHeight / 2f, right, yCenter + trackHeight / 2f); - updateTrack(canvas, activeTrackPaint, activeTrackRect, direction); + activeTrackRect.set( + left, yCenter - trackThickness / 2f, right, yCenter + trackThickness / 2f); + updateTrack(canvas, activeTrackPaint, activeTrackRect, trackCornerSize, direction); + } + } + + private float calculateStartTrackCornerSize(float trackCornerSize) { + if (values.isEmpty() || !hasGapBetweenThumbAndTrack()) { + return trackCornerSize; + } + int firstIdx = isRtl() || isVertical() ? values.size() - 1 : 0; + float currentX = valueToX(values.get(firstIdx)) - trackSidePadding; + if (currentX < trackCornerSize) { + return max(currentX, trackInsideCornerSize); + } + return trackCornerSize; + } + + private float calculateEndTrackCornerSize(float trackCornerSize) { + if (values.isEmpty() || !hasGapBetweenThumbAndTrack()) { + return trackCornerSize; } + int lastIdx = isRtl() || isVertical() ? 0 : values.size() - 1; + float currentX = valueToX(values.get(lastIdx)) - trackSidePadding; + if (currentX > trackWidth - trackCornerSize) { + return max(trackWidth - currentX, trackInsideCornerSize); + } + return trackCornerSize; } private void drawTrackIcons( @@ -2418,52 +2573,69 @@ private void drawTrackIcons( Log.w(TAG, "Track icons can only be used when only 1 thumb is present."); } - // draw active track icon - if (trackIconActive != null - && drawTrackIcon( - canvas, - activeTrackBounds, - trackIconActive, - trackIconActiveSize, - trackIconActiveColor)) { - return; - } - // draw inactive track icon if active not drawn - if (trackIconInactive != null) { - drawTrackIcon( - canvas, - inactiveTrackBounds, - trackIconInactive, - trackIconInactiveSize, - trackIconInactiveColor); - } + // draw track start icons + calculateBoundsAndDrawTrackIcon( + canvas, activeTrackBounds, trackIconActiveStart, trackIconActiveColor, true); + calculateBoundsAndDrawTrackIcon( + canvas, inactiveTrackBounds, trackIconInactiveStart, trackIconInactiveColor, true); + // draw track end icons + calculateBoundsAndDrawTrackIcon( + canvas, activeTrackBounds, trackIconActiveEnd, trackIconActiveColor, false); + calculateBoundsAndDrawTrackIcon( + canvas, inactiveTrackBounds, trackIconInactiveEnd, trackIconInactiveColor, false); } - @CanIgnoreReturnValue - private boolean drawTrackIcon( + private void calculateBoundsAndDrawTrackIcon( @NonNull Canvas canvas, @NonNull RectF trackBounds, - @NonNull Drawable drawable, - @Px int size, - ColorStateList color) { - Rect iconBounds = calculateTrackIconBounds(trackBounds, size); - if (trackBounds.left > iconBounds.left || trackBounds.right < iconBounds.right) { - // not enough space to draw icon - return false; + @Nullable Drawable icon, + @Nullable ColorStateList iconColor, + boolean isStart) { + if (icon != null) { + calculateTrackIconBounds(trackBounds, iconRectF, trackIconSize, isStart); + if (!iconRectF.isEmpty()) { + drawTrackIcon(canvas, iconRectF, icon, iconColor); + } } - DrawableCompat.setTintList(drawable, color); - drawable.setBounds(iconBounds); - drawable.draw(canvas); - return true; } - private Rect calculateTrackIconBounds(@NonNull RectF trackBounds, @Px int iconSize) { + private void drawTrackIcon( + @NonNull Canvas canvas, + @NonNull RectF iconBounds, + @NonNull Drawable icon, + @Nullable ColorStateList color) { + DrawableCompat.setTintList(icon, color); + if (isVertical()) { + rotationMatrix.mapRect(iconBounds); + } + iconBounds.round(iconRect); + icon.setBounds(iconRect); + icon.draw(canvas); + } + + private void calculateTrackIconBounds( + @NonNull RectF trackBounds, @NonNull RectF iconBounds, @Px int iconSize, boolean isStart) { float iconPadding = getResources().getDimension(R.dimen.m3_slider_track_icon_padding); - float iconLeft = - !isRtl() ? trackBounds.left + iconPadding : trackBounds.right - iconSize - iconPadding; + float iconLeft; + if (isStart) { + iconLeft = + isRtl() || isVertical() + ? trackBounds.right - iconSize - iconPadding + : trackBounds.left + iconPadding; + } else { + iconLeft = + isRtl() || isVertical() + ? trackBounds.left + iconPadding + : trackBounds.right - iconSize - iconPadding; + } float iconRight = iconLeft + iconSize; int iconTop = calculateTrackCenter() - iconSize / 2; - return new Rect((int) iconLeft, iconTop, (int) iconRight, iconTop + iconSize); + if (trackBounds.left > iconLeft - iconPadding || trackBounds.right < iconRight + iconPadding) { + // not enough space to draw icon + iconBounds.setEmpty(); + return; + } + iconBounds.set(iconLeft, iconTop, iconRight, iconTop + iconSize); } private boolean hasGapBetweenThumbAndTrack() { @@ -2479,9 +2651,9 @@ private enum FullCornerDirection { } private void updateTrack( - Canvas canvas, Paint paint, RectF bounds, FullCornerDirection direction) { - float leftCornerSize = getTrackCornerSize(); - float rightCornerSize = getTrackCornerSize(); + Canvas canvas, Paint paint, RectF bounds, float cornerSize, FullCornerDirection direction) { + float leftCornerSize = calculateStartTrackCornerSize(cornerSize); + float rightCornerSize = calculateEndTrackCornerSize(cornerSize); switch (direction) { case BOTH: break; @@ -2504,11 +2676,16 @@ private void updateTrack( paint.setAntiAlias(true); } + RectF rotated = new RectF(bounds); + if (isVertical()) { + rotationMatrix.mapRect(rotated); + } // Draws track path with rounded corners. trackPath.reset(); if (bounds.width() >= leftCornerSize + rightCornerSize) { // Fills one rounded rectangle. - trackPath.addRoundRect(bounds, getCornerRadii(leftCornerSize, rightCornerSize), Direction.CW); + trackPath.addRoundRect( + rotated, getCornerRadii(leftCornerSize, rightCornerSize), Direction.CW); canvas.drawPath(trackPath, paint); } else { // Clips the canvas and draws the fully rounded track. @@ -2516,7 +2693,7 @@ private void updateTrack( float maxCornerSize = max(leftCornerSize, rightCornerSize); canvas.save(); // Clips the canvas using the current bounds with the smaller corner size. - trackPath.addRoundRect(bounds, minCornerSize, minCornerSize, Direction.CW); + trackPath.addRoundRect(rotated, minCornerSize, minCornerSize, Direction.CW); canvas.clipPath(trackPath); // Then draws a rectangle with the minimum width for full corners. switch (direction) { @@ -2533,18 +2710,27 @@ private void updateTrack( bounds.centerX() + maxCornerSize, bounds.bottom); } + if (isVertical()) { + rotationMatrix.mapRect(cornerRect); + } canvas.drawRoundRect(cornerRect, maxCornerSize, maxCornerSize, paint); canvas.restore(); } } private float[] getCornerRadii(float leftSide, float rightSide) { - return new float[] { - leftSide, leftSide, - rightSide, rightSide, - rightSide, rightSide, - leftSide, leftSide - }; + if (isVertical()) { + return new float[] { + leftSide, leftSide, leftSide, leftSide, rightSide, rightSide, rightSide, rightSide + }; + } else { + return new float[] { + leftSide, leftSide, + rightSide, rightSide, + rightSide, rightSide, + leftSide, leftSide + }; + } } private void maybeDrawTicks(@NonNull Canvas canvas) { @@ -2587,17 +2773,25 @@ private void maybeDrawTicks(@NonNull Canvas canvas) { } private void maybeDrawStopIndicator(@NonNull Canvas canvas, int yCenter) { - if (trackStopIndicatorSize <= 0) { + if (trackStopIndicatorSize <= 0 || values.isEmpty()) { return; } // Draw stop indicator at the end of the track. - if (values.size() >= 1 && values.get(values.size() - 1) < valueTo) { - canvas.drawPoint(valueToX(valueTo), yCenter, stopIndicatorPaint); + if (values.get(values.size() - 1) < valueTo) { + drawStopIndicator(canvas, valueToX(valueTo), yCenter); } // Multiple thumbs, inactive track may be visible at the start. if (values.size() > 1 && values.get(0) > valueFrom) { - canvas.drawPoint(valueToX(valueFrom), yCenter, stopIndicatorPaint); + drawStopIndicator(canvas, valueToX(valueFrom), yCenter); + } + } + + private void drawStopIndicator(@NonNull Canvas canvas, float x, float y) { + if (isVertical()) { + canvas.drawPoint(y, x, stopIndicatorPaint); + } else { + canvas.drawPoint(x, y, stopIndicatorPaint); } } @@ -2626,6 +2820,9 @@ private void drawThumbs(@NonNull Canvas canvas, int width, int yCenter) { private void drawThumbDrawable( @NonNull Canvas canvas, int width, int top, float value, @NonNull Drawable thumbDrawable) { canvas.save(); + if (isVertical()) { + canvas.setMatrix(rotationMatrix); + } canvas.translate( trackSidePadding + (int) (normalizeValue(value) * width) @@ -2638,17 +2835,21 @@ private void drawThumbDrawable( private void maybeDrawCompatHalo(@NonNull Canvas canvas, int width, int top) { // Only draw the halo for devices that aren't using the ripple. if (shouldDrawCompatHalo()) { - int centerX = (int) (trackSidePadding + normalizeValue(values.get(focusedThumbIdx)) * width); + float centerX = trackSidePadding + normalizeValue(values.get(focusedThumbIdx)) * width; + float[] bounds = {centerX, top}; + if (isVertical()) { + rotationMatrix.mapPoints(bounds); + } if (VERSION.SDK_INT < VERSION_CODES.P) { // In this case we can clip the rect to allow drawing outside the bounds. canvas.clipRect( - centerX - haloRadius, - top - haloRadius, - centerX + haloRadius, - top + haloRadius, + bounds[0] - haloRadius, + bounds[1] - haloRadius, + bounds[0] + haloRadius, + bounds[1] + haloRadius, Op.UNION); } - canvas.drawCircle(centerX, top, haloRadius, haloPaint); + canvas.drawCircle(bounds[0], bounds[1], haloRadius, haloPaint); } } @@ -2661,14 +2862,15 @@ public boolean onTouchEvent(@NonNull MotionEvent event) { if (!isEnabled()) { return false; } - float x = event.getX(); - touchPosition = (x - trackSidePadding) / trackWidth; + + float eventCoordinate = isVertical() ? event.getY() : event.getX(); + touchPosition = (eventCoordinate - trackSidePadding) / trackWidth; touchPosition = max(0, touchPosition); touchPosition = min(1, touchPosition); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: - touchDownX = x; + touchDownX = eventCoordinate; // If we're inside a vertical scrolling container, // we should start dragging in ACTION_MOVE @@ -2695,7 +2897,8 @@ public boolean onTouchEvent(@NonNull MotionEvent event) { case MotionEvent.ACTION_MOVE: if (!thumbIsPressed) { // Check if we're trying to scroll vertically instead of dragging this Slider - if (isPotentialVerticalScroll(event) && abs(x - touchDownX) < scaledTouchSlop) { + if (isPotentialVerticalScroll(event) + && abs(eventCoordinate - touchDownX) < scaledTouchSlop) { return false; } getParent().requestDisallowInterceptTouchEvent(true); @@ -2795,7 +2998,8 @@ protected boolean pickActiveThumb() { break; } - boolean movingForward = isRtl() ? (valueX - touchX) > 0 : (valueX - touchX) < 0; + boolean movingForward = + (isRtl() || isVertical()) ? (valueX - touchX) > 0 : (valueX - touchX) < 0; // Keep replacing the activeThumbIdx, while the diff decreases. // If the diffs are equal we'll pick the thumb based on which direction we are dragging. if (compare(valueDiff, activeThumbDiff) < 0) { @@ -2823,7 +3027,7 @@ protected boolean pickActiveThumb() { private float getValueOfTouchPositionAbsolute() { float position = touchPosition; - if (isRtl()) { + if (isRtl() || isVertical()) { position = 1 - position; } return (position * (valueTo - valueFrom) + valueFrom); @@ -2863,7 +3067,7 @@ private boolean snapThumbToValue(int idx, float value) { private float getClampedValue(int idx, float value) { float minSeparation = getMinSeparation(); minSeparation = separationUnit == UNIT_PX ? dimenToValue(minSeparation) : minSeparation; - if (isRtl()) { + if (isRtl() || isVertical()) { minSeparation = -minSeparation; } @@ -2893,7 +3097,7 @@ private float getValueOfTouchPosition() { double position = snapPosition(touchPosition); // We might need to invert the touch position to get the correct value. - if (isRtl()) { + if (isRtl() || isVertical()) { position = 1 - position; } return (float) (position * (valueTo - valueFrom) + valueFrom); @@ -2964,16 +3168,13 @@ private ValueAnimator createLabelAnimator(boolean enter) { animator.setDuration(duration); animator.setInterpolator(interpolator); animator.addUpdateListener( - new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float fraction = (float) animation.getAnimatedValue(); - for (TooltipDrawable label : labels) { - label.setRevealFraction(fraction); - } - // Ensure the labels are redrawn even if the slider has stopped moving - postInvalidateOnAnimation(); + animation -> { + float fraction = (float) animation.getAnimatedValue(); + for (TooltipDrawable label : labels) { + label.setRevealFraction(fraction); } + // Ensure the labels are redrawn even if the slider has stopped moving + postInvalidateOnAnimation(); }); return animator; } @@ -3090,23 +3291,39 @@ private void setValueForLabel(TooltipDrawable label, float value) { } private void positionLabel(TooltipDrawable label, float value) { + // Calculate the difference between the bounds of this view and the bounds of the root view to + // correctly position this view in the overlay layer. + calculateLabelBounds(label, value); + if (isVertical()) { + RectF labelBounds = new RectF(labelRect); + rotationMatrix.mapRect(labelBounds); + labelBounds.round(labelRect); + } + DescendantOffsetUtils.offsetDescendantRect(ViewUtils.getContentView(this), this, labelRect); + label.setBounds(labelRect); + } + + private void calculateLabelBounds(TooltipDrawable label, float value) { int left = trackSidePadding + (int) (normalizeValue(value) * trackWidth) - label.getIntrinsicWidth() / 2; - int top = calculateTrackCenter() - (labelPadding + thumbHeight / 2); - label.setBounds(left, top - label.getIntrinsicHeight(), left + label.getIntrinsicWidth(), top); - - // Calculate the difference between the bounds of this view and the bounds of the root view to - // correctly position this view in the overlay layer. - Rect rect = new Rect(label.getBounds()); - DescendantOffsetUtils.offsetDescendantRect(ViewUtils.getContentView(this), this, rect); - label.setBounds(rect); + int right = left + label.getIntrinsicWidth(); + int bottom; + int top; + if (isVertical() && !isRtl()) { + top = calculateTrackCenter() + (labelPadding + thumbHeight / 2); + bottom = top + label.getIntrinsicHeight(); + } else { + bottom = calculateTrackCenter() - (labelPadding + thumbHeight / 2); + top = bottom - label.getIntrinsicHeight(); + } + labelRect.set(left, top, right, bottom); } private void invalidateTrack() { - inactiveTrackPaint.setStrokeWidth(trackHeight); - activeTrackPaint.setStrokeWidth(trackHeight); + inactiveTrackPaint.setStrokeWidth(trackThickness); + activeTrackPaint.setStrokeWidth(trackThickness); } /** @@ -3297,6 +3514,10 @@ final boolean isRtl() { return getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; } + final boolean isVertical() { + return widgetOrientation == VERTICAL; + } + /** * Attempts to move focus to next or previous thumb independent of layout direction and * returns whether the focused thumb changed. If focused thumb didn't change, we're at the view @@ -3330,7 +3551,7 @@ private boolean moveFocus(int direction) { * @see #moveFocus(int) */ private boolean moveFocusInAbsoluteDirection(int direction) { - if (isRtl()) { + if (isRtl() || isVertical()) { // Prevent integer overflow. direction = direction == Integer.MIN_VALUE ? Integer.MAX_VALUE : -direction; } @@ -3343,6 +3564,10 @@ private Float calculateIncrementForKey(int keyCode) { // Otherwise choose the smallest valid increment. float increment = isLongPress ? calculateStepIncrement(20) : calculateStepIncrement(); switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_UP: + return isVertical() ? increment : null; + case KeyEvent.KEYCODE_DPAD_DOWN: + return isVertical() ? -increment : null; case KeyEvent.KEYCODE_DPAD_LEFT: return isRtl() ? increment : -increment; case KeyEvent.KEYCODE_DPAD_RIGHT: @@ -3546,14 +3771,19 @@ void updateBoundsForVirtualViewId(int virtualViewId, Rect virtualViewBounds) { int y = calculateTrackCenter(); int touchTargetOffsetX = max(thumbWidth / 2, minTouchTargetSize / 2); int touchTargetOffsetY = max(thumbHeight / 2, minTouchTargetSize / 2); - virtualViewBounds.set( - x - touchTargetOffsetX, - y - touchTargetOffsetY, - x + touchTargetOffsetX, - y + touchTargetOffsetY); + RectF rect = + new RectF( + x - touchTargetOffsetX, + y - touchTargetOffsetY, + x + touchTargetOffsetX, + y + touchTargetOffsetY); + if (isVertical()) { + rotationMatrix.mapRect(rect); + } + virtualViewBounds.set((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom); } - private static class AccessibilityHelper extends ExploreByTouchHelper { + public static class AccessibilityHelper extends ExploreByTouchHelper { private final BaseSlider slider; final Rect virtualViewBounds = new Rect(); @@ -3576,7 +3806,7 @@ protected int getVirtualViewAt(float x, float y) { } @Override - protected void getVisibleVirtualViews(List virtualViewIds) { + protected void getVisibleVirtualViews(@NonNull List virtualViewIds) { for (int i = 0; i < slider.getValues().size(); i++) { virtualViewIds.add(i); } @@ -3584,7 +3814,7 @@ protected void getVisibleVirtualViews(List virtualViewIds) { @Override protected void onPopulateNodeForVirtualView( - int virtualViewId, AccessibilityNodeInfoCompat info) { + int virtualViewId, @NonNull AccessibilityNodeInfoCompat info) { info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS); @@ -3625,7 +3855,8 @@ protected void onPopulateNodeForVirtualView( if (values.size() > 1) { verbalValueType = startOrEndDescription(virtualViewId); } - contentDescription.append(String.format(Locale.US, "%s, %s", verbalValueType, verbalValue)); + contentDescription.append( + String.format(Locale.getDefault(), "%s, %s", verbalValueType, verbalValue)); info.setContentDescription(contentDescription.toString()); slider.updateBoundsForVirtualViewId(virtualViewId, virtualViewBounds); @@ -3648,7 +3879,7 @@ private String startOrEndDescription(int virtualViewId) { @Override protected boolean onPerformActionForVirtualView( - int virtualViewId, int action, Bundle arguments) { + int virtualViewId, int action, @Nullable Bundle arguments) { if (!slider.isEnabled()) { return false; } @@ -3680,7 +3911,7 @@ protected boolean onPerformActionForVirtualView( } // Swap the increment if we're in RTL. - if (slider.isRtl()) { + if (slider.isRtl() || slider.isVertical()) { increment = -increment; } diff --git a/material/java/com/google/android/material/slider/SliderOrientation.java b/material/java/com/google/android/material/slider/SliderOrientation.java new file mode 100644 index 000000000..1a6cd38bb --- /dev/null +++ b/material/java/com/google/android/material/slider/SliderOrientation.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * 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.google.android.material.slider; + +import android.widget.LinearLayout; + +/** Interface definition for slider's orientation. */ +public interface SliderOrientation { + int HORIZONTAL = LinearLayout.HORIZONTAL; + int VERTICAL = LinearLayout.VERTICAL; +} diff --git a/material/java/com/google/android/material/slider/res-public/values/public.xml b/material/java/com/google/android/material/slider/res-public/values/public.xml index 0724c21c6..9401e49ec 100644 --- a/material/java/com/google/android/material/slider/res-public/values/public.xml +++ b/material/java/com/google/android/material/slider/res-public/values/public.xml @@ -40,12 +40,13 @@ - - + + - - + + + diff --git a/material/java/com/google/android/material/slider/res/values/attrs.xml b/material/java/com/google/android/material/slider/res/values/attrs.xml index 50adea7c4..6fb576b15 100644 --- a/material/java/com/google/android/material/slider/res/values/attrs.xml +++ b/material/java/com/google/android/material/slider/res/values/attrs.xml @@ -21,7 +21,8 @@ - + + @@ -79,18 +80,20 @@ - - - - + + + + - - - - + + + + + + diff --git a/material/java/com/google/android/material/snackbar/res/values/styles.xml b/material/java/com/google/android/material/snackbar/res/values/styles.xml index 737bc3853..8e7030f35 100644 --- a/material/java/com/google/android/material/snackbar/res/values/styles.xml +++ b/material/java/com/google/android/material/snackbar/res/values/styles.xml @@ -57,7 +57,7 @@ @dimen/material_emphasis_high_type end @integer/design_snackbar_text_max_lines - viewStart + viewStart ?attr/textAppearanceBody2 ?attr/colorSurface @dimen/design_snackbar_padding_vertical diff --git a/material/java/com/google/android/material/tabs/res/values/styles.xml b/material/java/com/google/android/material/tabs/res/values/styles.xml index ad52e7e27..32b2d74bf 100644 --- a/material/java/com/google/android/material/tabs/res/values/styles.xml +++ b/material/java/com/google/android/material/tabs/res/values/styles.xml @@ -90,6 +90,7 @@ diff --git a/material/java/com/google/android/material/textfield/TextInputLayout.java b/material/java/com/google/android/material/textfield/TextInputLayout.java index 6f881df68..5fccb72bf 100644 --- a/material/java/com/google/android/material/textfield/TextInputLayout.java +++ b/material/java/com/google/android/material/textfield/TextInputLayout.java @@ -3808,9 +3808,9 @@ public int getEndIconMinSize() { } /** - * Sets {@link ImageView.ScaleType} for the start icon's ImageButton. + * Sets {@link ScaleType} for the start icon's ImageButton. * - * @param scaleType {@link ImageView.ScaleType} for the start icon's ImageButton. + * @param scaleType {@link ScaleType} for the start icon's ImageButton. * @attr ref android.support.design.button.R.styleable#TextInputLayout_startIconScaleType * @see #getStartIconScaleType() */ @@ -3819,9 +3819,9 @@ public void setStartIconScaleType(@NonNull ScaleType scaleType) { } /** - * Returns the {@link ImageView.ScaleType} for the start icon's ImageButton. + * Returns the {@link ScaleType} for the start icon's ImageButton. * - * @return Returns the {@link ImageView.ScaleType} for the start icon's ImageButton. + * @return Returns the {@link ScaleType} for the start icon's ImageButton. * @attr ref android.support.design.button.R.styleable#TextInputLayout_startIconScaleType * @see #setStartIconScaleType(ScaleType) */ @@ -3831,9 +3831,9 @@ public ScaleType getStartIconScaleType() { } /** - * Sets {@link ImageView.ScaleType} for the end icon's ImageButton. + * Sets {@link ScaleType} for the end icon's ImageButton. * - * @param scaleType {@link ImageView.ScaleType} for the end icon's ImageButton. + * @param scaleType {@link ScaleType} for the end icon's ImageButton. * @attr ref android.support.design.button.R.styleable#TextInputLayout_endIconScaleType * @see #getEndIconScaleType() */ @@ -3842,9 +3842,9 @@ public void setEndIconScaleType(@NonNull ScaleType scaleType) { } /** - * Returns the {@link ImageView.ScaleType} for the end icon's ImageButton. + * Returns the {@link ScaleType} for the end icon's ImageButton. * - * @return Returns the {@link ImageView.ScaleType} for the end icon's ImageButton. + * @return Returns the {@link ScaleType} for the end icon's ImageButton. * @attr ref android.support.design.button.R.styleable#TextInputLayout_endIconScaleType * @see #setEndIconScaleType(ScaleType) */ diff --git a/material/java/com/google/android/material/textfield/res/values-v23/styles.xml b/material/java/com/google/android/material/textfield/res/values-v23/styles.xml index fdf5ec61b..dcae3ea46 100644 --- a/material/java/com/google/android/material/textfield/res/values-v23/styles.xml +++ b/material/java/com/google/android/material/textfield/res/values-v23/styles.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + diff --git a/material/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java b/material/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java index 146d071e5..721d70899 100644 --- a/material/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java +++ b/material/java/com/google/android/material/theme/overlay/MaterialThemeOverlay.java @@ -42,8 +42,7 @@ */ public class MaterialThemeOverlay { - private MaterialThemeOverlay() { - } + private MaterialThemeOverlay() {} private static final int[] ANDROID_THEME_OVERLAY_ATTRS = new int[] {android.R.attr.theme, androidx.appcompat.R.attr.theme}; @@ -51,22 +50,49 @@ private MaterialThemeOverlay() { private static final int[] MATERIAL_THEME_OVERLAY_ATTR = new int[] {R.attr.materialThemeOverlay}; /** - * Uses the materialThemeOverlay attribute to create a themed context. This allows us to use - * MaterialThemeOverlay with a default style, and gives us some protection against losing our - * ThemeOverlay by clients who set android:theme or app:theme. If android:theme or app:theme is - * specified by the client, any attributes defined there will take precedence over attributes - * defined in materialThemeOverlay. + * Uses the materialThemeOverlay attribute to create a themed context. + * + *

This allows us to use MaterialThemeOverlay with a default style, and gives us some + * protection against losing our ThemeOverlay by clients who set android:theme or app:theme. + * If android:theme or app:theme is specified by the client, any attributes defined there + * will take precedence over attributes defined in materialThemeOverlay. */ @NonNull public static Context wrap( @NonNull Context context, - @Nullable AttributeSet attrs, + @Nullable AttributeSet set, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { + return wrap(context, set, defStyleAttr, defStyleRes, new int[] {}); + } + + /** + * Uses the materialThemeOverlay attribute and optionalAttr attributes to create a combined + * themed context. + * + *

The final theme overlay will apply materialThemeOverlay first, then the optionalAttr + * overlays in order. + * + *

This facilitates creating locally scoped, re-usable overlays for component variants. For + * example, if buttons can be one of two colors and one of three shapes, instead of creating a + * style for each color-shape combination, an overlay can be created for each color and + * each shape. The button can then wrap its context and pass both overlay attributes to + * optionalAttrs before reading color and shape values. + * + * @see #wrap(Context, AttributeSet, int, int) + */ + @NonNull + public static Context wrap( + @NonNull Context context, + @Nullable AttributeSet set, + @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, + @NonNull int[] optionalAttrs) { int materialThemeOverlayId = - obtainMaterialThemeOverlayId(context, attrs, defStyleAttr, defStyleRes); - boolean contextHasOverlay = context instanceof ContextThemeWrapper - && ((ContextThemeWrapper) context).getThemeResId() == materialThemeOverlayId; + obtainMaterialThemeOverlayId(context, set, defStyleAttr, defStyleRes); + boolean contextHasOverlay = + context instanceof ContextThemeWrapper + && ((ContextThemeWrapper) context).getThemeResId() == materialThemeOverlayId; if (materialThemeOverlayId == 0 || contextHasOverlay) { return context; @@ -74,9 +100,17 @@ public static Context wrap( Context contextThemeWrapper = new ContextThemeWrapper(context, materialThemeOverlayId); + int[] optionalOverlayIds = + obtainMaterialOverlayIds(context, set, optionalAttrs, defStyleAttr, defStyleRes); + for (int optionalOverlayId : optionalOverlayIds) { + if (optionalOverlayId != 0) { + contextThemeWrapper = new ContextThemeWrapper(contextThemeWrapper, optionalOverlayId); + } + } + // We want values set in android:theme or app:theme to always override values supplied by // materialThemeOverlay, so we'll wrap the context again if either of those are set. - int androidThemeOverlayId = obtainAndroidThemeOverlayId(context, attrs); + int androidThemeOverlayId = obtainAndroidThemeOverlayId(context, set); if (androidThemeOverlayId != 0) { contextThemeWrapper.getTheme().applyStyle(androidThemeOverlayId, true); } @@ -106,16 +140,33 @@ private static int obtainAndroidThemeOverlayId(@NonNull Context context, Attribu @StyleRes private static int obtainMaterialThemeOverlayId( @NonNull Context context, - @Nullable AttributeSet attrs, + @Nullable AttributeSet set, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { - TypedArray a = - context.obtainStyledAttributes( - attrs, MATERIAL_THEME_OVERLAY_ATTR, defStyleAttr, defStyleRes); - int materialThemeOverlayId = a.getResourceId(0 /* index */, 0 /* defaultVal */); - a.recycle(); + return obtainMaterialOverlayIds( + context, set, MATERIAL_THEME_OVERLAY_ATTR, defStyleAttr, defStyleRes)[0]; + } - return materialThemeOverlayId; + /** + * Retrieves the values of an array of Material overlay attributes, taking into account {@code + * defStyleAttr} and {@code defStyleRes} because Material overlay attributes should work from + * default styles. + */ + @NonNull + private static int[] obtainMaterialOverlayIds( + @NonNull Context context, + @Nullable AttributeSet set, + @NonNull int[] attrs, + @AttrRes int defStyleAttr, + @StyleRes int defStyleRes) { + int[] overlayIds = new int[attrs.length]; + if (attrs.length > 0) { + TypedArray a = context.obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes); + for (int i = 0; i < attrs.length; i++) { + overlayIds[i] = a.getResourceId(i, /* defaultVal= */ 0); + } + a.recycle(); + } + return overlayIds; } } - diff --git a/material/java/com/google/android/material/theme/res-public/values/public.xml b/material/java/com/google/android/material/theme/res-public/values/public.xml index 2f233185d..31ca7db01 100644 --- a/material/java/com/google/android/material/theme/res-public/values/public.xml +++ b/material/java/com/google/android/material/theme/res-public/values/public.xml @@ -75,4 +75,5 @@ + diff --git a/material/java/com/google/android/material/theme/res/values/themes_base.xml b/material/java/com/google/android/material/theme/res/values/themes_base.xml index 032e8ddcd..f9c886717 100644 --- a/material/java/com/google/android/material/theme/res/values/themes_base.xml +++ b/material/java/com/google/android/material/theme/res/values/themes_base.xml @@ -192,7 +192,9 @@ @dimen/m3_appbar_size_medium + @dimen/m3_appbar_size_medium_with_subtitle @dimen/m3_appbar_size_large + @dimen/m3_appbar_size_large_with_subtitle @style/ThemeOverlay.Material3.Light @@ -246,7 +248,7 @@ ?attr/textAppearanceTitleMedium ?attr/textAppearanceTitleMedium - ?attr/textAppearanceBodyMedium + ?attr/textAppearanceBodyMedium ?attr/textAppearanceTitleMedium ?attr/textAppearanceTitleMedium ?attr/textAppearanceBodyMedium @@ -480,7 +482,9 @@ @dimen/m3_appbar_size_medium + @dimen/m3_appbar_size_medium_with_subtitle @dimen/m3_appbar_size_large + @dimen/m3_appbar_size_large_with_subtitle @style/ThemeOverlay.Material3.Dark @@ -534,7 +538,7 @@ ?attr/textAppearanceTitleMedium ?attr/textAppearanceTitleMedium - ?attr/textAppearanceBodyMedium + ?attr/textAppearanceBodyMedium ?attr/textAppearanceTitleMedium ?attr/textAppearanceTitleMedium ?attr/textAppearanceBodyMedium @@ -652,8 +656,8 @@ @style/ThemeOverlay.MaterialComponents.BottomSheetDialog @style/ThemeOverlay.MaterialComponents.Dialog.Alert @style/ThemeOverlay.MaterialComponents.MaterialAlertDialog - @style/ThemeOverlay.MaterialComponents.Dialog - @style/ThemeOverlay.MaterialComponents.Dialog + @style/ThemeOverlay.MaterialComponents.Dialog + @style/ThemeOverlay.MaterialComponents.Dialog ?attr/textAppearanceSubtitle1 @@ -719,8 +723,8 @@ @style/ThemeOverlay.MaterialComponents.BottomSheetDialog @style/ThemeOverlay.MaterialComponents.Dialog.Alert @style/ThemeOverlay.MaterialComponents.MaterialAlertDialog - @style/ThemeOverlay.MaterialComponents.Dialog - @style/ThemeOverlay.MaterialComponents.Dialog + @style/ThemeOverlay.MaterialComponents.Dialog + @style/ThemeOverlay.MaterialComponents.Dialog ?attr/textAppearanceSubtitle1 diff --git a/material/java/com/google/android/material/timepicker/res/values/themes.xml b/material/java/com/google/android/material/timepicker/res/values/themes.xml index 96d4ccff4..94cdf6582 100644 --- a/material/java/com/google/android/material/timepicker/res/values/themes.xml +++ b/material/java/com/google/android/material/timepicker/res/values/themes.xml @@ -29,7 +29,7 @@