From b26e5494f3b88ac00f7428067d28a3970ab79ddf Mon Sep 17 00:00:00 2001 From: Artem Umerov Date: Thu, 27 Apr 2023 18:24:59 +0300 Subject: [PATCH] 24 24 --- .../fenrir/activity/SinglePhotoActivity.kt | 2 +- .../activity/gifpager/GifPagerActivity.kt | 3 +- .../activity/photopager/IPhotoPagerView.kt | 1 + .../activity/photopager/PhotoPagerActivity.kt | 134 ++++++++++++ .../photopager/PhotoPagerPresenter.kt | 119 ++++++++--- .../activity/storypager/StoryPagerActivity.kt | 2 + .../fenrir/api/model/VKApiPhotoTags.kt | 4 +- .../fenrir/domain/IPhotosInteractor.kt | 4 +- .../fenrir/domain/impl/PhotosInteractor.kt | 10 +- .../fenrir/domain/mappers/Dto2Model.kt | 12 ++ .../kotlin/dev/ragnarok/fenrir/model/Photo.kt | 27 +++ .../dev/ragnarok/fenrir/model/PhotoTags.kt | 181 ++++++++++++++++ .../ragnarok/fenrir/view/TouchImageView.kt | 96 +++++++-- .../main/res/drawable/photo_tag_border.xml | 13 ++ .../main/res/layout/content_photo_page.xml | 7 + .../src/main/res/layout/photo_tag_item.xml | 32 +++ .../activity/photopager/PhotoPagerActivity.kt | 4 +- .../filegallery/view/TouchImageView.kt | 96 +++++++-- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- ...dBufferDecompressingStreamNoFinalizer.java | 35 +++ .../main/java/com/github/luben/zstd/Zstd.java | 6 +- ...dBufferDecompressingStreamNoFinalizer.java | 14 +- ...tBufferDecompressingStreamNoFinalizer.java | 4 +- .../zstd/ZstdInputStreamNoFinalizer.java | 2 +- .../zxing/aztec/encoder/HighLevelEncoder.java | 7 +- .../jni/animation/libyuv/source/row_common.cc | 6 +- libfenrir/src/main/jni/thorvg/inc/thorvg.h | 63 +++--- .../thorvg/src/lib/sw_engine/tvgSwCommon.h | 5 +- .../thorvg/src/lib/sw_engine/tvgSwImage.cpp | 17 +- .../thorvg/src/lib/sw_engine/tvgSwMemPool.cpp | 8 +- .../thorvg/src/lib/sw_engine/tvgSwRaster.cpp | 27 +-- .../src/lib/sw_engine/tvgSwRasterTexmap.h | 10 +- .../src/lib/sw_engine/tvgSwRenderer.cpp | 27 +-- .../thorvg/src/lib/sw_engine/tvgSwRenderer.h | 3 +- .../jni/thorvg/src/lib/sw_engine/tvgSwRle.cpp | 15 +- .../src/main/jni/thorvg/src/lib/tvgCommon.h | 4 +- .../jni/thorvg/src/lib/tvgInitializer.cpp | 13 +- .../main/jni/thorvg/src/lib/tvgPicture.cpp | 6 +- .../main/jni/thorvg/src/lib/tvgPictureImpl.h | 40 ++-- .../src/main/jni/thorvg/src/lib/tvgRender.h | 14 +- .../jni/thorvg/src/lib/tvgTaskScheduler.cpp | 4 +- .../jni/thorvg/src/lib/tvgTaskScheduler.h | 57 +++-- .../thorvg/src/loaders/svg/tvgSvgCssStyle.cpp | 73 +++---- .../thorvg/src/loaders/svg/tvgSvgLoader.cpp | 187 ++++++++-------- .../src/loaders/svg/tvgSvgLoaderCommon.h | 61 ++++++ .../jni/thorvg/src/loaders/svg/tvgSvgPath.cpp | 9 - .../src/loaders/svg/tvgSvgSceneBuilder.cpp | 8 +- .../jni/thorvg/src/loaders/svg/tvgSvgUtil.cpp | 4 +- .../src/main/resources/META-INF/plugin.xml | 1 + .../widget/AsyncDifferConfig.java | 2 - .../recyclerview/widget/GapWorker.java | 6 +- .../widget/LinearLayoutManager.java | 47 ----- .../recyclerview/widget/RecyclerView.java | 199 ++++++++++++++---- .../widget/StaggeredGridLayoutManager.java | 1 - .../adapter/FragmentStateAdapter.java | 23 +- .../viewpager2/widget/ScrollEventAdapter.java | 2 +- .../viewpager2/widget/ViewPager2.java | 4 +- viewpager2/src/main/res/values/attrs.xml | 7 +- viewpager2/src/main/res/values/dimens.xml | 5 +- viewpager2/src/main/res/values/ids.xml | 5 +- 61 files changed, 1292 insertions(+), 490 deletions(-) create mode 100644 app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/PhotoTags.kt create mode 100644 app_fenrir/src/main/res/drawable/photo_tag_border.xml create mode 100644 app_fenrir/src/main/res/layout/photo_tag_item.xml diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt index 0a6c36875..45908b7a4 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt @@ -119,7 +119,6 @@ class SinglePhotoActivity : NoMainActivity(), PlaceProvider, AppStyleable { }).build() ) - ret.photo.setOnLongClickListener { doSaveOnDrive(true) true @@ -304,6 +303,7 @@ class SinglePhotoActivity : NoMainActivity(), PlaceProvider, AppStyleable { } private fun loadImage(url: String?) { + PicassoInstance.with().cancelRequest(photo) mLoadingNow = true resolveProgressVisibility(true) PicassoInstance.with() diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/gifpager/GifPagerActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/gifpager/GifPagerActivity.kt index 30a42850a..7aa597591 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/gifpager/GifPagerActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/gifpager/GifPagerActivity.kt @@ -268,6 +268,7 @@ class GifPagerActivity : AbsDocumentPreviewActivity if (event.pointerCount >= 2 || view.canScrollHorizontally(1) && view.canScrollHorizontally( -1 @@ -325,4 +326,4 @@ class GifPagerActivity : AbsDocumentPreviewActivity } mButtonWithUser = findViewById(R.id.with_user_button) mButtonWithUser?.setOnClickListener { presenter?.fireWithUserClick() } + mButtonWithUser?.setOnLongClickListener { + presenter?.fireWithUserLongClick() + true + } mButtonComments = findViewById(R.id.comments_button) mButtonComments?.setOnClickListener { presenter?.fireCommentsButtonClick() } buttonShare = findViewById(R.id.share_button) @@ -674,6 +684,10 @@ class PhotoPagerActivity : BaseMvpActivity mToolbar?.visibility = if (visible) View.VISIBLE else View.GONE } + override fun rebindPhotoAtPartial(position: Int) { + mPagerAdapter?.notifyItemChanged(position) + } + override fun rebindPhotoAt(position: Int) { mPagerAdapter?.notifyItemChanged(position) if (bShowPhotosLine && mAdapterRecycler.getSize() > 1) { @@ -761,13 +775,94 @@ class PhotoPagerActivity : BaseMvpActivity val reload: FloatingActionButton private val mPicassoLoadCallback: WeakPicassoLoadCallback val photo: TouchImageView + val tagsPlaceholder: FrameLayout val progress: RLottieImageView var animationDispose: Disposable = Disposable.disposed() private var mAnimationLoaded = false private var mLoadingNow = false + private var tagCleared = true + private var currentPhoto: Photo? = null + + fun clearTags() { + if (tagCleared) { + return + } + tagsPlaceholder.removeAllViews() + tagsPlaceholder.visibility = View.GONE + tagCleared = true + } + + fun addTags() { + if (!tagCleared) { + return + } + val currPhoto = currentPhoto ?: return + if (!currPhoto.showPhotoTags || currPhoto.photoTags.isNullOrEmpty()) { + return + } + val tags = currPhoto.photoTags + val stateValues = photo.getStateValues() ?: return + if (stateValues.viewWidth <= 0 || stateValues.viewHeight <= 0 || stateValues.bitmapWidth <= 0 || stateValues.bitmapHeight <= 0) { + return + } + val scaleX: Double = stateValues.matrix[Matrix.MSCALE_X].toDouble() + val bitmapWidth: Double = stateValues.bitmapWidth.toDouble() + val preScaledBitmapWidth = scaleX * bitmapWidth / 100.0 + val scaleY: Double = stateValues.matrix[Matrix.MSCALE_Y].toDouble() + val bitmapHeight: Double = stateValues.bitmapHeight.toDouble() + val preScaledBitmapHeight = scaleY * bitmapHeight / 100.0 + val transX: Double = stateValues.matrix[Matrix.MTRANS_X].toDouble() + val transY: Double = stateValues.matrix[Matrix.MTRANS_Y].toDouble() + + var has = false + for (i in tags.orEmpty()) { + val leftMargin = transX + i.getX() * preScaledBitmapWidth + val topMargin = transY + i.getY() * preScaledBitmapHeight + val layoutWidth = ((i.getX2() - i.getX()) * preScaledBitmapWidth) + val layoutHeight = ((i.getY2() - i.getY()) * preScaledBitmapHeight) + + if (leftMargin > stateValues.viewWidth || leftMargin < -50.0 || topMargin > stateValues.viewHeight || topMargin < -50.0) { + continue + } else { + val layoutParams = FrameLayout.LayoutParams(-2, -2) + layoutParams.leftMargin = leftMargin.toInt() + layoutParams.topMargin = topMargin.toInt() + val inflate = + layoutInflater.inflate(R.layout.photo_tag_item, null as ViewGroup?) + inflate.findViewById(R.id.tv_ph_tag_name)?.let { txt -> + txt.visibility = + if (i.getTaggedName().nonNullNoEmpty()) View.VISIBLE else View.GONE + txt.text = i.getTaggedName() + txt.tag = i + txt.setOnClickListener { + val photoTag = txt.tag as? PhotoTags + if (photoTag != null) { + PlaceFactory.getOwnerWallPlace( + Settings.get().accounts().current, photoTag.getUserId(), null + ).tryOpenWith(this@PhotoPagerActivity) + } + } + } + (inflate.findViewById(R.id.iv_ph_tag_border) as ImageView).layoutParams = + LinearLayout.LayoutParams(layoutWidth.toInt(), layoutHeight.toInt()) + tagsPlaceholder.addView(inflate, layoutParams) + if (!has) { + has = true + } + } + } + if (has) { + tagCleared = false + tagsPlaceholder.visibility = View.VISIBLE + } + } + fun bindTo(photo_image: Photo) { + clearTags() photo.resetZoom() + photo.orientationLocked = TouchImageView.OrientationLocked.HORIZONTAL val size: Int = photoSizeFromPrefs + currentPhoto = photo_image val url = photo_image.getUrlForSize(size, true) reload.setOnClickListener { reload.visibility = View.INVISIBLE @@ -829,6 +924,7 @@ class PhotoPagerActivity : BaseMvpActivity } private fun loadImage(url: String) { + PicassoInstance.with().cancelRequest(photo) mLoadingNow = true resolveProgressVisibility(true) PicassoInstance.with() @@ -850,6 +946,11 @@ class PhotoPagerActivity : BaseMvpActivity mLoadingNow = false resolveProgressVisibility(false) reload.visibility = View.INVISIBLE + + clearTags() + photo.doOnPreDraw { + addTags() + } } override fun onError(t: Throwable) { @@ -865,6 +966,7 @@ class PhotoPagerActivity : BaseMvpActivity photo.doubleTapMaxZoom = 4f progress = view.findViewById(idOfProgressBar()) reload = view.findViewById(R.id.goto_button) + tagsPlaceholder = view.findViewById(R.id.tags_placeholder) mPicassoLoadCallback = WeakPicassoLoadCallback(this) photo.setOnClickListener { presenter?.firePhotoTap() } } @@ -886,12 +988,42 @@ class PhotoPagerActivity : BaseMvpActivity if (rot >= 360f) { rot = 0f } + if (rot == 0f) { + ret.clearTags() + ret.addTags() + } else { + ret.clearTags() + } (ret.photo.drawable as Rotatable).rotate(rot) ret.photo.fitImageToView() ret.photo.invalidate() } true } + ret.photo.setOnStateChangeListener(object : TouchImageView.StateListener { + override fun onChangeState( + imageActionState: TouchImageView.ImageActionState, + zoomed: Boolean + ) { + when (imageActionState) { + TouchImageView.ImageActionState.NONE -> { + ret.addTags() + } + + TouchImageView.ImageActionState.CANT_MOVE, TouchImageView.ImageActionState.MOVE, TouchImageView.ImageActionState.FLING -> { + if (zoomed) { + ret.clearTags() + } + } + + TouchImageView.ImageActionState.ZOOM, TouchImageView.ImageActionState.ANIMATE_ZOOM -> { + ret.clearTags() + } + + else -> {} + } + } + }) ret.photo.setOnTouchListener { view: View, event: MotionEvent -> if (event.pointerCount >= 2 || view.canScrollHorizontally(1) && view.canScrollHorizontally( -1 @@ -914,10 +1046,12 @@ class PhotoPagerActivity : BaseMvpActivity return ret } + /* override fun onViewDetachedFromWindow(holder: PhotoViewHolder) { super.onViewDetachedFromWindow(holder) PicassoInstance.with().cancelRequest(holder.photo) } + */ override fun onBindViewHolder(holder: PhotoViewHolder, position: Int) { val photo = mPhotos[position] diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerPresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerPresenter.kt index df39393df..6f32c3ae7 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerPresenter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerPresenter.kt @@ -34,6 +34,7 @@ import dev.ragnarok.fenrir.model.Photo import dev.ragnarok.fenrir.model.PhotoAlbum import dev.ragnarok.fenrir.model.PhotoSize import dev.ragnarok.fenrir.nonNullNoEmpty +import dev.ragnarok.fenrir.orZero import dev.ragnarok.fenrir.picasso.PicassoInstance.Companion.with import dev.ragnarok.fenrir.place.PlaceFactory import dev.ragnarok.fenrir.push.OwnerInfo @@ -316,7 +317,15 @@ open class PhotoPagerPresenter internal constructor( if (ne.accessKey == null) { ne.setAccessKey(photo.accessKey) } + val old = mPhotos[currentIndex] mPhotos[currentIndex] = ne + mPhotos[currentIndex].let { uit -> + uit.setShowPhotoTags(old.showPhotoTags) + uit.setPhotoTags(old.photoTags) + uit.setMsgId(old.msgId) + uit.setDeleted(old.isDeleted) + uit.setMsgPeerId(old.msgPeerId) + } refreshInfoViews(false) } }) { }) @@ -571,40 +580,88 @@ open class PhotoPagerPresenter internal constructor( ) } + private fun showWithUserDialog(photo: Photo) { + photo.setShowPhotoTags(true) + view?.rebindPhotoAtPartial(currentIndex) + val buttons: MutableList = ArrayList(photo.photoTags?.size.orZero()) + for (i in photo.photoTags.orEmpty()) { + if (i.getUserId() != 0L) { + buttons.add(FunctionSource(i.getTaggedName(), R.drawable.person) { + PlaceFactory.getOwnerWallPlace( + accountId, i.getUserId(), null + ).tryOpenWith(context) + }) + } else { + buttons.add(FunctionSource(i.getTaggedName(), R.drawable.pencil) {}) + } + } + val adapter = ButtonAdapter(context, buttons) + MaterialAlertDialogBuilder(context) + .setTitle(R.string.has_tags) + .setPositiveButton(R.string.button_ok, null) + .setCancelable(true) + .setView( + Utils.createAlertRecycleFrame( + context, + adapter, + null, + accountId + ) + ) + .show() + } + fun fireWithUserClick() { val photo = current - appendDisposable( - InteractorFactory.createPhotosInteractor() - .getTags(accountId, photo.ownerId, photo.getObjectId(), photo.accessKey) - .fromIOToMain() - .subscribe({ - val buttons: MutableList = ArrayList(it.size) - for (i in it) { - if (i.user_id != 0L) { - buttons.add(FunctionSource(i.tagged_name, R.drawable.person) { - PlaceFactory.getOwnerWallPlace( - accountId, i.user_id, null - ).tryOpenWith(context) - }) - } else { - buttons.add(FunctionSource(i.tagged_name, R.drawable.pencil) {}) + if (photo.showPhotoTags) { + photo.setShowPhotoTags(false) + view?.rebindPhotoAtPartial(currentIndex) + return + } + if (photo.photoTags.isNullOrEmpty()) { + appendDisposable( + InteractorFactory.createPhotosInteractor() + .getTags(accountId, photo.ownerId, photo.getObjectId(), photo.accessKey) + .fromIOToMain() + .subscribe({ + photo.setPhotoTags(it) + photo.setShowPhotoTags(true) + view?.rebindPhotoAtPartial(currentIndex) + }) { throwable -> + view?.let { + showError( + it, + throwable + ) } - } - val adapter = ButtonAdapter(context, buttons) - MaterialAlertDialogBuilder(context) - .setTitle(R.string.has_tags) - .setPositiveButton(R.string.button_ok, null) - .setCancelable(true) - .setView(Utils.createAlertRecycleFrame(context, adapter, null, accountId)) - .show() - }) { throwable -> - view?.let { - showError( - it, - throwable - ) - } - }) + }) + } else { + photo.setShowPhotoTags(true) + view?.rebindPhotoAtPartial(currentIndex) + } + } + + fun fireWithUserLongClick() { + val photo = current + if (photo.photoTags.isNullOrEmpty()) { + appendDisposable( + InteractorFactory.createPhotosInteractor() + .getTags(accountId, photo.ownerId, photo.getObjectId(), photo.accessKey) + .fromIOToMain() + .subscribe({ + photo.setPhotoTags(it) + showWithUserDialog(photo) + }) { throwable -> + view?.let { + showError( + it, + throwable + ) + } + }) + } else { + showWithUserDialog(photo) + } } private fun hasPhotos(): Boolean { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/storypager/StoryPagerActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/storypager/StoryPagerActivity.kt index 194e0f326..cbf76e7cc 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/storypager/StoryPagerActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/storypager/StoryPagerActivity.kt @@ -519,6 +519,7 @@ class StoryPagerActivity : BaseMvpActivity private var mLoadingNow = false override fun bindTo(story: Story) { photo.resetZoom() + photo.orientationLocked = TouchImageView.OrientationLocked.VERTICAL if (story.isIs_expired) { createCustomToast(this@StoryPagerActivity).showToastError(R.string.is_expired) mLoadingNow = false @@ -587,6 +588,7 @@ class StoryPagerActivity : BaseMvpActivity } private fun loadImage(url: String) { + PicassoInstance.with().cancelRequest(photo) mLoadingNow = true resolveProgressVisibility(true) PicassoInstance.with() diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/VKApiPhotoTags.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/VKApiPhotoTags.kt index 5517863ac..40f285315 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/VKApiPhotoTags.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/VKApiPhotoTags.kt @@ -6,9 +6,9 @@ import kotlinx.serialization.Serializable class VKApiPhotoTags { var id = 0 var user_id = 0L - var placer_id = 0 + var placer_id = 0L var tagged_name: String? = null - var date: Long = 0 + var date: Long = 0L var x = 0.0 var y = 0.0 var x2 = 0.0 diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IPhotosInteractor.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IPhotosInteractor.kt index 1452fd757..56b667795 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IPhotosInteractor.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IPhotosInteractor.kt @@ -1,11 +1,11 @@ package dev.ragnarok.fenrir.domain -import dev.ragnarok.fenrir.api.model.VKApiPhotoTags import dev.ragnarok.fenrir.fragment.search.criteria.PhotoSearchCriteria import dev.ragnarok.fenrir.model.AccessIdPair import dev.ragnarok.fenrir.model.Comment import dev.ragnarok.fenrir.model.Photo import dev.ragnarok.fenrir.model.PhotoAlbum +import dev.ragnarok.fenrir.model.PhotoTags import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Single @@ -86,7 +86,7 @@ interface IPhotosInteractor { ownerId: Long?, photo_id: Int?, access_key: String? - ): Single> + ): Single> fun getAllComments( accountId: Long, diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/PhotosInteractor.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/PhotosInteractor.kt index 773134d91..fdfe84cf5 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/PhotosInteractor.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/PhotosInteractor.kt @@ -3,7 +3,6 @@ package dev.ragnarok.fenrir.domain.impl import android.provider.BaseColumns import dev.ragnarok.fenrir.api.interfaces.INetworker import dev.ragnarok.fenrir.api.model.VKApiPhotoAlbum -import dev.ragnarok.fenrir.api.model.VKApiPhotoTags import dev.ragnarok.fenrir.db.interfaces.IStorages import dev.ragnarok.fenrir.db.model.PhotoPatch import dev.ragnarok.fenrir.db.model.PhotoPatch.Like @@ -31,6 +30,7 @@ import dev.ragnarok.fenrir.model.CommentedType import dev.ragnarok.fenrir.model.IOwnersBundle import dev.ragnarok.fenrir.model.Photo import dev.ragnarok.fenrir.model.PhotoAlbum +import dev.ragnarok.fenrir.model.PhotoTags import dev.ragnarok.fenrir.model.criteria.PhotoAlbumsCriteria import dev.ragnarok.fenrir.model.criteria.PhotoCriteria import dev.ragnarok.fenrir.settings.Settings @@ -221,9 +221,13 @@ class PhotosInteractor(private val networker: INetworker, private val cache: ISt ownerId: Long?, photo_id: Int?, access_key: String? - ): Single> { + ): Single> { return networker.vkDefault(accountId) - .photos().getTags(ownerId, photo_id, access_key) + .photos().getTags(ownerId, photo_id, access_key).map { it1 -> + mapAll( + it1 + ) { transform(it) } + } } override fun getAllComments( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/mappers/Dto2Model.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/mappers/Dto2Model.kt index a5264494b..eb0f0a4f8 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/mappers/Dto2Model.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/mappers/Dto2Model.kt @@ -488,6 +488,18 @@ object Dto2Model { .setSrc(dto.src) } + fun transform(dto: VKApiPhotoTags): PhotoTags { + return PhotoTags(dto.id, dto.user_id) + .setPlacerId(dto.placer_id) + .setTaggedName(dto.tagged_name) + .setDate(dto.date) + .setX(dto.x) + .setY(dto.y) + .setX2(dto.x2) + .setY2(dto.y2) + .setViewed(dto.viewed) + } + fun transform(dto: VKApiDoc.Video): VideoPreview { return VideoPreview() .setHeight(dto.height) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/Photo.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/Photo.kt index e0e473332..cae9b9716 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/Photo.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/Photo.kt @@ -50,6 +50,10 @@ class Photo : AbsModel, ISomeones, ParcelNative.ParcelableNative { private set var msgPeerId = 0L private set + var photoTags: List? = null + private set + var showPhotoTags: Boolean = false + private set constructor() internal constructor(parcel: ParcelNative) { @@ -72,6 +76,8 @@ class Photo : AbsModel, ISomeones, ParcelNative.ParcelableNative { repostsCount = parcel.readInt() msgId = parcel.readInt() msgPeerId = parcel.readLong() + photoTags = parcel.readParcelableList(PhotoTags.NativeCreator) + showPhotoTags = parcel.readBoolean() } internal constructor(parcel: Parcel) { @@ -94,6 +100,8 @@ class Photo : AbsModel, ISomeones, ParcelNative.ParcelableNative { repostsCount = parcel.readInt() msgId = parcel.readInt() msgPeerId = parcel.readLong() + photoTags = parcel.createTypedArrayList(PhotoTags.CREATOR) + showPhotoTags = parcel.getBoolean() } @AbsModelType @@ -175,6 +183,21 @@ class Photo : AbsModel, ISomeones, ParcelNative.ParcelableNative { return this } + fun setPhotoTags(photoTags: List?): Photo { + this.photoTags = photoTags + return this + } + + fun setShowPhotoTags(showPhotoTags: Boolean): Photo { + this.showPhotoTags = showPhotoTags + return this + } + + fun toggleShowPhotoTags(): Photo { + showPhotoTags = !showPhotoTags + return this + } + fun getUrlForSize(@PhotoSize size: Int, excludeNonAspectRatio: Boolean): String? { return sizes?.getUrlForSize(size, excludeNonAspectRatio) } @@ -228,6 +251,8 @@ class Photo : AbsModel, ISomeones, ParcelNative.ParcelableNative { parcel.writeInt(repostsCount) parcel.writeInt(msgId) parcel.writeLong(msgPeerId) + parcel.writeTypedList(photoTags) + parcel.putBoolean(showPhotoTags) } fun generateWebLink(): String { @@ -267,6 +292,8 @@ class Photo : AbsModel, ISomeones, ParcelNative.ParcelableNative { dest.writeInt(repostsCount) dest.writeInt(msgId) dest.writeLong(msgPeerId) + dest.writeParcelableList(photoTags) + dest.writeBoolean(showPhotoTags) } companion object { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/PhotoTags.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/PhotoTags.kt new file mode 100644 index 000000000..52782e0c0 --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/PhotoTags.kt @@ -0,0 +1,181 @@ +package dev.ragnarok.fenrir.model + +import android.os.Parcel +import android.os.Parcelable +import dev.ragnarok.fenrir.module.parcel.ParcelNative +import kotlinx.serialization.Serializable + +@Serializable +class PhotoTags : Parcelable, ParcelNative.ParcelableNative { + private var id = 0 + private var user_id = 0L + private var placer_id = 0L + private var tagged_name: String? = null + private var date: Long = 0L + private var x = 0.0 + private var y = 0.0 + private var x2 = 0.0 + private var y2 = 0.0 + private var viewed = 0 + + constructor(id: Int, user_id: Long) { + this.id = id + this.user_id = user_id + } + + internal constructor(parcel: Parcel) { + id = parcel.readInt() + user_id = parcel.readLong() + placer_id = parcel.readLong() + tagged_name = parcel.readString() + date = parcel.readLong() + x = parcel.readDouble() + y = parcel.readDouble() + x2 = parcel.readDouble() + y2 = parcel.readDouble() + viewed = parcel.readInt() + } + + internal constructor(parcel: ParcelNative) { + id = parcel.readInt() + user_id = parcel.readLong() + placer_id = parcel.readLong() + tagged_name = parcel.readString() + date = parcel.readLong() + x = parcel.readDouble() + y = parcel.readDouble() + x2 = parcel.readDouble() + y2 = parcel.readDouble() + viewed = parcel.readInt() + } + + override fun writeToParcel(parcel: Parcel, i: Int) { + parcel.writeInt(id) + parcel.writeLong(user_id) + parcel.writeLong(placer_id) + parcel.writeString(tagged_name) + parcel.writeLong(date) + parcel.writeDouble(x) + parcel.writeDouble(y) + parcel.writeDouble(x2) + parcel.writeDouble(y2) + parcel.writeInt(viewed) + } + + override fun writeToParcelNative(dest: ParcelNative) { + dest.writeInt(id) + dest.writeLong(user_id) + dest.writeLong(placer_id) + dest.writeString(tagged_name) + dest.writeLong(date) + dest.writeDouble(x) + dest.writeDouble(y) + dest.writeDouble(x2) + dest.writeDouble(y2) + dest.writeInt(viewed) + } + + fun getId(): Int { + return id + } + + fun getUserId(): Long { + return user_id + } + + fun getPlacerId(): Long { + return placer_id + } + + fun setPlacerId(placer_id: Long): PhotoTags { + this.placer_id = placer_id + return this + } + + fun getTaggedName(): String? { + return tagged_name + } + + fun setTaggedName(tagged_name: String?): PhotoTags { + this.tagged_name = tagged_name + return this + } + + fun getDate(): Long { + return date + } + + fun setDate(date: Long): PhotoTags { + this.date = date + return this + } + + fun getX(): Double { + return x + } + + fun setX(x: Double): PhotoTags { + this.x = x + return this + } + + fun getY(): Double { + return y + } + + fun setY(y: Double): PhotoTags { + this.y = y + return this + } + + fun getX2(): Double { + return x2 + } + + fun setX2(x2: Double): PhotoTags { + this.x2 = x2 + return this + } + + fun getY2(): Double { + return y2 + } + + fun setY2(y2: Double): PhotoTags { + this.y2 = y2 + return this + } + + fun getViewed(): Int { + return viewed + } + + fun setViewed(viewed: Int): PhotoTags { + this.viewed = viewed + return this + } + + override fun describeContents(): Int { + return 0 + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PhotoTags { + return PhotoTags(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + val NativeCreator: ParcelNative.Creator = + object : ParcelNative.Creator { + override fun readFromParcelNative(dest: ParcelNative): PhotoTags { + return PhotoTags(dest) + } + + } + } +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/view/TouchImageView.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/view/TouchImageView.kt index 426a40739..060ad2f00 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/view/TouchImageView.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/view/TouchImageView.kt @@ -20,6 +20,7 @@ import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.LinearInterpolator import android.widget.OverScroller import androidx.appcompat.widget.AppCompatImageView +import com.squareup.picasso3.Rotatable import dev.ragnarok.fenrir.R import dev.ragnarok.fenrir.fromIOToMain import dev.ragnarok.fenrir.getParcelableCompat @@ -79,8 +80,8 @@ open class TouchImageView @JvmOverloads constructor( private var viewSizeChangeFixedPixel: FixedPixel? = FixedPixel.CENTER private var orientationJustChanged = false - internal enum class ImageActionState { - NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM + enum class ImageActionState { + NONE, DRAG, MOVE, CANT_MOVE, ZOOM, FLING, ANIMATE_ZOOM } private var imageActionState: ImageActionState? = null @@ -123,9 +124,16 @@ open class TouchImageView @JvmOverloads constructor( private var touchCoordinatesListener: OnTouchCoordinatesListener? = null private var doubleTapListener: OnDoubleTapListener? = null private var userTouchListener: OnTouchListener? = null + private var stateListener: StateListener? = null private var touchImageViewListener: OnTouchImageViewListener? = null private var animDrawable: AnimatedFileDrawable? = null + enum class OrientationLocked { + HORIZONTAL, VERTICAL + } + + var orientationLocked = OrientationLocked.HORIZONTAL + init { super.setClickable(true) orientation = resources.configuration.orientation @@ -163,6 +171,14 @@ open class TouchImageView @JvmOverloads constructor( isRotateImageToFitScreen = rotateImageToFitScreen } + interface StateListener { + fun onChangeState(imageActionState: ImageActionState, zoomed: Boolean) + } + + fun setOnStateChangeListener(onStateListener: StateListener) { + stateListener = onStateListener + } + override fun setOnTouchListener(onTouchListener: OnTouchListener?) { userTouchListener = onTouchListener } @@ -969,11 +985,11 @@ open class TouchImageView @JvmOverloads constructor( } internal fun setState(imageActionState: ImageActionState) { + if (this.imageActionState == imageActionState) { + return + } this.imageActionState = imageActionState - } - - fun canScrollHorizontallyFroyo(direction: Int): Boolean { - return canScrollHorizontally(direction) + stateListener?.onChangeState(imageActionState, isZoomed) } override fun canScrollHorizontally(direction: Int): Boolean { @@ -1067,7 +1083,7 @@ open class TouchImageView @JvmOverloads constructor( } gestureDetector.onTouchEvent(event) val curr = PointF(event.x, event.y) - if (imageActionState == ImageActionState.NONE || imageActionState == ImageActionState.DRAG || imageActionState == ImageActionState.FLING) { + if (imageActionState == ImageActionState.NONE || imageActionState == ImageActionState.DRAG || imageActionState == ImageActionState.MOVE || imageActionState == ImageActionState.CANT_MOVE || imageActionState == ImageActionState.FLING) { when (event.action) { MotionEvent.ACTION_DOWN -> { last.set(curr) @@ -1075,9 +1091,29 @@ open class TouchImageView @JvmOverloads constructor( setState(ImageActionState.DRAG) } - MotionEvent.ACTION_MOVE -> if (imageActionState == ImageActionState.DRAG) { + MotionEvent.ACTION_MOVE -> if (imageActionState == ImageActionState.DRAG || imageActionState == ImageActionState.MOVE || imageActionState == ImageActionState.CANT_MOVE) { val deltaX = curr.x - last.x val deltaY = curr.y - last.y + if (orientationLocked == OrientationLocked.HORIZONTAL) { + if (deltaX < 1 && deltaX > -1 || abs(deltaX) < abs(deltaY) || canScrollHorizontally( + if (deltaX < 0) 1 else -1 + ) + ) { + setState(ImageActionState.MOVE) + } else { + setState(ImageActionState.CANT_MOVE) + } + } else { + if (deltaY < 1 && deltaY > -1 || abs(deltaY) < abs(deltaX) || canScrollVertically( + if (deltaY < 0) 1 else -1 + ) + ) { + setState(ImageActionState.MOVE) + } else { + setState(ImageActionState.CANT_MOVE) + } + } + val fixTransX = getFixDragTrans(deltaX, viewWidth.toFloat(), imageWidth) val fixTransY = getFixDragTrans(deltaY, viewHeight.toFloat(), imageHeight) touchMatrix.postTranslate(fixTransX, fixTransY) @@ -1085,9 +1121,13 @@ open class TouchImageView @JvmOverloads constructor( last[curr.x] = curr.y } - MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> setState( - ImageActionState.NONE - ) + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_POINTER_UP -> { + if (imageActionState != ImageActionState.FLING) { + setState( + ImageActionState.NONE + ) + } + } } } @@ -1355,7 +1395,6 @@ open class TouchImageView @JvmOverloads constructor( } fun cancelFling() { - setState(ImageActionState.NONE) scroller.forceFinished(true) } @@ -1365,6 +1404,9 @@ open class TouchImageView @JvmOverloads constructor( // Listener runnable updated with each frame of fling animation. touchImageViewListener?.onMove() if (scroller.isFinished) { + if (imageActionState == ImageActionState.FLING) { + setState(ImageActionState.NONE) + } return } if (scroller.computeScrollOffset()) { @@ -1378,9 +1420,10 @@ open class TouchImageView @JvmOverloads constructor( fixTrans() imageMatrix = touchMatrix compatPostOnAnimation(this) + } else if (imageActionState == ImageActionState.FLING) { + setState(ImageActionState.NONE) } } - } private class CompatScroller(context: Context?) { @@ -1549,6 +1592,33 @@ open class TouchImageView @JvmOverloads constructor( } + class StateValues( + val viewWidth: Int, + val viewHeight: Int, + val bitmapWidth: Int, + val bitmapHeight: Int, + viewMatrix: Matrix + ) { + val matrix = FloatArray(9) + + init { + viewMatrix.getValues(matrix) + } + } + + fun getStateValues(): StateValues? { + if (drawable == null || drawable is Rotatable && (drawable as Rotatable).getRotation() != 0f) { + return null + } + return StateValues( + viewWidth, + viewHeight, + drawable.intrinsicWidth, + drawable.intrinsicHeight, + touchMatrix + ) + } + companion object { private const val STATE = "instanceState" diff --git a/app_fenrir/src/main/res/drawable/photo_tag_border.xml b/app_fenrir/src/main/res/drawable/photo_tag_border.xml new file mode 100644 index 000000000..a47f31dc4 --- /dev/null +++ b/app_fenrir/src/main/res/drawable/photo_tag_border.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app_fenrir/src/main/res/layout/content_photo_page.xml b/app_fenrir/src/main/res/layout/content_photo_page.xml index 08d0fac7b..8ab9e1791 100644 --- a/app_fenrir/src/main/res/layout/content_photo_page.xml +++ b/app_fenrir/src/main/res/layout/content_photo_page.xml @@ -10,6 +10,13 @@ android:layout_height="match_parent" android:layout_centerInParent="true" /> + + + + + + + + diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/activity/photopager/PhotoPagerActivity.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/activity/photopager/PhotoPagerActivity.kt index 1b6c91930..b8b9a0e6e 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/activity/photopager/PhotoPagerActivity.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/activity/photopager/PhotoPagerActivity.kt @@ -563,12 +563,12 @@ class PhotoPagerActivity : BaseMvpActivity } mLoadingNow = true resolveProgressVisibility(true) + PicassoInstance.with().cancelRequest(photo) if (!image.isGif) { PicassoInstance.with() .load(image.photo_url) .into(photo, mPicassoLoadCallback) } else { - PicassoInstance.with().cancelRequest(photo) photo.fromAnimFile(Uri.parse(image.photo_url).toFile()) mLoadingNow = false resolveProgressVisibility(true) @@ -640,10 +640,12 @@ class PhotoPagerActivity : BaseMvpActivity return ret } + /* override fun onViewDetachedFromWindow(holder: PhotoViewHolder) { super.onViewDetachedFromWindow(holder) PicassoInstance.with().cancelRequest(holder.photo) } + */ override fun onBindViewHolder(holder: PhotoViewHolder, position: Int) { val photo = mPhotos[position] diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/view/TouchImageView.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/view/TouchImageView.kt index 382ba8a1a..25c0b9d81 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/view/TouchImageView.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/view/TouchImageView.kt @@ -20,6 +20,7 @@ import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.LinearInterpolator import android.widget.OverScroller import androidx.appcompat.widget.AppCompatImageView +import com.squareup.picasso3.Rotatable import dev.ragnarok.fenrir.module.FenrirNative import dev.ragnarok.fenrir.module.animation.AnimatedFileDrawable import dev.ragnarok.filegallery.R @@ -64,8 +65,8 @@ open class TouchImageView @JvmOverloads constructor( private var viewSizeChangeFixedPixel: FixedPixel? = FixedPixel.CENTER private var orientationJustChanged = false - internal enum class ImageActionState { - NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM + enum class ImageActionState { + NONE, DRAG, MOVE, CANT_MOVE, ZOOM, FLING, ANIMATE_ZOOM } private var imageActionState: ImageActionState? = null @@ -108,9 +109,16 @@ open class TouchImageView @JvmOverloads constructor( private var touchCoordinatesListener: OnTouchCoordinatesListener? = null private var doubleTapListener: OnDoubleTapListener? = null private var userTouchListener: OnTouchListener? = null + private var stateListener: StateListener? = null private var touchImageViewListener: OnTouchImageViewListener? = null private var animDrawable: AnimatedFileDrawable? = null + enum class OrientationLocked { + HORIZONTAL, VERTICAL + } + + var orientationLocked = OrientationLocked.HORIZONTAL + init { super.setClickable(true) orientation = resources.configuration.orientation @@ -148,6 +156,14 @@ open class TouchImageView @JvmOverloads constructor( isRotateImageToFitScreen = rotateImageToFitScreen } + interface StateListener { + fun onChangeState(imageActionState: ImageActionState, zoomed: Boolean) + } + + fun setOnStateChangeListener(onStateListener: StateListener) { + stateListener = onStateListener + } + override fun setOnTouchListener(onTouchListener: OnTouchListener?) { userTouchListener = onTouchListener } @@ -882,11 +898,11 @@ open class TouchImageView @JvmOverloads constructor( } internal fun setState(imageActionState: ImageActionState) { + if (this.imageActionState == imageActionState) { + return + } this.imageActionState = imageActionState - } - - fun canScrollHorizontallyFroyo(direction: Int): Boolean { - return canScrollHorizontally(direction) + stateListener?.onChangeState(imageActionState, isZoomed) } override fun canScrollHorizontally(direction: Int): Boolean { @@ -980,7 +996,7 @@ open class TouchImageView @JvmOverloads constructor( } gestureDetector.onTouchEvent(event) val curr = PointF(event.x, event.y) - if (imageActionState == ImageActionState.NONE || imageActionState == ImageActionState.DRAG || imageActionState == ImageActionState.FLING) { + if (imageActionState == ImageActionState.NONE || imageActionState == ImageActionState.DRAG || imageActionState == ImageActionState.MOVE || imageActionState == ImageActionState.CANT_MOVE || imageActionState == ImageActionState.FLING) { when (event.action) { MotionEvent.ACTION_DOWN -> { last.set(curr) @@ -988,9 +1004,29 @@ open class TouchImageView @JvmOverloads constructor( setState(ImageActionState.DRAG) } - MotionEvent.ACTION_MOVE -> if (imageActionState == ImageActionState.DRAG) { + MotionEvent.ACTION_MOVE -> if (imageActionState == ImageActionState.DRAG || imageActionState == ImageActionState.MOVE || imageActionState == ImageActionState.CANT_MOVE) { val deltaX = curr.x - last.x val deltaY = curr.y - last.y + if (orientationLocked == OrientationLocked.HORIZONTAL) { + if (deltaX < 1 && deltaX > -1 || abs(deltaX) < abs(deltaY) || canScrollHorizontally( + if (deltaX < 0) 1 else -1 + ) + ) { + setState(ImageActionState.MOVE) + } else { + setState(ImageActionState.CANT_MOVE) + } + } else { + if (deltaY < 1 && deltaY > -1 || abs(deltaY) < abs(deltaX) || canScrollVertically( + if (deltaY < 0) 1 else -1 + ) + ) { + setState(ImageActionState.MOVE) + } else { + setState(ImageActionState.CANT_MOVE) + } + } + val fixTransX = getFixDragTrans(deltaX, viewWidth.toFloat(), imageWidth) val fixTransY = getFixDragTrans(deltaY, viewHeight.toFloat(), imageHeight) touchMatrix.postTranslate(fixTransX, fixTransY) @@ -998,9 +1034,13 @@ open class TouchImageView @JvmOverloads constructor( last[curr.x] = curr.y } - MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> setState( - ImageActionState.NONE - ) + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_POINTER_UP -> { + if (imageActionState != ImageActionState.FLING) { + setState( + ImageActionState.NONE + ) + } + } } } @@ -1268,7 +1308,6 @@ open class TouchImageView @JvmOverloads constructor( } fun cancelFling() { - setState(ImageActionState.NONE) scroller.forceFinished(true) } @@ -1278,6 +1317,9 @@ open class TouchImageView @JvmOverloads constructor( // Listener runnable updated with each frame of fling animation. touchImageViewListener?.onMove() if (scroller.isFinished) { + if (imageActionState == ImageActionState.FLING) { + setState(ImageActionState.NONE) + } return } if (scroller.computeScrollOffset()) { @@ -1291,9 +1333,10 @@ open class TouchImageView @JvmOverloads constructor( fixTrans() imageMatrix = touchMatrix compatPostOnAnimation(this) + } else if (imageActionState == ImageActionState.FLING) { + setState(ImageActionState.NONE) } } - } private class CompatScroller(context: Context?) { @@ -1462,6 +1505,33 @@ open class TouchImageView @JvmOverloads constructor( } + class StateValues( + val viewWidth: Int, + val viewHeight: Int, + val bitmapWidth: Int, + val bitmapHeight: Int, + viewMatrix: Matrix + ) { + val matrix = FloatArray(9) + + init { + viewMatrix.getValues(matrix) + } + } + + fun getStateValues(): StateValues? { + if (drawable == null || drawable is Rotatable && (drawable as Rotatable).getRotation() != 0f) { + return null + } + return StateValues( + viewWidth, + viewHeight, + drawable.intrinsicWidth, + drawable.intrinsicHeight, + touchMatrix + ) + } + companion object { private const val STATE = "instanceState" diff --git a/build.gradle b/build.gradle index 3504a73dc..303dd39bb 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ buildscript { ext.autoValueVersion = "1.10.1" //common libraries - ext.kotlin_version = "1.8.20" + ext.kotlin_version = "1.8.21" ext.kotlin_coroutines = "1.7.0-RC" ext.kotlin_serializer = "1.5.0" ext.okhttpLibraryVersion = "5.0.0-alpha.11" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4172f742b..307ca87e4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Aug 17 12:17:04 MSK 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/libfenrir/src/main/java/com/github/luben/zstd/BaseZstdBufferDecompressingStreamNoFinalizer.java b/libfenrir/src/main/java/com/github/luben/zstd/BaseZstdBufferDecompressingStreamNoFinalizer.java index b45081579..5b9a20066 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/BaseZstdBufferDecompressingStreamNoFinalizer.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/BaseZstdBufferDecompressingStreamNoFinalizer.java @@ -10,7 +10,13 @@ public abstract class BaseZstdBufferDecompressingStreamNoFinalizer implements Cl protected boolean closed; private boolean finishedFrame; private boolean streamEnd; + /** + * This field is set by the native call to represent the number of bytes consumed from {@link #source} buffer. + */ private int consumed; + /** + * This field is set by the native call to represent the number of bytes produced into the target buffer. + */ private int produced; BaseZstdBufferDecompressingStreamNoFinalizer(ByteBuffer source) { @@ -27,6 +33,9 @@ protected ByteBuffer refill(ByteBuffer toRefill) { return toRefill; } + /** + * @return false if all data is processed and no more data is available from the {@link #source} + */ public boolean hasRemaining() { return !streamEnd && (source.hasRemaining() || !finishedFrame); } @@ -52,6 +61,15 @@ public BaseZstdBufferDecompressingStreamNoFinalizer setDict(ZstdDictDecompress d return this; } + /** + * Set the value of zstd parameter ZSTD_d_windowLogMax. + * + * @param windowLogMax window size in bytes + * @return this instance of {@link BaseZstdBufferDecompressingStreamNoFinalizer} + * @throws ZstdIOException if there is an error while setting the configuration natively. + * + * @see Zstd's ZSTD_d_windowLogMax parameter + */ public BaseZstdBufferDecompressingStreamNoFinalizer setLongMax(int windowLogMax) throws IOException { long size = Zstd.setDecompressionLongMax(stream, windowLogMax); if (Zstd.isError(size)) { @@ -106,6 +124,23 @@ public void close() { } } } + + /** + * Reads the content of the de-compressed stream into the target buffer. + *

This method will block until the chunk of compressed data stored in {@link #source} has been decompressed and + * written into the target buffer. After each execution, this method will refill the {@link #source} buffer, using + * {@link #refill(ByteBuffer)}. + *

To read the full stream of decompressed data, this method should be called in a loop while {@link #hasRemaining()} + * is true. + *

The target buffer will be written starting from {@link ByteBuffer#position()}. The {@link ByteBuffer#position()} + * of source and the target buffers will be modified to represent the data read and written respectively. + * + * @param target buffer to store the read bytes from uncompressed stream. + * @return the number of bytes read into the target buffer. + * @throws ZstdIOException if an error occurs while reading. + * @throws IllegalArgumentException if provided source or target buffers are incorrectly configured. + * @throws IOException if the stream is closed before reading. + */ public abstract int read(ByteBuffer target) throws IOException; abstract long createDStream(); 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 5e37aaa24..21e49eb18 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/Zstd.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/Zstd.java @@ -544,7 +544,7 @@ public static long decompressedSize(byte[] src, int srcPosition, int srcSize, bo public static long decompressedSize(byte[] src, int srcPosition, int srcSize) { return decompressedSize(src, srcPosition, srcSize, false); } - + /** * Return the original size of a compressed buffer (if known) * @@ -579,7 +579,7 @@ public static long decompressedSize(byte[] src) { * 0 if the original size is not known */ public static native long decompressedDirectByteBufferSize(ByteBuffer src, int srcPosition, int srcSize, boolean magicless); - + /** * Return the original size of a compressed buffer (if known) * @@ -694,7 +694,7 @@ public static long decompressedDirectByteBufferSize(ByteBuffer src, int srcPosit * @return DictId or 0 if not available */ public static long getDictIdFromDictDirect(ByteBuffer dict) { - int length = dict.limit() - dict.position(); + int length = dict.limit() - dict.position(); if (!dict.isDirect()) { throw new IllegalArgumentException("dict must be a direct buffer"); } diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdBufferDecompressingStreamNoFinalizer.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdBufferDecompressingStreamNoFinalizer.java index 7001b097d..d74de4433 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/ZstdBufferDecompressingStreamNoFinalizer.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdBufferDecompressingStreamNoFinalizer.java @@ -9,8 +9,8 @@ public ZstdBufferDecompressingStreamNoFinalizer(ByteBuffer source) { if (source.isDirect()) { throw new IllegalArgumentException("Source buffer should be a non-direct buffer"); } - stream = createDStreamNative(); - initDStreamNative(stream); + stream = createDStream(); + initDStream(stream); } @Override @@ -38,8 +38,14 @@ long initDStream(long stream) { @Override long decompressStream(long stream, ByteBuffer dst, int dstOffset, int dstSize, ByteBuffer src, int srcOffset, int srcSize) { - byte[] targetArr = Zstd.extractArray(dst); - byte[] sourceArr = Zstd.extractArray(source); + if (!src.hasArray()) { + throw new IllegalArgumentException("provided source ByteBuffer lacks array"); + } + if (!dst.hasArray()) { + throw new IllegalArgumentException("provided destination ByteBuffer lacks array"); + } + byte[] targetArr = dst.array(); + byte[] sourceArr = src.array(); return decompressStreamNative(stream, targetArr, dstOffset, dstSize, sourceArr, srcOffset, srcSize); } diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferDecompressingStreamNoFinalizer.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferDecompressingStreamNoFinalizer.java index 2e63c2fa0..db2a0ac03 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferDecompressingStreamNoFinalizer.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdDirectBufferDecompressingStreamNoFinalizer.java @@ -10,8 +10,8 @@ public ZstdDirectBufferDecompressingStreamNoFinalizer(ByteBuffer source) { throw new IllegalArgumentException("Source buffer should be a direct buffer"); } this.source = source; - stream = createDStreamNative(); - initDStreamNative(stream); + stream = createDStream(); + initDStream(stream); } @Override diff --git a/libfenrir/src/main/java/com/github/luben/zstd/ZstdInputStreamNoFinalizer.java b/libfenrir/src/main/java/com/github/luben/zstd/ZstdInputStreamNoFinalizer.java index bc5d1d988..74ac1f113 100644 --- a/libfenrir/src/main/java/com/github/luben/zstd/ZstdInputStreamNoFinalizer.java +++ b/libfenrir/src/main/java/com/github/luben/zstd/ZstdInputStreamNoFinalizer.java @@ -133,7 +133,7 @@ int readInternal(byte[] dst, int offset, int len) throws IOException { throw new IOException("Stream closed"); } - // guard agains buffer overflows + // guard against buffer overflows if (offset < 0 || len > dst.length - offset) { throw new IndexOutOfBoundsException("Requested length " + len + " from offset " + offset + " in buffer of size " + dst.length); diff --git a/libfenrir/src/main/java/com/google/zxing/aztec/encoder/HighLevelEncoder.java b/libfenrir/src/main/java/com/google/zxing/aztec/encoder/HighLevelEncoder.java index 1d0cd4b32..2f4325b74 100644 --- a/libfenrir/src/main/java/com/google/zxing/aztec/encoder/HighLevelEncoder.java +++ b/libfenrir/src/main/java/com/google/zxing/aztec/encoder/HighLevelEncoder.java @@ -207,12 +207,7 @@ public BitArray encode() { } } // We are left with a set of states. Find the shortest one. - State minState = Collections.min(states, new Comparator() { - @Override - public int compare(State a, State b) { - return a.getBitCount() - b.getBitCount(); - } - }); + State minState = Collections.min(states, (a, b) -> a.getBitCount() - b.getBitCount()); // Convert it to a bit array, and return. return minState.toBitArray(text); } diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_common.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_common.cc index afccdb8d6..74c9be87e 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_common.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_common.cc @@ -429,12 +429,12 @@ void ARGBToARGB4444Row_C(const uint8_t* src_argb, uint8_t* dst_rgb, int width) { void ABGRToAR30Row_C(const uint8_t* src_abgr, uint8_t* dst_ar30, int width) { int x; for (x = 0; x < width; ++x) { - uint32_t b0 = (src_abgr[0] >> 6) | ((uint32_t)(src_abgr[0]) << 2); + uint32_t r0 = (src_abgr[0] >> 6) | ((uint32_t)(src_abgr[0]) << 2); uint32_t g0 = (src_abgr[1] >> 6) | ((uint32_t)(src_abgr[1]) << 2); - uint32_t r0 = (src_abgr[2] >> 6) | ((uint32_t)(src_abgr[2]) << 2); + uint32_t b0 = (src_abgr[2] >> 6) | ((uint32_t)(src_abgr[2]) << 2); uint32_t a0 = (src_abgr[3] >> 6); *(uint32_t*)(dst_ar30) = - STATIC_CAST(uint32_t, r0 | (g0 << 10) | (b0 << 20) | (a0 << 30)); + STATIC_CAST(uint32_t, b0 | (g0 << 10) | (r0 << 20) | (a0 << 30)); dst_ar30 += 4; src_abgr += 4; } diff --git a/libfenrir/src/main/jni/thorvg/inc/thorvg.h b/libfenrir/src/main/jni/thorvg/inc/thorvg.h index 9315f19fd..c2fcc8023 100644 --- a/libfenrir/src/main/jni/thorvg/inc/thorvg.h +++ b/libfenrir/src/main/jni/thorvg/inc/thorvg.h @@ -19,17 +19,32 @@ #include #include -#ifdef TVG_BUILD - #if defined(_WIN32) && !defined(__clang__) - #define TVG_EXPORT __declspec(dllexport) - #define TVG_DEPRECATED __declspec(deprecated) +#ifdef TVG_API + #undef TVG_API +#endif + +#if defined(_WIN32) && !defined(__clang__) + #if TVG_BUILD + #if TVG_EXPORT + #define TVG_API __declspec(dllexport) + #else + #define TVG_API + #endif #else - #define TVG_EXPORT __attribute__ ((visibility ("default"))) - #define TVG_DEPRECATED __attribute__ ((__deprecated__)) + #define TVG_API __declspec(dllimport) #endif + #define TVG_DEPRECATED __declspec(deprecated) #else - #define TVG_EXPORT - #define TVG_DEPRECATED + #if TVG_BUILD + #if TVG_EXPORT + #define TVG_API __attribute__ ((visibility ("default"))) + #else + #define TVG_API + #endif + #else + #define TVG_API + #endif + #define TVG_DEPRECATED __attribute__ ((__deprecated__)) #endif #ifdef __cplusplus @@ -222,7 +237,7 @@ struct Polygon * Paint represents such a graphical object and its behaviors such as duplication, transformation and composition. * TVG recommends the user to regard a paint as a set of volatile commands. They can prepare a Paint and then request a Canvas to run them. */ -class TVG_EXPORT Paint +class TVG_API Paint { public: virtual ~Paint(); @@ -390,7 +405,7 @@ class TVG_EXPORT Paint * It specifies the gradient behavior in case the area defined by the gradient bounds * is smaller than the area to be filled. */ -class TVG_EXPORT Fill +class TVG_API Fill { public: /** @@ -494,7 +509,7 @@ class TVG_EXPORT Fill * @note A Canvas behavior depends on the raster engine though the final content of the buffer is expected to be identical. * @warning The Paint objects belonging to one Canvas can't be shared among multiple Canvases. */ -class TVG_EXPORT Canvas +class TVG_API Canvas { public: Canvas(RenderMethod*); @@ -590,7 +605,7 @@ class TVG_EXPORT Canvas * Besides the APIs inherited from the Fill class, it enables setting and getting the linear gradient bounds. * The behavior outside the gradient bounds depends on the value specified in the spread API. */ -class TVG_EXPORT LinearGradient final : public Fill +class TVG_API LinearGradient final : public Fill { public: ~LinearGradient(); @@ -655,7 +670,7 @@ class TVG_EXPORT LinearGradient final : public Fill * @brief A class representing the radial gradient fill of the Shape object. * */ -class TVG_EXPORT RadialGradient final : public Fill +class TVG_API RadialGradient final : public Fill { public: ~RadialGradient(); @@ -718,7 +733,7 @@ class TVG_EXPORT RadialGradient final : public Fill * The stroke of Shape is an optional property in case the Shape needs to be represented with/without the outline borders. * It's efficient since the shape path and the stroking path can be shared with each other. It's also convenient when controlling both in one context. */ -class TVG_EXPORT Shape final : public Paint +class TVG_API Shape final : public Paint { public: ~Shape(); @@ -1110,7 +1125,7 @@ class TVG_EXPORT Shape final : public Paint * * @note Supported formats are depended on the available TVG loaders. */ -class TVG_EXPORT Picture final : public Paint +class TVG_API Picture final : public Paint { public: ~Picture(); @@ -1224,15 +1239,15 @@ class TVG_EXPORT Picture final : public Paint * @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh. * @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh. * - * @retval Result::Success When succeed. - * @retval Result::Unknown If fails + * @return Result::Success When succeed. + * @return Result::Unknown If fails * * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect. * @warning Please do not use it, this API is not official one. It could be modified in the next version. * * @BETA_API */ - Result mesh(const Polygon* triangles, const uint32_t triangleCnt) noexcept; + Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept; /** * @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh. @@ -1288,7 +1303,7 @@ class TVG_EXPORT Picture final : public Paint * As a group, the scene can be transformed, made translucent and composited with other target paints, * its children will be affected by the scene world. */ -class TVG_EXPORT Scene final : public Paint +class TVG_API Scene final : public Paint { public: ~Scene(); @@ -1360,7 +1375,7 @@ class TVG_EXPORT Scene final : public Paint * * @brief A class for the rendering graphical elements with a software raster engine. */ -class TVG_EXPORT SwCanvas final : public Canvas +class TVG_API SwCanvas final : public Canvas { public: ~SwCanvas(); @@ -1451,7 +1466,7 @@ class TVG_EXPORT SwCanvas final : public Canvas * * @BETA_API */ -class TVG_EXPORT GlCanvas final : public Canvas +class TVG_API GlCanvas final : public Canvas { public: ~GlCanvas(); @@ -1483,7 +1498,7 @@ class TVG_EXPORT GlCanvas final : public Canvas * * @brief A class that enables initialization and termination of the TVG engines. */ -class TVG_EXPORT Initializer final +class TVG_API Initializer final { public: /** @@ -1545,7 +1560,7 @@ class TVG_EXPORT Initializer final * * @since 0.5 */ -class TVG_EXPORT Saver final +class TVG_API Saver final { public: ~Saver(); @@ -1615,7 +1630,7 @@ class TVG_EXPORT Saver final * * @BETA_API */ -class TVG_EXPORT Accessor final +class TVG_API Accessor final { public: ~Accessor(); diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwCommon.h b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwCommon.h index db22a57e0..3e26990a5 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwCommon.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwCommon.h @@ -322,7 +322,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); void strokeFree(SwStroke* stroke); -bool imagePrepare(SwImage* image, Polygon* triangles, uint32_t triangleCount, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); +bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage* image); @@ -353,8 +353,7 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx); bool rasterCompositor(SwSurface* surface); bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id); bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); -bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); -bool rasterImageMesh(SwSurface* surface, SwImage* image, const Polygon* triangles, const uint32_t triangleCount, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); bool rasterClear(SwSurface* surface); diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwImage.cpp index fdb78f062..9e215dbef 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwImage.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwImage.cpp @@ -34,7 +34,7 @@ static inline bool _onlyShifted(const Matrix* m) } -static bool _genOutline(SwImage* image, Polygon* triangles, uint32_t triangleCount, const Matrix* transform, SwMpool* mpool, unsigned tid) +static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid) { image->outline = mpoolReqOutline(mpool, tid); auto outline = image->outline; @@ -53,7 +53,7 @@ static bool _genOutline(SwImage* image, Polygon* triangles, uint32_t triangleCou } Point to[4]; - if (triangleCount > 0) { + if (mesh->triangleCnt > 0) { // TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple // places. We should be able to re-use one we have already done? Also see: // tvgPictureImpl.h --> bounds @@ -63,10 +63,11 @@ static bool _genOutline(SwImage* image, Polygon* triangles, uint32_t triangleCou // i.e. copy tvgSwShape.capp -> _genOutline? // // TODO: Cntrs? - Point min = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; - Point max = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; + auto triangles = mesh->triangles; + auto min = triangles[0].vertex[0].pt; + auto max = triangles[0].vertex[0].pt; - for (uint32_t i = 0; i < triangleCount; ++i) { + for (uint32_t i = 0; i < mesh->triangleCnt; ++i) { if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; @@ -82,7 +83,6 @@ static bool _genOutline(SwImage* image, Polygon* triangles, uint32_t triangleCou if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y; else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y; } - to[0] = {min.x, min.y}; to[1] = {max.x, min.y}; to[2] = {max.x, max.y}; @@ -95,6 +95,7 @@ static bool _genOutline(SwImage* image, Polygon* triangles, uint32_t triangleCou to[2] = {w, h}; to[3] = {0, h}; } + for (int i = 0; i < 4; i++) { outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform); outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT; @@ -118,7 +119,7 @@ static bool _genOutline(SwImage* image, Polygon* triangles, uint32_t triangleCou /* External Class Implementation */ /************************************************************************/ -bool imagePrepare(SwImage* image, Polygon* triangles, uint32_t triangleCount, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) +bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) { image->direct = _onlyShifted(transform); @@ -136,7 +137,7 @@ bool imagePrepare(SwImage* image, Polygon* triangles, uint32_t triangleCount, co else image->scaled = false; } - if (!_genOutline(image, triangles, triangleCount, transform, mpool, tid)) return false; + if (!_genOutline(image, mesh, transform, mpool, tid)) return false; return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct); } diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp index a87b3fd17..05ff9ddf0 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp @@ -60,16 +60,16 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx) SwMpool* mpoolInit(unsigned threads) { - if (threads == 0) threads = 1; + auto allocSize = threads + 1; auto mpool = static_cast(calloc(sizeof(SwMpool), 1)); - mpool->outline = static_cast(calloc(1, sizeof(SwOutline) * threads)); + mpool->outline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); if (!mpool->outline) goto err; - mpool->strokeOutline = static_cast(calloc(1, sizeof(SwOutline) * threads)); + mpool->strokeOutline = static_cast(calloc(1, sizeof(SwOutline) * allocSize)); if (!mpool->strokeOutline) goto err; - mpool->allocSize = threads; + mpool->allocSize = allocSize; return mpool; diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRaster.cpp index 68f20d156..c49a05953 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRaster.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRaster.cpp @@ -687,18 +687,18 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons return false; } -static bool _transformedRGBAImageMesh(SwSurface* surface, const SwImage* image, const Polygon* triangles, const uint32_t count, const Matrix* transform, const SwBBox* region, uint32_t opacity) +static bool _transformedRGBAImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity) { if (_compositing(surface)) { if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, _alpha); + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, _ialpha); + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, surface->blender.lumaValue); + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, surface->blender.lumaValue); } } else { - return _rasterTexmapPolygonMesh(surface, image, triangles, count, transform, region, opacity, nullptr); + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, nullptr); } return false; } @@ -1561,19 +1561,7 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint } -bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) -{ - //Verify Boundary - if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast(surface->w) || bbox.min.y >= static_cast(surface->h)) return false; - - //TOOD: switch (image->format) - //TODO: case: _rasterRGBImage() - //TODO: case: _rasterGrayscaleImage() - //TODO: case: _rasterAlphaImage() - return _rasterRGBAImage(surface, image, transform, bbox, opacity); -} - -bool rasterImageMesh(SwSurface* surface, SwImage* image, const Polygon* triangles, const uint32_t count, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) { //Verify Boundary if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast(surface->w) || bbox.min.y >= static_cast(surface->h)) return false; @@ -1582,5 +1570,6 @@ bool rasterImageMesh(SwSurface* surface, SwImage* image, const Polygon* triangle //TODO: case: _rasterRGBImageMesh() //TODO: case: _rasterGrayscaleImageMesh() //TODO: case: _rasterAlphaImageMesh() - return _transformedRGBAImageMesh(surface, image, triangles, count, transform, &bbox, opacity); + if (mesh && mesh->triangleCnt > 0) return _transformedRGBAImageMesh(surface, image, mesh, transform, &bbox, opacity); + else return _rasterRGBAImage(surface, image, transform, bbox, opacity); } \ No newline at end of file diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h index b01c8c358..dec7395b2 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -602,16 +602,16 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Should provide two Polygons, one for each triangle. // TODO: region? */ -static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const Polygon* triangles, const uint32_t triangleCount, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) +static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t)) { //Exceptions: No dedicated drawing area? if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; // Step polygons once to transform - auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * triangleCount); + auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt); float ys = FLT_MAX, ye = -1.0f; - for (uint32_t i = 0; i < triangleCount; i++) { - transformedTris[i] = triangles[i]; + for (uint32_t i = 0; i < mesh->triangleCnt; i++) { + transformedTris[i] = mesh->triangles[i]; mathMultiply(&transformedTris[i].vertex[0].pt, transform); mathMultiply(&transformedTris[i].vertex[1].pt, transform); mathMultiply(&transformedTris[i].vertex[2].pt, transform); @@ -635,7 +635,7 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c // Get AA spans and step polygons again to draw auto aaSpans = _AASpans(ys, ye, image, region); if (aaSpans) { - for (uint32_t i = 0; i < triangleCount; i++) { + for (uint32_t i = 0; i < mesh->triangleCnt; i++) { _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blendMethod, aaSpans); } // Apply to surface (note: frees the AA spans) diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp index fddcd31a0..d69b21561 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp @@ -258,8 +258,7 @@ struct SwSceneTask : SwTask struct SwImageTask : SwTask { SwImage image; - Polygon* triangles; - uint32_t triangleCnt; + const RenderMesh* mesh = nullptr; //Should be valid ptr in action bool clip(SwRleData* target) override { @@ -282,10 +281,10 @@ struct SwImageTask : SwTask imageReset(&image); if (!image.data || image.w == 0 || image.h == 0) goto end; - if (!imagePrepare(&image, triangles, triangleCnt, transform, clipRegion, bbox, mpool, tid)) goto end; + if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end; // TODO: How do we clip the triangle mesh? Only clip non-meshed images for now - if (triangleCnt == 0 && clips.count > 0) { + if (mesh->triangleCnt == 0 && clips.count > 0) { if (!imageGenRle(&image, bbox, false)) goto end; if (image.rle) { for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { @@ -469,18 +468,7 @@ bool SwRenderer::renderImage(RenderData data) if (task->opacity == 0) return true; - return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity); -} - - -bool SwRenderer::renderImageMesh(RenderData data) -{ - auto task = static_cast(data); - task->done(); - - if (task->opacity == 0) return true; - - return rasterImageMesh(surface, &task->image, task->triangles, task->triangleCnt, task->transform, task->bbox, task->opacity); + return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity); } @@ -662,7 +650,7 @@ bool SwRenderer::endComposite(Compositor* cmp) //Default is alpha blending if (p->method == CompositeMethod::None) { - return rasterImage(surface, &p->image, nullptr, p->bbox, p->opacity); + return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity); } return true; @@ -721,7 +709,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, } -RenderData SwRenderer::prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) +RenderData SwRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) { //prepare task auto task = static_cast(data); @@ -731,8 +719,7 @@ RenderData SwRenderer::prepare(Surface* image, Polygon* triangles, uint32_t tria task->image.w = image->w; task->image.h = image->h; task->image.stride = image->stride; - task->triangles = triangles; - task->triangleCnt = triangleCnt; + task->mesh = mesh; } return prepareCommon(task, transform, opacity, clips, flags); } diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.h b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.h index 5ceb1a1c5..6eff37823 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRenderer.h @@ -38,11 +38,10 @@ class SwRenderer : public RenderMethod public: RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; - RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; + RenderData prepare(Surface* image, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; - bool renderImageMesh(RenderData data) override; bool postRender() override; bool dispose(RenderData data) override; RenderRegion region(RenderData data) override; diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRle.cpp index 52c831ef3..a0c36b1c9 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRle.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRle.cpp @@ -773,7 +773,7 @@ static int _genRle(RleWorker& rw) } -static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans) +static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt) { auto out = outSpans; auto spans = target->spans; @@ -794,7 +794,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar //Try clipping with all clip spans which have a same y coordinate. auto temp = clipSpans; - while(temp->y == clipSpans->y) { + while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) { auto sx1 = spans->x; auto sx2 = sx1 + spans->len; auto cx1 = temp->x; @@ -815,6 +815,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar out->len = len; out->coverage = (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8); ++out; + --outSpansCnt; } ++temp; } @@ -824,7 +825,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar } -static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt) +static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt) { auto out = outSpans; auto spans = targetRle->spans; @@ -834,7 +835,7 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl auto maxx = minx + static_cast(bbox->max.x - bbox->min.x) - 1; auto maxy = miny + static_cast(bbox->max.y - bbox->min.y) - 1; - while (spanCnt && spans < end) { + while (outSpansCnt > 0 && spans < end) { if (spans->y > maxy) { spans = end; break; @@ -851,13 +852,13 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl out->x = spans->x; out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1); } - if (out->len != 0) { + if (out->len > 0) { out->y = spans->y; out->coverage = spans->coverage; ++out; + --outSpansCnt; } ++spans; - --spanCnt; } return out; } @@ -1120,7 +1121,7 @@ void rleClipPath(SwRleData *rle, const SwRleData *clip) if (rle->size == 0 || clip->size == 0) return; auto spanCnt = rle->size > clip->size ? rle->size : clip->size; auto spans = static_cast(malloc(sizeof(SwSpan) * (spanCnt))); - auto spansEnd = _intersectSpansRegion(clip, rle, spans); + auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt); _replaceClipSpan(rle, spans, spansEnd - spans); diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgCommon.h b/libfenrir/src/main/jni/thorvg/src/lib/tvgCommon.h index 9c3888a5c..d7bd58410 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgCommon.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgCommon.h @@ -71,8 +71,8 @@ enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown }; constexpr auto LogBgColor = "\033[42m"; //bg green constexpr auto GreyColor = "\033[90m"; //grey constexpr auto ResetColors = "\033[0m"; //default - #define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s l.%d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) - #define TVGLOG(tag, fmt, ...) fprintf(stderr, "%s[L]%s %s" tag "%s (%s l.%d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) + #define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) + #define TVGLOG(tag, fmt, ...) fprintf(stderr, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__) #else #define TVGERR(...) #define TVGLOG(...) diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgInitializer.cpp b/libfenrir/src/main/jni/thorvg/src/lib/tvgInitializer.cpp index a1e930760..b7326a9fb 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgInitializer.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgInitializer.cpp @@ -44,6 +44,11 @@ static int _initCnt = 0; static uint16_t _version = 0; +//enum class operation helper +static constexpr bool operator &(CanvasEngine a, CanvasEngine b) +{ + return int(a) & int(b); +} static bool _buildVersionInfo() { @@ -87,12 +92,12 @@ Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept { auto nonSupport = true; - if (static_cast(engine) & static_cast(CanvasEngine::Sw)) { + if (engine & CanvasEngine::Sw) { #ifdef THORVG_SW_RASTER_SUPPORT if (!SwRenderer::init(threads)) return Result::FailedAllocation; nonSupport = false; #endif - } else if (static_cast(engine) & static_cast(CanvasEngine::Gl)) { + } else if (engine & CanvasEngine::Gl) { #ifdef THORVG_GL_RASTER_SUPPORT if (!GlRenderer::init(threads)) return Result::FailedAllocation; nonSupport = false; @@ -121,12 +126,12 @@ Result Initializer::term(CanvasEngine engine) noexcept auto nonSupport = true; - if (static_cast(engine) & static_cast(CanvasEngine::Sw)) { + if (engine & CanvasEngine::Sw) { #ifdef THORVG_SW_RASTER_SUPPORT if (!SwRenderer::term()) return Result::InsufficientCondition; nonSupport = false; #endif - } else if (static_cast(engine) & static_cast(CanvasEngine::Gl)) { + } else if (engine & CanvasEngine::Gl) { #ifdef THORVG_GL_RASTER_SUPPORT if (!GlRenderer::term()) return Result::InsufficientCondition; nonSupport = false; diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgPicture.cpp b/libfenrir/src/main/jni/thorvg/src/lib/tvgPicture.cpp index 2b1d0f54c..98ffe9017 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgPicture.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgPicture.cpp @@ -121,7 +121,7 @@ const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept } -Result Picture::mesh(const Polygon* triangles, const uint32_t triangleCnt) noexcept +Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept { if (!triangles && triangleCnt > 0) return Result::InvalidArguments; if (triangles && triangleCnt == 0) return Result::InvalidArguments; @@ -133,6 +133,6 @@ Result Picture::mesh(const Polygon* triangles, const uint32_t triangleCnt) noexc uint32_t Picture::mesh(const Polygon** triangles) const noexcept { - if (triangles) *triangles = pImpl->triangles; - return pImpl->triangleCnt; + if (triangles) *triangles = pImpl->rm.triangles; + return pImpl->rm.triangleCnt; } diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgPictureImpl.h b/libfenrir/src/main/jni/thorvg/src/lib/tvgPictureImpl.h index 3d2abdd86..92bb0baf3 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgPictureImpl.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgPictureImpl.h @@ -64,17 +64,15 @@ struct Picture::Impl Paint* paint = nullptr; //vector picture uses Surface* surface = nullptr; //bitmap picture uses - Polygon* triangles = nullptr; //mesh data - uint32_t triangleCnt = 0; //mesh triangle count RenderData rd = nullptr; //engine data float w = 0, h = 0; uint32_t rendererColorSpace = 0; + RenderMesh rm; //mesh data bool resizing = false; ~Impl() { if (paint) delete(paint); - free(triangles); free(surface); } @@ -136,7 +134,7 @@ struct Picture::Impl if (surface) { auto transform = resizeTransform(pTransform); - rd = renderer.prepare(surface, triangles, triangleCnt, rd, &transform, opacity, clips, static_cast(pFlag | flag)); + rd = renderer.prepare(surface, &rm, rd, &transform, opacity, clips, static_cast(pFlag | flag)); } else if (paint) { if (resizing) { loader->resize(paint, w, h); @@ -149,10 +147,7 @@ struct Picture::Impl bool render(RenderMethod &renderer) { - if (surface) { - if (triangles) return renderer.renderImageMesh(rd); - else return renderer.renderImage(rd); - } + if (surface) return renderer.renderImage(rd); else if (paint) return paint->pImpl->render(renderer); return false; } @@ -177,11 +172,12 @@ struct Picture::Impl bool bounds(float* x, float* y, float* w, float* h) { - if (triangleCnt > 0) { - Point min = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; - Point max = { triangles[0].vertex[0].pt.x, triangles[0].vertex[0].pt.y }; + if (rm.triangleCnt > 0) { + auto triangles = rm.triangles; + auto min = triangles[0].vertex[0].pt; + auto max = triangles[0].vertex[0].pt; - for (uint32_t i = 0; i < triangleCnt; ++i) { + for (uint32_t i = 0; i < rm.triangleCnt; ++i) { if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x; else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x; if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y; @@ -259,13 +255,13 @@ struct Picture::Impl void mesh(const Polygon* triangles, const uint32_t triangleCnt) { if (triangles && triangleCnt > 0) { - this->triangleCnt = triangleCnt; - this->triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt); - memcpy(this->triangles, triangles, sizeof(Polygon) * triangleCnt); + this->rm.triangleCnt = triangleCnt; + this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt); + memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt); } else { - free(this->triangles); - this->triangles = nullptr; - this->triangleCnt = 0; + free(this->rm.triangles); + this->rm.triangles = nullptr; + this->rm.triangleCnt = 0; } } @@ -287,10 +283,10 @@ struct Picture::Impl dup->h = h; dup->resizing = resizing; - if (triangleCnt > 0) { - dup->triangleCnt = triangleCnt; - dup->triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt); - memcpy(dup->triangles, triangles, sizeof(Polygon) * triangleCnt); + if (rm.triangleCnt > 0) { + dup->rm.triangleCnt = rm.triangleCnt; + dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt); + memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt); } return ret.release(); diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgRender.h b/libfenrir/src/main/jni/thorvg/src/lib/tvgRender.h index 56a97a858..b113e091a 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgRender.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgRender.h @@ -48,6 +48,17 @@ struct Compositor uint32_t opacity; }; +struct RenderMesh +{ + Polygon* triangles = nullptr; + uint32_t triangleCnt = 0; + + ~RenderMesh() + { + free(triangles); + } +}; + struct RenderRegion { int32_t x, y, w, h; @@ -188,11 +199,10 @@ class RenderMethod virtual ~RenderMethod() {} virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) = 0; virtual RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; - virtual RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(Surface* image, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0; - virtual bool renderImageMesh(RenderData data) = 0; virtual bool postRender() = 0; virtual bool dispose(RenderData data) = 0; virtual RenderRegion region(RenderData data) = 0; diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.cpp b/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.cpp index 6b4b93c5d..019468083 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.cpp +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.cpp @@ -105,7 +105,7 @@ struct TaskSchedulerImpl uint32_t threadCnt; vector threads; vector taskQueues; - uint32_t idx = 0; + atomic idx{0}; TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt) { @@ -135,7 +135,7 @@ struct TaskSchedulerImpl } if (!success && !taskQueues[i].pop(&task)) break; - (*task)(i); + (*task)(i + 1); } } diff --git a/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.h b/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.h index 36c494c9e..cad887513 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/tvgTaskScheduler.h @@ -43,56 +43,83 @@ struct TaskScheduler struct Task { private: - mutex mtx; - bool finished = true; - bool running = false; + mutex finishedMtx; + mutex preparedMtx; + condition_variable cv; + bool finished = true; //if run() finished + bool prepared = false; //the task is requested public: - virtual ~Task() = default; + virtual ~Task() + { + if (!prepared) return; + + //Guarantee the task is finished by TaskScheduler. + unique_lock lock(preparedMtx); + + while (prepared) { + cv.wait(lock); + } + } void done(unsigned tid = 0) { if (finished) return; - unique_lock lock(mtx); + lock_guard lock(finishedMtx); if (finished) return; //the job hasn't been launched yet. - running = true; - run(tid); - running = false; + + //set finished so that operator() quickly returns. finished = true; + + run(tid); } protected: virtual void run(unsigned tid) = 0; private: + void finish() + { + lock_guard lock(preparedMtx); + prepared = false; + cv.notify_one(); + } + void operator()(unsigned tid) { - if (finished || running) return; + if (finished) { + finish(); + return; + } - lock_guard lock(mtx); + lock_guard lock(finishedMtx); - if (finished || running) return; + if (finished) { + finish(); + return; + } - running = true; run(tid); - running = false; + finished = true; + + finish(); } void prepare() { finished = false; + prepared = true; } friend struct TaskSchedulerImpl; }; - - } #endif //_TVG_TASK_SCHEDULER_H_ + \ No newline at end of file diff --git a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp index 6c1679c17..694e6d1eb 100644 --- a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp @@ -32,13 +32,13 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) { if (from == nullptr) return; //Copy the properties of 'from' only if they were explicitly set (not the default ones). - if (from->curColorSet && !((int)to->flags & (int)SvgStyleFlags::Color)) { + if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) { to->color = from->color; to->curColorSet = true; - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color); + to->flags = (to->flags | SvgStyleFlags::Color); } //Fill - if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Fill)) { + if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) { to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; @@ -46,21 +46,21 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) if (to->fill.paint.url) free(to->fill.paint.url); to->fill.paint.url = strdup(from->fill.paint.url); } - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill); + to->fill.flags = (to->fill.flags | SvgFillFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Fill); } - if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) { + if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) { to->fill.opacity = from->fill.opacity; - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Opacity); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillOpacity); + to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::FillOpacity); } - if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) { + if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) { to->fill.fillRule = from->fill.fillRule; - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::FillRule); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule); + to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule); + to->flags = (to->flags | SvgStyleFlags::FillRule); } //Stroke - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Stroke)) { + if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) { to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; @@ -68,45 +68,45 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) if (to->stroke.paint.url) free(to->stroke.paint.url); to->stroke.paint.url = strdup(from->stroke.paint.url); } - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint); + to->flags = (to->flags | SvgStyleFlags::Stroke); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) { + if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) { to->stroke.opacity = from->stroke.opacity; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Opacity); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeOpacity); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::StrokeOpacity); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) { + if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) { to->stroke.width = from->stroke.width; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Width); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width); + to->flags = (to->flags | SvgStyleFlags::StrokeWidth); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) { + if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) { if (from->stroke.dash.array.count > 0) { to->stroke.dash.array.clear(); to->stroke.dash.array.reserve(from->stroke.dash.array.count); for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) { to->stroke.dash.array.push(from->stroke.dash.array.data[i]); } - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeDashArray); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash); + to->flags = (to->flags | SvgStyleFlags::StrokeDashArray); } } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) { + if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) { to->stroke.cap = from->stroke.cap; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Cap); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineCap); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap); + to->flags = (to->flags | SvgStyleFlags::StrokeLineCap); } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) { + if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) { to->stroke.join = from->stroke.join; - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Join); - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin); + to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join); + to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin); } //Opacity //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity' - if (from->opacity < 255 && !((int)to->flags & (int)SvgStyleFlags::Opacity)) { + if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) { to->opacity = from->opacity; - to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity); + to->flags = (to->flags | SvgStyleFlags::Opacity); } } @@ -118,11 +118,11 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) void cssCopyStyleAttr(SvgNode* to, const SvgNode* from) { //Copy matrix attribute - if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) { + if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) { to->transform = (Matrix*)malloc(sizeof(Matrix)); if (to->transform) { *to->transform = *from->transform; - to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)SvgStyleFlags::Transform); + to->style->flags = (to->style->flags | SvgStyleFlags::Transform); } } //Copy style attribute @@ -155,12 +155,12 @@ SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType t SvgNode* cssFindStyleNode(const SvgNode* style, const char* title) { - if (!style) return nullptr; + if (!style || !title) return nullptr; auto child = style->child.data; for (uint32_t i = 0; i < style->child.count; ++i, ++child) { if ((*child)->type == SvgNodeType::CssStyle) { - if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child); + if ((*child)->id && !strcmp((*child)->id, title)) return (*child); } } return nullptr; @@ -175,9 +175,6 @@ void cssUpdateStyle(SvgNode* doc, SvgNode* style) if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) { cssCopyStyleAttr(*child, cssNode); } - if (auto cssNode = cssFindStyleNode(style, nullptr)) { - cssCopyStyleAttr(*child, cssNode); - } cssUpdateStyle(*child, style); } } diff --git a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoader.cpp index 8a6229f47..0eee115a3 100644 --- a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -864,16 +864,16 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) if (!strcmp(key, "width")) { doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); - doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Width); + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width); } else if (!strcmp(key, "height")) { doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical); - doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Height); + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height); } else if (!strcmp(key, "viewBox")) { if (_parseNumber(&value, &doc->vx)) { if (_parseNumber(&value, &doc->vy)) { if (_parseNumber(&value, &doc->vw)) { if (_parseNumber(&value, &doc->vh)) { - doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Viewbox); + doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox); loader->svgParse->global.h = doc->vh; } loader->svgParse->global.w = doc->vw; @@ -882,11 +882,11 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value) } loader->svgParse->global.x = doc->vx; } - if (((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) { + if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) { doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox); TVGLOG("SVG", "Negative values of the width and/or height - the attribute invalidated."); } - if (!((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox)) { + if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { loader->svgParse->global.x = loader->svgParse->global.y = 0.0f; loader->svgParse->global.w = loader->svgParse->global.h = 1.0f; } @@ -933,7 +933,7 @@ static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, co static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { SvgStyleProperty* style = node->style; - style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint); + style->fill.flags = (style->fill.flags | SvgFillFlags::Paint); _handlePaintAttr(&style->fill.paint, value); } @@ -941,47 +941,47 @@ static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, con static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { SvgStyleProperty* style = node->style; - style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint); + style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint); _handlePaintAttr(&style->stroke.paint, value); } static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity); node->style->stroke.opacity = _toOpacity(value); } static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash); _parseDashArray(loader, value, &node->style->stroke.dash); } static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width); node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal); } static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap); node->style->stroke.cap = _toLineCap(value); } static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join); + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join); node->style->stroke.join = _toLineJoin(value); } static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule); + node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule); node->style->fill.fillRule = _toFillRule(value); } @@ -994,7 +994,7 @@ static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity); + node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity); node->style->fill.opacity = _toOpacity(value); } @@ -1046,7 +1046,7 @@ static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { - node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)SvgStyleFlags::PaintOrder); + node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder); node->style->paintOrder = _toPaintOrder(value); } @@ -1122,8 +1122,8 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) { if (style) { styleTags[i].tagHandler(loader, node, value); - node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag); - } else if (!((int)node->style->flags & (int)styleTags[i].flag)) { + node->style->flags = (node->style->flags | styleTags[i].flag); + } else if (!(node->style->flags & styleTags[i].flag)) { styleTags[i].tagHandler(loader, node, value); } return true; @@ -1352,11 +1352,11 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha doc->viewFlag = SvgViewFlag::None; func(buf, bufLength, _attrParseSvgNode, loader); - if (!((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox)) { - if ((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Width) { + if (!(doc->viewFlag & SvgViewFlag::Viewbox)) { + if (doc->viewFlag & SvgViewFlag::Width) { loader->svgParse->global.w = doc->w; } - if ((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Height) { + if (doc->viewFlag & SvgViewFlag::Height) { loader->svgParse->global.h = doc->h; } } @@ -2179,7 +2179,7 @@ static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->cx = from->radial->cx; to->radial->isCxPercentage = from->radial->isCxPercentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Cx); + to->flags = (to->flags | SvgGradientFlags::Cx); } @@ -2187,7 +2187,7 @@ static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->cy = from->radial->cy; to->radial->isCyPercentage = from->radial->isCyPercentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Cy); + to->flags = (to->flags | SvgGradientFlags::Cy); } @@ -2195,7 +2195,7 @@ static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->fx = from->radial->fx; to->radial->isFxPercentage = from->radial->isFxPercentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Fx); + to->flags = (to->flags | SvgGradientFlags::Fx); } @@ -2203,7 +2203,7 @@ static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->fy = from->radial->fy; to->radial->isFyPercentage = from->radial->isFyPercentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Fy); + to->flags = (to->flags | SvgGradientFlags::Fy); } @@ -2211,7 +2211,7 @@ static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from) { to->radial->r = from->radial->r; to->radial->isRPercentage = from->radial->isRPercentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::R); + to->flags = (to->flags | SvgGradientFlags::R); } @@ -2254,7 +2254,7 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) { radialTags[i].tagHandler(loader, radial, value); - grad->flags = (SvgGradientFlags)((int)grad->flags | (int)radialTags[i].flag); + grad->flags = (grad->flags | radialTags[i].flag); return true; } } @@ -2264,13 +2264,13 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char grad->id = _copyId(value); } else if (!strcmp(key, "spreadMethod")) { grad->spread = _parseSpreadValue(value); - grad->flags = (SvgGradientFlags)((int)grad->flags | (int)SvgGradientFlags::SpreadMethod); + grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { if (grad->ref && value) free(grad->ref); grad->ref = _idFromHref(value); } else if (!strcmp(key, "gradientUnits")) { if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; - grad->flags = (SvgGradientFlags)((int)grad->flags | (int)SvgGradientFlags::GradientUnits); + grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { @@ -2329,10 +2329,10 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value) if (!strcmp(key, "stop-opacity")) { stop->a = _toOpacity(value); - loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity); + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity); } else if (!strcmp(key, "stop-color")) { _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); - loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor); + loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); } else { return false; } @@ -2349,11 +2349,11 @@ static bool _attrParseStops(void* data, const char* key, const char* value) if (!strcmp(key, "offset")) { stop->offset = _toOffset(value); } else if (!strcmp(key, "stop-opacity")) { - if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) { + if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) { stop->a = _toOpacity(value); } } else if (!strcmp(key, "stop-color")) { - if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) { + if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); } } else if (!strcmp(key, "style")) { @@ -2454,7 +2454,7 @@ static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->x1 = from->linear->x1; to->linear->isX1Percentage = from->linear->isX1Percentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::X1); + to->flags = (to->flags | SvgGradientFlags::X1); } @@ -2462,7 +2462,7 @@ static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->x2 = from->linear->x2; to->linear->isX2Percentage = from->linear->isX2Percentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::X2); + to->flags = (to->flags | SvgGradientFlags::X2); } @@ -2470,7 +2470,7 @@ static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->y1 = from->linear->y1; to->linear->isY1Percentage = from->linear->isY1Percentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Y1); + to->flags = (to->flags | SvgGradientFlags::Y1); } @@ -2478,7 +2478,7 @@ static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from) { to->linear->y2 = from->linear->y2; to->linear->isY2Percentage = from->linear->isY2Percentage; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::Y2); + to->flags = (to->flags | SvgGradientFlags::Y2); } @@ -2520,7 +2520,7 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) { linear_tags[i].tagHandler(loader, linear, value); - grad->flags = (SvgGradientFlags)((int)grad->flags | (int)linear_tags[i].flag); + grad->flags = (grad->flags | linear_tags[i].flag); return true; } } @@ -2530,13 +2530,13 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char grad->id = _copyId(value); } else if (!strcmp(key, "spreadMethod")) { grad->spread = _parseSpreadValue(value); - grad->flags = (SvgGradientFlags)((int)grad->flags | (int)SvgGradientFlags::SpreadMethod); + grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod); } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) { if (grad->ref && value) free(grad->ref); grad->ref = _idFromHref(value); } else if (!strcmp(key, "gradientUnits")) { if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true; - grad->flags = (SvgGradientFlags)((int)grad->flags | (int)SvgGradientFlags::GradientUnits); + grad->flags = (grad->flags | SvgGradientFlags::GradientUnits); } else if (!strcmp(key, "gradientTransform")) { grad->transform = _parseTransformationMatrix(value); } else { @@ -2624,16 +2624,14 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty { if (!to || !from) return; - if (!((int)to->flags & (int)SvgGradientFlags::SpreadMethod) && - ((int)from->flags & (int)SvgGradientFlags::SpreadMethod)) { + if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) { to->spread = from->spread; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::SpreadMethod); + to->flags = (to->flags | SvgGradientFlags::SpreadMethod); } - bool gradUnitSet = (int)to->flags & (int)SvgGradientFlags::GradientUnits; - if (!((int)to->flags & (int)SvgGradientFlags::GradientUnits) && - ((int)from->flags & (int)SvgGradientFlags::GradientUnits)) { + bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits); + if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) { to->userSpace = from->userSpace; - to->flags = (SvgGradientFlags)((int)to->flags | (int)SvgGradientFlags::GradientUnits); + to->flags = (to->flags | SvgGradientFlags::GradientUnits); } if (!to->transform && from->transform) { @@ -2643,8 +2641,8 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty if (to->type == SvgGradientType::Linear && from->type == SvgGradientType::Linear) { for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) { - bool coordSet = (int)to->flags & (int)linear_tags[i].flag; - if (!((int)to->flags & (int)linear_tags[i].flag) && ((int)from->flags & (int)linear_tags[i].flag)) { + bool coordSet = to->flags & linear_tags[i].flag; + if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) { linear_tags[i].tagInheritHandler(to, from); } @@ -2660,8 +2658,8 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty } } else if (to->type == SvgGradientType::Radial && from->type == SvgGradientType::Radial) { for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) { - bool coordSet = (int)to->flags & (int)radialTags[i].flag; - if (!((int)to->flags & (int)radialTags[i].flag) && ((int)from->flags & (int)radialTags[i].flag)) { + bool coordSet = (to->flags & radialTags[i].flag); + if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) { radialTags[i].tagInheritHandler(to, from); } @@ -2731,11 +2729,11 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren child->color = parent->color; child->curColorSet = parent->curColorSet; } - if (!((int)child->flags & (int)SvgStyleFlags::PaintOrder)) { + if (!(child->flags & SvgStyleFlags::PaintOrder)) { child->paintOrder = parent->paintOrder; } //Fill - if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) { + if (!(child->fill.flags & SvgFillFlags::Paint)) { child->fill.paint.color = parent->fill.paint.color; child->fill.paint.none = parent->fill.paint.none; child->fill.paint.curColor = parent->fill.paint.curColor; @@ -2744,14 +2742,14 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren child->fill.paint.url = _copyId(parent->fill.paint.url); } } - if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) { + if (!(child->fill.flags & SvgFillFlags::Opacity)) { child->fill.opacity = parent->fill.opacity; } - if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) { + if (!(child->fill.flags & SvgFillFlags::FillRule)) { child->fill.fillRule = parent->fill.fillRule; } //Stroke - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) { + if (!(child->stroke.flags & SvgStrokeFlags::Paint)) { child->stroke.paint.color = parent->stroke.paint.color; child->stroke.paint.none = parent->stroke.paint.none; child->stroke.paint.curColor = parent->stroke.paint.curColor; @@ -2760,13 +2758,13 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren child->stroke.paint.url = _copyId(parent->stroke.paint.url); } } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) { + if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) { child->stroke.opacity = parent->stroke.opacity; } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) { + if (!(child->stroke.flags & SvgStrokeFlags::Width)) { child->stroke.width = parent->stroke.width; } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) { + if (!(child->stroke.flags & SvgStrokeFlags::Dash)) { if (parent->stroke.dash.array.count > 0) { child->stroke.dash.array.clear(); child->stroke.dash.array.reserve(parent->stroke.dash.array.count); @@ -2775,10 +2773,10 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren } } } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) { + if (!(child->stroke.flags & SvgStrokeFlags::Cap)) { child->stroke.cap = parent->stroke.cap; } - if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) { + if (!(child->stroke.flags & SvgStrokeFlags::Join)) { child->stroke.join = parent->stroke.join; } } @@ -2792,12 +2790,12 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) to->color = from->color; to->curColorSet = true; } - if (((int)from->flags & (int)SvgStyleFlags::PaintOrder)) { + if (from->flags & SvgStyleFlags::PaintOrder) { to->paintOrder = from->paintOrder; } //Fill - to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags); - if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) { + to->fill.flags = (to->fill.flags | from->fill.flags); + if (from->fill.flags & SvgFillFlags::Paint) { to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; @@ -2806,15 +2804,15 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) to->fill.paint.url = _copyId(from->fill.paint.url); } } - if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) { + if (from->fill.flags & SvgFillFlags::Opacity) { to->fill.opacity = from->fill.opacity; } - if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) { + if (from->fill.flags & SvgFillFlags::FillRule) { to->fill.fillRule = from->fill.fillRule; } //Stroke - to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags); - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) { + to->stroke.flags = (to->stroke.flags | from->stroke.flags); + if (from->stroke.flags & SvgStrokeFlags::Paint) { to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; @@ -2823,13 +2821,13 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) to->stroke.paint.url = _copyId(from->stroke.paint.url); } } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) { + if (from->stroke.flags & SvgStrokeFlags::Opacity) { to->stroke.opacity = from->stroke.opacity; } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) { + if (from->stroke.flags & SvgStrokeFlags::Width) { to->stroke.width = from->stroke.width; } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) { + if (from->stroke.flags & SvgStrokeFlags::Dash) { if (from->stroke.dash.array.count > 0) { to->stroke.dash.array.clear(); to->stroke.dash.array.reserve(from->stroke.dash.array.count); @@ -2838,10 +2836,10 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) } } } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) { + if (from->stroke.flags & SvgStrokeFlags::Cap) { to->stroke.cap = from->stroke.cap; } - if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) { + if (from->stroke.flags & SvgStrokeFlags::Join) { to->stroke.join = from->stroke.join; } } @@ -2856,7 +2854,7 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) } //Copy style attribute _styleCopy(to->style, from->style); - to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags); + to->style->flags = (to->style->flags | from->style->flags); if (from->style->clipPath.url) { if (to->style->clipPath.url) free(to->style->clipPath.url); to->style->clipPath.url = strdup(from->style->clipPath.url); @@ -3268,18 +3266,24 @@ static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Arraystyle->fill.paint.url) { - if (node->style->fill.paint.gradient) { - node->style->fill.paint.gradient->clear(); - free(node->style->fill.paint.gradient); + auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url); + if (newGrad) { + if (node->style->fill.paint.gradient) { + node->style->fill.paint.gradient->clear(); + free(node->style->fill.paint.gradient); + } + node->style->fill.paint.gradient = newGrad; } - node->style->fill.paint.gradient = _gradientDup(loader, gradients, node->style->fill.paint.url); } if (node->style->stroke.paint.url) { - if (node->style->stroke.paint.gradient) { - node->style->stroke.paint.gradient->clear(); - free(node->style->stroke.paint.gradient); + auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url); + if (newGrad) { + if (node->style->stroke.paint.gradient) { + node->style->stroke.paint.gradient->clear(); + free(node->style->stroke.paint.gradient); + } + node->style->stroke.paint.gradient = newGrad; } - node->style->stroke.paint.gradient = _gradientDup(loader, gradients, node->style->stroke.paint.url); } } } @@ -3486,10 +3490,10 @@ void SvgLoader::run(unsigned tid) _updateComposite(loaderData.doc, loaderData.doc); if (defs) _updateComposite(loaderData.doc, defs); + _updateStyle(loaderData.doc, nullptr); + if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients); - - _updateStyle(loaderData.doc, nullptr); } root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath, viewFlag); } @@ -3516,21 +3520,21 @@ bool SvgLoader::header() h = 1.0f; //Return the brief resource info such as viewbox: - if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width) { + if (viewFlag & SvgViewFlag::Width) { w = loaderData.doc->node.doc.w; } - if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height) { + if (viewFlag & SvgViewFlag::Height) { h = loaderData.doc->node.doc.h; } //Override size - if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) { + if (viewFlag & SvgViewFlag::Viewbox) { vx = loaderData.doc->node.doc.vx; vy = loaderData.doc->node.doc.vy; vw = loaderData.doc->node.doc.vw; vh = loaderData.doc->node.doc.vh; - if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width)) w = vw; - if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height)) h = vh; + if (!(viewFlag & SvgViewFlag::Width)) w = vw; + if (!(viewFlag & SvgViewFlag::Height)) h = vh; } else { vw = w; vh = h; @@ -3600,8 +3604,7 @@ bool SvgLoader::read() { if (!content || size == 0) return false; - if (((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) && - (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) { + if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) { TVGLOG("SVG", "The width and/or height set to 0 - rendering disabled."); renderingDisabled = true; } @@ -3610,9 +3613,9 @@ bool SvgLoader::read() //In case no viewbox and width/height data is provided the completion of loading //has to be forced, in order to establish this data based on the whole picture bounding box. - if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) && - (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width) || !((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height))) + if (!(viewFlag & SvgViewFlag::Viewbox) && (!(viewFlag & SvgViewFlag::Width) || !(viewFlag & SvgViewFlag::Height))) { this->done(); + } return true; } diff --git a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index c19086fe3..e66000f60 100644 --- a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h @@ -80,6 +80,16 @@ enum class SvgFillFlags ClipPath = 0x16 }; +constexpr bool operator &(SvgFillFlags a, SvgFillFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgFillFlags operator |(SvgFillFlags a, SvgFillFlags b) +{ + return SvgFillFlags(int(a) | int(b)); +} + enum class SvgStrokeFlags { Paint = 0x1, @@ -92,6 +102,17 @@ enum class SvgStrokeFlags Dash = 0x80, }; +constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStrokeFlags operator |(SvgStrokeFlags a, SvgStrokeFlags b) +{ + return SvgStrokeFlags(int(a) | int(b)); +} + + enum class SvgGradientType { Linear, @@ -119,6 +140,16 @@ enum class SvgStyleFlags PaintOrder = 0x10000 }; +constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStyleFlags operator |(SvgStyleFlags a, SvgStyleFlags b) +{ + return SvgStyleFlags(int(a) | int(b)); +} + enum class SvgStopStyleFlags { StopDefault = 0x0, @@ -126,6 +157,16 @@ enum class SvgStopStyleFlags StopColor = 0x02 }; +constexpr bool operator &(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgStopStyleFlags operator |(SvgStopStyleFlags a, SvgStopStyleFlags b) +{ + return SvgStopStyleFlags(int(a) | int(b)); +} + enum class SvgGradientFlags { None = 0x0, @@ -142,6 +183,16 @@ enum class SvgGradientFlags Fy = 0x400 }; +constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b) +{ + return int(a) & int(b); +} + +constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b) +{ + return SvgGradientFlags(int(a) | int(b)); +} + enum class SvgFillRule { Winding = 0, @@ -171,6 +222,16 @@ enum class SvgViewFlag Viewbox = 0x04 //viewBox x,y,w,h - used only if all 4 are correctly set }; +constexpr bool operator &(SvgViewFlag a, SvgViewFlag b) +{ + return static_cast(a) & static_cast(b); +} + +constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b) +{ + return SvgViewFlag(int(a) | int(b)); +} + enum class AspectRatioAlign { None, diff --git a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgPath.cpp b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgPath.cpp index 6195807af..e044931b5 100644 --- a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgPath.cpp +++ b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgPath.cpp @@ -52,7 +52,6 @@ #include #include -#include #include #include "tvgSvgLoaderCommon.h" #include "tvgSvgPath.h" @@ -545,11 +544,6 @@ bool svgPathToTvgPath(const char* svgPath, Array& cmds, Array& cmds, Array(static_cast(a) | static_cast(b)); } + constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return (static_cast(a) & static_cast(b)); } @@ -763,9 +765,9 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, static void _applySvgViewFlag(const Scene* scene, float& vx, float& vy, float& vw, float& vh, float& w, float& h, SvgViewFlag viewFlag) { - bool noViewbox = !((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox); - bool noWidth = !((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width); - bool noHeight = !((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height); + bool noViewbox = !(viewFlag & SvgViewFlag::Viewbox); + bool noWidth = !(viewFlag & SvgViewFlag::Width); + bool noHeight = !(viewFlag & SvgViewFlag::Height); if (noViewbox) { float x, y; diff --git a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgUtil.cpp index af42e82e1..12d20c973 100644 --- a/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgUtil.cpp +++ b/libfenrir/src/main/jni/thorvg/src/loaders/svg/tvgSvgUtil.cpp @@ -94,8 +94,8 @@ float svgUtilStrtof(const char *nPtr, char **endPtr) if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3; else goto error; - if (tolower(*(iter + 3)) == 'i') { - if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5; + if (tolower(*(iter)) == 'i') { + if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') && (tolower(*(iter + 4)) == 'y')) iter += 5; else goto error; } if (endPtr) *endPtr = (char *)(iter); diff --git a/other_tool/getter-setter-fix-plugin-gradle/src/main/resources/META-INF/plugin.xml b/other_tool/getter-setter-fix-plugin-gradle/src/main/resources/META-INF/plugin.xml index 13ca6c467..5690ba0bf 100644 --- a/other_tool/getter-setter-fix-plugin-gradle/src/main/resources/META-INF/plugin.xml +++ b/other_tool/getter-setter-fix-plugin-gradle/src/main/resources/META-INF/plugin.xml @@ -1,3 +1,4 @@ + dev.ragnarok.intellij.getter_setter_plugin Getter Setter Kotlin Fixer diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java b/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java index ccd9cfae6..7a102fdba 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java @@ -50,7 +50,6 @@ public final class AsyncDifferConfig { mDiffCallback = diffCallback; } - /** @hide */ @SuppressWarnings("WeakerAccess") @RestrictTo(RestrictTo.Scope.LIBRARY) @Nullable @@ -94,7 +93,6 @@ public Builder(@NonNull DiffUtil.ItemCallback diffCallback) { * @param executor The executor which can run tasks in the UI thread. * @return this * - * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY) @NonNull diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/GapWorker.java b/viewpager2/src/main/java/androidx/recyclerview/widget/GapWorker.java index 8c44cf09e..a94c06280 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/GapWorker.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/GapWorker.java @@ -157,7 +157,7 @@ void clearPrefetchPositions() { } public void add(RecyclerView recyclerView) { - if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) { + if (RecyclerView.sDebugAssertionsEnabled && mRecyclerViews.contains(recyclerView)) { throw new IllegalStateException("RecyclerView already present in worker list!"); } mRecyclerViews.add(recyclerView); @@ -165,7 +165,7 @@ public void add(RecyclerView recyclerView) { public void remove(RecyclerView recyclerView) { boolean removeSuccess = mRecyclerViews.remove(recyclerView); - if (RecyclerView.DEBUG && !removeSuccess) { + if (RecyclerView.sDebugAssertionsEnabled && !removeSuccess) { throw new IllegalStateException("RecyclerView removal failed!"); } } @@ -175,7 +175,7 @@ public void remove(RecyclerView recyclerView) { */ void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) { if (recyclerView.isAttachedToWindow()) { - if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) { + if (RecyclerView.sDebugAssertionsEnabled && !mRecyclerViews.contains(recyclerView)) { throw new IllegalStateException("attempting to post unregistered view!"); } if (mPostTimeNs == 0) { diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java b/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java index 74350de23..3a3f1728e 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java @@ -27,7 +27,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import android.widget.ListView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -87,30 +86,6 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements */ private boolean mLastStackFromEnd; - /** - * Whether the last layout filled the entire viewport - * - * If the last layout did not fill the viewport, we should not attempt to calculate an - * anchoring based on the current children (other than if one is focused), because there - * isn't any scrolling that could have occurred that would indicate a position in the list - * that needs to be preserved - and in fact, trying to do so could produce the wrong result, - * such as the case of anchoring to a loading spinner at the end of the list. - */ - private boolean mLastLayoutFilledViewport = false; - - /** - * Whether the *current* layout filled the entire viewport - * - * This is used to populate mLastLayoutFilledViewport. It exists as a separate variable - * because we need to populate it at the correct moment, which is tricky due to the - * LayoutManager layout being called multiple times. We want to not set it in prelayout - * (because that's not the real layout), but we want to set it the *first* time that the - * actual layout is run, because for certain non-exact layout cases, there are two passes, - * with the second pass being provided an EXACTLY spec (when the actual spec was non-exact). - * This would otherwise incorrectly believe the viewport was filled, because it was provided - * just enough space to contain the content, and thus it would always fill the viewport. - */ - private Boolean mThisLayoutFilledViewport = null; /** * Defines if layout should be calculated from end to start. @@ -290,10 +265,6 @@ public void onInitializeAccessibilityEvent(AccessibilityEvent event) { public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(recycler, state, info); - // Set the class name so this is treated as a list. This helps accessibility services - // distinguish lists from one row or one column grids. - info.setClassName(ListView.class.getName()); - // TODO(b/251823537) if (mRecyclerView.mAdapter != null && mRecyclerView.mAdapter.getItemCount() > 0) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -801,10 +772,6 @@ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State fixOffset = fixLayoutStartGap(startOffset, recycler, state, false); startOffset += fixOffset; endOffset += fixOffset; - if (!state.isPreLayout() && mThisLayoutFilledViewport == null) { - mThisLayoutFilledViewport = - (startOffset <= mOrientationHelper.getStartAfterPadding()); - } } else { int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true); startOffset += fixOffset; @@ -812,10 +779,6 @@ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State fixOffset = fixLayoutEndGap(endOffset, recycler, state, false); startOffset += fixOffset; endOffset += fixOffset; - if (!state.isPreLayout() && mThisLayoutFilledViewport == null) { - mThisLayoutFilledViewport = - (endOffset >= mOrientationHelper.getEndAfterPadding()); - } } } layoutForPredictiveAnimations(recycler, state, startOffset, endOffset); @@ -837,8 +800,6 @@ public void onLayoutCompleted(RecyclerView.State state) { mPendingSavedState = null; // we don't need this anymore mPendingScrollPosition = RecyclerView.NO_POSITION; mPendingScrollPositionOffset = INVALID_OFFSET; - mLastLayoutFilledViewport = mThisLayoutFilledViewport != null && mThisLayoutFilledViewport; - mThisLayoutFilledViewport = null; mAnchorInfo.reset(); } @@ -948,19 +909,11 @@ private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler, if (getChildCount() == 0) { return false; } - final View focused = getFocusedChild(); if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) { anchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused)); return true; } - - // If we did not fill the layout, don't anchor. This prevents, for example, - // anchoring to the bottom of the list when there is a loading indicator. - if (!mLastLayoutFilledViewport) { - return false; - } - if (mLastStackFromEnd != mStackFromEnd) { return false; } diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java index a71ef172b..e1264963a 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java @@ -219,7 +219,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView, static final String TAG = "RecyclerView"; - static final boolean DEBUG = false; + static boolean sDebugAssertionsEnabled = false; + static boolean sVerboseLoggingEnabled = false; static final boolean VERBOSE_TRACING = false; @@ -282,9 +283,20 @@ public class RecyclerView extends ViewGroup implements ScrollingView, */ private static final float FLING_DESTRETCH_FACTOR = 4f; + /** + * A {@link android.content.pm.PackageManager} feature specifying if a device's rotary encoder + * has low resolution. Low resolution rotary encoders produce small number of + * {@link MotionEvent}s per a 360 degree rotation, meaning that each {@link MotionEvent} has + * large scroll values, which make {@link #scrollBy(int, int)} calls feel broken (due to the + * fact that each event produces large scrolls, and scrolling with large pixels causes a visible + * jump that does not feel smooth). As such, we will try adjusting our handling of generic + * motion caused by such low resolution rotary encoders differently to make the scrolling + * experience smooth. + */ + static final String LOW_RES_ROTARY_ENCODER_FEATURE = "android.hardware.rotaryencoder.lowres"; + static final boolean DISPATCH_TEMP_DETACH = false; - /** @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @IntDef({HORIZONTAL, VERTICAL}) @Retention(RetentionPolicy.SOURCE) @@ -386,6 +398,35 @@ public class RecyclerView extends ViewGroup implements ScrollingView, private static final Class[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE = new Class[]{Context.class, AttributeSet.class, int.class, int.class}; + /** + * Enable internal assertions about RecyclerView's state and throw exceptions if the + * assertions are violated. + *

+ * This is primarily intended to diagnose problems with RecyclerView, and + * should not be enabled in production unless you have a specific reason to + * do so. + *

+ * Enabling this may negatively affect performance and/or stability. + * + * @param debugAssertionsEnabled true to enable assertions; false to disable them + */ + public static void setDebugAssertionsEnabled(boolean debugAssertionsEnabled) { + RecyclerView.sDebugAssertionsEnabled = debugAssertionsEnabled; + } + + /** + * Enable verbose logging within RecyclerView itself. + *

+ * Enabling this may negatively affect performance and reduce the utility of logcat due to + * high-volume logging. This generally should not be enabled in production + * unless you have a specific reason for doing so. + * + * @param verboseLoggingEnabled true to enable logging; false to disable it + */ + public static void setVerboseLoggingEnabled(boolean verboseLoggingEnabled) { + RecyclerView.sVerboseLoggingEnabled = verboseLoggingEnabled; + } + private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver(); final Recycler mRecycler = new Recycler(); @@ -654,6 +695,12 @@ public void run() { private int mLastAutoMeasureNonExactMeasuredWidth = 0; private int mLastAutoMeasureNonExactMeasuredHeight = 0; + /** + * Whether or not the device has {@link #LOW_RES_ROTARY_ENCODER_FEATURE}. Computed once and + * cached, since it's a static value that would not change on consecutive calls. + */ + @VisibleForTesting boolean mLowResRotaryEncoderFeature; + /** * The callback to convert view info diffs into animations. */ @@ -764,6 +811,9 @@ public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int } a.recycle(); + mLowResRotaryEncoderFeature = + context.getPackageManager().hasSystemFeature(LOW_RES_ROTARY_ENCODER_FEATURE); + // Create the layoutManager if specified. createLayoutManager(context, layoutManagerName, attrs, defStyleAttr, 0); @@ -974,10 +1024,16 @@ public void attachViewToParent(View child, int index, throw new IllegalArgumentException("Called attach on a child which is not" + " detached: " + vh + exceptionLabel()); } - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "reAttach " + vh); } vh.clearTmpDetachFlag(); + } else { + if (sDebugAssertionsEnabled) { + throw new IllegalArgumentException( + "No ViewHolder found for child: " + child + ", index: " + index + + exceptionLabel()); + } } RecyclerView.this.attachViewToParent(child, index, layoutParams); } @@ -992,11 +1048,16 @@ public void detachViewFromParent(int offset) { throw new IllegalArgumentException("called detach on an already" + " detached child " + vh + exceptionLabel()); } - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "tmpDetach " + vh); } vh.addFlags(ViewHolder.FLAG_TMP_DETACHED); } + } else { + if (sDebugAssertionsEnabled) { + throw new IllegalArgumentException( + "No view at offset " + offset + exceptionLabel()); + } } RecyclerView.this.detachViewFromParent(offset); } @@ -1030,7 +1091,7 @@ public ViewHolder findViewHolder(int position) { // ensure it is not hidden because for adapter helper, the only thing matter is that // LM thinks view is a child. if (mChildHelper.isHidden(vh.itemView)) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "assuming view holder cannot be find because it is hidden"); } return null; @@ -1545,7 +1606,7 @@ boolean removeAnimatingView(View view) { final ViewHolder viewHolder = getChildViewHolderInt(view); mRecycler.unscrapView(viewHolder); mRecycler.recycleViewHolderInternal(viewHolder); - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "after removing animated view: " + view + ", " + this); } } @@ -1629,7 +1690,7 @@ void setScrollState(int state) { if (state == mScrollState) { return; } - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState, new Exception()); } @@ -2398,7 +2459,7 @@ void startInterceptRequestLayout() { void stopInterceptRequestLayout(boolean performLayoutChildren) { if (mInterceptRequestLayoutDepth < 1) { //noinspection PointlessBooleanExpression - if (DEBUG) { + if (sDebugAssertionsEnabled) { throw new IllegalStateException("stopInterceptRequestLayout was called more " + "times than startInterceptRequestLayout." + exceptionLabel()); @@ -3899,6 +3960,8 @@ public boolean onGenericMotionEvent(MotionEvent event) { if (mLayoutSuppressed) { return false; } + + boolean useSmoothScroll = false; if (event.getAction() == MotionEvent.ACTION_SCROLL) { final float vScroll, hScroll; if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) { @@ -3928,14 +3991,25 @@ public boolean onGenericMotionEvent(MotionEvent event) { vScroll = 0f; hScroll = 0f; } + // Use smooth scrolling for low resolution rotary encoders to avoid the visible + // pixel jumps that would be caused by doing regular scrolling. + useSmoothScroll = mLowResRotaryEncoderFeature; } else { vScroll = 0f; hScroll = 0f; } - if (vScroll != 0 || hScroll != 0) { - nestedScrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor), - (int) (vScroll * mScaledVerticalScrollFactor), event, TYPE_NON_TOUCH); + int scaledVScroll = (int) (vScroll * mScaledVerticalScrollFactor); + int scaledHScroll = (int) (hScroll * mScaledHorizontalScrollFactor); + if (useSmoothScroll) { + OverScroller overScroller = mViewFlinger.mOverScroller; + // Account for any remaining scroll from a previous generic motion event. + scaledVScroll += overScroller.getFinalY() - overScroller.getCurrY(); + scaledHScroll += overScroller.getFinalX() - overScroller.getCurrX(); + smoothScrollBy(scaledHScroll, scaledVScroll, /* interpolator= */ null, + UNDEFINED_DURATION, /* withNestedScrolling= */ true); + } else { + nestedScrollByInternal(scaledHScroll, scaledVScroll, event, TYPE_NON_TOUCH); } } return false; @@ -4096,7 +4170,7 @@ void onExitLayoutOrScroll() { void onExitLayoutOrScroll(boolean enableChangeEvents) { mLayoutOrScrollCounter--; if (mLayoutOrScrollCounter < 1) { - if (DEBUG && mLayoutOrScrollCounter < 0) { + if (sDebugAssertionsEnabled && mLayoutOrScrollCounter < 0) { throw new IllegalStateException("layout or scroll counter cannot go below zero." + "Some calls are not matching" + exceptionLabel()); } @@ -4811,6 +4885,11 @@ protected void removeDetachedView(View child, boolean animate) { throw new IllegalArgumentException("Called removeDetachedView with a view which" + " is not flagged as tmp detached." + vh + exceptionLabel()); } + } else { + if (sDebugAssertionsEnabled) { + throw new IllegalArgumentException( + "No ViewHolder found for child: " + child + exceptionLabel()); + } } // Clear any android.view.animation.Animation that may prevent the item from @@ -5012,7 +5091,7 @@ void saveOldPositions() { final int childCount = mChildHelper.getUnfilteredChildCount(); for (int i = 0; i < childCount; i++) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); - if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) { + if (sDebugAssertionsEnabled && holder.mPosition == -1 && !holder.isRemoved()) { throw new IllegalStateException("view holder cannot have position -1 unless it" + " is removed" + exceptionLabel()); } @@ -5051,7 +5130,7 @@ void offsetPositionRecordsForMove(int from, int to) { if (holder == null || holder.mPosition < start || holder.mPosition > end) { continue; } - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " + holder); } @@ -5072,7 +5151,7 @@ void offsetPositionRecordsForInsert(int positionStart, int itemCount) { for (int i = 0; i < childCount; i++) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " + holder + " now at position " + (holder.mPosition + itemCount)); } @@ -5092,7 +5171,7 @@ void offsetPositionRecordsForRemove(int positionStart, int itemCount, final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i)); if (holder != null && !holder.shouldIgnore()) { if (holder.mPosition >= positionEnd) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i + " holder " + holder + " now at position " + (holder.mPosition - itemCount)); @@ -5100,7 +5179,7 @@ void offsetPositionRecordsForRemove(int positionStart, int itemCount, holder.offsetPosition(-itemCount, applyToPreLayout); mState.mStructureChanged = true; } else if (holder.mPosition >= positionStart) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i + " holder " + holder + " now REMOVED"); } @@ -6281,7 +6360,7 @@ public void putRecycledView(ViewHolder scrap) { PoolingContainer.callPoolingContainerOnRelease(scrap.itemView); return; } - if (DEBUG && scrapHeap.contains(scrap)) { + if (sDebugAssertionsEnabled && scrapHeap.contains(scrap)) { throw new IllegalArgumentException("this scrap item already exists"); } scrap.resetInternal(); @@ -6536,7 +6615,7 @@ boolean validateViewHolderForOffsetPosition(ViewHolder holder) { // if it is a removed holder, nothing to verify since we cannot ask adapter anymore // if it is not removed, verify the type and id. if (holder.isRemoved()) { - if (DEBUG && !mState.isPreLayout()) { + if (sDebugAssertionsEnabled && !mState.isPreLayout()) { throw new IllegalStateException("should not receive a removed view unless it" + " is pre layout" + exceptionLabel()); } @@ -6582,7 +6661,30 @@ private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offs // abort - we have a deadline we can't meet return false; } + + // Holders being bound should be either fully attached or fully detached. + // We don't want to bind with views that are temporarily detached, because that + // creates a situation in which they are unable to reason about their attach state + // properly. + // For example, isAttachedToWindow will return true, but the itemView will lack a + // parent. This breaks, among other possible issues, anything involving traversing + // the view tree, such as ViewTreeLifecycleOwner. + // Thus, we temporarily reattach any temp-detached holders for the bind operation. + // See https://issuetracker.google.com/265347515 for additional details on problems + // resulting from this + boolean reattachedForBind = false; + if (holder.isTmpDetached()) { + attachViewToParent(holder.itemView, getChildCount(), + holder.itemView.getLayoutParams()); + reattachedForBind = true; + } + mAdapter.bindViewHolder(holder, offsetPosition); + + if (reattachedForBind) { + detachViewFromParent(holder.itemView); + } + long endBindNs = getNanoTime(); mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs); attachAccessibilityDelegateOnBind(holder); @@ -6785,7 +6887,7 @@ ViewHolder tryGetViewHolderForPositionByDeadline(int position, } } if (holder == null) { // fallback to pool - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline(" + position + ") fetching from shared pool"); } @@ -6815,7 +6917,7 @@ ViewHolder tryGetViewHolderForPositionByDeadline(int position, long end = getNanoTime(); mRecyclerPool.factorInCreateTime(type, end - start); - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder"); } } @@ -6842,7 +6944,7 @@ ViewHolder tryGetViewHolderForPositionByDeadline(int position, // do not update unless we absolutely have to. holder.mPreLayoutPosition = position; } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { - if (DEBUG && holder.isRemoved()) { + if (sDebugAssertionsEnabled && holder.isRemoved()) { throw new IllegalStateException("Removed holder should be bound and it should" + " come here only in pre-layout. Holder: " + holder + exceptionLabel()); @@ -6981,11 +7083,11 @@ void recycleAndClearCachedViews() { * @param cachedViewIndex The index of the view in cached views list */ void recycleCachedViewAt(int cachedViewIndex) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "Recycling cached view at index " + cachedViewIndex); } ViewHolder viewHolder = mCachedViews.get(cachedViewIndex); - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder); } addViewHolderToRecycledViewPool(viewHolder, true); @@ -7023,7 +7125,7 @@ void recycleViewHolderInternal(ViewHolder holder) { && mAdapter.onFailedToRecycleView(holder); boolean cached = false; boolean recycled = false; - if (DEBUG && mCachedViews.contains(holder)) { + if (sDebugAssertionsEnabled && mCachedViews.contains(holder)) { throw new IllegalArgumentException("cached view received recycle internal? " + holder + exceptionLabel()); } @@ -7069,7 +7171,7 @@ void recycleViewHolderInternal(ViewHolder holder) { // TODO: consider cancelling an animation when an item is removed scrollBy, // to return it to the pool faster - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will " + "re-visit here. We are still removing it from animation lists" + exceptionLabel()); @@ -7271,7 +7373,7 @@ ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRu if (!dryRun) { mCachedViews.remove(i); } - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position + ") found match in cache: " + holder); } @@ -7351,7 +7453,7 @@ void dispatchViewRecycled(@NonNull ViewHolder holder) { if (mState != null) { mViewInfoStore.removeViewHolder(holder); } - if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder); + if (sVerboseLoggingEnabled) Log.d(TAG, "dispatchViewRecycled: " + holder); } void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, @@ -7385,7 +7487,7 @@ void offsetPositionRecordsForMove(int from, int to) { } else { holder.offsetPosition(inBetweenOffset, false); } - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " + holder); } @@ -7397,7 +7499,7 @@ void offsetPositionRecordsForInsert(int insertedAt, int count) { for (int i = 0; i < cachedCount; i++) { final ViewHolder holder = mCachedViews.get(i); if (holder != null && holder.mPosition >= insertedAt) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " + holder + " now at position " + (holder.mPosition + count)); } @@ -7420,7 +7522,7 @@ void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToP final ViewHolder holder = mCachedViews.get(i); if (holder != null) { if (holder.mPosition >= removedEnd) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "offsetPositionRecordsForRemove cached " + i + " holder " + holder + " now at position " + (holder.mPosition - count)); @@ -7761,6 +7863,23 @@ public final void bindViewHolder(@NonNull VH holder, int position) { Trace.beginSection(TRACE_BIND_VIEW_TAG); } holder.mBindingAdapter = this; + if (sDebugAssertionsEnabled) { + if (holder.itemView.getParent() == null + && (ViewCompat.isAttachedToWindow(holder.itemView) + != holder.isTmpDetached())) { + throw new IllegalStateException("Temp-detached state out of sync with reality. " + + "holder.isTmpDetached(): " + holder.isTmpDetached() + + ", attached to window: " + + ViewCompat.isAttachedToWindow(holder.itemView) + + ", holder: " + holder); + } + if (holder.itemView.getParent() == null + && ViewCompat.isAttachedToWindow(holder.itemView)) { + throw new IllegalStateException( + "Attempting to bind attached holder with no parent" + + " (AKA temp detached): " + holder); + } + } onBindViewHolder(holder, position, holder.getUnmodifiedPayloads()); if (rootBind) { holder.clearPayload(); @@ -9170,7 +9289,7 @@ public boolean canScrollVertically() { * @param position Scroll to this adapter position. */ public void scrollToPosition(int position) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract"); } } @@ -9351,7 +9470,7 @@ private void addViewInt(View child, int index, boolean disappearing) { } } if (lp.mPendingInvalidate) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder); } holder.itemView.invalidate(); @@ -9943,7 +10062,7 @@ public void detachAndScrapAttachedViews(@NonNull Recycler recycler) { private void scrapOrRecycleView(Recycler recycler, int index, View view) { final ViewHolder viewHolder = getChildViewHolderInt(view); if (viewHolder.shouldIgnore()) { - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "ignoring view " + viewHolder); } return; @@ -12164,6 +12283,11 @@ List getUnmodifiedPayloads() { } void resetInternal() { + if (sDebugAssertionsEnabled && isTmpDetached()) { + throw new IllegalStateException("Attempting to reset temp-detached ViewHolder: " + + this + ". ViewHolders should be fully detached before resetting."); + } + mFlags = 0; mPosition = NO_POSITION; mOldPosition = NO_POSITION; @@ -12243,7 +12367,7 @@ public final void setIsRecyclable(boolean recyclable) { mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1; if (mIsRecyclableCount < 0) { mIsRecyclableCount = 0; - if (DEBUG) { + if (sDebugAssertionsEnabled) { throw new RuntimeException("isRecyclable decremented below 0: " + "unmatched pair of setIsRecyable() calls for " + this); } @@ -12254,7 +12378,7 @@ public final void setIsRecyclable(boolean recyclable) { } else if (recyclable && mIsRecyclableCount == 0) { mFlags &= ~FLAG_NOT_RECYCLABLE; } - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this); } } @@ -12849,7 +12973,7 @@ public void instantScrollToPosition(int position) { protected void onChildAttachedToWindow(View child) { if (getChildPosition(child) == getTargetPosition()) { mTargetView = child; - if (DEBUG) { + if (sVerboseLoggingEnabled) { Log.d(TAG, "smooth scroll target view has been attached"); } } @@ -13174,7 +13298,6 @@ public void notifyItemMoved(int fromPosition, int toPosition) { /** * This is public so that the CREATOR can be accessed on cold launch. * - * @hide */ @RestrictTo(LIBRARY) public static class SavedState extends AbsSavedState { diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java b/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java index b8b3e7a4f..63ce8e4fb 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java @@ -2120,7 +2120,6 @@ public void scrollToPositionWithOffset(int position, int offset) { requestLayout(); } - /** @hide */ @Override @RestrictTo(LIBRARY) public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, diff --git a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java index 0b8499eba..59eb602fa 100644 --- a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java +++ b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java @@ -34,6 +34,8 @@ import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.OptIn; +import androidx.annotation.RequiresOptIn; import androidx.collection.ArraySet; import androidx.collection.LongSparseArray; import androidx.core.view.ViewCompat; @@ -182,22 +184,10 @@ public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int ensureFragment(position); /** Special case when {@link RecyclerView} decides to keep the {@link container} - * attached to the window, but not to the view hierarchy (i.e. parent is null) */ + * attached to the window, resulting in no {@link `onViewAttachedToWindow} callback later */ final FrameLayout container = holder.getContainer(); if (ViewCompat.isAttachedToWindow(container)) { - if (container.getParent() != null) { - throw new IllegalStateException("Design assumption violated."); - } - container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - if (container.getParent() != null) { - container.removeOnLayoutChangeListener(this); - placeFragmentInViewHolder(holder); - } - } - }); + placeFragmentInViewHolder(holder); } gcFragments(); @@ -846,6 +836,7 @@ public List dispatchPreAdded(Fragment fragment) { return result; } + @OptIn(markerClass = ExperimentalFragmentStateAdapterApi.class) public List dispatchPreSavedInstanceState(Fragment fragment) { List result = new ArrayList<>(); for (FragmentTransactionCallback callback : mCallbacks) { @@ -891,6 +882,7 @@ public OnPostEventListener onFragmentPreAdded(@NonNull Fragment fragment) { * @return Listener called after the operation */ @NonNull + @ExperimentalFragmentStateAdapterApi // Experimental in v1.1.*. To become stable in v1.2.*. public OnPostEventListener onFragmentPreSavedInstanceState(@NonNull Fragment fragment) { return NO_OP; } @@ -950,4 +942,7 @@ public void unregisterFragmentTransactionCallback( @NonNull FragmentTransactionCallback callback) { mFragmentEventDispatcher.unregisterCallback(callback); } + + @RequiresOptIn(level = RequiresOptIn.Level.WARNING) + public @interface ExperimentalFragmentStateAdapterApi { } } diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java index cd59b0edd..4368102ea 100644 --- a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java +++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java @@ -21,6 +21,7 @@ import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE; import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING; import static androidx.viewpager2.widget.ViewPager2.ScrollState; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.view.View; @@ -42,7 +43,6 @@ * relative to the pages and exposes this position via ({@link #getRelativeScrollPosition()}. */ final class ScrollEventAdapter extends RecyclerView.OnScrollListener { - /** @hide */ @Retention(SOURCE) @IntDef({STATE_IDLE, STATE_IN_PROGRESS_MANUAL_DRAG, STATE_IN_PROGRESS_SMOOTH_SCROLL, STATE_IN_PROGRESS_IMMEDIATE_SCROLL, STATE_IN_PROGRESS_FAKE_DRAG}) diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java index 16694f697..a6e7f31b8 100644 --- a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java +++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java @@ -18,6 +18,7 @@ import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.SuppressLint; @@ -75,7 +76,6 @@ * @see androidx.viewpager.widget.ViewPager */ public final class ViewPager2 extends ViewGroup { - /** @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @Retention(SOURCE) @IntDef({ORIENTATION_HORIZONTAL, ORIENTATION_VERTICAL}) @@ -85,14 +85,12 @@ public final class ViewPager2 extends ViewGroup { public static final int ORIENTATION_HORIZONTAL = RecyclerView.HORIZONTAL; public static final int ORIENTATION_VERTICAL = RecyclerView.VERTICAL; - /** @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @Retention(SOURCE) @IntDef({SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING}) public @interface ScrollState { } - /** @hide */ @SuppressWarnings("WeakerAccess") @RestrictTo(LIBRARY_GROUP_PREFIX) @Retention(SOURCE) diff --git a/viewpager2/src/main/res/values/attrs.xml b/viewpager2/src/main/res/values/attrs.xml index 1a6c5a486..13ef9fa41 100644 --- a/viewpager2/src/main/res/values/attrs.xml +++ b/viewpager2/src/main/res/values/attrs.xml @@ -1,4 +1,5 @@ - - + \ No newline at end of file