From 0cecbf0ab316649b485b594fc927bc4e2937d6d9 Mon Sep 17 00:00:00 2001 From: ShaksterNano <54268387+shaksternano@users.noreply.github.com> Date: Sun, 29 Sep 2024 19:32:09 +0100 Subject: [PATCH] refactor: handle `ImageProcessor` chaining in `TransformedImageReader` --- .../borgar/core/media/ImageProcessor.kt | 10 +++--- .../borgar/core/media/MediaProcessing.kt | 1 + .../core/media/SimpleMediaProcessingConfig.kt | 14 -------- .../core/media/reader/BaseMediaReader.kt | 14 ++++++++ .../ConstantFrameDurationMediaReader.kt | 8 +++++ .../reader/LimitedDurationMediaReader.kt | 13 +++++++- .../borgar/core/media/reader/MediaReader.kt | 33 +++++++++++++++++-- .../core/media/reader/ZippedImageReader.kt | 8 +++++ .../shaksternano/borgar/core/task/SpinTask.kt | 14 ++------ .../borgar/core/task/TemplateTask.kt | 13 ++------ 10 files changed, 83 insertions(+), 45 deletions(-) diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/ImageProcessor.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/ImageProcessor.kt index f40acac1..f909a419 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/ImageProcessor.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/ImageProcessor.kt @@ -15,15 +15,13 @@ interface ImageProcessor : SuspendCloseable { override suspend fun close() = Unit } -infix fun ImageProcessor?.then(after: ImageProcessor?): ImageProcessor<*> { - return if (this == null && after != null) { +infix fun ImageProcessor.then(after: ImageProcessor): ImageProcessor<*> { + return if (this is IdentityImageProcessor) { after - } else if (this != null && after == null) { + } else if (after is IdentityImageProcessor) { this - } else if (this != null && after != null) { - ChainedImageProcessor(this, after) } else { - throw IllegalArgumentException("Both processors are null") + ChainedImageProcessor(this, after) } } diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/MediaProcessing.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/MediaProcessing.kt index 7d5bacc5..6b1fa58e 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/MediaProcessing.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/MediaProcessing.kt @@ -60,6 +60,7 @@ private suspend fun processMedia( outputFormat: String, maxFileSize: Long, ): Path = useAllIgnored(imageReader, audioReader) { + println(imageReader) var outputSize: Long var resizeRatio = 1.0 val maxResizeAttempts = 3 diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/SimpleMediaProcessingConfig.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/SimpleMediaProcessingConfig.kt index 9f6f2cc6..907feba0 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/SimpleMediaProcessingConfig.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/SimpleMediaProcessingConfig.kt @@ -21,20 +21,6 @@ open class SimpleMediaProcessingConfig( return imageReader.transform(processor, outputFormat) } - override fun then(after: MediaProcessingConfig): MediaProcessingConfig { - return if (after is SimpleMediaProcessingConfig) { - val newOutputName: String = after.outputName.ifBlank { - outputName - } - SimpleMediaProcessingConfig( - processor then after.processor, - newOutputName, - ) - } else { - super.then(after) - } - } - override fun toString(): String { return "SimpleMediaProcessingConfig(processor=$processor, outputName='$outputName')" } diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/BaseMediaReader.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/BaseMediaReader.kt index 0257f045..2b94f9ed 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/BaseMediaReader.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/BaseMediaReader.kt @@ -78,6 +78,13 @@ private class ReversedReader>( override suspend fun reversed(): MediaReader = reader override suspend fun close() = reader.close() + + override fun toString(): String { + return "ReversedReader(" + + "reader=$reader" + + ", reversedFrameInfo=$reversedFrameInfo" + + ")" + } } private data class ReversedFrameInfo( @@ -126,6 +133,13 @@ open class ChangedSpeedReader>( } override suspend fun close() = reader.close() + + override fun toString(): String { + return "ChangedSpeedReader(" + + "reader=$reader" + + ", speedMultiplier=$speedMultiplier" + + ")" + } } abstract class BaseImageReader : BaseMediaReader() { diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ConstantFrameDurationMediaReader.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ConstantFrameDurationMediaReader.kt index e69a2d51..08dfcd82 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ConstantFrameDurationMediaReader.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ConstantFrameDurationMediaReader.kt @@ -50,4 +50,12 @@ class ConstantFrameDurationMediaReader>( } override suspend fun close() = reader.close() + + override fun toString(): String { + return "ConstantFrameDurationMediaReader(" + + "reader=$reader" + + ", frameDuration=$frameDuration" + + ", frameCount=$frameCount" + + ")" + } } diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/LimitedDurationMediaReader.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/LimitedDurationMediaReader.kt index e762f6b7..6820d628 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/LimitedDurationMediaReader.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/LimitedDurationMediaReader.kt @@ -38,6 +38,14 @@ class LimitedDurationMediaReader> internal constructor( } override suspend fun close() = reader.close() + + override fun toString(): String { + return "LimitedDurationMediaReader(" + + "reader=$reader" + + ", frameCount=$frameCount" + + ", duration=$duration" + + ")" + } } @Suppress("FunctionName") @@ -69,7 +77,10 @@ private suspend fun readerInfo(reader: MediaReader<*>, maxDuration: Duration): R return ReaderInfo(frameCount, totalDuration) } -internal data class ReaderInfo(val frameCount: Int, val duration: Duration) { +data class ReaderInfo( + val frameCount: Int, + val duration: Duration +) { constructor(reader: MediaReader<*>) : this( reader.frameCount, reader.duration, diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/MediaReader.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/MediaReader.kt index 480179a0..955c9b40 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/MediaReader.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/MediaReader.kt @@ -8,6 +8,8 @@ import io.github.shaksternano.gifcodec.AsyncExecutor import io.github.shaksternano.gifcodec.use import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlin.time.Duration interface MediaReader> : SuspendCloseable { @@ -74,6 +76,8 @@ suspend fun > MediaReader.readContent(timestamp: Duratio fun ImageReader.transform(processor: ImageProcessor<*>, outputFormat: String): ImageReader { return if (processor is IdentityImageProcessor) { this + } else if (this is TransformedImageReader<*>) { + this then processor } else { TransformedImageReader(this, processor, outputFormat) } @@ -94,6 +98,7 @@ private class TransformedImageReader( override val loopCount: Int = reader.loopCount private val flow: Flow = reader.asFlow() + private val mutex: Mutex = Mutex() private lateinit var constantData: T override suspend fun readFrame(timestamp: Duration): ImageFrame { @@ -122,8 +127,19 @@ private class TransformedImageReader( } private suspend fun initConstantData(frame: ImageFrame) { - if (!::constantData.isInitialized) { - constantData = processor.constantData(frame, flow, outputFormat) + if (::constantData.isInitialized) return + mutex.withLock { + if (!::constantData.isInitialized) { + constantData = processor.constantData(frame, flow, outputFormat) + } + } + } + + infix fun then(after: ImageProcessor<*>): TransformedImageReader<*> { + return if (after is IdentityImageProcessor) { + this + } else { + TransformedImageReader(reader, processor then after, outputFormat) } } @@ -131,4 +147,17 @@ private class TransformedImageReader( reader, processor, ) + + override fun toString(): String { + return "TransformedImageReader(" + + "reader=$reader" + + ", processor=$processor" + + ", outputFormat='$outputFormat'" + + ", mutex=$mutex" + + ", constantData=${ + if (::constantData.isInitialized) constantData.toString() + else "[not initialized]" + }" + + ")" + } } diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ZippedImageReader.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ZippedImageReader.kt index 937c1781..dff6f40b 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ZippedImageReader.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/media/reader/ZippedImageReader.kt @@ -75,4 +75,12 @@ class ZippedImageReader( ZippedImageReader(firstReader.changeSpeed(speedMultiplier), secondReader.changeSpeed(speedMultiplier)) override suspend fun close() = closeAll(firstReader, secondReader) + + override fun toString(): String { + return "ZippedImageReader(" + + "firstReader=$firstReader" + + ", secondReader=$secondReader" + + ", firstControlling=$firstControlling" + + ")" + } } diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/SpinTask.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/SpinTask.kt index a9205b4c..5d0bbc28 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/SpinTask.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/SpinTask.kt @@ -28,7 +28,6 @@ private const val BASE_FRAMES_PER_ROTATION = 150.0 private data class SpinConfig( private val spinSpeed: Double, private val backgroundColor: Color?, - private val afterProcessor: ImageProcessor<*>? = null, ) : MediaProcessingConfig { private val rotationDuration: Duration = run { @@ -49,23 +48,16 @@ private data class SpinConfig( spinSpeed, rotationDuration, backgroundColor, - ) then afterProcessor + ) return ConstantFrameDurationMediaReader(imageReader, SPIN_FRAME_DURATION, totalDuration).transform( processor, outputFormat, ) } - override fun transformOutputFormat(inputFormat: String): String = - if (isStaticOnly(inputFormat)) "gif" + override fun transformOutputFormat(inputFormat: String): String { + return if (isStaticOnly(inputFormat)) "gif" else inputFormat - - override fun then(after: MediaProcessingConfig): MediaProcessingConfig { - return if (after is SimpleMediaProcessingConfig) { - copy(afterProcessor = afterProcessor then after.processor) - } else { - super.then(after) - } } } diff --git a/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/TemplateTask.kt b/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/TemplateTask.kt index b96605bf..32c501c2 100644 --- a/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/TemplateTask.kt +++ b/core/src/main/kotlin/io/github/shaksternano/borgar/core/task/TemplateTask.kt @@ -32,7 +32,6 @@ private data class TemplateConfig( private val template: Template, private val text: String?, private val nonTextParts: Map, - private val afterProcessor: ImageProcessor<*>? = null, ) : MediaProcessingConfig { override val outputName: String = template.resultName @@ -41,10 +40,10 @@ private data class TemplateConfig( if (text.isNullOrBlank()) { val templateReader = template.getImageReader() val zipped = ZippedImageReader(imageReader, templateReader) - val processor = TemplateImageContentProcessor(template) then afterProcessor + val processor = TemplateImageContentProcessor(template) zipped.transform(processor, outputFormat) } else { - val processor = TemplateTextContentProcessor(text, nonTextParts, template) then afterProcessor + val processor = TemplateTextContentProcessor(text, nonTextParts, template) imageReader.transform(processor, outputFormat) } @@ -56,14 +55,6 @@ private data class TemplateConfig( } else { inputFormat } - - override fun then(after: MediaProcessingConfig): MediaProcessingConfig { - return if (after is SimpleMediaProcessingConfig) { - copy(afterProcessor = afterProcessor then after.processor) - } else { - super.then(after) - } - } } private class TemplateImageContentProcessor(