Remove calls to wxApp::SetTopWindow() from the samples and documentation.
[wxWidgets.git] / samples / ribbon / fix_xpm.lua
1 #!/usr/bin/lua
2 -------------------------------------------------------------------------------
3 -- Name:        fix_xpm.lua
4 -- Purpose:     Fix XPM files for use in Ribbon sample
5 -- Author:      Peter Cawley
6 -- Modified by:
7 -- Created:     2009-07-06
8 -- RCS-ID:      $Id$
9 -- Copyright:   (C) Copyright 2009, Peter Cawley
10 -- Licence:     wxWindows Library Licence
11 -------------------------------------------------------------------------------
12 -- My preferred image editor (Paint Shop Pro 9) spits out XPM files, but with
13 -- some deficiencies:
14 -- 1) Specifies a 256 colour palette, even when less than 256 colours are used
15 -- 2) Transparency is replaced by a non-transparent colour
16 -- 3) Does not name the C array appropriately
17 -- 4) Array and strings not marked const
18
19 assert(_VERSION == "Lua 5.1", "Lua 5.1 is required")
20 local lpeg = require "lpeg"
21
22 -- Parse command line
23 local args = {...}
24 local filename = assert(...,"Expected filename as first command line argument")
25 local arg_transparent
26 local arg_name
27 local arg_out
28 for i = 2, select('#', ...) do
29   local arg = args[i]
30   if arg == "/?" or arg == "-?" or arg == "--help" then
31     print("Usage: filename [transparent=<colour>|(x,y)] [name=<array_name>] "..
32           "[out=<filename>]")
33     print("In addition to the transparent colour and name changes, the "..
34           "palette will be also be optimised")
35     print "Examples:"
36     print("  in.xpm transparent=Gray100 -- Modifies in.xpm, replacing "..
37           "Gray100 with transparent")
38     print("  in.xpm transparent=(0,0) -- Modifies in.xpm, replacing "..
39           "whichever colour is at (0,0) with transparent")
40     print("  in.xpm name=out_xpm out=out.xpm -- Copies in.xpm to out.xpm, "..
41           "and changes the array name to out_xpm")
42     return
43   end
44   arg_transparent = arg:match"transparent=(.*)" or arg_transparent
45   arg_name = arg:match"name=(.*)" or arg_name
46   arg_out = arg:match"out=(.*)" or arg_out
47 end
48
49 -- XPM parsing
50 local grammars = {}
51 do
52   local C, P, R, S, V = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V
53   local Ct = lpeg.Ct
54   local comment = P"/*" * (1 - P"*/") ^ 0 * P"*/"
55   local ws = (S" \r\n\t" + comment) ^ 0
56   local function tokens(...)
57     local result = ws
58     for i, arg in ipairs{...} do
59       if type(arg) == "table" then
60         arg = P(arg[1]) ^ -1
61       end
62       result = result * P(arg) * ws
63     end
64     return result
65   end
66   grammars.file = P { "xpm";
67     xpm = P"/* XPM */" * ws *
68          tokens("static",{"const"},"char","*",{"const"}) * V"name" *
69          tokens("[","]","=","{") * V"lines",
70     name = C(R("az","AZ","__") * R("az","AZ","09","__") ^ 0),
71     lines = Ct(V"line" ^ 1),
72     line = ws * P'"' * C((1 - P'"') ^ 0) * P'"' * (tokens"," + V"eof"),
73     eof = tokens("}",";") * P(1) ^ 0,
74   }
75   grammars.values = P { "values";
76     values = Ct(V"value" * (S" \r\n\t" ^ 1 * V"value") ^ 3),
77     value = C(R"09" ^ 1) / tonumber,
78   }
79   function make_remaining_grammars(cpp)
80     local char = R"\32\126" - S[['"\]] -- Most of lower ASCII
81     local colour = char
82     for i = 2, cpp do
83       colour = colour * char
84     end
85     grammars.colour = P { "line";
86       line = C(colour) * Ct(Ct(ws * V"key" * ws * V"col") ^ 1),
87       key = C(P"g4" + S"msgc"),
88       col = V"name" + V"hex",
89       name = C(R("az","AZ","__") * R("az","AZ","09","__") ^ 0),
90       hex = C(P"#" * R("09","af","AF") ^ 3),
91     }
92     grammars.pixels = P { "line";
93       line = Ct(C(colour) ^ 1),
94     }
95   end
96 end
97
98 -- Load file
99 local file = assert(io.open(filename,"rt"))
100 local filedata = file:read"*a"
101 file:close()
102
103 local xpm = {}
104 xpm.name, xpm.lines = grammars.file:match(filedata)
105 local values_table = assert(grammars.values:match(xpm.lines[1]))
106 xpm.width, xpm.height, xpm.ncolours, xpm.cpp = unpack(values_table)
107 make_remaining_grammars(xpm.cpp)
108 xpm.colours = {}
109 xpm.colours_full = {}
110 for i = 1, xpm.ncolours do
111   local name, data = grammars.colour:match(xpm.lines[1 + i])
112   local colour = ""
113   for _, datum in ipairs(data) do
114     if datum[1] == "c" then
115       colour = datum[2]
116       break
117     end
118   end
119   assert(colour, "No colour data for " .. name)
120   xpm.colours[name] = colour
121   xpm.colours_full[i] = {name = name, unpack(data)}
122 end
123 xpm.pixels = {}
124 for y = 1, xpm.height do
125   xpm.pixels[y] = grammars.pixels:match(xpm.lines[1 + xpm.ncolours + y])
126   if not xpm.pixels[y] or #xpm.pixels[y] ~= xpm.width then
127     error("Line " .. y .. " is invalid")
128   end
129 end
130
131 -- Fix palette
132 repeat
133   local n_colours_used = 0
134   local colours_used = setmetatable({}, {__newindex = function(t, k, v)
135     n_colours_used = n_colours_used + 1
136     rawset(t, k, v)
137   end})
138   for y = 1, xpm.height do
139     for x = 1, xpm.width do
140       colours_used[xpm.pixels[y][x]] = true
141     end
142   end
143   if n_colours_used == xpm.ncolours then
144     break
145   end
146   local chars =" .abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567"
147   local cpp = (n_colours_used > #chars) and 2 or 1
148   local nalloc = 0
149   local colour_map = setmetatable({}, {__index = function(t, k)
150     nalloc = nalloc + 1
151     local v
152     if cpp == 1 then
153       v = chars:sub(nalloc, nalloc)
154     else
155       local a, b = math.floor(nalloc / #chars) + 1, (nalloc % #chars) + 1
156       v = chars:sub(a, a) .. chars:sub(b, b)
157     end
158     t[k] = v
159     return v
160   end})
161   for y = 1, xpm.height do
162     for x = 1, xpm.width do
163       xpm.pixels[y][x] = colour_map[xpm.pixels[y][x]]
164     end
165   end
166   local new_colours_full = {}
167   for i, colour in ipairs(xpm.colours_full) do
168     if colours_used[colour.name] then
169       colour.name = colour_map[colour.name]
170       new_colours_full[#new_colours_full + 1] = colour
171     end
172   end
173   xpm.colours_full = new_colours_full
174   local new_colours = {}
175   for name, value in pairs(xpm.colours) do
176     if colours_used[name] then
177       new_colours[colour_map[name]] = value
178     end
179   end
180   xpm.colours = new_colours
181   xpm.cpp = cpp
182   xpm.ncolours = nalloc
183 until true
184
185 -- Fix transparency
186 if arg_transparent then
187   local name
188   local x, y = arg_transparent:match"[(](%d+),(%d+)[)]"
189   if x and y then
190     name = xpm.pixels[y + 1][x + 1]
191   else
192     for n, c in pairs(xpm.colours) do
193       if c == arg_transparent then
194         name = n
195         break
196       end
197     end
198   end
199   if not name then
200     error("Cannot convert " .. arg_transparent .. " to transparent as the "..
201           "colour is not present in the file")
202   end
203   xpm.colours[name] = "None"
204   for i, colour in ipairs(xpm.colours_full) do
205     if colour.name == name then
206       for i, data in ipairs(colour) do
207         if data[1] == "c" then
208           data[2] = "None"
209           break
210         end
211       end
212       break
213     end
214   end
215 end
216
217 -- Fix name
218 xpm.name = arg_name or xpm.name
219
220 -- Save
221 local file = assert(io.open(arg_out or filename, "wt"))
222 file:write"/* XPM */\n"
223 file:write("static const char *const " .. xpm.name .. "[] = {\n")
224 file:write(('"%i %i %i %i",\n'):format(xpm.width, xpm.height, xpm.ncolours,
225                                        xpm.cpp))
226 for _, colour in ipairs(xpm.colours_full) do
227   file:write('"' .. colour.name)
228   for _, data in ipairs(colour) do
229     file:write(" " .. data[1] .. " " .. data[2])
230   end
231   file:write('",\n')
232 end
233 for i, row in ipairs(xpm.pixels) do
234   file:write('"' .. table.concat(row) .. (i == xpm.height and '"\n' or '",\n'))
235 end
236 file:write("};\n")