forked from koreader/koreader-base
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjpeg.lua
146 lines (121 loc) · 4.87 KB
/
jpeg.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
--[[--
Module for JPEG decoding/encoding.
This is a LuaJIT FFI wrapper for turbojpeg.
@module ffi.jpeg
]]
local ffi = require("ffi")
local C = ffi.C
local BB = require("ffi/blitbuffer")
require("ffi/posix_h")
require("ffi/turbojpeg_h")
-- The turbojpeg library symbols are versioned, so it should always be
-- backward compatible: the major & patch numbers are always 0, and when
-- a new API version is made available, the minor number is incremented.
local ok, turbojpeg = pcall(ffi.loadlib, "turbojpeg", "0.3.0")
if not ok then
turbojpeg = ffi.load("turbojpeg")
end
local Jpeg = {}
function Jpeg.openDocument(filename, color)
local fh = io.open(filename, "rb")
assert(fh, "couldn't open JPG file")
local data = fh:read("*a")
fh:close()
return Jpeg.openDocumentFromMem(data, color)
end
function Jpeg.openDocumentFromMem(data, color)
local handle = turbojpeg.tj3Init(turbojpeg.TJINIT_DECOMPRESS)
assert(handle, "no TurboJPEG API decompressor handle")
-- Gotta go fast!
turbojpeg.tj3Set(handle, turbojpeg.TJPARAM_FASTUPSAMPLE, 1)
turbojpeg.tj3Set(handle, turbojpeg.TJPARAM_FASTDCT, 1)
if turbojpeg.tj3DecompressHeader(handle, ffi.cast("const unsigned char*", data), #data) < 0 then
turbojpeg.tj3Destroy(handle)
error("reading JPEG header")
end
local width = turbojpeg.tj3Get(handle, turbojpeg.TJPARAM_JPEGWIDTH)
local height = turbojpeg.tj3Get(handle, turbojpeg.TJPARAM_JPEGHEIGHT)
--[[
local inSubsamp = turbojpeg.tj3Get(handle, turbojpeg.TJPARAM_SUBSAMP)
local inColorspace = turbojpeg.tj3Get(handle, turbojpeg.TJPARAM_COLORSPACE)
--]]
local image_bb
local format
local components
if color then
image_bb = BB.new(width, height, BB.TYPE_BBRGB24)
components = 3
format = turbojpeg.TJPF_RGB
else
image_bb = BB.new(width, height, BB.TYPE_BB8)
components = 1
format = turbojpeg.TJPF_GRAY
end
if turbojpeg.tj3Decompress8(handle, ffi.cast("unsigned char*", data), #data,
ffi.cast("unsigned char*", image_bb.data), image_bb.stride, format) < 0 then
turbojpeg.tj3Destroy(handle)
error("decoding JPEG file")
end
turbojpeg.tj3Destroy(handle)
return image_bb, width, height, components
end
function Jpeg.encodeToFile(filename, source_ptr, w, stride, h, quality, color_type, subsample)
local handle = turbojpeg.tj3Init(turbojpeg.TJINIT_COMPRESS)
assert(handle, "no TurboJPEG API compressor handle")
turbojpeg.tj3Set(handle, turbojpeg.TJPARAM_SUBSAMP, subsample or turbojpeg.TJSAMP_420)
turbojpeg.tj3Set(handle, turbojpeg.TJPARAM_QUALITY, quality or 75)
turbojpeg.tj3Set(handle, turbojpeg.TJPARAM_FASTDCT, 1)
local jpeg_size = ffi.new("size_t [1]")
local jpeg_image = ffi.new("unsigned char* [1]")
if turbojpeg.tj3Compress8(handle, source_ptr, w, stride, h, color_type or turbojpeg.TJPF_RGB, jpeg_image, jpeg_size) == 0 then
local fhandle = C.open(filename, bit.bor(C.O_WRONLY, C.O_CREAT, C.O_TRUNC, C.O_CLOEXEC), ffi.cast("int", bit.bor(C.S_IRUSR, C.S_IWUSR, C.S_IRGRP, C.S_IROTH)))
if fhandle >= 0 then
C.write(fhandle, jpeg_image[0], jpeg_size[0])
C.close(fhandle)
end
end
turbojpeg.tj3Destroy(handle)
-- Handles NULL pointers sanely
turbojpeg.tj3Free(jpeg_image[0])
end
-- convert rgb to grayscale
-- gray = 0.299 R + 0.587 G + 0.114 B
-- https://www.dynamsoft.com/blog/insights/image-processing/image-processing-101-color-space-conversion/
function Jpeg.convertToGray(source_ptr, stride, h)
for y = 0, h - 1 do
local offs = y * stride
local offs_8bit = offs
for _ = 0, tonumber(stride), 3 do
local r = .299 * source_ptr[offs]
offs = offs + 1
local g = .587 * source_ptr[offs]
offs = offs + 1
local b = .114 * source_ptr[offs]
offs = offs + 1
source_ptr[offs_8bit] = math.floor(r + g + b + 0.5)
offs_8bit = offs_8bit + 1
end
end
end
function Jpeg.writeBMP(filename, source_ptr, w, stride, h, grayscale)
local handle = turbojpeg.tj3Init(turbojpeg.TJINIT_COMPRESS)
assert(handle, "no TurboJPEG API compressor handle")
local pixel_format
if grayscale then
pixel_format = turbojpeg.TJPF_GRAY
Jpeg.convertToGray(source_ptr, stride, h)
else
pixel_format = turbojpeg.TJPF_RGB
end
-- if file extension is not ".bmp" tjSaveImage uses netpbm format!
if filename:sub(-#".bmp") == ".bmp" then
turbojpeg.tj3SaveImage8(handle, filename, source_ptr, w, stride, h, pixel_format)
else
os.remove(filename)
local tmp_filename = filename .. ".tmp.bmp"
turbojpeg.tj3SaveImage8(handle, tmp_filename, source_ptr, w, stride, h, pixel_format)
os.rename(tmp_filename, filename)
end
turbojpeg.tj3Destroy(handle)
end
return Jpeg