diff options
author | Franklin Wei <git@fwei.tk> | 2017-04-29 18:21:56 -0400 |
---|---|---|
committer | Franklin Wei <git@fwei.tk> | 2017-04-29 18:24:42 -0400 |
commit | 881746789a489fad85aae8317555f73dbe261556 (patch) | |
tree | cec2946362c4698c8db3c10f3242ef546c2c22dd /apps/plugins/puzzles/src/icons/icon.pl | |
parent | 03dd4b92be7dcd5c8ab06da3810887060e06abd5 (diff) | |
download | rockbox-881746789a489fad85aae8317555f73dbe261556.tar.gz rockbox-881746789a489fad85aae8317555f73dbe261556.zip |
puzzles: refactor and resync with upstream
This brings puzzles up-to-date with upstream revision
2d333750272c3967cfd5cd3677572cddeaad5932, though certain changes made
by me, including cursor-only Untangle and some compilation fixes
remain. Upstream code has been moved to its separate subdirectory and
future syncs can be done by simply copying over the new sources.
Change-Id: Ia6506ca5f78c3627165ea6791d38db414ace0804
Diffstat (limited to 'apps/plugins/puzzles/src/icons/icon.pl')
-rwxr-xr-x | apps/plugins/puzzles/src/icons/icon.pl | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/apps/plugins/puzzles/src/icons/icon.pl b/apps/plugins/puzzles/src/icons/icon.pl new file mode 100755 index 0000000000..fcb1aa3e2c --- /dev/null +++ b/apps/plugins/puzzles/src/icons/icon.pl | |||
@@ -0,0 +1,270 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | # Take a collection of input image files and convert them into a | ||
4 | # multi-resolution Windows .ICO icon file. | ||
5 | # | ||
6 | # The input images can be treated as having four different colour | ||
7 | # depths: | ||
8 | # | ||
9 | # - 24-bit true colour | ||
10 | # - 8-bit with custom palette | ||
11 | # - 4-bit using the Windows 16-colour palette (see comment below | ||
12 | # for details) | ||
13 | # - 1-bit using black and white only. | ||
14 | # | ||
15 | # The images can be supplied in any input format acceptable to | ||
16 | # ImageMagick, but their actual colour usage must already be | ||
17 | # appropriate for the specified mode; this script will not do any | ||
18 | # substantive conversion. So if an image intended to be used in 4- | ||
19 | # or 1-bit mode contains any colour not in the appropriate fixed | ||
20 | # palette, that's a fatal error; if an image to be used in 8-bit | ||
21 | # mode contains more than 256 distinct colours, that's also a fatal | ||
22 | # error. | ||
23 | # | ||
24 | # Command-line syntax is: | ||
25 | # | ||
26 | # icon.pl -depth imagefile [imagefile...] [-depth imagefile [imagefile...]] | ||
27 | # | ||
28 | # where `-depth' is one of `-24', `-8', `-4' or `-1', and tells the | ||
29 | # script how to treat all the image files given after that option | ||
30 | # until the next depth option. For example, you might execute | ||
31 | # | ||
32 | # icon.pl -24 48x48x24.png 32x32x24.png -8 32x32x8.png -1 monochrome.png | ||
33 | # | ||
34 | # to build an icon file containing two differently sized 24-bit | ||
35 | # images, one 8-bit image and one black and white image. | ||
36 | # | ||
37 | # Windows .ICO files support a 1-bit alpha channel on all these | ||
38 | # image types. That is, any pixel can be either opaque or fully | ||
39 | # transparent, but not partially transparent. The alpha channel is | ||
40 | # separate from the main image data, meaning that `transparent' is | ||
41 | # not required to take up a palette entry. (So an 8-bit image can | ||
42 | # have 256 distinct _opaque_ colours, plus transparent pixels as | ||
43 | # well.) If the input images have alpha channels, they will be used | ||
44 | # to determine which pixels of the icon are transparent, by simple | ||
45 | # quantisation half way up (e.g. in a PNG image with an 8-bit alpha | ||
46 | # channel, alpha values of 00-7F will be mapped to transparent | ||
47 | # pixels, and 80-FF will become opaque). | ||
48 | |||
49 | # The Windows 16-colour palette consists of: | ||
50 | # - the eight corners of the colour cube (000000, 0000FF, 00FF00, | ||
51 | # 00FFFF, FF0000, FF00FF, FFFF00, FFFFFF) | ||
52 | # - dim versions of the seven non-black corners, at 128/255 of the | ||
53 | # brightness (000080, 008000, 008080, 800000, 800080, 808000, | ||
54 | # 808080) | ||
55 | # - light grey at 192/255 of full brightness (C0C0C0). | ||
56 | %win16pal = ( | ||
57 | "\x00\x00\x00\x00" => 0, | ||
58 | "\x00\x00\x80\x00" => 1, | ||
59 | "\x00\x80\x00\x00" => 2, | ||
60 | "\x00\x80\x80\x00" => 3, | ||
61 | "\x80\x00\x00\x00" => 4, | ||
62 | "\x80\x00\x80\x00" => 5, | ||
63 | "\x80\x80\x00\x00" => 6, | ||
64 | "\xC0\xC0\xC0\x00" => 7, | ||
65 | "\x80\x80\x80\x00" => 8, | ||
66 | "\x00\x00\xFF\x00" => 9, | ||
67 | "\x00\xFF\x00\x00" => 10, | ||
68 | "\x00\xFF\xFF\x00" => 11, | ||
69 | "\xFF\x00\x00\x00" => 12, | ||
70 | "\xFF\x00\xFF\x00" => 13, | ||
71 | "\xFF\xFF\x00\x00" => 14, | ||
72 | "\xFF\xFF\xFF\x00" => 15, | ||
73 | ); | ||
74 | @win16pal = sort { $win16pal{$a} <=> $win16pal{$b} } keys %win16pal; | ||
75 | |||
76 | # The black and white palette consists of black (000000) and white | ||
77 | # (FFFFFF), obviously. | ||
78 | %win2pal = ( | ||
79 | "\x00\x00\x00\x00" => 0, | ||
80 | "\xFF\xFF\xFF\x00" => 1, | ||
81 | ); | ||
82 | @win2pal = sort { $win16pal{$a} <=> $win2pal{$b} } keys %win2pal; | ||
83 | |||
84 | @hdr = (); | ||
85 | @dat = (); | ||
86 | |||
87 | $depth = undef; | ||
88 | foreach $_ (@ARGV) { | ||
89 | if (/^-(24|8|4|1)$/) { | ||
90 | $depth = $1; | ||
91 | } elsif (defined $depth) { | ||
92 | &readicon($_, $depth); | ||
93 | } else { | ||
94 | $usage = 1; | ||
95 | } | ||
96 | } | ||
97 | if ($usage || length @hdr == 0) { | ||
98 | print "usage: icon.pl ( -24 | -8 | -4 | -1 ) image [image...]\n"; | ||
99 | print " [ ( -24 | -8 | -4 | -1 ) image [image...] ...]\n"; | ||
100 | exit 0; | ||
101 | } | ||
102 | |||
103 | # Now write out the output icon file. | ||
104 | print pack "vvv", 0, 1, scalar @hdr; # file-level header | ||
105 | $filepos = 6 + 16 * scalar @hdr; | ||
106 | for ($i = 0; $i < scalar @hdr; $i++) { | ||
107 | print $hdr[$i]; | ||
108 | print pack "V", $filepos; | ||
109 | $filepos += length($dat[$i]); | ||
110 | } | ||
111 | for ($i = 0; $i < scalar @hdr; $i++) { | ||
112 | print $dat[$i]; | ||
113 | } | ||
114 | |||
115 | sub readicon { | ||
116 | my $filename = shift @_; | ||
117 | my $depth = shift @_; | ||
118 | my $pix; | ||
119 | my $i; | ||
120 | my %pal; | ||
121 | |||
122 | # Determine the icon's width and height. | ||
123 | my $w = `identify -format %w $filename`; | ||
124 | my $h = `identify -format %h $filename`; | ||
125 | |||
126 | # Read the file in as RGBA data. We flip vertically at this | ||
127 | # point, to avoid having to do it ourselves (.BMP and hence | ||
128 | # .ICO are bottom-up). | ||
129 | my $data = []; | ||
130 | open IDATA, "convert -set colorspace sRGB -flip -depth 8 $filename rgba:- |"; | ||
131 | push @$data, $rgb while (read IDATA,$rgb,4,0) == 4; | ||
132 | close IDATA; | ||
133 | # Check we have the right amount of data. | ||
134 | $xl = $w * $h; | ||
135 | $al = scalar @$data; | ||
136 | die "wrong amount of image data ($al, expected $xl) from $filename\n" | ||
137 | unless $al == $xl; | ||
138 | |||
139 | # Build the alpha channel now, so we can exclude transparent | ||
140 | # pixels from the palette analysis. We replace transparent | ||
141 | # pixels with undef in the data array. | ||
142 | # | ||
143 | # We quantise the alpha channel half way up, so that alpha of | ||
144 | # 0x80 or more is taken to be fully opaque and 0x7F or less is | ||
145 | # fully transparent. Nasty, but the best we can do without | ||
146 | # dithering (and don't even suggest we do that!). | ||
147 | my $x; | ||
148 | my $y; | ||
149 | my $alpha = ""; | ||
150 | |||
151 | for ($y = 0; $y < $h; $y++) { | ||
152 | my $currbyte = 0, $currbits = 0; | ||
153 | for ($x = 0; $x < (($w+31)|31)-31; $x++) { | ||
154 | $pix = ($x < $w ? $data->[$y*$w+$x] : "\x00\x00\x00\xFF"); | ||
155 | my @rgba = unpack "CCCC", $pix; | ||
156 | $currbyte <<= 1; | ||
157 | $currbits++; | ||
158 | if ($rgba[3] < 0x80) { | ||
159 | if ($x < $w) { | ||
160 | $data->[$y*$w+$x] = undef; | ||
161 | } | ||
162 | $currbyte |= 1; # MS has the alpha channel inverted :-) | ||
163 | } else { | ||
164 | # Might as well flip RGBA into BGR0 while we're here. | ||
165 | if ($x < $w) { | ||
166 | $data->[$y*$w+$x] = pack "CCCC", | ||
167 | $rgba[2], $rgba[1], $rgba[0], 0; | ||
168 | } | ||
169 | } | ||
170 | if ($currbits >= 8) { | ||
171 | $alpha .= pack "C", $currbyte; | ||
172 | $currbits -= 8; | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | |||
177 | # For an 8-bit image, check we have at most 256 distinct | ||
178 | # colours, and build the palette. | ||
179 | %pal = (); | ||
180 | if ($depth == 8) { | ||
181 | my $palindex = 0; | ||
182 | foreach $pix (@$data) { | ||
183 | next unless defined $pix; | ||
184 | $pal{$pix} = $palindex++ unless defined $pal{$pix}; | ||
185 | } | ||
186 | die "too many colours in 8-bit image $filename\n" unless $palindex <= 256; | ||
187 | } elsif ($depth == 4) { | ||
188 | %pal = %win16pal; | ||
189 | } elsif ($depth == 1) { | ||
190 | %pal = %win2pal; | ||
191 | } | ||
192 | |||
193 | my $raster = ""; | ||
194 | if ($depth < 24) { | ||
195 | # For a non-24-bit image, flatten the image into one palette | ||
196 | # index per pixel. | ||
197 | $pad = 32 / $depth; # number of pixels to pad scanline to 4-byte align | ||
198 | $pmask = $pad-1; | ||
199 | for ($y = 0; $y < $h; $y++) { | ||
200 | my $currbyte = 0, $currbits = 0; | ||
201 | for ($x = 0; $x < (($w+$pmask)|$pmask)-$pmask; $x++) { | ||
202 | $currbyte <<= $depth; | ||
203 | $currbits += $depth; | ||
204 | if ($x < $w && defined ($pix = $data->[$y*$w+$x])) { | ||
205 | if (!defined $pal{$pix}) { | ||
206 | my $pixprintable = unpack "H*", $pix; | ||
207 | die "illegal colour value $pixprintable at pixel ($x,$y) in $filename\n"; | ||
208 | } | ||
209 | $currbyte |= $pal{$pix}; | ||
210 | } | ||
211 | if ($currbits >= 8) { | ||
212 | $raster .= pack "C", $currbyte; | ||
213 | $currbits -= 8; | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | } else { | ||
218 | # For a 24-bit image, reverse the order of the R,G,B values | ||
219 | # and stick a padding zero on the end. | ||
220 | # | ||
221 | # (In this loop we don't need to bother padding the | ||
222 | # scanline out to a multiple of four bytes, because every | ||
223 | # pixel takes four whole bytes anyway.) | ||
224 | for ($i = 0; $i < scalar @$data; $i++) { | ||
225 | if (defined $data->[$i]) { | ||
226 | $raster .= $data->[$i]; | ||
227 | } else { | ||
228 | $raster .= "\x00\x00\x00\x00"; | ||
229 | } | ||
230 | } | ||
231 | $depth = 32; # and adjust this | ||
232 | } | ||
233 | |||
234 | # Prepare the icon data. First the header... | ||
235 | my $data = pack "VVVvvVVVVVV", | ||
236 | 40, # size of bitmap info header | ||
237 | $w, # icon width | ||
238 | $h*2, # icon height (x2 to indicate the subsequent alpha channel) | ||
239 | 1, # 1 plane (common to all MS image formats) | ||
240 | $depth, # bits per pixel | ||
241 | 0, # no compression | ||
242 | length $raster, # image size | ||
243 | 0, 0, 0, 0; # resolution, colours used, colours important (ignored) | ||
244 | # ... then the palette ... | ||
245 | if ($depth <= 8) { | ||
246 | my $ncols = (1 << $depth); | ||
247 | my $palette = "\x00\x00\x00\x00" x $ncols; | ||
248 | foreach $i (keys %pal) { | ||
249 | substr($palette, $pal{$i}*4, 4) = $i; | ||
250 | } | ||
251 | $data .= $palette; | ||
252 | } | ||
253 | # ... the raster data we already had ready ... | ||
254 | $data .= $raster; | ||
255 | # ... and the alpha channel we already had as well. | ||
256 | $data .= $alpha; | ||
257 | |||
258 | # Prepare the header which will represent this image in the | ||
259 | # icon file. | ||
260 | my $header = pack "CCCCvvV", | ||
261 | $w, $h, # width and height (this time the real height) | ||
262 | 1 << $depth, # number of colours, if less than 256 | ||
263 | 0, # reserved | ||
264 | 1, # planes | ||
265 | $depth, # bits per pixel | ||
266 | length $data; # size of real icon data | ||
267 | |||
268 | push @hdr, $header; | ||
269 | push @dat, $data; | ||
270 | } | ||