/src/ghostpdl/base/gen_ordered.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | /* Ordered Dither Screen Creation Tool. */ |
16 | | |
17 | | #include <stdlib.h> |
18 | | |
19 | | #ifdef GS_LIB_BUILD |
20 | | #define LIB_BUILD |
21 | | |
22 | | #include "std.h" |
23 | | #include "string_.h" |
24 | | #include "gsmemory.h" |
25 | | #include "math_.h" |
26 | | #include "gp.h" |
27 | | |
28 | 0 | # define ALLOC(mem, size) (gs_alloc_bytes((gs_memory_t *)mem, size, "gen_ordered")) |
29 | 0 | # define FREE(mem, ptr) gs_free_object((gs_memory_t *)mem, ptr, "gen_ordered") |
30 | | |
31 | 0 | # define PRINTF(mem, str) outprintf((gs_memory_t *)mem, str) |
32 | 0 | # define PRINTF2(mem, str, v1, v2) outprintf((gs_memory_t *)mem, str, v1, v2) |
33 | 0 | # define PRINTF4(mem, str, v1, v2, v3, v4) outprintf((gs_memory_t *)mem, str, v1, v2, v3, v4) |
34 | 0 | # define PRINTF7(mem, str, v1, v2, v3, v4, v5, v6, v7) outprintf((gs_memory_t *)mem, str, v1, v2, v3, v4, v5, v6, v7) |
35 | 0 | # define EPRINTF(mem, str) errprintf((gs_memory_t *)mem, str) |
36 | 0 | # define EPRINTF1(mem, str, v1) errprintf((gs_memory_t *)mem, str, v1) |
37 | 0 | # define EPRINTF3(mem, str, v1, v2, v3) errprintf((gs_memory_t *)mem, str, v1, v2, v3) |
38 | | # define _FILE gp_file |
39 | | # define FOPEN(mem, v1, v2) gp_fopen(mem, v1, v2) |
40 | | # define FCLOSE(fid) gp_fclose(fid) |
41 | | # define FPRINTF(fid, str) gp_fprintf(fid, str) |
42 | | # define FPRINTF1(fid, str, v1) gp_fprintf(fid, str, v1) |
43 | | # define FPRINTF2(fid, str, v1, v2) gp_fprintf(fid, str, v1, v2) |
44 | | # define FPRINTF6(fid, str, v1, v2, v3, v4, v5, v6) gp_fprintf(fid, str, v1, v2, v3, v4, v5, v6) |
45 | | # define FWRITE(v1, v2, v3, fid) gp_fwrite(v1, v2, v3, fid) |
46 | | |
47 | | |
48 | | #endif /* defined GS_LIB_BUILD */ |
49 | | |
50 | | #ifndef LIB_BUILD |
51 | | |
52 | | #include <stdio.h> |
53 | | #include <string.h> |
54 | | #include <memory.h> |
55 | | #include <sys/stat.h> |
56 | | #include <math.h> |
57 | | |
58 | | typedef unsigned char byte; |
59 | | #define false 0 |
60 | | #define true 1 |
61 | | #ifndef __cplusplus |
62 | | typedef int bool; |
63 | | #endif /* __cpluplus */ |
64 | | |
65 | | /* Needed if standalone (main) */ |
66 | | |
67 | | # define ALLOC(mem, size) (malloc(size)) |
68 | | # define FREE(mem, ptr) (free(ptr)) |
69 | | |
70 | | # define PRINTF(mem, str) printf(str) |
71 | | # define PRINTF2(mem, str, v1, v2) printf(str, v1, v2) |
72 | | # define PRINTF4(mem, str, v1, v2, v3, v4) printf(str, v1, v2, v3, v4) |
73 | | # define PRINTF7(mem, str, v1, v2, v3, v4, v5, v6, v7) printf(str, v1, v2, v3, v4, v5, v6, v7) |
74 | | # define EPRINTF(mem, str) fprintf(stderr, str) |
75 | | # define EPRINTF1(mem, str, v1) fprintf(stderr, str, v1) |
76 | | # define EPRINTF3(mem, str, v1, v2, v3) fprintf(stderr, str, v1, v2, v3) |
77 | | # define _FILE FILE |
78 | | # define FOPEN(mem, v1, v2) fopen(v1, v2) |
79 | | # define FCLOSE(fid) fclose(fid) |
80 | | # define FPRINTF(fid, str) fprintf(fid, str) |
81 | | # define FPRINTF1(fid, str, v1) fprintf(fid, str, v1) |
82 | | # define FPRINTF2(fid, str, v1, v2) fprintf(fid, str, v1, v2) |
83 | | # define FPRINTF6(fid, str, v1, v2, v3, v4, v5, v6) fprintf(fid, str, v1, v2, v3, v4, v5, v6) |
84 | | # define FWRITE(v1, v2, v3, fid) fwrite(v1, v2, v3, fid) |
85 | | |
86 | | #endif /* ndef LIB_BUILD */ |
87 | | |
88 | | #include "gen_ordered.h" |
89 | | |
90 | | typedef struct htsc_point_s { |
91 | | double x; |
92 | | double y; |
93 | | } htsc_point_t; |
94 | | |
95 | | typedef struct htsc_threshpoint { |
96 | | int x; |
97 | | int y; |
98 | | int value; |
99 | | int index; |
100 | | double dist_to_center; |
101 | | } htsc_threshpoint_t; |
102 | | |
103 | | typedef struct htsc_vertices_s { |
104 | | htsc_point_t lower_left; |
105 | | htsc_point_t upper_left; |
106 | | htsc_point_t upper_right; |
107 | | htsc_point_t lower_right; |
108 | | } htsc_vertices_t; |
109 | | |
110 | | typedef struct htsc_dot_shape_search_s { |
111 | | double norm; |
112 | | int index_x; |
113 | | int index_y; |
114 | | } htsc_dot_shape_search_t; |
115 | | |
116 | | typedef struct htsc_matrix_s { |
117 | | htsc_vector_t row[2]; |
118 | | } htsc_matrix_t; |
119 | | |
120 | | typedef struct htsc_dither_pos_s { |
121 | | htsc_point_t *point; |
122 | | int number_points; |
123 | | int *locations; |
124 | | } htsc_dither_pos_t; |
125 | | |
126 | | static void htsc_determine_cell_shape(int *x, int *y, int *v, int *u, |
127 | | int *N, htsc_param_t params, void *mem); |
128 | | static double htsc_spot_value(spottype_t spot_type, double x, double y); |
129 | | static int htsc_getpoint(htsc_dig_grid_t *dig_grid, int x, int y); |
130 | | static void htsc_setpoint(htsc_dig_grid_t *dig_grid, int x, int y, int value); |
131 | | static int htsc_create_dot_mask(htsc_dig_grid_t *dig_grid, int x, int y, int u, int v, |
132 | | double screen_angle, htsc_vertices_t vertices); |
133 | | static void htsc_find_bin_center(htsc_dig_grid_t *dot_grid, htsc_vector_t *bin_center); |
134 | | static int htsc_sumsum(htsc_dig_grid_t dig_grid); |
135 | | static void htsc_create_dot_profile(htsc_dig_grid_t *dig_grid, int N, int x, int y, |
136 | | int u, int v, double horiz_dpi, |
137 | | double vert_dpi, htsc_vertices_t vertices, |
138 | | htsc_point_t *one_index, spottype_t spot_type, |
139 | | htsc_matrix_t trans_matrix); |
140 | | static int htsc_allocate_supercell(htsc_dig_grid_t *super_cell, int x, int y, int u, |
141 | | int v, int target_size, bool use_holladay_grid, |
142 | | htsc_dig_grid_t dot_grid, int N, int *S, int *H, int *L); |
143 | | static void htsc_tile_supercell(htsc_dig_grid_t *super_cell, htsc_dig_grid_t *dot_grid, |
144 | | int x, int y, int u, int v, int N); |
145 | | int create_2d_gauss_filter(double *filter, int x_size, int y_size, |
146 | | double stdvalx, double stdvaly, gs_memory_t *mem); |
147 | | static int htsc_create_holladay_mask(htsc_dig_grid_t super_cell, int H, int L, |
148 | | double gamma, htsc_dig_grid_t *final_mask); |
149 | | static int htsc_create_dither_mask(htsc_dig_grid_t super_cell, |
150 | | htsc_dig_grid_t *final_mask, int verbose, |
151 | | int num_levels, int y, int x, double vert_dpi, |
152 | | double horiz_dpi, int N, double gamma, |
153 | | htsc_dig_grid_t dot_grid, htsc_point_t one_index, |
154 | | gs_memory_t *mem); |
155 | | static int htsc_create_nondithered_mask(htsc_dig_grid_t super_cell, int H, int L, |
156 | | double gamma, htsc_dig_grid_t *final_mask); |
157 | | static int htsc_gcd(int a, int b); |
158 | | static int htsc_lcm(int a, int b); |
159 | | static int htsc_matrix_inverse(htsc_matrix_t matrix_in, htsc_matrix_t *matrix_out); |
160 | | static void htsc_matrix_vector_mult(htsc_matrix_t matrix_in, htsc_vector_t vector_in, |
161 | | htsc_vector_t *vector_out); |
162 | | static int htsc_mask_to_tos(htsc_dig_grid_t *final_mask); |
163 | | #if RAW_SCREEN_DUMP |
164 | | static int htsc_dump_screen(htsc_dig_grid_t *dig_grid, char filename[], gs_memory_t *mem); |
165 | | static int htsc_dump_float_image(double *image, int height, int width, double max_val, |
166 | | char filename[], gs_memory_t *mem); |
167 | | static int htsc_dump_byte_image(byte *image, int height, int width, double max_val, |
168 | | char filename[], gs_memory_t *mem); |
169 | | #endif |
170 | | |
171 | | /* Initialize default values */ |
172 | | void htsc_set_default_params(htsc_param_t *params) |
173 | 0 | { |
174 | 0 | params->scr_ang = 0; |
175 | 0 | params->targ_scr_ang = 0; |
176 | 0 | params->targ_lpi = 75; |
177 | 0 | params->vert_dpi = 300; |
178 | 0 | params->horiz_dpi = 300; |
179 | 0 | params->targ_quant_spec = false; |
180 | 0 | params->targ_quant = 256; |
181 | 0 | params->targ_size = 1; |
182 | 0 | params->targ_size_spec = false; |
183 | 0 | params->spot_type = CIRCLE; |
184 | 0 | params->holladay = false; |
185 | 0 | params->gamma = 1.0; |
186 | 0 | params->output_format = OUTPUT_TOS; |
187 | 0 | params->verbose = 0; |
188 | 0 | } |
189 | | |
190 | | int |
191 | | htsc_gen_ordered(htsc_param_t params, int *S, htsc_dig_grid_t *final_mask, gs_memory_t *mem) |
192 | 0 | { |
193 | 0 | int num_levels; |
194 | 0 | int x=0, y=0, v=0, u=0, N=0; |
195 | 0 | htsc_vertices_t vertices; |
196 | 0 | htsc_vector_t bin_center; |
197 | 0 | htsc_point_t one_index = { 0., 0. }; |
198 | 0 | htsc_dig_grid_t dot_grid; |
199 | 0 | htsc_dig_grid_t super_cell; |
200 | 0 | int code; |
201 | 0 | int H, L; |
202 | 0 | htsc_matrix_t trans_matrix, trans_matrix_inv; |
203 | 0 | int min_size; |
204 | |
|
205 | 0 | dot_grid.data = NULL; |
206 | 0 | dot_grid.memory = final_mask->memory; |
207 | 0 | super_cell.data = NULL; |
208 | 0 | super_cell.memory = final_mask->memory; |
209 | 0 | final_mask->data = NULL; |
210 | 0 | params.targ_scr_ang = params.targ_scr_ang % 90; |
211 | 0 | params.scr_ang = params.targ_scr_ang; |
212 | | /* Get the vector values that define the small cell shape */ |
213 | 0 | htsc_determine_cell_shape(&x, &y, &v, &u, &N, params, final_mask->memory); |
214 | | |
215 | | /* Figure out how many levels to dither across. */ |
216 | 0 | if (params.targ_quant_spec) { |
217 | 0 | num_levels = ROUND((double) params.targ_quant / (double)N); |
218 | 0 | } else { |
219 | 0 | num_levels = 1; |
220 | 0 | } |
221 | 0 | if (num_levels < 1) num_levels = 1; |
222 | 0 | if (num_levels == 1) { |
223 | 0 | if (params.verbose > 0) |
224 | 0 | PRINTF(final_mask->memory, "No additional dithering , creating minimal sized periodic screen\n"); |
225 | 0 | params.targ_size = 1; |
226 | 0 | } |
227 | | |
228 | | /* Lower left of the cell is at the origin. Define the other vertices */ |
229 | 0 | vertices.lower_left.x = 0; |
230 | 0 | vertices.lower_left.y = 0; |
231 | 0 | vertices.upper_left.x = x; |
232 | 0 | vertices.upper_left.y = y; |
233 | 0 | vertices.upper_right.x = x + u; |
234 | 0 | vertices.upper_right.y = y + v; |
235 | 0 | vertices.lower_right.x = u; |
236 | 0 | vertices.lower_right.y = v; |
237 | | |
238 | | /* Create the matrix that is used to get us correctly into the dot shape function */ |
239 | 0 | trans_matrix.row[0].xy[0] = u; |
240 | 0 | trans_matrix.row[0].xy[1] = x; |
241 | 0 | trans_matrix.row[1].xy[0] = v; |
242 | 0 | trans_matrix.row[1].xy[1] = y; |
243 | 0 | code = htsc_matrix_inverse(trans_matrix, &trans_matrix_inv); |
244 | 0 | if (code < 0) { |
245 | 0 | EPRINTF(final_mask->memory, "ERROR! Singular Matrix Inversion!\n"); |
246 | 0 | return -1; |
247 | 0 | } |
248 | | |
249 | | /* Create a binary mask that indicates where we need to define the dot turn |
250 | | on sequence or dot profile */ |
251 | 0 | code = htsc_create_dot_mask(&dot_grid, x, y, u, v, params.scr_ang, vertices); |
252 | 0 | if (code < 0) { |
253 | 0 | return code; |
254 | 0 | } |
255 | | #if RAW_SCREEN_DUMP |
256 | | code = htsc_dump_screen(&dot_grid, "mask", mem); |
257 | | if (code < 0) |
258 | | return code; |
259 | | #endif |
260 | | /* A sanity check */ |
261 | 0 | if (htsc_sumsum(dot_grid) != -N) { |
262 | 0 | EPRINTF(final_mask->memory, "ERROR! grid size problem!\n"); |
263 | 0 | return -1; |
264 | 0 | } |
265 | | |
266 | | /* From the binary mask, find the center point. This is needed to remove |
267 | | ambiguity during the TOS calculation from the dot profile. We want to |
268 | | turn on those dots that are closests to the center first when there |
269 | | are ties */ |
270 | 0 | htsc_find_bin_center(&dot_grid, &bin_center); |
271 | | |
272 | | |
273 | | /* Now actually determine the turn on sequence */ |
274 | 0 | htsc_create_dot_profile(&dot_grid, N, x, y, u, v, params.horiz_dpi, |
275 | 0 | params.vert_dpi, vertices, &one_index, |
276 | 0 | params.spot_type, trans_matrix_inv); |
277 | |
|
278 | | #if RAW_SCREEN_DUMP |
279 | | code = htsc_dump_screen(&dot_grid, "dot_profile", mem); |
280 | | if (code < 0) |
281 | | return code; |
282 | | #endif |
283 | | /* Allocate super cell */ |
284 | 0 | code = htsc_allocate_supercell(&super_cell, x, y, u, v, params.targ_size, |
285 | 0 | params.holladay, dot_grid, N, S, &H, &L); |
286 | 0 | if (code < 0) { |
287 | 0 | EPRINTF(final_mask->memory, "ERROR! grid size problem!\n"); |
288 | 0 | return -1; |
289 | 0 | } |
290 | | |
291 | | /* Make a warning about large requested quantization levels with no -s set */ |
292 | 0 | if (params.targ_size == 1 && num_levels > 1) { |
293 | 0 | min_size = (int)ceil((double)params.targ_quant / (double)N); |
294 | 0 | EPRINTF1(final_mask->memory, "To achieve %d quantization levels with the desired lpi,\n", params.targ_quant); |
295 | 0 | EPRINTF1(final_mask->memory, "it is necessary to specify a SuperCellSize (-s) of at least %d.\n", min_size); |
296 | 0 | EPRINTF(final_mask->memory, "Note that an even larger size may be needed to reduce pattern artifacts.\n"); |
297 | 0 | EPRINTF(final_mask->memory, "Because no SuperCellSize was specified, the minimum possible size\n"); |
298 | 0 | EPRINTF(final_mask->memory, "that can approximate the requested angle and lpi will be created\n"); |
299 | 0 | EPRINTF1(final_mask->memory, "with %d quantization levels.\n", N); |
300 | 0 | } |
301 | | |
302 | | /* Go ahead and fill up the super cell grid with our growth dot values */ |
303 | 0 | htsc_tile_supercell(&super_cell, &dot_grid, x, y, u, v, N); |
304 | | #if RAW_SCREEN_DUMP |
305 | | code = htsc_dump_screen(&super_cell, "super_cell_tiled", mem); |
306 | | if (code < 0) |
307 | | return code; |
308 | | #endif |
309 | | /* If we are using the Holladay grid (non dithered) then we are done. */ |
310 | 0 | if (params.holladay) { |
311 | 0 | code = htsc_create_holladay_mask(super_cell, H, L, params.gamma, final_mask); |
312 | 0 | } else { |
313 | 0 | if ((super_cell.height == dot_grid.height && |
314 | 0 | super_cell.width == dot_grid.width) || num_levels == 1) { |
315 | 0 | code = htsc_create_nondithered_mask(super_cell, H, L, params.gamma, final_mask); |
316 | 0 | } else { |
317 | | /* Dont allow nonsense settings */ |
318 | 0 | if (num_levels * N > super_cell.height * super_cell.width) { |
319 | 0 | EPRINTF3(final_mask->memory, |
320 | 0 | "Notice, %d quantization levels not possible with super cell of %d by %d\n", |
321 | 0 | num_levels, super_cell.height, super_cell.width); |
322 | 0 | num_levels = ROUND((super_cell.height * super_cell.width) / (double)N); |
323 | 0 | EPRINTF1(final_mask->memory, "Reducing dithering quantization to %d\n", |
324 | 0 | num_levels); |
325 | 0 | EPRINTF1(final_mask->memory, "For an effective quantization of %d\n", |
326 | 0 | super_cell.height * super_cell.width); |
327 | 0 | } |
328 | 0 | code = htsc_create_dither_mask(super_cell, final_mask, params.verbose, num_levels, |
329 | 0 | y, x, params.vert_dpi, params.horiz_dpi, N, |
330 | 0 | params.gamma, dot_grid, one_index, mem); |
331 | 0 | } |
332 | 0 | } |
333 | 0 | final_mask->bin_center = bin_center; |
334 | | |
335 | | /* Now if the requested format is turn-on-sequence, convert the "data" */ |
336 | 0 | if (code == 0 && params.output_format == OUTPUT_TOS) { |
337 | 0 | code = htsc_mask_to_tos(final_mask); |
338 | 0 | } |
339 | | /* result in in final_mask, clean up working arrays allocated. */ |
340 | 0 | FREE(final_mask->memory, dot_grid.data); |
341 | 0 | FREE(final_mask->memory, super_cell.data); |
342 | 0 | return code; |
343 | 0 | } |
344 | | |
345 | | /* comparison for use in qsort */ |
346 | | static int |
347 | | compare(const void * a, const void * b) |
348 | 0 | { |
349 | 0 | const htsc_threshpoint_t *val_a = a; |
350 | 0 | const htsc_threshpoint_t *val_b = b; |
351 | 0 | double cost = val_a->value - val_b->value; |
352 | | |
353 | | /* If same value, use distance to center for decision */ |
354 | 0 | if (cost == 0) |
355 | 0 | cost = val_a->dist_to_center - val_b->dist_to_center; |
356 | 0 | if (cost == 0) |
357 | 0 | return 0; |
358 | 0 | if (cost < 0) |
359 | 0 | return -1; |
360 | 0 | return 1; |
361 | 0 | } |
362 | | |
363 | | static int |
364 | | htsc_mask_to_tos(htsc_dig_grid_t *final_mask) |
365 | 0 | { |
366 | 0 | int width = final_mask->width; |
367 | 0 | int height = final_mask->height; |
368 | 0 | htsc_vector_t center = final_mask->bin_center; |
369 | 0 | int *buff_ptr = final_mask->data; |
370 | 0 | int x, y, k = 0; |
371 | 0 | int count = height * width; |
372 | 0 | htsc_threshpoint_t *values; |
373 | 0 | int *tos; |
374 | |
|
375 | 0 | values = (htsc_threshpoint_t *) ALLOC(final_mask->memory, |
376 | 0 | sizeof(htsc_threshpoint_t) * width * height); |
377 | 0 | if (values == NULL) { |
378 | 0 | EPRINTF(final_mask->memory, "ERROR! malloc failure in htsc_mask_to_tos!\n"); |
379 | 0 | return -1; |
380 | 0 | } |
381 | 0 | tos = (int *) ALLOC(final_mask->memory, sizeof(int) * 2 * height * width); |
382 | 0 | if (tos == NULL) { |
383 | 0 | FREE(final_mask->memory, values); |
384 | 0 | EPRINTF(final_mask->memory, "ERROR! malloc failure in htsc_mask_to_tos!\n"); |
385 | 0 | return -1; |
386 | 0 | } |
387 | | /* Do a sort on the values and then output the coordinates */ |
388 | | /* First get a list made with the unsorted values and coordinates */ |
389 | 0 | for (y = 0; y < height; y++) { |
390 | 0 | for ( x = 0; x < width; x++ ) { |
391 | 0 | values[k].value = *buff_ptr; |
392 | 0 | values[k].x = x; |
393 | 0 | values[k].y = y; |
394 | 0 | values[k].index = k; |
395 | 0 | values[k].dist_to_center = (x - center.xy[0]) * (x - center.xy[0]) + |
396 | 0 | (y - center.xy[1]) * (y - center.xy[1]); |
397 | 0 | buff_ptr++; |
398 | 0 | k = k + 1; |
399 | 0 | } |
400 | 0 | } |
401 | | #if RAW_SCREEN_DUMP |
402 | | EPRINTF(final_mask->memory, "Unsorted\n"); |
403 | | for (k = 0; k < count; k++) { |
404 | | EPRINTF(final_mask->memory, "Index %d : x = %d y = %d dist = %4.2lf value = %d \n", |
405 | | values[k].index, values[k].x, values[k].y, values[k].dist_to_center, values[k].value); |
406 | | } |
407 | | #endif |
408 | | /* Sort */ |
409 | 0 | qsort(values, (size_t)height * width, sizeof(htsc_threshpoint_t), compare); |
410 | | #if RAW_SCREEN_DUMP |
411 | | EPRINTF(final_mask->memory, "Sorted\n"); |
412 | | for (k = 0; k < count; k++) { |
413 | | EPRINTF(final_mask->memory, "Index %d : x = %d y = %d dist = %4.2lf value = %d \n", |
414 | | values[k].index, values[k].x, values[k].y, values[k].dist_to_center, values[k].value); |
415 | | } |
416 | | #endif |
417 | |
|
418 | 0 | FREE(final_mask->memory, final_mask->data); |
419 | 0 | final_mask->data = tos; |
420 | 0 | buff_ptr = tos; |
421 | |
|
422 | 0 | for (k=0; k < count; k++) { |
423 | 0 | *buff_ptr++ = values[count - 1 - k].x; |
424 | 0 | *buff_ptr++ = values[count - 1 - k].y; |
425 | 0 | } |
426 | 0 | FREE(final_mask->memory, values); |
427 | 0 | return 0; |
428 | 0 | } |
429 | | |
430 | | static void |
431 | | htsc_determine_cell_shape(int *x_out, int *y_out, int *v_out, |
432 | | int *u_out, int *N_out, htsc_param_t params, void *mem) |
433 | 0 | { |
434 | 0 | double x = 0., y = 0., v = 0., u = 0., N = 0.; |
435 | 0 | double frac, scaled_x; |
436 | 0 | double ratio; |
437 | 0 | const double pi = 3.14159265358979323846f; |
438 | 0 | double true_angle, lpi; |
439 | 0 | double prev_lpi, max_lpi = 0.; |
440 | 0 | bool use = false; |
441 | 0 | double x_use = 0.,y_use = 0.; |
442 | | |
443 | | /* Go through and find the rational angle options that gets us to the |
444 | | best LPI. Pick the one that is just over what is requested. |
445 | | That is really our limiting factor here. Pick it and |
446 | | then figure out how much dithering we need to do to get to the proper |
447 | | number of levels. */ |
448 | 0 | frac = tan( params.scr_ang * pi / 180.0 ); |
449 | 0 | ratio = frac * params.horiz_dpi / params.vert_dpi; |
450 | 0 | scaled_x = params.horiz_dpi / params.vert_dpi; |
451 | | /* The minimal step is in x */ |
452 | 0 | prev_lpi = 0; |
453 | 0 | if (ratio < 1 && ratio != 0) { |
454 | 0 | if (params.verbose > 0) { |
455 | 0 | PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n"); |
456 | 0 | PRINTF(mem, "-----------------------------------------------------------\n"); |
457 | 0 | } |
458 | 0 | for (x = 1; x < 11; x++) { |
459 | 0 | x_use = x; |
460 | 0 | y=ROUND((double) x_use / ratio); |
461 | 0 | true_angle = 180.0 * atan(((double) x_use / params.horiz_dpi) / ( (double) y / params.vert_dpi) ) / pi; |
462 | 0 | lpi = 1.0/( sqrt( ((double) y / params.vert_dpi) * ( (double) y / params.vert_dpi) + |
463 | 0 | ( (double) x_use / params.horiz_dpi) * ((double) x_use / params.horiz_dpi) )); |
464 | 0 | v = -x_use / scaled_x; |
465 | 0 | u = y * scaled_x; |
466 | 0 | N = y *u - x_use * v; |
467 | 0 | if (prev_lpi == 0) { |
468 | 0 | prev_lpi = lpi; |
469 | 0 | if (params.targ_lpi > lpi) { |
470 | 0 | EPRINTF(mem, "Warning this lpi is not achievable!\n"); |
471 | 0 | EPRINTF(mem, "Resulting screen will be poorly quantized\n"); |
472 | 0 | EPRINTF(mem, "or completely stochastic!\n"); |
473 | 0 | use = true; |
474 | 0 | } |
475 | 0 | max_lpi = lpi; |
476 | 0 | } |
477 | 0 | if (prev_lpi >= params.targ_lpi && lpi < params.targ_lpi) { |
478 | 0 | if (prev_lpi == max_lpi) { |
479 | 0 | EPRINTF(mem, "Notice lpi is at the maximimum level possible.\n"); |
480 | 0 | EPRINTF(mem, "This may result in poor quantization. \n"); |
481 | 0 | } |
482 | | /* Reset these to previous x */ |
483 | 0 | x_use = x - 1; |
484 | 0 | y=ROUND((double) x_use / ratio); |
485 | 0 | true_angle = |
486 | 0 | 180.0 * atan(((double) x_use / params.horiz_dpi) / ( (double) y / params.vert_dpi) ) / pi; |
487 | 0 | lpi = |
488 | 0 | 1.0/( sqrt( ((double) y / params.vert_dpi) * ( (double) y / params.vert_dpi) + |
489 | 0 | ( (double) x_use / params.horiz_dpi) * ((double) x_use / params.horiz_dpi) )); |
490 | 0 | v = -x_use / scaled_x; |
491 | 0 | u = y * scaled_x; |
492 | 0 | N = y *u - x_use * v; |
493 | 0 | use = true; |
494 | 0 | } |
495 | 0 | if (use == true) { |
496 | 0 | if (prev_lpi == max_lpi) { |
497 | | /* Print out the final one that we will use */ |
498 | 0 | if (params.verbose > 0) |
499 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
500 | 0 | x_use,y,u,v,true_angle,lpi,N); |
501 | 0 | } |
502 | 0 | break; |
503 | 0 | } |
504 | 0 | if (params.verbose > 0) |
505 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
506 | 0 | x_use,y,u,v,true_angle,lpi,N); |
507 | 0 | prev_lpi = lpi; |
508 | 0 | } |
509 | 0 | x = x_use; |
510 | 0 | } |
511 | 0 | if (ratio >= 1) { |
512 | | /* The minimal step is in y */ |
513 | 0 | if (params.verbose > 0) { |
514 | 0 | PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n"); |
515 | 0 | PRINTF(mem, "-----------------------------------------------------------\n"); |
516 | 0 | } |
517 | 0 | for (y = 1, lpi = 99999999; lpi > params.targ_lpi; y++) { |
518 | 0 | y_use = y; |
519 | 0 | x = ROUND(y_use * ratio); |
520 | | /* compute the true angle */ |
521 | 0 | true_angle = 180.0 * atan((x / params.horiz_dpi) / (y_use / params.vert_dpi)) / pi; |
522 | 0 | lpi = 1.0 / sqrt( (y_use / params.vert_dpi) * (y_use / params.vert_dpi) + |
523 | 0 | (x / params.horiz_dpi) * (x / params.horiz_dpi)); |
524 | 0 | v = ROUND(-x / scaled_x); |
525 | 0 | u = ROUND(y_use * scaled_x); |
526 | 0 | N = y_use * u - x * v; |
527 | 0 | if (prev_lpi == 0) { |
528 | 0 | prev_lpi = lpi; |
529 | 0 | if (params.targ_lpi > lpi) { |
530 | 0 | EPRINTF(mem, "Warning this lpi is not achievable!\n"); |
531 | 0 | EPRINTF(mem, "Resulting screen will be poorly quantized\n"); |
532 | 0 | EPRINTF(mem, "or completely stochastic!\n"); |
533 | 0 | use = true; |
534 | 0 | } |
535 | 0 | max_lpi = lpi; |
536 | 0 | } |
537 | 0 | if (prev_lpi >= params.targ_lpi && lpi < params.targ_lpi) { |
538 | 0 | if (prev_lpi == max_lpi) { |
539 | 0 | EPRINTF(mem, "Warning lpi will be slightly lower than target.\n"); |
540 | 0 | EPRINTF(mem, "An increase will result in poor \n"); |
541 | 0 | EPRINTF(mem, "quantization or a completely stochastic screen!\n"); |
542 | 0 | } else { |
543 | | /* Reset these to previous x */ |
544 | 0 | y_use = y - 1; |
545 | 0 | x = ROUND(y_use * ratio); |
546 | | /* compute the true angle */ |
547 | 0 | true_angle = 180.0 * atan((x / params.horiz_dpi) / (y_use / params.vert_dpi)) / pi; |
548 | 0 | lpi = 1.0 / sqrt( (y_use / params.vert_dpi) * (y_use / params.vert_dpi) + |
549 | 0 | (x / params.horiz_dpi) * (x / params.horiz_dpi)); |
550 | 0 | v = ROUND(-x / scaled_x); |
551 | 0 | u = ROUND(y_use * scaled_x); |
552 | 0 | N = y_use * u - x * v; |
553 | 0 | } |
554 | 0 | use = true; |
555 | 0 | } |
556 | 0 | if (use == true) { |
557 | 0 | if (prev_lpi == max_lpi) { |
558 | | /* Print out the final one that we will use */ |
559 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
560 | 0 | x,y_use,u,v,true_angle,lpi,N); |
561 | 0 | } |
562 | 0 | break; |
563 | 0 | } |
564 | 0 | if (params.verbose > 0) |
565 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
566 | 0 | x,y_use,u,v,true_angle,lpi,N); |
567 | 0 | prev_lpi = lpi; |
568 | 0 | } |
569 | 0 | y = y_use; |
570 | 0 | } |
571 | 0 | if (ratio == 0) { |
572 | | /* 0 degrees */ |
573 | 0 | if (scaled_x >= 1) { |
574 | 0 | if (params.verbose > 0) { |
575 | 0 | PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n"); |
576 | 0 | PRINTF(mem, "-----------------------------------------------------------\n"); |
577 | 0 | } |
578 | 0 | for (y = 1, lpi=9999999; lpi > params.targ_lpi; y++) { |
579 | 0 | y_use = y; |
580 | 0 | x = ROUND( y_use * ratio ); |
581 | 0 | v = ROUND(-x / scaled_x); |
582 | 0 | u = ROUND(y_use * scaled_x); |
583 | 0 | N = y_use * u - x * v; |
584 | 0 | true_angle = 0; |
585 | 0 | lpi = 1.0/(double) sqrt( (double) ((y_use / params.vert_dpi) * |
586 | 0 | (y_use / params.vert_dpi) + (x / params.horiz_dpi) * (x / params.horiz_dpi)) ); |
587 | 0 | if (prev_lpi == 0) { |
588 | 0 | prev_lpi = lpi; |
589 | 0 | if (params.targ_lpi > lpi) { |
590 | 0 | EPRINTF(mem, "Warning this lpi is not achievable!\n"); |
591 | 0 | EPRINTF(mem, "Resulting screen will be poorly quantized\n"); |
592 | 0 | EPRINTF(mem, "or completely stochastic!\n"); |
593 | 0 | use = true; |
594 | 0 | } |
595 | 0 | max_lpi = lpi; |
596 | 0 | } |
597 | 0 | if (prev_lpi >= params.targ_lpi && lpi < params.targ_lpi) { |
598 | 0 | if (prev_lpi == max_lpi) { |
599 | 0 | EPRINTF(mem, "Warning lpi will be slightly lower than target.\n"); |
600 | 0 | EPRINTF(mem, "An increase will result in poor \n"); |
601 | 0 | EPRINTF(mem, "quantization or a completely stochastic screen!\n"); |
602 | 0 | } else { |
603 | | /* Reset these to previous x */ |
604 | 0 | y_use = y - 1; |
605 | 0 | x = ROUND( y_use * ratio ); |
606 | 0 | v = ROUND(-x / scaled_x); |
607 | 0 | u = ROUND(y_use * scaled_x); |
608 | 0 | N = y_use * u - x * v; |
609 | 0 | true_angle = 0; |
610 | 0 | lpi = 1.0/(double) sqrt( (double) ((y_use / params.vert_dpi) * |
611 | 0 | (y_use / params.vert_dpi) + (x / params.horiz_dpi) * (x / params.horiz_dpi)) ); |
612 | 0 | } |
613 | 0 | use = true; |
614 | 0 | } |
615 | 0 | if (use == true) { |
616 | 0 | if (prev_lpi == max_lpi) { |
617 | | /* Print out the final one that we will use */ |
618 | 0 | if (params.verbose > 0) |
619 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
620 | 0 | x,y_use,u,v,true_angle,lpi,N); |
621 | 0 | } |
622 | 0 | break; |
623 | 0 | } |
624 | 0 | if (params.verbose > 0) |
625 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
626 | 0 | x,y_use,u,v,true_angle,lpi,N); |
627 | 0 | prev_lpi = lpi; |
628 | 0 | } |
629 | 0 | y = y_use; |
630 | 0 | } else { |
631 | 0 | if (params.verbose > 0) { |
632 | 0 | PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n"); |
633 | 0 | PRINTF(mem, "-----------------------------------------------------------\n"); |
634 | 0 | } |
635 | 0 | for (x = 1; x < 11; x++) { |
636 | 0 | x_use = x; |
637 | 0 | y = ROUND(x_use * ratio); |
638 | 0 | true_angle = 0; |
639 | 0 | lpi = 1.0/( sqrt( (y / params.vert_dpi) * (y / params.vert_dpi) + |
640 | 0 | (x_use / params.horiz_dpi) * (x_use / params.horiz_dpi) )); |
641 | 0 | v = ROUND( -x_use / scaled_x); |
642 | 0 | u = ROUND( y * scaled_x); |
643 | 0 | N = y *u - x_use * v; |
644 | 0 | if (prev_lpi == 0) { |
645 | 0 | prev_lpi = lpi; |
646 | 0 | if (params.targ_lpi > lpi) { |
647 | 0 | EPRINTF(mem, "Warning this lpi is not achievable!\n"); |
648 | 0 | EPRINTF(mem, "Resulting screen will be poorly quantized\n"); |
649 | 0 | EPRINTF(mem, "or completely stochastic!\n"); |
650 | 0 | use = true; |
651 | 0 | } |
652 | 0 | max_lpi = lpi; |
653 | 0 | } |
654 | 0 | if (prev_lpi > params.targ_lpi && lpi < params.targ_lpi) { |
655 | 0 | if (prev_lpi == max_lpi) { |
656 | 0 | EPRINTF(mem, "Warning lpi will be slightly lower than target.\n"); |
657 | 0 | EPRINTF(mem, "An increase will result in poor \n"); |
658 | 0 | EPRINTF(mem, "quantization or a completely stochastic screen!\n"); |
659 | 0 | } else { |
660 | | /* Reset these to previous x */ |
661 | 0 | x_use = x - 1; |
662 | 0 | y = ROUND(x_use * ratio); |
663 | 0 | true_angle = 0; |
664 | 0 | lpi = 1.0/( sqrt( (y / params.vert_dpi) * (y / params.vert_dpi) + |
665 | 0 | (x_use / params.horiz_dpi) * (x_use / params.horiz_dpi) )); |
666 | 0 | v = ROUND( -x_use / scaled_x); |
667 | 0 | u = ROUND( y * scaled_x); |
668 | 0 | N = y *u - x_use * v; |
669 | 0 | } |
670 | 0 | use = true; |
671 | 0 | } |
672 | 0 | if (use == true) { |
673 | 0 | if (prev_lpi == max_lpi) { |
674 | | /* Print out the final one that we will use */ |
675 | 0 | if (params.verbose > 0) |
676 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
677 | 0 | x_use,y,u,v,true_angle,lpi,N); |
678 | 0 | } |
679 | 0 | break; |
680 | 0 | } |
681 | 0 | if (params.verbose > 0) |
682 | 0 | PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n", |
683 | 0 | x_use,y,u,v,true_angle,lpi,N); |
684 | 0 | prev_lpi = lpi; |
685 | 0 | } |
686 | 0 | x = x_use; |
687 | 0 | } |
688 | 0 | } |
689 | 0 | *x_out = (int)x; |
690 | 0 | *y_out = (int)y; |
691 | 0 | *v_out = (int)v; |
692 | 0 | *u_out = (int)u; |
693 | 0 | *N_out = (int)N; |
694 | 0 | } |
695 | | |
696 | | static void |
697 | | htsc_create_dot_profile(htsc_dig_grid_t *dig_grid, int N, int x, int y, int u, int v, |
698 | | double horiz_dpi, double vert_dpi, htsc_vertices_t vertices, |
699 | | htsc_point_t *one_index, spottype_t spot_type, htsc_matrix_t trans_matrix) |
700 | 0 | { |
701 | 0 | int done, dot_index, hole_index, count, index_x, index_y; |
702 | 0 | htsc_dot_shape_search_t dot_search; |
703 | 0 | int k, val_min; |
704 | 0 | double dist; |
705 | 0 | int j; |
706 | 0 | htsc_vector_t vector_in, vector_out; |
707 | 0 | double x_offset = (x % 2 == 0 ? 0.5 : 0); |
708 | 0 | double y_offset = (y % 2 == 0 ? 0.5 : 0); |
709 | |
|
710 | 0 | done = 0; |
711 | 0 | dot_index = 1; |
712 | 0 | hole_index = N; |
713 | 0 | count = 0; |
714 | 0 | val_min=MIN(0,v); |
715 | |
|
716 | 0 | while (!done) { |
717 | | |
718 | | /* First perform a search for largest dot value for those remaining |
719 | | dots */ |
720 | 0 | index_x = 0; |
721 | 0 | dot_search.index_x = 0; |
722 | 0 | dot_search.index_y = 0; |
723 | 0 | dot_search.norm = -100000000; /* Hopefully the dot func is not this small */ |
724 | 0 | for (k = 0; k < x + u; k++) { |
725 | 0 | index_y = 0; |
726 | 0 | for (j = val_min; j < y; j++) { |
727 | 0 | if ( htsc_getpoint(dig_grid, index_x, index_y) == -1 ) { |
728 | | |
729 | | /* For the spot function we want to make sure that |
730 | | we are properly adjusted to be in the range from |
731 | | -1 to +1. j and k are moving in the transformed |
732 | | (skewed/rotated) space. First untransform the value */ |
733 | 0 | vector_in.xy[0] = k + x_offset; |
734 | 0 | vector_in.xy[1] = j + y_offset; |
735 | 0 | htsc_matrix_vector_mult(trans_matrix, vector_in, |
736 | 0 | &vector_out); |
737 | | |
738 | | /* And so now we are in the range 0, 1 get us to -.5, .5 */ |
739 | 0 | vector_out.xy[0] = 2.0 * vector_out.xy[0] - 1.0; |
740 | 0 | vector_out.xy[1] = 2.0 * vector_out.xy[1] - 1.0; |
741 | 0 | dist = htsc_spot_value(spot_type, vector_out.xy[0], |
742 | 0 | vector_out.xy[1]); |
743 | 0 | if (dist > dot_search.norm) { |
744 | 0 | dot_search.norm = dist; |
745 | 0 | dot_search.index_x = index_x; |
746 | 0 | dot_search.index_y = index_y; |
747 | 0 | } |
748 | |
|
749 | 0 | } |
750 | 0 | index_y++; |
751 | 0 | } |
752 | 0 | index_x++; |
753 | 0 | } |
754 | | /* Assign the index for this position */ |
755 | 0 | htsc_setpoint(dig_grid, dot_search.index_x, dot_search.index_y, |
756 | 0 | dot_index); |
757 | 0 | dot_index++; |
758 | 0 | count++; |
759 | 0 | if (count == N) { |
760 | 0 | done = 1; |
761 | 0 | break; |
762 | 0 | } |
763 | | |
764 | | /* The ones position for the dig_grid is located at the first dot_search |
765 | | entry. We need this later so grab it now */ |
766 | 0 | if (count == 1) { |
767 | 0 | one_index->x = dot_search.index_x; |
768 | 0 | one_index->y = dot_search.index_y; |
769 | 0 | } |
770 | | |
771 | | /* Now search for the closest one to a vertex (of those remaining). |
772 | | and assign the current largest index */ |
773 | 0 | index_x = 0; |
774 | 0 | dot_search.index_x = 0; |
775 | 0 | dot_search.index_y = 0; |
776 | 0 | dot_search.norm = 10000000000; /* Or this large */ |
777 | 0 | for (k = 0; k < x + u; k++) { |
778 | 0 | index_y = 0; |
779 | 0 | for (j = val_min; j < y; j++) { |
780 | 0 | if ( htsc_getpoint(dig_grid, index_x, index_y) == -1 ) { |
781 | | |
782 | | /* For the spot function we want to make sure that |
783 | | we are properly adjusted to be in the range from |
784 | | -1 to +1. j and k are moving in the transformed |
785 | | (skewed/rotated) space. First untransform the value */ |
786 | 0 | vector_in.xy[0] = k + x_offset; |
787 | 0 | vector_in.xy[1] = j + y_offset; |
788 | 0 | htsc_matrix_vector_mult(trans_matrix, vector_in, |
789 | 0 | &vector_out); |
790 | | |
791 | | /* And so now we are in the range 0, 1 get us to -.5, .5 */ |
792 | 0 | vector_out.xy[0] = 2.0 * vector_out.xy[0] - 1.0; |
793 | 0 | vector_out.xy[1] = 2.0 * vector_out.xy[1] - 1.0; |
794 | 0 | dist = htsc_spot_value(spot_type, vector_out.xy[0], |
795 | 0 | vector_out.xy[1]); |
796 | |
|
797 | 0 | if (dist < dot_search.norm) { |
798 | 0 | dot_search.norm = dist; |
799 | 0 | dot_search.index_x = index_x; |
800 | 0 | dot_search.index_y = index_y; |
801 | 0 | } |
802 | 0 | } |
803 | 0 | index_y++; |
804 | 0 | } |
805 | 0 | index_x++; |
806 | 0 | } |
807 | | /* Assign the index for this position */ |
808 | 0 | htsc_setpoint(dig_grid, dot_search.index_x, dot_search.index_y, hole_index); |
809 | 0 | hole_index--; |
810 | 0 | count++; |
811 | 0 | if (count == N) { |
812 | 0 | done = 1; |
813 | 0 | break; |
814 | 0 | } |
815 | 0 | } |
816 | 0 | } |
817 | | |
818 | | /* This creates a mask for creating the dot shape */ |
819 | | static int |
820 | | htsc_create_dot_mask(htsc_dig_grid_t *dot_grid, int x, int y, int u, int v, |
821 | | double screen_angle, htsc_vertices_t vertices) |
822 | 0 | { |
823 | 0 | int k,j,val_min,index_x,index_y; |
824 | 0 | double slope1, slope2; |
825 | 0 | int t1,t2,t3,t4,point_in; |
826 | 0 | double b3, b4; |
827 | 0 | htsc_point_t test_point; |
828 | |
|
829 | 0 | if (screen_angle != 0) { |
830 | 0 | slope1 = (double) y / (double) x; |
831 | 0 | slope2 = (double) v / (double) u; |
832 | 0 | val_min=MIN(0,v); |
833 | 0 | dot_grid->height = abs(val_min) + y; |
834 | 0 | dot_grid->width = x + u; |
835 | 0 | dot_grid->data = |
836 | 0 | (int *) ALLOC(dot_grid->memory, (size_t)dot_grid->height * dot_grid->width * sizeof(int)); |
837 | 0 | if (dot_grid->data == NULL) |
838 | 0 | return -1; |
839 | 0 | memset(dot_grid->data,0,(size_t)dot_grid->height * dot_grid->width * sizeof(int)); |
840 | 0 | index_x = 0; |
841 | 0 | for (k = 0; k < (x+u); k++) { |
842 | 0 | index_y=0; |
843 | 0 | for (j = val_min; j < y; j++) { |
844 | 0 | test_point.x = k + 0.5; |
845 | 0 | test_point.y = j + 0.5; |
846 | | /* test 1 and 2 */ |
847 | 0 | t1 = (slope1 * test_point.x >= test_point.y); |
848 | 0 | t2 = (slope2 * test_point.x <= test_point.y); |
849 | | /* test 3 */ |
850 | 0 | b3 = vertices.upper_left.y - slope2 * vertices.upper_left.x; |
851 | 0 | t3 = ((slope2 * test_point.x + b3) > test_point.y); |
852 | | /* test 4 */ |
853 | 0 | b4 = vertices.lower_right.y - slope1 * vertices.lower_right.x; |
854 | 0 | t4=((slope1 * test_point.x + b4) < test_point.y); |
855 | 0 | point_in = (t1 && t2 && t3 && t4); |
856 | 0 | if (point_in) { |
857 | 0 | htsc_setpoint(dot_grid, index_x, index_y, -1); |
858 | 0 | } |
859 | 0 | index_y++; |
860 | 0 | } |
861 | 0 | index_x++; |
862 | 0 | } |
863 | 0 | } else { |
864 | | /* All points are valid */ |
865 | 0 | dot_grid->height = y; |
866 | 0 | dot_grid->width = u; |
867 | 0 | dot_grid->data = (int *) ALLOC(dot_grid->memory, (size_t)y * u * sizeof(int)); |
868 | 0 | if (dot_grid->data == NULL) |
869 | 0 | return -1; |
870 | 0 | memset(dot_grid->data, -1, (size_t)y * u * sizeof(int)); |
871 | 0 | val_min = 0; |
872 | 0 | } |
873 | 0 | return 0; |
874 | 0 | } |
875 | | |
876 | | static void |
877 | | htsc_find_bin_center(htsc_dig_grid_t *dot_grid, htsc_vector_t *bin_center) |
878 | 0 | { |
879 | 0 | int h = dot_grid->height; |
880 | 0 | int w = dot_grid->width; |
881 | 0 | int min_y = h + 1; |
882 | 0 | int min_x = w + 1; |
883 | 0 | int max_y = -1; |
884 | 0 | int max_x = -1; |
885 | 0 | int x, y; |
886 | |
|
887 | 0 | for (x = 0; x < w; x++) { |
888 | 0 | for (y = 0; y < h; y++) { |
889 | 0 | if (htsc_getpoint(dot_grid, x, y) == -1) |
890 | 0 | { |
891 | 0 | if (x < min_x) |
892 | 0 | min_x = x; |
893 | 0 | if (x > max_x) |
894 | 0 | max_x = x; |
895 | 0 | if (y < min_y) |
896 | 0 | min_y = y; |
897 | 0 | if (y > max_y) |
898 | 0 | max_y = y; |
899 | 0 | } |
900 | 0 | } |
901 | 0 | } |
902 | 0 | bin_center->xy[0] = (max_x - min_x) / 2.0; |
903 | 0 | bin_center->xy[1] = (max_y - min_y) / 2.0; |
904 | 0 | } |
905 | | |
906 | | static int |
907 | | htsc_getpoint(htsc_dig_grid_t *dig_grid, int x, int y) |
908 | 0 | { |
909 | 0 | return dig_grid->data[ y * dig_grid->width + x]; |
910 | 0 | } |
911 | | |
912 | | static void |
913 | | htsc_setpoint(htsc_dig_grid_t *dig_grid, int x, int y, int value) |
914 | 0 | { |
915 | 0 | int kk = 0;; |
916 | 0 | if (x < 0 || x > dig_grid->width-1 || y < 0 || y > dig_grid->height-1) { |
917 | 0 | kk++; /* Here to catch issues during debug */ |
918 | 0 | } |
919 | 0 | dig_grid->data[ y * dig_grid->width + x] = value; |
920 | 0 | } |
921 | | |
922 | | static int |
923 | | htsc_sumsum(htsc_dig_grid_t dig_grid) |
924 | 0 | { |
925 | |
|
926 | 0 | int pos; |
927 | 0 | int grid_size = dig_grid.width * dig_grid.height; |
928 | 0 | int value = 0; |
929 | 0 | int *ptr = dig_grid.data; |
930 | |
|
931 | 0 | for (pos = 0; pos < grid_size; pos++) { |
932 | 0 | value += (*ptr++); |
933 | 0 | } |
934 | 0 | return value; |
935 | 0 | } |
936 | | |
937 | | static int |
938 | | htsc_gcd(int a, int b) |
939 | 0 | { |
940 | 0 | if ( a == 0 && b == 0 ) return 0; |
941 | 0 | if ( b == 0 ) return a; |
942 | 0 | if ( a == 0 ) return b; |
943 | 0 | while (1) { |
944 | 0 | a = a % b; |
945 | 0 | if (a == 0) { |
946 | 0 | return b; |
947 | 0 | } |
948 | 0 | b = b % a; |
949 | 0 | if (b == 0) { |
950 | 0 | return a; |
951 | 0 | } |
952 | 0 | } |
953 | 0 | } |
954 | | |
955 | | static int |
956 | | htsc_lcm(int a, int b) |
957 | 0 | { |
958 | 0 | int product = a * b; |
959 | 0 | int gcd = htsc_gcd(a,b); |
960 | 0 | int lcm; |
961 | |
|
962 | 0 | if (gcd == 0) |
963 | 0 | return -1; |
964 | | |
965 | 0 | lcm = product/gcd; |
966 | 0 | return lcm; |
967 | 0 | } |
968 | | |
969 | | static int |
970 | | htsc_matrix_inverse(htsc_matrix_t matrix_in, htsc_matrix_t *matrix_out) |
971 | 0 | { |
972 | 0 | double determinant; |
973 | |
|
974 | 0 | determinant = matrix_in.row[0].xy[0] * matrix_in.row[1].xy[1] - |
975 | 0 | matrix_in.row[0].xy[1] * matrix_in.row[1].xy[0]; |
976 | 0 | if (determinant == 0) |
977 | 0 | return -1; |
978 | 0 | matrix_out->row[0].xy[0] = matrix_in.row[1].xy[1] / determinant; |
979 | 0 | matrix_out->row[0].xy[1] = -matrix_in.row[0].xy[1] / determinant; |
980 | 0 | matrix_out->row[1].xy[0] = -matrix_in.row[1].xy[0] / determinant; |
981 | 0 | matrix_out->row[1].xy[1] = matrix_in.row[0].xy[0] / determinant; |
982 | 0 | return 0; |
983 | 0 | } |
984 | | |
985 | | static void |
986 | | htsc_matrix_vector_mult(htsc_matrix_t matrix_in, htsc_vector_t vector_in, |
987 | | htsc_vector_t *vector_out) |
988 | 0 | { |
989 | 0 | vector_out->xy[0] = matrix_in.row[0].xy[0] * vector_in.xy[0] + |
990 | 0 | matrix_in.row[0].xy[1] * vector_in.xy[1]; |
991 | 0 | vector_out->xy[1] = matrix_in.row[1].xy[0] * vector_in.xy[0] + |
992 | 0 | matrix_in.row[1].xy[1] * vector_in.xy[1]; |
993 | 0 | } |
994 | | |
995 | | static int |
996 | | htsc_allocate_supercell(htsc_dig_grid_t *super_cell, int x, int y, int u, |
997 | | int v, int target_size, bool use_holladay_grid, |
998 | | htsc_dig_grid_t dot_grid, int N, int *S, int *H, int *L) |
999 | 0 | { |
1000 | 0 | htsc_matrix_t matrix; |
1001 | 0 | htsc_matrix_t matrix_inv; |
1002 | 0 | int code; |
1003 | 0 | int k, j; |
1004 | 0 | htsc_vector_t vector_in, m_and_n; |
1005 | 0 | int Dfinal; |
1006 | 0 | double diff_val[2]; |
1007 | 0 | double m_and_n_round; |
1008 | 0 | int lcm_value; |
1009 | 0 | int super_size_x, super_size_y; |
1010 | 0 | int min_vert_number; |
1011 | 0 | int a, b; |
1012 | | |
1013 | | /* Use Holladay Algorithm to create rectangular matrix for screening */ |
1014 | 0 | *H = htsc_gcd((int) abs(y), (int) abs(v)); |
1015 | 0 | if (*H == 0) |
1016 | 0 | return -1; |
1017 | | |
1018 | 0 | *L = N / *H; |
1019 | | /* Compute the shift factor */ |
1020 | 0 | matrix.row[0].xy[0] = x; |
1021 | 0 | matrix.row[0].xy[1] = u; |
1022 | 0 | matrix.row[1].xy[0] = y; |
1023 | 0 | matrix.row[1].xy[1] = v; |
1024 | |
|
1025 | 0 | code = htsc_matrix_inverse(matrix, &matrix_inv); |
1026 | 0 | if (code < 0) { |
1027 | 0 | EPRINTF(dot_grid.memory, "ERROR! matrix singular!\n"); |
1028 | 0 | return -1; |
1029 | 0 | } |
1030 | 0 | vector_in.xy[1] = *H; |
1031 | 0 | Dfinal = 0; |
1032 | 0 | for (k = 1; k < *L+1; k++) { |
1033 | 0 | vector_in.xy[0] = k; |
1034 | 0 | htsc_matrix_vector_mult(matrix_inv, vector_in, &m_and_n); |
1035 | 0 | for (j = 0; j < 2; j++) { |
1036 | 0 | m_and_n_round = ROUND(m_and_n.xy[j]); |
1037 | 0 | diff_val[j] = fabs((double) m_and_n.xy[j] - (double) m_and_n_round); |
1038 | 0 | } |
1039 | 0 | if (diff_val[0] < 0.00000001 && diff_val[1] < 0.00000001) { |
1040 | 0 | Dfinal = k; |
1041 | 0 | break; |
1042 | 0 | } |
1043 | 0 | } |
1044 | 0 | if (Dfinal == 0) { |
1045 | 0 | EPRINTF(dot_grid.memory, "ERROR! computing Holladay Grid\n"); |
1046 | 0 | return -1; |
1047 | 0 | } |
1048 | 0 | *S = *L - Dfinal; |
1049 | | /* Make a large screen of multiple cells and then vary |
1050 | | the growth rate of the dots to get additional quantization levels |
1051 | | The macrocell must be H*a by L*b where a and b are integers due to the |
1052 | | periodicity of the screen. */ |
1053 | | |
1054 | | /* To create the Holladay screen (no stocastic stuff), |
1055 | | select the size H and L to create the matrix i.e. super_size_x |
1056 | | and super_size_y need to be H by L at least and then just take an |
1057 | | H by L section. */ |
1058 | | /* Force a periodicity in the screen to avoid the shift factor */ |
1059 | 0 | if (*S != 0) { |
1060 | 0 | lcm_value = htsc_lcm(*L,*S); |
1061 | 0 | if (lcm_value < 0) |
1062 | 0 | return -1; |
1063 | | |
1064 | 0 | min_vert_number = *H * lcm_value / *S; |
1065 | 0 | } else { |
1066 | 0 | lcm_value = *L; |
1067 | 0 | min_vert_number = *H; |
1068 | 0 | } |
1069 | | |
1070 | 0 | a = (int)ceil((double) target_size / (double) lcm_value); |
1071 | 0 | b = (int)ceil((double) target_size / (double) min_vert_number); |
1072 | | |
1073 | | /* super_cell Size is b*min_vert_number by a*lcm_value |
1074 | | create the large cell */ |
1075 | |
|
1076 | 0 | if (use_holladay_grid) { |
1077 | 0 | super_size_x = MAX(*L, dot_grid.width); |
1078 | 0 | super_size_y = MAX(*H, dot_grid.height); |
1079 | 0 | } else { |
1080 | 0 | super_size_x = a * lcm_value; |
1081 | 0 | super_size_y = b * min_vert_number; |
1082 | 0 | } |
1083 | 0 | super_cell->height = super_size_y; |
1084 | 0 | super_cell->width = super_size_x; |
1085 | 0 | super_cell->data = |
1086 | 0 | (int *) ALLOC(dot_grid.memory, (size_t)super_size_x * super_size_y * sizeof(int)); |
1087 | 0 | if (super_cell->data == NULL) |
1088 | 0 | return -1; |
1089 | 0 | memset(super_cell->data, 0, (size_t)super_size_x * super_size_y * sizeof(int)); |
1090 | 0 | return 0; |
1091 | 0 | } |
1092 | | |
1093 | | static void |
1094 | | htsc_supercell_assign_point(int new_k, int new_j, int sc_xsize, int sc_ysize, |
1095 | | htsc_dig_grid_t *super_cell, int val, int *num_set ) |
1096 | 0 | { |
1097 | 0 | if (new_k >= 0 && new_j >= 0 && new_k < sc_xsize && new_j < sc_ysize) { |
1098 | 0 | if (htsc_getpoint(super_cell,new_k,new_j) == 0) { |
1099 | 0 | htsc_setpoint(super_cell,new_k,new_j,val); |
1100 | 0 | (*num_set)++; |
1101 | 0 | } |
1102 | 0 | } |
1103 | 0 | } |
1104 | | |
1105 | | static void |
1106 | | htsc_tile_supercell(htsc_dig_grid_t *super_cell, htsc_dig_grid_t *dot_grid, |
1107 | | int x, int y, int u, int v, int N) |
1108 | 0 | { |
1109 | 0 | int sc_ysize = super_cell->height; |
1110 | 0 | int sc_xsize = super_cell->width; |
1111 | 0 | int dot_ysize = dot_grid->height; |
1112 | 0 | int dot_xsize = dot_grid->width; |
1113 | 0 | int total_num = sc_ysize * sc_xsize; |
1114 | 0 | bool done = false; |
1115 | 0 | int k,j; |
1116 | 0 | int new_k, new_j; |
1117 | 0 | int num_set = 0; |
1118 | 0 | int val; |
1119 | |
|
1120 | 0 | for (k = 0; k < dot_xsize; k++) { |
1121 | 0 | for (j = 0; j < dot_ysize; j++) { |
1122 | 0 | val = htsc_getpoint(dot_grid,k,j); |
1123 | 0 | if (val > 0) { |
1124 | 0 | htsc_setpoint(super_cell,k,j,val); |
1125 | 0 | num_set++; |
1126 | 0 | } |
1127 | 0 | } |
1128 | 0 | } |
1129 | 0 | if (num_set == total_num) { |
1130 | 0 | done = true; |
1131 | 0 | } |
1132 | 0 | while (!done) { |
1133 | 0 | for (k = 0; k < sc_xsize; k++) { |
1134 | 0 | for (j = 0; j < sc_ysize; j++) { |
1135 | 0 | val = htsc_getpoint(super_cell,k,j); |
1136 | 0 | if (val != 0) { |
1137 | 0 | new_k = k - x; |
1138 | 0 | new_j = j - y; |
1139 | 0 | htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize, |
1140 | 0 | super_cell, val, &num_set); |
1141 | 0 | new_k = k + x; |
1142 | 0 | new_j = j + y; |
1143 | 0 | htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize, |
1144 | 0 | super_cell, val, &num_set); |
1145 | 0 | new_k = k - u; |
1146 | 0 | new_j = j - v; |
1147 | 0 | htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize, |
1148 | 0 | super_cell, val, &num_set); |
1149 | 0 | new_k = k + u; |
1150 | 0 | new_j = j + v; |
1151 | 0 | htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize, |
1152 | 0 | super_cell, val, &num_set); |
1153 | 0 | } |
1154 | 0 | } |
1155 | 0 | } |
1156 | 0 | if (num_set == total_num) { |
1157 | 0 | done = true; |
1158 | 0 | } |
1159 | 0 | } |
1160 | 0 | } |
1161 | | |
1162 | | /* Create 2d gaussian filter that varies with respect to coordinate |
1163 | | spatial resolution */ |
1164 | | int |
1165 | | create_2d_gauss_filter(double *filter, int x_size, int y_size, |
1166 | | double stdvalx, double stdvaly, gs_memory_t *mem) |
1167 | 0 | { |
1168 | 0 | int x_half_size = (x_size-1)/2; |
1169 | 0 | int y_half_size = (y_size-1)/2; |
1170 | 0 | int k,j; |
1171 | 0 | double arg, val; |
1172 | 0 | double sum = 0; |
1173 | 0 | double max_val = 0; |
1174 | 0 | int total_size = x_size * y_size; |
1175 | 0 | int index_x, index_y; |
1176 | 0 | int code = 0; |
1177 | |
|
1178 | 0 | for (j = -y_half_size; j < y_half_size+1; j++) { |
1179 | 0 | index_y = j + y_half_size; |
1180 | 0 | for (k = -x_half_size; k < x_half_size+1; k++) { |
1181 | 0 | arg = -(k * k / (stdvalx * stdvalx) + |
1182 | 0 | j * j / (stdvaly * stdvaly) ) /2; |
1183 | 0 | val = exp(arg); |
1184 | 0 | sum += val; |
1185 | 0 | if (val > max_val) max_val = val; |
1186 | 0 | index_x = k + x_half_size; |
1187 | 0 | filter[index_y * x_size + index_x] = val; |
1188 | 0 | } |
1189 | 0 | } |
1190 | 0 | for (j = 0; j < total_size; j++) { |
1191 | 0 | filter[j]/=sum; |
1192 | 0 | } |
1193 | | #if RAW_SCREEN_DUMP |
1194 | | code = htsc_dump_float_image(filter, y_size, x_size, max_val/sum, "guass_filt", mem); |
1195 | | #endif |
1196 | 0 | return code; |
1197 | 0 | } |
1198 | | |
1199 | | /* 2-D convolution (or correlation) with periodic boundary condition */ |
1200 | | static void |
1201 | | htsc_apply_filter(byte *screen_matrix, int num_cols_sc, |
1202 | | int num_rows_sc, double *filter, int num_cols_filt, |
1203 | | int num_rows_filt, double *screen_blur, |
1204 | | double *max_val, htsc_point_t *max_pos, double *min_val, |
1205 | | htsc_point_t *min_pos) |
1206 | 0 | { |
1207 | 0 | int k,j,kk,jj; |
1208 | 0 | double sum; |
1209 | 0 | int half_cols_filt = (num_cols_filt-1)/2; |
1210 | 0 | int half_rows_filt = (num_rows_filt-1)/2; |
1211 | 0 | int j_circ,k_circ; |
1212 | 0 | double fmax_val = -1; |
1213 | 0 | double fmin_val = 100000000; |
1214 | 0 | htsc_point_t fmax_pos = { 0.0, 0.0 }, fmin_pos = { 0.0, 0.0 }; |
1215 | |
|
1216 | 0 | for (j = 0; j < num_rows_sc; j++) { |
1217 | 0 | for (k = 0; k < num_cols_sc; k++) { |
1218 | 0 | sum = 0.0; |
1219 | 0 | for (jj = -half_rows_filt; jj <= half_rows_filt; jj++) { |
1220 | 0 | j_circ = j + jj; |
1221 | 0 | if (j_circ < 0) { |
1222 | 0 | j_circ = |
1223 | 0 | (num_rows_sc - ((-j_circ) % num_rows_sc)) % num_rows_sc; |
1224 | 0 | } |
1225 | 0 | if (j_circ > (num_rows_sc - 1)) { |
1226 | 0 | j_circ = j_circ % num_rows_sc; |
1227 | 0 | } |
1228 | | /* In case modulo is of a negative number */ |
1229 | 0 | if (j_circ < 0) |
1230 | 0 | j_circ = j_circ + num_rows_sc; |
1231 | 0 | for (kk = -half_cols_filt; kk <= half_cols_filt; kk++) { |
1232 | 0 | k_circ = k + kk; |
1233 | 0 | if (k_circ < 0) { |
1234 | 0 | k_circ = |
1235 | 0 | (num_cols_sc - ((-k_circ) % num_cols_sc)) % num_cols_sc; |
1236 | 0 | } |
1237 | 0 | if (k_circ > (num_cols_sc - 1)) { |
1238 | 0 | k_circ = k_circ % num_cols_sc; |
1239 | 0 | } |
1240 | | /* In case modulo is of a negative number */ |
1241 | 0 | if (k_circ < 0) |
1242 | 0 | k_circ = k_circ + num_cols_sc; |
1243 | 0 | sum += (double) screen_matrix[k_circ + j_circ * num_cols_sc] * |
1244 | 0 | filter[ (jj + half_rows_filt) * num_cols_filt + (kk + half_cols_filt)]; |
1245 | 0 | } |
1246 | 0 | } |
1247 | 0 | screen_blur[j * num_cols_sc + k] = sum; |
1248 | 0 | if (sum > fmax_val) { |
1249 | 0 | fmax_val = sum; |
1250 | 0 | fmax_pos.x = k; |
1251 | 0 | fmax_pos.y = j; |
1252 | 0 | } |
1253 | 0 | if (sum < fmin_val) { |
1254 | 0 | fmin_val = sum; |
1255 | 0 | fmin_pos.x = k; |
1256 | 0 | fmin_pos.y = j; |
1257 | 0 | } |
1258 | 0 | } |
1259 | 0 | } |
1260 | 0 | *max_val = fmax_val; |
1261 | 0 | *min_val = fmin_val; |
1262 | 0 | *max_pos = fmax_pos; |
1263 | 0 | *min_pos = fmin_pos; |
1264 | 0 | } |
1265 | | |
1266 | | static int |
1267 | | htsc_add_dots(byte *screen_matrix, int num_cols, int num_rows, |
1268 | | double horiz_dpi, double vert_dpi, double lpi_act, |
1269 | | unsigned short *pos_x, unsigned short *pos_y, |
1270 | | int *locate, int num_dots, |
1271 | | htsc_dither_pos_t *dot_level_position, int level_num, |
1272 | | int num_dots_add, void *mem) |
1273 | 0 | { |
1274 | 0 | double xscale = horiz_dpi / vert_dpi; |
1275 | 0 | double sigma_y = vert_dpi / lpi_act; |
1276 | 0 | double sigma_x = sigma_y * xscale; |
1277 | 0 | int sizefiltx, sizefilty; |
1278 | 0 | double *filter; |
1279 | 0 | double *screen_blur; |
1280 | 0 | int white_pos; |
1281 | 0 | double max_val, min_val; |
1282 | 0 | htsc_point_t max_pos, min_pos; |
1283 | 0 | int k,j; |
1284 | 0 | int dist, curr_dist; |
1285 | 0 | htsc_dither_pos_t curr_position; |
1286 | 0 | int code; |
1287 | |
|
1288 | 0 | sizefiltx = ROUND(sigma_x * 4); |
1289 | 0 | sizefilty = ROUND(sigma_y * 4); |
1290 | 0 | if ( ((double) sizefiltx / 2.0) == (sizefiltx >> 1)) { |
1291 | 0 | sizefiltx += 1; |
1292 | 0 | } |
1293 | 0 | if ( ((double) sizefilty / 2.0) == (sizefilty >> 1)) { |
1294 | 0 | sizefilty += 1; |
1295 | 0 | } |
1296 | 0 | filter = (double*) ALLOC(mem, sizeof(double) * sizefilty * sizefiltx); |
1297 | 0 | if (filter == NULL) |
1298 | 0 | return -1; |
1299 | 0 | code = create_2d_gauss_filter(filter, sizefiltx, sizefilty, (double)sizefiltx, (double)sizefilty, mem); |
1300 | 0 | if (code < 0) |
1301 | 0 | return code; |
1302 | | |
1303 | 0 | screen_blur = (double*)ALLOC(mem, sizeof(double) * num_cols * num_rows); |
1304 | 0 | if (screen_blur == NULL) { |
1305 | 0 | FREE(mem, filter); |
1306 | 0 | return -1; |
1307 | 0 | } |
1308 | | |
1309 | 0 | for (j = 0; j < num_dots_add; j++) { |
1310 | | |
1311 | | /* Perform the blur */ |
1312 | 0 | htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx, |
1313 | 0 | sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos); |
1314 | | |
1315 | | /* Find the closest OFF dot to the min position. */ |
1316 | 0 | white_pos = 0; |
1317 | 0 | dist = (num_cols) * (num_cols) + (num_rows) * (num_rows); |
1318 | 0 | for (k = 0; k < num_dots; k++) { |
1319 | 0 | curr_dist = (pos_y[k] - (int)min_pos.y) * (pos_y[k] - (int)min_pos.y) + |
1320 | 0 | (pos_x[k] - (int)min_pos.x) * (pos_x[k] - (int)min_pos.x); |
1321 | 0 | if (curr_dist < dist && |
1322 | 0 | screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 0) { |
1323 | 0 | white_pos = k; |
1324 | 0 | dist = curr_dist; |
1325 | 0 | } |
1326 | 0 | } |
1327 | | |
1328 | | /* Set this dot to white */ |
1329 | 0 | screen_matrix[pos_x[white_pos] + num_cols * pos_y[white_pos]] = 1; |
1330 | | |
1331 | | /* Update position information */ |
1332 | 0 | curr_position = dot_level_position[level_num]; |
1333 | 0 | curr_position.point[j].x = pos_x[white_pos]; |
1334 | 0 | curr_position.point[j].y = pos_y[white_pos]; |
1335 | 0 | curr_position.locations[j] = locate[white_pos]; |
1336 | 0 | } |
1337 | 0 | FREE(mem, filter); |
1338 | 0 | FREE(mem, screen_blur); |
1339 | 0 | return 0; |
1340 | 0 | } |
1341 | | |
1342 | | static int |
1343 | | htsc_init_dot_position(byte *screen_matrix, int num_cols, int num_rows, |
1344 | | double horiz_dpi, double vert_dpi, double lpi_act, |
1345 | | unsigned short *pos_x, unsigned short *pos_y, int num_dots, |
1346 | | htsc_dither_pos_t *dot_level_position, void *mem) |
1347 | 0 | { |
1348 | 0 | double xscale = horiz_dpi / vert_dpi; |
1349 | 0 | double sigma_y = vert_dpi / lpi_act; |
1350 | 0 | double sigma_x = sigma_y * xscale; |
1351 | 0 | int sizefiltx, sizefilty; |
1352 | 0 | double *filter; |
1353 | 0 | bool done = false; |
1354 | 0 | double *screen_blur; |
1355 | 0 | int white_pos, black_pos; |
1356 | 0 | double max_val, min_val; |
1357 | 0 | htsc_point_t max_pos, min_pos; |
1358 | 0 | int k; |
1359 | 0 | int dist, curr_dist; |
1360 | 0 | bool found_it; |
1361 | 0 | int code = 0; |
1362 | |
|
1363 | 0 | sizefiltx = ROUND(sigma_x * 4); |
1364 | 0 | sizefilty = ROUND(sigma_y * 4); |
1365 | 0 | if ( ((double) sizefiltx / 2.0) == (sizefiltx >> 1)) { |
1366 | 0 | sizefiltx += 1; |
1367 | 0 | } |
1368 | 0 | if ( ((double) sizefilty / 2.0) == (sizefilty >> 1)) { |
1369 | 0 | sizefilty += 1; |
1370 | 0 | } |
1371 | 0 | filter = (double*) ALLOC(mem, sizeof(double) * sizefilty * sizefiltx); |
1372 | 0 | if (filter == NULL) |
1373 | 0 | return -1; |
1374 | 0 | code = create_2d_gauss_filter(filter, sizefiltx, sizefilty, (double)sizefiltx, (double)sizefilty, mem); |
1375 | 0 | if (code < 0) |
1376 | 0 | return code; |
1377 | | |
1378 | 0 | screen_blur = (double*) ALLOC(mem, sizeof(double) * num_cols * num_rows); |
1379 | 0 | if (screen_blur == NULL) { |
1380 | 0 | FREE(mem, filter); |
1381 | 0 | return -1; |
1382 | 0 | } |
1383 | | /* Start moving dots until the whitest and darkest dot are the same */ |
1384 | 0 | while (!done) { |
1385 | | /* Blur */ |
1386 | 0 | htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx, |
1387 | 0 | sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos); |
1388 | | #if RAW_SCREEN_DUMP |
1389 | | code = htsc_dump_float_image(screen_blur, num_cols, num_rows, max_val, "blur_one", mem); |
1390 | | if (code < 0) |
1391 | | return code; |
1392 | | #endif |
1393 | | /* Find the closest on dot to the max position */ |
1394 | 0 | black_pos = 0; |
1395 | 0 | dist = (pos_y[0] - (int)max_pos.y) * (pos_y[0] - (int)max_pos.y) + |
1396 | 0 | (pos_x[0] - (int)max_pos.x) * (pos_x[0] - (int)max_pos.x); |
1397 | 0 | for ( k = 1; k < num_dots; k++) { |
1398 | 0 | curr_dist = (pos_y[k] - (int)max_pos.y) * (pos_y[k] - (int)max_pos.y) + |
1399 | 0 | (pos_x[k] - (int)max_pos.x) * (pos_x[k] - (int)max_pos.x); |
1400 | 0 | if (curr_dist < dist && |
1401 | 0 | screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 1) { |
1402 | 0 | black_pos = k; |
1403 | 0 | dist = curr_dist; |
1404 | 0 | } |
1405 | 0 | } |
1406 | | /* Set this dot to black */ |
1407 | 0 | screen_matrix[pos_x[black_pos] + num_cols * pos_y[black_pos]] = 0; |
1408 | | /* Blur again */ |
1409 | 0 | htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx, |
1410 | 0 | sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos); |
1411 | | /* Find the closest OFF dot to the min position. */ |
1412 | 0 | white_pos = 0; |
1413 | 0 | dist = (pos_y[0] - (int)min_pos.y) * (pos_y[0] - (int)min_pos.y) + |
1414 | 0 | (pos_x[0] - (int)min_pos.x) * (pos_x[0] - (int)min_pos.x); |
1415 | 0 | for ( k = 1; k < num_dots; k++) { |
1416 | 0 | curr_dist = (pos_y[k] - (int)min_pos.y) * (pos_y[k] - (int)min_pos.y) + |
1417 | 0 | (pos_x[k] - (int)min_pos.x) * (pos_x[k] - (int)min_pos.x); |
1418 | 0 | if (curr_dist < dist && |
1419 | 0 | screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 0) { |
1420 | 0 | white_pos = k; |
1421 | 0 | dist = curr_dist; |
1422 | 0 | } |
1423 | 0 | } |
1424 | | /* Set this dot to white */ |
1425 | 0 | screen_matrix[pos_x[white_pos] + num_cols * pos_y[white_pos]] = 1; |
1426 | | /* If it is the same dot as before, then we are done */ |
1427 | | /* There could be a danger here of cycles longer than 2 */ |
1428 | 0 | if (white_pos == black_pos) { |
1429 | 0 | done = true; |
1430 | 0 | FREE(mem, screen_blur); |
1431 | 0 | FREE(mem, filter); |
1432 | 0 | return 0; |
1433 | 0 | } else { |
1434 | | /* We need to update our dot position information */ |
1435 | | /* find where the old white one was and replace it */ |
1436 | 0 | found_it = false; |
1437 | 0 | for (k = 0; k < dot_level_position->number_points; k++) { |
1438 | 0 | if (dot_level_position->point[k].x == pos_x[black_pos] && |
1439 | 0 | dot_level_position->point[k].y == pos_y[black_pos]) { |
1440 | 0 | found_it = true; |
1441 | 0 | dot_level_position->point[k].x = pos_x[white_pos]; |
1442 | 0 | dot_level_position->point[k].y = pos_y[white_pos]; |
1443 | 0 | dot_level_position->locations[k] = |
1444 | 0 | pos_x[white_pos] + pos_y[white_pos] * num_cols; |
1445 | 0 | break; |
1446 | 0 | } |
1447 | 0 | } |
1448 | 0 | if (!found_it) { |
1449 | 0 | EPRINTF(mem, "ERROR! bug in dot location accounting\n"); |
1450 | 0 | FREE(mem, filter); |
1451 | 0 | FREE(mem, screen_blur); |
1452 | 0 | return -1; |
1453 | 0 | } |
1454 | 0 | } |
1455 | 0 | } |
1456 | 0 | FREE(mem, filter); |
1457 | 0 | FREE(mem, screen_blur); |
1458 | 0 | return 0; |
1459 | 0 | } |
1460 | | |
1461 | | static int |
1462 | | htsc_create_dither_mask(htsc_dig_grid_t super_cell, htsc_dig_grid_t *final_mask, |
1463 | | int verbose, int num_levels, int y, int x, double vert_dpi, |
1464 | | double horiz_dpi, int N, double gamma, |
1465 | | htsc_dig_grid_t dot_grid, htsc_point_t dot_grid_one_index, |
1466 | | gs_memory_t *mem) |
1467 | 0 | { |
1468 | 0 | int *dot_levels = NULL; |
1469 | 0 | int *locate = NULL; |
1470 | 0 | double percent, perc_val; |
1471 | 0 | int code = 0, k, j, h, jj, mm; |
1472 | 0 | int width_supercell = super_cell.width; |
1473 | 0 | int height_supercell = super_cell.height; |
1474 | 0 | int number_points = width_supercell * height_supercell; |
1475 | 0 | int num_dots = 0; |
1476 | 0 | double step_size; |
1477 | 0 | int curr_size; |
1478 | 0 | int rand_pos, dots_needed; |
1479 | 0 | int done; |
1480 | 0 | double lpi_act; |
1481 | 0 | double vert_scale = ((double) y / vert_dpi); |
1482 | 0 | double horiz_scale = ((double) x / horiz_dpi); |
1483 | 0 | byte *screen_matrix = NULL; |
1484 | 0 | unsigned short *pos_x = NULL, *pos_y = NULL; |
1485 | 0 | htsc_dither_pos_t *dot_level_pos = NULL, *curr_dot_level = NULL; |
1486 | 0 | int count; |
1487 | 0 | int prev_dot_level, curr_num_dots; |
1488 | 0 | double mag_offset, temp_dbl; |
1489 | 0 | int *thresholds = NULL, val; |
1490 | 0 | int j_index, k_index, threshold_value; |
1491 | 0 | int *dot_level_sort = NULL; |
1492 | 0 | bool found; |
1493 | |
|
1494 | 0 | lpi_act = 1.0/((double) sqrt( vert_scale * vert_scale + |
1495 | 0 | horiz_scale * horiz_scale)); |
1496 | 0 | if (num_levels > 1) { |
1497 | 0 | curr_size = 2 * MAX(height_supercell, width_supercell); |
1498 | 0 | locate = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_size); |
1499 | 0 | if (locate == NULL) { |
1500 | 0 | code = -1; |
1501 | 0 | goto out; |
1502 | 0 | } |
1503 | 0 | screen_matrix = (byte*) ALLOC(dot_grid.memory, sizeof(byte) * number_points); |
1504 | 0 | if (screen_matrix == NULL) { |
1505 | 0 | code = -1; |
1506 | 0 | goto out; |
1507 | 0 | } |
1508 | 0 | memset(screen_matrix, 0, sizeof(byte) * number_points); |
1509 | | |
1510 | | /* Determine the number of dots in the screen and their index */ |
1511 | 0 | for (j = 0; j < number_points; j++) { |
1512 | 0 | if (super_cell.data[j] == 1) { |
1513 | 0 | locate[num_dots] = j; |
1514 | 0 | num_dots++; |
1515 | 0 | if (num_dots == (curr_size - 1)) { |
1516 | 0 | int *tmp = locate; |
1517 | |
|
1518 | 0 | curr_size = curr_size * 2; |
1519 | 0 | locate = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_size); |
1520 | 0 | if (locate == NULL) { |
1521 | 0 | code = -1; |
1522 | 0 | goto out; |
1523 | 0 | } |
1524 | 0 | memcpy(locate, tmp, sizeof(int) * (num_dots+1)); |
1525 | 0 | FREE(dot_grid.memory, tmp); |
1526 | 0 | } |
1527 | 0 | } |
1528 | 0 | } |
1529 | | |
1530 | | /* Convert the 1-D locate positions to 2-D positions so that we can |
1531 | | use a distance metric to the dot center locations. Also allocate |
1532 | | the structure for our dot positioning information */ |
1533 | 0 | pos_x = (unsigned short*) ALLOC(dot_grid.memory, sizeof(unsigned short) * num_dots); |
1534 | 0 | if (pos_x == NULL) { |
1535 | 0 | code = -1; |
1536 | 0 | goto out; |
1537 | 0 | } |
1538 | 0 | pos_y = (unsigned short*) ALLOC(dot_grid.memory, sizeof(unsigned short) * num_dots); |
1539 | 0 | if (pos_y == NULL) { |
1540 | 0 | code = -1; |
1541 | 0 | goto out; |
1542 | 0 | } |
1543 | 0 | for (k = 0; k < num_dots; k++) { |
1544 | 0 | pos_x[k] = locate[k] % width_supercell; |
1545 | 0 | pos_y[k] = (locate[k] - pos_x[k]) / width_supercell; |
1546 | 0 | } |
1547 | | |
1548 | | /* Note that number of quantization levels is not tied to number of dots |
1549 | | in the macro screen. */ |
1550 | 0 | dot_level_pos = |
1551 | 0 | (htsc_dither_pos_t*) ALLOC(dot_grid.memory, sizeof(htsc_dither_pos_t) * num_levels); |
1552 | 0 | if (dot_level_pos == NULL) { |
1553 | 0 | code = -1; |
1554 | 0 | goto out; |
1555 | 0 | } |
1556 | 0 | dot_levels = (int*) ALLOC(dot_grid.memory, sizeof(int) * num_levels); |
1557 | 0 | if (dot_levels == NULL) { |
1558 | 0 | code = -1; |
1559 | 0 | goto out; |
1560 | 0 | } |
1561 | 0 | percent = 1.0 / ((double)num_levels + 1.0); |
1562 | 0 | prev_dot_level = 0; |
1563 | 0 | for (k = 0; k < num_levels; k++) { |
1564 | 0 | perc_val = (k + 1) * percent; |
1565 | 0 | dot_levels[k] = ROUND(num_dots * perc_val); |
1566 | 0 | curr_num_dots = dot_levels[k] -prev_dot_level; |
1567 | 0 | prev_dot_level = dot_levels[k]; |
1568 | 0 | dot_level_pos[k].locations = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_num_dots); |
1569 | 0 | if (dot_level_pos[k].locations == NULL) { |
1570 | 0 | code = -1; |
1571 | 0 | goto out; |
1572 | 0 | } |
1573 | 0 | dot_level_pos[k].point = |
1574 | 0 | (htsc_point_t*) ALLOC(dot_grid.memory, sizeof(htsc_point_t) * curr_num_dots); |
1575 | 0 | if (dot_level_pos[k].point == NULL) { |
1576 | 0 | code = -1; |
1577 | 0 | goto out; |
1578 | 0 | } |
1579 | 0 | dot_level_pos[k].number_points = curr_num_dots; |
1580 | 0 | } |
1581 | | |
1582 | | /* An initial random location for the first level */ |
1583 | 0 | dots_needed = dot_levels[0]; |
1584 | 0 | count = 0; |
1585 | 0 | if (dots_needed > 0) { |
1586 | 0 | done = 0; |
1587 | 0 | while (!done) { |
1588 | 0 | rand_pos = ROUND((num_dots-1) * (double) rand() / (double) RAND_MAX); |
1589 | 0 | if (screen_matrix[locate[rand_pos]] != 1) { |
1590 | 0 | screen_matrix[locate[rand_pos]] = 1; |
1591 | 0 | dot_level_pos->locations[count] = locate[rand_pos]; |
1592 | 0 | dot_level_pos->point[count].x = pos_x[rand_pos]; |
1593 | 0 | dot_level_pos->point[count].y = pos_y[rand_pos]; |
1594 | 0 | dots_needed--; |
1595 | 0 | count++; |
1596 | 0 | } |
1597 | 0 | if (dots_needed == 0) { |
1598 | 0 | done = 1; |
1599 | 0 | } |
1600 | 0 | } |
1601 | 0 | } |
1602 | | #if RAW_SCREEN_DUMP |
1603 | | code = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell, |
1604 | | 1, "screen0_init", mem); |
1605 | | if (code < 0) |
1606 | | goto out; |
1607 | | #endif |
1608 | | /* Rearrange these dots into a pleasing pattern, but only if there is |
1609 | | * more than 1. Otherwise there are none to move */ |
1610 | 0 | if (dot_levels[0] > 1) |
1611 | 0 | code = htsc_init_dot_position(screen_matrix, width_supercell, |
1612 | 0 | height_supercell, horiz_dpi, vert_dpi, lpi_act, |
1613 | 0 | pos_x, pos_y, num_dots, dot_level_pos, final_mask->memory); |
1614 | 0 | if (code < 0) |
1615 | 0 | goto out; |
1616 | | #if RAW_SCREEN_DUMP |
1617 | | code = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell, |
1618 | | 1, "screen0_arrange", mem); |
1619 | | if (code < 0) |
1620 | | goto out; |
1621 | | #endif |
1622 | | /* Now we want to introduce more dots at each level */ |
1623 | 0 | for (k = 1; k < num_levels; k++) { |
1624 | 0 | code = htsc_add_dots(screen_matrix, width_supercell, height_supercell, |
1625 | 0 | horiz_dpi, vert_dpi, lpi_act, pos_x, pos_y, locate, |
1626 | 0 | num_dots, dot_level_pos, k, |
1627 | 0 | dot_level_pos[k].number_points, final_mask->memory); |
1628 | 0 | if (code < 0) |
1629 | 0 | goto out; |
1630 | | #if RAW_SCREEN_DUMP |
1631 | | { |
1632 | | char str_name[30]; |
1633 | | snprintf(str_name, 30, "screen%d_arrange",k); |
1634 | | code = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell, |
1635 | | 1, str_name, mem); |
1636 | | if (code < 0) |
1637 | | goto out; |
1638 | | } |
1639 | | #endif |
1640 | 0 | } |
1641 | | |
1642 | 0 | if (verbose > 0) |
1643 | 0 | PRINTF(final_mask->memory, "\n--Dot Positions--\n"); |
1644 | 0 | for (k = 0; k < num_levels; k++) { |
1645 | 0 | if (verbose > 0) |
1646 | 0 | PRINTF2(final_mask->memory, "dot_level_pos %d: number_points = %d\n", |
1647 | 0 | k, dot_level_pos[k].number_points); |
1648 | 0 | for (j = 0; j < dot_level_pos[k].number_points; j++) { |
1649 | 0 | if (verbose > 0) |
1650 | 0 | PRINTF4(final_mask->memory, "\tpoint: %d: locations = %d x = %3.2lf y = %3.2lf\n", |
1651 | 0 | j, dot_level_pos[k].locations[j], dot_level_pos[k].point[j].x, dot_level_pos[k].point[j].y); |
1652 | 0 | } |
1653 | 0 | } |
1654 | | |
1655 | | /* Create the threshold mask. */ |
1656 | 0 | step_size = (MAXVAL + 1.0) / (double) N; |
1657 | 0 | thresholds = (int*) ALLOC(dot_grid.memory, sizeof(int) * N); |
1658 | 0 | if (thresholds == NULL) { |
1659 | 0 | code = -1; |
1660 | 0 | goto out; |
1661 | 0 | } |
1662 | 0 | for (k = 0; k < N; k++) { |
1663 | 0 | thresholds[N-1-k] = (int)((k + 1) * step_size - (step_size / 2)); |
1664 | 0 | } |
1665 | 0 | mag_offset = |
1666 | 0 | (double) (thresholds[0]-thresholds[1]) / (double) (num_levels+1); |
1667 | 0 | if ( gamma != 1.0) { |
1668 | 0 | for (k = 0; k < N; k++) { |
1669 | 0 | temp_dbl = |
1670 | 0 | (double) pow((double) thresholds[k] / MAXVAL, |
1671 | 0 | (double) gamma); |
1672 | 0 | thresholds[k] = ROUND(temp_dbl * MAXVAL); |
1673 | 0 | } |
1674 | 0 | } |
1675 | | |
1676 | | /* Now use the indices from the large screen to look up the mask and |
1677 | | apply the offset to the threshold values to dither the rate at which |
1678 | | the dots turn on */ |
1679 | | /* allocate the mask */ |
1680 | 0 | final_mask->height = height_supercell; |
1681 | 0 | final_mask->width = width_supercell; |
1682 | 0 | final_mask->data = |
1683 | 0 | (int*) ALLOC(dot_grid.memory, sizeof(int) * height_supercell * width_supercell); |
1684 | 0 | if (final_mask->data == NULL) { |
1685 | 0 | code = -1; |
1686 | 0 | goto out; |
1687 | 0 | } |
1688 | | |
1689 | | /* We need to associate the locate index with a particular level |
1690 | | for the when the dot begins to turn on. Go through the dot_level_pos |
1691 | | array to get the values. Probably should create this earlier and avoid |
1692 | | this */ |
1693 | 0 | dot_level_sort = (int*) ALLOC(dot_grid.memory, sizeof(int) * num_dots); |
1694 | 0 | if (dot_level_sort == NULL) { |
1695 | 0 | code = -1; |
1696 | 0 | goto out; |
1697 | 0 | } |
1698 | 0 | for (h = 0; h < num_dots; h++) { |
1699 | 0 | found = false; |
1700 | 0 | for (jj = 0; jj < num_levels; jj++) { |
1701 | 0 | curr_dot_level = &(dot_level_pos[jj]); |
1702 | 0 | for (mm = 0; mm < curr_dot_level->number_points; mm++) { |
1703 | 0 | if (pos_x[h] == curr_dot_level->point[mm].x && |
1704 | 0 | pos_y[h] == curr_dot_level->point[mm].y ) { |
1705 | 0 | found = true; |
1706 | 0 | dot_level_sort[h] = jj + 1; |
1707 | 0 | break; /* Break from position search(within level) */ |
1708 | 0 | } |
1709 | 0 | } |
1710 | 0 | if (found == true) { /* break from level search */ |
1711 | 0 | break; |
1712 | 0 | } |
1713 | 0 | } |
1714 | 0 | if (found == false) { |
1715 | 0 | dot_level_sort[h] = 0; |
1716 | 0 | } |
1717 | 0 | } |
1718 | |
|
1719 | 0 | for (h = 0; h < num_dots; h++) { |
1720 | 0 | for (j = 0; j < dot_grid.height; j++) { |
1721 | 0 | for (k = 0; k < dot_grid.width; k++) { |
1722 | 0 | val = htsc_getpoint(&dot_grid, k, j); |
1723 | 0 | if (val != 0) { |
1724 | | |
1725 | | /* Assign a offset threshold values */ |
1726 | 0 | j_index = |
1727 | 0 | (pos_y[h] + j - (int) dot_grid_one_index.y) % height_supercell; |
1728 | | |
1729 | | /* In case we have modulo of a negative number */ |
1730 | 0 | if (j_index < 0) j_index = j_index + height_supercell; |
1731 | 0 | k_index = |
1732 | 0 | (pos_x[h] + k - (int) dot_grid_one_index.x) % width_supercell; |
1733 | | |
1734 | | /* In case we have modulo of a negative number */ |
1735 | 0 | if (k_index < 0) k_index = k_index + width_supercell; |
1736 | 0 | threshold_value = (int)(thresholds[val-1] + |
1737 | 0 | mag_offset * dot_level_sort[h]); |
1738 | 0 | if (threshold_value > MAXVAL) threshold_value = (int)MAXVAL; |
1739 | 0 | if (threshold_value < 0) threshold_value = 0; |
1740 | 0 | htsc_setpoint(final_mask,k_index,j_index,threshold_value); |
1741 | 0 | } |
1742 | 0 | } |
1743 | 0 | } |
1744 | 0 | } |
1745 | 0 | out: |
1746 | 0 | if (dot_level_pos) { |
1747 | 0 | for (k = 0; k < num_levels; k++) { |
1748 | 0 | FREE(dot_grid.memory, dot_level_pos[k].locations); |
1749 | 0 | FREE(dot_grid.memory, dot_level_pos[k].point); |
1750 | 0 | } |
1751 | 0 | } |
1752 | 0 | FREE(dot_grid.memory, locate); |
1753 | 0 | FREE(dot_grid.memory, screen_matrix); |
1754 | 0 | FREE(dot_grid.memory, pos_x); |
1755 | 0 | FREE(dot_grid.memory, pos_y); |
1756 | 0 | FREE(dot_grid.memory, dot_level_pos); |
1757 | 0 | FREE(dot_grid.memory, dot_levels); |
1758 | 0 | FREE(dot_grid.memory, thresholds); |
1759 | 0 | FREE(dot_grid.memory, dot_level_sort); |
1760 | 0 | } |
1761 | 0 | return code; |
1762 | 0 | } |
1763 | | |
1764 | | static int |
1765 | | htsc_create_holladay_mask(htsc_dig_grid_t super_cell, int H, int L, |
1766 | | double gamma, htsc_dig_grid_t *final_mask) |
1767 | 0 | { |
1768 | 0 | double step_size = (MAXVAL + 1.0) /( (double) H * (double) L); |
1769 | 0 | int k, j, code = 0; |
1770 | 0 | double *thresholds = NULL; |
1771 | 0 | int number_points = H * L; |
1772 | 0 | double half_step = step_size / 2.0; |
1773 | 0 | double temp; |
1774 | 0 | int index_point; |
1775 | 0 | int value; |
1776 | 0 | double white_scale = 253.0 / 255.0; /* To ensure white is white */ |
1777 | |
|
1778 | 0 | final_mask->height = H; |
1779 | 0 | final_mask->width = L; |
1780 | 0 | final_mask->data = (int *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(int)); |
1781 | 0 | if (final_mask->data == NULL) { |
1782 | 0 | code = -1; |
1783 | 0 | goto out; |
1784 | 0 | } |
1785 | | |
1786 | 0 | thresholds = (double *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(double)); |
1787 | 0 | if (thresholds == NULL) { |
1788 | 0 | code = -1; |
1789 | 0 | goto out; |
1790 | 0 | } |
1791 | 0 | for (k = 0; k < number_points; k++) { |
1792 | 0 | temp = ((k+1) * step_size - half_step) / MAXVAL; |
1793 | 0 | if ( gamma != 1.0) { |
1794 | | /* Possible linearization */ |
1795 | 0 | temp = (double) pow((double) temp, (double) gamma); |
1796 | 0 | } |
1797 | 0 | thresholds[number_points - k - 1] = |
1798 | 0 | ROUND(temp * MAXVAL * white_scale + 1); |
1799 | 0 | } |
1800 | 0 | memset(final_mask->data, 0, (size_t)H * L * sizeof(int)); |
1801 | |
|
1802 | 0 | for (j = 0; j < H; j++) { |
1803 | 0 | for (k = 0; k < L; k++) { |
1804 | 0 | index_point = htsc_getpoint(&super_cell,k,j) - 1; |
1805 | 0 | value = (int) floor(thresholds[index_point]); |
1806 | 0 | htsc_setpoint(final_mask,k,j,value); |
1807 | 0 | } |
1808 | 0 | } |
1809 | 0 | out: |
1810 | 0 | FREE(final_mask->memory, thresholds); |
1811 | 0 | return code; |
1812 | 0 | } |
1813 | | |
1814 | | static int |
1815 | | htsc_create_nondithered_mask(htsc_dig_grid_t super_cell, int H, int L, |
1816 | | double gamma, htsc_dig_grid_t *final_mask) |
1817 | 0 | { |
1818 | 0 | double step_size = (MAXVAL + 1) /( (double) H * (double) L); |
1819 | 0 | int k, j, code = 0; |
1820 | 0 | double *thresholds = NULL; |
1821 | 0 | int number_points = H * L; |
1822 | 0 | double half_step = step_size / 2.0; |
1823 | 0 | double temp; |
1824 | 0 | int index_point; |
1825 | 0 | int value; |
1826 | 0 | double white_scale = 253.0 / 255.0; /* To ensure white is white */ |
1827 | |
|
1828 | 0 | final_mask->height = super_cell.height; |
1829 | 0 | final_mask->width = super_cell.width; |
1830 | 0 | final_mask->data = (int *) ALLOC(final_mask->memory, |
1831 | 0 | (size_t)super_cell.height * super_cell.width * |
1832 | 0 | sizeof(int)); |
1833 | 0 | if (final_mask->data == NULL) { |
1834 | 0 | code = -1; |
1835 | 0 | goto out; |
1836 | 0 | } |
1837 | 0 | thresholds = (double *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(double)); |
1838 | 0 | if (thresholds == NULL) { |
1839 | 0 | code = -1; |
1840 | 0 | goto out; |
1841 | 0 | } |
1842 | 0 | for (k = 0; k < number_points; k++) { |
1843 | 0 | temp = ((k+1) * step_size - half_step) / MAXVAL; |
1844 | 0 | if ( gamma != 1.0) { |
1845 | | /* Possible linearization */ |
1846 | 0 | temp = (double) pow((double) temp, (double) gamma); |
1847 | 0 | } |
1848 | 0 | thresholds[number_points - k - 1] = |
1849 | 0 | ROUND(temp * MAXVAL * white_scale + 1); |
1850 | 0 | } |
1851 | 0 | memset(final_mask->data, 0, (size_t)super_cell.height * super_cell.width * |
1852 | 0 | sizeof(int)); |
1853 | 0 | for (j = 0; j < super_cell.height; j++) { |
1854 | 0 | for (k = 0; k < super_cell.width; k++) { |
1855 | 0 | index_point = htsc_getpoint(&super_cell,k,j) - 1; |
1856 | 0 | value = (int) floor(thresholds[index_point]); |
1857 | 0 | htsc_setpoint(final_mask,k,j,value); |
1858 | 0 | } |
1859 | 0 | } |
1860 | 0 | out: |
1861 | 0 | FREE(final_mask->memory, thresholds); |
1862 | 0 | return code; |
1863 | 0 | } |
1864 | | |
1865 | | /* Various spot functions */ |
1866 | | static double |
1867 | | htsc_spot_circle(double x, double y) |
1868 | 0 | { |
1869 | 0 | return 1.0 - (x*x + y*y); |
1870 | 0 | } |
1871 | | |
1872 | | static double |
1873 | | htsc_spot_redbook(double x, double y) |
1874 | 0 | { |
1875 | 0 | return (180.0 * (double) cos(x) + 180.0 * (double) cos(y)) / 2.0; |
1876 | 0 | } |
1877 | | |
1878 | | static double |
1879 | | htsc_spot_inverted_round(double x, double y) |
1880 | 0 | { |
1881 | 0 | return (x*x + y*y) - 1.0; |
1882 | 0 | } |
1883 | | |
1884 | | static double |
1885 | | htsc_spot_rhomboid(double x, double y) |
1886 | 0 | { |
1887 | 0 | return 1.0 - ((double) fabs(y) * 0.8 + (double) fabs(x)) / 2.0; |
1888 | 0 | } |
1889 | | |
1890 | | static double |
1891 | | htsc_spot_linex(double x, double y) |
1892 | 0 | { |
1893 | 0 | return 1.0 - (double) fabs(y); |
1894 | 0 | } |
1895 | | |
1896 | | static double |
1897 | | htsc_spot_liney(double x, double y) |
1898 | 0 | { |
1899 | 0 | return 1.0 - (double) fabs(x); |
1900 | 0 | } |
1901 | | |
1902 | | static double |
1903 | | htsc_spot_diamond(double x, double y) |
1904 | 0 | { |
1905 | 0 | double abs_y = (double) fabs(y); |
1906 | 0 | double abs_x = (double) fabs(x); |
1907 | |
|
1908 | 0 | if ((abs_y + abs_x) <= 0.75) { |
1909 | 0 | return 1.0 - (abs_x * abs_x + abs_y * abs_y); |
1910 | 0 | } else { |
1911 | 0 | if ((abs_y + abs_x) <= 1.23) { |
1912 | 0 | return 1.0 - (0.76 * abs_y + abs_x); |
1913 | 0 | } else { |
1914 | 0 | return ((abs_x - 1.0) * (abs_x - 1.0) + |
1915 | 0 | (abs_y - 1.0) * (abs_y - 1.0)) - 1.0; |
1916 | 0 | } |
1917 | 0 | } |
1918 | 0 | } |
1919 | | |
1920 | | static double |
1921 | | htsc_spot_diamond2(double x, double y) |
1922 | 0 | { |
1923 | 0 | double xy = (double) fabs(x) + (double) fabs(y); |
1924 | |
|
1925 | 0 | if (xy <= 1.0) { |
1926 | 0 | return 1.0 - xy * xy / 2.0; |
1927 | 0 | } else { |
1928 | 0 | return 1.0 - (2.0 * xy * xy - 4.0 * (xy - 1.0) * (xy - 1.0)) / 4.0; |
1929 | 0 | } |
1930 | 0 | } |
1931 | | |
1932 | | static double |
1933 | | htsc_spot_roundspot(double x, double y) |
1934 | 0 | { |
1935 | 0 | double xy = (double)fabs(x) + (double)fabs(y); |
1936 | |
|
1937 | 0 | if (xy <= 1.0) { |
1938 | 0 | return 1.0 - (x*x + y*y); |
1939 | 0 | } else { |
1940 | 0 | return ((fabs(x) - 1.0) * (fabs(x) - 1.0)) + ((fabs(y) - 1.0) * (fabs(y) - 1.0)) - 1.0; |
1941 | 0 | } |
1942 | 0 | } |
1943 | | |
1944 | | static double |
1945 | | htsc_spot_value(spottype_t spot_type, double x, double y) |
1946 | 0 | { |
1947 | 0 | switch (spot_type) { |
1948 | 0 | case CIRCLE: |
1949 | 0 | return htsc_spot_circle(x,y); |
1950 | 0 | case REDBOOK: |
1951 | 0 | return htsc_spot_redbook(x,y); |
1952 | 0 | case INVERTED: |
1953 | 0 | return htsc_spot_inverted_round(x,y); |
1954 | 0 | case RHOMBOID: |
1955 | 0 | return htsc_spot_rhomboid(x,y); |
1956 | 0 | case LINE_X: |
1957 | 0 | return htsc_spot_linex(x,y); |
1958 | 0 | case LINE_Y: |
1959 | 0 | return htsc_spot_liney(x,y); |
1960 | 0 | case DIAMOND1: |
1961 | 0 | return htsc_spot_diamond(x,y); |
1962 | 0 | case DIAMOND2: |
1963 | 0 | return htsc_spot_diamond2(x,y); |
1964 | 0 | case ROUNDSPOT: |
1965 | 0 | return htsc_spot_roundspot(x,y); |
1966 | 0 | case CUSTOM: /* A spot (pun intended) for users to define their own */ |
1967 | 0 | return htsc_spot_circle(x,y); |
1968 | 0 | default: |
1969 | 0 | return htsc_spot_circle(x,y); |
1970 | 0 | } |
1971 | 0 | } |
1972 | | |
1973 | | #if FINAL_SCREEN_DUMP |
1974 | | |
1975 | | /* Save turn on order list, Assume that htsc_gen_ordered has already converted to TOS */ |
1976 | | static int |
1977 | | htsc_save_tos(htsc_dig_grid_t *final_mask, gs_memory_t *mem) |
1978 | | { |
1979 | | int width = final_mask->width; |
1980 | | int height = final_mask->height; |
1981 | | int *buff_ptr; |
1982 | | _FILE *fid; |
1983 | | int k = 0; |
1984 | | int count = height * width; |
1985 | | |
1986 | | fid = FOPEN(mem, "turn_on_seq.out", "w"); |
1987 | | if (fid == NULL) |
1988 | | return -1; |
1989 | | |
1990 | | FPRINTF2(fid, "# W=%d H=%d\n", width, height); |
1991 | | |
1992 | | /* Write out */ |
1993 | | buff_ptr = final_mask->data; |
1994 | | for (k = 0; k < count; k++) { |
1995 | | FPRINTF2(fid, "%d\t%d\n", *buff_ptr++, *buff_ptr++); |
1996 | | } |
1997 | | FCLOSE(fid); |
1998 | | return 0; |
1999 | | } |
2000 | | |
2001 | | int |
2002 | | htsc_save_screen(htsc_dig_grid_t *final_mask, bool use_holladay_grid, int S, |
2003 | | htsc_param_t params, gs_memory_t *mem) |
2004 | | { |
2005 | | char full_file_name[FULL_FILE_NAME_LENGTH]; |
2006 | | _FILE *fid; |
2007 | | int x, y, code = 0; |
2008 | | int *buff_ptr = final_mask->data; |
2009 | | int width = final_mask->width; |
2010 | | int height = final_mask->height; |
2011 | | byte data; |
2012 | | unsigned short data_short; |
2013 | | output_format_type output_format = params.output_format; |
2014 | | char *output_extension = (output_format == OUTPUT_PS) ? "ps" : |
2015 | | ((output_format == OUTPUT_PPM) ? "ppm" : |
2016 | | ((output_format == OUTPUT_RAW16 ? "16.raw" : "raw"))); |
2017 | | |
2018 | | if (output_format == OUTPUT_TOS) { |
2019 | | /* We need to figure out the turn-on sequence from the threshold |
2020 | | array */ |
2021 | | code = htsc_save_tos(final_mask, mem); |
2022 | | } else { |
2023 | | if (use_holladay_grid) { |
2024 | | snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_Holladay_Shift%d_%dx%d.%s", S, width, |
2025 | | height, output_extension); |
2026 | | } else { |
2027 | | snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_Dithered_%dx%d.%s", width, height, |
2028 | | output_extension); |
2029 | | } |
2030 | | fid = FOPEN(mem, full_file_name, "wb"); |
2031 | | if (fid == NULL) |
2032 | | return -1; |
2033 | | |
2034 | | if (output_format == OUTPUT_PPM) |
2035 | | FPRINTF6(fid, "P5\n" |
2036 | | "# Halftone threshold array, %s, [%d, %d], S=%d\n" |
2037 | | "%d %d\n" |
2038 | | "255\n", |
2039 | | use_holladay_grid ? "Holladay_Shift" : "Dithered", width, height, |
2040 | | S, width, height); |
2041 | | if (output_format != OUTPUT_PS) { |
2042 | | /* Both BIN and PPM format write the same binary data */ |
2043 | | if (output_format == OUTPUT_RAW || output_format == OUTPUT_PPM) { |
2044 | | for (y = 0; y < height; y++) { |
2045 | | for (x = 0; x < width; x++) { |
2046 | | data_short = (unsigned short)(*buff_ptr & 0xffff); |
2047 | | data_short = ROUND((double)data_short * 255.0 / MAXVAL); |
2048 | | if (data_short > 255) data_short = 255; |
2049 | | data = (byte)(data_short & 0xff); |
2050 | | FWRITE(&data, sizeof(byte), 1, fid); |
2051 | | buff_ptr++; |
2052 | | } |
2053 | | } |
2054 | | } else { /* raw16 data */ |
2055 | | for (y = 0; y < height; y++) { |
2056 | | for (x = 0; x < width; x++) { |
2057 | | data_short = (unsigned short)(*buff_ptr & 0xffff); |
2058 | | FWRITE(&data_short, sizeof(short), 1, fid); |
2059 | | buff_ptr++; |
2060 | | } |
2061 | | } |
2062 | | } |
2063 | | } else { /* ps output format */ |
2064 | | if (params.targ_quant <= 256) { |
2065 | | /* Output PS HalftoneType 3 dictionary */ |
2066 | | FPRINTF2(fid, "%%!PS\n" |
2067 | | "<< /HalftoneType 3\n" |
2068 | | " /Width %d\n" |
2069 | | " /Height %d\n" |
2070 | | " /Thresholds <\n", |
2071 | | width, height); |
2072 | | |
2073 | | for (y = 0; y < height; y++) { |
2074 | | for (x = 0; x < width; x++) { |
2075 | | data_short = (unsigned short)(*buff_ptr & 0xffff); |
2076 | | data_short = ROUND((double)data_short * 255.0 / MAXVAL); |
2077 | | if (data_short > 255) data_short = 255; |
2078 | | data = (byte)(data_short & 0xff); |
2079 | | FPRINTF1(fid, "%02x", data); |
2080 | | buff_ptr++; |
2081 | | if ((x & 0x1f) == 0x1f && (x != (width - 1))) |
2082 | | FPRINTF(fid, "\n"); |
2083 | | } |
2084 | | FPRINTF(fid, "\n"); |
2085 | | } |
2086 | | FPRINTF(fid, " >\n" |
2087 | | ">>\n" |
2088 | | ); |
2089 | | } else { |
2090 | | /* Output PS HalftoneType 16 dictionary. Single array. */ |
2091 | | FPRINTF(fid, "%%!PS\n" |
2092 | | "%% Create a 'filter' from local hex data\n" |
2093 | | "{ currentfile /ASCIIHexDecode filter /ReusableStreamDecode filter } exec\n"); |
2094 | | /* hex data follows, 'file' object will be left on stack */ |
2095 | | for (y = 0; y < height; y++) { |
2096 | | for (x = 0; x < width; x++) { |
2097 | | data_short = (unsigned short)(*buff_ptr & 0xffff); |
2098 | | FPRINTF1(fid, "%04x", data_short); |
2099 | | buff_ptr++; |
2100 | | if ((x & 0x1f) == 0x1f && (x != (width - 1))) |
2101 | | FPRINTF(fid, "\n"); |
2102 | | } |
2103 | | FPRINTF(fid, "\n"); /* end of one row */ |
2104 | | } |
2105 | | FPRINTF(fid, ">\n"); /* ASCIIHexDecode EOF */ |
2106 | | FPRINTF2(fid, |
2107 | | "<< /Thresholds 2 index %% file object for the 16-bit data\n" |
2108 | | " /HalftoneType 16\n" |
2109 | | " /Width %d\n" |
2110 | | " /Height %d\n" |
2111 | | ">>\n" |
2112 | | "exch pop %% discard ResuableStreamDecode file leaving the Halftone dict.\n", |
2113 | | width, height); |
2114 | | } |
2115 | | } |
2116 | | FCLOSE(fid); |
2117 | | } |
2118 | | return code; |
2119 | | } |
2120 | | #endif |
2121 | | |
2122 | | #if RAW_SCREEN_DUMP |
2123 | | static int |
2124 | | htsc_dump_screen(htsc_dig_grid_t *dig_grid, char filename[], gs_memory_t *mem) |
2125 | | { |
2126 | | char full_file_name[FULL_FILE_NAME_LENGTH]; |
2127 | | _FILE *fid; |
2128 | | int x,y; |
2129 | | int *buff_ptr = dig_grid->data; |
2130 | | int width = dig_grid->width; |
2131 | | int height = dig_grid->height; |
2132 | | byte data[3]; |
2133 | | |
2134 | | snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_%s_%dx%dx3.raw",filename,width,height); |
2135 | | fid = FOPEN(mem, full_file_name,"wb"); |
2136 | | if (fid == NULL) |
2137 | | return -1; |
2138 | | |
2139 | | for (y = 0; y < height; y++) { |
2140 | | for ( x = 0; x < width; x++ ) { |
2141 | | if (*buff_ptr < 0) { |
2142 | | data[0] = 255; |
2143 | | data[1] = 0; |
2144 | | data[2] = 0; |
2145 | | } else if (*buff_ptr > 255) { |
2146 | | data[0] = 0; |
2147 | | data[1] = 255; |
2148 | | data[2] = 0; |
2149 | | } else { |
2150 | | data[0] = *buff_ptr; |
2151 | | data[1] = *buff_ptr; |
2152 | | data[2] = *buff_ptr; |
2153 | | } |
2154 | | FWRITE(data,sizeof(unsigned char),3,fid); |
2155 | | buff_ptr++; |
2156 | | } |
2157 | | } |
2158 | | FCLOSE(fid); |
2159 | | return 0; |
2160 | | } |
2161 | | |
2162 | | static int |
2163 | | htsc_dump_float_image(double *image, int height, int width, double max_val, |
2164 | | char filename[], gs_memory_t *mem) |
2165 | | { |
2166 | | char full_file_name[FULL_FILE_NAME_LENGTH]; |
2167 | | _FILE *fid; |
2168 | | int x,y; |
2169 | | int data; |
2170 | | byte data_byte; |
2171 | | |
2172 | | snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Float_%s_%dx%d.raw",filename,width,height); |
2173 | | fid = FOPEN(mem, full_file_name,"wb"); |
2174 | | if (fid == NULL) |
2175 | | return -1; |
2176 | | |
2177 | | for (y = 0; y < height; y++) { |
2178 | | for ( x = 0; x < width; x++ ) { |
2179 | | data = (255.0 * image[x + y * width] / max_val); |
2180 | | if (data > 255) data = 255; |
2181 | | if (data < 0) data = 0; |
2182 | | data_byte = data; |
2183 | | FWRITE(&data_byte,sizeof(byte),1,fid); |
2184 | | } |
2185 | | } |
2186 | | FCLOSE(fid); |
2187 | | return 0; |
2188 | | } |
2189 | | |
2190 | | static int |
2191 | | htsc_dump_byte_image(byte *image, int height, int width, double max_val, |
2192 | | char filename[], gs_memory_t *mem) |
2193 | | { |
2194 | | char full_file_name[FULL_FILE_NAME_LENGTH]; |
2195 | | _FILE *fid; |
2196 | | int x,y; |
2197 | | int data; |
2198 | | byte data_byte; |
2199 | | |
2200 | | snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "ByteScaled_%s_%dx%d.raw",filename,width,height); |
2201 | | fid = FOPEN(mem, full_file_name,"wb"); |
2202 | | if (fid == NULL) |
2203 | | return -1; |
2204 | | |
2205 | | for (y = 0; y < height; y++) { |
2206 | | for ( x = 0; x < width; x++ ) { |
2207 | | data = (255.0 * image[x + y * width] / max_val); |
2208 | | if (data > 255) data = 255; |
2209 | | if (data < 0) data = 0; |
2210 | | data_byte = data; |
2211 | | FWRITE(&data_byte,sizeof(byte),1,fid); |
2212 | | } |
2213 | | } |
2214 | | FCLOSE(fid); |
2215 | | return 0; |
2216 | | } |
2217 | | #endif |