Files
koreader/frontend/device/screen.lua
Hans-Werner Hilse bcf0a9923c (really?) fix refresh viewport calculations
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.
2014-11-17 10:04:36 +01:00

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