Coverage Report

Created: 2025-08-28 07:06

/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
    if (sum) {
1191
0
        for (j = 0; j < total_size; j++) {
1192
0
            filter[j]/=sum;
1193
0
        }
1194
0
    }
1195
#if RAW_SCREEN_DUMP
1196
    code = htsc_dump_float_image(filter, y_size, x_size, max_val/sum, "guass_filt", mem);
1197
#endif
1198
0
    return code;
1199
0
}
1200
1201
/* 2-D convolution (or correlation) with periodic boundary condition */
1202
static void
1203
htsc_apply_filter(byte *screen_matrix, int num_cols_sc,
1204
                  int num_rows_sc, double *filter, int num_cols_filt,
1205
                  int num_rows_filt, double *screen_blur,
1206
    double *max_val, htsc_point_t *max_pos, double *min_val,
1207
                  htsc_point_t *min_pos)
1208
0
{
1209
0
    int k,j,kk,jj;
1210
0
    double sum;
1211
0
    int half_cols_filt = (num_cols_filt-1)/2;
1212
0
    int half_rows_filt = (num_rows_filt-1)/2;
1213
0
    int j_circ,k_circ;
1214
0
    double fmax_val = -1;
1215
0
    double fmin_val = 100000000;
1216
0
    htsc_point_t fmax_pos = { 0.0, 0.0 }, fmin_pos = { 0.0, 0.0 };
1217
1218
0
    for (j = 0; j < num_rows_sc; j++) {
1219
0
        for (k = 0; k < num_cols_sc; k++) {
1220
0
            sum = 0.0;
1221
0
            for (jj = -half_rows_filt; jj <= half_rows_filt; jj++) {
1222
0
                j_circ = j + jj;
1223
0
                if (j_circ < 0) {
1224
0
                    j_circ =
1225
0
                        (num_rows_sc - ((-j_circ) % num_rows_sc)) % num_rows_sc;
1226
0
                }
1227
0
                if (j_circ > (num_rows_sc - 1)) {
1228
0
                    j_circ = j_circ % num_rows_sc;
1229
0
                }
1230
                /* In case modulo is of a negative number */
1231
0
                if (j_circ < 0)
1232
0
                    j_circ = j_circ + num_rows_sc;
1233
0
                for (kk = -half_cols_filt; kk <= half_cols_filt; kk++) {
1234
0
                    k_circ = k + kk;
1235
0
                    if (k_circ < 0) {
1236
0
                        k_circ =
1237
0
                            (num_cols_sc - ((-k_circ) % num_cols_sc)) % num_cols_sc;
1238
0
                    }
1239
0
                    if (k_circ > (num_cols_sc - 1)) {
1240
0
                        k_circ = k_circ % num_cols_sc;
1241
0
                    }
1242
                    /* In case modulo is of a negative number */
1243
0
                    if (k_circ < 0)
1244
0
                        k_circ = k_circ + num_cols_sc;
1245
0
                    sum += (double) screen_matrix[k_circ + j_circ * num_cols_sc] *
1246
0
                            filter[ (jj + half_rows_filt) * num_cols_filt + (kk + half_cols_filt)];
1247
0
                }
1248
0
            }
1249
0
            screen_blur[j * num_cols_sc + k] = sum;
1250
0
            if (sum > fmax_val) {
1251
0
                fmax_val = sum;
1252
0
                fmax_pos.x = k;
1253
0
                fmax_pos.y = j;
1254
0
            }
1255
0
            if (sum < fmin_val) {
1256
0
                fmin_val = sum;
1257
0
                fmin_pos.x = k;
1258
0
                fmin_pos.y = j;
1259
0
            }
1260
0
        }
1261
0
    }
1262
0
    *max_val = fmax_val;
1263
0
    *min_val = fmin_val;
1264
0
    *max_pos = fmax_pos;
1265
0
    *min_pos = fmin_pos;
1266
0
}
1267
1268
static int
1269
htsc_add_dots(byte *screen_matrix, int num_cols, int num_rows,
1270
              double horiz_dpi, double vert_dpi, double lpi_act,
1271
              unsigned short *pos_x, unsigned short *pos_y,
1272
              int *locate, int num_dots,
1273
              htsc_dither_pos_t *dot_level_position, int level_num,
1274
              int num_dots_add, void *mem)
1275
0
{
1276
0
    double xscale = horiz_dpi / vert_dpi;
1277
0
    double sigma_y = vert_dpi / lpi_act;
1278
0
    double sigma_x = sigma_y * xscale;
1279
0
    int sizefiltx, sizefilty;
1280
0
    double *filter;
1281
0
    double *screen_blur;
1282
0
    int white_pos;
1283
0
    double max_val, min_val;
1284
0
    htsc_point_t max_pos, min_pos;
1285
0
    int k,j;
1286
0
    int dist, curr_dist;
1287
0
    htsc_dither_pos_t curr_position;
1288
0
    int code;
1289
1290
0
    sizefiltx = ROUND(sigma_x * 4);
1291
0
    sizefilty = ROUND(sigma_y * 4);
1292
0
    if ( ((double) sizefiltx / 2.0) == (sizefiltx >> 1)) {
1293
0
        sizefiltx += 1;
1294
0
    }
1295
0
    if ( ((double) sizefilty / 2.0) == (sizefilty >> 1)) {
1296
0
        sizefilty += 1;
1297
0
    }
1298
0
    filter = (double*) ALLOC(mem, sizeof(double) * sizefilty * sizefiltx);
1299
0
    if (filter == NULL)
1300
0
        return -1;
1301
0
    code = create_2d_gauss_filter(filter, sizefiltx, sizefilty, (double)sizefiltx, (double)sizefilty, mem);
1302
0
    if (code < 0)
1303
0
        return code;
1304
1305
0
    screen_blur = (double*)ALLOC(mem, sizeof(double) * num_cols * num_rows);
1306
0
    if (screen_blur == NULL) {
1307
0
        FREE(mem, filter);
1308
0
        return -1;
1309
0
    }
1310
1311
0
    for (j = 0; j < num_dots_add; j++) {
1312
1313
        /* Perform the blur */
1314
0
        htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx,
1315
0
                  sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos);
1316
1317
        /* Find the closest OFF dot to the min position.  */
1318
0
        white_pos = 0;
1319
0
        dist = (num_cols) * (num_cols) + (num_rows) * (num_rows);
1320
0
        for (k = 0; k < num_dots; k++) {
1321
0
            curr_dist = (pos_y[k] - (int)min_pos.y) * (pos_y[k] - (int)min_pos.y) +
1322
0
                        (pos_x[k] - (int)min_pos.x) * (pos_x[k] - (int)min_pos.x);
1323
0
            if (curr_dist < dist &&
1324
0
                screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 0) {
1325
0
                white_pos = k;
1326
0
                dist = curr_dist;
1327
0
            }
1328
0
        }
1329
1330
        /* Set this dot to white */
1331
0
        screen_matrix[pos_x[white_pos] + num_cols * pos_y[white_pos]] = 1;
1332
1333
        /* Update position information */
1334
0
        curr_position = dot_level_position[level_num];
1335
0
        curr_position.point[j].x = pos_x[white_pos];
1336
0
        curr_position.point[j].y = pos_y[white_pos];
1337
0
        curr_position.locations[j] = locate[white_pos];
1338
0
    }
1339
0
    FREE(mem, filter);
1340
0
    FREE(mem, screen_blur);
1341
0
    return 0;
1342
0
}
1343
1344
static int
1345
htsc_init_dot_position(byte *screen_matrix, int num_cols, int num_rows,
1346
                       double horiz_dpi, double vert_dpi, double lpi_act,
1347
                       unsigned short *pos_x, unsigned short *pos_y, int num_dots,
1348
                       htsc_dither_pos_t *dot_level_position, void *mem)
1349
0
{
1350
0
    double xscale = horiz_dpi / vert_dpi;
1351
0
    double sigma_y = vert_dpi / lpi_act;
1352
0
    double sigma_x = sigma_y * xscale;
1353
0
    int sizefiltx, sizefilty;
1354
0
    double *filter;
1355
0
    bool done = false;
1356
0
    double *screen_blur;
1357
0
    int white_pos, black_pos;
1358
0
    double max_val, min_val;
1359
0
    htsc_point_t max_pos, min_pos;
1360
0
    int k;
1361
0
    int dist, curr_dist;
1362
0
    bool found_it;
1363
0
    int code = 0;
1364
1365
0
    sizefiltx = ROUND(sigma_x * 4);
1366
0
    sizefilty = ROUND(sigma_y * 4);
1367
0
    if ( ((double) sizefiltx / 2.0) == (sizefiltx >> 1)) {
1368
0
        sizefiltx += 1;
1369
0
    }
1370
0
    if ( ((double) sizefilty / 2.0) == (sizefilty >> 1)) {
1371
0
        sizefilty += 1;
1372
0
    }
1373
0
    filter = (double*) ALLOC(mem, sizeof(double) * sizefilty * sizefiltx);
1374
0
    if (filter == NULL)
1375
0
        return -1;
1376
0
    code = create_2d_gauss_filter(filter, sizefiltx, sizefilty, (double)sizefiltx, (double)sizefilty, mem);
1377
0
    if (code < 0)
1378
0
        return code;
1379
1380
0
    screen_blur = (double*) ALLOC(mem, sizeof(double) * num_cols * num_rows);
1381
0
    if (screen_blur == NULL) {
1382
0
        FREE(mem, filter);
1383
0
        return -1;
1384
0
    }
1385
    /* Start moving dots until the whitest and darkest dot are the same */
1386
0
    while (!done) {
1387
        /* Blur */
1388
0
        htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx,
1389
0
                  sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos);
1390
#if RAW_SCREEN_DUMP
1391
        code = htsc_dump_float_image(screen_blur, num_cols, num_rows, max_val, "blur_one", mem);
1392
        if (code < 0)
1393
            return code;
1394
#endif
1395
    /* Find the closest on dot to the max position */
1396
0
        black_pos = 0;
1397
0
        dist = (pos_y[0] - (int)max_pos.y) * (pos_y[0] - (int)max_pos.y) +
1398
0
               (pos_x[0] - (int)max_pos.x) * (pos_x[0] - (int)max_pos.x);
1399
0
        for ( k = 1; k < num_dots; k++) {
1400
0
            curr_dist = (pos_y[k] - (int)max_pos.y) * (pos_y[k] - (int)max_pos.y) +
1401
0
                        (pos_x[k] - (int)max_pos.x) * (pos_x[k] - (int)max_pos.x);
1402
0
            if (curr_dist < dist &&
1403
0
                screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 1) {
1404
0
                black_pos = k;
1405
0
                dist = curr_dist;
1406
0
            }
1407
0
        }
1408
        /* Set this dot to black */
1409
0
        screen_matrix[pos_x[black_pos] + num_cols * pos_y[black_pos]] = 0;
1410
        /* Blur again */
1411
0
        htsc_apply_filter(screen_matrix, num_cols, num_rows, filter, sizefiltx,
1412
0
                  sizefilty, screen_blur, &max_val, &max_pos, &min_val, &min_pos);
1413
        /* Find the closest OFF dot to the min position. */
1414
0
        white_pos = 0;
1415
0
        dist = (pos_y[0] - (int)min_pos.y) * (pos_y[0] - (int)min_pos.y) +
1416
0
               (pos_x[0] - (int)min_pos.x) * (pos_x[0] - (int)min_pos.x);
1417
0
        for ( k = 1; k < num_dots; k++) {
1418
0
            curr_dist = (pos_y[k] - (int)min_pos.y) * (pos_y[k] - (int)min_pos.y) +
1419
0
                        (pos_x[k] - (int)min_pos.x) * (pos_x[k] - (int)min_pos.x);
1420
0
            if (curr_dist < dist &&
1421
0
                screen_matrix[pos_x[k] + num_cols * pos_y[k]] == 0) {
1422
0
                white_pos = k;
1423
0
                dist = curr_dist;
1424
0
            }
1425
0
        }
1426
        /* Set this dot to white */
1427
0
        screen_matrix[pos_x[white_pos] + num_cols * pos_y[white_pos]] = 1;
1428
        /* If it is the same dot as before, then we are done */
1429
        /* There could be a danger here of cycles longer than 2 */
1430
0
        if (white_pos == black_pos) {
1431
0
            done = true;
1432
0
            FREE(mem, screen_blur);
1433
0
            FREE(mem, filter);
1434
0
            return 0;
1435
0
        } else {
1436
            /* We need to update our dot position information */
1437
            /* find where the old white one was and replace it */
1438
0
            found_it = false;
1439
0
            for (k = 0; k < dot_level_position->number_points; k++) {
1440
0
                if (dot_level_position->point[k].x == pos_x[black_pos] &&
1441
0
                    dot_level_position->point[k].y == pos_y[black_pos]) {
1442
0
                    found_it = true;
1443
0
                    dot_level_position->point[k].x = pos_x[white_pos];
1444
0
                    dot_level_position->point[k].y = pos_y[white_pos];
1445
0
                    dot_level_position->locations[k] =
1446
0
                        pos_x[white_pos] + pos_y[white_pos] * num_cols;
1447
0
                    break;
1448
0
                }
1449
0
            }
1450
0
            if (!found_it) {
1451
0
                EPRINTF(mem, "ERROR! bug in dot location accounting\n");
1452
0
                FREE(mem, filter);
1453
0
                FREE(mem, screen_blur);
1454
0
                return -1;
1455
0
            }
1456
0
        }
1457
0
    }
1458
0
    FREE(mem, filter);
1459
0
    FREE(mem, screen_blur);
1460
0
    return 0;
1461
0
}
1462
1463
static int
1464
htsc_create_dither_mask(htsc_dig_grid_t super_cell, htsc_dig_grid_t *final_mask,
1465
                          int verbose, int num_levels, int y, int x, double vert_dpi,
1466
                          double horiz_dpi, int N, double gamma,
1467
                          htsc_dig_grid_t dot_grid, htsc_point_t dot_grid_one_index,
1468
                          gs_memory_t *mem)
1469
0
{
1470
0
    int *dot_levels = NULL;
1471
0
    int *locate = NULL;
1472
0
    double percent, perc_val;
1473
0
    int code = 0, k, j, h, jj, mm;
1474
0
    int width_supercell = super_cell.width;
1475
0
    int height_supercell = super_cell.height;
1476
0
    int number_points = width_supercell * height_supercell;
1477
0
    int num_dots = 0;
1478
0
    double step_size;
1479
0
    int curr_size;
1480
0
    int rand_pos, dots_needed;
1481
0
    int done;
1482
0
    double lpi_act;
1483
0
    double vert_scale = ((double) y / vert_dpi);
1484
0
    double horiz_scale = ((double) x / horiz_dpi);
1485
0
    byte *screen_matrix = NULL;
1486
0
    unsigned short *pos_x = NULL, *pos_y = NULL;
1487
0
    htsc_dither_pos_t *dot_level_pos = NULL, *curr_dot_level = NULL;
1488
0
    int count;
1489
0
    int prev_dot_level, curr_num_dots;
1490
0
    double mag_offset, temp_dbl;
1491
0
    int *thresholds = NULL, val;
1492
0
    int j_index, k_index, threshold_value;
1493
0
    int *dot_level_sort = NULL;
1494
0
    bool found;
1495
1496
0
    lpi_act = 1.0/((double) sqrt( vert_scale * vert_scale +
1497
0
                                  horiz_scale * horiz_scale));
1498
0
    if (num_levels > 1) {
1499
0
        curr_size = 2 * MAX(height_supercell, width_supercell);
1500
0
        locate = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_size);
1501
0
        if (locate == NULL) {
1502
0
            code = -1;
1503
0
            goto out;
1504
0
        }
1505
0
        screen_matrix = (byte*) ALLOC(dot_grid.memory, sizeof(byte) * number_points);
1506
0
        if (screen_matrix == NULL) {
1507
0
            code = -1;
1508
0
            goto out;
1509
0
        }
1510
0
        memset(screen_matrix, 0, sizeof(byte) * number_points);
1511
1512
        /* Determine the number of dots in the screen and their index */
1513
0
        for (j = 0; j < number_points; j++) {
1514
0
            if (super_cell.data[j] == 1) {
1515
0
                locate[num_dots] = j;
1516
0
                num_dots++;
1517
0
                if (num_dots == (curr_size - 1)) {
1518
0
                    int *tmp = locate;
1519
1520
0
                    curr_size = curr_size * 2;
1521
0
                    locate = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_size);
1522
0
                    if (locate == NULL) {
1523
0
                        code = -1;
1524
0
                        goto out;
1525
0
                    }
1526
0
                    memcpy(locate, tmp, sizeof(int) * (num_dots+1));
1527
0
                    FREE(dot_grid.memory, tmp);
1528
0
                }
1529
0
            }
1530
0
        }
1531
1532
       /* Convert the 1-D locate positions to 2-D positions so that we can
1533
          use a distance metric to the dot center locations. Also allocate
1534
          the structure for our dot positioning information */
1535
0
        pos_x = (unsigned short*) ALLOC(dot_grid.memory, sizeof(unsigned short) * num_dots);
1536
0
        if (pos_x == NULL) {
1537
0
            code = -1;
1538
0
            goto out;
1539
0
        }
1540
0
        pos_y = (unsigned short*) ALLOC(dot_grid.memory, sizeof(unsigned short) * num_dots);
1541
0
        if (pos_y == NULL) {
1542
0
            code = -1;
1543
0
            goto out;
1544
0
        }
1545
0
        for (k = 0; k < num_dots; k++) {
1546
0
            pos_x[k] = locate[k] % width_supercell;
1547
0
            pos_y[k] = (locate[k] - pos_x[k]) / width_supercell;
1548
0
        }
1549
1550
        /* Note that number of quantization levels is not tied to number of dots
1551
           in the macro screen.  */
1552
0
        dot_level_pos =
1553
0
            (htsc_dither_pos_t*) ALLOC(dot_grid.memory, sizeof(htsc_dither_pos_t) * num_levels);
1554
0
        if (dot_level_pos == NULL) {
1555
0
            code = -1;
1556
0
            goto out;
1557
0
        }
1558
0
        dot_levels = (int*) ALLOC(dot_grid.memory, sizeof(int) * num_levels);
1559
0
        if (dot_levels == NULL) {
1560
0
            code = -1;
1561
0
            goto out;
1562
0
        }
1563
0
        percent = 1.0 / ((double)num_levels + 1.0);
1564
0
        prev_dot_level = 0;
1565
0
        for (k = 0; k < num_levels; k++) {
1566
0
            perc_val = (k + 1) * percent;
1567
0
            dot_levels[k] = ROUND(num_dots * perc_val);
1568
0
            curr_num_dots = dot_levels[k] -prev_dot_level;
1569
0
            prev_dot_level = dot_levels[k];
1570
0
            dot_level_pos[k].locations = (int*) ALLOC(dot_grid.memory, sizeof(int) * curr_num_dots);
1571
0
            if (dot_level_pos[k].locations == NULL) {
1572
0
                code = -1;
1573
0
                goto out;
1574
0
            }
1575
0
            dot_level_pos[k].point =
1576
0
                (htsc_point_t*) ALLOC(dot_grid.memory, sizeof(htsc_point_t) * curr_num_dots);
1577
0
            if (dot_level_pos[k].point == NULL) {
1578
0
                code = -1;
1579
0
                goto out;
1580
0
            }
1581
0
            dot_level_pos[k].number_points = curr_num_dots;
1582
0
        }
1583
1584
        /* An initial random location for the first level  */
1585
0
        dots_needed = dot_levels[0];
1586
0
        count = 0;
1587
0
        if (dots_needed > 0) {
1588
0
            done = 0;
1589
0
            while (!done) {
1590
0
                rand_pos = ROUND((num_dots-1) * (double) rand() / (double) RAND_MAX);
1591
0
                if (screen_matrix[locate[rand_pos]] != 1) {
1592
0
                    screen_matrix[locate[rand_pos]] = 1;
1593
0
                    dot_level_pos->locations[count] = locate[rand_pos];
1594
0
                    dot_level_pos->point[count].x = pos_x[rand_pos];
1595
0
                    dot_level_pos->point[count].y = pos_y[rand_pos];
1596
0
                    dots_needed--;
1597
0
                    count++;
1598
0
                }
1599
0
                if (dots_needed == 0) {
1600
0
                    done = 1;
1601
0
                }
1602
0
            }
1603
0
        }
1604
#if RAW_SCREEN_DUMP
1605
        code  = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell,
1606
                             1, "screen0_init", mem);
1607
        if (code < 0)
1608
            goto out;
1609
#endif
1610
        /* Rearrange these dots into a pleasing pattern, but only if there is
1611
        * more than 1.  Otherwise there are none to move */
1612
0
        if (dot_levels[0] > 1)
1613
0
            code = htsc_init_dot_position(screen_matrix, width_supercell,
1614
0
                               height_supercell, horiz_dpi, vert_dpi, lpi_act,
1615
0
                               pos_x, pos_y, num_dots, dot_level_pos, final_mask->memory);
1616
0
        if (code < 0)
1617
0
            goto out;
1618
#if RAW_SCREEN_DUMP
1619
        code  = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell,
1620
                             1, "screen0_arrange", mem);
1621
        if (code < 0)
1622
            goto out;
1623
#endif
1624
        /* Now we want to introduce more dots at each level */
1625
0
        for (k = 1; k < num_levels; k++) {
1626
0
            code = htsc_add_dots(screen_matrix, width_supercell, height_supercell,
1627
0
                          horiz_dpi, vert_dpi, lpi_act, pos_x, pos_y, locate,
1628
0
                          num_dots, dot_level_pos, k,
1629
0
                          dot_level_pos[k].number_points, final_mask->memory);
1630
0
        if (code < 0)
1631
0
            goto out;
1632
#if RAW_SCREEN_DUMP
1633
            {
1634
            char str_name[30];
1635
            snprintf(str_name, 30, "screen%d_arrange",k);
1636
            code = htsc_dump_byte_image(screen_matrix, width_supercell, height_supercell,
1637
                                 1, str_name, mem);
1638
            if (code < 0)
1639
                goto out;
1640
            }
1641
#endif
1642
0
        }
1643
1644
0
        if (verbose > 0)
1645
0
            PRINTF(final_mask->memory, "\n--Dot Positions--\n");
1646
0
        for (k = 0; k < num_levels; k++) {
1647
0
            if (verbose > 0)
1648
0
                PRINTF2(final_mask->memory, "dot_level_pos %d: number_points = %d\n",
1649
0
                        k, dot_level_pos[k].number_points);
1650
0
            for (j = 0; j < dot_level_pos[k].number_points; j++) {
1651
0
                if (verbose > 0)
1652
0
                    PRINTF4(final_mask->memory, "\tpoint: %d: locations = %d x = %3.2lf y = %3.2lf\n",
1653
0
                           j, dot_level_pos[k].locations[j], dot_level_pos[k].point[j].x, dot_level_pos[k].point[j].y);
1654
0
            }
1655
0
        }
1656
1657
        /* Create the threshold mask. */
1658
0
        step_size = (MAXVAL + 1.0) / (double) N;
1659
0
        thresholds = (int*) ALLOC(dot_grid.memory, sizeof(int) * N);
1660
0
        if (thresholds == NULL) {
1661
0
            code = -1;
1662
0
            goto out;
1663
0
        }
1664
0
        for (k = 0; k < N; k++) {
1665
0
            thresholds[N-1-k] = (int)((k + 1) * step_size - (step_size / 2));
1666
0
        }
1667
0
        mag_offset =
1668
0
            (double) (thresholds[0]-thresholds[1]) / (double) (num_levels+1);
1669
0
        if ( gamma != 1.0) {
1670
0
            for (k = 0; k < N; k++) {
1671
0
                temp_dbl =
1672
0
                    (double) pow((double) thresholds[k] / MAXVAL,
1673
0
                                 (double) gamma);
1674
0
                thresholds[k] = ROUND(temp_dbl * MAXVAL);
1675
0
            }
1676
0
        }
1677
1678
        /* Now use the indices from the large screen to look up the mask and
1679
           apply the offset to the threshold values to dither the rate at which
1680
           the dots turn on */
1681
        /* allocate the mask */
1682
0
        final_mask->height = height_supercell;
1683
0
        final_mask->width = width_supercell;
1684
0
        final_mask->data =
1685
0
            (int*) ALLOC(dot_grid.memory, sizeof(int) * height_supercell * width_supercell);
1686
0
        if (final_mask->data == NULL) {
1687
0
            code = -1;
1688
0
            goto out;
1689
0
        }
1690
1691
        /* We need to associate the locate index with a particular level
1692
           for the when the dot begins to turn on.  Go through the dot_level_pos
1693
           array to get the values.  Probably should create this earlier and avoid
1694
           this */
1695
0
        dot_level_sort = (int*) ALLOC(dot_grid.memory, sizeof(int) * num_dots);
1696
0
        if (dot_level_sort == NULL) {
1697
0
            code = -1;
1698
0
            goto out;
1699
0
        }
1700
0
        for (h = 0; h < num_dots; h++) {
1701
0
            found = false;
1702
0
            for (jj = 0; jj < num_levels; jj++) {
1703
0
                curr_dot_level = &(dot_level_pos[jj]);
1704
0
                for (mm = 0; mm < curr_dot_level->number_points; mm++) {
1705
0
                    if (pos_x[h] == curr_dot_level->point[mm].x &&
1706
0
                        pos_y[h] == curr_dot_level->point[mm].y ) {
1707
0
                        found = true;
1708
0
                        dot_level_sort[h] = jj + 1;
1709
0
                        break;  /* Break from position search(within level) */
1710
0
                    }
1711
0
                }
1712
0
                if (found == true) { /* break from level search */
1713
0
                    break;
1714
0
                }
1715
0
            }
1716
0
            if (found == false) {
1717
0
                dot_level_sort[h] = 0;
1718
0
            }
1719
0
        }
1720
1721
0
        for (h = 0; h < num_dots; h++) {
1722
0
            for (j = 0; j < dot_grid.height; j++) {
1723
0
                for (k = 0; k < dot_grid.width; k++) {
1724
0
                    val = htsc_getpoint(&dot_grid, k, j);
1725
0
                    if (val != 0) {
1726
1727
                        /* Assign a offset threshold values */
1728
0
                        j_index =
1729
0
                            (pos_y[h] + j - (int) dot_grid_one_index.y) % height_supercell;
1730
1731
                        /* In case we have modulo of a negative number */
1732
0
                        if (j_index < 0) j_index = j_index + height_supercell;
1733
0
                        k_index =
1734
0
                            (pos_x[h] + k - (int) dot_grid_one_index.x) % width_supercell;
1735
1736
                        /* In case we have modulo of a negative number */
1737
0
                        if (k_index < 0) k_index = k_index + width_supercell;
1738
0
                        threshold_value = (int)(thresholds[val-1] +
1739
0
                                                mag_offset * dot_level_sort[h]);
1740
0
                        if (threshold_value > MAXVAL) threshold_value = (int)MAXVAL;
1741
0
                        if (threshold_value < 0) threshold_value = 0;
1742
0
                        htsc_setpoint(final_mask,k_index,j_index,threshold_value);
1743
0
                    }
1744
0
                }
1745
0
            }
1746
0
        }
1747
0
out:
1748
0
        if (dot_level_pos) {
1749
0
            for (k = 0; k < num_levels; k++) {
1750
0
                FREE(dot_grid.memory, dot_level_pos[k].locations);
1751
0
                FREE(dot_grid.memory, dot_level_pos[k].point);
1752
0
            }
1753
0
        }
1754
0
        FREE(dot_grid.memory, locate);
1755
0
        FREE(dot_grid.memory, screen_matrix);
1756
0
        FREE(dot_grid.memory, pos_x);
1757
0
        FREE(dot_grid.memory, pos_y);
1758
0
        FREE(dot_grid.memory, dot_level_pos);
1759
0
        FREE(dot_grid.memory, dot_levels);
1760
0
        FREE(dot_grid.memory, thresholds);
1761
0
        FREE(dot_grid.memory, dot_level_sort);
1762
0
    }
1763
0
    return code;
1764
0
}
1765
1766
static int
1767
htsc_create_holladay_mask(htsc_dig_grid_t super_cell, int H, int L,
1768
                          double gamma, htsc_dig_grid_t *final_mask)
1769
0
{
1770
0
    double step_size = (MAXVAL + 1.0) /( (double) H * (double) L);
1771
0
    int k, j, code = 0;
1772
0
    double *thresholds = NULL;
1773
0
    int number_points = H * L;
1774
0
    double half_step = step_size / 2.0;
1775
0
    double temp;
1776
0
    int index_point;
1777
0
    int value;
1778
0
    double white_scale = 253.0 / 255.0; /* To ensure white is white */
1779
1780
0
    final_mask->height = H;
1781
0
    final_mask->width = L;
1782
0
    final_mask->data = (int *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(int));
1783
0
    if (final_mask->data == NULL) {
1784
0
        code = -1;
1785
0
        goto out;
1786
0
    }
1787
1788
0
    thresholds = (double *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(double));
1789
0
    if (thresholds == NULL) {
1790
0
        code = -1;
1791
0
        goto out;
1792
0
    }
1793
0
    for (k = 0; k < number_points; k++) {
1794
0
         temp = ((k+1) * step_size - half_step) / MAXVAL;
1795
0
         if ( gamma != 1.0) {
1796
             /* Possible linearization */
1797
0
            temp = (double) pow((double) temp, (double) gamma);
1798
0
         }
1799
0
         thresholds[number_points - k - 1] =
1800
0
                                    ROUND(temp * MAXVAL * white_scale + 1);
1801
0
    }
1802
0
    memset(final_mask->data, 0, (size_t)H * L * sizeof(int));
1803
1804
0
    for (j = 0; j < H; j++) {
1805
0
        for (k = 0; k < L; k++) {
1806
0
            index_point = htsc_getpoint(&super_cell,k,j) - 1;
1807
0
            value = (int) floor(thresholds[index_point]);
1808
0
            htsc_setpoint(final_mask,k,j,value);
1809
0
        }
1810
0
    }
1811
0
out:
1812
0
    FREE(final_mask->memory, thresholds);
1813
0
    return code;
1814
0
}
1815
1816
static int
1817
htsc_create_nondithered_mask(htsc_dig_grid_t super_cell, int H, int L,
1818
                          double gamma, htsc_dig_grid_t *final_mask)
1819
0
{
1820
0
    double step_size = (MAXVAL +  1) /( (double) H * (double) L);
1821
0
    int k, j, code = 0;
1822
0
    double *thresholds = NULL;
1823
0
    int number_points = H * L;
1824
0
    double half_step = step_size / 2.0;
1825
0
    double temp;
1826
0
    int index_point;
1827
0
    int value;
1828
0
    double white_scale = 253.0 / 255.0; /* To ensure white is white */
1829
1830
0
    final_mask->height = super_cell.height;
1831
0
    final_mask->width = super_cell.width;
1832
0
    final_mask->data = (int *) ALLOC(final_mask->memory,
1833
0
             (size_t)super_cell.height * super_cell.width *
1834
0
                                      sizeof(int));
1835
0
    if (final_mask->data == NULL) {
1836
0
        code = -1;
1837
0
        goto out;
1838
0
    }
1839
0
    thresholds = (double *) ALLOC(final_mask->memory, (size_t)H * L * sizeof(double));
1840
0
    if (thresholds == NULL) {
1841
0
        code = -1;
1842
0
        goto out;
1843
0
    }
1844
0
    for (k = 0; k < number_points; k++) {
1845
0
         temp = ((k+1) * step_size - half_step) / MAXVAL;
1846
0
         if ( gamma != 1.0) {
1847
             /* Possible linearization */
1848
0
            temp = (double) pow((double) temp, (double) gamma);
1849
0
         }
1850
0
         thresholds[number_points - k - 1] =
1851
0
                                    ROUND(temp * MAXVAL * white_scale + 1);
1852
0
    }
1853
0
    memset(final_mask->data, 0, (size_t)super_cell.height * super_cell.width *
1854
0
                                sizeof(int));
1855
0
    for (j = 0; j < super_cell.height; j++) {
1856
0
        for (k = 0; k < super_cell.width; k++) {
1857
0
            index_point = htsc_getpoint(&super_cell,k,j) - 1;
1858
0
            value = (int) floor(thresholds[index_point]);
1859
0
            htsc_setpoint(final_mask,k,j,value);
1860
0
        }
1861
0
    }
1862
0
out:
1863
0
    FREE(final_mask->memory, thresholds);
1864
0
    return code;
1865
0
}
1866
1867
/* Various spot functions */
1868
static double
1869
htsc_spot_circle(double x, double y)
1870
0
{
1871
0
    return 1.0 - (x*x + y*y);
1872
0
}
1873
1874
static double
1875
htsc_spot_redbook(double x, double y)
1876
0
{
1877
0
    return (180.0 * (double) cos(x) + 180.0 * (double) cos(y)) / 2.0;
1878
0
}
1879
1880
static double
1881
htsc_spot_inverted_round(double x, double y)
1882
0
{
1883
0
    return (x*x + y*y) - 1.0;
1884
0
}
1885
1886
static double
1887
htsc_spot_rhomboid(double x, double y)
1888
0
{
1889
0
    return 1.0 - ((double) fabs(y) * 0.8 + (double) fabs(x)) / 2.0;
1890
0
}
1891
1892
static double
1893
htsc_spot_linex(double x, double y)
1894
0
{
1895
0
    return 1.0 - (double) fabs(y);
1896
0
}
1897
1898
static double
1899
htsc_spot_liney(double x, double y)
1900
0
{
1901
0
    return 1.0 - (double) fabs(x);
1902
0
}
1903
1904
static double
1905
htsc_spot_diamond(double x, double y)
1906
0
{
1907
0
    double abs_y = (double) fabs(y);
1908
0
    double abs_x = (double) fabs(x);
1909
1910
0
    if ((abs_y + abs_x) <= 0.75) {
1911
0
        return 1.0 - (abs_x * abs_x + abs_y * abs_y);
1912
0
    } else {
1913
0
        if ((abs_y + abs_x) <= 1.23) {
1914
0
            return 1.0 - (0.76  * abs_y + abs_x);
1915
0
        } else {
1916
0
            return ((abs_x - 1.0) * (abs_x - 1.0) +
1917
0
                    (abs_y - 1.0) * (abs_y - 1.0)) - 1.0;
1918
0
        }
1919
0
    }
1920
0
}
1921
1922
static double
1923
htsc_spot_diamond2(double x, double y)
1924
0
{
1925
0
    double xy = (double) fabs(x) + (double) fabs(y);
1926
1927
0
    if (xy <= 1.0) {
1928
0
        return 1.0 - xy * xy / 2.0;
1929
0
    } else {
1930
0
        return 1.0 - (2.0 * xy * xy - 4.0 * (xy - 1.0) * (xy - 1.0)) / 4.0;
1931
0
    }
1932
0
}
1933
1934
static double
1935
htsc_spot_roundspot(double x, double y)
1936
0
{
1937
0
    double xy = (double)fabs(x) + (double)fabs(y);
1938
1939
0
    if (xy <= 1.0) {
1940
0
        return 1.0 - (x*x + y*y);
1941
0
    } else {
1942
0
        return ((fabs(x) - 1.0) * (fabs(x) - 1.0)) + ((fabs(y) - 1.0) * (fabs(y) - 1.0)) - 1.0;
1943
0
    }
1944
0
}
1945
1946
static double
1947
htsc_spot_value(spottype_t spot_type, double x, double y)
1948
0
{
1949
0
    switch (spot_type) {
1950
0
        case CIRCLE:
1951
0
            return htsc_spot_circle(x,y);
1952
0
        case REDBOOK:
1953
0
            return htsc_spot_redbook(x,y);
1954
0
        case INVERTED:
1955
0
            return htsc_spot_inverted_round(x,y);
1956
0
        case RHOMBOID:
1957
0
            return htsc_spot_rhomboid(x,y);
1958
0
        case LINE_X:
1959
0
            return htsc_spot_linex(x,y);
1960
0
        case LINE_Y:
1961
0
            return htsc_spot_liney(x,y);
1962
0
        case DIAMOND1:
1963
0
            return htsc_spot_diamond(x,y);
1964
0
        case DIAMOND2:
1965
0
            return htsc_spot_diamond2(x,y);
1966
0
        case ROUNDSPOT:
1967
0
            return htsc_spot_roundspot(x,y);
1968
0
        case CUSTOM:  /* A spot (pun intended) for users to define their own */
1969
0
            return htsc_spot_circle(x,y);
1970
0
        default:
1971
0
            return htsc_spot_circle(x,y);
1972
0
    }
1973
0
}
1974
1975
#if FINAL_SCREEN_DUMP
1976
1977
/* Save turn on order list, Assume that htsc_gen_ordered has already converted to TOS */
1978
static int
1979
htsc_save_tos(htsc_dig_grid_t *final_mask, gs_memory_t *mem)
1980
{
1981
    int width = final_mask->width;
1982
    int height = final_mask->height;
1983
    int *buff_ptr;
1984
    _FILE *fid;
1985
    int k = 0;
1986
    int count = height * width;
1987
1988
    fid = FOPEN(mem, "turn_on_seq.out", "w");
1989
    if (fid == NULL)
1990
        return -1;
1991
1992
    FPRINTF2(fid, "# W=%d H=%d\n", width, height);
1993
1994
    /* Write out */
1995
    buff_ptr = final_mask->data;
1996
    for (k = 0; k < count; k++) {
1997
        FPRINTF2(fid, "%d\t%d\n", *buff_ptr++, *buff_ptr++);
1998
    }
1999
    FCLOSE(fid);
2000
    return 0;
2001
}
2002
2003
int
2004
htsc_save_screen(htsc_dig_grid_t *final_mask, bool use_holladay_grid, int S,
2005
    htsc_param_t params, gs_memory_t *mem)
2006
{
2007
    char full_file_name[FULL_FILE_NAME_LENGTH];
2008
    _FILE *fid;
2009
    int x, y, code = 0;
2010
    int *buff_ptr = final_mask->data;
2011
    int width = final_mask->width;
2012
    int height = final_mask->height;
2013
    byte data;
2014
    unsigned short data_short;
2015
    output_format_type output_format = params.output_format;
2016
    char *output_extension = (output_format == OUTPUT_PS) ? "ps" :
2017
        ((output_format == OUTPUT_PPM) ? "ppm" :
2018
            ((output_format == OUTPUT_RAW16 ? "16.raw" : "raw")));
2019
2020
    if (output_format == OUTPUT_TOS) {
2021
        /* We need to figure out the turn-on sequence from the threshold
2022
           array */
2023
        code = htsc_save_tos(final_mask, mem);
2024
    } else {
2025
        if (use_holladay_grid) {
2026
            snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_Holladay_Shift%d_%dx%d.%s", S, width,
2027
                height, output_extension);
2028
        } else {
2029
            snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_Dithered_%dx%d.%s", width, height,
2030
                output_extension);
2031
        }
2032
        fid = FOPEN(mem, full_file_name, "wb");
2033
        if (fid == NULL)
2034
            return -1;
2035
2036
        if (output_format == OUTPUT_PPM)
2037
            FPRINTF6(fid, "P5\n"
2038
                "# Halftone threshold array, %s, [%d, %d], S=%d\n"
2039
                "%d %d\n"
2040
                "255\n",
2041
                use_holladay_grid ? "Holladay_Shift" : "Dithered", width, height,
2042
                S, width, height);
2043
        if (output_format != OUTPUT_PS) {
2044
            /* Both BIN and PPM format write the same binary data */
2045
            if (output_format == OUTPUT_RAW || output_format == OUTPUT_PPM) {
2046
                for (y = 0; y < height; y++) {
2047
                    for (x = 0; x < width; x++) {
2048
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2049
                        data_short = ROUND((double)data_short * 255.0 / MAXVAL);
2050
                        if (data_short > 255) data_short = 255;
2051
                        data = (byte)(data_short & 0xff);
2052
                        FWRITE(&data, sizeof(byte), 1, fid);
2053
                        buff_ptr++;
2054
                    }
2055
                }
2056
            } else {  /* raw16 data */
2057
                for (y = 0; y < height; y++) {
2058
                    for (x = 0; x < width; x++) {
2059
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2060
                        FWRITE(&data_short, sizeof(short), 1, fid);
2061
                        buff_ptr++;
2062
                    }
2063
                }
2064
            }
2065
        } else {  /* ps output format */
2066
            if (params.targ_quant <= 256) {
2067
                /* Output PS HalftoneType 3 dictionary */
2068
                FPRINTF2(fid, "%%!PS\n"
2069
                    "<< /HalftoneType 3\n"
2070
                    "   /Width  %d\n"
2071
                    "   /Height %d\n"
2072
                    "   /Thresholds <\n",
2073
                    width, height);
2074
2075
                for (y = 0; y < height; y++) {
2076
                    for (x = 0; x < width; x++) {
2077
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2078
                        data_short = ROUND((double)data_short * 255.0 / MAXVAL);
2079
                        if (data_short > 255) data_short = 255;
2080
                        data = (byte)(data_short & 0xff);
2081
                        FPRINTF1(fid, "%02x", data);
2082
                        buff_ptr++;
2083
                        if ((x & 0x1f) == 0x1f && (x != (width - 1)))
2084
                            FPRINTF(fid, "\n");
2085
                    }
2086
                    FPRINTF(fid, "\n");
2087
                }
2088
                FPRINTF(fid, "   >\n"
2089
                    ">>\n"
2090
                );
2091
            } else {
2092
                /* Output PS HalftoneType 16 dictionary. Single array. */
2093
                FPRINTF(fid, "%%!PS\n"
2094
                    "%% Create a 'filter' from local hex data\n"
2095
                    "{ currentfile /ASCIIHexDecode filter /ReusableStreamDecode filter } exec\n");
2096
                /* hex data follows, 'file' object will be left on stack */
2097
                for (y = 0; y < height; y++) {
2098
                    for (x = 0; x < width; x++) {
2099
                        data_short = (unsigned short)(*buff_ptr & 0xffff);
2100
                        FPRINTF1(fid, "%04x", data_short);
2101
                        buff_ptr++;
2102
                        if ((x & 0x1f) == 0x1f && (x != (width - 1)))
2103
                            FPRINTF(fid, "\n");
2104
                    }
2105
                    FPRINTF(fid, "\n"); /* end of one row */
2106
                }
2107
                FPRINTF(fid, ">\n");  /* ASCIIHexDecode EOF */
2108
                FPRINTF2(fid,
2109
                    "<< /Thresholds 2 index    %% file object for the 16-bit data\n"
2110
                    "   /HalftoneType 16\n"
2111
                    "   /Width  %d\n"
2112
                    "   /Height %d\n"
2113
                    ">>\n"
2114
                    "exch pop     %% discard ResuableStreamDecode file leaving the Halftone dict.\n",
2115
                    width, height);
2116
            }
2117
        }
2118
        FCLOSE(fid);
2119
    }
2120
    return code;
2121
}
2122
#endif
2123
2124
#if RAW_SCREEN_DUMP
2125
static int
2126
htsc_dump_screen(htsc_dig_grid_t *dig_grid, char filename[], gs_memory_t *mem)
2127
{
2128
    char full_file_name[FULL_FILE_NAME_LENGTH];
2129
    _FILE *fid;
2130
    int x,y;
2131
    int *buff_ptr = dig_grid->data;
2132
    int width = dig_grid->width;
2133
    int height = dig_grid->height;
2134
    byte data[3];
2135
2136
    snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Screen_%s_%dx%dx3.raw",filename,width,height);
2137
    fid = FOPEN(mem, full_file_name,"wb");
2138
    if (fid == NULL)
2139
        return -1;
2140
2141
    for (y = 0; y < height; y++) {
2142
        for ( x = 0; x < width; x++ ) {
2143
            if (*buff_ptr < 0) {
2144
                data[0] = 255;
2145
                data[1] = 0;
2146
                data[2] = 0;
2147
            } else if (*buff_ptr > 255) {
2148
                data[0] = 0;
2149
                data[1] = 255;
2150
                data[2] = 0;
2151
            } else {
2152
                data[0] = *buff_ptr;
2153
                data[1] = *buff_ptr;
2154
                data[2] = *buff_ptr;
2155
            }
2156
            FWRITE(data,sizeof(unsigned char),3,fid);
2157
            buff_ptr++;
2158
        }
2159
    }
2160
    FCLOSE(fid);
2161
    return 0;
2162
}
2163
2164
static int
2165
htsc_dump_float_image(double *image, int height, int width, double max_val,
2166
                      char filename[], gs_memory_t *mem)
2167
{
2168
    char full_file_name[FULL_FILE_NAME_LENGTH];
2169
    _FILE *fid;
2170
    int x,y;
2171
    int data;
2172
    byte data_byte;
2173
2174
    snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "Float_%s_%dx%d.raw",filename,width,height);
2175
    fid = FOPEN(mem, full_file_name,"wb");
2176
    if (fid == NULL)
2177
        return -1;
2178
2179
    for (y = 0; y < height; y++) {
2180
        for ( x = 0; x < width; x++ ) {
2181
            data = (255.0 * image[x + y * width] / max_val);
2182
            if (data > 255) data = 255;
2183
            if (data < 0) data = 0;
2184
            data_byte = data;
2185
            FWRITE(&data_byte,sizeof(byte),1,fid);
2186
        }
2187
    }
2188
    FCLOSE(fid);
2189
    return 0;
2190
}
2191
2192
static int
2193
htsc_dump_byte_image(byte *image, int height, int width, double max_val,
2194
                      char filename[], gs_memory_t *mem)
2195
{
2196
    char full_file_name[FULL_FILE_NAME_LENGTH];
2197
    _FILE *fid;
2198
    int x,y;
2199
    int data;
2200
    byte data_byte;
2201
2202
    snprintf(full_file_name, FULL_FILE_NAME_LENGTH, "ByteScaled_%s_%dx%d.raw",filename,width,height);
2203
    fid = FOPEN(mem, full_file_name,"wb");
2204
    if (fid == NULL)
2205
        return -1;
2206
2207
    for (y = 0; y < height; y++) {
2208
        for ( x = 0; x < width; x++ ) {
2209
            data = (255.0 * image[x + y * width] / max_val);
2210
            if (data > 255) data = 255;
2211
            if (data < 0) data = 0;
2212
            data_byte = data;
2213
            FWRITE(&data_byte,sizeof(byte),1,fid);
2214
        }
2215
    }
2216
    FCLOSE(fid);
2217
    return 0;
2218
}
2219
#endif