Skip to content
Merged
29 changes: 22 additions & 7 deletions app/src/main/java/com/ethran/notable/data/PageDataManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import com.ethran.notable.data.model.BackgroundType.AutoPdf.getPage
import com.ethran.notable.data.model.BackgroundType.CoverImage
import com.ethran.notable.data.model.BackgroundType.ImageRepeating
import com.ethran.notable.editor.canvas.CanvasEventBus
import com.ethran.notable.editor.utils.persistBitmapFull
import com.ethran.notable.editor.utils.persistBitmapThumbnail
import com.ethran.notable.editor.utils.saveHQPagePreview
import com.ethran.notable.editor.utils.savePageThumbnail
import com.ethran.notable.io.IN_IGNORED
import com.ethran.notable.io.fileObserverEventNames
import com.ethran.notable.io.loadBackgroundBitmap
Expand Down Expand Up @@ -245,7 +245,12 @@ class PageDataManager @Inject constructor(
} catch (e: Exception) {
// All other unexpected exceptions
log.e("Error caching neighbor pages", e)
appEventBus.tryEmit(AppEvent.ActionHint("Error encountered while caching neighbors", 5000))
appEventBus.tryEmit(
AppEvent.ActionHint(
"Error encountered while caching neighbors",
5000
)
)

}

Expand Down Expand Up @@ -406,14 +411,14 @@ class PageDataManager @Inject constructor(
}

scope.launch(Dispatchers.IO) {
persistBitmapFull(
saveHQPagePreview(
context,
bitmap,
pageId,
currentScroll,
currentZoomLevel
)
persistBitmapThumbnail(context, bitmap, pageId)
savePageThumbnail(context, bitmap, pageId)
}
}
}
Expand Down Expand Up @@ -746,7 +751,12 @@ class PageDataManager @Inject constructor(
}
if (!waitForFileAvailable(filePath)) {
log.w("File changed, but does not exist: $filePath")
appEventBus.tryEmit(AppEvent.ActionHint("Background does not exist", 3000))
appEventBus.tryEmit(
AppEvent.ActionHint(
"Background does not exist",
3000
)
)
return@launch
} else
observeBackgroundFile(pageId, filePath)
Expand Down Expand Up @@ -780,7 +790,12 @@ class PageDataManager @Inject constructor(
invalidateBackground(pid)
if (pid == currentPage) {
CanvasEventBus.forceUpdate.emit(null)
appEventBus.tryEmit(AppEvent.ActionHint("Background file changed", 4000))
appEventBus.tryEmit(
AppEvent.ActionHint(
"Background file changed",
4000
)
)
}
}
}
Expand Down
59 changes: 41 additions & 18 deletions app/src/main/java/com/ethran/notable/editor/PageView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.unit.IntOffset
import androidx.core.graphics.createBitmap
import androidx.core.graphics.toRect
import com.ethran.notable.R
import com.ethran.notable.SCREEN_HEIGHT
import com.ethran.notable.SCREEN_WIDTH
import com.ethran.notable.data.CachedBackground
Expand All @@ -29,7 +33,7 @@ import com.ethran.notable.editor.drawing.drawBg
import com.ethran.notable.editor.drawing.drawOnCanvasFromPage
import com.ethran.notable.editor.utils.div
import com.ethran.notable.editor.utils.divideStrokesFromCut
import com.ethran.notable.editor.utils.loadPersistBitmap
import com.ethran.notable.editor.utils.loadHQPagePreview
import com.ethran.notable.editor.utils.minus
import com.ethran.notable.editor.utils.plus
import com.ethran.notable.editor.utils.strokeBounds
Expand Down Expand Up @@ -428,7 +432,7 @@ class PageView(

// load background, fast, if it is accurate enough.
private fun loadInitialBitmap(): Boolean {
val bitmapFromDisc = loadPersistBitmap(
val bitmapFromDisc = loadHQPagePreview(
context = context,
pageID = currentPageId,
scroll = scroll,
Expand All @@ -450,10 +454,7 @@ class PageView(
// draw just background.
val backgroundType = pageDataManager.getBackgroundType()
if (backgroundType == BackgroundType.Native) {
val bg = pageDataManager.getBackgroundName()
drawBg(
context, windowedCanvas, backgroundType, bg, scroll, 1f, this
)
drawBgToCanvas(null)
} else
windowedCanvas.drawColor(Color.WHITE)
return false
Expand Down Expand Up @@ -666,18 +667,7 @@ class PageView(

log.d("Redrawing full logical rect: $redrawRect")
windowedCanvas.drawColor(Color.BLACK)
val backgroundType = pageDataManager.getBackgroundType() ?: BackgroundType.Native
val bg = pageDataManager.getBackgroundName()
drawBg(
context,
windowedCanvas,
backgroundType,
bg,
scroll,
zoomLevel.value,
this,
redrawRect
)
drawBgToCanvas(redrawRect)
pageDataManager.cacheBitmap(currentPageId, windowedBitmap)

drawAreaScreenCoordinates(redrawRect)
Expand Down Expand Up @@ -823,6 +813,39 @@ class PageView(

}

fun drawBgToCanvas(clipRect: Rect?) {
val backgroundType = pageDataManager.getBackgroundType() ?: BackgroundType.Native
val bg = pageDataManager.getBackgroundName()
val pageNumber = currentPageNumber
val scale = zoomLevel.value
val bgImage: Bitmap? =
when (backgroundType) {
BackgroundType.Image, BackgroundType.CoverImage, BackgroundType.AutoPdf,
is BackgroundType.Pdf, BackgroundType.ImageRepeating -> {
if (backgroundType is BackgroundType.Image && bg == "iris") {
val resId = R.drawable.iris
ImageBitmap.imageResource(context.resources, resId).asAndroidBitmap()
} else {
getOrLoadBackground(bg, pageNumber, scale)
}
Comment on lines +819 to +830
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drawBgToCanvas always uses pageNumber = currentPageNumber when loading backgrounds. For BackgroundType.Pdf, the correct page index is backgroundType.page, not the current on-screen page number (which is used for AutoPdf). As written, fixed-PDF backgrounds can render the wrong PDF page.

Copilot uses AI. Check for mistakes.
}
BackgroundType.Native -> {
null
}
}
drawBg(
canvas = windowedCanvas,
backgroundType = backgroundType,
background = bg,
scroll = scroll,
resourceBitmap = bgImage,
scale = scale,
repeat = false,
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repeat is hard-coded to false when calling drawBg, so BackgroundType.ImageRepeating will no longer repeat its bitmap. Pass repeat = (backgroundType is BackgroundType.ImageRepeating) (or equivalent) to preserve existing behavior.

Suggested change
repeat = false,
repeat = backgroundType is BackgroundType.ImageRepeating,

Copilot uses AI. Check for mistakes.
clipRect = clipRect
)
}


fun updateDimensions(newWidth: Int, newHeight: Int) {
if (newWidth != viewWidth || newHeight != viewHeight) {
log.d("Updating dimensions: $newWidth x $newHeight")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import com.ethran.notable.editor.PageView
import com.ethran.notable.editor.state.History
import com.ethran.notable.editor.utils.ImageHandler
import com.ethran.notable.editor.utils.cleanAllStrokes
import com.ethran.notable.editor.utils.loadPreview
import com.ethran.notable.editor.utils.loadPagePreviewOrFallback
import com.ethran.notable.editor.utils.partialRefreshRegionOnce
import com.ethran.notable.editor.utils.selectRectangle
import com.ethran.notable.editor.utils.waitForEpdRefresh
import com.onyx.android.sdk.extension.isNull
import io.shipbook.shipbooksdk.ShipBook
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -217,7 +216,7 @@ class CanvasObserverRegistry(
// We need to close all menus
if (it) {
CanvasEventBus.closeMenusSignal.emit(Unit)
waitForEpdRefresh()
// waitForEpdRefresh()
}
inputHandler.updateIsDrawing()
}
Expand Down Expand Up @@ -289,13 +288,12 @@ class CanvasObserverRegistry(
val pageUpdatedAtMs = pageDataManager.getPageUpdatedAt(pageId)

val previewBitmap = withContext(Dispatchers.IO) {
loadPreview(
loadPagePreviewOrFallback(
context = drawCanvas.context,
pageIdToLoad = pageId,
expectedWidth = page.viewWidth,
expectedHeight = page.viewHeight,
pageNumber = pageNumber,
requireExactMatch = false,
pageUpdatedAtMs = pageUpdatedAtMs
)
}
Expand Down
126 changes: 18 additions & 108 deletions app/src/main/java/com/ethran/notable/editor/drawing/backgrounds.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.ethran.notable.editor.drawing

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
Expand All @@ -10,22 +9,13 @@ import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.unit.IntOffset
import com.ethran.notable.R
import com.ethran.notable.SCREEN_HEIGHT
import com.ethran.notable.SCREEN_WIDTH
import com.ethran.notable.data.datastore.GlobalAppSettings
import com.ethran.notable.data.model.BackgroundType
import com.ethran.notable.editor.PageView
import com.ethran.notable.editor.utils.scaleRect
import com.ethran.notable.io.getPdfPageCount
import com.ethran.notable.io.loadBackgroundBitmap
import com.ethran.notable.utils.logCallStack
import com.onyx.android.sdk.extension.copy
import com.onyx.android.sdk.extension.isNotNull
import io.shipbook.shipbooksdk.ShipBook
import kotlin.math.cos
import kotlin.math.floor
Expand Down Expand Up @@ -185,40 +175,6 @@ fun drawHexagon(canvas: Canvas, centerX: Float, centerY: Float, r: Float) {
canvas.drawPath(path, defaultPaintStroke)
}

fun drawBackgroundImages(
context: Context,
canvas: Canvas,
backgroundImage: String,
scroll: Offset,
page: PageView? = null,
scale: Float = 1.0F,
repeat: Boolean = false,
) {
try {
val imageBitmap = when (backgroundImage) {
"iris" -> {
val resId = R.drawable.iris
ImageBitmap.imageResource(context.resources, resId).asAndroidBitmap()
}

else -> {
if (page != null) {
page.getOrLoadBackground(backgroundImage, -1, scale)
} else {
loadBackgroundBitmap(backgroundImage, -1, scale)
}
}
}

if (imageBitmap != null) {
drawBitmapToCanvas(canvas, imageBitmap, scroll, scale, repeat)
} else {
log.e("Failed to load image from $backgroundImage")
}
} catch (e: Exception) {
log.e("Error loading background image: ${e.message}", e)
}
}

fun drawTitleBox(canvas: Canvas) {

Expand Down Expand Up @@ -257,37 +213,6 @@ fun drawTitleBox(canvas: Canvas) {
}


fun drawPdfPage(
canvas: Canvas,
pdfUriString: String,
pageNumber: Int,
scroll: Offset,
page: PageView? = null,
scale: Float = 1.0f
) {
if (pageNumber < 0) {
log.e("Page number should not be ${pageNumber}, uri: $pdfUriString")
logCallStack("DrawPdfPage")
return
}
try {
val imageBitmap = if (page != null) {
page.getOrLoadBackground(pdfUriString, pageNumber, scale)
} else {
// here, if we don't have page, we assume are doing export,
// so background have to be in better quality
// (it is scaled down, but still takes whole screen, not like when we render it)
loadBackgroundBitmap(pdfUriString, pageNumber, 1f)
}
if (imageBitmap.isNotNull()) {
drawBitmapToCanvas(canvas, imageBitmap, scroll, scale, false)
}

} catch (e: Exception) {
log.e("drawPdfPage: Failed to render PDF", e)
}
}

fun drawBitmapToCanvas(
canvas: Canvas, imageBitmap: Bitmap, scroll: Offset, scale: Float, repeat: Boolean
) {
Expand Down Expand Up @@ -342,50 +267,23 @@ fun drawBitmapToCanvas(
}

fun drawBg(
context: Context,
canvas: Canvas,
backgroundType: BackgroundType,
background: String,
scroll: Offset = Offset.Zero,
scale: Float = 1f, // When exporting, we change scale of canvas. therefore canvas.width/height is scaled
page: PageView? = null,
clipRect: Rect? = null // before the scaling
resourceBitmap: Bitmap?,
scale: Float = 1f, // When exporting, we change scale of canvas. therefore canvas.width/height is scaled
repeat: Boolean = false, // for repeating image
clipRect: Rect? = null, // before the scaling
) {

log.v("Loading the background")
clipRect?.let {
canvas.save()
canvas.clipRect(scaleRect(it, scale))
}
when (backgroundType) {
is BackgroundType.Image -> {
drawBackgroundImages(context, canvas, background, scroll, page, scale)
}

is BackgroundType.ImageRepeating -> {
drawBackgroundImages(context, canvas, background, scroll, page, scale, true)
}

is BackgroundType.CoverImage -> {
drawBackgroundImages(context, canvas, background, Offset.Zero, page, scale)
drawTitleBox(canvas)
}

is BackgroundType.AutoPdf -> {
if (page == null) return
val pageNumber = page.currentPageNumber
if (0 <= pageNumber && pageNumber < getPdfPageCount(background)) drawPdfPage(
canvas, background, pageNumber, scroll, page, scale
)
else {
log.w("Page number $pageNumber is out of bounds")
canvas.drawColor(Color.WHITE)
}
}

is BackgroundType.Pdf -> {
drawPdfPage(canvas, background, backgroundType.page, scroll, page, scale)
}

// draw native background for it we don't need a resource
is BackgroundType.Native -> {
when (background) {
"blank" -> canvas.drawColor(Color.WHITE)
Expand All @@ -398,6 +296,18 @@ fun drawBg(
}
}
}

else -> {
if (resourceBitmap != null) {
drawBitmapToCanvas(canvas, resourceBitmap, scroll, scale, repeat)
if (backgroundType is BackgroundType.CoverImage) {
drawTitleBox(canvas)
}
} else {
log.i("No resource provided to draw, maybe out of pages in pdf?")
canvas.drawColor(Color.WHITE)
}
}
}
drawMargin(canvas, scroll, scale)

Expand Down
Loading
Loading