2
0
mirror of https://github.com/koreader/koreader synced 2024-10-31 21:20:20 +00:00
koreader/frontend/ui/widget/imagewidget.lua

188 lines
5.5 KiB
Lua
Raw Normal View History

--[[--
2016-11-16 09:31:39 +00:00
ImageWidget shows an image from a file or memory
2016-11-16 09:31:39 +00:00
Show image from file example:
UIManager:show(ImageWidget:new{
file = "resources/info-i.png",
-- Make sure alpha is set to true if png has transparent background
-- alpha = true,
})
2016-11-16 09:31:39 +00:00
Show image from memory example:
UIManager:show(ImageWidget:new{
-- bitmap_buffer should be a block of memory that holds the raw
-- uncompressed bitmap.
image = bitmap_buffer,
})
]]
2013-10-18 20:38:07 +00:00
local Widget = require("ui/widget/widget")
2014-11-20 07:52:05 +00:00
local Screen = require("device").screen
2014-08-22 09:22:41 +00:00
local CacheItem = require("cacheitem")
local Mupdf = require("ffi/mupdf")
2013-10-18 20:38:07 +00:00
local Geom = require("ui/geometry")
2014-08-22 09:22:41 +00:00
local Cache = require("cache")
local logger = require("logger")
2014-08-22 09:22:41 +00:00
local ImageCache = Cache:new{
max_memsize = 2*1024*1024, -- 2M of image cache
current_memsize = 0,
cache = {},
-- this will hold the LRU order of the cache
cache_order = {}
}
local ImageCacheItem = CacheItem:new{}
function ImageCacheItem:onFree()
if self.bb.free then
logger.dbg("free image blitbuffer", self.bb)
2014-08-22 09:22:41 +00:00
self.bb:free()
end
end
2013-03-12 17:18:53 +00:00
2013-10-18 20:38:07 +00:00
local ImageWidget = Widget:new{
2014-03-13 13:52:43 +00:00
file = nil,
image = nil,
2014-03-13 13:52:43 +00:00
invert = nil,
dim = nil,
hide = nil,
-- if width or height is given, image will rescale to the given size
width = nil,
height = nil,
2014-11-20 07:52:05 +00:00
-- if autoscale is true image will be rescaled according to screen dpi
autoscale = false,
-- when alpha is set to true, alpha values from the image will be honored
alpha = false,
-- when autostretch is set to true, image will be stretched to best fit the
-- widget size. i.e. either fit the width or fit the height according to the
-- original image size.
autostretch = false,
-- when overflow is set to true, image will be stretched to fit the widget
-- size vertically and horizontally, without impact original aspect ratio.
-- But overflow part will be ignored.
overflow = false,
2014-03-13 13:52:43 +00:00
_bb = nil
2013-03-12 17:18:53 +00:00
}
function ImageWidget:_loadimage()
self._bb = self.image
end
function ImageWidget:_loadfile()
2014-03-13 13:52:43 +00:00
local itype = string.lower(string.match(self.file, ".+%.([^.]+)") or "")
if itype == "png" or itype == "jpg" or itype == "jpeg"
or itype == "tiff" then
2014-08-22 09:22:41 +00:00
local hash = "image|"..self.file.."|"..(self.width or "").."|"..(self.height or "")
local cache = ImageCache:check(hash)
if cache then
-- hit cache
self._bb = cache.bb
else
if self.height and self.height > 200 then -- don't cache big images
self._bb = Mupdf.renderImageFile(self.file, self.width, self.height)
else
-- cache this image
logger.dbg("cache", hash)
cache = ImageCacheItem:new{
bb = Mupdf.renderImageFile(self.file, self.width, self.height),
}
cache.size = cache.bb.pitch * cache.bb.h * cache.bb:getBpp() / 8
ImageCache:insert(hash, cache)
self._bb = cache.bb
end
2014-08-22 09:22:41 +00:00
end
else
error("Image file type not supported.")
2014-03-13 13:52:43 +00:00
end
end
function ImageWidget:_render()
if self._bb then -- already rendered
return
end
if self.image then
self:_loadimage()
elseif self.file then
self:_loadfile()
else
error("cannot render image")
end
2014-11-20 07:52:05 +00:00
local native_w, native_h = self._bb:getWidth(), self._bb:getHeight()
local w, h = self.width, self.height
2014-11-20 07:52:05 +00:00
if self.autoscale then
local dpi_scale = Screen:getDPI() / 167
-- rounding off to power of 2 to avoid alias with pow(2, floor(log(x)/log(2))
local scale = math.pow(2, math.max(0, math.floor(math.log(dpi_scale)/0.69)))
w, h = scale * native_w, scale * native_h
2016-04-15 20:21:39 +00:00
elseif self.width and self.height then
if self.autostretch then
local ratio = native_w / self.width / native_h * self.height
if ratio < 1 then
h = self.height
w = self.width * ratio
else
h = self.height * ratio
w = self.width
end
elseif self.overflow then
local ratio = native_w / self.width / native_h * self.height
if ratio < 1 then
h = self.height / ratio
w = self.width
else
h = self.height
w = self.width / ratio
end
end
2014-11-20 07:52:05 +00:00
end
if (w and w ~= native_w) or (h and h ~= native_h) then
self._bb = self._bb:scale(w or native_w, h or native_h)
end
2013-03-12 17:18:53 +00:00
end
function ImageWidget:getSize()
2014-08-22 09:22:41 +00:00
self:_render()
2014-03-13 13:52:43 +00:00
return Geom:new{ w = self._bb:getWidth(), h = self._bb:getHeight() }
2013-03-12 17:18:53 +00:00
end
2014-08-14 12:11:21 +00:00
function ImageWidget:rotate(degree)
2014-08-22 09:22:41 +00:00
self:_render()
2014-08-14 12:11:21 +00:00
self._bb:rotate(degree)
end
2013-03-12 17:18:53 +00:00
function ImageWidget:paintTo(bb, x, y)
2014-08-22 09:22:41 +00:00
if self.hide then return end
-- self:_reader is called in getSize method
2014-03-13 13:52:43 +00:00
local size = self:getSize()
self.dimen = Geom:new{
x = x, y = y,
w = size.w,
h = size.h
2014-03-13 13:52:43 +00:00
}
if self.alpha == true then
bb:alphablitFrom(self._bb, x, y, 0, 0, size.w, size.h)
else
bb:blitFrom(self._bb, x, y, 0, 0, size.w, size.h)
end
2014-03-13 13:52:43 +00:00
if self.invert then
bb:invertRect(x, y, size.w, size.h)
end
if self.dim then
bb:dimRect(x, y, size.w, size.h)
end
2013-03-12 17:18:53 +00:00
end
function ImageWidget:free()
if self.image then
self.image:free()
self.image = nil
end
end
2013-10-18 20:38:07 +00:00
return ImageWidget