Coverage Report

Created: 2025-06-24 07:01

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