-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdraw.lua
More file actions
143 lines (119 loc) · 5.53 KB
/
Copy pathdraw.lua
File metadata and controls
143 lines (119 loc) · 5.53 KB
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
--[[
Draw on an image from the command line, without acrionphoto.
nexuslua draw.lua <input image> <output image>
A plugin is not tied to the application that usually hosts it: acrionphoto is one caller of
"acrion image tools", and the nexuslua interpreter is another. The difference is that
acrionphoto fills in the image parameters from its two panes, whereas a script opens an image
itself and passes the fields on - which is what this file shows.
It also shows the three things worth knowing about talking to an asynchronous plugin:
* every send returns immediately and the answer arrives as a message of its own, so a
sequence of steps is a chain of handlers rather than a list of statements;
* a message that works *in place* replies with no image payload - the fields come back in
`original_message.parameters`, which is where the next step takes them from;
* the drawing messages of this plugin are themselves written in Lua (see main.lua), so
everything below could equally well be done in this file. Sending them is the point here:
a plugin is a library that happens to be reachable by message.
]]
local tools = "acrion image tools"
local input = arg[1]
local output = arg[2]
if not input or not output then
print("usage: nexuslua draw.lua <input image> <output image>")
return
end
--- The seven fields that describe an image.
--
-- Every message of every image plugin expects exactly these. Copied field by field on
-- purpose: the reply also carries `original_message` and `reply_to`, which are the envelope
-- rather than the image.
local function ImageOf(reply)
return {
imageBuffer = reply.imageBuffer,
width = reply.width,
height = reply.height,
channels = reply.channels,
depth = reply.depth,
minBrightness = reply.minBrightness,
maxBrightness = reply.maxBrightness
}
end
local pending = 0
--- Sends one drawing message and counts it, so the save can wait for the last one.
local function Draw(image, message, shape)
pending = pending + 1
send(tools, message, mergetables(image, mergetables(shape,
{ reply_to = { agent = "main", message = "OnDrawn" } })))
end
function OnOpened(reply)
if reply.error then
print("could not open " .. input .. ": " .. tostring(reply.error))
return
end
local image = ImageOf(reply)
print(string.format("opened %s: %dx%d, %d channel(s), depth %d",
input, image.width, image.height, image.channels, image.depth))
local w, h = image.width, image.height
-- A frame one tenth in from each edge.
Draw(image, "CallDrawRectangle", {
rectX = w // 10, rectY = h // 10,
rectWidth = w - 2 * (w // 10), rectHeight = h - 2 * (h // 10),
lineWidth = 3,
colorRed = 1.0, colorGreen = 1.0, colorBlue = 1.0 })
-- Two diagonals. A colour is three values in [0, 1] mapped onto the brightness range of
-- *this* image, so the same numbers are correct for an 8 bit PNG and for a floating point
-- FITS frame.
Draw(image, "CallDrawLine", { x0 = 0, y0 = 0, x1 = w - 1, y1 = h - 1,
colorRed = 1.0, colorGreen = 0.2, colorBlue = 0.2 })
Draw(image, "CallDrawLine", { x0 = w - 1, y0 = 0, x1 = 0, y1 = h - 1,
colorRed = 0.2, colorGreen = 0.4, colorBlue = 1.0 })
-- A filled ellipse in the middle, clipped to the left half so that the clip rectangle is
-- visible in the result.
Draw(image, "CallDrawEllipse", {
centerX = w // 2, centerY = h // 2,
radiusX = w // 4, radiusY = h // 4,
filled = true,
clipX = 0, clipY = 0, clipWidth = w // 2, clipHeight = h,
colorRed = 0.9, colorGreen = 0.9, colorBlue = 0.2 })
-- A caption. The font is part of the plugin, so this comes out the same on a machine with
-- no fonts installed at all - and `scale` derived from the width keeps it readable on a
-- 3314 pixel astro frame as well as on a thumbnail.
Draw(image, "CallDrawText", {
textX = w // 10 + 4, textY = h // 10 + 4,
text = "acrion image tools\ndrawn from a script",
scale = math.max(1, w // 400),
colorRed = 0.2, colorGreen = 1.0, colorBlue = 0.4 })
end
function OnDrawn(reply)
if reply.error then
print("drawing failed: " .. tostring(reply.error))
return
end
-- Every drawing message reports the region it changed. A host repaints that rectangle
-- instead of the whole image; a script can use it to see what a call actually did.
if reply.invalidate then
print(string.format(" %s changed %dx%d at %d/%d",
reply.original_message.message_name,
reply.invalidate.width, reply.invalidate.height,
reply.invalidate.x, reply.invalidate.y))
end
pending = pending - 1
if pending > 0 then
return
end
-- The drawing worked in place, so this reply carries no image of its own - the message
-- that caused it does.
send(tools, "CallSaveImageFile", mergetables(ImageOf(reply.original_message.parameters),
{ path = output, reply_to = { agent = "main", message = "OnSaved" } }))
end
function OnSaved(reply)
if reply.error then
print("could not save " .. output .. ": " .. tostring(reply.error))
else
print("wrote " .. output)
end
end
addmessage("OnOpened")
addmessage("OnDrawn")
addmessage("OnSaved")
send(tools, "CallOpenImageFile",
{ path = input, reply_to = { agent = "main", message = "OnOpened" } })