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