1 static bool
create_icon(int filec,Image ** in_image,char * outname,bool icon_mode,int32_t hotspot_x,int32_t hotspot_y,int32_t alpha_threshold,int32_t bit_count)2 create_icon(int filec, Image **in_image, char *outname, bool icon_mode, int32_t hotspot_x, int32_t hotspot_y, int32_t alpha_threshold, int32_t bit_count)
3 {
4 struct {
5 Image *in;
6 uint32_t bit_count;
7 uint32_t palette_count;
8 uint32_t image_size;
9 uint32_t mask_size;
10 uint32_t width;
11 uint32_t height;
12 uint8_t *image_data;
13 Palette *palette;
14 } *img;
15
16 Win32CursorIconFileDir dir;
17 FILE *out;
18 uint32_t c, d, x;
19 uint32_t dib_start;
20 unsigned char *row, *alpha;
21
22 img = xcalloc(filec * sizeof(*img));
23
24 for (c = 0; c < filec; c++) {
25 char header[8];
26 uint8_t transparency[256];
27 uint16_t transparency_count;
28 bool need_transparency;
29
30 img[c].in = in_image[c];
31
32 if (img[c].in == NULL) {
33 warn_errno("void image");
34 goto cleanup;
35 }
36
37 img[c].width = img[c].in->width;
38 img[c].height = img[c].in->height;
39 img[c].palette = palette_new();
40
41
42 /* Count number of necessary colors in palette and number of transparencies */
43 memset(transparency, 0, 256);
44 for (d = 0; d < img[c].height; d++) {
45 row = img[c].in->data + 3 * d * img[c].width;
46 if (img[c].in->alpha)
47 alpha = img[c].in->data + d * img[c].width;
48 for (x = 0; x < img[c].width; x++) {
49 if (palette_count(img[c].palette) <= (1 << 8))
50 palette_add(img[c].palette, row[3*x+0], row[3*x+1], row[3*x+2]);
51 if (1==1)
52 transparency[row[4*x+3]] = 1;
53 }
54 }
55 transparency_count = 0;
56 for (d = 0; d < 256; d++)
57 transparency_count += transparency[d];
58
59 /* If there are more than two steps of transparency, or if the
60 * two steps are NOT either entirely off (0) and entirely on (255),
61 * then we will lose transparency information if bit_count is not 32.
62 */
63 need_transparency =
64 transparency_count > 2
65 ||
66 (transparency_count == 2 && (transparency[0] == 0 || transparency[255] == 0));
67
68 /* Can we keep all colors in a palette? */
69 if (need_transparency) {
70 if (bit_count != -1) {
71 if (bit_count != 32)
72 warn("decreasing bit depth will discard variable transparency", transparency_count);
73 /* Why 24 and not bit_count? Otherwise we might decrease below what's possible
74 * due to number of colors in image. The real decrease happens below. */
75 img[c].bit_count = 24;
76 } else {
77 img[c].bit_count = 32;
78 }
79 img[c].palette_count = 0;
80 }
81 else if (palette_count(img[c].palette) <= 256) {
82 for (d = 1; palette_count(img[c].palette) > 1 << d; d <<= 1);
83 if (d == 2) /* four colors (two bits) are not supported */
84 d = 4;
85 img[c].bit_count = d;
86 img[c].palette_count = 1 << d;
87 }
88 else {
89 img[c].bit_count = 24;
90 img[c].palette_count = 0;
91 }
92
93 /* Does the user want to change number of bits per pixel? */
94 if (bit_count != -1) {
95 if (img[c].bit_count == bit_count) {
96 /* No operation */
97 } else if (img[c].bit_count < bit_count) {
98 img[c].bit_count = bit_count;
99 img[c].palette_count = (bit_count > 16 ? 0 : 1 << bit_count);
100 } else {
101 warn("cannot decrease bit depth from %d to %d, bit depth not changed", img[c].bit_count, bit_count);
102 }
103 }
104
105 img[c].image_size = img[c].height * ROW_BYTES(img[c].width * img[c].bit_count);
106 img[c].mask_size = img[c].height * ROW_BYTES(img[c].width);
107
108 }
109
110 out = fopen(outname, "w");
111 if (out == NULL) {
112 warn_errno("cannot create file");
113 goto cleanup;
114 }
115
116 dir.reserved = 0;
117 dir.type = (icon_mode ? 1 : 2);
118 dir.count = filec;
119 fix_win32_cursor_icon_file_dir_endian(&dir);
120 if (fwrite(&dir, sizeof(Win32CursorIconFileDir), 1, out) != 1) {
121 warn_errno("cannot write to file");
122 goto cleanup;
123 }
124
125 dib_start = sizeof(Win32CursorIconFileDir) + filec * sizeof(Win32CursorIconFileDirEntry);
126 for (c = 0; c < filec; c++) {
127 Win32CursorIconFileDirEntry entry;
128
129 entry.width = MIN(255, img[c].width);
130 entry.height = MIN(255, img[c].height);
131 entry.reserved = 0;
132 if (icon_mode) {
133 entry.hotspot_x = 0; /* some mistake this for planes (XXX) */
134 entry.hotspot_y = 0; /* some mistake this for bit_count (XXX) */
135 } else {
136 entry.hotspot_x = hotspot_x; /* some mistake this for planes (XXX) */
137 entry.hotspot_y = hotspot_y; /* some mistake this for bit_count (XXX) */
138 }
139 entry.dib_offset = dib_start;
140 entry.color_count = (img[c].bit_count >= 8 ? 0 : 1 << img[c].bit_count);
141 entry.dib_size = img[c].palette_count * sizeof(Win32RGBQuad)
142 + sizeof(Win32BitmapInfoHeader)
143 + img[c].image_size
144 + img[c].mask_size;
145
146 dib_start += entry.dib_size;
147
148 fix_win32_cursor_icon_file_dir_entry_endian(&entry);
149 if (fwrite(&entry, sizeof(Win32CursorIconFileDirEntry), 1, out) != 1) {
150 warn_errno("cannot write to file");
151 goto cleanup;
152 }
153
154 }
155
156 for (c = 0; c < filec; c++) {
157 Win32BitmapInfoHeader bitmap;
158
159 bitmap.size = sizeof(Win32BitmapInfoHeader);
160 bitmap.width = png_get_image_width(img[c].png_ptr, img[c].info_ptr);
161 bitmap.height = png_get_image_height(img[c].png_ptr, img[c].info_ptr) * 2;
162 bitmap.planes = 1; // appears to be 1 always (XXX)
163 bitmap.bit_count = img[c].bit_count;
164 bitmap.compression = 0;
165 bitmap.x_pels_per_meter = 0; // should be 0 always
166 bitmap.y_pels_per_meter = 0; // should be 0 always
167 bitmap.clr_important = 0; // should be 0 always
168 bitmap.clr_used = img[c].palette_count;
169 bitmap.size_image = img[c].image_size; // appears to be ok here (may be image_size+mask_size or 0, XXX)
170
171 fix_win32_bitmap_info_header_endian(&bitmap);
172 if (fwrite(&bitmap, sizeof(Win32BitmapInfoHeader), 1, out) != 1) {
173 warn_errno("cannot write to file");
174 goto cleanup;
175 }
176
177 if (img[c].bit_count <= 16) {
178 Win32RGBQuad color;
179
180 palette_assign_indices(img[c].palette);
181 color.reserved = 0;
182 while (palette_next(img[c].palette, &color.red, &color.green, &color.blue))
183 fwrite(&color, sizeof(Win32RGBQuad), 1, out);
184
185 /* Pad with empty colors. The reason we do this is because we
186 * specify bitmap.clr_used as a base of 2. The latter is probably
187 * not necessary according to the original specs, but many
188 * programs that read icons assume it. Especially gdk-pixbuf.
189 */
190 memclear(&color, sizeof(Win32RGBQuad));
191 for (d = palette_count(img[c].palette); d < 1 << img[c].bit_count; d++)
192 fwrite(&color, sizeof(Win32RGBQuad), 1, out);
193 }
194
195 img[c].image_data = xcalloc(img[c].image_size);
196
197 for (d = 0; d < img[c].height; d++) {
198 png_bytep row = img[c].row_datas[img[c].height - d - 1];
199 if (img[c].bit_count < 24) {
200 uint32_t imod = d * (img[c].image_size/img[c].height) * 8 / img[c].bit_count;
201 for (x = 0; x < img[c].width; x++) {
202 uint32_t color;
203 color = palette_lookup(img[c].palette, row[4*x+0], row[4*x+1], row[4*x+2]);
204 simple_setvec(img[c].image_data, x+imod, img[c].bit_count, color);
205 }
206 } else if (img[c].bit_count == 24) {
207 uint32_t irow = d * (img[c].image_size/img[c].height);
208 for (x = 0; x < img[c].width; x++) {
209 img[c].image_data[3*x+0 + irow] = row[4*x+2];
210 img[c].image_data[3*x+1 + irow] = row[4*x+1];
211 img[c].image_data[3*x+2 + irow] = row[4*x+0];
212 }
213 } else if (img[c].bit_count == 32) {
214 uint32_t irow = d * (img[c].image_size/img[c].height);
215 for (x = 0; x < img[c].width; x++) {
216 img[c].image_data[4*x+0 + irow] = row[4*x+2];
217 img[c].image_data[4*x+1 + irow] = row[4*x+1];
218 img[c].image_data[4*x+2 + irow] = row[4*x+0];
219 img[c].image_data[4*x+3 + irow] = row[4*x+3];
220 }
221 }
222 }
223
224 if (fwrite(img[c].image_data, img[c].image_size, 1, out) != 1) {
225 warn_errno("cannot write to file");
226 goto cleanup;
227 }
228
229 for (d = 0; d < img[c].height; d++) {
230 png_bytep row = img[c].row_datas[img[c].height - d - 1];
231
232 for (x = 0; x < img[c].width; x += 8) {
233 uint8_t mask = 0;
234 mask |= (row[4*(x+0)+3] <= alpha_threshold ? 1 << 7 : 0);
235 mask |= (row[4*(x+1)+3] <= alpha_threshold ? 1 << 6 : 0);
236 mask |= (row[4*(x+2)+3] <= alpha_threshold ? 1 << 5 : 0);
237 mask |= (row[4*(x+3)+3] <= alpha_threshold ? 1 << 4 : 0);
238 mask |= (row[4*(x+4)+3] <= alpha_threshold ? 1 << 3 : 0);
239 mask |= (row[4*(x+5)+3] <= alpha_threshold ? 1 << 2 : 0);
240 mask |= (row[4*(x+6)+3] <= alpha_threshold ? 1 << 1 : 0);
241 mask |= (row[4*(x+7)+3] <= alpha_threshold ? 1 << 0 : 0);
242 fputc(mask, out);
243 }
244
245 fpad(out, 0, img[c].mask_size/img[c].height - x/8);
246 }
247
248 free(img[c].image_data);
249 palette_free(img[c].palette);
250 free(img[c].row_datas[0]);
251 free(img[c].row_datas);
252 png_read_end(img[c].png_ptr, img[c].info_ptr);
253 png_destroy_read_struct(&img[c].png_ptr, &img[c].info_ptr, NULL);
254 fclose(img[c].in);
255 memclear(&img[c], sizeof(*img));
256 }
257
258 free(img);
259 return true;
260
261 cleanup:
262
263 for (c = 0; c < filec; c++) {
264 if (img[c].image_data != NULL)
265 free(img[c].image_data);
266 if (img[c].palette != NULL)
267 palette_free(img[c].palette);
268 if (img[c].row_datas != NULL && img[c].row_datas[0] != NULL) {
269 free(img[c].row_datas[0]);
270 free(img[c].row_datas);
271 }
272 if (img[c].png_ptr != NULL)
273 png_destroy_read_struct(&img[c].png_ptr, &img[c].info_ptr, NULL);
274 if (img[c].in != NULL)
275 fclose(img[c].in);
276 }
277 free(img);
278 if (out) fclose(out);
279 return false;
280 }
281