Color inversion in pages color filter #372
This commit is contained in:
@@ -6,35 +6,27 @@ import android.graphics.ColorMatrixColorFilter
|
||||
class ReaderColorFilter(
|
||||
val brightness: Float,
|
||||
val contrast: Float,
|
||||
val isInverted: Boolean,
|
||||
) {
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = brightness == 0f && contrast == 0f
|
||||
get() = !isInverted && brightness == 0f && contrast == 0f
|
||||
|
||||
fun toColorFilter(): ColorMatrixColorFilter {
|
||||
val cm = ColorMatrix()
|
||||
val scale = brightness + 1f
|
||||
cm.setScale(scale, scale, scale, 1f)
|
||||
if (isInverted) {
|
||||
cm.inverted()
|
||||
}
|
||||
cm.setBrightness(brightness)
|
||||
cm.setContrast(contrast)
|
||||
return ColorMatrixColorFilter(cm)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ReaderColorFilter
|
||||
|
||||
if (brightness != other.brightness) return false
|
||||
if (contrast != other.contrast) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = brightness.hashCode()
|
||||
result = 31 * result + contrast.hashCode()
|
||||
return result
|
||||
private fun ColorMatrix.setBrightness(brightness: Float) {
|
||||
val scale = brightness + 1f
|
||||
val matrix = ColorMatrix()
|
||||
matrix.setScale(scale, scale, scale, 1f)
|
||||
postConcat(matrix)
|
||||
}
|
||||
|
||||
private fun ColorMatrix.setContrast(contrast: Float) {
|
||||
@@ -49,4 +41,32 @@ class ReaderColorFilter(
|
||||
val matrix = ColorMatrix(array)
|
||||
postConcat(matrix)
|
||||
}
|
||||
|
||||
private fun ColorMatrix.inverted() {
|
||||
val matrix = floatArrayOf(
|
||||
-1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
|
||||
0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
|
||||
0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
)
|
||||
set(matrix)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ReaderColorFilter
|
||||
|
||||
if (brightness != other.brightness) return false
|
||||
if (contrast != other.contrast) return false
|
||||
return isInverted == other.isInverted
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = brightness.hashCode()
|
||||
result = 31 * result + contrast.hashCode()
|
||||
result = 31 * result + isInverted.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.updateLayoutParams
|
||||
@@ -26,6 +27,7 @@ import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.indicator
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.setChecked
|
||||
import org.koitharu.kotatsu.core.util.ext.setValueRounded
|
||||
import org.koitharu.kotatsu.databinding.ActivityColorFilterBinding
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -39,7 +41,7 @@ import com.google.android.material.R as materialR
|
||||
class ColorFilterConfigActivity :
|
||||
BaseActivity<ActivityColorFilterBinding>(),
|
||||
Slider.OnChangeListener,
|
||||
View.OnClickListener {
|
||||
View.OnClickListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
@Inject
|
||||
lateinit var coil: ImageLoader
|
||||
@@ -58,6 +60,7 @@ class ColorFilterConfigActivity :
|
||||
val formatter = PercentLabelFormatter(resources)
|
||||
viewBinding.sliderContrast.setLabelFormatter(formatter)
|
||||
viewBinding.sliderBrightness.setLabelFormatter(formatter)
|
||||
viewBinding.switchInvert.setOnCheckedChangeListener(this)
|
||||
viewBinding.buttonDone.setOnClickListener(this)
|
||||
viewBinding.buttonReset.setOnClickListener(this)
|
||||
|
||||
@@ -80,6 +83,10 @@ class ColorFilterConfigActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
|
||||
viewModel.setInversion(isChecked)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_done -> viewModel.save()
|
||||
@@ -103,13 +110,14 @@ class ColorFilterConfigActivity :
|
||||
private fun onColorFilterChanged(readerColorFilter: ReaderColorFilter?) {
|
||||
viewBinding.sliderBrightness.setValueRounded(readerColorFilter?.brightness ?: 0f)
|
||||
viewBinding.sliderContrast.setValueRounded(readerColorFilter?.contrast ?: 0f)
|
||||
viewBinding.switchInvert.setChecked(readerColorFilter?.isInverted ?: false, false)
|
||||
viewBinding.imageViewAfter.colorFilter = readerColorFilter?.toColorFilter()
|
||||
}
|
||||
|
||||
private fun onPreviewChanged(preview: MangaPage?) {
|
||||
if (preview == null) return
|
||||
ImageRequest.Builder(this@ColorFilterConfigActivity)
|
||||
.data(preview.url)
|
||||
.data(preview)
|
||||
.scale(Scale.FILL)
|
||||
.decodeRegion()
|
||||
.tag(preview.source)
|
||||
@@ -117,7 +125,7 @@ class ColorFilterConfigActivity :
|
||||
.error(R.drawable.ic_error_placeholder)
|
||||
.size(ViewSizeResolver(viewBinding.imageViewBefore))
|
||||
.allowRgb565(false)
|
||||
.target(ShadowViewTarget(viewBinding.imageViewBefore, viewBinding.imageViewAfter))
|
||||
.target(DoubleViewTarget(viewBinding.imageViewBefore, viewBinding.imageViewAfter))
|
||||
.enqueueWith(coil)
|
||||
}
|
||||
|
||||
|
||||
@@ -55,12 +55,32 @@ class ColorFilterConfigViewModel @Inject constructor(
|
||||
|
||||
fun setBrightness(brightness: Float) {
|
||||
val cf = colorFilter.value
|
||||
colorFilter.value = ReaderColorFilter(brightness, cf?.contrast ?: 0f).takeUnless { it.isEmpty }
|
||||
colorFilter.value = ReaderColorFilter(
|
||||
brightness = brightness,
|
||||
contrast = cf?.contrast ?: 0f,
|
||||
isInverted = cf?.isInverted ?: false,
|
||||
).takeUnless { it.isEmpty }
|
||||
}
|
||||
|
||||
fun setContrast(contrast: Float) {
|
||||
val cf = colorFilter.value
|
||||
colorFilter.value = ReaderColorFilter(cf?.brightness ?: 0f, contrast).takeUnless { it.isEmpty }
|
||||
colorFilter.value = ReaderColorFilter(
|
||||
brightness = cf?.brightness ?: 0f,
|
||||
contrast = contrast,
|
||||
isInverted = cf?.isInverted ?: false,
|
||||
).takeUnless { it.isEmpty }
|
||||
}
|
||||
|
||||
fun setInversion(invert: Boolean) {
|
||||
val cf = colorFilter.value
|
||||
if (invert == cf?.isInverted) {
|
||||
return
|
||||
}
|
||||
colorFilter.value = ReaderColorFilter(
|
||||
brightness = cf?.brightness ?: 0f,
|
||||
contrast = cf?.contrast ?: 0f,
|
||||
isInverted = invert,
|
||||
).takeUnless { it.isEmpty }
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
|
||||
@@ -4,15 +4,15 @@ import android.graphics.drawable.Drawable
|
||||
import android.widget.ImageView
|
||||
import coil.target.ImageViewTarget
|
||||
|
||||
class ShadowViewTarget(
|
||||
view: ImageView,
|
||||
private val shadowView: ImageView,
|
||||
) : ImageViewTarget(view) {
|
||||
class DoubleViewTarget(
|
||||
primaryView: ImageView,
|
||||
private val secondaryView: ImageView,
|
||||
) : ImageViewTarget(primaryView) {
|
||||
|
||||
override var drawable: Drawable?
|
||||
get() = super.drawable
|
||||
set(value) {
|
||||
super.drawable = value
|
||||
shadowView.setImageDrawable(value?.constantState?.newDrawable())
|
||||
secondaryView.setImageDrawable(value?.constantState?.newDrawable())
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class ReaderSettings(
|
||||
get() = settings.zoomMode
|
||||
|
||||
val colorFilter: ReaderColorFilter?
|
||||
get() = colorFilterFlow.value
|
||||
get() = colorFilterFlow.value?.takeUnless { it.isEmpty }
|
||||
|
||||
val isPagesNumbersEnabled: Boolean
|
||||
get() = settings.isPagesNumbersEnabled
|
||||
|
||||
Reference in New Issue
Block a user