Coverage Report

Created: 2026-10-28 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gen_ordered.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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;
372
0
    htsc_threshpoint_t *values;
373
0
    int *tos;
374
375
0
    if (check_int_multiply(height, width, &count) < 0)
376
0
        return -1;
377
0
    if (check_int_multiply(count, sizeof(htsc_threshpoint_t), &x) < 0)
378
0
        return -1;
379
380
0
    values = (htsc_threshpoint_t *) ALLOC(final_mask->memory,
381
0
                                          sizeof(htsc_threshpoint_t) * width * height);
382
0
    if (values == NULL) {
383
0
        EPRINTF(final_mask->memory, "ERROR! malloc failure in htsc_mask_to_tos!\n");
384
0
        return -1;
385
0
    }
386
0
    if (check_int_multiply(count, 2, &x) < 0)
387
0
        return -1;
388
0
    if (check_int_multiply(x, sizeof(int), &x) < 0)
389
0
        return -1;
390
0
    tos = (int *) ALLOC(final_mask->memory, sizeof(int) * 2 * height * width);
391
0
    if (tos == NULL) {
392
0
        FREE(final_mask->memory, values);
393
0
        EPRINTF(final_mask->memory, "ERROR! malloc failure in htsc_mask_to_tos!\n");
394
0
        return -1;
395
0
    }
396
    /* Do a sort on the values and then output the coordinates */
397
    /* First get a list made with the unsorted values and coordinates */
398
0
    for (y = 0; y < height; y++) {
399
0
        for ( x = 0; x < width; x++ ) {
400
0
            values[k].value = *buff_ptr;
401
0
            values[k].x = x;
402
0
            values[k].y = y;
403
0
            values[k].index = k;
404
0
            values[k].dist_to_center = (x - center.xy[0]) * (x - center.xy[0]) +
405
0
                                       (y - center.xy[1]) * (y - center.xy[1]);
406
0
            buff_ptr++;
407
0
            k = k + 1;
408
0
        }
409
0
    }
410
#if RAW_SCREEN_DUMP
411
    EPRINTF(final_mask->memory, "Unsorted\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
        /* Sort */
418
0
    qsort(values, (size_t)height * width, sizeof(htsc_threshpoint_t), compare);
419
#if RAW_SCREEN_DUMP
420
    EPRINTF(final_mask->memory, "Sorted\n");
421
    for (k = 0; k < count; k++) {
422
        EPRINTF(final_mask->memory, "Index %d : x = %d y = %d dist = %4.2lf value = %d \n",
423
                values[k].index, values[k].x, values[k].y, values[k].dist_to_center, values[k].value);
424
    }
425
#endif
426
427
0
    FREE(final_mask->memory, final_mask->data);
428
0
    final_mask->data = tos;
429
0
    buff_ptr = tos;
430
431
0
    for (k=0; k < count; k++) {
432
0
        *buff_ptr++ = values[count - 1 - k].x;
433
0
        *buff_ptr++ = values[count - 1 - k].y;
434
0
    }
435
0
    FREE(final_mask->memory, values);
436
0
    return 0;
437
0
}
438
439
static void
440
htsc_determine_cell_shape(int *x_out, int *y_out, int *v_out,
441
                          int *u_out, int *N_out, htsc_param_t params, void *mem)
442
0
{
443
0
    double x = 0., y = 0., v = 0., u = 0., N = 0.;
444
0
    double frac, scaled_x;
445
0
    double ratio;
446
0
    const double  pi = 3.14159265358979323846f;
447
0
    double true_angle, lpi;
448
0
    double prev_lpi, max_lpi = 0.;
449
0
    bool use = false;
450
0
    double x_use = 0.,y_use = 0.;
451
452
    /* Go through and find the rational angle options that gets us to the
453
       best LPI.  Pick the one that is just over what is requested.
454
       That is really our limiting factor here.  Pick it and
455
       then figure out how much dithering we need to do to get to the proper
456
       number of levels.  */
457
0
    frac = tan( params.scr_ang * pi / 180.0 );
458
0
    ratio = frac * params.horiz_dpi / params.vert_dpi;
459
0
    scaled_x = params.horiz_dpi / params.vert_dpi;
460
    /* The minimal step is in x */
461
0
    prev_lpi = 0;
462
0
    if (ratio < 1 && ratio != 0) {
463
0
        if (params.verbose > 0) {
464
0
            PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n");
465
0
            PRINTF(mem, "-----------------------------------------------------------\n");
466
0
        }
467
0
        for (x = 1; x < 11; x++) {
468
0
            x_use = x;
469
0
            y=ROUND((double) x_use / ratio);
470
0
            true_angle = 180.0 * atan(((double) x_use / params.horiz_dpi) / ( (double) y / params.vert_dpi) ) / pi;
471
0
            lpi = 1.0/( sqrt( ((double) y / params.vert_dpi) * ( (double) y / params.vert_dpi) +
472
0
                                    ( (double) x_use / params.horiz_dpi) * ((double) x_use / params.horiz_dpi) ));
473
0
            v = -x_use / scaled_x;
474
0
            u = y * scaled_x;
475
0
            N = y *u - x_use * v;
476
0
            if (prev_lpi == 0) {
477
0
                prev_lpi = lpi;
478
0
                if (params.targ_lpi > lpi) {
479
0
                    EPRINTF(mem, "Warning this lpi is not achievable!\n");
480
0
                    EPRINTF(mem, "Resulting screen will be poorly quantized\n");
481
0
                    EPRINTF(mem, "or completely stochastic!\n");
482
0
                    use = true;
483
0
                }
484
0
                max_lpi = lpi;
485
0
            }
486
0
            if (prev_lpi >= params.targ_lpi && lpi < params.targ_lpi) {
487
0
                if (prev_lpi == max_lpi) {
488
0
                    EPRINTF(mem, "Notice lpi is at the maximimum level possible.\n");
489
0
                    EPRINTF(mem, "This may result in poor quantization. \n");
490
0
                }
491
                /* Reset these to previous x */
492
0
                x_use = x - 1;
493
0
                y=ROUND((double) x_use / ratio);
494
0
                true_angle =
495
0
                    180.0 * atan(((double) x_use / params.horiz_dpi) / ( (double) y / params.vert_dpi) ) / pi;
496
0
                lpi =
497
0
                    1.0/( sqrt( ((double) y / params.vert_dpi) * ( (double) y / params.vert_dpi) +
498
0
                                        ( (double) x_use / params.horiz_dpi) * ((double) x_use / params.horiz_dpi) ));
499
0
                v = -x_use / scaled_x;
500
0
                u = y * scaled_x;
501
0
                N = y *u - x_use * v;
502
0
                use = true;
503
0
            }
504
0
            if (use == true) {
505
0
                if (prev_lpi == max_lpi) {
506
                    /* Print out the final one that we will use */
507
0
                    if (params.verbose > 0)
508
0
                        PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
509
0
                                x_use,y,u,v,true_angle,lpi,N);
510
0
                }
511
0
                break;
512
0
            }
513
0
            if (params.verbose > 0)
514
0
                PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
515
0
                        x_use,y,u,v,true_angle,lpi,N);
516
0
            prev_lpi = lpi;
517
0
        }
518
0
        x = x_use;
519
0
    }
520
0
    if (ratio >= 1) {
521
        /* The minimal step is in y */
522
0
        if (params.verbose > 0) {
523
0
            PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n");
524
0
            PRINTF(mem, "-----------------------------------------------------------\n");
525
0
        }
526
0
        for (y = 1, lpi = 99999999; lpi > params.targ_lpi; y++) {
527
0
            y_use = y;
528
0
            x = ROUND(y_use * ratio);
529
            /* compute the true angle */
530
0
            true_angle = 180.0 * atan((x / params.horiz_dpi) / (y_use / params.vert_dpi)) / pi;
531
0
            lpi = 1.0 / sqrt( (y_use / params.vert_dpi) * (y_use / params.vert_dpi) +
532
0
                                (x / params.horiz_dpi) * (x / params.horiz_dpi));
533
0
            v = ROUND(-x / scaled_x);
534
0
            u = ROUND(y_use * scaled_x);
535
0
            N = y_use * u - x * v;
536
0
            if (prev_lpi == 0) {
537
0
                prev_lpi = lpi;
538
0
                if (params.targ_lpi > lpi) {
539
0
                    EPRINTF(mem, "Warning this lpi is not achievable!\n");
540
0
                    EPRINTF(mem, "Resulting screen will be poorly quantized\n");
541
0
                    EPRINTF(mem, "or completely stochastic!\n");
542
0
                    use = true;
543
0
                }
544
0
                max_lpi = lpi;
545
0
            }
546
0
            if (prev_lpi >= params.targ_lpi && lpi < params.targ_lpi) {
547
0
                if (prev_lpi == max_lpi) {
548
0
                    EPRINTF(mem, "Warning lpi will be slightly lower than target.\n");
549
0
                    EPRINTF(mem, "An increase will result in poor \n");
550
0
                    EPRINTF(mem, "quantization or a completely stochastic screen!\n");
551
0
                } else {
552
                    /* Reset these to previous x */
553
0
                    y_use = y - 1;
554
0
                    x = ROUND(y_use * ratio);
555
                    /* compute the true angle */
556
0
                    true_angle = 180.0 * atan((x / params.horiz_dpi) / (y_use / params.vert_dpi)) / pi;
557
0
                    lpi = 1.0 / sqrt( (y_use / params.vert_dpi) * (y_use / params.vert_dpi) +
558
0
                                        (x / params.horiz_dpi) * (x / params.horiz_dpi));
559
0
                    v = ROUND(-x / scaled_x);
560
0
                    u = ROUND(y_use * scaled_x);
561
0
                    N = y_use * u - x * v;
562
0
                }
563
0
                use = true;
564
0
            }
565
0
            if (use == true) {
566
0
                if (prev_lpi == max_lpi) {
567
                    /* Print out the final one that we will use */
568
0
                    PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
569
0
                                    x,y_use,u,v,true_angle,lpi,N);
570
0
                }
571
0
                break;
572
0
            }
573
0
            if (params.verbose > 0)
574
0
                PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
575
0
                        x,y_use,u,v,true_angle,lpi,N);
576
0
            prev_lpi = lpi;
577
0
        }
578
0
        y = y_use;
579
0
    }
580
0
    if (ratio == 0) {
581
        /* 0 degrees */
582
0
        if (scaled_x >= 1) {
583
0
            if (params.verbose > 0) {
584
0
                PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n");
585
0
                PRINTF(mem, "-----------------------------------------------------------\n");
586
0
            }
587
0
            for (y = 1, lpi=9999999; lpi > params.targ_lpi; y++) {
588
0
                y_use = y;
589
0
                x = ROUND( y_use * ratio );
590
0
                v = ROUND(-x / scaled_x);
591
0
                u = ROUND(y_use * scaled_x);
592
0
                N = y_use * u - x * v;
593
0
                true_angle = 0;
594
0
                lpi = 1.0/(double) sqrt( (double) ((y_use / params.vert_dpi) *
595
0
                    (y_use / params.vert_dpi) + (x / params.horiz_dpi) * (x / params.horiz_dpi)) );
596
0
                if (prev_lpi == 0) {
597
0
                    prev_lpi = lpi;
598
0
                    if (params.targ_lpi > lpi) {
599
0
                        EPRINTF(mem, "Warning this lpi is not achievable!\n");
600
0
                        EPRINTF(mem, "Resulting screen will be poorly quantized\n");
601
0
                        EPRINTF(mem, "or completely stochastic!\n");
602
0
                        use = true;
603
0
                    }
604
0
                    max_lpi = lpi;
605
0
                }
606
0
                if (prev_lpi >= params.targ_lpi && lpi < params.targ_lpi) {
607
0
                    if (prev_lpi == max_lpi) {
608
0
                        EPRINTF(mem, "Warning lpi will be slightly lower than target.\n");
609
0
                        EPRINTF(mem, "An increase will result in poor \n");
610
0
                        EPRINTF(mem, "quantization or a completely stochastic screen!\n");
611
0
                    } else {
612
                        /* Reset these to previous x */
613
0
                        y_use = y - 1;
614
0
                        x = ROUND( y_use * ratio );
615
0
                        v = ROUND(-x / scaled_x);
616
0
                        u = ROUND(y_use * scaled_x);
617
0
                        N = y_use * u - x * v;
618
0
                        true_angle = 0;
619
0
                        lpi = 1.0/(double) sqrt( (double) ((y_use / params.vert_dpi) *
620
0
                            (y_use / params.vert_dpi) + (x / params.horiz_dpi) * (x / params.horiz_dpi)) );
621
0
                    }
622
0
                    use = true;
623
0
                }
624
0
                if (use == true) {
625
0
                    if (prev_lpi == max_lpi) {
626
                        /* Print out the final one that we will use */
627
0
                        if (params.verbose > 0)
628
0
                            PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
629
0
                                    x,y_use,u,v,true_angle,lpi,N);
630
0
                    }
631
0
                    break;
632
0
                }
633
0
                if (params.verbose > 0)
634
0
                    PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
635
0
                            x,y_use,u,v,true_angle,lpi,N);
636
0
                prev_lpi = lpi;
637
0
            }
638
0
            y = y_use;
639
0
        } else {
640
0
        if (params.verbose > 0) {
641
0
            PRINTF(mem, "x\ty\tu\tv\tAngle\tLPI\tLevels\n");
642
0
            PRINTF(mem, "-----------------------------------------------------------\n");
643
0
        }
644
0
            for (x = 1; x < 11; x++) {
645
0
                x_use = x;
646
0
                y = ROUND(x_use * ratio);
647
0
                true_angle = 0;
648
0
                lpi = 1.0/( sqrt( (y / params.vert_dpi) * (y / params.vert_dpi) +
649
0
                            (x_use / params.horiz_dpi) * (x_use / params.horiz_dpi) ));
650
0
                v = ROUND( -x_use / scaled_x);
651
0
                u = ROUND( y * scaled_x);
652
0
                N = y  *u - x_use * v;
653
0
                if (prev_lpi == 0) {
654
0
                    prev_lpi = lpi;
655
0
                    if (params.targ_lpi > lpi) {
656
0
                        EPRINTF(mem, "Warning this lpi is not achievable!\n");
657
0
                        EPRINTF(mem, "Resulting screen will be poorly quantized\n");
658
0
                        EPRINTF(mem, "or completely stochastic!\n");
659
0
                        use = true;
660
0
                    }
661
0
                    max_lpi = lpi;
662
0
                }
663
0
                if (prev_lpi > params.targ_lpi && lpi < params.targ_lpi) {
664
0
                    if (prev_lpi == max_lpi) {
665
0
                        EPRINTF(mem, "Warning lpi will be slightly lower than target.\n");
666
0
                        EPRINTF(mem, "An increase will result in poor \n");
667
0
                        EPRINTF(mem, "quantization or a completely stochastic screen!\n");
668
0
                    } else {
669
                        /* Reset these to previous x */
670
0
                        x_use = x - 1;
671
0
                        y = ROUND(x_use * ratio);
672
0
                        true_angle = 0;
673
0
                        lpi = 1.0/( sqrt( (y / params.vert_dpi) * (y / params.vert_dpi) +
674
0
                                    (x_use / params.horiz_dpi) * (x_use / params.horiz_dpi) ));
675
0
                        v = ROUND( -x_use / scaled_x);
676
0
                        u = ROUND( y * scaled_x);
677
0
                        N = y  *u - x_use * v;
678
0
                    }
679
0
                    use = true;
680
0
                }
681
0
                if (use == true) {
682
0
                    if (prev_lpi == max_lpi) {
683
                        /* Print out the final one that we will use */
684
0
                        if (params.verbose > 0)
685
0
                            PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
686
0
                                    x_use,y,u,v,true_angle,lpi,N);
687
0
                    }
688
0
                    break;
689
0
                }
690
0
                if (params.verbose > 0)
691
0
                    PRINTF7(mem, "%3.0lf\t%3.0lf\t%3.0lf\t%3.0lf\t%3.1lf\t%3.1lf\t%3.0lf\n",
692
0
                            x_use,y,u,v,true_angle,lpi,N);
693
0
                prev_lpi = lpi;
694
0
            }
695
0
            x = x_use;
696
0
        }
697
0
    }
698
0
    *x_out = (int)x;
699
0
    *y_out = (int)y;
700
0
    *v_out = (int)v;
701
0
    *u_out = (int)u;
702
0
    *N_out = (int)N;
703
0
}
704
705
static void
706
htsc_create_dot_profile(htsc_dig_grid_t *dig_grid, int N, int x, int y, int u, int v,
707
                        double horiz_dpi, double vert_dpi, htsc_vertices_t vertices,
708
                        htsc_point_t *one_index, spottype_t spot_type, htsc_matrix_t trans_matrix)
709
0
{
710
0
    int done, dot_index, hole_index, count, index_x, index_y;
711
0
    htsc_dot_shape_search_t dot_search;
712
0
    int k, val_min;
713
0
    double dist;
714
0
    int j;
715
0
    htsc_vector_t vector_in, vector_out;
716
0
    double x_offset = (x % 2 == 0 ? 0.5 : 0);
717
0
    double y_offset = (y % 2 == 0 ? 0.5 : 0);
718
719
0
    done = 0;
720
0
    dot_index = 1;
721
0
    hole_index = N;
722
0
    count = 0;
723
0
    val_min=MIN(0,v);
724
725
0
    while (!done) {
726
727
        /* First perform a search for largest dot value for those remaining
728
           dots */
729
0
        index_x = 0;
730
0
        dot_search.index_x = 0;
731
0
        dot_search.index_y = 0;
732
0
        dot_search.norm = -100000000; /* Hopefully the dot func is not this small */
733
0
        for (k = 0; k < x + u; k++) {
734
0
            index_y = 0;
735
0
            for (j = val_min; j < y; j++) {
736
0
                if ( htsc_getpoint(dig_grid, index_x, index_y) == -1 ) {
737
738
                    /* For the spot function we want to make sure that
739
                       we are properly adjusted to be in the range from
740
                       -1 to +1.  j and k are moving in the transformed
741
                       (skewed/rotated) space. First untransform the value */
742
0
                    vector_in.xy[0] = k + x_offset;
743
0
                    vector_in.xy[1] = j + y_offset;
744
0
                    htsc_matrix_vector_mult(trans_matrix, vector_in,
745
0
                                            &vector_out);
746
747
                    /* And so now we are in the range 0, 1 get us to -.5, .5 */
748
0
                    vector_out.xy[0] = 2.0 * vector_out.xy[0] - 1.0;
749
0
                    vector_out.xy[1] = 2.0 * vector_out.xy[1] - 1.0;
750
0
                    dist = htsc_spot_value(spot_type, vector_out.xy[0],
751
0
                                           vector_out.xy[1]);
752
0
                    if (dist > dot_search.norm) {
753
0
                        dot_search.norm = dist;
754
0
                        dot_search.index_x = index_x;
755
0
                        dot_search.index_y = index_y;
756
0
                    }
757
758
0
                }
759
0
                index_y++;
760
0
            }
761
0
            index_x++;
762
0
        }
763
        /* Assign the index for this position */
764
0
        htsc_setpoint(dig_grid, dot_search.index_x, dot_search.index_y,
765
0
                      dot_index);
766
0
        dot_index++;
767
0
        count++;
768
0
        if (count == N) {
769
0
            done = 1;
770
0
            break;
771
0
        }
772
773
        /* The ones position for the dig_grid is located at the first dot_search
774
           entry.  We need this later so grab it now */
775
0
        if (count == 1) {
776
0
            one_index->x = dot_search.index_x;
777
0
            one_index->y = dot_search.index_y;
778
0
        }
779
780
        /* Now search for the closest one to a vertex (of those remaining).
781
           and assign the current largest index */
782
0
        index_x = 0;
783
0
        dot_search.index_x = 0;
784
0
        dot_search.index_y = 0;
785
0
        dot_search.norm = 10000000000;  /* Or this large */
786
0
        for (k = 0; k < x + u; k++) {
787
0
            index_y = 0;
788
0
            for (j = val_min; j < y; j++) {
789
0
                if ( htsc_getpoint(dig_grid, index_x, index_y) == -1 ) {
790
791
                    /* For the spot function we want to make sure that
792
                       we are properly adjusted to be in the range from
793
                       -1 to +1.  j and k are moving in the transformed
794
                       (skewed/rotated) space. First untransform the value */
795
0
                    vector_in.xy[0] = k + x_offset;
796
0
                    vector_in.xy[1] = j + y_offset;
797
0
                    htsc_matrix_vector_mult(trans_matrix, vector_in,
798
0
                                            &vector_out);
799
800
                    /* And so now we are in the range 0, 1 get us to -.5, .5 */
801
0
                    vector_out.xy[0] = 2.0 * vector_out.xy[0] - 1.0;
802
0
                    vector_out.xy[1] = 2.0 * vector_out.xy[1] - 1.0;
803
0
                    dist = htsc_spot_value(spot_type, vector_out.xy[0],
804
0
                                           vector_out.xy[1]);
805
806
0
                    if (dist < dot_search.norm) {
807
0
                        dot_search.norm = dist;
808
0
                        dot_search.index_x = index_x;
809
0
                        dot_search.index_y = index_y;
810
0
                    }
811
0
                }
812
0
                index_y++;
813
0
            }
814
0
            index_x++;
815
0
        }
816
        /* Assign the index for this position */
817
0
        htsc_setpoint(dig_grid, dot_search.index_x, dot_search.index_y, hole_index);
818
0
        hole_index--;
819
0
        count++;
820
0
        if (count == N) {
821
0
            done = 1;
822
0
            break;
823
0
        }
824
0
    }
825
0
}
826
827
/* This creates a mask for creating the dot shape */
828
static int
829
htsc_create_dot_mask(htsc_dig_grid_t *dot_grid, int x, int y, int u, int v,
830
                 double screen_angle, htsc_vertices_t vertices)
831
0
{
832
0
    int k,j,val_min,index_x,index_y;
833
0
    double slope1, slope2;
834
0
    int t1,t2,t3,t4,point_in;
835
0
    double b3, b4;
836
0
    htsc_point_t test_point;
837
838
0
    if (screen_angle != 0) {
839
0
        slope1 = (double) y / (double) x;
840
0
        slope2 = (double) v / (double) u;
841
0
        val_min=MIN(0,v);
842
0
        dot_grid->height = abs(val_min) + y;
843
0
        dot_grid->width = x + u;
844
0
        if (check_int_multiply(dot_grid->height, dot_grid->width, &t1) < 0)
845
0
            return -1;
846
0
        if (check_int_multiply(t1, sizeof(int), &t1) < 0)
847
0
            return -1;
848
0
        dot_grid->data =
849
0
    (int *) ALLOC(dot_grid->memory, (size_t)dot_grid->height * dot_grid->width * sizeof(int));
850
0
        if (dot_grid->data == NULL)
851
0
            return -1;
852
0
        memset(dot_grid->data,0,(size_t)dot_grid->height * dot_grid->width * sizeof(int));
853
0
        index_x = 0;
854
0
        for (k = 0; k < (x+u); k++) {
855
0
            index_y=0;
856
0
            for (j = val_min; j < y; j++) {
857
0
                test_point.x = k + 0.5;
858
0
                test_point.y = j + 0.5;
859
                /* test 1 and 2 */
860
0
                t1 = (slope1 * test_point.x >= test_point.y);
861
0
                t2 = (slope2 * test_point.x <= test_point.y);
862
                /* test 3 */
863
0
                b3 = vertices.upper_left.y - slope2 * vertices.upper_left.x;
864
0
                t3 = ((slope2 * test_point.x + b3) > test_point.y);
865
                /* test 4 */
866
0
                b4 = vertices.lower_right.y - slope1 * vertices.lower_right.x;
867
0
                t4=((slope1 * test_point.x + b4) < test_point.y);
868
0
                point_in = (t1 && t2 && t3 && t4);
869
0
                if (point_in) {
870
0
                    htsc_setpoint(dot_grid, index_x, index_y, -1);
871
0
                }
872
0
                index_y++;
873
0
            }
874
0
            index_x++;
875
0
        }
876
0
    } else {
877
        /* All points are valid */
878
0
        dot_grid->height = y;
879
0
        dot_grid->width = u;
880
0
        if (check_int_multiply(y, u, &t1) < 0)
881
0
            return -1;
882
0
        if (check_int_multiply(sizeof(int), t1, &t1) < 0)
883
0
            return -1;
884
0
        dot_grid->data = (int *) ALLOC(dot_grid->memory, (size_t)y * u * sizeof(int));
885
0
        if (dot_grid->data == NULL)
886
0
            return -1;
887
0
        memset(dot_grid->data, -1, (size_t)y * u * sizeof(int));
888
0
        val_min = 0;
889
0
    }
890
0
    return 0;
891
0
}
892
893
static void
894
htsc_find_bin_center(htsc_dig_grid_t *dot_grid, htsc_vector_t *bin_center)
895
0
{
896
0
    int h = dot_grid->height;
897
0
    int w = dot_grid->width;
898
0
    int min_y = h + 1;
899
0
    int min_x = w + 1;
900
0
    int max_y = -1;
901
0
    int max_x = -1;
902
0
    int x, y;
903
904
0
    for (x = 0; x < w; x++) {
905
0
        for (y = 0; y < h; y++) {
906
0
            if (htsc_getpoint(dot_grid, x, y) == -1)
907
0
            {
908
0
                if (x < min_x)
909
0
                    min_x = x;
910
0
                if (x > max_x)
911
0
                    max_x = x;
912
0
                if (y < min_y)
913
0
                    min_y = y;
914
0
                if (y > max_y)
915
0
                    max_y = y;
916
0
            }
917
0
        }
918
0
    }
919
0
    bin_center->xy[0] = (max_x - min_x) / 2.0;
920
0
    bin_center->xy[1] = (max_y - min_y) / 2.0;
921
0
}
922
923
static int
924
htsc_getpoint(htsc_dig_grid_t *dig_grid, int x, int y)
925
0
{
926
0
    return dig_grid->data[ y * dig_grid->width + x];
927
0
}
928
929
static void
930
htsc_setpoint(htsc_dig_grid_t *dig_grid, int x, int y, int value)
931
0
{
932
0
    int kk = 0;;
933
0
    if (x < 0 || x > dig_grid->width-1 || y < 0 || y > dig_grid->height-1) {
934
0
        kk++;  /* Here to catch issues during debug */
935
0
    }
936
0
    dig_grid->data[ y * dig_grid->width + x] = value;
937
0
}
938
939
static int
940
htsc_sumsum(htsc_dig_grid_t dig_grid)
941
0
{
942
943
0
    int pos;
944
0
    int grid_size =  dig_grid.width *  dig_grid.height;
945
0
    int value = 0;
946
0
    int *ptr = dig_grid.data;
947
948
0
    for (pos = 0; pos < grid_size; pos++) {
949
0
        value += (*ptr++);
950
0
    }
951
0
    return value;
952
0
}
953
954
static int
955
htsc_gcd(int a, int b)
956
0
{
957
0
    if ( a == 0 && b == 0 ) return 0;
958
0
    if ( b == 0 ) return a;
959
0
    if ( a == 0 ) return b;
960
0
    while (1) {
961
0
        a = a % b;
962
0
        if (a == 0) {
963
0
            return b;
964
0
        }
965
0
        b = b % a;
966
0
        if (b == 0) {
967
0
            return a;
968
0
        }
969
0
    }
970
0
}
971
972
static int
973
htsc_lcm(int a, int b)
974
0
{
975
0
    int product;
976
0
    int gcd = htsc_gcd(a,b);
977
0
    int lcm;
978
979
0
    if (gcd == 0)
980
0
        return -1;
981
982
0
    if (check_int_multiply(a, b, &product) < 0)
983
0
        return -1;
984
985
0
    lcm = product/gcd;
986
0
    return lcm;
987
0
}
988
989
static int
990
htsc_matrix_inverse(htsc_matrix_t matrix_in, htsc_matrix_t *matrix_out)
991
0
{
992
0
    double determinant;
993
994
0
    determinant = matrix_in.row[0].xy[0] * matrix_in.row[1].xy[1] -
995
0
                  matrix_in.row[0].xy[1] * matrix_in.row[1].xy[0];
996
0
    if (determinant == 0)
997
0
        return -1;
998
0
    matrix_out->row[0].xy[0] = matrix_in.row[1].xy[1] / determinant;
999
0
    matrix_out->row[0].xy[1] = -matrix_in.row[0].xy[1] / determinant;
1000
0
    matrix_out->row[1].xy[0] = -matrix_in.row[1].xy[0] / determinant;
1001
0
    matrix_out->row[1].xy[1] = matrix_in.row[0].xy[0] / determinant;
1002
0
    return 0;
1003
0
}
1004
1005
static void
1006
htsc_matrix_vector_mult(htsc_matrix_t matrix_in, htsc_vector_t vector_in,
1007
                   htsc_vector_t *vector_out)
1008
0
{
1009
0
    vector_out->xy[0] = matrix_in.row[0].xy[0] * vector_in.xy[0] +
1010
0
                        matrix_in.row[0].xy[1] * vector_in.xy[1];
1011
0
    vector_out->xy[1] = matrix_in.row[1].xy[0] * vector_in.xy[0] +
1012
0
                        matrix_in.row[1].xy[1] * vector_in.xy[1];
1013
0
}
1014
1015
static int
1016
htsc_allocate_supercell(htsc_dig_grid_t *super_cell, int x, int y, int u,
1017
                        int v, int target_size, bool use_holladay_grid,
1018
                        htsc_dig_grid_t dot_grid, int N, int *S, int *H, int *L)
1019
0
{
1020
0
    htsc_matrix_t matrix;
1021
0
    htsc_matrix_t matrix_inv;
1022
0
    int code;
1023
0
    int k, j;
1024
0
    htsc_vector_t vector_in, m_and_n;
1025
0
    int Dfinal;
1026
0
    double diff_val[2];
1027
0
    double m_and_n_round;
1028
0
    int lcm_value;
1029
0
    int super_size_x, super_size_y;
1030
0
    int min_vert_number;
1031
0
    int a, b;
1032
1033
    /* Use Holladay Algorithm to create rectangular matrix for screening */
1034
0
    *H = htsc_gcd((int) abs(y), (int) abs(v));
1035
0
    if (*H == 0)
1036
0
        return -1;
1037
1038
0
    *L = N / *H;
1039
    /* Compute the shift factor */
1040
0
    matrix.row[0].xy[0] = x;
1041
0
    matrix.row[0].xy[1] = u;
1042
0
    matrix.row[1].xy[0] = y;
1043
0
    matrix.row[1].xy[1] = v;
1044
1045
0
    code = htsc_matrix_inverse(matrix, &matrix_inv);
1046
0
    if (code < 0) {
1047
0
        EPRINTF(dot_grid.memory, "ERROR! matrix singular!\n");
1048
0
        return -1;
1049
0
    }
1050
0
    vector_in.xy[1] = *H;
1051
0
    Dfinal = 0;
1052
0
    for (k = 1; k < *L+1; k++) {
1053
0
        vector_in.xy[0] = k;
1054
0
        htsc_matrix_vector_mult(matrix_inv, vector_in, &m_and_n);
1055
0
        for (j = 0; j < 2; j++) {
1056
0
            m_and_n_round = ROUND(m_and_n.xy[j]);
1057
0
            diff_val[j] = fabs((double) m_and_n.xy[j] -  (double) m_and_n_round);
1058
0
        }
1059
0
        if (diff_val[0] < 0.00000001 && diff_val[1] < 0.00000001) {
1060
0
            Dfinal = k;
1061
0
            break;
1062
0
        }
1063
0
    }
1064
0
    if (Dfinal == 0) {
1065
0
        EPRINTF(dot_grid.memory, "ERROR! computing Holladay Grid\n");
1066
0
        return -1;
1067
0
    }
1068
0
    *S = *L - Dfinal;
1069
  /* Make a large screen of multiple cells and then vary
1070
     the growth rate of the dots to get additional quantization levels
1071
     The macrocell must be H*a by L*b where a and b are integers due to the
1072
     periodicity of the screen. */
1073
1074
    /* To create the Holladay screen (no stocastic stuff),
1075
       select the size H and L to create the matrix i.e. super_size_x
1076
       and super_size_y need to be H by L at least and then just take an
1077
       H by L section. */
1078
    /* Force a periodicity in the screen to avoid the shift factor */
1079
0
    if (*S != 0) {
1080
0
        lcm_value = htsc_lcm(*L,*S);
1081
0
        if (lcm_value < 0)
1082
0
            return -1;
1083
1084
0
        min_vert_number = *H * lcm_value / *S;
1085
0
    } else {
1086
0
        lcm_value = *L;
1087
0
        min_vert_number = *H;
1088
0
    }
1089
1090
0
    a = (int)ceil((double) target_size / (double) lcm_value);
1091
0
    b = (int)ceil((double) target_size / (double) min_vert_number);
1092
1093
    /* super_cell Size is  b*min_vert_number by a*lcm_value
1094
       create the large cell */
1095
1096
0
    if (use_holladay_grid) {
1097
0
        super_size_x = MAX(*L, dot_grid.width);
1098
0
        super_size_y = MAX(*H, dot_grid.height);
1099
0
    } else {
1100
0
        super_size_x = a * lcm_value;
1101
0
        super_size_y = b * min_vert_number;
1102
0
    }
1103
0
    super_cell->height = super_size_y;
1104
0
    super_cell->width = super_size_x;
1105
0
    if (dot_grid.width > super_cell->width || dot_grid.height > super_cell->height)
1106
0
        return -1;
1107
0
    if (check_int_multiply(super_size_x, super_size_y, &a) < 0)
1108
0
        return -1;
1109
0
    if (check_int_multiply(sizeof(int), a, &a) < 0)
1110
0
        return -1;
1111
0
    super_cell->data =
1112
0
        (int *) ALLOC(dot_grid.memory, (size_t)super_size_x * super_size_y * sizeof(int));
1113
0
    if (super_cell->data == NULL)
1114
0
        return -1;
1115
0
    memset(super_cell->data, 0, (size_t)super_size_x * super_size_y * sizeof(int));
1116
0
    return 0;
1117
0
}
1118
1119
static void
1120
htsc_supercell_assign_point(int new_k, int new_j, int sc_xsize, int sc_ysize,
1121
                            htsc_dig_grid_t *super_cell, int val, int *num_set )
1122
0
{
1123
0
    if (new_k >= 0 && new_j >= 0 && new_k < sc_xsize && new_j < sc_ysize) {
1124
0
        if (htsc_getpoint(super_cell,new_k,new_j) == 0) {
1125
0
            htsc_setpoint(super_cell,new_k,new_j,val);
1126
0
            (*num_set)++;
1127
0
        }
1128
0
    }
1129
0
}
1130
1131
static void
1132
htsc_tile_supercell(htsc_dig_grid_t *super_cell, htsc_dig_grid_t *dot_grid,
1133
                 int x, int y, int u, int v, int N)
1134
0
{
1135
0
    int sc_ysize = super_cell->height;
1136
0
    int sc_xsize = super_cell->width;
1137
0
    int dot_ysize = dot_grid->height;
1138
0
    int dot_xsize = dot_grid->width;
1139
0
    int total_num = sc_ysize * sc_xsize;
1140
0
    bool done = false;
1141
0
    int k,j;
1142
0
    int new_k, new_j;
1143
0
    int num_set = 0;
1144
0
    int val;
1145
1146
0
    for (k = 0; k < dot_xsize; k++) {
1147
0
        for (j = 0; j < dot_ysize; j++) {
1148
0
            val = htsc_getpoint(dot_grid,k,j);
1149
0
            if (val > 0) {
1150
0
                htsc_setpoint(super_cell,k,j,val);
1151
0
                num_set++;
1152
0
            }
1153
0
        }
1154
0
    }
1155
0
    if (num_set == total_num) {
1156
0
            done = true;
1157
0
    }
1158
0
    while (!done) {
1159
0
        for (k = 0; k < sc_xsize; k++) {
1160
0
            for (j = 0; j < sc_ysize; j++) {
1161
0
                val = htsc_getpoint(super_cell,k,j);
1162
0
                if (val != 0) {
1163
0
                    new_k = k - x;
1164
0
                    new_j = j - y;
1165
0
                    htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize,
1166
0
                                                super_cell, val, &num_set);
1167
0
                    new_k = k + x;
1168
0
                    new_j = j + y;
1169
0
                    htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize,
1170
0
                                                super_cell, val, &num_set);
1171
0
                    new_k = k - u;
1172
0
                    new_j = j - v;
1173
0
                    htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize,
1174
0
                                                super_cell, val, &num_set);
1175
0
                    new_k = k + u;
1176
0
                    new_j = j + v;
1177
0
                    htsc_supercell_assign_point(new_k, new_j, sc_xsize, sc_ysize,
1178
0
                                                super_cell, val, &num_set);
1179
0
                }
1180
0
            }
1181
0
        }
1182
0
        if (num_set == total_num) {
1183
0
            done = true;
1184
0
        }
1185
0
    }
1186
0
}
1187
1188
/* Create 2d gaussian filter that varies with respect to coordinate
1189
   spatial resolution */
1190
int
1191
create_2d_gauss_filter(double *filter, int x_size, int y_size,
1192
    double stdvalx, double stdvaly, gs_memory_t *mem)
1193
0
{
1194
0
    int x_half_size  = (x_size-1)/2;
1195
0
    int y_half_size = (y_size-1)/2;
1196
0
    int k,j;
1197
0
    double arg, val;
1198
0
    double sum = 0;
1199
0
    double max_val = 0;
1200
0
    int total_size = x_size * y_size;
1201
0
    int index_x, index_y;
1202
0
    int code = 0;
1203
1204
0
    for (j = -y_half_size; j < y_half_size+1; j++) {
1205
0
        index_y = j + y_half_size;
1206
0
        for (k = -x_half_size; k < x_half_size+1; k++) {
1207
0
            arg   = -(k  * k / (stdvalx * stdvalx) +
1208
0
                      j * j / (stdvaly * stdvaly) ) /2;
1209
0
            val = exp(arg);
1210
0
            sum += val;
1211
0
            if (val > max_val) max_val = val;
1212
0
            index_x = k + x_half_size;
1213
0
            filter[index_y * x_size + index_x] = val;
1214
0
        }
1215
0
    }
1216
0
    if (sum) {
1217
0
        for (j = 0; j < total_size; j++) {
1218
0
            filter[j]/=sum;
1219
0
        }
1220
0
    }
1221
#if RAW_SCREEN_DUMP
1222
    code = htsc_dump_float_image(filter, y_size, x_size, max_val/sum, "guass_filt", mem);
1223
#endif
1224
0
    return code;
1225
0
}
1226
1227
/* 2-D convolution (or correlation) with periodic boundary condition */
1228
static void
1229
htsc_apply_filter(byte *screen_matrix, int num_cols_sc,
1230
                  int num_rows_sc, double *filter, int num_cols_filt,
1231
                  int num_rows_filt, double *screen_blur,
1232
    double *max_val, htsc_point_t *max_pos, double *min_val,
1233
                  htsc_point_t *min_pos)
1234
0
{
1235
0
    int k,j,kk,jj;
1236
0
    double sum;
1237
0
    int half_cols_filt = (num_cols_filt-1)/2;
1238
0
    int half_rows_filt = (num_rows_filt-1)/2;
1239
0
    int j_circ,k_circ;
1240
0
    double fmax_val = -1;
1241
0
    double fmin_val = 100000000;
1242
0
    htsc_point_t fmax_pos = { 0.0, 0.0 }, fmin_pos = { 0.0, 0.0 };
1243
1244
0
    for (j = 0; j < num_rows_sc; j++) {
1245
0
        for (k = 0; k < num_cols_sc; k++) {
1246
0
            sum = 0.0;
1247
0
            for (jj = -half_rows_filt; jj <= half_rows_filt; jj++) {
1248
0
                j_circ = j + jj;
1249
0
                if (j_circ < 0) {
1250
0
                    j_circ =
1251
0
                        (num_rows_sc - ((-j_circ) % num_rows_sc)) % num_rows_sc;
1252
0
                }
1253
0
                if (j_circ > (num_rows_sc - 1)) {
1254
0
                    j_circ = j_circ % num_rows_sc;
1255
0
                }
1256
                /* In case modulo is of a negative number */
1257
0
                if (j_circ < 0)
1258
0
                    j_circ = j_circ + num_rows_sc;
1259
0
                for (kk = -half_cols_filt; kk <= half_cols_filt; kk++) {
1260
0
                    k_circ = k + kk;
1261
0
                    if (k_circ < 0) {
1262
0
                        k_circ =
1263
0
                            (num_cols_sc - ((-k_circ) % num_cols_sc)) % num_cols_sc;
1264
0
                    }
1265
0
                    if (k_circ > (num_cols_sc - 1)) {
1266
0
                        k_circ = k_circ % num_cols_sc;
1267
0
                    }
1268
                    /* In case modulo is of a negative number */
1269
0
                    if (k_circ < 0)
1270
0
                        k_circ = k_circ + num_cols_sc;
1271
0
                    sum += (double) screen_matrix[k_circ + j_circ * num_cols_sc] *
1272
0
                            filter[ (jj + half_rows_filt) * num_cols_filt + (kk + half_cols_filt)];
1273
0
                }
1274
0
            }
1275
0
            screen_blur[j * num_cols_sc + k] = sum;
1276
0
            if (sum > fmax_val) {
1277
0
                fmax_val = sum;
1278
0
                fmax_pos.x = k;
1279
0
                fmax_pos.y = j;
1280
0
            }
1281
0
            if (sum < fmin_val) {
1282
0
                fmin_val = sum;
1283
0
                fmin_pos.x = k;
1284
0
                fmin_pos.y = j;
1285
0
            }
1286
0
        }
1287
0
    }
1288
0
    *max_val = fmax_val;
1289
0
    *min_val = fmin_val;
1290
0
    *max_pos = fmax_pos;
1291
0
    *min_pos = fmin_pos;
1292
0
}
1293
1294
static int
1295
htsc_add_dots(byte *screen_matrix, int num_cols, int num_rows,
1296
              double horiz_dpi, double vert_dpi, double lpi_act,
1297
              unsigned short *pos_x, unsigned short *pos_y,
1298
              int *locate, int num_dots,
1299
              htsc_dither_pos_t *dot_level_position, int level_num,
1300
              int num_dots_add, void *mem)
1301
0
{
1302
0
    double xscale = horiz_dpi / vert_dpi;
1303
0
    double sigma_y = vert_dpi / lpi_act;
1304
0
    double sigma_x = sigma_y * xscale;
1305
0
    int sizefiltx, sizefilty;
1306
0
    double *filter;
1307
0
    double *screen_blur;
1308
0
    int white_pos;
1309
0
    double max_val, min_val;
1310
0
    htsc_point_t max_pos, min_pos;
1311
0
    int k,j;
1312
0
    int dist, curr_dist;
1313
0
    htsc_dither_pos_t curr_position;
1314
0
    int code;
1315
1316
0
    sizefiltx = ROUND(sigma_x * 4);
1317
0
    sizefilty = ROUND(sigma_y * 4);
1318
0
    if ( ((double) sizefiltx / 2.0) == (sizefiltx >> 1)) {
1319
0
        sizefiltx += 1;
1320
0
    }
1321
0
    if ( ((double) sizefilty / 2.0) == (sizefilty >> 1)) {
1322
0
        sizefilty += 1;
1323
0
    }
1324
0
    if (sizeof(double) * sizefilty * sizefiltx > max_int)
1325
0
        return -1;
1326
1327
0
    filter = (double*) ALLOC(mem, sizeof(double) * sizefilty * sizefiltx);
1328
0
    if (filter == NULL)
1329
0
        return -1;
1330
0
    code = create_2d_gauss_filter(filter, sizefiltx, sizefilty, (double)sizefiltx, (double)sizefilty, mem);
1331
0
    if (code < 0)
1332
0
        return code;
1333
1334
0
    if (check_int_multiply(num_cols, num_rows, &j) < 0)
1335
0
        return -1;
1336
0
    if (check_int_multiply(sizeof(double), j, &j) < 0)
1337
0
        return -1;
1338
0
    screen_blur = (double*)ALLOC(mem, sizeof(double) * num_cols * num_rows);
1339
0
    if (screen_blur == NULL) {
1340
0
        FREE(mem, filter);
1341
0
        return -1;
1342
0
    }
1343
1344
0
    for (j = 0; j < num_dots_add; j++) {
1345
1346
        /* Perform the blur */
1347
0
        htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx,
1348
0
                  sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos);
1349
1350
        /* Find the closest OFF dot to the min position.  */
1351
0
        white_pos = 0;
1352
0
        dist = (num_cols) * (num_cols) + (num_rows) * (num_rows);
1353
0
        for (k = 0; k < num_dots; k++) {
1354
0
            curr_dist = (pos_y[k] - (int)min_pos.y) * (pos_y[k] - (int)min_pos.y) +
1355
0
                        (pos_x[k] - (int)min_pos.x) * (pos_x[k] - (int)min_pos.x);
1356
0
            if (curr_dist < dist &&
1357
0
                screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 0) {
1358
0
                white_pos = k;
1359
0
                dist = curr_dist;
1360
0
            }
1361
0
        }
1362
1363
        /* Set this dot to white */
1364
0
        screen_matrix[pos_x[white_pos] + num_cols * pos_y[white_pos]] = 1;
1365
1366
        /* Update position information */
1367
0
        curr_position = dot_level_position[level_num];
1368
0
        curr_position.point[j].x = pos_x[white_pos];
1369
0
        curr_position.point[j].y = pos_y[white_pos];
1370
0
        curr_position.locations[j] = locate[white_pos];
1371
0
    }
1372
0
    FREE(mem, filter);
1373
0
    FREE(mem, screen_blur);
1374
0
    return 0;
1375
0
}
1376
1377
static int
1378
htsc_init_dot_position(byte *screen_matrix, int num_cols, int num_rows,
1379
                       double horiz_dpi, double vert_dpi, double lpi_act,
1380
                       unsigned short *pos_x, unsigned short *pos_y, int num_dots,
1381
                       htsc_dither_pos_t *dot_level_position, void *mem)
1382
0
{
1383
0
    double xscale = horiz_dpi / vert_dpi;
1384
0
    double sigma_y = vert_dpi / lpi_act;
1385
0
    double sigma_x = sigma_y * xscale;
1386
0
    int sizefiltx, sizefilty;
1387
0
    double *filter;
1388
0
    bool done = false;
1389
0
    double *screen_blur;
1390
0
    int white_pos, black_pos;
1391
0
    double max_val, min_val;
1392
0
    htsc_point_t max_pos, min_pos;
1393
0
    int k;
1394
0
    int dist, curr_dist;
1395
0
    bool found_it;
1396
0
    int code = 0;
1397
1398
0
    sizefiltx = ROUND(sigma_x * 4);
1399
0
    sizefilty = ROUND(sigma_y * 4);
1400
0
    if ( ((double) sizefiltx / 2.0) == (sizefiltx >> 1)) {
1401
0
        sizefiltx += 1;
1402
0
    }
1403
0
    if ( ((double) sizefilty / 2.0) == (sizefilty >> 1)) {
1404
0
        sizefilty += 1;
1405
0
    }
1406
0
    if (sizeof(double) * sizefilty * sizefiltx > max_int)
1407
0
        return -1;
1408
0
    filter = (double*) ALLOC(mem, sizeof(double) * sizefilty * sizefiltx);
1409
0
    if (filter == NULL)
1410
0
        return -1;
1411
0
    code = create_2d_gauss_filter(filter, sizefiltx, sizefilty, (double)sizefiltx, (double)sizefilty, mem);
1412
0
    if (code < 0)
1413
0
        return code;
1414
1415
0
    if (check_int_multiply(num_cols, num_rows, &k) < 0)
1416
0
        return -1;
1417
0
    if (check_int_multiply(sizeof(double), k, &k) < 0)
1418
0
        return -1;
1419
0
    screen_blur = (double*) ALLOC(mem, sizeof(double) * num_cols * num_rows);
1420
0
    if (screen_blur == NULL) {
1421
0
        FREE(mem, filter);
1422
0
        return -1;
1423
0
    }
1424
    /* Start moving dots until the whitest and darkest dot are the same */
1425
0
    while (!done) {
1426
        /* Blur */
1427
0
        htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx,
1428
0
                  sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos);
1429
#if RAW_SCREEN_DUMP
1430
        code = htsc_dump_float_image(screen_blur, num_cols, num_rows, max_val, "blur_one", mem);
1431
        if (code < 0)
1432
            return code;
1433
#endif
1434
    /* Find the closest on dot to the max position */
1435
0
        black_pos = 0;
1436
0
        dist = (pos_y[0] - (int)max_pos.y) * (pos_y[0] - (int)max_pos.y) +
1437
0
               (pos_x[0] - (int)max_pos.x) * (pos_x[0] - (int)max_pos.x);
1438
0
        for ( k = 1; k < num_dots; k++) {
1439
0
            curr_dist = (pos_y[k] - (int)max_pos.y) * (pos_y[k] - (int)max_pos.y) +
1440
0
                        (pos_x[k] - (int)max_pos.x) * (pos_x[k] - (int)max_pos.x);
1441
0
            if (curr_dist < dist &&
1442
0
                screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 1) {
1443
0
                black_pos = k;
1444
0
                dist = curr_dist;
1445
0
            }
1446
0
        }
1447
        /* Set this dot to black */
1448
0
        screen_matrix[pos_x[black_pos] + num_cols * pos_y[black_pos]] = 0;
1449
        /* Blur again */
1450
0
        htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx,
1451
0
                  sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos);
1452
        /* Find the closest OFF dot to the min position. */
1453
0
        white_pos = 0;
1454
0
        dist = (pos_y[0] - (int)min_pos.y) * (pos_y[0] - (int)min_pos.y) +
1455
0
               (pos_x[0] - (int)min_pos.x) * (pos_x[0] - (int)min_pos.x);
1456
0
        for ( k = 1; k < num_dots; k++) {
1457
0
            curr_dist = (pos_y[k] - (int)min_pos.y) * (pos_y[k] - (int)min_pos.y) +
1458
0
                        (pos_x[k] - (int)min_pos.x) * (pos_x[k] - (int)min_pos.x);
1459
0
            if (curr_dist < dist &&
1460
0
                screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 0) {
1461
0
                white_pos = k;
1462
0
                dist = curr_dist;
1463
0
            }
1464
0
        }
1465
        /* Set this dot to white */
1466
0
        screen_matrix[pos_x[white_pos] + num_cols * pos_y[white_pos]] = 1;
1467
        /* If it is the same dot as before, then we are done */
1468
        /* There could be a danger here of cycles longer than 2 */
1469
0
        if (white_pos == black_pos) {
1470
0
            done = true;
1471
0
            FREE(mem, screen_blur);
1472
0
            FREE(mem, filter);
1473
0
            return 0;
1474
0
        } else {
1475
            /* We need to update our dot position information */
1476
            /* find where the old white one was and replace it */
1477
0
            found_it = false;
1478
0
            for (k = 0; k < dot_level_position->number_points; k++) {
1479
0
                if (dot_level_position->point[k].x == pos_x[black_pos] &&
1480
0
                    dot_level_position->point[k].y == pos_y[black_pos]) {
1481
0
                    found_it = true;
1482
0
                    dot_level_position->point[k].x = pos_x[white_pos];
1483
0
                    dot_level_position->point[k].y = pos_y[white_pos];
1484
0
                    dot_level_position->locations[k] =
1485
0
                        pos_x[white_pos] + pos_y[white_pos] * num_cols;
1486
0
                    break;
1487
0
                }
1488
0
            }
1489
0
            if (!found_it) {
1490
0
                EPRINTF(mem, "ERROR! bug in dot location accounting\n");
1491
0
                FREE(mem, filter);
1492
0
                FREE(mem, screen_blur);
1493
0
                return -1;
1494
0
            }
1495
0
        }
1496
0
    }
1497
0
    FREE(mem, filter);
1498
0
    FREE(mem, screen_blur);
1499
0
    return 0;
1500
0
}
1501
1502
static int
1503
htsc_create_dither_mask(htsc_dig_grid_t super_cell, htsc_dig_grid_t *final_mask,
1504
                          int verbose, int num_levels, int y, int x, double vert_dpi,
1505
                          double horiz_dpi, int N, double gamma,
1506
                          htsc_dig_grid_t dot_grid, htsc_point_t dot_grid_one_index,
1507
                          gs_memory_t *mem)
1508
0
{
1509
0
    int *dot_levels = NULL;
1510
0
    int *locate = NULL;
1511
0
    double percent, perc_val;
1512
0
    int code = 0, k, j, h, jj, mm;
1513
0
    int width_supercell = super_cell.width;
1514
0
    int height_supercell = super_cell.height;
1515
0
    int number_points = width_supercell * height_supercell;
1516
0
    int num_dots = 0;
1517
0
    double step_size;
1518
0
    int curr_size;
1519
0
    int rand_pos, dots_needed;
1520
0
    int done;
1521
0
    double lpi_act;
1522
0
    double vert_scale = ((double) y / vert_dpi);
1523
0
    double horiz_scale = ((double) x / horiz_dpi);
1524
0
    byte *screen_matrix = NULL;
1525
0
    unsigned short *pos_x = NULL, *pos_y = NULL;
1526
0
    htsc_dither_pos_t *dot_level_pos = NULL, *curr_dot_level = NULL;
1527
0
    int count;
1528
0
    int prev_dot_level, curr_num_dots;
1529
0
    double mag_offset, temp_dbl;
1530
0
    int *thresholds = NULL, val;
1531
0
    int j_index, k_index, threshold_value;
1532
0
    int *dot_level_sort = NULL;
1533
0
    bool found;
1534
1535
0
    lpi_act = 1.0/((double) sqrt( vert_scale * vert_scale +
1536
0
                                  horiz_scale * horiz_scale));
1537
0
    if (num_levels > 1) {
1538
0
        curr_size = MAX(height_supercell, width_supercell);
1539
0
        if (check_int_multiply(curr_size, 2, &curr_size) < 0) {
1540
0
            code = -1;
1541
0
            goto out;
1542
0
        }
1543
0
        if (check_int_multiply(curr_size, sizeof(int), &count) < 0) {
1544
0
            code = -1;
1545
0
            goto out;
1546
0
        }
1547
0
        locate = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_size);
1548
0
        if (locate == NULL) {
1549
0
            code = -1;
1550
0
            goto out;
1551
0
        }
1552
0
        if (check_int_multiply(number_points, sizeof(byte), &count) < 0) {
1553
0
            code = -1;
1554
0
            goto out;
1555
0
        }
1556
0
        screen_matrix = (byte*) ALLOC(dot_grid.memory, sizeof(byte) * number_points);
1557
0
        if (screen_matrix == NULL) {
1558
0
            code = -1;
1559
0
            goto out;
1560
0
        }
1561
0
        memset(screen_matrix, 0, sizeof(byte) * number_points);
1562
1563
        /* Determine the number of dots in the screen and their index */
1564
0
        for (j = 0; j < number_points; j++) {
1565
0
            if (super_cell.data[j] == 1) {
1566
0
                locate[num_dots] = j;
1567
0
                num_dots++;
1568
0
                if (num_dots == (curr_size - 1)) {
1569
0
                    int *tmp = locate;
1570
1571
0
                    if (check_int_multiply(curr_size, 2, &curr_size) < 0) {
1572
0
                        code = -1;
1573
0
                        goto out;
1574
0
                    }
1575
0
                    if (check_int_multiply(curr_size, sizeof(int), &count) < 0) {
1576
0
                        code = -1;
1577
0
                        goto out;
1578
0
                    }
1579
0
                    locate = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_size);
1580
0
                    if (locate == NULL) {
1581
0
                        code = -1;
1582
0
                        goto out;
1583
0
                    }
1584
0
                    memcpy(locate, tmp, sizeof(int) * (num_dots+1));
1585
0
                    FREE(dot_grid.memory, tmp);
1586
0
                }
1587
0
            }
1588
0
        }
1589
1590
       /* Convert the 1-D locate positions to 2-D positions so that we can
1591
          use a distance metric to the dot center locations. Also allocate
1592
          the structure for our dot positioning information */
1593
0
        if (check_int_multiply(sizeof(unsigned short), num_dots, &count) < 0) {
1594
0
            code = -1;
1595
0
            goto out;
1596
0
        }
1597
0
        pos_x = (unsigned short*) ALLOC(dot_grid.memory, sizeof(unsigned short) * num_dots);
1598
0
        if (pos_x == NULL) {
1599
0
            code = -1;
1600
0
            goto out;
1601
0
        }
1602
0
        pos_y = (unsigned short*) ALLOC(dot_grid.memory, sizeof(unsigned short) * num_dots);
1603
0
        if (pos_y == NULL) {
1604
0
            code = -1;
1605
0
            goto out;
1606
0
        }
1607
0
        for (k = 0; k < num_dots; k++) {
1608
0
            pos_x[k] = locate[k] % width_supercell;
1609
0
            pos_y[k] = (locate[k] - pos_x[k]) / width_supercell;
1610
0
        }
1611
1612
        /* Note that number of quantization levels is not tied to number of dots
1613
           in the macro screen.  */
1614
0
        if (check_int_multiply(sizeof(htsc_dither_pos_t), num_levels, &count) < 0) {
1615
0
            code = -1;
1616
0
            goto out;
1617
0
        }
1618
0
        dot_level_pos =
1619
0
            (htsc_dither_pos_t*) ALLOC(dot_grid.memory, sizeof(htsc_dither_pos_t) * num_levels);
1620
0
        if (dot_level_pos == NULL) {
1621
0
            code = -1;
1622
0
            goto out;
1623
0
        }
1624
0
        if (check_int_multiply(sizeof(int), num_levels, &count) < 0) {
1625
0
            code = -1;
1626
0
            goto out;
1627
0
        }
1628
0
        dot_levels = (int*) ALLOC(dot_grid.memory, sizeof(int) * num_levels);
1629
0
        if (dot_levels == NULL) {
1630
0
            code = -1;
1631
0
            goto out;
1632
0
        }
1633
0
        percent = 1.0 / ((double)num_levels + 1.0);
1634
0
        prev_dot_level = 0;
1635
0
        for (k = 0; k < num_levels; k++) {
1636
0
            perc_val = (k + 1) * percent;
1637
0
            dot_levels[k] = ROUND(num_dots * perc_val);
1638
0
            curr_num_dots = dot_levels[k] -prev_dot_level;
1639
0
            prev_dot_level = dot_levels[k];
1640
0
            if (check_int_multiply(sizeof(int), curr_num_dots, &count) < 0) {
1641
0
                code = -1;
1642
0
                goto out;
1643
0
            }
1644
0
            dot_level_pos[k].locations = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_num_dots);
1645
0
            if (dot_level_pos[k].locations == NULL) {
1646
0
                code = -1;
1647
0
                goto out;
1648
0
            }
1649
0
            if (check_int_multiply(sizeof(htsc_point_t), curr_num_dots, &count) < 0) {
1650
0
                code = -1;
1651
0
                goto out;
1652
0
            }
1653
0
            dot_level_pos[k].point =
1654
0
                (htsc_point_t*) ALLOC(dot_grid.memory, sizeof(htsc_point_t) * curr_num_dots);
1655
0
            if (dot_level_pos[k].point == NULL) {
1656
0
                code = -1;
1657
0
                goto out;
1658
0
            }
1659
0
            dot_level_pos[k].number_points = curr_num_dots;
1660
0
        }
1661
1662
        /* An initial random location for the first level  */
1663
0
        dots_needed = dot_levels[0];
1664
0
        count = 0;
1665
0
        if (dots_needed > 0) {
1666
0
            done = 0;
1667
0
            while (!done) {
1668
0
                rand_pos = ROUND((num_dots-1) * (double) rand() / (double) RAND_MAX);
1669
0
                if (screen_matrix[locate[rand_pos]] != 1) {
1670
0
                    screen_matrix[locate[rand_pos]] = 1;
1671
0
                    dot_level_pos->locations[count] = locate[rand_pos];
1672
0
                    dot_level_pos->point[count].x = pos_x[rand_pos];
1673
0
                    dot_level_pos->point[count].y = pos_y[rand_pos];
1674
0
                    dots_needed--;
1675
0
                    count++;
1676
0
                }
1677
0
                if (dots_needed == 0) {
1678
0
                    done = 1;
1679
0
                }
1680
0
            }
1681
0
        }
1682
#if RAW_SCREEN_DUMP
1683
        code  = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell,
1684
                             1, "screen0_init", mem);
1685
        if (code < 0)
1686
            goto out;
1687
#endif
1688
        /* Rearrange these dots into a pleasing pattern, but only if there is
1689
        * more than 1.  Otherwise there are none to move */
1690
0
        if (dot_levels[0] > 1)
1691
0
            code = htsc_init_dot_position(screen_matrix, width_supercell,
1692
0
                               height_supercell, horiz_dpi, vert_dpi, lpi_act,
1693
0
                               pos_x, pos_y, num_dots, dot_level_pos, final_mask->memory);
1694
0
        if (code < 0)
1695
0
            goto out;
1696
#if RAW_SCREEN_DUMP
1697
        code  = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell,
1698
                             1, "screen0_arrange", mem);
1699
        if (code < 0)
1700
            goto out;
1701
#endif
1702
        /* Now we want to introduce more dots at each level */
1703
0
        for (k = 1; k < num_levels; k++) {
1704
0
            code = htsc_add_dots(screen_matrix, width_supercell, height_supercell,
1705
0
                          horiz_dpi, vert_dpi, lpi_act, pos_x, pos_y, locate,
1706
0
                          num_dots, dot_level_pos, k,
1707
0
                          dot_level_pos[k].number_points, final_mask->memory);
1708
0
        if (code < 0)
1709
0
            goto out;
1710
#if RAW_SCREEN_DUMP
1711
            {
1712
            char str_name[30];
1713
            snprintf(str_name, 30, "screen%d_arrange",k);
1714
            code = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell,
1715
                                 1, str_name, mem);
1716
            if (code < 0)
1717
                goto out;
1718
            }
1719
#endif
1720
0
        }
1721
1722
0
        if (verbose > 0)
1723
0
            PRINTF(final_mask->memory, "\n--Dot Positions--\n");
1724
0
        for (k = 0; k < num_levels; k++) {
1725
0
            if (verbose > 0)
1726
0
                PRINTF2(final_mask->memory, "dot_level_pos %d: number_points = %d\n",
1727
0
                        k, dot_level_pos[k].number_points);
1728
0
            for (j = 0; j < dot_level_pos[k].number_points; j++) {
1729
0
                if (verbose > 0)
1730
0
                    PRINTF4(final_mask->memory, "\tpoint: %d: locations = %d x = %3.2lf y = %3.2lf\n",
1731
0
                           j, dot_level_pos[k].locations[j], dot_level_pos[k].point[j].x, dot_level_pos[k].point[j].y);
1732
0
            }
1733
0
        }
1734
1735
        /* Create the threshold mask. */
1736
0
        step_size = (MAXVAL + 1.0) / (double) N;
1737
0
        if (check_int_multiply(sizeof(int), N, &count) < 0) {
1738
0
            code = -1;
1739
0
            goto out;
1740
0
        }
1741
0
        thresholds = (int*) ALLOC(dot_grid.memory, sizeof(int) * N);
1742
0
        if (thresholds == NULL) {
1743
0
            code = -1;
1744
0
            goto out;
1745
0
        }
1746
0
        for (k = 0; k < N; k++) {
1747
0
            thresholds[N-1-k] = (int)((k + 1) * step_size - (step_size / 2));
1748
0
        }
1749
0
        mag_offset =
1750
0
            (double) (thresholds[0]-thresholds[1]) / (double) (num_levels+1);
1751
0
        if ( gamma != 1.0) {
1752
0
            for (k = 0; k < N; k++) {
1753
0
                temp_dbl =
1754
0
                    (double) pow((double) thresholds[k] / MAXVAL,
1755
0
                                 (double) gamma);
1756
0
                thresholds[k] = ROUND(temp_dbl * MAXVAL);
1757
0
            }
1758
0
        }
1759
1760
        /* Now use the indices from the large screen to look up the mask and
1761
           apply the offset to the threshold values to dither the rate at which
1762
           the dots turn on */
1763
        /* allocate the mask */
1764
0
        final_mask->height = height_supercell;
1765
0
        final_mask->width = width_supercell;
1766
0
        if (check_int_multiply(height_supercell, width_supercell, &count) < 0) {
1767
0
            code = -1;
1768
0
            goto out;
1769
0
        }
1770
0
        final_mask->data =
1771
0
            (int*) ALLOC(dot_grid.memory, sizeof(int) * height_supercell * width_supercell);
1772
0
        if (final_mask->data == NULL) {
1773
0
            code = -1;
1774
0
            goto out;
1775
0
        }
1776
1777
        /* We need to associate the locate index with a particular level
1778
           for the when the dot begins to turn on.  Go through the dot_level_pos
1779
           array to get the values.  Probably should create this earlier and avoid
1780
           this */
1781
0
        if (check_int_multiply(sizeof(int), num_dots, &count) < 0) {
1782
0
            code = -1;
1783
0
            goto out;
1784
0
        }
1785
0
        dot_level_sort = (int*) ALLOC(dot_grid.memory, sizeof(int) * num_dots);
1786
0
        if (dot_level_sort == NULL) {
1787
0
            code = -1;
1788
0
            goto out;
1789
0
        }
1790
0
        for (h = 0; h < num_dots; h++) {
1791
0
            found = false;
1792
0
            for (jj = 0; jj < num_levels; jj++) {
1793
0
                curr_dot_level = &(dot_level_pos[jj]);
1794
0
                for (mm = 0; mm < curr_dot_level->number_points; mm++) {
1795
0
                    if (pos_x[h] == curr_dot_level->point[mm].x &&
1796
0
                        pos_y[h] == curr_dot_level->point[mm].y ) {
1797
0
                        found = true;
1798
0
                        dot_level_sort[h] = jj + 1;
1799
0
                        break;  /* Break from position search(within level) */
1800
0
                    }
1801
0
                }
1802
0
                if (found == true) { /* break from level search */
1803
0
                    break;
1804
0
                }
1805
0
            }
1806
0
            if (found == false) {
1807
0
                dot_level_sort[h] = 0;
1808
0
            }
1809
0
        }
1810
1811
0
        for (h = 0; h < num_dots; h++) {
1812
0
            for (j = 0; j < dot_grid.height; j++) {
1813
0
                for (k = 0; k < dot_grid.width; k++) {
1814
0
                    val = htsc_getpoint(&dot_grid, k, j);
1815
0
                    if (val != 0) {
1816
1817
                        /* Assign a offset threshold values */
1818
0
                        j_index =
1819
0
                            (pos_y[h] + j - (int) dot_grid_one_index.y) % height_supercell;
1820
1821
                        /* In case we have modulo of a negative number */
1822
0
                        if (j_index < 0) j_index = j_index + height_supercell;
1823
0
                        k_index =
1824
0
                            (pos_x[h] + k - (int) dot_grid_one_index.x) % width_supercell;
1825
1826
                        /* In case we have modulo of a negative number */
1827
0
                        if (k_index < 0) k_index = k_index + width_supercell;
1828
0
                        threshold_value = (int)(thresholds[val-1] +
1829
0
                                                mag_offset * dot_level_sort[h]);
1830
0
                        if (threshold_value > MAXVAL) threshold_value = (int)MAXVAL;
1831
0
                        if (threshold_value < 0) threshold_value = 0;
1832
0
                        htsc_setpoint(final_mask,k_index,j_index,threshold_value);
1833
0
                    }
1834
0
                }
1835
0
            }
1836
0
        }
1837
0
out:
1838
0
        if (dot_level_pos) {
1839
0
            for (k = 0; k < num_levels; k++) {
1840
0
                FREE(dot_grid.memory, dot_level_pos[k].locations);
1841
0
                FREE(dot_grid.memory, dot_level_pos[k].point);
1842
0
            }
1843
0
        }
1844
0
        FREE(dot_grid.memory, locate);
1845
0
        FREE(dot_grid.memory, screen_matrix);
1846
0
        FREE(dot_grid.memory, pos_x);
1847
0
        FREE(dot_grid.memory, pos_y);
1848
0
        FREE(dot_grid.memory, dot_level_pos);
1849
0
        FREE(dot_grid.memory, dot_levels);
1850
0
        FREE(dot_grid.memory, thresholds);
1851
0
        FREE(dot_grid.memory, dot_level_sort);
1852
0
    }
1853
0
    return code;
1854
0
}
1855
1856
static int
1857
htsc_create_holladay_mask(htsc_dig_grid_t super_cell, int H, int L,
1858
                          double gamma, htsc_dig_grid_t *final_mask)
1859
0
{
1860
0
    double step_size = (MAXVAL + 1.0) /( (double) H * (double) L);
1861
0
    int k, j, code = 0;
1862
0
    double *thresholds = NULL;
1863
0
    int number_points = H * L;
1864
0
    double half_step = step_size / 2.0;
1865
0
    double temp;
1866
0
    int index_point;
1867
0
    int value;
1868
0
    double white_scale = 253.0 / 255.0; /* To ensure white is white */
1869
1870
0
    final_mask->height = H;
1871
0
    final_mask->width = L;
1872
0
    if (check_int_multiply(H, L, &value) < 0) {
1873
0
        code = -1;
1874
0
        goto out;
1875
0
    }
1876
0
    if (check_int_multiply(sizeof(int), value, &value) < 0) {
1877
0
        code = -1;
1878
0
        goto out;
1879
0
    }
1880
0
    final_mask->data = (int *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(int));
1881
0
    if (final_mask->data == NULL) {
1882
0
        code = -1;
1883
0
        goto out;
1884
0
    }
1885
1886
0
    if (check_int_multiply(H, L, &value) < 0) {
1887
0
        code = -1;
1888
0
        goto out;
1889
0
    }
1890
0
    if (check_int_multiply(sizeof(double), value, &value) < 0) {
1891
0
        code = -1;
1892
0
        goto out;
1893
0
    }
1894
0
    thresholds = (double *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(double));
1895
0
    if (thresholds == NULL) {
1896
0
        code = -1;
1897
0
        goto out;
1898
0
    }
1899
0
    for (k = 0; k < number_points; k++) {
1900
0
         temp = ((k+1) * step_size - half_step) / MAXVAL;
1901
0
         if ( gamma != 1.0) {
1902
             /* Possible linearization */
1903
0
            temp = (double) pow((double) temp, (double) gamma);
1904
0
         }
1905
0
         thresholds[number_points - k - 1] =
1906
0
                                    ROUND(temp * MAXVAL * white_scale + 1);
1907
0
    }
1908
0
    memset(final_mask->data, 0, (size_t)H * L * sizeof(int));
1909
1910
0
    for (j = 0; j < H; j++) {
1911
0
        for (k = 0; k < L; k++) {
1912
0
            index_point = htsc_getpoint(&super_cell,k,j) - 1;
1913
0
            value = (int) floor(thresholds[index_point]);
1914
0
            htsc_setpoint(final_mask,k,j,value);
1915
0
        }
1916
0
    }
1917
0
out:
1918
0
    FREE(final_mask->memory, thresholds);
1919
0
    return code;
1920
0
}
1921
1922
static int
1923
htsc_create_nondithered_mask(htsc_dig_grid_t super_cell, int H, int L,
1924
                          double gamma, htsc_dig_grid_t *final_mask)
1925
0
{
1926
0
    double step_size = (MAXVAL +  1) /( (double) H * (double) L);
1927
0
    int k, j, code = 0;
1928
0
    double *thresholds = NULL;
1929
0
    int number_points = H * L;
1930
0
    double half_step = step_size / 2.0;
1931
0
    double temp;
1932
0
    int index_point;
1933
0
    int value;
1934
0
    double white_scale = 253.0 / 255.0; /* To ensure white is white */
1935
1936
0
    final_mask->height = super_cell.height;
1937
0
    final_mask->width = super_cell.width;
1938
0
    if (check_int_multiply(super_cell.height, super_cell.width, &value) < 0) {
1939
0
        code = -1;
1940
0
        goto out;
1941
0
    }
1942
0
    if (check_int_multiply(sizeof(int), value, &value) < 0) {
1943
0
        code = -1;
1944
0
        goto out;
1945
0
    }
1946
0
    final_mask->data = (int *) ALLOC(final_mask->memory,
1947
0
             (size_t)super_cell.height * super_cell.width *
1948
0
                                      sizeof(int));
1949
0
    if (final_mask->data == NULL) {
1950
0
        code = -1;
1951
0
        goto out;
1952
0
    }
1953
0
    if (check_int_multiply(H, L, &value) < 0) {
1954
0
        code = -1;
1955
0
        goto out;
1956
0
    }
1957
0
    if (check_int_multiply(sizeof(double), value, &value) < 0) {
1958
0
        code = -1;
1959
0
        goto out;
1960
0
    }
1961
0
    thresholds = (double *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(double));
1962
0
    if (thresholds == NULL) {
1963
0
        code = -1;
1964
0
        goto out;
1965
0
    }
1966
0
    for (k = 0; k < number_points; k++) {
1967
0
         temp = ((k+1) * step_size - half_step) / MAXVAL;
1968
0
         if ( gamma != 1.0) {
1969
             /* Possible linearization */
1970
0
            temp = (double) pow((double) temp, (double) gamma);
1971
0
         }
1972
0
         thresholds[number_points - k - 1] =
1973
0
                                    ROUND(temp * MAXVAL * white_scale + 1);
1974
0
    }
1975
0
    memset(final_mask->data, 0, (size_t)super_cell.height * super_cell.width *
1976
0
                                sizeof(int));
1977
0
    for (j = 0; j < super_cell.height; j++) {
1978
0
        for (k = 0; k < super_cell.width; k++) {
1979
0
            index_point = htsc_getpoint(&super_cell,k,j) - 1;
1980
0
            value = (int) floor(thresholds[index_point]);
1981
0
            htsc_setpoint(final_mask,k,j,value);
1982
0
        }
1983
0
    }
1984
0
out:
1985
0
    FREE(final_mask->memory, thresholds);
1986
0
    return code;
1987
0
}
1988
1989
/* Various spot functions */
1990
static double
1991
htsc_spot_circle(double x, double y)
1992
0
{
1993
0
    return 1.0 - (x*x + y*y);
1994
0
}
1995
1996
static double
1997
htsc_spot_redbook(double x, double y)
1998
0
{
1999
0
    return (180.0 * (double) cos(x) + 180.0 * (double) cos(y)) / 2.0;
2000
0
}
2001
2002
static double
2003
htsc_spot_inverted_round(double x, double y)
2004
0
{
2005
0
    return (x*x + y*y) - 1.0;
2006
0
}
2007
2008
static double
2009
htsc_spot_rhomboid(double x, double y)
2010
0
{
2011
0
    return 1.0 - ((double) fabs(y) * 0.8 + (double) fabs(x)) / 2.0;
2012
0
}
2013
2014
static double
2015
htsc_spot_linex(double x, double y)
2016
0
{
2017
0
    return 1.0 - (double) fabs(y);
2018
0
}
2019
2020
static double
2021
htsc_spot_liney(double x, double y)
2022
0
{
2023
0
    return 1.0 - (double) fabs(x);
2024
0
}
2025
2026
static double
2027
htsc_spot_diamond(double x, double y)
2028
0
{
2029
0
    double abs_y = (double) fabs(y);
2030
0
    double abs_x = (double) fabs(x);
2031
2032
0
    if ((abs_y + abs_x) <= 0.75) {
2033
0
        return 1.0 - (abs_x * abs_x + abs_y * abs_y);
2034
0
    } else {
2035
0
        if ((abs_y + abs_x) <= 1.23) {
2036
0
            return 1.0 - (0.76  * abs_y + abs_x);
2037
0
        } else {
2038
0
            return ((abs_x - 1.0) * (abs_x - 1.0) +
2039
0
                    (abs_y - 1.0) * (abs_y - 1.0)) - 1.0;
2040
0
        }
2041
0
    }
2042
0
}
2043
2044
static double
2045
htsc_spot_diamond2(double x, double y)
2046
0
{
2047
0
    double xy = (double) fabs(x) + (double) fabs(y);
2048
2049
0
    if (xy <= 1.0) {
2050
0
        return 1.0 - xy * xy / 2.0;
2051
0
    } else {
2052
0
        return 1.0 - (2.0 * xy * xy - 4.0 * (xy - 1.0) * (xy - 1.0)) / 4.0;
2053
0
    }
2054
0
}
2055
2056
static double
2057
htsc_spot_roundspot(double x, double y)
2058
0
{
2059
0
    double xy = (double)fabs(x) + (double)fabs(y);
2060
2061
0
    if (xy <= 1.0) {
2062
0
        return 1.0 - (x*x + y*y);
2063
0
    } else {
2064
0
        return ((fabs(x) - 1.0) * (fabs(x) - 1.0)) + ((fabs(y) - 1.0) * (fabs(y) - 1.0)) - 1.0;
2065
0
    }
2066
0
}
2067
2068
static double
2069
htsc_spot_value(spottype_t spot_type, double x, double y)
2070
0
{
2071
0
    switch (spot_type) {
2072
0
        case CIRCLE:
2073
0
            return htsc_spot_circle(x,y);
2074
0
        case REDBOOK:
2075
0
            return htsc_spot_redbook(x,y);
2076
0
        case INVERTED:
2077
0
            return htsc_spot_inverted_round(x,y);
2078
0
        case RHOMBOID:
2079
0
            return htsc_spot_rhomboid(x,y);
2080
0
        case LINE_X:
2081
0
            return htsc_spot_linex(x,y);
2082
0
        case LINE_Y:
2083
0
            return htsc_spot_liney(x,y);
2084
0
        case DIAMOND1:
2085
0
            return htsc_spot_diamond(x,y);
2086
0
        case DIAMOND2:
2087
0
            return htsc_spot_diamond2(x,y);
2088
0
        case ROUNDSPOT:
2089
0
            return htsc_spot_roundspot(x,y);
2090
0
        case CUSTOM:  /* A spot (pun intended) for users to define their own */
2091
0
            return htsc_spot_circle(x,y);
2092
0
        default:
2093
0
            return htsc_spot_circle(x,y);
2094
0
    }
2095
0
}
2096
2097
#if FINAL_SCREEN_DUMP
2098
2099
/* Save turn on order list, Assume that htsc_gen_ordered has already converted to TOS */
2100
static int
2101
htsc_save_tos(htsc_dig_grid_t *final_mask, gs_memory_t *mem)
2102
{
2103
    int width = final_mask->width;
2104
    int height = final_mask->height;
2105
    int *buff_ptr;
2106
    _FILE *fid;
2107
    int k = 0;
2108
    int count = height * width;
2109
2110
    fid = FOPEN(mem, "turn_on_seq.out", "w");
2111
    if (fid == NULL)
2112
        return -1;
2113
2114
    FPRINTF2(fid, "# W=%d H=%d\n", width, height);
2115
2116
    /* Write out */
2117
    buff_ptr = final_mask->data;
2118
    for (k = 0; k < count; k++) {
2119
        FPRINTF2(fid, "%d\t%d\n", *buff_ptr++, *buff_ptr++);
2120
    }
2121
    FCLOSE(fid);
2122
    return 0;
2123
}
2124
2125
int
2126
htsc_save_screen(htsc_dig_grid_t *final_mask, bool use_holladay_grid, int S,
2127
    htsc_param_t params, gs_memory_t *mem)
2128
{
2129
    char full_file_name[FULL_FILE_NAME_LENGTH];
2130
    _FILE *fid;
2131
    int x, y, code = 0;
2132
    int *buff_ptr = final_mask->data;
2133
    int width = final_mask->width;
2134
    int height = final_mask->height;
2135
    byte data;
2136
    unsigned short data_short;
2137
    output_format_type output_format = params.output_format;
2138
    char *output_extension = (output_format == OUTPUT_PS) ? "ps" :
2139
        ((output_format == OUTPUT_PPM) ? "ppm" :
2140
            ((output_format == OUTPUT_RAW16 ? "16.raw" : "raw")));
2141
2142
    if (output_format == OUTPUT_TOS) {
2143
        /* We need to figure out the turn-on sequence from the threshold
2144
           array */
2145
        code = htsc_save_tos(final_mask, mem);
2146
    } else {
2147
        if (use_holladay_grid) {
2148
            snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_Holladay_Shift%d_%dx%d.%s", S, width,
2149
                height, output_extension);
2150
        } else {
2151
            snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_Dithered_%dx%d.%s", width, height,
2152
                output_extension);
2153
        }
2154
        fid = FOPEN(mem, full_file_name, "wb");
2155
        if (fid == NULL)
2156
            return -1;
2157
2158
        if (output_format == OUTPUT_PPM)
2159
            FPRINTF6(fid, "P5\n"
2160
                "# Halftone threshold array, %s, [%d, %d], S=%d\n"
2161
                "%d %d\n"
2162
                "255\n",
2163
                use_holladay_grid ? "Holladay_Shift" : "Dithered", width, height,
2164
                S, width, height);
2165
        if (output_format != OUTPUT_PS) {
2166
            /* Both BIN and PPM format write the same binary data */
2167
            if (output_format == OUTPUT_RAW || output_format == OUTPUT_PPM) {
2168
                for (y = 0; y < height; y++) {
2169
                    for (x = 0; x < width; x++) {
2170
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2171
                        data_short = ROUND((double)data_short * 255.0 / MAXVAL);
2172
                        if (data_short > 255) data_short = 255;
2173
                        data = (byte)(data_short & 0xff);
2174
                        FWRITE(&data, sizeof(byte), 1, fid);
2175
                        buff_ptr++;
2176
                    }
2177
                }
2178
            } else {  /* raw16 data */
2179
                for (y = 0; y < height; y++) {
2180
                    for (x = 0; x < width; x++) {
2181
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2182
                        FWRITE(&data_short, sizeof(short), 1, fid);
2183
                        buff_ptr++;
2184
                    }
2185
                }
2186
            }
2187
        } else {  /* ps output format */
2188
            if (params.targ_quant <= 256) {
2189
                /* Output PS HalftoneType 3 dictionary */
2190
                FPRINTF2(fid, "%%!PS\n"
2191
                    "<< /HalftoneType 3\n"
2192
                    "   /Width  %d\n"
2193
                    "   /Height %d\n"
2194
                    "   /Thresholds <\n",
2195
                    width, height);
2196
2197
                for (y = 0; y < height; y++) {
2198
                    for (x = 0; x < width; x++) {
2199
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2200
                        data_short = ROUND((double)data_short * 255.0 / MAXVAL);
2201
                        if (data_short > 255) data_short = 255;
2202
                        data = (byte)(data_short & 0xff);
2203
                        FPRINTF1(fid, "%02x", data);
2204
                        buff_ptr++;
2205
                        if ((x & 0x1f) == 0x1f && (x != (width - 1)))
2206
                            FPRINTF(fid, "\n");
2207
                    }
2208
                    FPRINTF(fid, "\n");
2209
                }
2210
                FPRINTF(fid, "   >\n"
2211
                    ">>\n"
2212
                );
2213
            } else {
2214
                /* Output PS HalftoneType 16 dictionary. Single array. */
2215
                FPRINTF(fid, "%%!PS\n"
2216
                    "%% Create a 'filter' from local hex data\n"
2217
                    "{ currentfile /ASCIIHexDecode filter /ReusableStreamDecode filter } exec\n");
2218
                /* hex data follows, 'file' object will be left on stack */
2219
                for (y = 0; y < height; y++) {
2220
                    for (x = 0; x < width; x++) {
2221
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2222
                        FPRINTF1(fid, "%04x", data_short);
2223
                        buff_ptr++;
2224
                        if ((x & 0x1f) == 0x1f && (x != (width - 1)))
2225
                            FPRINTF(fid, "\n");
2226
                    }
2227
                    FPRINTF(fid, "\n"); /* end of one row */
2228
                }
2229
                FPRINTF(fid, ">\n");  /* ASCIIHexDecode EOF */
2230
                FPRINTF2(fid,
2231
                    "<< /Thresholds 2 index    %% file object for the 16-bit data\n"
2232
                    "   /HalftoneType 16\n"
2233
                    "   /Width  %d\n"
2234
                    "   /Height %d\n"
2235
                    ">>\n"
2236
                    "exch pop     %% discard ResuableStreamDecode file leaving the Halftone dict.\n",
2237
                    width, height);
2238
            }
2239
        }
2240
        FCLOSE(fid);
2241
    }
2242
    return code;
2243
}
2244
#endif
2245
2246
#if RAW_SCREEN_DUMP
2247
static int
2248
htsc_dump_screen(htsc_dig_grid_t *dig_grid, char filename[], gs_memory_t *mem)
2249
{
2250
    char full_file_name[FULL_FILE_NAME_LENGTH];
2251
    _FILE *fid;
2252
    int x,y;
2253
    int *buff_ptr = dig_grid->data;
2254
    int width = dig_grid->width;
2255
    int height = dig_grid->height;
2256
    byte data[3];
2257
2258
    snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_%s_%dx%dx3.raw",filename,width,height);
2259
    fid = FOPEN(mem, full_file_name,"wb");
2260
    if (fid == NULL)
2261
        return -1;
2262
2263
    for (y = 0; y < height; y++) {
2264
        for ( x = 0; x < width; x++ ) {
2265
            if (*buff_ptr < 0) {
2266
                data[0] = 255;
2267
                data[1] = 0;
2268
                data[2] = 0;
2269
            } else if (*buff_ptr > 255) {
2270
                data[0] = 0;
2271
                data[1] = 255;
2272
                data[2] = 0;
2273
            } else {
2274
                data[0] = *buff_ptr;
2275
                data[1] = *buff_ptr;
2276
                data[2] = *buff_ptr;
2277
            }
2278
            FWRITE(data,sizeof(unsigned char),3,fid);
2279
            buff_ptr++;
2280
        }
2281
    }
2282
    FCLOSE(fid);
2283
    return 0;
2284
}
2285
2286
static int
2287
htsc_dump_float_image(double *image, int height, int width, double max_val,
2288
                      char filename[], gs_memory_t *mem)
2289
{
2290
    char full_file_name[FULL_FILE_NAME_LENGTH];
2291
    _FILE *fid;
2292
    int x,y;
2293
    int data;
2294
    byte data_byte;
2295
2296
    snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Float_%s_%dx%d.raw",filename,width,height);
2297
    fid = FOPEN(mem, full_file_name,"wb");
2298
    if (fid == NULL)
2299
        return -1;
2300
2301
    for (y = 0; y < height; y++) {
2302
        for ( x = 0; x < width; x++ ) {
2303
            data = (255.0 * image[x + y * width] / max_val);
2304
            if (data > 255) data = 255;
2305
            if (data < 0) data = 0;
2306
            data_byte = data;
2307
            FWRITE(&data_byte,sizeof(byte),1,fid);
2308
        }
2309
    }
2310
    FCLOSE(fid);
2311
    return 0;
2312
}
2313
2314
static int
2315
htsc_dump_byte_image(byte *image, int height, int width, double max_val,
2316
                      char filename[], gs_memory_t *mem)
2317
{
2318
    char full_file_name[FULL_FILE_NAME_LENGTH];
2319
    _FILE *fid;
2320
    int x,y;
2321
    int data;
2322
    byte data_byte;
2323
2324
    snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "ByteScaled_%s_%dx%d.raw",filename,width,height);
2325
    fid = FOPEN(mem, full_file_name,"wb");
2326
    if (fid == NULL)
2327
        return -1;
2328
2329
    for (y = 0; y < height; y++) {
2330
        for ( x = 0; x < width; x++ ) {
2331
            data = (255.0 * image[x + y * width] / max_val);
2332
            if (data > 255) data = 255;
2333
            if (data < 0) data = 0;
2334
            data_byte = data;
2335
            FWRITE(&data_byte,sizeof(byte),1,fid);
2336
        }
2337
    }
2338
    FCLOSE(fid);
2339
    return 0;
2340
}
2341
#endif