Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gshtx.c
Line
Count
Source
1
/* Copyright (C) 2001-2023 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
16
17
/* Stand-alone halftone/transfer function related code */
18
#include "memory_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gsstruct.h"
22
#include "gsutil.h"   /* for gs_next_ids */
23
#include "gxfmap.h"
24
#include "gzstate.h"
25
#include "gzht.h"
26
#include "gshtx.h"    /* must come after g*ht.h */
27
28
/*
29
 * Procedure to free the set of components when a halftone is released.
30
 */
31
static void
32
free_comps(
33
              gs_memory_t * pmem,
34
              void *pvht,
35
              client_name_t cname
36
)
37
0
{
38
0
    gs_ht *pht = (gs_ht *) pvht;
39
40
0
    gs_free_object(pmem, pht->params.ht_multiple.components, cname);
41
0
    gs_free_object(pmem, pvht, cname);
42
0
}
43
44
/*
45
 * Stub transfer function, to be applied to components that are not provided
46
 * with a transfer function.
47
 */
48
static float
49
null_closure_transfer(
50
                         double val,
51
                         const gx_transfer_map * pmap_dummy,  /* NOTUSED */
52
                         const void *dummy  /* NOTUSED */
53
)
54
0
{
55
0
    return val;
56
0
}
57
58
/*
59
 * Build a gs_ht halftone structure.
60
 */
61
int
62
gs_ht_build(
63
               gs_ht ** ppht,
64
               uint num_comps,
65
               gs_memory_t * pmem
66
)
67
0
{
68
0
    gs_ht *pht;
69
0
    gs_ht_component *phtc;
70
0
    int i;
71
72
    /* must have at least one component */
73
0
    *ppht = 0;
74
0
    if (num_comps == 0)
75
0
        return_error(gs_error_rangecheck);
76
77
    /* allocate the halftone and the array of components */
78
0
    rc_alloc_struct_1(pht,
79
0
                      gs_ht,
80
0
                      &st_gs_ht,
81
0
                      pmem,
82
0
                      return_error(gs_error_VMerror),
83
0
                      "gs_ht_build"
84
0
        );
85
0
    phtc = gs_alloc_struct_array(pmem,
86
0
                                 num_comps,
87
0
                                 gs_ht_component,
88
0
                                 &st_ht_comp_element,
89
0
                                 "gs_ht_build"
90
0
        );
91
0
    if (phtc == 0) {
92
0
        gs_free_object(pmem, pht, "gs_ht_build");
93
0
        return_error(gs_error_VMerror);
94
0
    }
95
    /* initialize the halftone */
96
0
    pht->type = ht_type_multiple;
97
0
    pht->rc.free = free_comps;
98
0
    pht->objtype = HT_OBJTYPE_DEFAULT;
99
0
    pht->params.ht_multiple.components = phtc;
100
0
    pht->params.ht_multiple.num_comp = num_comps;
101
102
0
    for (i = 0; i < num_comps; i++) {
103
0
        phtc[i].comp_number = i;
104
0
        phtc[i].cname = 0;
105
0
        phtc[i].type = ht_type_none;
106
0
    }
107
108
0
    *ppht = pht;
109
110
0
    return 0;
111
0
}
112
113
/*
114
 * Set a spot-function halftone component in a gs_ht halftone.
115
 */
116
int
117
gs_ht_set_spot_comp(
118
                       gs_ht * pht,
119
                       int comp,
120
                       double freq,
121
                       double angle,
122
                       float (*spot_func) (double, double),
123
                       bool accurate,
124
                       gs_ht_transfer_proc transfer,
125
                       const void *client_data
126
)
127
0
{
128
0
    gs_ht_component *phtc = &(pht->params.ht_multiple.components[comp]);
129
130
0
    if (comp >= pht->params.ht_multiple.num_comp)
131
0
        return_error(gs_error_rangecheck);
132
0
    if (phtc->type != ht_type_none)
133
0
        return_error(gs_error_invalidaccess);
134
135
0
    phtc->type = ht_type_spot;
136
0
    phtc->params.ht_spot.screen.frequency = freq;
137
0
    phtc->params.ht_spot.screen.angle = angle;
138
0
    phtc->params.ht_spot.screen.spot_function = spot_func;
139
0
    phtc->params.ht_spot.accurate_screens = accurate;
140
0
    phtc->params.ht_spot.transfer = gs_mapped_transfer;
141
142
0
    phtc->params.ht_spot.transfer_closure.proc =
143
0
        (transfer == 0 ? null_closure_transfer : transfer);
144
0
    phtc->params.ht_spot.transfer_closure.data = client_data;
145
146
0
    return 0;
147
0
}
148
149
/*
150
 * Set a threshold halftone component in a gs_ht halftone. Note that the
151
 * caller is responsible for releasing the threshold data.
152
 */
153
int
154
gs_ht_set_threshold_comp(
155
                            gs_ht * pht,
156
                            int comp,
157
                            int width,
158
                            int height,
159
                            const gs_const_string * thresholds,
160
                            gs_ht_transfer_proc transfer,
161
                            const void *client_data
162
)
163
0
{
164
0
    gs_ht_component *phtc = &(pht->params.ht_multiple.components[comp]);
165
166
0
    if (comp >= pht->params.ht_multiple.num_comp)
167
0
        return_error(gs_error_rangecheck);
168
0
    if (phtc->type != ht_type_none)
169
0
        return_error(gs_error_invalidaccess);
170
171
0
    phtc->type = ht_type_threshold;
172
0
    phtc->params.ht_threshold.width = width;
173
0
    phtc->params.ht_threshold.height = height;
174
0
    phtc->params.ht_threshold.thresholds = *thresholds;
175
0
    phtc->params.ht_threshold.transfer = gs_mapped_transfer;
176
177
0
    phtc->params.ht_threshold.transfer_closure.proc =
178
0
        (transfer == 0 ? null_closure_transfer : transfer);
179
0
    phtc->params.ht_threshold.transfer_closure.data = client_data;
180
181
0
    return 0;
182
0
}
183
184
/*
185
 * Increase the reference count of a gs_ht structure by 1.
186
 */
187
void
188
gs_ht_reference(
189
                   gs_ht * pht
190
)
191
0
{
192
0
    rc_increment(pht);
193
0
}
194
195
/*
196
 * Decrement the reference count of a gs_ht structure by 1. Free the
197
 * structure if the reference count reaches 0.
198
 */
199
void
200
gs_ht_release(
201
                 gs_ht * pht
202
)
203
0
{
204
0
    rc_decrement_only(pht, "gs_ht_release");
205
0
}
206
207
/*
208
 *  Verify that a gs_ht halftone is legitimate.
209
 */
210
static int
211
check_ht(
212
            gs_ht * pht
213
)
214
0
{
215
0
    int i;
216
0
    int num_comps = pht->params.ht_multiple.num_comp;
217
218
0
    if (pht->type != ht_type_multiple)
219
0
        return_error(gs_error_unregistered);
220
0
    for (i = 0; i < num_comps; i++) {
221
0
        gs_ht_component *phtc = &(pht->params.ht_multiple.components[i]);
222
0
        if ((phtc->type != ht_type_spot) && (phtc->type != ht_type_threshold))
223
0
            return_error(gs_error_unregistered);
224
0
    }
225
0
    return 0;
226
0
}
227
228
/*
229
 *  Load a transfer map from a gs_ht_transfer_proc function.
230
 */
231
static void
232
build_transfer_map(
233
                      gs_ht_component * phtc,
234
                      gx_transfer_map * pmap
235
)
236
0
{
237
0
    gs_ht_transfer_proc proc;
238
0
    const void *client_info;
239
0
    int i;
240
0
    frac *values = pmap->values;
241
242
0
    if (phtc->type == ht_type_spot) {
243
0
        proc = phtc->params.ht_spot.transfer_closure.proc;
244
0
        client_info = phtc->params.ht_spot.transfer_closure.data;
245
0
    } else {
246
0
        proc = phtc->params.ht_threshold.transfer_closure.proc;
247
0
        client_info = phtc->params.ht_threshold.transfer_closure.data;
248
0
    }
249
250
0
    for (i = 0; i < transfer_map_size; i++) {
251
0
        float fval =
252
0
            proc(i * (1 / (double)(transfer_map_size - 1)), pmap, client_info);
253
254
0
        values[i] =
255
0
            (fval <= 0.0 ? frac_0 : fval >= 1.0 ? frac_1 :
256
0
             float2frac(fval));
257
0
    }
258
0
}
259
260
/*
261
 *  Allocate the order and transfer maps required by a halftone, and perform
262
 *  some elementary initialization. This will also build the component index
263
 *  to order index map.
264
 */
265
static gx_ht_order_component *
266
alloc_ht_order(
267
                  const gs_ht * pht,
268
                  gs_memory_t * pmem,
269
                  byte * comp2order
270
)
271
0
{
272
0
    int num_comps = pht->params.ht_multiple.num_comp;
273
0
    gx_ht_order_component *pocs = gs_alloc_struct_array(
274
0
                                                           pmem,
275
0
                                           pht->params.ht_multiple.num_comp,
276
0
                                                      gx_ht_order_component,
277
0
                                             &st_ht_order_component_element,
278
0
                                                           "alloc_ht_order"
279
0
    );
280
0
    int inext = 0;
281
0
    int i;
282
283
0
    if (pocs == 0)
284
0
        return 0;
285
0
    pocs->corder.transfer = 0;
286
287
0
    for (i = 0; i < num_comps; i++) {
288
0
        gs_ht_component *phtc = &(pht->params.ht_multiple.components[i]);
289
0
        gx_transfer_map *pmap = gs_alloc_struct(pmem,
290
0
                                                gx_transfer_map,
291
0
                                                &st_transfer_map,
292
0
                                                "alloc_ht_order"
293
0
        );
294
295
0
        if (pmap == 0) {
296
0
            int j;
297
298
0
            for (j = 0; j < inext; j++)
299
0
                gs_free_object(pmem, pocs[j].corder.transfer, "alloc_ht_order");
300
0
            gs_free_object(pmem, pocs, "alloc_ht_order");
301
0
            return 0;
302
0
        }
303
0
        pmap->proc = gs_mapped_transfer;
304
0
        pmap->id = gs_next_ids(pmem, 1);
305
0
        pocs[inext].corder.levels = 0;
306
0
        pocs[inext].corder.bit_data = 0;
307
0
        pocs[inext].corder.cache = 0;
308
0
        pocs[inext].corder.transfer = pmap;
309
0
        pocs[inext].cname = phtc->cname;
310
0
        pocs[inext].comp_number = phtc->comp_number;
311
0
        comp2order[i] = inext++;
312
0
    }
313
314
0
    return pocs;
315
0
}
316
317
/*
318
 *  Build the halftone order for one component.
319
 */
320
static int
321
build_component(
322
                   gs_ht_component * phtc,
323
                   gx_ht_order * porder,
324
                   gs_gstate * pgs,
325
                   gs_memory_t * pmem
326
)
327
0
{
328
0
    if (phtc->type == ht_type_spot) {
329
0
        gs_screen_enum senum;
330
0
        int code;
331
332
0
        code = gx_ht_process_screen_memory(&senum,
333
0
                                           pgs,
334
0
                                           &phtc->params.ht_spot.screen,
335
0
                                      phtc->params.ht_spot.accurate_screens,
336
0
                                           pmem
337
0
            );
338
0
        if (code < 0)
339
0
            return code;
340
341
        /* avoid wiping out the transfer structure pointer */
342
0
        senum.order.transfer = porder->transfer;
343
0
        *porder = senum.order;
344
345
0
    } else {     /* ht_type_threshold */
346
0
        int code;
347
0
        gx_transfer_map *transfer = porder->transfer;
348
349
0
        porder->params.M = phtc->params.ht_threshold.width;
350
0
        porder->params.N = 0;
351
0
        porder->params.R = 1;
352
0
        porder->params.M1 = phtc->params.ht_threshold.height;
353
0
        porder->params.N1 = 0;
354
0
        porder->params.R1 = 1;
355
0
        code = gx_ht_alloc_threshold_order(porder,
356
0
                                           phtc->params.ht_threshold.width,
357
0
                                           phtc->params.ht_threshold.height,
358
0
                                           256,
359
0
                                           pmem
360
0
            );
361
0
        if (code < 0)
362
0
            return code;
363
0
        gx_ht_construct_threshold_order(
364
0
                                porder,
365
0
                                phtc->params.ht_threshold.thresholds.data
366
0
            );
367
        /*
368
         * gx_ht_construct_threshold_order wipes out transfer map pointer,
369
         * restore it here.
370
         */
371
0
        porder->transfer = transfer;
372
0
    }
373
374
0
    build_transfer_map(phtc, porder->transfer);
375
0
    return 0;
376
0
}
377
378
/*
379
 * Free an order array and all elements it points to.
380
 */
381
static void
382
free_order_array(
383
                    gx_ht_order_component * pocs,
384
                    int num_comps,
385
                    gs_memory_t * pmem
386
)
387
0
{
388
0
    int i;
389
390
0
    for (i = 0; i < num_comps; i++)
391
0
        gx_ht_order_release(&(pocs[i].corder), pmem, true);
392
0
    gs_free_object(pmem, pocs, "gs_ht_install");
393
0
}
394
395
/*
396
 *  Install a gs_ht halftone as the current halftone in the graphic state.
397
 */
398
int
399
gs_ht_install(
400
                 gs_gstate * pgs,
401
                 gs_ht * pht
402
)
403
0
{
404
0
    int code = 0;
405
0
    gs_memory_t *pmem = pht->rc.memory;
406
0
    gx_device_halftone dev_ht;
407
0
    gx_ht_order_component *pocs;
408
0
    byte comp2order[32];  /* ample component to order map */
409
0
    int num_comps = pht->params.ht_multiple.num_comp;
410
0
    int i;
411
412
    /* perform so sanity checks (must have one default component) */
413
0
    if ((code = check_ht(pht)) != 0)
414
0
        return code;
415
416
    /* allocate the halftone order structure and transfer maps */
417
0
    if ((pocs = alloc_ht_order(pht, pmem, comp2order)) == 0)
418
0
        return_error(gs_error_VMerror);
419
420
    /* build all of the order for each component */
421
0
    for (i = 0; i < num_comps; i++) {
422
0
        int j = comp2order[i];
423
424
0
        code = build_component(&(pht->params.ht_multiple.components[i]),
425
0
                               &(pocs[j].corder),
426
0
                               pgs,
427
0
                               pmem
428
0
            );
429
430
0
        if ((code >= 0) && (j != 0)) {
431
0
            gx_ht_cache *pcache;
432
433
0
            pcache = gx_ht_alloc_cache(pmem,
434
0
                                       4,
435
0
                                       pocs[j].corder.raster *
436
0
                                       (pocs[j].corder.num_bits /
437
0
                                        pocs[j].corder.width) * 4
438
0
                );
439
440
0
            if (pcache == 0)
441
0
                code = gs_note_error(gs_error_VMerror);
442
0
            else {
443
0
                pocs[j].corder.cache = pcache;
444
0
                gx_ht_init_cache(pmem, pcache, &(pocs[j].corder));
445
0
            }
446
0
        }
447
0
        if (code < 0)
448
0
            break;
449
0
    }
450
451
0
    if (code < 0) {
452
0
        free_order_array(pocs, num_comps, pmem);
453
0
        return code;
454
0
    }
455
    /* initialize the device halftone structure */
456
0
    dev_ht.rc.memory = pmem;
457
0
    dev_ht.order = pocs[0].corder;  /* Default */
458
0
    if (num_comps == 1) {
459
        /* we have only a Default; we don't need components. */
460
0
        gs_free_object(pmem, pocs, "gs_ht_install");
461
0
        dev_ht.components = 0;
462
0
    } else {
463
0
        dev_ht.components = pocs;
464
0
        dev_ht.num_comp = num_comps;
465
0
    }
466
467
    /* at last, actually install the halftone in the graphic state */
468
0
    if ((code = gx_ht_install(pgs, (gs_halftone *) pht, &dev_ht)) < 0)
469
0
        gx_device_halftone_release(&dev_ht, pmem);
470
0
    return code;
471
0
}
472
473
/* ---------------- Mask-defined halftones ---------------- */
474
475
/*
476
 * Create a halftone order from an array of explicit masks.  This is
477
 * silly, because the rendering machinery actually wants masks, but doing
478
 * it right seems to require too many changes in existing code.
479
 */
480
static int
481
create_mask_bits(const byte * mask1, const byte * mask2,
482
                 int width, int height, gx_ht_bit * bits)
483
0
{
484
    /*
485
     * We do this with the slowest, simplest possible algorithm....
486
     */
487
0
    int width_bytes = (width + 7) >> 3;
488
0
    int x, y;
489
0
    int count = 0;
490
491
0
    for (y = 0; y < height; ++y)
492
0
        for (x = 0; x < width; ++x) {
493
0
            int offset = y * width_bytes + (x >> 3);
494
0
            byte bit_mask = 0x80 >> (x & 7);
495
496
0
            if ((mask1[offset] ^ mask2[offset]) & bit_mask) {
497
0
                if (bits)
498
0
                    gx_ht_construct_bit(&bits[count], width, y * width + x);
499
0
                ++count;
500
0
            }
501
0
        }
502
0
    return count;
503
0
}
504
static int
505
create_mask_order(gx_ht_order * porder, gs_gstate * pgs,
506
                  const gs_client_order_halftone * phcop,
507
                  gs_memory_t * mem)
508
0
{
509
0
    int width_bytes = (phcop->width + 7) >> 3;
510
0
    const byte *masks = (const byte *)phcop->client_data;
511
0
    int bytes_per_mask = width_bytes * phcop->height;
512
0
    const byte *prev_mask;
513
0
    int num_levels = phcop->num_levels;
514
0
    int num_bits = 0;
515
0
    int i;
516
0
    int code;
517
518
    /* Do a first pass to compute how many bits entries will be needed. */
519
0
    for (prev_mask = masks, num_bits = 0, i = 0;
520
0
         i < num_levels - 1;
521
0
         ++i, prev_mask += bytes_per_mask
522
0
        )
523
0
        num_bits += create_mask_bits(prev_mask, prev_mask + bytes_per_mask,
524
0
                                     phcop->width, phcop->height, NULL);
525
0
    code = gx_ht_alloc_client_order(porder, phcop->width, phcop->height,
526
0
                                    num_levels, num_bits, mem);
527
0
    if (code < 0)
528
0
        return code;
529
    /* Fill in the bits and levels entries. */
530
0
    for (prev_mask = masks, num_bits = 0, i = 0;
531
0
         i < num_levels - 1;
532
0
         ++i, prev_mask += bytes_per_mask
533
0
        ) {
534
0
        porder->levels[i] = num_bits;
535
0
        num_bits += create_mask_bits(prev_mask, prev_mask + bytes_per_mask,
536
0
                                     phcop->width, phcop->height,
537
0
                                     ((gx_ht_bit *)porder->bit_data) +
538
0
                                      num_bits);
539
0
    }
540
0
    porder->levels[num_levels - 1] = num_bits;
541
0
    return 0;
542
0
}
543
544
/* Define the client-order halftone procedure structure. */
545
static const gs_client_order_ht_procs_t mask_order_procs =
546
{
547
    create_mask_order
548
};
549
550
/*
551
 * Define a halftone by an explicit set of masks.  We translate these
552
 * internally into a threshold array, since that's what the halftone
553
 * rendering machinery knows how to deal with.
554
 */
555
int
556
gs_ht_set_mask_comp(gs_ht * pht,
557
                    int component_index,
558
                    int width, int height, int num_levels,
559
                    const byte * masks,   /* width x height x num_levels bits */
560
                    gs_ht_transfer_proc transfer,
561
                    const void *client_data)
562
0
{
563
0
    gs_ht_component *phtc =
564
0
    &(pht->params.ht_multiple.components[component_index]);
565
566
0
    if (component_index >= pht->params.ht_multiple.num_comp)
567
0
        return_error(gs_error_rangecheck);
568
0
    if (phtc->type != ht_type_none)
569
0
        return_error(gs_error_invalidaccess);
570
571
0
    phtc->type = ht_type_client_order;
572
0
    phtc->params.client_order.width = width;
573
0
    phtc->params.client_order.height = height;
574
0
    phtc->params.client_order.num_levels = num_levels;
575
0
    phtc->params.client_order.procs = &mask_order_procs;
576
0
    phtc->params.client_order.client_data = masks;
577
0
    phtc->params.client_order.transfer_closure.proc =
578
0
        (transfer == 0 ? null_closure_transfer : transfer);
579
0
    phtc->params.client_order.transfer_closure.data = client_data;
580
581
0
    return 0;
582
583
0
}