mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
This adds more documentation about the viewport offset calculations for refreshing rotated viewports. The old behaviour also was (still) buggy, even after the latest round of fixes. Now, the offsets when rotated are hopefully calculated correct.
244 lines
6.6 KiB
Lua
244 lines
6.6 KiB
Lua
local Blitbuffer = require("ffi/blitbuffer")
|
|
local einkfb = require("ffi/framebuffer")
|
|
local Geom = require("ui/geometry")
|
|
local util = require("ffi/util")
|
|
local DEBUG = require("dbg")
|
|
|
|
--[[
|
|
Codes for rotation modes:
|
|
|
|
1 for no rotation,
|
|
2 for landscape with bottom on the right side of screen, etc.
|
|
|
|
2
|
|
+--------------+
|
|
| +----------+ |
|
|
| | | |
|
|
| | Freedom! | |
|
|
| | | |
|
|
| | | |
|
|
3 | | | | 1
|
|
| | | |
|
|
| | | |
|
|
| +----------+ |
|
|
| |
|
|
| |
|
|
+--------------+
|
|
0
|
|
--]]
|
|
|
|
|
|
local Screen = {
|
|
cur_rotation_mode = 0,
|
|
native_rotation_mode = nil,
|
|
blitbuffer_rotation_mode = 0,
|
|
|
|
bb = nil,
|
|
saved_bb = nil,
|
|
|
|
screen_size = Geom:new(),
|
|
viewport = nil,
|
|
|
|
fb = einkfb.open("/dev/fb0"),
|
|
-- will be set upon loading by Device class:
|
|
device = nil,
|
|
}
|
|
|
|
function Screen:new(o)
|
|
local o = o or {}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
if o.init then o:init() end
|
|
return o
|
|
end
|
|
|
|
function Screen:init()
|
|
self.bb = self.fb.bb
|
|
self.blitbuffer_rotation_mode = self.bb:getRotation()
|
|
-- asking the framebuffer for orientation is error prone,
|
|
-- so we do this simple heuristic (for now)
|
|
self.screen_size.w = self.bb:getWidth()
|
|
self.screen_size.h = self.bb:getHeight()
|
|
if self.screen_size.w > self.screen_size.h then
|
|
self.native_rotation_mode = 1
|
|
self.screen_size.w, self.screen_size.h = self.screen_size.h, self.screen_size.w
|
|
else
|
|
self.native_rotation_mode = 0
|
|
end
|
|
self.cur_rotation_mode = self.native_rotation_mode
|
|
end
|
|
|
|
--[[
|
|
set a rectangle that represents the area of the screen we are working on
|
|
--]]
|
|
function Screen:setViewport(viewport)
|
|
self.viewport = self.screen_size:intersect(viewport)
|
|
self.bb = self.fb.bb:viewport(
|
|
self.viewport.x, self.viewport.y,
|
|
self.viewport.w, self.viewport.h)
|
|
end
|
|
|
|
function Screen:refresh(refresh_type, waveform_mode, wait_for_marker, x, y, w, h)
|
|
if self.viewport and x and y then
|
|
--[[
|
|
we need to adapt the coordinates when we have a viewport.
|
|
this adaptation depends on the rotation:
|
|
|
|
0,0 fb.w
|
|
+---+---------------------------+---+
|
|
| |v.y v.y| |
|
|
|v.x| |vx2|
|
|
+---+---------------------------+---+
|
|
| | v.w | |
|
|
| | | |
|
|
| | | |
|
|
| |v.h (viewport) | |
|
|
| | | | fb.h
|
|
| | | |
|
|
| | | |
|
|
| | | |
|
|
+---+---------------------------+---+
|
|
|v.x| |vx2|
|
|
| |vy2 vy2| |
|
|
+---+---------------------------+---+
|
|
|
|
The viewport offset v.y/v.x only applies when rotation is 0 degrees.
|
|
For other rotations (0,0 is in one of the other edges), we need to
|
|
recalculate the offsets.
|
|
--]]
|
|
|
|
local vx2 = self.screen_size.w - (self.viewport.x + self.viewport.w)
|
|
local vy2 = self.screen_size.h - (self.viewport.y + self.viewport.h)
|
|
|
|
if self.cur_rotation_mode == 0 then
|
|
-- (0,0) is at top left of screen
|
|
x = x + self.viewport.x
|
|
y = y + self.viewport.y
|
|
elseif self.cur_rotation_mode == 1 then
|
|
-- (0,0) is at bottom left of screen
|
|
x = x + vy2
|
|
y = y + self.viewport.x
|
|
elseif self.cur_rotation_mode == 2 then
|
|
-- (0,0) is at bottom right of screen
|
|
x = x + vx2
|
|
y = y + vy2
|
|
else
|
|
-- (0,0) is at top right of screen
|
|
x = x + self.viewport.y
|
|
y = y + vx2
|
|
end
|
|
end
|
|
self.fb:refresh(refresh_type, waveform_mode, wait_for_marker, x, y, w, h)
|
|
end
|
|
|
|
function Screen:getSize()
|
|
return Geom:new{w = self.bb:getWidth(), h = self.bb:getHeight()}
|
|
end
|
|
|
|
function Screen:getWidth()
|
|
return self.bb:getWidth()
|
|
end
|
|
|
|
function Screen:getScreenWidth()
|
|
return self.screen_size.w
|
|
end
|
|
|
|
function Screen:getScreenHeight()
|
|
return self.screen_size.h
|
|
end
|
|
|
|
function Screen:getHeight()
|
|
return self.bb:getHeight()
|
|
end
|
|
|
|
function Screen:getDPI()
|
|
if self.dpi == nil then
|
|
self.dpi = G_reader_settings:readSetting("screen_dpi")
|
|
end
|
|
if self.dpi == nil then
|
|
self.dpi = self.device.display_dpi
|
|
end
|
|
if self.dpi == nil then
|
|
self.dpi = 160
|
|
end
|
|
return self.dpi
|
|
end
|
|
|
|
function Screen:setDPI(dpi)
|
|
G_reader_settings:saveSetting("screen_dpi", dpi)
|
|
end
|
|
|
|
function Screen:scaleByDPI(px)
|
|
-- scaled positive px should also be positive
|
|
return math.ceil(px * self:getDPI()/167)
|
|
end
|
|
|
|
function Screen:getRotationMode()
|
|
return self.cur_rotation_mode
|
|
end
|
|
|
|
function Screen:getScreenMode()
|
|
if self:getWidth() > self:getHeight() then
|
|
return "landscape"
|
|
else
|
|
return "portrait"
|
|
end
|
|
end
|
|
|
|
function Screen:setRotationMode(mode)
|
|
self.bb:rotateAbsolute(-90 * (mode - self.native_rotation_mode - self.blitbuffer_rotation_mode))
|
|
if self.viewport then
|
|
self.fb.bb:setRotation(self.bb:getRotation())
|
|
end
|
|
self.cur_rotation_mode = mode
|
|
end
|
|
|
|
function Screen:setScreenMode(mode)
|
|
if mode == "portrait" then
|
|
if self.cur_rotation_mode ~= 0 then
|
|
self:setRotationMode(0)
|
|
end
|
|
elseif mode == "landscape" then
|
|
if self.cur_rotation_mode == 0 or self.cur_rotation_mode == 2 then
|
|
self:setRotationMode(DLANDSCAPE_CLOCKWISE_ROTATION and 1 or 3)
|
|
elseif self.cur_rotation_mode == 1 or self.cur_rotation_mode == 3 then
|
|
self:setRotationMode((self.cur_rotation_mode + 2) % 4)
|
|
end
|
|
end
|
|
end
|
|
|
|
function Screen:toggleNightMode()
|
|
self.bb:invert()
|
|
if self.viewport then
|
|
-- invert and blank out the full framebuffer when we are working on a viewport
|
|
self.fb.bb:invert()
|
|
self.fb.bb:fill(Blitbuffer.COLOR_WHITE)
|
|
end
|
|
end
|
|
|
|
function Screen:saveCurrentBB()
|
|
if self.saved_bb then self.saved_bb:free() end
|
|
self.saved_bb = self.bb:copy()
|
|
end
|
|
|
|
function Screen:restoreFromSavedBB()
|
|
if self.saved_bb then
|
|
self.bb:blitFullFrom(self.saved_bb)
|
|
-- free data
|
|
self.saved_bb:free()
|
|
self.saved_bb = nil
|
|
end
|
|
end
|
|
|
|
function Screen:shot(filename)
|
|
DEBUG("write PNG file", filename)
|
|
self.bb:writePNG(filename)
|
|
end
|
|
|
|
function Screen:close()
|
|
DEBUG("close screen framebuffer")
|
|
self.fb:close()
|
|
end
|
|
|
|
return Screen
|