diff --git a/e2e-test/references/FiltersE2ETestCase.alt3.png b/e2e-test/references/FiltersE2ETestCase.alt3.png new file mode 100644 index 000000000..3e05f40ef Binary files /dev/null and b/e2e-test/references/FiltersE2ETestCase.alt3.png differ diff --git a/korge-sandbox/src/commonMain/kotlin/FiltersSample.kt b/korge-sandbox/src/commonMain/kotlin/FiltersSample.kt index 68eda2338..395255eda 100644 --- a/korge-sandbox/src/commonMain/kotlin/FiltersSample.kt +++ b/korge-sandbox/src/commonMain/kotlin/FiltersSample.kt @@ -2,10 +2,7 @@ import com.soywiz.klock.* import com.soywiz.korge.time.* import com.soywiz.korge.view.* import com.soywiz.korge.view.filter.* -import com.soywiz.korim.bitmap.* -import com.soywiz.korim.color.* import com.soywiz.korim.format.* -import com.soywiz.korio.async.* import com.soywiz.korio.file.std.* import com.soywiz.korio.lang.* @@ -51,7 +48,7 @@ object FiltersE2ETestCase : E2ETestCase() { val bitmap = resourcesVfs["korge.png"].readBitmap().toBMP32().premultiplied() println("PREPARING VIEWS...") image(bitmap).scale(.5).position(0, 0).addFilter(WaveFilter(time = 0.5.seconds)) - image(bitmap).scale(.5).position(256, 0).addFilter(BlurFilter(initialRadius = 6.0)) + image(bitmap).scale(.5).position(256, 0).addFilter(BlurFilter(radius = 6.0)) //image(bitmap).scale(.5).position(256, 0).addFilter(BlurFilter(initialRadius = 4.0)) image(bitmap).scale(.5).position(512, 0).addFilter(TransitionFilter(TransitionFilter.Transition.SWEEP, reversed = false, smooth = true, ratio = 0.5)) image(bitmap).scale(.5).position(0, 256).addFilter(PageFilter(hratio = 0.5, hamplitude1 = 20.0)) diff --git a/korge-sandbox/src/commonMain/kotlin/Main.kt b/korge-sandbox/src/commonMain/kotlin/Main.kt index fbec73960..bcdd9d5b7 100644 --- a/korge-sandbox/src/commonMain/kotlin/Main.kt +++ b/korge-sandbox/src/commonMain/kotlin/Main.kt @@ -32,7 +32,9 @@ import kotlin.random.* suspend fun main() = Korge(bgcolor = Colors.DARKCYAN.mix(Colors.BLACK, 0.8), clipBorders = false //, debugAg = true ) { - mainCustomSolidRectShader() + //mainFiltersRenderToBitmap() + mainBlur() + //mainCustomSolidRectShader() //mainMipmaps() //mainColorTransformFilter() //mainExifTest() diff --git a/korge-sandbox/src/commonMain/kotlin/MainBlur.kt b/korge-sandbox/src/commonMain/kotlin/MainBlur.kt new file mode 100644 index 000000000..ecbfb516e --- /dev/null +++ b/korge-sandbox/src/commonMain/kotlin/MainBlur.kt @@ -0,0 +1,160 @@ +import com.soywiz.kmem.* +import com.soywiz.kmem.clamp +import com.soywiz.korge.input.* +import com.soywiz.korge.ui.* +import com.soywiz.korge.view.* +import com.soywiz.korge.view.filter.* +import com.soywiz.korim.color.* +import com.soywiz.korim.format.* +import com.soywiz.korim.text.* +import com.soywiz.korio.async.* +import com.soywiz.korio.file.std.* +import com.soywiz.korma.geom.* +import com.soywiz.korma.math.* + +suspend fun Stage.mainBlur() { + solidRect(stage.width, stage.height, Colors.WHITE) + val bitmap = resourcesVfs["korim.png"].readBitmap() + + val initialBlur = 6.0 + var filterScale = 1.0 + + val blur0a = DirectionalBlurFilter(angle = 0.degrees, radius = initialBlur) + val blur0b = DirectionalBlurFilter(angle = 90.degrees, radius = initialBlur) + val blur0c = DirectionalBlurFilter(angle = 45.degrees, radius = initialBlur) + val blur1 = BlurFilter(initialBlur) + //val blur1 = DirectionalBlurFilter(angle = 0.degrees, radius = 32.0) + //val blur1 = DirectionalBlurFilter(angle = 90.degrees, radius = 32.0) + val blur2 = OldBlurFilter(initialBlur) + + val image0b = image(bitmap).xy(700, 100).filters(blur0b) + val image0a = image(bitmap).xy(700, 400).filters(blur0a) + val image0c = image(bitmap).xy(900, 100).filters(blur0c) + + val image1 = image(bitmap) + .xy(100, 100) + .filters(blur1) + + val rotatedBitmap = image(bitmap) + .xy(150, 300) + .scale(0.75) + .anchor(Anchor.CENTER) + .rotation(45.degrees) + .filters(blur1) + + val image2 = image(bitmap) + //solidRect(128, 128, Colors.RED) + .xy(300, 100) + .filters(blur2) + //.visible(false) + + val dropshadowFilter = DropshadowFilter(blurRadius = 1.0, shadowColor = Colors.RED.withAd(0.3)) + val image3 = image(bitmap).xy(500, 100).filters(dropshadowFilter) + + val colorMatrixFilter = ColorMatrixFilter(ColorMatrixFilter.SEPIA_MATRIX, blendRatio = 0.5) + val image4 = image(bitmap).xy(500, 250).filters(colorMatrixFilter) + + val transitionFilter = TransitionFilter(TransitionFilter.Transition.CIRCULAR, reversed = false, ratio = 0.5) + val image4b = image(bitmap).xy(370, 250).filters(transitionFilter) + + val pageFilter = PageFilter() + val image5 = image(bitmap).xy(500, 450).filters(pageFilter) + + val waveFilter = WaveFilter() + val image6 = image(bitmap).xy(500, 600).filters(waveFilter) + + val flagFilter = FlagFilter() + val image7 = image(bitmap).xy(700, 600).filters(flagFilter) + + val image8 = image(bitmap).xy(900, 600).filters(blur1, waveFilter, blur1, pageFilter) + + uiVerticalStack(padding = 2.0, width = 370.0) { + xy(50, 400) + uiHorizontalFill { + uiText("Blur radius").apply { textColor = Colors.BLACK } + uiSlider(value = initialBlur, max = 32, step = 0.1).changed { blur1.radius = it.toDouble() } + } + uiHorizontalFill { + uiText("Drop radius").apply { textColor = Colors.BLACK } + uiSlider(value = dropshadowFilter.blurRadius.toInt(), max = 32).changed { dropshadowFilter.blurRadius = it.toDouble() } + } + uiHorizontalFill { + uiText("Drop X").apply { textColor = Colors.BLACK } + uiSlider(value = dropshadowFilter.dropX.toInt(), min = -32, max = +32).changed { dropshadowFilter.dropX = it.toDouble() } + } + uiHorizontalFill { + uiText("Drop Y").apply { textColor = Colors.BLACK } + uiSlider(value = dropshadowFilter.dropY.toInt(), min = -32, max = +32).changed { dropshadowFilter.dropY = it.toDouble() } + } + uiHorizontalFill { + uiButton("black").clicked { dropshadowFilter.shadowColor = Colors.BLACK.withAd(dropshadowFilter.shadowColor.ad) } + uiButton("red").clicked { dropshadowFilter.shadowColor = Colors.RED.withAd(dropshadowFilter.shadowColor.ad) } + uiButton("green").clicked { dropshadowFilter.shadowColor = Colors.GREEN.withAd(dropshadowFilter.shadowColor.ad) } + uiButton("blue").clicked { dropshadowFilter.shadowColor = Colors.BLUE.withAd(dropshadowFilter.shadowColor.ad) } + } + uiHorizontalFill { + uiText("Drop Alpha").apply { textColor = Colors.BLACK } + uiSlider(value = dropshadowFilter.shadowColor.a, min = 0, max = 255).changed { dropshadowFilter.shadowColor = dropshadowFilter.shadowColor.withA(it.toInt()) } + } + uiHorizontalFill { + uiText("Rotation").apply { textColor = Colors.BLACK } + uiSlider(value = rotatedBitmap.rotation.degrees.toInt(), min = 0, max = 360).changed { rotatedBitmap.rotation = it.degrees } + } + uiHorizontalFill { + uiButton("circular").clicked { transitionFilter.transition = TransitionFilter.Transition.CIRCULAR } + uiButton("diagonal1").clicked { transitionFilter.transition = TransitionFilter.Transition.DIAGONAL1 } + uiButton("diagonal2").clicked { transitionFilter.transition = TransitionFilter.Transition.DIAGONAL2 } + uiButton("sweep").clicked { transitionFilter.transition = TransitionFilter.Transition.SWEEP } + uiButton("horizontal").clicked { transitionFilter.transition = TransitionFilter.Transition.HORIZONTAL } + uiButton("vertical").clicked { transitionFilter.transition = TransitionFilter.Transition.VERTICAL } + } + uiHorizontalFill { + uiText("Blend").apply { textColor = Colors.BLACK } + uiSlider(value = 0.5, min = 0.0, max = 1.0, step = 0.1).changed { + colorMatrixFilter.blendRatio = it + pageFilter.hamplitude0 = it + transitionFilter.ratio = it + pageFilter.hratio = it + waveFilter.timeSeconds = it + flagFilter.timeSeconds = it + } + } + uiHorizontalFill { + uiText("Filter Scale").apply { textColor = Colors.BLACK } + uiSlider(value = 1.0, min = 0.2, max = 2.0, step = 0.1).changed { + filterScale = it + } + } + } + + addUpdater { + //blur2.radius = blur1.radius + blur2.radius = blur1.radius / 2.0 + blur0a.radius = blur1.radius + blur0b.radius = blur1.radius + blur0c.radius = blur1.radius + + image0a.filterScale = filterScale + image0b.filterScale = filterScale + image0c.filterScale = filterScale + image1.filterScale = filterScale + rotatedBitmap.filterScale = filterScale + image2.filterScale = filterScale + image3.filterScale = filterScale + image4.filterScale = filterScale + image5.filterScale = filterScale + image6.filterScale = filterScale + image4b.filterScale = filterScale + image7.filterScale = filterScale + image8.filterScale = filterScale + + //println(blur1.radius) + } + + /* + while (true) { + tween(blur1::radius[0.0], time = 1.seconds) + tween(blur1::radius[32.0], time = 1.seconds) + } + */ +} diff --git a/korge-sandbox/src/commonMain/kotlin/MainExifTest.kt b/korge-sandbox/src/commonMain/kotlin/MainExifTest.kt index dd5348065..229b4a1ab 100644 --- a/korge-sandbox/src/commonMain/kotlin/MainExifTest.kt +++ b/korge-sandbox/src/commonMain/kotlin/MainExifTest.kt @@ -12,7 +12,7 @@ suspend fun Stage.mainExifTest() { val info = file.readBitmapInfo() image(file.readBitmapSliceWithOrientation()) .scale(0.2) - .filters(BlurFilter()) + .filters(OldBlurFilter()) //println(info) } diff --git a/korge-sandbox/src/commonMain/kotlin/MainFilterScale.kt b/korge-sandbox/src/commonMain/kotlin/MainFilterScale.kt index 6e7d4d193..bc4c65168 100644 --- a/korge-sandbox/src/commonMain/kotlin/MainFilterScale.kt +++ b/korge-sandbox/src/commonMain/kotlin/MainFilterScale.kt @@ -15,10 +15,8 @@ suspend fun Stage.mainFilterScale() { //.filters(BlurFilter()) //.filters(WaveFilter()) - val combo = uiComboBox(items = listOf(0.0, 0.01, 0.05, 0.075, 0.125, 0.25, 0.44, 0.5, 0.75, 0.95, 0.99, 1.0)).xy(400, 100) - combo.onSelectionUpdate { - image.filterScale = it.selectedItem ?: 1.0 - } + val combo = uiSlider(value = 1.0, max = 1.0, step = 0.01).xy(400, 100).changed { image.filterScale = it } + //val combo = uiComboBox(items = listOf(0.0, 0.01, 0.05, 0.075, 0.125, 0.25, 0.44, 0.5, 0.75, 0.95, 0.99, 1.0)).xy(400, 100).onSelectionUpdate { image.filterScale = it.selectedItem ?: 1.0 } // This reproduces a bug (black right and bottom border) at least on macOS with M1 image.filterScale = 0.99 diff --git a/korge-sandbox/src/commonMain/kotlin/MainFiltersRenderToBitmap.kt b/korge-sandbox/src/commonMain/kotlin/MainFiltersRenderToBitmap.kt new file mode 100644 index 000000000..29f2f2099 --- /dev/null +++ b/korge-sandbox/src/commonMain/kotlin/MainFiltersRenderToBitmap.kt @@ -0,0 +1,27 @@ +import com.soywiz.klock.* +import com.soywiz.korge.view.* +import com.soywiz.korge.view.filter.* +import com.soywiz.korim.format.* +import com.soywiz.korio.file.std.* + +suspend fun Stage.mainFiltersRenderToBitmap() { + println("LOADING IMAGE...") + val bitmap = resourcesVfs["korge.png"].readBitmap() + val container = FixedSizeContainer(width, height).apply { + //scale(2.0, 2.0) + println("PREPARING VIEWS...") + image(bitmap).scale(.5).position(0, 0).addFilter(WaveFilter(time = 0.5.seconds)) + //image(bitmap).scale(.5).position(256, 0).addFilter(DirectionalBlurFilter(radius = 32.0)) + image(bitmap).scale(.5).position(256, 0).addFilter(BlurFilter(radius = 32.0)) + image(bitmap).scale(.5).position(512, 0).addFilter(TransitionFilter(TransitionFilter.Transition.SWEEP, reversed = false, smooth = true, ratio = 0.5)) + image(bitmap).scale(.5).position(0, 256).addFilter(PageFilter(hratio = 0.5, hamplitude1 = 20.0)) + image(bitmap).scale(.5).position(256, 256).addFilter(Convolute3Filter(Convolute3Filter.KERNEL_SHARPEN)) + image(bitmap).scale(.5).position(512, 256).addFilter(SwizzleColorsFilter("bgga")) + println("VIEWS PREPARED") + } + + //image(stage.renderToBitmap(views)).scale(0.4).xy(800, 50) + addChild(container) + image(container.renderToBitmap(views)).scale(1.0).xy(50, 50) + //container.visible = false +} diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/render/RenderContext.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/render/RenderContext.kt index 5efa6aff1..da2b359c2 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/render/RenderContext.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/render/RenderContext.kt @@ -76,6 +76,11 @@ class RenderContext constructor( /** Pool of [Point] objects that could be used temporarily by renders */ val pointPool = Pool(reset = { it.setTo(0, 0) }, preallocate = 8) { Point() } + val tempMargin: MutableMarginInt = MutableMarginInt() + val tempMatrix: Matrix = Matrix() + + val identityMatrix = Matrix() + /** * Allows to toggle whether stencil-based masks are enabled or not. */ diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/render/Texture.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/render/Texture.kt index faa903d43..49532a931 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/render/Texture.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/render/Texture.kt @@ -55,13 +55,13 @@ class Texture( /** Wether the texture is multiplied or not */ val premultiplied get() = base.premultiplied /** Left position of the region of the texture in pixels */ - val x = left + val x: Int get() = left /** Top position of the region of the texture in pixels */ - val y = top + val y: Int get() = top /** Width of this texture region in pixels */ - override val width = right - left + override val width: Int get() = right - left /** Height of this texture region in pixels */ - override val height = bottom - top + override val height: Int get() = bottom - top /** Left coord of the texture region as a ratio (a value between 0 and 1) */ val x0: Float = (left).toFloat() / base.width.toFloat() @@ -105,6 +105,14 @@ class Texture( return Texture(base, tleft, ttop, tright, tbottom) } + fun sliceBoundsUnclamped(left: Int, top: Int, right: Int, bottom: Int): Texture { + val tleft = (this.x + left) + val tright = (this.x + right) + val ttop = (this.y + top) + val tbottom = (this.y + bottom) + return Texture(base, tleft, ttop, tright, tbottom) + } + companion object { /** * Creates a [Texture] from a texture [agBase] and its wanted size [width], [height]. @@ -126,6 +134,9 @@ class Texture( override fun close() = base.close() override fun toString(): String = "Texture($base, (x=$x, y=$y, width=$width, height=$height))" + + fun xcoord(x: Int): Float = (this.x + x).toFloat() / base.width.toFloat() + fun ycoord(y: Int): Float = (this.y + y).toFloat() / base.height.toFloat() } //suspend fun VfsFile.readTexture(ag: AG, imageFormats: ImageFormats, mipmaps: Boolean = true): Texture { diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/View.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/View.kt index 1f7d90d27..a0e2fda63 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/View.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/View.kt @@ -5,6 +5,7 @@ package com.soywiz.korge.view import com.soywiz.kds.* import com.soywiz.kds.iterators.* import com.soywiz.klock.* +import com.soywiz.kmem.* import com.soywiz.korev.* import com.soywiz.korge.baseview.* import com.soywiz.korge.component.* @@ -844,11 +845,13 @@ abstract class View internal constructor( /** Usually a value between [0.0, 1.0] */ var filterScale: Double = 1.0 + set(value) { + field = value.clamp(0.03125, 1.5) + } fun renderFiltered(ctx: RenderContext, filter: Filter) { val bounds = getLocalBoundsOptimizedAnchored() - val borderEffect = filter.border ctx.matrixPool.alloc { tempMat2d -> val tryFilterScale = filterScale val texWidthNoBorder = (bounds.width * tryFilterScale).toInt().coerceAtLeast(1) @@ -856,32 +859,15 @@ abstract class View internal constructor( val realFilterScale = (texWidthNoBorder.toDouble() / bounds.width) - val texWidth = texWidthNoBorder + borderEffect * 2 - val texHeight = texHeightNoBorder + borderEffect * 2 + val texWidth = texWidthNoBorder + val texHeight = texHeightNoBorder - val addx = -bounds.x + borderEffect - val addy = -bounds.y + borderEffect + val addx = -bounds.x + val addy = -bounds.y //println("FILTER: $texWidth, $texHeight : $globalMatrixInv, $globalMatrix, addx=$addx, addy=$addy, renderColorAdd=$renderColorAdd, renderColorMulInt=$renderColorMulInt, blendMode=$blendMode") //println("FILTER($this): $texWidth, $texHeight : bounds=${bounds} addx=$addx, addy=$addy, renderColorAdd=$renderColorAdd, renderColorMul=$renderColorMul, blendMode=$blendMode") - /* - run { - val bmp = ctx.renderToBitmap(texWidth, texHeight) { - tempMat2d.copyFrom(globalMatrixInv) - tempMat2d.translate(addx, addy) - //println("globalMatrixInv:$globalMatrixInv, tempMat2d=$tempMat2d") - //println("texWidth=$texWidth, texHeight=$texHeight, $bounds, addx=$addx, addy=$addy, globalMatrix=$globalMatrix, globalMatrixInv:$globalMatrixInv, tempMat2d=$tempMat2d") - ctx.batch.setViewMatrixTemp(tempMat2d) { - renderInternal(ctx) - } - } - com.soywiz.korio.async.launchImmediately(ctx.coroutineContext) { - bmp.writeTo("/tmp/bitmap.png".uniVfs, PNG) - } - } - */ - ctx.renderToTexture(texWidth, texHeight, render = { tempMat2d.copyFrom(globalMatrixInv) //tempMat2d.copyFrom(globalMatrix) @@ -891,9 +877,11 @@ abstract class View internal constructor( //println("texWidth=$texWidth, texHeight=$texHeight, $bounds, addx=$addx, addy=$addy, globalMatrix=$globalMatrix, globalMatrixInv:$globalMatrixInv, tempMat2d=$tempMat2d") @Suppress("DEPRECATION") ctx.batch.setViewMatrixTemp(tempMat2d) { + // @TODO: Set blendMode to normal, colorMul to WHITE, colorAdd to NEUTRAL renderInternal(ctx) } }) { texture -> + //println("texWidthHeight=$texWidth,$texHeight") tempMat2d.copyFrom(globalMatrix) tempMat2d.pretranslate(-addx, -addy) tempMat2d.prescale(1.0 / realFilterScale) @@ -905,7 +893,8 @@ abstract class View internal constructor( texHeight, renderColorAdd, renderColorMul, - blendMode + blendMode, + realFilterScale ) } } diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/BlurFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/BlurFilter.kt index 361753c2f..2c9450274 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/BlurFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/BlurFilter.kt @@ -1,54 +1,25 @@ package com.soywiz.korge.view.filter -import com.soywiz.kmem.* import com.soywiz.korge.debug.* -import com.soywiz.korge.render.* import com.soywiz.korge.view.* -import com.soywiz.korim.color.* import com.soywiz.korma.geom.* import com.soywiz.korui.* -import kotlin.math.* -class BlurFilter(initialRadius: Double = 4.0) : Filter { - private val gaussianBlurs = mutableListOf() - private val composedFilters = arrayListOf() - private val composed = ComposedFilter(composedFilters) - var radius: Double = initialRadius - set(value) { field = value.clamp(0.0, 32.0) } - //override val border: Int get() = composed.border - override val border: Int get() = (radius * 3).toInt() - val nsteps get() = radius.toIntCeil() - - override fun render( - ctx: RenderContext, - matrix: Matrix, - texture: Texture, - texWidth: Int, - texHeight: Int, - renderColorAdd: ColorAdd, - renderColorMul: RGBA, - blendMode: BlendMode - ) { - val nsteps = this.nsteps - // Cache values - while (gaussianBlurs.size < nsteps) { - gaussianBlurs.add(Convolute3Filter(Matrix3D(Convolute3Filter.KERNEL_GAUSSIAN_BLUR), gaussianBlurs.size.toDouble(), applyAlpha = true)) +class BlurFilter(radius: Double) : ComposedFilter() { + companion object { + @Deprecated("", ReplaceWith("BlurFilter(radius = initialRadius)")) + operator fun invoke(initialRadius: Double = 4.0, dummy: Unit = Unit): BlurFilter = BlurFilter(radius = initialRadius) + } + private val horizontal = DirectionalBlurFilter(angle = 0.degrees, radius).also { filters.add(it) } + private val vertical = DirectionalBlurFilter(angle = 90.degrees, radius).also { filters.add(it) } + var radius: Double = radius + set(value) { + field = value + horizontal.radius = radius + vertical.radius = radius } - //println("border: $border") - - composedFilters.clear() - val scale = radius != ceil(radius) - - for (n in 0 until nsteps) { - val isLast = n == nsteps - 1 - val blur = gaussianBlurs[n] - composedFilters.add(blur) - val ratio = if (scale && isLast) 1.0 - (ceil(radius) - radius) else 1.0 - blur.weights.setToInterpolated(Convolute3Filter.KERNEL_IDENTITY, Convolute3Filter.KERNEL_GAUSSIAN_BLUR, ratio) - } - composed.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode) - } + override val isIdentity: Boolean get() = radius == 0.0 override fun buildDebugComponent(views: Views, container: UiContainer) { container.uiEditableValue(::radius) diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ComposedFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ComposedFilter.kt index bce33dd16..3bad43c5e 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ComposedFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ComposedFilter.kt @@ -1,5 +1,7 @@ package com.soywiz.korge.view.filter +import com.soywiz.kds.iterators.* +import com.soywiz.kmem.* import com.soywiz.korge.render.* import com.soywiz.korge.view.* import com.soywiz.korim.color.* @@ -9,14 +11,32 @@ import com.soywiz.korui.* /** * Allows to create a single [Filter] that will render several [filters] in order. */ -class ComposedFilter(val filters: List) : Filter { +open class ComposedFilter private constructor(val filters: MutableList, unit: Unit = Unit) : Filter { + constructor() : this(mutableListOf()) + constructor(filters: List) : this(if (filters is MutableList) filters else filters.toMutableList()) constructor(vararg filters: Filter) : this(filters.toList()) override val allFilters: List get() = filters.flatMap { it.allFilters } - override val border get() = filters.sumBy { it.border } + override fun computeBorder(out: MutableMarginInt) { + var sumLeft = 0 + var sumTop = 0 + var sumRight = 0 + var sumBottom = 0 + filters.fastForEach { + it.computeBorder(out) + sumLeft += out.left + sumRight += out.right + sumTop += out.top + sumBottom += out.bottom + } + out.setTo(sumTop, sumRight, sumBottom, sumLeft) + //println(out) + } - override fun render( + open val isIdentity: Boolean get() = false + + final override fun render( ctx: RenderContext, matrix: Matrix, texture: Texture, @@ -24,17 +44,13 @@ class ComposedFilter(val filters: List) : Filter { texHeight: Int, renderColorAdd: ColorAdd, renderColorMul: RGBA, - blendMode: BlendMode + blendMode: BlendMode, + filterScale: Double, ) { - if (filters.isEmpty()) { - IdentityFilter.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode) - } else { - renderIndex(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode, filters.size - 1) - } + if (isIdentity) return IdentityFilter.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode, filterScale) + renderIndex(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode, filterScale, filters.size - 1) } - private val identity = Matrix() - fun renderIndex( ctx: RenderContext, matrix: Matrix, @@ -44,25 +60,18 @@ class ComposedFilter(val filters: List) : Filter { renderColorAdd: ColorAdd, renderColorMul: RGBA, blendMode: BlendMode, - level: Int + filterScale: Double, + level: Int, ) { + if (level < 0 || filters.isEmpty()) { + return IdentityFilter.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode, filterScale) + } //println("ComposedFilter.renderIndex: $level") val filter = filters[filters.size - level - 1] - val newTexWidth = (texWidth + filter.border) - val newTexHeight = (texHeight + filter.border) - // @TODO: We only need two render textures - ctx.renderToTexture(newTexWidth, newTexHeight, { - ctx.batch.setViewMatrixTemp(identity) { - filter.render(ctx, identity, texture, it.width, it.height, renderColorAdd, renderColorMul, blendMode) - } - }, { newtex -> - //println("newtex=${newtex.width}x${newtex.height}") - if (level > 0) { - renderIndex(ctx, matrix, newtex, newtex.width, newtex.height, renderColorAdd, renderColorMul, blendMode, level - 1) - } else { - IdentityFilter.render(ctx, matrix, newtex, newtex.width, newtex.height, renderColorAdd, renderColorMul, blendMode) - } - }) + + filter.renderToTextureWithBorder(ctx, matrix, texture, texWidth, texHeight, filterScale) { newtex, newmatrix -> + renderIndex(ctx, newmatrix, newtex, newtex.width, newtex.height, renderColorAdd, renderColorMul, blendMode, filterScale, level - 1) + } } override fun buildDebugComponent(views: Views, container: UiContainer) { diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Convolute3Filter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Convolute3Filter.kt index d75f2b4cb..1bf18265b 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Convolute3Filter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Convolute3Filter.kt @@ -1,5 +1,6 @@ package com.soywiz.korge.view.filter +import com.soywiz.kmem.* import com.soywiz.korag.* import com.soywiz.korag.shader.* import com.soywiz.korge.debug.* @@ -95,9 +96,12 @@ class Convolute3Filter( /** Whether or not kernel must be applied to the alpha component */ var applyAlpha by uniforms.storageFor(u_ApplyAlpha).boolDelegateX(applyAlpha) - override val border: Int get() = dist.toInt() override val fragment = FRAGMENT_SHADER + override fun computeBorder(out: MutableMarginInt) { + out.setTo(dist.toIntCeil()) + } + var namedKernel: String get() = NAMED_KERNELS.entries.firstOrNull { it.value == weights }?.key ?: NAMED_KERNELS.keys.first() set(value) { weights = (NAMED_KERNELS[value] ?: KERNEL_IDENTITY) } diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/DirectionalBlurFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/DirectionalBlurFilter.kt new file mode 100644 index 000000000..824cd219b --- /dev/null +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/DirectionalBlurFilter.kt @@ -0,0 +1,94 @@ +package com.soywiz.korge.view.filter + +import com.soywiz.kmem.* +import com.soywiz.korag.* +import com.soywiz.korag.shader.* +import com.soywiz.korag.shader.gl.* +import com.soywiz.korge.debug.* +import com.soywiz.korge.render.* +import com.soywiz.korge.view.* +import com.soywiz.korim.color.* +import com.soywiz.korma.geom.* +import com.soywiz.korui.* +import kotlin.math.* + +// https://en.wikipedia.org/wiki/Gaussian_blur +class DirectionalBlurFilter(var angle: Angle = 0.degrees, var radius: Double = 4.0) : ShaderFilter() { + companion object { + private val u_radius = Uniform("u_radius", VarType.Float1) + private val u_constant1 = Uniform("u_constant1", VarType.Float1) + private val u_constant2 = Uniform("u_constant2", VarType.Float1) + private val u_direction = Uniform("u_direction", VarType.Float2) + + val FRAGMENT = FragmentShader { + val loopLen = createTemp(Int1) + val gaussianResult = createTemp(Float1) + IF (u_radius lt 1f.lit) { + SET(out, tex(fragmentCoords)) + } ELSE { + //run { + SET(out, vec4(0f.lit, 0f.lit, 0f.lit, 0f.lit)) + SET(loopLen, int(ceil(u_radius))) + FOR_0_UNTIL_FIXED_BREAK(loopLen, maxLen = 1024) { x -> + val xfloat = createTemp(Float1) + SET(xfloat, float(x)) + SET(gaussianResult, u_constant1 * exp((-xfloat * xfloat) * u_constant2)) + SET(out, out + (tex(fragmentCoords + (u_direction * xfloat)) * gaussianResult)) + IF(x ne 0.lit) { + SET(out, out + (tex(fragmentCoords - (u_direction * xfloat)) * gaussianResult)) + } + } + } + //SET(out["ba"], vec2(1f.lit, 1f.lit)) + //SET(out["a"], 1f.lit) + }.also { + //println(it.toNewGlslString(GlslConfig())) + } + } + + private val qfactor: Double = sqrt(2 * ln(255.0)) + + //private val rradius: Double get() = (radius * ln(radius).coerceAtLeast(1.0)).coerceAtLeast(0.0) + private val rradius: Double get() = (radius * qfactor) + + override fun computeBorder(out: MutableMarginInt) { + val radius = this.rradius + out.setTo( + (angle.sine.absoluteValue * radius).toIntCeil(), + (angle.cosine.absoluteValue * radius).toIntCeil(), + ) + } + + private fun gaussian(x: Double, constant1: Double, constant2: Double): Double = constant1 * exp((-x * x) * constant2) + + override fun updateUniforms(ctx: RenderContext, filterScale: Double) { + val radius = this.rradius * filterScale + //println("rradius=$rradius") + //val sigma = max(radius / 3.0, 0.9) + val sigma = (radius + 1) / qfactor + //val sigma = 128.0 + //println("radius=$radius, sigma=$sigma") + val constant1 = 1.0 / (sigma * sqrt(2.0 * PI)) + val constant2 = 1.0 / (2.0 * sigma * sigma) + + var scaleSum = 0.0 + for (n in 0 until radius.toIntCeil()) { + val gauss = gaussian(n.toDouble(), constant1, constant2) + scaleSum += if (n != 0) gauss * 2 else gauss + } + + uniforms[u_radius] = radius + uniforms[u_constant1] = constant1 * (1.0 / scaleSum) + uniforms[u_constant2] = constant2 + uniforms[u_direction] = Vector3D(angle.cosine, angle.sine, 0.0) + } + + override val fragment: FragmentShader get() = FRAGMENT + + override val isIdentity: Boolean get() = radius == 0.0 + + override fun buildDebugComponent(views: Views, container: UiContainer) { + container.uiEditableValue(::angle) + container.uiEditableValue(::radius) + } +} diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/DropShadowFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/DropShadowFilter.kt index e4465ced3..527b9c6d1 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/DropShadowFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/DropShadowFilter.kt @@ -1,21 +1,28 @@ package com.soywiz.korge.view.filter +import com.soywiz.kmem.* import com.soywiz.korge.render.* import com.soywiz.korge.view.* import com.soywiz.korim.color.* import com.soywiz.korma.geom.* +import kotlin.math.* open class DropshadowFilter( var dropX: Double = 10.0, var dropY: Double = 10.0, - val shadowColor: RGBA = Colors.BLACK.withAd(0.75), - val blurRadius: Double = 4.0, - val smoothing: Boolean = true + var shadowColor: RGBA = Colors.BLACK.withAd(0.75), + var blurRadius: Double = 4.0, + var smoothing: Boolean = true ) : Filter { - private val tm: Matrix = Matrix() - private val blur = BlurFilter(16.0) - private val identity = Matrix() + + override fun computeBorder(out: MutableMarginInt) { + blur.computeBorder(out) + out.right += dropX.absoluteValue.toIntCeil() + out.left += dropX.absoluteValue.toIntCeil() + out.top += dropY.absoluteValue.toIntCeil() + out.bottom += dropY.absoluteValue.toIntCeil() + } override fun render( ctx: RenderContext, @@ -25,40 +32,38 @@ open class DropshadowFilter( texHeight: Int, renderColorAdd: ColorAdd, renderColorMul: RGBA, - blendMode: BlendMode + blendMode: BlendMode, + filterScale: Double, ) { //println(blur.border) blur.radius = blurRadius - val newTexWidth = texWidth + blur.border - val newTexHeight = texHeight + blur.border - ctx.renderToTexture(newTexWidth, newTexHeight, { - identity.identity() - identity.translate(blur.border, blur.border) - blur.render(ctx, identity, texture, newTexWidth, newTexHeight, renderColorAdd, renderColorMul, blendMode) - }, { newtex -> - tm.copyFrom(matrix) - tm.pretranslate(dropX - blur.border, dropY - blur.border) + + blur.renderToTextureWithBorder(ctx, matrix, texture, texWidth, texHeight, filterScale) { newtex, matrix -> ctx.useBatcher { batch -> batch.drawQuad( newtex, - m = tm, + m = matrix, + x = (dropX * filterScale).toFloat(), + y = (dropY * filterScale).toFloat(), filtering = smoothing, colorAdd = ColorAdd(+255, +255, +255, 0), colorMul = shadowColor, blendFactors = blendMode.factors, program = BatchBuilder2D.getTextureLookupProgram(texture.premultiplied, add = BatchBuilder2D.AddType.PRE_ADD) ) - - batch.drawQuad( - texture, - m = matrix, - filtering = smoothing, - colorAdd = renderColorAdd, - colorMul = renderColorMul, - blendFactors = blendMode.factors, - program = BatchBuilder2D.getTextureLookupProgram(texture.premultiplied, add = BatchBuilder2D.AddType.NO_ADD) - ) } - }) + } + + ctx.useBatcher { batch -> + batch.drawQuad( + texture, + m = matrix, + filtering = smoothing, + colorAdd = renderColorAdd, + colorMul = renderColorMul, + blendFactors = blendMode.factors, + program = BatchBuilder2D.getTextureLookupProgram(texture.premultiplied, add = BatchBuilder2D.AddType.NO_ADD) + ) + } } } diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Filter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Filter.kt index eafa060ec..1e882d38b 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Filter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/Filter.kt @@ -1,5 +1,6 @@ package com.soywiz.korge.view.filter +import com.soywiz.kmem.* import com.soywiz.korag.* import com.soywiz.korag.shader.* import com.soywiz.korge.debug.* @@ -14,7 +15,7 @@ import com.soywiz.korui.* * * A filter is in charge of rendering a precomputed texture of a [View]. * - * [Filter] defines a [border]. The border is the amount of pixels, the generated texture should be grown from each side: left, top, right and bottom. + * [Filter] defines a [computeBorder]. The border is the amount of pixels, the generated texture should be grown from each side: left, top, right and bottom. * For example, a Gauissan Blur effect would require a bigger texture to blur the edges. * * [Filter] defines how to render the precomputed texture of the View inside the [render] method. @@ -40,11 +41,16 @@ interface Filter : KorgeDebugNode { * A 0 value means that the texture should be passed with its original size. * A 1 value means that the texture should be passed width 2 more pixels of width and height (1 left, 1 right), (1 top, 1 bottom) */ + @Deprecated("") val border: Int get() = 0 + fun computeBorder(out: MutableMarginInt) { + out.setTo(border) + } + /** * The method in charge of rendering the texture transformed using [ctx] [RenderContext] and [matrix]. - * The method receives a [texture] that should be the original image with [border] additional pixels on each side. + * The method receives a [texture] that should be the original image with [computeBorder] additional pixels on each side. */ fun render( ctx: RenderContext, @@ -54,9 +60,52 @@ interface Filter : KorgeDebugNode { texHeight: Int, renderColorAdd: ColorAdd, renderColorMul: RGBA, - blendMode: BlendMode + blendMode: BlendMode, + filterScale: Double, ) override fun buildDebugComponent(views: Views, container: UiContainer) { } } + +fun Filter.getBorder(out: MutableMarginInt = MutableMarginInt()): MarginInt { + computeBorder(out) + return out +} + +fun Filter.renderToTextureWithBorder( + ctx: RenderContext, + matrix: Matrix, + texture: Texture, + texWidth: Int, + texHeight: Int, + filterScale: Double, + block: (texture: Texture, matrix: Matrix) -> Unit, +) { + val filter = this + val margin = filter.getBorder(ctx.tempMargin) + + val borderLeft = (margin.left * filterScale).toIntCeil() + val borderTop = (margin.top * filterScale).toIntCeil() + + val newTexWidth = texWidth + (margin.leftPlusRight * filterScale).toIntCeil() + val newTexHeight = texHeight + (margin.topPlusBottom * filterScale).toIntCeil() + + //println("texWidth=$newTexWidth,$newTexHeight") + + ctx.renderToTexture(newTexWidth, newTexHeight, { + ctx.matrixPool.alloc { matrix -> + matrix.identity() + matrix.translate(borderLeft, borderTop) + ctx.batch.setViewMatrixTemp(ctx.identityMatrix) { + filter.render(ctx, matrix, texture, newTexWidth, newTexHeight, ColorAdd.NEUTRAL, Colors.WHITE, BlendMode.NORMAL, filterScale) + } + } + }) { newtex -> + ctx.matrixPool.alloc { matrix2 -> + matrix2.copyFrom(matrix) + matrix2.pretranslate(-borderLeft, -borderTop) + block(newtex, matrix2) + } + } +} diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FilterExt.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FilterExt.kt index 78df895ce..9b4ef5081 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FilterExt.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FilterExt.kt @@ -3,7 +3,6 @@ package com.soywiz.korge.view.filter import com.soywiz.kds.* import com.soywiz.korge.debug.* import com.soywiz.korge.view.* -import com.soywiz.korio.lang.* import com.soywiz.korui.* var Views.registerFilterSerialization: Boolean by Extra.Property { false } diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FlagFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FlagFilter.kt index 959e473ec..fd6a2e67e 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FlagFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/FlagFilter.kt @@ -9,6 +9,7 @@ import com.soywiz.korag.shader.VarType import com.soywiz.korag.shader.storageFor import com.soywiz.korge.debug.uiEditableValue import com.soywiz.korge.view.Views +import com.soywiz.korma.geom.* import com.soywiz.korui.UiContainer import kotlin.math.PI import kotlin.math.absoluteValue @@ -36,16 +37,16 @@ class FlagFilter( val u_Time = Uniform("time", VarType.Float1) private val FRAGMENT_SHADER = FragmentShader { - apply { - val x01 = fragmentCoords01.x - (ceil(abs(u_amplitude)) / u_TextureSize.x) - val offsetY = sin((x01 * u_crestCount - u_Time * u_cyclesPerSecond) * PI.lit) * u_amplitude * x01 - out setTo tex(vec2(fragmentCoords.x, fragmentCoords.y - offsetY)) - } + //val x01 = fragmentCoords01.x - (ceil(abs(u_amplitude)) / u_TextureSize.x) + val x01 = createTemp(Float1) + SET(x01, v_Tex01.x) + val offsetY = sin((x01 * u_crestCount - u_Time * u_cyclesPerSecond) * PI.lit) * u_amplitude * x01 + SET(out, tex(vec2(fragmentCoords.x, fragmentCoords.y - offsetY))) } } /** Maximum amplitude of the wave on the Y axis */ - var amplitude by uniforms.storageFor(u_amplitude).doubleDelegateX(amplitude) + var amplitude by scaledUniforms.storageFor(u_amplitude).doubleDelegateX(amplitude) /** Number of wave crests in the X axis */ var crestCount by uniforms.storageFor(u_crestCount).doubleDelegateX(crestCount) @@ -63,9 +64,12 @@ class FlagFilter( timeSeconds = value.seconds } - override val border: Int get() = amplitude.absoluteValue.toIntCeil() override val fragment = FRAGMENT_SHADER + override fun computeBorder(out: MutableMarginInt) { + out.setTo(amplitude.absoluteValue.toIntCeil()) + } + override fun buildDebugComponent(views: Views, container: UiContainer) { container.uiEditableValue(::amplitude) container.uiEditableValue(::crestCount) diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/IdentityFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/IdentityFilter.kt index f8ec5ac77..be51a8c1a 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/IdentityFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/IdentityFilter.kt @@ -21,7 +21,8 @@ open class IdentityFilter(val smoothing: Boolean) : Filter { texHeight: Int, renderColorAdd: ColorAdd, renderColorMul: RGBA, - blendMode: BlendMode + blendMode: BlendMode, + filterScale: Double, ) { ctx.useBatcher { batch -> batch.drawQuad( diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/OldBlurFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/OldBlurFilter.kt new file mode 100644 index 000000000..e4b1274c6 --- /dev/null +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/OldBlurFilter.kt @@ -0,0 +1,62 @@ +package com.soywiz.korge.view.filter + +import com.soywiz.kmem.* +import com.soywiz.korge.debug.* +import com.soywiz.korge.render.* +import com.soywiz.korge.view.* +import com.soywiz.korim.color.* +import com.soywiz.korma.geom.* +import com.soywiz.korui.* +import kotlin.math.* + +class OldBlurFilter(radius: Double = 4.0) : Filter { + companion object { + @Deprecated("", ReplaceWith("OldBlurFilter(radius = initialRadius)")) + operator fun invoke(initialRadius: Double = 4.0, dummy: Unit = Unit): OldBlurFilter = OldBlurFilter(radius = initialRadius) + } + + private val gaussianBlurs = mutableListOf() + private val composedFilters = arrayListOf() + private val composed = ComposedFilter(composedFilters) + var radius: Double = radius + set(value) { field = value.clamp(0.0, 32.0) } + //override val border: Int get() = composed.border + override fun computeBorder(out: MutableMarginInt) = composed.computeBorder(out) + + override fun render( + ctx: RenderContext, + matrix: Matrix, + texture: Texture, + texWidth: Int, + texHeight: Int, + renderColorAdd: ColorAdd, + renderColorMul: RGBA, + blendMode: BlendMode, + filterScale: Double, + ) { + val radius = this.radius * sqrt(filterScale) // + val nsteps = (radius).toIntCeil() + // Cache values + while (gaussianBlurs.size < nsteps) { + gaussianBlurs.add(Convolute3Filter(Matrix3D(Convolute3Filter.KERNEL_GAUSSIAN_BLUR), gaussianBlurs.size.toDouble(), applyAlpha = true)) + } + + //println("border: $border") + + composedFilters.clear() + val scale = radius != ceil(radius) + + for (n in 0 until nsteps) { + val isLast = n == nsteps - 1 + val blur = gaussianBlurs[n] + composedFilters.add(blur) + val ratio = if (scale && isLast) 1.0 - (ceil(radius) - radius) else 1.0 + blur.weights.setToInterpolated(Convolute3Filter.KERNEL_IDENTITY, Convolute3Filter.KERNEL_GAUSSIAN_BLUR, ratio) + } + composed.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode, filterScale) + } + + override fun buildDebugComponent(views: Views, container: UiContainer) { + container.uiEditableValue(::radius) + } +} diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/PageFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/PageFilter.kt index b23534c39..89c4933bd 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/PageFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/PageFilter.kt @@ -1,10 +1,11 @@ package com.soywiz.korge.view.filter +import com.soywiz.kmem.* import com.soywiz.korag.* import com.soywiz.korag.shader.* import com.soywiz.korge.debug.* -import com.soywiz.korge.internal.* import com.soywiz.korge.view.* +import com.soywiz.korma.geom.* import com.soywiz.korui.* import kotlin.math.* @@ -29,27 +30,29 @@ class PageFilter( private fun Program.Builder.sin01(arg: Operand) = sin(arg * (PI.lit * 0.5.lit)) private val FRAGMENT_SHADER = FragmentShader { + val x01 = DefaultShaders.t_Temp0["zw"] + SET(x01, v_Tex01) for (n in 0..1) { - val vr = fragmentCoords01[n] + val vr = x01[n] val offset = u_Offset[n] val amplitudes = if (n == 0) u_HAmplitude else u_VAmplitude val tmp = DefaultShaders.t_Temp0[n] IF(vr lt offset) { - val ratio = (vr - 0.0.lit) / offset - tmp setTo mix(amplitudes[0], amplitudes[1], sin01(ratio)) + val ratio = ((vr - 0.0.lit) / offset) + SET(tmp, mix(amplitudes[0], amplitudes[1], sin01(ratio))) } ELSE { - val ratio = (vr - offset) / (1.0.lit - offset) - tmp setTo mix(amplitudes[2], amplitudes[1], sin01(1.0.lit + ratio)) + val ratio = 1.0.lit + ((vr - offset) / (1.0.lit - offset)) + SET(tmp, mix(amplitudes[2], amplitudes[1], sin01(ratio))) } } - out setTo tex(fragmentCoords + DefaultShaders.t_Temp0["yx"]) + SET(out, tex(fragmentCoords + DefaultShaders.t_Temp0["yx"])) } } private val offset = uniforms.storageFor(u_Offset) - private val hamplitude = uniforms.storageFor(u_HAmplitude) - private val vamplitude = uniforms.storageFor(u_VAmplitude) + private val hamplitude = scaledUniforms.storageFor(u_HAmplitude) + private val vamplitude = scaledUniforms.storageFor(u_VAmplitude) var hratio by offset.doubleDelegateX(default = hratio) var hamplitude0 by hamplitude.doubleDelegate(0, default = hamplitude0) @@ -61,9 +64,12 @@ class PageFilter( var vamplitude1 by vamplitude.doubleDelegate(1, default = vamplitude1) var vamplitude2 by vamplitude.doubleDelegate(2, default = vamplitude2) - override val border: Int get() = max(max(abs(hamplitude0), abs(hamplitude1)), abs(hamplitude2)).toInt() override val fragment = FRAGMENT_SHADER + override fun computeBorder(out: MutableMarginInt) { + out.setTo(max(max(abs(hamplitude0), abs(hamplitude1)), abs(hamplitude2)).toIntCeil()) + } + override fun buildDebugComponent(views: Views, container: UiContainer) { container.uiEditableValue(::hratio) container.uiEditableValue(::hamplitude0) diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ShaderFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ShaderFilter.kt index 6120899bc..47c7613d1 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ShaderFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/ShaderFilter.kt @@ -1,19 +1,20 @@ package com.soywiz.korge.view.filter +import com.soywiz.kmem.* import com.soywiz.korag.* import com.soywiz.korag.shader.* import com.soywiz.korge.render.* import com.soywiz.korge.view.* import com.soywiz.korge.view.filter.ShaderFilter.Companion.fragmentCoords -import com.soywiz.korge.view.filter.ShaderFilter.Companion.fragmentCoords01 import com.soywiz.korge.view.filter.ShaderFilter.Companion.tex +import com.soywiz.korim.bitmap.* import com.soywiz.korim.color.* import com.soywiz.korma.geom.* /** * Abstract class for [View] [Filter]s that paints the [Texture] using a [FragmentShader] ([fragment]). * - * Inherited versions of this filter, usually inherit [border] and [fragment] properties. + * Inherited versions of this filter, usually inherit [computeBorder] and [fragment] properties. * * When building shaders by calling FragmentShader { ... }, you will have additionally access to: * [fragmentCoords], [fragmentCoords01] properties and [tex] method to be used inside the shader. @@ -22,10 +23,14 @@ abstract class ShaderFilter : Filter { companion object { //val u_Time = Uniform("time", VarType.Float1) val u_TextureSize = Uniform("effectTextureSize", VarType.Float2) + val u_MaxTexCoords = Uniform("u_MaxTexCoords", VarType.Float2) + val u_filterScale = Uniform("u_filterScale", VarType.Float1) - val Program.Builder.fragmentCoords01 get() = DefaultShaders.v_Tex["xy"] - val Program.Builder.fragmentCoords get() = fragmentCoords01 * u_TextureSize - fun Program.Builder.tex(coords: Operand) = texture2D(DefaultShaders.u_Tex, coords / u_TextureSize) + val Program.ExpressionBuilder.v_Tex01: Operand get() = (DefaultShaders.v_Tex["xy"] / u_MaxTexCoords) + + val Program.ExpressionBuilder.fragmentCoords01 get() = DefaultShaders.v_Tex["xy"] + val Program.ExpressionBuilder.fragmentCoords get() = fragmentCoords01 * u_TextureSize + fun Program.ExpressionBuilder.tex(coords: Operand) = texture2D(DefaultShaders.u_Tex, coords / u_TextureSize) protected fun createProgram(vertex: VertexShader, fragment: FragmentShader, premultiplied: Boolean): Program { return Program(vertex, fragment.appending { @@ -50,13 +55,19 @@ abstract class ShaderFilter : Filter { var filtering = true private val textureSizeHolder = FloatArray(2) + private val textureMaxTexCoords = FloatArray(2) + + val scaledUniforms = AG.UniformValues() val uniforms = AG.UniformValues( //Filter.u_Time to timeHolder, - Filter.u_TextureSize to textureSizeHolder + u_TextureSize to textureSizeHolder, + u_MaxTexCoords to textureMaxTexCoords, ) - override val border: Int = 0 + override fun computeBorder(out: MutableMarginInt) { + out.setTo(0) + } /** The [VertexShader] used this this [Filter] */ open val vertex: VertexShader = BatchBuilder2D.VERTEX @@ -67,9 +78,32 @@ abstract class ShaderFilter : Filter { private val programPremult: Program by lazy { createProgram(vertex, fragment, true) } private val programNormal: Program by lazy { createProgram(vertex, fragment, false) } - protected open fun updateUniforms(ctx: RenderContext) { + //@CallSuper + protected open fun updateUniforms(ctx: RenderContext, filterScale: Double) { + } + + private fun _updateUniforms(ctx: RenderContext, filterScale: Double) { + uniforms[u_filterScale] = filterScale + scaledUniforms.fastForEach { uniform, value -> + when (value) { + is FloatArray -> { + if (uniform !in uniforms) { + uniforms[uniform] = value.copyOf() + } + val out = (uniforms[uniform] as FloatArray) + for (n in out.indices) out[n] = (value[n] * filterScale).toFloat() + } + else -> TODO() + } + + } + updateUniforms(ctx, filterScale) } + //private var slice: MutableBmpCoordsWithInstanceBase? = null + + open val isIdentity: Boolean get() = false + override fun render( ctx: RenderContext, matrix: Matrix, @@ -78,20 +112,46 @@ abstract class ShaderFilter : Filter { texHeight: Int, renderColorAdd: ColorAdd, renderColorMul: RGBA, - blendMode: BlendMode + blendMode: BlendMode, + filterScale: Double, ) { + if (isIdentity) return IdentityFilter.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode, filterScale) + + val _margin = getBorder(ctx.tempMargin) + val marginLeft = (_margin.left * filterScale).toIntCeil() + val marginRight = (_margin.right * filterScale).toIntCeil() + val marginTop = (_margin.top * filterScale).toIntCeil() + val marginBottom = (_margin.bottom * filterScale).toIntCeil() + //if (slice == null) { + // slice = MutableBmpCoordsWithInstanceBase(texture.base, texture) + //} + //slice!!.setBasicCoords( + // texture.xcoord(-marginLeft), + // texture.ycoord(-marginTop), + // texture.xcoord(texture.width + marginRight), + // texture.ycoord(texture.height + marginBottom), + //) + //println("$this.render()") // @TODO: Precompute vertices textureSizeHolder[0] = texture.base.width.toFloat() textureSizeHolder[1] = texture.base.height.toFloat() - updateUniforms(ctx) + textureMaxTexCoords[0] = texWidth.toFloat() / texture.base.width.toFloat() + textureMaxTexCoords[1] = texHeight.toFloat() / texture.base.height.toFloat() + _updateUniforms(ctx, filterScale) ctx.useBatcher { batch -> batch.setTemporalUniforms(this.uniforms) { //println("renderColorMulInt=" + RGBA(renderColorMulInt)) //println("blendMode:$blendMode") + + val slice = texture.sliceBoundsUnclamped(-marginLeft, -marginTop, texture.width + marginRight, texture.height + marginBottom) + + //println("matrix=$matrix, slice=$slice, marginLeft=$marginLeft") batch.drawQuad( - texture, + slice, + x = -marginLeft.toFloat(), + y = -marginTop.toFloat(), m = matrix, filtering = filtering, colorAdd = renderColorAdd, diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/SwizzleColorFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/SwizzleColorFilter.kt index a325d13bb..f63bc630e 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/SwizzleColorFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/SwizzleColorFilter.kt @@ -43,8 +43,9 @@ class SwizzleColorsFilter(initialSwizzle: String = "rgba") : Filter { texHeight: Int, renderColorAdd: ColorAdd, renderColorMul: RGBA, - blendMode: BlendMode - ) = proxy.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode) + blendMode: BlendMode, + filterScale: Double, + ) = proxy.render(ctx, matrix, texture, texWidth, texHeight, renderColorAdd, renderColorMul, blendMode, filterScale) override fun buildDebugComponent(views: Views, container: UiContainer) { container.uiEditableValue(::swizzle) diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/TransitionFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/TransitionFilter.kt index c7dcaf082..e355b361c 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/TransitionFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/TransitionFilter.kt @@ -56,22 +56,24 @@ class TransitionFilter( private val u_Smooth = Uniform("u_Smooth", VarType.Float1) private val u_Ratio = Uniform("u_Ratio", VarType.Float1) private val u_Mask = Uniform("u_Mask", VarType.TextureUnit) + private val FRAGMENT_SHADER = Filter.DEFAULT_FRAGMENT.appending { - t_Temp1.x setTo texture2D(u_Mask, v_Tex["xy"]).r + val alpha = t_Temp1.x + SET(alpha, texture2D(u_Mask, v_Tex01).r) IF(u_Reversed eq 1f.lit) { - t_Temp1.x setTo 1f.lit - t_Temp1.x + SET(t_Temp1.x, 1f.lit - t_Temp1.x) } - t_Temp1.x setTo clamp(t_Temp1.x + ((u_Ratio * 2f.lit) - 1f.lit), 0f.lit, 1f.lit) + SET(alpha, clamp(alpha + ((u_Ratio * 2f.lit) - 1f.lit), 0f.lit, 1f.lit)) IF(u_Smooth ne 1f.lit) { IF(t_Temp1.x ge 1f.lit) { - t_Temp1.x setTo 1f.lit + SET(t_Temp1.x, 1f.lit) } ELSE { - t_Temp1.x setTo 0f.lit + SET(t_Temp1.x, 0f.lit) } } - out setTo (out * t_Temp1.x) - //out setTo texture2D(u_Mask, v_Tex["xy"]) - //out setTo vec4(1.lit, 1.lit, 1.lit, 1.lit) + SET(out, (out * alpha)) + //SET(out, texture2D(u_Mask, v_Tex01)) + //SET(out, vec4(1.lit, 0.lit, 1.lit, 1.lit)) } } @@ -88,7 +90,7 @@ class TransitionFilter( var smooth by uniforms.storageFor(u_Smooth).boolDelegateX(smooth) var ratio by s_ratio.doubleDelegateX(ratio) - override fun updateUniforms(ctx: RenderContext) { + override fun updateUniforms(ctx: RenderContext, filterScale: Double) { textureUnit.texture = ctx.getTex(transition.bmp).base } diff --git a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/WaveFilter.kt b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/WaveFilter.kt index c8528b6a4..3b019dc56 100644 --- a/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/WaveFilter.kt +++ b/korge/src/commonMain/kotlin/com/soywiz/korge/view/filter/WaveFilter.kt @@ -4,9 +4,8 @@ import com.soywiz.klock.* import com.soywiz.korag.DefaultShaders.t_Temp0 import com.soywiz.korag.shader.* import com.soywiz.korge.debug.* -import com.soywiz.korge.internal.* import com.soywiz.korge.view.* -import com.soywiz.korio.lang.* +import com.soywiz.korma.geom.* import com.soywiz.korui.* import kotlin.math.* @@ -36,15 +35,17 @@ class WaveFilter( apply { val tmpx = t_Temp0.x val tmpy = t_Temp0.y - tmpx setTo sin(PI.lit * ((fragmentCoords01.x * u_crestCount.x) + u_Time * u_cyclesPerSecond.x)) - tmpy setTo sin(PI.lit * ((fragmentCoords01.y * u_crestCount.y) + u_Time * u_cyclesPerSecond.y)) - out setTo tex(fragmentCoords - vec2(tmpy * u_Amplitude.x, tmpx * u_Amplitude.y)) + val tmpxy = t_Temp0["zw"] + SET(tmpxy, v_Tex01) + SET(tmpx, sin(PI.lit * ((tmpxy.x * u_crestCount.x) + u_Time * u_cyclesPerSecond.x))) + SET(tmpy, sin(PI.lit * ((tmpxy.y * u_crestCount.y) + u_Time * u_cyclesPerSecond.y))) + SET(out, tex(fragmentCoords - vec2(tmpy * u_Amplitude.x, tmpx * u_Amplitude.y))) //out["b"] setTo ((sin(u_Time * PI) + 1.0) / 2.0) } } } - private val amplitude = uniforms.storageFor(u_Amplitude) + private val amplitude = scaledUniforms.storageFor(u_Amplitude) private val crestCount = uniforms.storageFor(u_crestCount) private val cyclesPerSecond = uniforms.storageFor(u_cyclesPerSecond) @@ -71,9 +72,12 @@ class WaveFilter( get() = timeSeconds.seconds set(value) { timeSeconds = value.seconds } - override val border: Int get() = max(amplitudeX.absoluteValue, amplitudeY.absoluteValue) override val fragment = FRAGMENT_SHADER + override fun computeBorder(out: MutableMarginInt) { + out.setTo(amplitudeY.absoluteValue, amplitudeX.absoluteValue) + } + override fun buildDebugComponent(views: Views, container: UiContainer) { container.uiEditableValue(::amplitudeX) container.uiEditableValue(::amplitudeY) diff --git a/korge/src/jvmTest/resources/korge/render/FSprites1.log b/korge/src/jvmTest/resources/korge/render/FSprites1.log index 560790305..c1660a18a 100644 --- a/korge/src/jvmTest/resources/korge/render/FSprites1.log +++ b/korge/src/jvmTest/resources/korge/render/FSprites1.log @@ -78,7 +78,7 @@ void main() { v_Tex = (vec2(mix(a_uv0.x, a_uv1.x, a_xy.x), mix(a_uv0.y, a_uv1.y, a_xy.y)) * temp1.zw); temp0.x = cos(a_rangle); temp0.y = sin(a_rangle); - temp2 = mat2(temp0.x, (0.0 - temp0.y), temp0.y, temp0.x); + temp2 = mat2(temp0.x, (-(temp0.y)), temp0.y, temp0.x); temp0.zw = (temp1.xy * a_scale); temp0.xy = (temp2 * ((a_xy - a_axy) * temp0.zw)); gl_Position = ((u_ProjMat * u_ViewMat) * vec4((temp0.xy + vec2(a_rxy.x, a_rxy.y)), 0.0, 1.0)); diff --git a/korge/src/jvmTest/resources/korge/render/FSprites2.log b/korge/src/jvmTest/resources/korge/render/FSprites2.log index 49c81b6e5..d625844b8 100644 --- a/korge/src/jvmTest/resources/korge/render/FSprites2.log +++ b/korge/src/jvmTest/resources/korge/render/FSprites2.log @@ -85,7 +85,7 @@ void main() { v_Tex = (vec2(mix(a_uv0.x, a_uv1.x, a_xy.x), mix(a_uv0.y, a_uv1.y, a_xy.y)) * temp1.zw); temp0.x = cos(a_rangle); temp0.y = sin(a_rangle); - temp2 = mat2(temp0.x, (0.0 - temp0.y), temp0.y, temp0.x); + temp2 = mat2(temp0.x, (-(temp0.y)), temp0.y, temp0.x); temp0.zw = (temp1.xy * a_scale); temp0.xy = (temp2 * ((a_xy - a_axy) * temp0.zw)); gl_Position = ((u_ProjMat * u_ViewMat) * vec4((temp0.xy + vec2(a_rxy.x, a_rxy.y)), 0.0, 1.0)); diff --git a/korge/src/jvmTest/resources/korge/render/FSprites3.log b/korge/src/jvmTest/resources/korge/render/FSprites3.log index cbb9efe36..c04dec7be 100644 --- a/korge/src/jvmTest/resources/korge/render/FSprites3.log +++ b/korge/src/jvmTest/resources/korge/render/FSprites3.log @@ -92,7 +92,7 @@ void main() { v_Tex = (vec2(mix(a_uv0.x, a_uv1.x, a_xy.x), mix(a_uv0.y, a_uv1.y, a_xy.y)) * temp1.zw); temp0.x = cos(a_rangle); temp0.y = sin(a_rangle); - temp2 = mat2(temp0.x, (0.0 - temp0.y), temp0.y, temp0.x); + temp2 = mat2(temp0.x, (-(temp0.y)), temp0.y, temp0.x); temp0.zw = (temp1.xy * a_scale); temp0.xy = (temp2 * ((a_xy - a_axy) * temp0.zw)); gl_Position = ((u_ProjMat * u_ViewMat) * vec4((temp0.xy + vec2(a_rxy.x, a_rxy.y)), 0.0, 1.0)); diff --git a/korge/src/jvmTest/resources/korge/render/FSprites4.log b/korge/src/jvmTest/resources/korge/render/FSprites4.log index 8d446e900..01ef0d079 100644 --- a/korge/src/jvmTest/resources/korge/render/FSprites4.log +++ b/korge/src/jvmTest/resources/korge/render/FSprites4.log @@ -98,7 +98,7 @@ void main() { v_Tex = (vec2(mix(a_uv0.x, a_uv1.x, a_xy.x), mix(a_uv0.y, a_uv1.y, a_xy.y)) * temp1.zw); temp0.x = cos(a_rangle); temp0.y = sin(a_rangle); - temp2 = mat2(temp0.x, (0.0 - temp0.y), temp0.y, temp0.x); + temp2 = mat2(temp0.x, (-(temp0.y)), temp0.y, temp0.x); temp0.zw = (temp1.xy * a_scale); temp0.xy = (temp2 * ((a_xy - a_axy) * temp0.zw)); gl_Position = ((u_ProjMat * u_ViewMat) * vec4((temp0.xy + vec2(a_rxy.x, a_rxy.y)), 0.0, 1.0)); diff --git a/korge/src/jvmTest/resources/korge/render/ViewFilterRetina.log b/korge/src/jvmTest/resources/korge/render/ViewFilterRetina.log index ec5b3feee..7f0ae97e1 100644 --- a/korge/src/jvmTest/resources/korge/render/ViewFilterRetina.log +++ b/korge/src/jvmTest/resources/korge/render/ViewFilterRetina.log @@ -85,7 +85,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [512.0, 512.0] -::draw.ERROR.Unexpected:[Uniform(effectTextureSize)] +::draw.uniform.Uniform(u_MaxTexCoords) = [1.0, 1.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 +::draw.ERROR.Unexpected:[Uniform(effectTextureSize), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) @@ -129,6 +131,7 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [512.0, 512.0] +::draw.uniform.Uniform(u_MaxTexCoords) = [1.0, 1.0] ::draw.uniform.Uniform(colorMatrix) = Matrix3D( [ 0.33, 0.59, 0.11, 0 ], [ 0.33, 0.59, 0.11, 0 ], @@ -136,8 +139,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) [ 0, 0, 0, 1 ], ) ::draw.uniform.Uniform(blendRatio) = [1.0, 0.0, 0.0, 0.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 ::draw.ERROR.Missing:[Uniform(u_Tex)] -::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3)] +::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) @@ -263,7 +267,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [512.0, 512.0] -::draw.ERROR.Unexpected:[Uniform(effectTextureSize)] +::draw.uniform.Uniform(u_MaxTexCoords) = [1.0, 1.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 +::draw.ERROR.Unexpected:[Uniform(effectTextureSize), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) @@ -306,6 +312,7 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [512.0, 512.0] +::draw.uniform.Uniform(u_MaxTexCoords) = [1.0, 1.0] ::draw.uniform.Uniform(colorMatrix) = Matrix3D( [ 0.33, 0.59, 0.11, 0 ], [ 0.33, 0.59, 0.11, 0 ], @@ -313,8 +320,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) [ 0, 0, 0, 1 ], ) ::draw.uniform.Uniform(blendRatio) = [1.0, 0.0, 0.0, 0.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 ::draw.ERROR.Missing:[Uniform(u_Tex)] -::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3)] +::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) @@ -435,7 +443,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [512.0, 512.0] -::draw.ERROR.Unexpected:[Uniform(effectTextureSize)] +::draw.uniform.Uniform(u_MaxTexCoords) = [1.0, 1.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 +::draw.ERROR.Unexpected:[Uniform(effectTextureSize), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) @@ -477,6 +487,7 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [512.0, 512.0] +::draw.uniform.Uniform(u_MaxTexCoords) = [1.0, 1.0] ::draw.uniform.Uniform(colorMatrix) = Matrix3D( [ 0.33, 0.59, 0.11, 0 ], [ 0.33, 0.59, 0.11, 0 ], @@ -484,8 +495,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) [ 0, 0, 0, 1 ], ) ::draw.uniform.Uniform(blendRatio) = [1.0, 0.0, 0.0, 0.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 ::draw.ERROR.Missing:[Uniform(u_Tex)] -::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3)] +::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) diff --git a/korge/src/jvmTest/resources/korge/render/ViewsJvmTestFilter.log b/korge/src/jvmTest/resources/korge/render/ViewsJvmTestFilter.log index cee0aba69..4e044986b 100644 --- a/korge/src/jvmTest/resources/korge/render/ViewsJvmTestFilter.log +++ b/korge/src/jvmTest/resources/korge/render/ViewsJvmTestFilter.log @@ -17,7 +17,7 @@ Buffer[2].afterSetMem(mem[49152]) draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.program=Program(name=BatchBuilder2D.NoPremultiplied.Tinted.NoAdd, attributes=[a_Tex, a_TexIndex, a_Col, a_Col2, a_Pos], uniforms=[u_ProjMat, u_ViewMat, u_Tex0, u_Tex1, u_Tex2, u_Tex3]) ::draw.renderState=RenderState(depthFunc=ALWAYS, depthMask=true, depthNear=0.0, depthFar=1.0, lineWidth=1.0, frontFace=BOTH) -::draw.scissor=Scissor(x=0, y=0, width=12, height=12) +::draw.scissor=Scissor(x=0, y=0, width=10, height=10) ::draw.stencil=StencilState(enabled=false, triangleFace=FRONT_AND_BACK, compareMode=ALWAYS, actionOnBothPass=KEEP, actionOnDepthFail=KEEP, actionOnDepthPassStencilFail=KEEP, referenceValue=0, readMask=255, writeMask=255) ::draw.blending=Blending(srcRGB=SOURCE_ALPHA, dstRGB=ONE_MINUS_SOURCE_ALPHA, srcA=ONE, dstA=ONE_MINUS_SOURCE_ALPHA, eqRGB=ADD, eqA=ADD) ::draw.colorMask=ColorMaskState(red=true, green=true, blue=true, alpha=true) @@ -28,8 +28,8 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) [ 0, 0, 0, 1 ], ) ::draw.uniform.Uniform(u_ViewMat) = Matrix3D( - [ 1, 0, 0, 1 ], - [ 0, 1, 0, 1 ], + [ 1, 0, 0, 0 ], + [ 0, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ], ) @@ -58,7 +58,7 @@ Buffer[2].afterSetMem(mem[49152]) draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.program=Program(name=program, attributes=[a_Tex, a_TexIndex, a_Col, a_Col2, a_Pos], uniforms=[u_ProjMat, u_ViewMat, u_Tex, effectTextureSize, colorMatrix, blendRatio]) ::draw.renderState=RenderState(depthFunc=ALWAYS, depthMask=true, depthNear=0.0, depthFar=1.0, lineWidth=1.0, frontFace=BOTH) -::draw.scissor=Scissor(x=0, y=0, width=12, height=12) +::draw.scissor=Scissor(x=0, y=0, width=10, height=10) ::draw.stencil=StencilState(enabled=false, triangleFace=FRONT_AND_BACK, compareMode=ALWAYS, actionOnBothPass=KEEP, actionOnDepthFail=KEEP, actionOnDepthPassStencilFail=KEEP, referenceValue=0, readMask=255, writeMask=255) ::draw.blending=Blending(srcRGB=SOURCE_ALPHA, dstRGB=ONE_MINUS_SOURCE_ALPHA, srcA=ONE, dstA=ONE_MINUS_SOURCE_ALPHA, eqRGB=ADD, eqA=ADD) ::draw.colorMask=ColorMaskState(red=true, green=true, blue=true, alpha=true) @@ -79,6 +79,7 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [64.0, 64.0] +::draw.uniform.Uniform(u_MaxTexCoords) = [0.15625, 0.15625] ::draw.uniform.Uniform(colorMatrix) = Matrix3D( [ 0.33, 0.59, 0.11, 0 ], [ 0.33, 0.59, 0.11, 0 ], @@ -86,8 +87,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) [ 0, 0, 0, 1 ], ) ::draw.uniform.Uniform(blendRatio) = [1.0, 0.0, 0.0, 0.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 ::draw.ERROR.Missing:[Uniform(u_Tex)] -::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3)] +::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) @@ -95,9 +97,9 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.attribute[1][0]=Attribute(a_TexIndex, type=UByte1, normalized=false, offset=null, active=true, precision=LOW, divisor=0) ::draw.indices=[0, 1, 2, 3, 0, 2] ::draw.vertex[0]: a_Pos[0,0], a_Tex[0,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[1]: a_Pos[12,0], a_Tex[0.1875,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[2]: a_Pos[12,12], a_Tex[0.1875,0.1875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[3]: a_Pos[0,12], a_Tex[0,0.1875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[1]: a_Pos[10,0], a_Tex[0.15625,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[2]: a_Pos[10,10], a_Tex[0.15625,0.15625], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[3]: a_Pos[0,10], a_Tex[0,0.15625], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] createTexture():2 createRenderBuffer():3 RenderBuffer[3].setSize(64, 64) @@ -109,7 +111,7 @@ Buffer[2].afterSetMem(mem[49152]) draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.program=Program(name=program, attributes=[a_Tex, a_TexIndex, a_Col, a_Col2, a_Pos], uniforms=[u_ProjMat, u_ViewMat, u_Tex, effectTextureSize, dist, weights, apply_alpha]) ::draw.renderState=RenderState(depthFunc=ALWAYS, depthMask=true, depthNear=0.0, depthFar=1.0, lineWidth=1.0, frontFace=BOTH) -::draw.scissor=Scissor(x=0, y=0, width=13, height=13) +::draw.scissor=Scissor(x=0, y=0, width=12, height=12) ::draw.stencil=StencilState(enabled=false, triangleFace=FRONT_AND_BACK, compareMode=ALWAYS, actionOnBothPass=KEEP, actionOnDepthFail=KEEP, actionOnDepthPassStencilFail=KEEP, referenceValue=0, readMask=255, writeMask=255) ::draw.blending=Blending(srcRGB=SOURCE_ALPHA, dstRGB=ONE_MINUS_SOURCE_ALPHA, srcA=ONE, dstA=ONE_MINUS_SOURCE_ALPHA, eqRGB=ADD, eqA=ADD) ::draw.colorMask=ColorMaskState(red=true, green=true, blue=true, alpha=true) @@ -130,6 +132,7 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [64.0, 64.0] +::draw.uniform.Uniform(u_MaxTexCoords) = [0.1875, 0.1875] ::draw.uniform.Uniform(weights) = Matrix3D( [ -1, -1, -1, 0 ], [ -1, 8, -1, 0 ], @@ -138,18 +141,19 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ) ::draw.uniform.Uniform(dist) = [1.0, 0.0, 0.0, 0.0] ::draw.uniform.Uniform(apply_alpha) = [0.0, 0.0, 0.0, 0.0] +::draw.uniform.Uniform(u_filterScale) = 1.0 ::draw.ERROR.Missing:[Uniform(u_Tex)] -::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3)] +::draw.ERROR.Unexpected:[Uniform(u_Tex0), Uniform(u_Tex1), Uniform(u_Tex2), Uniform(u_Tex3), Uniform(u_MaxTexCoords), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) ::draw.attribute[0][3]=Attribute(a_Col2, type=Byte4, normalized=true, offset=null, active=true, precision=DEFAULT, divisor=0) ::draw.attribute[1][0]=Attribute(a_TexIndex, type=UByte1, normalized=false, offset=null, active=true, precision=LOW, divisor=0) ::draw.indices=[0, 1, 2, 3, 0, 2] -::draw.vertex[0]: a_Pos[0,0], a_Tex[0,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[1]: a_Pos[12,0], a_Tex[0.1875,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[2]: a_Pos[12,12], a_Tex[0.1875,0.1875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[3]: a_Pos[0,12], a_Tex[0,0.1875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[0]: a_Pos[0,0], a_Tex[-0.015625,-0.015625], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[1]: a_Pos[12,0], a_Tex[0.171875,-0.015625], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[2]: a_Pos[12,12], a_Tex[0.171875,0.171875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[3]: a_Pos[0,12], a_Tex[-0.015625,0.171875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] createTexture():3 Buffer[0].afterSetMem(mem[393216]) Buffer[1].afterSetMem(mem[4]) @@ -184,8 +188,8 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.attribute[1][0]=Attribute(a_TexIndex, type=UByte1, normalized=false, offset=null, active=true, precision=LOW, divisor=0) ::draw.indices=[0, 1, 2, 3, 0, 2] ::draw.vertex[0]: a_Pos[-1,-1], a_Tex[0,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[1]: a_Pos[12,-1], a_Tex[0.203125,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[2]: a_Pos[12,12], a_Tex[0.203125,0.203125], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] -::draw.vertex[3]: a_Pos[-1,12], a_Tex[0,0.203125], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[1]: a_Pos[11,-1], a_Tex[0.1875,0], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[2]: a_Pos[11,11], a_Tex[0.1875,0.1875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] +::draw.vertex[3]: a_Pos[-1,11], a_Tex[0,0.1875], a_Col[1,1,1,1], a_Col2[0.49803922,0.49803922,0.49803922,0.49803922], a_TexIndex[0] disposeTemporalPerFrameStuff() flipInternal() \ No newline at end of file diff --git a/korge/src/jvmTest/resources/korge/view/ref/TransitionTest.log b/korge/src/jvmTest/resources/korge/view/ref/TransitionTest.log index 855c9976a..6c998cd57 100644 --- a/korge/src/jvmTest/resources/korge/view/ref/TransitionTest.log +++ b/korge/src/jvmTest/resources/korge/view/ref/TransitionTest.log @@ -60,7 +60,7 @@ Buffer[0].afterSetMem(mem[393216]) Buffer[1].afterSetMem(mem[4]) Buffer[2].afterSetMem(mem[49152]) draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) -::draw.program=Program(name=program, attributes=[a_Tex, a_TexIndex, a_Col, a_Col2, a_Pos], uniforms=[u_ProjMat, u_ViewMat, u_Tex0, u_Tex1, u_Tex2, u_Tex3, u_Mask, u_Reversed, u_Ratio, u_Smooth]) +::draw.program=Program(name=program, attributes=[a_Tex, a_TexIndex, a_Col, a_Col2, a_Pos], uniforms=[u_ProjMat, u_ViewMat, u_Tex0, u_Tex1, u_Tex2, u_Tex3, u_Mask, u_MaxTexCoords, u_Reversed, u_Ratio, u_Smooth]) ::draw.renderState=RenderState(depthFunc=ALWAYS, depthMask=true, depthNear=0.0, depthFar=1.0, lineWidth=1.0, frontFace=BOTH) ::draw.scissor=Scissor(x=100, y=0, width=600, height=600) ::draw.stencil=StencilState(enabled=false, triangleFace=FRONT_AND_BACK, compareMode=ALWAYS, actionOnBothPass=KEEP, actionOnDepthFail=KEEP, actionOnDepthPassStencilFail=KEEP, referenceValue=0, readMask=255, writeMask=255) @@ -83,11 +83,13 @@ draw(vertexCount=6, instances=1, indices=Buffer[2], type=TRIANGLES, offset=0) ::draw.uniform.Uniform(u_Tex2) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(u_Tex3) = TextureUnit(texture=null, linear=true, trilinear=null) ::draw.uniform.Uniform(effectTextureSize) = [512.0, 512.0] +::draw.uniform.Uniform(u_MaxTexCoords) = [1.0, 1.0] ::draw.uniform.Uniform(u_Ratio) = [0.5, 0.0, 0.0, 0.0] ::draw.uniform.Uniform(u_Mask) = TextureUnit(texture=Texture[2], linear=true, trilinear=null) ::draw.uniform.Uniform(u_Reversed) = [0.0, 0.0, 0.0, 0.0] ::draw.uniform.Uniform(u_Smooth) = [1.0, 0.0, 0.0, 0.0] -::draw.ERROR.Unexpected:[Uniform(effectTextureSize)] +::draw.uniform.Uniform(u_filterScale) = 1.0 +::draw.ERROR.Unexpected:[Uniform(effectTextureSize), Uniform(u_filterScale)] ::draw.attribute[0][0]=Attribute(a_Pos, type=Float2, normalized=false, offset=null, active=true, precision=HIGH, divisor=0) ::draw.attribute[0][1]=Attribute(a_Tex, type=Float2, normalized=false, offset=null, active=true, precision=MEDIUM, divisor=0) ::draw.attribute[0][2]=Attribute(a_Col, type=Byte4, normalized=true, offset=null, active=true, precision=LOW, divisor=0) diff --git a/korim/src/commonMain/kotlin/com/soywiz/korim/bitmap/BitmapSlice.kt b/korim/src/commonMain/kotlin/com/soywiz/korim/bitmap/BitmapSlice.kt index 81f00cb29..1512b2b6e 100644 --- a/korim/src/commonMain/kotlin/com/soywiz/korim/bitmap/BitmapSlice.kt +++ b/korim/src/commonMain/kotlin/com/soywiz/korim/bitmap/BitmapSlice.kt @@ -116,6 +116,62 @@ open class BmpCoordsWithInstanceBase( } } +open class MutableBmpCoordsWithInstanceBase( + override var base: T, + override var tl_x: Float, override var tl_y: Float, + override var tr_x: Float, override var tr_y: Float, + override var br_x: Float, override var br_y: Float, + override var bl_x: Float, override var bl_y: Float, + override var name: String? = null +) : BmpCoordsWithT { + constructor(base: T, coords: BmpCoords, name: String? = null) : this( + base, + coords.tl_x, coords.tl_y, + coords.tr_x, coords.tr_y, + coords.br_x, coords.br_y, + coords.bl_x, coords.bl_y, + name + ) + constructor(base: BmpCoordsWithT, name: String? = null) : this(base.base, base, name ?: base.name) + + fun setTo( + tl_x: Float, tl_y: Float, + tr_x: Float, tr_y: Float, + br_x: Float, br_y: Float, + bl_x: Float, bl_y: Float, + ) { + this.tl_x = tl_x + this.tl_y = tl_y + this.tr_x = tr_x + this.tr_y = tr_y + this.br_x = br_x + this.br_y = br_y + this.bl_x = bl_x + this.bl_y = bl_y + } + + fun setTo(coords: BmpCoords) { + setTo( + coords.tl_x, coords.tl_y, coords.tr_x, coords.tr_y, + coords.br_x, coords.br_y, coords.bl_x, coords.bl_y, + ) + } + + fun setTo(base: T, coords: BmpCoords, name: String? = null) { + this.base = base + setTo(coords) + this.name = name + } + + override fun close() { + (base as? Closeable)?.close() + } + + fun setBasicCoords(x0: Float, y0: Float, x1: Float, y1: Float) { + setTo(x0, y0, x1, y0, x1, y1, x0, y1) + } +} + open class UntransformedSizeBmpCoordsWithInstance( val baseCoords: BmpCoordsWithT ) : BmpCoordsWithInstanceBase(baseCoords) { diff --git a/korma/src/commonMain/kotlin/com/soywiz/korma/geom/Rectangle.kt b/korma/src/commonMain/kotlin/com/soywiz/korma/geom/Rectangle.kt index d59260d5c..df439b43f 100644 --- a/korma/src/commonMain/kotlin/com/soywiz/korma/geom/Rectangle.kt +++ b/korma/src/commonMain/kotlin/com/soywiz/korma/geom/Rectangle.kt @@ -3,7 +3,6 @@ package com.soywiz.korma.geom import com.soywiz.korma.internal.* import com.soywiz.korma.interpolation.* import com.soywiz.korma.math.isAlmostEquals -import com.soywiz.korma.math.isAlmostZero import kotlin.math.* interface IRectangle {