fix(reader): preserve KOReader covers in night mode

Counter-invert custom cover pixels before KOReader applies its screen-wide night mode so artwork retains its original colors.

Closes #1885
This commit is contained in:
neon
2026-10-21 18:51:54 +00:00
parent fbd7a2e125
commit 9d1319ef8b
3 changed files with 94 additions and 0 deletions
@@ -16,6 +16,7 @@ data (titles, authors, quotes) may be shortened; pass data = true for those.
local BD = require("ui/bidi")
local CenterContainer = require("ui/widget/container/centercontainer")
local Screen = require("device").screen
local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local HorizontalGroup = require("ui/widget/horizontalgroup")
@@ -637,6 +638,7 @@ function BoardCover:paintTo(bb, x, y)
local px_, py_ = x + math.floor((w - img.w) / 2), y + math.floor((h - img.h) / 2)
bb:blitFrom(img.bb, px_, py_, 0, 0, img.w, img.h)
if self.dim then bb:lightenRect(x, y, w, h, 0.55) end
if Screen.night_mode then bb:invertRect(px_, py_, img.w, img.h) end
if self.border > 0 then bb:paintBorder(px_, py_, img.w, img.h, self.border, self.border_col or INK.gb) end
return
end
@@ -658,6 +660,9 @@ function BoardCover:paintTo(bb, x, y)
elseif self.lighten then
bb:lightenRect(dx, dy, dw, dh, self.lighten)
end
if Screen.night_mode then
bb:invertRect(dx, dy, dw, dh)
end
if self.border > 0 then
bb:paintBorder(dx, dy, dw, dh, self.border, self.border_col or INK.gb)
end
@@ -0,0 +1,88 @@
package.path = "koreader-plugin/bookorbit.koplugin/?.lua;" .. package.path
local widget = {}
function widget:extend(options)
local class = options or {}
class.__index = class
return setmetatable(class, { __index = self })
end
function widget:new(options)
return setmetatable(options or {}, { __index = self })
end
for _, name in ipairs({
"ui/geometry", "ui/widget/container/centercontainer", "ui/widget/container/framecontainer",
"ui/widget/container/leftcontainer", "ui/widget/container/rightcontainer",
"ui/widget/container/widgetcontainer", "ui/widget/horizontalgroup", "ui/widget/horizontalspan",
"ui/widget/overlapgroup", "ui/widget/textboxwidget", "ui/widget/textwidget",
"ui/widget/verticalgroup", "ui/widget/verticalspan", "ui/widget/widget",
}) do
package.loaded[name] = widget
end
local native_width, native_height = 100, 150
local native = {
getWidth = function() return native_width end,
getHeight = function() return native_height end,
}
local scaled = { free = function() end }
package.loaded["ui/renderimage"] = {
renderImageFile = function() return native end,
scaleBlitBuffer = function() return scaled end,
}
package.loaded["ui/bidi"] = {}
package.loaded["bookorbit_board_icons"] = {}
package.loaded["bookorbit_board_metrics"] = {
INK = { gb = 32, ge = 224 },
px = function(value) return value end,
}
local Screen = { night_mode = false }
package.loaded["device"] = { screen = Screen }
local BoardUI = require("bookorbit_board_ui")
local cover = BoardUI.cover({}, 100, 150, {
path = "cover.jpg",
border = 1,
dim = true,
})
local calls = {}
local bb = setmetatable({}, {
__index = function(_, name)
return function(_, ...)
table.insert(calls, { name, ... })
end
end,
})
cover:paintTo(bb, 7, 11)
assert(calls[1][1] == "blitFrom", "the cover image is painted")
assert(calls[2][1] == "lightenRect", "the offline treatment is painted over the image")
assert(calls[3][1] == "paintBorder", "day mode leaves the image colors unchanged")
calls = {}
Screen.night_mode = true
cover:paintTo(bb, 7, 11)
assert(calls[1][1] == "blitFrom", "the cover image is painted in night mode")
assert(calls[2][1] == "lightenRect", "the offline treatment stays before night-mode correction")
assert(calls[3][1] == "invertRect", "night mode counter-inverts the image before the screen is inverted")
assert(calls[3][2] == 7 and calls[3][3] == 11 and calls[3][4] == 100 and calls[3][5] == 150,
"only the painted image rectangle is counter-inverted")
assert(calls[4][1] == "paintBorder", "the UI border remains subject to night-mode inversion")
native_width, native_height = 100, 100
local plated = BoardUI.cover({}, 100, 150, {
path = "square-cover.jpg",
border = 1,
plate = true,
})
calls = {}
plated:paintTo(bb, 7, 11)
assert(calls[1][1] == "paintRect" and calls[2][1] == "blitFrom", "a nonstandard cover is painted on its plate")
assert(calls[3][1] == "invertRect", "a plated cover is also corrected in night mode")
assert(calls[3][2] == 7 and calls[3][3] == 36 and calls[3][4] == 100 and calls[3][5] == 100,
"the plate stays inverted with the UI while the image keeps its original colors")
assert(calls[4][1] == "paintBorder", "the plated image border remains subject to night-mode inversion")
print("bookorbit_board_cover_night_mode_test.lua: passed")
@@ -14,6 +14,7 @@ for _, name in ipairs({
end
package.loaded["ui/bidi"] = {}
package.loaded["ui/renderimage"] = {}
package.loaded["device"] = { screen = {} }
package.loaded["bookorbit_board_icons"] = {}
package.loaded["bookorbit_board_metrics"] = { INK = {}, px = function(n) return n end }