EditPageScreen: apply output quad
This commit is contained in:
@@ -185,7 +185,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
onLoad = { id -> viewModel.loadCropInitialState(id)},
|
onLoad = { id -> viewModel.loadCropInitialState(id)},
|
||||||
initState = cropInitialState,
|
initState = cropInitialState,
|
||||||
navigation = navigation,
|
navigation = navigation,
|
||||||
onUpdatePageQuad = { id, quad, onComplete -> },
|
onUpdatePageQuad = { quad -> viewModel.setCurrentPageUserQuad(quad) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Screen.Main.Document -> {
|
is Screen.Main.Document -> {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import org.fairscan.app.ui.state.DocumentUiModel
|
|||||||
import org.fairscan.app.ui.state.PageThumbnail
|
import org.fairscan.app.ui.state.PageThumbnail
|
||||||
import org.fairscan.imageprocessing.ColorMode
|
import org.fairscan.imageprocessing.ColorMode
|
||||||
import org.fairscan.imageprocessing.ImageSize
|
import org.fairscan.imageprocessing.ImageSize
|
||||||
|
import org.fairscan.imageprocessing.Quad
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@@ -189,6 +190,22 @@ class MainViewModel(val imageRepository: ImageRepository): ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCurrentPageUserQuad(userQuad: Quad) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val currentPage = currentPage()
|
||||||
|
val totalRotation = currentPage.totalRotation()
|
||||||
|
val rotateIterations = (4 - totalRotation.degrees / 90) % 4
|
||||||
|
val newQuad = userQuad.rotate90(rotateIterations, ImageSize(1, 1))
|
||||||
|
_loadingPageId.value = currentPage.id
|
||||||
|
val pages = withContext(Dispatchers.IO) {
|
||||||
|
imageRepository.setUserQuad(currentPage.id, newQuad)
|
||||||
|
imageRepository.pages()
|
||||||
|
}
|
||||||
|
_pages.value = pages
|
||||||
|
_loadingPageId.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun currentPage(): ScanPage {
|
private fun currentPage(): ScanPage {
|
||||||
val index = _currentPageIndex.value
|
val index = _currentPageIndex.value
|
||||||
val pages = _pages.value
|
val pages = _pages.value
|
||||||
@@ -234,8 +251,7 @@ class MainViewModel(val imageRepository: ImageRepository): ViewModel() {
|
|||||||
?: return@launch
|
?: return@launch
|
||||||
|
|
||||||
val metadata = page.metadata
|
val metadata = page.metadata
|
||||||
val baseRotation = metadata?.baseRotation ?: Rotation.R0
|
val rotation = page.totalRotation()
|
||||||
val rotation = baseRotation.add(page.manualRotation)
|
|
||||||
|
|
||||||
val bitmap = withContext(Dispatchers.IO) {
|
val bitmap = withContext(Dispatchers.IO) {
|
||||||
val source = imageRepository.source(page.id)
|
val source = imageRepository.source(page.id)
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ data class PageV2(
|
|||||||
val baseRotationDegrees: Int = 0,
|
val baseRotationDegrees: Int = 0,
|
||||||
val manualRotationDegrees: Int = 0,
|
val manualRotationDegrees: Int = 0,
|
||||||
val quad: NormalizedQuad? = null,
|
val quad: NormalizedQuad? = null,
|
||||||
|
val quadVersion: Int = 0,
|
||||||
|
val userQuad: NormalizedQuad? = null,
|
||||||
val isColored: Boolean? = null,
|
val isColored: Boolean? = null,
|
||||||
val colorMode: ColorMode? = null,
|
val colorMode: ColorMode? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import kotlinx.serialization.json.decodeFromJsonElement
|
|||||||
import kotlinx.serialization.json.int
|
import kotlinx.serialization.json.int
|
||||||
import kotlinx.serialization.json.jsonObject
|
import kotlinx.serialization.json.jsonObject
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
import org.fairscan.app.domain.ExportQuality
|
|
||||||
import org.fairscan.app.domain.Jpeg
|
import org.fairscan.app.domain.Jpeg
|
||||||
import org.fairscan.app.domain.PageMetadata
|
import org.fairscan.app.domain.PageMetadata
|
||||||
import org.fairscan.app.domain.PageViewKey
|
import org.fairscan.app.domain.PageViewKey
|
||||||
@@ -91,7 +90,7 @@ class ImageRepository(
|
|||||||
return when {
|
return when {
|
||||||
metadataPages != null ->
|
metadataPages != null ->
|
||||||
metadataPages
|
metadataPages
|
||||||
.filter { processedImageFileName(it.id, it.colorMode) in filesOnDisk }
|
.filter { processedImageFileName(it.id, it.colorMode, it.quadVersion) in filesOnDisk }
|
||||||
.toMutableList()
|
.toMutableList()
|
||||||
else ->
|
else ->
|
||||||
filesOnDisk
|
filesOnDisk
|
||||||
@@ -135,7 +134,7 @@ class ImageRepository(
|
|||||||
pages.pages().mapNotNull {
|
pages.pages().mapNotNull {
|
||||||
runCatching {
|
runCatching {
|
||||||
val manualRotation = Rotation.fromDegrees(it.manualRotationDegrees)
|
val manualRotation = Rotation.fromDegrees(it.manualRotationDegrees)
|
||||||
ScanPage(it.id, manualRotation, it.colorMode, it.toMetadata())
|
ScanPage(it.id, manualRotation, it.colorMode, it.quadVersion, it.toMetadata())
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,7 +142,7 @@ class ImageRepository(
|
|||||||
suspend fun add(processed: Jpeg, source: Jpeg, metadata: PageMetadata, colorMode: ColorMode) =
|
suspend fun add(processed: Jpeg, source: Jpeg, metadata: PageMetadata, colorMode: ColorMode) =
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
val id = "${System.currentTimeMillis()}"
|
val id = "${System.currentTimeMillis()}"
|
||||||
val key = PageViewKey(id, Rotation.R0, colorMode)
|
val key = PageViewKey(id, Rotation.R0, colorMode, 0)
|
||||||
processedImageFile(key).writeBytes(processed.bytes)
|
processedImageFile(key).writeBytes(processed.bytes)
|
||||||
sourceFile(id).writeBytes(source.bytes)
|
sourceFile(id).writeBytes(source.bytes)
|
||||||
pages.addOrReplace(
|
pages.addOrReplace(
|
||||||
@@ -162,18 +161,64 @@ class ImageRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setColorMode(id: String, colorMode: ColorMode) {
|
suspend fun setColorMode(id: String, colorMode: ColorMode) {
|
||||||
val key = PageViewKey(id, Rotation.R0, colorMode)
|
updatePage(id) { page, metadata ->
|
||||||
val processedFile = processedImageFile(key)
|
PageUpdate(
|
||||||
val metadata = mutex.withLock { pages.get(id)?.toMetadata() }
|
updatedPage = page.copy(colorMode = colorMode),
|
||||||
|
normalizedQuad = metadata.normalizedQuad,
|
||||||
|
colorMode = colorMode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setUserQuad(id: String, newQuad: Quad) {
|
||||||
|
updatePage(id) { page, metadata ->
|
||||||
|
PageUpdate(
|
||||||
|
updatedPage = page.copy(
|
||||||
|
quadVersion = page.quadVersion + 1,
|
||||||
|
userQuad = newQuad.toSerializable(),
|
||||||
|
),
|
||||||
|
normalizedQuad = newQuad,
|
||||||
|
colorMode = page.colorMode ?: metadata.autoColorMode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class PageUpdate(
|
||||||
|
val updatedPage: PageV2,
|
||||||
|
val normalizedQuad: Quad,
|
||||||
|
val colorMode: ColorMode,
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun updatePage(
|
||||||
|
id: String,
|
||||||
|
buildUpdate: (PageV2, PageMetadata) -> PageUpdate
|
||||||
|
) {
|
||||||
|
val page = mutex.withLock { pages.get(id) }
|
||||||
|
val metadata = page?.toMetadata() ?: return
|
||||||
val sourceFile = sourceFile(id)
|
val sourceFile = sourceFile(id)
|
||||||
if (metadata == null || !sourceFile.exists())
|
if (!sourceFile.exists())
|
||||||
return
|
return
|
||||||
|
|
||||||
|
val update = buildUpdate(page, metadata)
|
||||||
|
val key = PageViewKey(
|
||||||
|
pageId = id,
|
||||||
|
rotation = Rotation.R0,
|
||||||
|
colorMode = update.colorMode,
|
||||||
|
quadVersion = update.updatedPage.quadVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
val processedFile = processedImageFile(key)
|
||||||
val job = processingJobs.computeIfAbsent(key) {
|
val job = processingJobs.computeIfAbsent(key) {
|
||||||
scope.async(Dispatchers.IO) {
|
scope.async(Dispatchers.IO) {
|
||||||
if (!processedFile.exists()) {
|
if (!processedFile.exists()) {
|
||||||
val sourceJpeg = Jpeg(sourceFile.readBytes())
|
val sourceJpeg = Jpeg(sourceFile.readBytes())
|
||||||
val processedJpeg = transformations.process(sourceJpeg, metadata, colorMode)
|
val processedJpeg =
|
||||||
|
transformations.process(
|
||||||
|
sourceJpeg,
|
||||||
|
normalizedQuad = update.normalizedQuad,
|
||||||
|
baseRotation = metadata.baseRotation,
|
||||||
|
colorMode = update.colorMode
|
||||||
|
)
|
||||||
processedFile.writeBytes(processedJpeg.bytes)
|
processedFile.writeBytes(processedJpeg.bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,7 +230,7 @@ class ImageRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
pages.update(id) { it.copy(colorMode = colorMode) }
|
pages.update(id) { update.updatedPage }
|
||||||
saveMetadata()
|
saveMetadata()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,14 +293,18 @@ class ImageRepository(
|
|||||||
|
|
||||||
// --- Other operations ---
|
// --- Other operations ---
|
||||||
|
|
||||||
private fun processedImageFileName(id: String, colorMode: ColorMode?) : String =
|
private fun processedImageFileName(id: String, colorMode: ColorMode?, quadVersion: Int) : String {
|
||||||
if (colorMode == null)
|
val sb = StringBuilder(id)
|
||||||
"${id}.jpg"
|
if (colorMode != null)
|
||||||
else
|
sb.append(".").append(colorMode.name.lowercase())
|
||||||
"${id}.${colorMode.name.lowercase()}.jpg"
|
if (quadVersion > 0)
|
||||||
|
sb.append(".q").append(quadVersion)
|
||||||
|
sb.append(".jpg")
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
private fun processedImageFile(key: PageViewKey) : File =
|
private fun processedImageFile(key: PageViewKey) : File =
|
||||||
File(processedDir, processedImageFileName(key.pageId, key.colorMode))
|
File(processedDir, processedImageFileName(key.pageId, key.colorMode, key.quadVersion))
|
||||||
|
|
||||||
private fun sourceFile(id: String): File =
|
private fun sourceFile(id: String): File =
|
||||||
File(sourceDir, "$id.jpg")
|
File(sourceDir, "$id.jpg")
|
||||||
@@ -352,7 +401,7 @@ fun NormalizedQuad.toQuad(): Quad =
|
|||||||
fun PageV2.toMetadata(): PageMetadata? {
|
fun PageV2.toMetadata(): PageMetadata? {
|
||||||
if (quad == null || isColored == null) return null
|
if (quad == null || isColored == null) return null
|
||||||
return PageMetadata(
|
return PageMetadata(
|
||||||
quad.toQuad(),
|
(userQuad ?: quad).toQuad(),
|
||||||
Rotation.fromDegrees(baseRotationDegrees),
|
Rotation.fromDegrees(baseRotationDegrees),
|
||||||
if (isColored) ColorMode.COLOR else ColorMode.GRAYSCALE
|
if (isColored) ColorMode.COLOR else ColorMode.GRAYSCALE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,8 +15,9 @@
|
|||||||
package org.fairscan.app.data
|
package org.fairscan.app.data
|
||||||
|
|
||||||
import org.fairscan.app.domain.Jpeg
|
import org.fairscan.app.domain.Jpeg
|
||||||
import org.fairscan.app.domain.PageMetadata
|
import org.fairscan.app.domain.Rotation
|
||||||
import org.fairscan.imageprocessing.ColorMode
|
import org.fairscan.imageprocessing.ColorMode
|
||||||
|
import org.fairscan.imageprocessing.Quad
|
||||||
|
|
||||||
interface ImageTransformations {
|
interface ImageTransformations {
|
||||||
|
|
||||||
@@ -24,6 +25,11 @@ interface ImageTransformations {
|
|||||||
|
|
||||||
fun resizeToThumbnail(input: Jpeg): Jpeg
|
fun resizeToThumbnail(input: Jpeg): Jpeg
|
||||||
|
|
||||||
fun process(source: Jpeg, metadata: PageMetadata, colorMode: ColorMode): Jpeg
|
fun process(
|
||||||
|
source: Jpeg,
|
||||||
|
normalizedQuad: Quad,
|
||||||
|
baseRotation: Rotation,
|
||||||
|
colorMode: ColorMode
|
||||||
|
): Jpeg
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -48,11 +48,11 @@ suspend fun jpegsForExport(
|
|||||||
JpegProvider {
|
JpegProvider {
|
||||||
val source = imageRepository.source(page.id)
|
val source = imageRepository.source(page.id)
|
||||||
val metadata = page.metadata
|
val metadata = page.metadata
|
||||||
val manualRotation = page.manualRotation
|
|
||||||
val colorMode = page.colorMode
|
val colorMode = page.colorMode
|
||||||
if (source != null && metadata != null && colorMode != null) {
|
if (source != null && metadata != null && colorMode != null) {
|
||||||
val rotation = metadata.baseRotation.add(manualRotation)
|
val rotation = page.totalRotation()
|
||||||
processedImage(source, metadata, rotation, colorMode, exportQuality)
|
val normalizedQuad = metadata.normalizedQuad
|
||||||
|
processedImage(source, normalizedQuad, rotation, colorMode, exportQuality)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
jpeg(page, imageRepository)
|
jpeg(page, imageRepository)
|
||||||
|
|||||||
@@ -27,19 +27,19 @@ data class ScanPage(
|
|||||||
val id: String,
|
val id: String,
|
||||||
val manualRotation: Rotation,
|
val manualRotation: Rotation,
|
||||||
val colorMode: ColorMode?,
|
val colorMode: ColorMode?,
|
||||||
|
val quadVersion: Int,
|
||||||
val metadata: PageMetadata?,
|
val metadata: PageMetadata?,
|
||||||
) {
|
) {
|
||||||
fun key(): PageViewKey = PageViewKey(id, manualRotation, colorMode)
|
fun key() = PageViewKey(id, manualRotation, colorMode, quadVersion)
|
||||||
|
fun totalRotation() = manualRotation.add(metadata?.baseRotation ?: Rotation.R0)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PageViewKey(
|
data class PageViewKey(
|
||||||
val pageId: String,
|
val pageId: String,
|
||||||
val rotation: Rotation,
|
val rotation: Rotation,
|
||||||
val colorMode: ColorMode?,
|
val colorMode: ColorMode?,
|
||||||
) {
|
val quadVersion: Int,
|
||||||
val saveKey: String get() = "$pageId-${rotation.degrees}-$colorMode"
|
)
|
||||||
}
|
|
||||||
|
|
||||||
enum class Rotation(val degrees: Int) {
|
enum class Rotation(val degrees: Int) {
|
||||||
R0(0),
|
R0(0),
|
||||||
R90(90),
|
R90(90),
|
||||||
|
|||||||
@@ -73,14 +73,19 @@ class ImageProcessor(private val thumbnailSizePx: Int) : ImageTransformations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun process(source: Jpeg, metadata: PageMetadata, colorMode: ColorMode): Jpeg {
|
override fun process(
|
||||||
return processedImage(source, metadata, metadata.baseRotation, colorMode, ExportQuality.BALANCED)
|
source: Jpeg,
|
||||||
|
normalizedQuad: Quad,
|
||||||
|
baseRotation: Rotation,
|
||||||
|
colorMode: ColorMode
|
||||||
|
): Jpeg {
|
||||||
|
return processedImage(source, normalizedQuad, baseRotation, colorMode, ExportQuality.BALANCED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun processedImage(
|
fun processedImage(
|
||||||
source: Jpeg,
|
source: Jpeg,
|
||||||
metadata: PageMetadata,
|
normalizedQuad: Quad,
|
||||||
rotation: Rotation,
|
rotation: Rotation,
|
||||||
colorMode: ColorMode,
|
colorMode: ColorMode,
|
||||||
exportQuality: ExportQuality,
|
exportQuality: ExportQuality,
|
||||||
@@ -90,7 +95,7 @@ fun processedImage(
|
|||||||
var page: Mat? = null
|
var page: Mat? = null
|
||||||
try {
|
try {
|
||||||
sourceMat = source.toMat()
|
sourceMat = source.toMat()
|
||||||
val quad = metadata.normalizedQuad.scaledTo(1, 1, sourceMat.width(), sourceMat.height())
|
val quad = normalizedQuad.scaledTo(1, 1, sourceMat.width(), sourceMat.height())
|
||||||
page = extractDocument(sourceMat, quad, rotationDegrees, colorMode, exportQuality.maxPixels)
|
page = extractDocument(sourceMat, quad, rotationDegrees, colorMode, exportQuality.maxPixels)
|
||||||
return Jpeg.fromMat(page, exportQuality.jpegQuality)
|
return Jpeg.fromMat(page, exportQuality.jpegQuality)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ fun dummyNavigation(): Navigation {
|
|||||||
|
|
||||||
fun fakeDocument(pageIds: ImmutableList<String>, context: Context): DocumentUiModel {
|
fun fakeDocument(pageIds: ImmutableList<String>, context: Context): DocumentUiModel {
|
||||||
val pageKeys = pageIds.map {
|
val pageKeys = pageIds.map {
|
||||||
PageThumbnail(PageViewKey(it, Rotation.R0, ColorMode.COLOR), fakeImage(it, context))
|
PageThumbnail(PageViewKey(it, Rotation.R0, ColorMode.COLOR, 0), fakeImage(it, context))
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
return DocumentUiModel(pageKeys)
|
return DocumentUiModel(pageKeys)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ fun DocumentScreenPreview() {
|
|||||||
listOf(1, 2).map { "gallica.bnf.fr-bpt6k5530456s-$it" }.toImmutableList(),
|
listOf(1, 2).map { "gallica.bnf.fr-bpt6k5530456s-$it" }.toImmutableList(),
|
||||||
LocalContext.current
|
LocalContext.current
|
||||||
)
|
)
|
||||||
val key = PageViewKey("123", Rotation.R0, null)
|
val key = PageViewKey("123", Rotation.R0, null, 0)
|
||||||
DocumentScreen(
|
DocumentScreen(
|
||||||
uiState = DocumentUiState(1, CurrentPageUiState(key,image, COLOR, true), document),
|
uiState = DocumentUiState(1, CurrentPageUiState(key,image, COLOR, true), document),
|
||||||
navigation = dummyNavigation(),
|
navigation = dummyNavigation(),
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ fun EditPageScreen(
|
|||||||
onLoad: (String) -> Unit,
|
onLoad: (String) -> Unit,
|
||||||
initState: CropInitState,
|
initState: CropInitState,
|
||||||
navigation: Navigation,
|
navigation: Navigation,
|
||||||
onUpdatePageQuad: (String, Quad, onComplete: () -> Unit) -> Unit,
|
onUpdatePageQuad: (Quad) -> Unit,
|
||||||
) {
|
) {
|
||||||
val state = remember { EditPageScreenState() }
|
val state = remember { EditPageScreenState() }
|
||||||
val quadHandler = remember { QuadEditingHandler() }
|
val quadHandler = remember { QuadEditingHandler() }
|
||||||
@@ -125,21 +125,9 @@ fun EditPageScreen(
|
|||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.windowInsetsPadding(WindowInsets.safeDrawing),
|
.windowInsetsPadding(WindowInsets.safeDrawing),
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
/*
|
state.editableQuad?.let { onUpdatePageQuad(it) }
|
||||||
val quad = state.editableQuad
|
|
||||||
if (quad != null) {
|
|
||||||
// Reverse the total rotation to get back to original source image coordinates
|
|
||||||
val rotateIterations = (4 - totalRotation.value.degrees / 90) % 4
|
|
||||||
val originalQuad = quad.rotate90(rotateIterations, ImageSize(1, 1))
|
|
||||||
onUpdatePageQuad(pageId, originalQuad) {
|
|
||||||
navigation.back()
|
navigation.back()
|
||||||
}
|
}
|
||||||
state.setInitialQuad(quad)
|
|
||||||
} else {
|
|
||||||
navigation.back()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +324,7 @@ fun EditPageScreenPreview() {
|
|||||||
onLoad = {},
|
onLoad = {},
|
||||||
initState = CropInitState.Ready("123",dummyImage, quad),
|
initState = CropInitState.Ready("123",dummyImage, quad),
|
||||||
navigation = dummyNavigation(),
|
navigation = dummyNavigation(),
|
||||||
onUpdatePageQuad = { _,_,_ -> },
|
onUpdatePageQuad = { _ -> },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.assertj.core.api.Assertions.assertThat
|
|||||||
import org.fairscan.app.domain.Jpeg
|
import org.fairscan.app.domain.Jpeg
|
||||||
import org.fairscan.app.domain.PageMetadata
|
import org.fairscan.app.domain.PageMetadata
|
||||||
import org.fairscan.app.domain.PageViewKey
|
import org.fairscan.app.domain.PageViewKey
|
||||||
|
import org.fairscan.app.domain.Rotation
|
||||||
import org.fairscan.app.domain.Rotation.R0
|
import org.fairscan.app.domain.Rotation.R0
|
||||||
import org.fairscan.app.domain.Rotation.R180
|
import org.fairscan.app.domain.Rotation.R180
|
||||||
import org.fairscan.app.domain.Rotation.R270
|
import org.fairscan.app.domain.Rotation.R270
|
||||||
@@ -62,7 +63,7 @@ class ImageRepositoryTest {
|
|||||||
fun repo(
|
fun repo(
|
||||||
rotate: (Jpeg, Int) -> Jpeg = { input, _ -> input },
|
rotate: (Jpeg, Int) -> Jpeg = { input, _ -> input },
|
||||||
resizeToThumbnail: (Jpeg) -> Jpeg = { input -> jpeg(input.bytes[0]) },
|
resizeToThumbnail: (Jpeg) -> Jpeg = { input -> jpeg(input.bytes[0]) },
|
||||||
process: (Jpeg, PageMetadata, ColorMode) -> Jpeg = { _, _, _ ->
|
process: (Jpeg, Quad, Rotation, ColorMode) -> Jpeg = { _, _, _, _ ->
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
): ImageRepository {
|
): ImageRepository {
|
||||||
@@ -71,8 +72,12 @@ class ImageRepositoryTest {
|
|||||||
rotate(input, rotationDegrees)
|
rotate(input, rotationDegrees)
|
||||||
override fun resizeToThumbnail(input: Jpeg): Jpeg =
|
override fun resizeToThumbnail(input: Jpeg): Jpeg =
|
||||||
resizeToThumbnail(input)
|
resizeToThumbnail(input)
|
||||||
override fun process(source: Jpeg, metadata: PageMetadata, colorMode: ColorMode): Jpeg =
|
override fun process(
|
||||||
process(source, metadata, colorMode)
|
source: Jpeg,
|
||||||
|
normalizedQuad: Quad,
|
||||||
|
baseRotation: Rotation,
|
||||||
|
colorMode: ColorMode
|
||||||
|
): Jpeg = process(source, normalizedQuad, baseRotation, colorMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ImageRepository(getFilesDir(), transformations, testScope)
|
return ImageRepository(getFilesDir(), transformations, testScope)
|
||||||
@@ -86,7 +91,7 @@ class ImageRepositoryTest {
|
|||||||
repo.add(jpeg, jpeg(51), metadata1, COLOR)
|
repo.add(jpeg, jpeg(51), metadata1, COLOR)
|
||||||
assertThat(repo.imageIds()).hasSize(1)
|
assertThat(repo.imageIds()).hasSize(1)
|
||||||
val id = repo.imageIds()[0]
|
val id = repo.imageIds()[0]
|
||||||
val key = PageViewKey(id, R0, COLOR)
|
val key = PageViewKey(id, R0, COLOR, 0)
|
||||||
assertThat(repo.jpegBytes(key)).isEqualTo(jpeg)
|
assertThat(repo.jpegBytes(key)).isEqualTo(jpeg)
|
||||||
assertThat(repo.getThumbnail(key)?.bytes).isEqualTo(byteArrayOf(101))
|
assertThat(repo.getThumbnail(key)?.bytes).isEqualTo(byteArrayOf(101))
|
||||||
|
|
||||||
@@ -153,7 +158,7 @@ class ImageRepositoryTest {
|
|||||||
File(processedDir(), "1-90.jpg").writeBytes(bytes)
|
File(processedDir(), "1-90.jpg").writeBytes(bytes)
|
||||||
val repo = repo()
|
val repo = repo()
|
||||||
assertThat(repo.imageIds()).containsExactly("1")
|
assertThat(repo.imageIds()).containsExactly("1")
|
||||||
assertThat(repo.jpegBytes(PageViewKey("1", R0, null))?.bytes).isEqualTo(bytes)
|
assertThat(repo.jpegBytes(PageViewKey("1", R0, null, 0))?.bytes).isEqualTo(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -182,14 +187,14 @@ class ImageRepositoryTest {
|
|||||||
File(processedDir(), "1-90.jpg").writeBytes(bytes)
|
File(processedDir(), "1-90.jpg").writeBytes(bytes)
|
||||||
val repo = repo()
|
val repo = repo()
|
||||||
assertThat(repo.imageIds()).containsExactly("1")
|
assertThat(repo.imageIds()).containsExactly("1")
|
||||||
assertThat(repo.jpegBytes(PageViewKey("1", R0, null))?.bytes).isEqualTo(bytes)
|
assertThat(repo.jpegBytes(PageViewKey("1", R0, null, 0))?.bytes).isEqualTo(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should return null on invalid id`() = runTest {
|
fun `should return null on invalid id`() = runTest {
|
||||||
val repo = repo()
|
val repo = repo()
|
||||||
assertThat(repo.imageIds()).isEmpty()
|
assertThat(repo.imageIds()).isEmpty()
|
||||||
assertThat(repo.jpegBytes(PageViewKey("x", R0, COLOR))).isNull()
|
assertThat(repo.jpegBytes(PageViewKey("x", R0, COLOR, 0))).isNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -239,7 +244,7 @@ class ImageRepositoryTest {
|
|||||||
fun setColorMode_should_process_and_update_metadata() = runTest {
|
fun setColorMode_should_process_and_update_metadata() = runTest {
|
||||||
val jpeg1 = jpeg(10)
|
val jpeg1 = jpeg(10)
|
||||||
val repo = repo(
|
val repo = repo(
|
||||||
process = { jpeg ,meta, mode ->
|
process = { _, _ , _, mode ->
|
||||||
assertThat(mode).isEqualTo(GRAYSCALE)
|
assertThat(mode).isEqualTo(GRAYSCALE)
|
||||||
jpeg(41)
|
jpeg(41)
|
||||||
}
|
}
|
||||||
@@ -249,7 +254,7 @@ class ImageRepositoryTest {
|
|||||||
val id = repo.pages().first().id
|
val id = repo.pages().first().id
|
||||||
repo.setColorMode(id, GRAYSCALE)
|
repo.setColorMode(id, GRAYSCALE)
|
||||||
assertThat(repo.pages().first().colorMode).isEqualTo(GRAYSCALE)
|
assertThat(repo.pages().first().colorMode).isEqualTo(GRAYSCALE)
|
||||||
val key = PageViewKey(id, R0, GRAYSCALE)
|
val key = PageViewKey(id, R0, GRAYSCALE, 0)
|
||||||
assertThat(repo.jpegBytes(key)?.bytes).isEqualTo(byteArrayOf(41))
|
assertThat(repo.jpegBytes(key)?.bytes).isEqualTo(byteArrayOf(41))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +262,7 @@ class ImageRepositoryTest {
|
|||||||
fun setColorMode_should_not_run_twice_in_parallel() = runTest {
|
fun setColorMode_should_not_run_twice_in_parallel() = runTest {
|
||||||
var processCalls = 0
|
var processCalls = 0
|
||||||
val repo = repo(
|
val repo = repo(
|
||||||
process = { _, _, _ ->
|
process = { _, _, _, _ ->
|
||||||
processCalls++
|
processCalls++
|
||||||
runBlocking { delay(10) }
|
runBlocking { delay(10) }
|
||||||
jpeg(1)
|
jpeg(1)
|
||||||
@@ -269,7 +274,7 @@ class ImageRepositoryTest {
|
|||||||
launch { repo.setColorMode(id, GRAYSCALE) }
|
launch { repo.setColorMode(id, GRAYSCALE) }
|
||||||
launch { repo.setColorMode(id, GRAYSCALE) }
|
launch { repo.setColorMode(id, GRAYSCALE) }
|
||||||
}
|
}
|
||||||
val key = PageViewKey(id, R0, GRAYSCALE)
|
val key = PageViewKey(id, R0, GRAYSCALE, 0)
|
||||||
assertThat(repo.jpegBytes(key)?.bytes).isEqualTo(byteArrayOf(1))
|
assertThat(repo.jpegBytes(key)?.bytes).isEqualTo(byteArrayOf(1))
|
||||||
assertThat(processCalls).isEqualTo(1)
|
assertThat(processCalls).isEqualTo(1)
|
||||||
}
|
}
|
||||||
@@ -307,11 +312,11 @@ class ImageRepositoryTest {
|
|||||||
fun metadata() {
|
fun metadata() {
|
||||||
val quad = quad1.toSerializable()
|
val quad = quad1.toSerializable()
|
||||||
|
|
||||||
assertThat(PageV2("1", 0, 0, null,true).toMetadata()).isNull()
|
assertThat(PageV2("1", 0, 0, quad = null, isColored = true).toMetadata()).isNull()
|
||||||
assertThat(PageV2("1", 0, 0, quad, null).toMetadata()).isNull()
|
assertThat(PageV2("1", 0, 0, quad).toMetadata()).isNull()
|
||||||
|
|
||||||
listOf(true, false).forEach { isColored ->
|
listOf(true, false).forEach { isColored ->
|
||||||
val metadata = PageV2("1", 0, 0, quad, isColored).toMetadata()
|
val metadata = PageV2("1", 0, 0, quad, isColored = isColored).toMetadata()
|
||||||
assertThat(metadata).isNotNull()
|
assertThat(metadata).isNotNull()
|
||||||
assertThat(metadata!!.autoColorMode).isEqualTo(
|
assertThat(metadata!!.autoColorMode).isEqualTo(
|
||||||
if (isColored) COLOR else GRAYSCALE
|
if (isColored) COLOR else GRAYSCALE
|
||||||
|
|||||||
Reference in New Issue
Block a user