Feature: Image Editing
Relevant source files
The following files were used as context for generating this wiki page:
- app/src/main/java/com/musagenius/ocrapp/data/utils/ImageEditor.kt
- app/src/main/java/com/musagenius/ocrapp/MainActivity.kt
- app/src/main/java/com/musagenius/ocrapp/data/ocr/ImagePreprocessor.kt
- app/src/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt
- app/src/main/java/com/musagenius/ocrapp/presentation/ui/ocr/OCRResultScreen.kt
The Image Editing feature provides users with the ability to perform basic modifications, such as rotation and cropping, on images selected from the device's gallery. This step is crucial for preparing images for Optical Character Recognition (OCR), ensuring they are correctly oriented and focused on the relevant area, thereby improving the accuracy of text extraction. The feature is integrated into the app's workflow between image selection and OCR processing.
The editing functionality is primarily encapsulated within the ImageEditor utility class, which handles the low-level bitmap manipulations. The application's navigation graph, defined in MainActivity, orchestrates the user flow, directing the user from the gallery selection to the ImageEditorScreen, and subsequently to the OCRResultScreen upon saving the edits.
Architecture and Components
The Image Editing feature is composed of several key components that work together to provide a seamless user experience. The core logic is decoupled from the UI, following clean architecture principles.
The diagram below illustrates the main components and their relationships.
Key Components
| Component | File | Description |
|---|---|---|
MainActivity | MainActivity.kt | Contains the Jetpack Navigation graph. It defines the route to the ImageEditorScreen and handles passing the image URI as an argument. It also manages navigation to the OCRResultScreen after editing is complete. |
ImageEditorScreen | ImageEditorScreen.kt | The UI component where the user interacts with the image to perform edits. It receives the image URI, and upon saving, returns the URI of the newly edited image. (Note: File not present in context, behavior inferred from MainActivity.kt). |
ImageEditor | ImageEditor.kt | A singleton utility class responsible for all image manipulation logic, including rotation, cropping, and saving the modified bitmap to a new file. |
ImageCompressor | ImageCompressor.kt | A dependency of ImageEditor, used to load the initial bitmap from a source URI. (Note: File not present, inferred from ImageEditor.kt constructor). |
ImagePreprocessor | ImagePreprocessor.kt | Contains related image manipulation logic, such as an alternative rotateBitmap function, scaling, and quality assessment, which complements the overall image handling capabilities of the app. |
Sources: app/src/main/java/com/musagenius/ocrapp/MainActivity.kt:65-87, app/src/main/java/com/musagenius/ocrapp/data/utils/ImageEditor.kt:17-23, app/src/main/java/com/musagenius/ocrapp/data/ocr/ImagePreprocessor.kt:18-24
Data Flow
The image editing process follows a specific sequence, starting from the user selecting an image from the gallery and ending with the edited image being sent for OCR.
This sequence diagram shows the interactions between the user, UI, and data-layer components.
Core Logic: ImageEditor
The ImageEditor class is the heart of the editing feature. It is an injectable singleton that provides suspend functions for performing image manipulations on background threads to avoid blocking the UI.
Sources: app/src/main/java/com/musagenius/ocrapp/data/utils/ImageEditor.kt:17-23
Class Diagram
Key Functions
| Function | Parameters | Return Type | Description |
|---|---|---|---|
rotateBitmap | bitmap: Bitmap, degrees: Float | Bitmap | Rotates the input bitmap by the specified degrees using android.graphics.Matrix. It recycles the original bitmap if a new one is created. |
cropBitmap | bitmap: Bitmap, x: Int, y: Int, width: Int, height: Int | Bitmap | Crops the bitmap to the specified rectangle. It ensures crop bounds are valid and recycles the original bitmap. |
saveBitmapToUri | bitmap: Bitmap, quality: Int = 90 | Result<Uri> | Saves the given bitmap to a temporary .jpg file in the app's external files directory with a default JPEG quality of 90. Returns a Result containing the Uri of the saved file. |
editImage | sourceUri: Uri, rotationDegrees: Float, cropRect: CropRect? | Result<Uri> | The main orchestration function. It loads a bitmap from the sourceUri, applies rotation and/or cropping, saves the result to a new file, and returns the Uri of the edited image. |
Sources: app/src/main/java/com/musagenius/ocrapp/data/utils/ImageEditor.kt:29-132
The editImage function provides a complete pipeline for applying edits:
// Source: app/src/main/java/com/musagenius/ocrapp/data/utils/ImageEditor.kt:102-132
suspend fun editImage(
sourceUri: Uri,
rotationDegrees: Float = 0f,
cropRect: CropRect? = null
): Result<Uri> = withContext(Dispatchers.IO) {
try {
// 1. Load bitmap from URI
var bitmap = imageCompressor.loadBitmapFromUri(sourceUri)
?: return@withContext Result.failure(Exception("Failed to load image"))
// 2. Apply rotation if specified
if (rotationDegrees != 0f) {
bitmap = rotateBitmap(bitmap, rotationDegrees)
}
// 3. Apply crop if specified
if (cropRect != null) {
bitmap = cropBitmap(
bitmap,
cropRect.x,
cropRect.y,
cropRect.width,
cropRect.height
)
}
// 4. Save edited bitmap to file
val result = saveBitmapToUri(bitmap, quality = 90)
// 5. Clean up bitmap
if (!bitmap.isRecycled) {
bitmap.recycle()
}
result
} catch (e: Exception) {
Result.failure(e)
}
}
Navigation Integration
The MainActivity is responsible for integrating the ImageEditorScreen into the application's navigation flow. When an image is selected from the gallery on the CameraScreen, it triggers a navigation event to the editor.
Sources: app/src/main/java/com/musagenius/ocrapp/MainActivity.kt:60-63
The navigation graph includes a composable destination for the editor route:
- Route:
Screen.ImageEditor.routewhich is defined asimage_editor/{imageUri}. - Argument: It accepts a mandatory
imageUristring argument. The URI is encoded before being passed in the route and decoded upon arrival. - Callbacks:
onImageSaved: This callback is invoked when the user saves their edits. It receives theUriof the new, edited image. The app then navigates to theOCRResultScreen, passing this new URI. The navigation action also pops the back stack up to theCameraScreen, removing the editor from the history.onCancel: If the user cancels the editing process, this callback is invoked, and the app simply pops the back stack, returning to the previous screen (CameraScreen).
Sources: app/src/main/java/com/musagenius/ocrapp/MainActivity.kt:65-87
Conclusion
The Image Editing feature is a well-defined module that enhances the application's core OCR functionality. By providing essential tools for rotation and cropping, it empowers users to improve the quality of input images, leading to more accurate text extraction. The feature's architecture effectively separates the complex image manipulation logic in the ImageEditor utility from the UI and navigation concerns, which are handled by Jetpack Compose and Navigation components. This separation makes the feature robust, testable, and easy to maintain.