Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gxidata.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
16
17
/* Generic image enumeration and cleanup */
18
#include "gx.h"
19
#include "memory_.h"
20
#include "gserrors.h"
21
#include "gxdevice.h"
22
#include "gxcpath.h"
23
#include "gximage.h"
24
#include "gsicc_cache.h"
25
#include "gxgstate.h"
26
27
#ifdef WITH_CAL
28
#include "cal.h"
29
#endif
30
31
/* Forward declarations */
32
static void update_strip(gx_image_enum *penum);
33
static void repack_bit_planes(const gx_image_plane_t *src_planes,
34
                               const ulong *offsets, int num_planes,
35
                               byte *buffer, int width,
36
                               const sample_lookup_t * ptab, int spread);
37
static gx_device *setup_image_device(const gx_image_enum *penum);
38
39
/* Process the next piece of an ImageType 1 image. */
40
int
41
gx_image1_plane_data(gx_image_enum_common_t * info,
42
                     const gx_image_plane_t * planes, int height,
43
                     int *rows_used)
44
47.2M
{
45
47.2M
    gx_image_enum *penum = (gx_image_enum *) info;
46
47.2M
    gx_device *dev;
47
47.2M
    const int y = penum->y;
48
47.2M
    int y_end = min(y + height, penum->rect.h);
49
47.2M
    int width_spp = penum->rect.w * penum->spp;
50
47.2M
    int num_planes = penum->num_planes;
51
47.2M
    int num_components_per_plane = 1;
52
53
47.2M
#define BCOUNT(plane)   /* bytes per data row */\
54
47.2M
  (((penum->rect.w + (plane).data_x) * penum->spp * penum->bps / num_planes\
55
42.8M
    + 7) >> 3)
56
57
47.2M
    fixed adjust = penum->adjust;
58
47.2M
    ulong offsets[GS_IMAGE_MAX_COMPONENTS];
59
47.2M
    int ignore_data_x;
60
47.2M
    bool bit_planar = penum->num_planes > penum->spp;
61
47.2M
    int code;
62
63
    /* Sanity check */
64
47.2M
    if (penum->pgs != NULL && penum->pgs->level < penum->pgs_level) {
65
1
        code = gs_note_error(gs_error_undefinedresult);
66
1
        goto out;
67
1
    }
68
47.2M
    if (height == 0) {
69
0
        *rows_used = 0;
70
0
        return 0;
71
0
    }
72
47.2M
    dev = setup_image_device(penum);
73
74
    /* Now render complete rows. */
75
76
47.2M
    if (penum->used.y) {
77
        /*
78
         * Processing was interrupted by an error.  Skip over rows
79
         * already processed.
80
         */
81
0
        int px;
82
83
0
        for (px = 0; px < num_planes; ++px)
84
0
            offsets[px] = (size_t)planes[px].raster * penum->used.y;
85
0
        penum->used.y = 0;
86
0
    } else
87
47.2M
        memset(offsets, 0, num_planes * sizeof(offsets[0]));
88
47.2M
    if (num_planes == 1 && penum->plane_depths[0] != penum->bps) {
89
        /* A single plane with multiple components. */
90
12.5M
        num_components_per_plane = penum->plane_depths[0] / penum->bps;
91
12.5M
    }
92
105M
    for (; penum->y < y_end; penum->y++) {
93
58.0M
        int px;
94
58.0M
        const byte *buffer;
95
58.0M
        int sourcex;
96
58.0M
        int x_used = penum->used.x;
97
58.0M
        int skip = 0;
98
99
        /* Bump DDA's if it doesn't cause overflow */
100
58.0M
        penum->cur.x = dda_current(penum->dda.row.x);
101
58.0M
        if (max_int - any_abs(penum->dda.row.x.step.dQ) > any_abs(penum->cur.x))
102
58.0M
            dda_next(penum->dda.row.x);
103
58.0M
        penum->cur.y = dda_current(penum->dda.row.y);
104
58.0M
        if (max_int - any_abs(penum->dda.row.y.step.dQ) > any_abs(penum->cur.y))
105
58.0M
            dda_next(penum->dda.row.y);
106
107
58.0M
        if (penum->interpolate == interp_off && penum->skip_next_line && penum->skip_next_line(penum, dev)) {
108
219k
            goto mt;
109
57.7M
        } else if (penum->skip_next_line == NULL) {
110
            /* Previously we skipped these calculations if we were interpolating.
111
             * Bug 706881 shows us that we can't skip this in all cases, because
112
             * the values are needed when halftoning for landscape files at least.
113
             * Easiest to always perform the calculations. */
114
47.4M
            switch (penum->posture) {
115
42.0M
                case image_portrait:
116
42.0M
                    {    /* Precompute integer y and height, */
117
                        /* and check for clipping. */
118
42.0M
                        fixed yc = penum->cur.y,
119
42.0M
                              yn = dda_current(penum->dda.row.y);
120
121
42.0M
                        if (yn < yc) {
122
2.78M
                            fixed temp = yn;
123
124
2.78M
                            yn = yc;
125
2.78M
                            yc = temp;
126
2.78M
                        }
127
42.0M
                        yc -= adjust;
128
42.0M
                        yn += adjust;
129
42.0M
                        if (penum->interpolate == interp_off)
130
42.0M
                        {
131
42.0M
                            if (yc >= penum->clip_outer.q.y)
132
8.53M
                                goto mt;
133
33.5M
                            if (yn <= penum->clip_outer.p.y)
134
3.43M
                                goto mt;
135
33.5M
                        }
136
30.1M
                        penum->yci = fixed2int_pixround_perfect(yc);
137
30.1M
                        penum->hci = fixed2int_pixround_perfect(yn) - penum->yci;
138
30.1M
                        if (penum->interpolate == interp_off && penum->hci == 0)
139
4.30M
                            goto mt;
140
30.1M
                        if_debug2m('b', penum->memory, "[b]yci=%d, hci=%d\n",
141
25.7M
                                   penum->yci, penum->hci);
142
25.7M
                    }
143
0
                    break;
144
1.01M
                case image_landscape:
145
1.01M
                    {    /* Check for no pixel centers in x. */
146
1.01M
                        fixed xc = penum->cur.x,
147
1.01M
                              xn = dda_current(penum->dda.row.x);
148
149
1.01M
                        if (xn < xc) {
150
1.78k
                            fixed temp = xn;
151
152
1.78k
                            xn = xc;
153
1.78k
                            xc = temp;
154
1.78k
                        }
155
1.01M
                        xc -= adjust;
156
1.01M
                        xn += adjust;
157
1.01M
                        if (penum->interpolate == interp_off)
158
1.01M
                        {
159
1.01M
                            if (xc >= penum->clip_outer.q.x)
160
2.02k
                                goto mt;
161
1.01M
                            if (xn <= penum->clip_outer.p.x)
162
67.9k
                                goto mt;
163
1.01M
                        }
164
943k
                        penum->xci = fixed2int_pixround_perfect(xc);
165
943k
                        penum->wci = fixed2int_pixround_perfect(xn) - penum->xci;
166
943k
                        if (penum->interpolate == interp_off && penum->wci == 0)
167
15.0k
                            goto mt;
168
943k
                        if_debug2m('b', penum->memory, "[b]xci=%d, wci=%d\n",
169
928k
                                   penum->xci, penum->wci);
170
928k
                    }
171
0
                    break;
172
4.37M
                case image_skewed:
173
4.37M
                    ;
174
47.4M
            }
175
47.4M
        }
176
41.4M
        if (0)
177
0
        {
178
16.5M
        mt:
179
16.5M
            skip = 1;
180
16.5M
        }
181
58.0M
        if (bit_planar) {
182
            /* Repack the bit planes into byte-wide samples. */
183
184
0
            buffer = penum->buffer;
185
0
            sourcex = 0;
186
0
            if (!skip)
187
0
                for (px = 0; px < num_planes; px += penum->bps)
188
0
                    repack_bit_planes(planes, offsets, penum->bps, penum->buffer,
189
0
                                      penum->rect.w, &penum->map[px].table,
190
0
                                      penum->spread);
191
0
            for (px = 0; px < num_planes; ++px)
192
0
                offsets[px] += planes[px].raster;
193
58.0M
        } else {
194
            /*
195
             * Normally, we unpack the data into the buffer, but if
196
             * there is only one plane and we don't need to expand the
197
             * input samples, we may use the data directly.
198
             */
199
58.0M
            sourcex = planes[0].data_x;
200
58.0M
            if (!skip)
201
41.4M
                buffer =
202
41.4M
                    (*penum->unpack)(penum->buffer, &sourcex,
203
41.4M
                                     planes[0].data + offsets[0],
204
41.4M
                                     planes[0].data_x, BCOUNT(planes[0]),
205
41.4M
                                     &penum->map[0], penum->spread, num_components_per_plane);
206
16.5M
            else
207
16.5M
                buffer = NULL;
208
209
58.0M
            offsets[0] += planes[0].raster;
210
59.4M
            for (px = 1; px < num_planes; ++px) {
211
1.41M
                if (!skip)
212
1.41M
                    (*penum->unpack)(penum->buffer + (px << penum->log2_xbytes),
213
1.41M
                                     &ignore_data_x,
214
1.41M
                                     planes[px].data + offsets[px],
215
1.41M
                                     planes[px].data_x, BCOUNT(planes[px]),
216
1.41M
                                     &penum->map[px], penum->spread, 1);
217
1.41M
                offsets[px] += planes[px].raster;
218
1.41M
            }
219
58.0M
        }
220
#ifdef DEBUG
221
        if (gs_debug_c('b'))
222
            dmprintf1(dev->memory, "[b]image1 y=%d\n", y);
223
        if (gs_debug_c('B')) {
224
            int i, n = width_spp;
225
            byte *buftemp = (buffer == NULL) ? penum->buffer : (byte *)buffer;
226
227
            if (penum->bps > 8)
228
                n *= 2;
229
            else if (penum->bps == 1 && penum->unpack_bps == 8)
230
                n = (n + 7) / 8;
231
            dmlputs(dev->memory, "[B]row:");
232
            for (i = 0; i < n; i++)
233
                dmprintf1(dev->memory, " %02x", buftemp[i]);
234
            dmputs(dev->memory, "\n");
235
        }
236
#endif
237
58.0M
        if (!skip)
238
41.4M
        {
239
41.4M
            update_strip(penum);
240
41.4M
            if (x_used) {
241
                /*
242
                 * Processing was interrupted by an error.  Skip over pixels
243
                 * already processed.
244
                 */
245
0
                dda_advance(penum->dda.pixel0.x, x_used);
246
0
                dda_advance(penum->dda.pixel0.y, x_used);
247
0
                penum->used.x = 0;
248
0
            }
249
41.4M
            if_debug2m('b', penum->memory, "[b]pixel0 x=%g, y=%g\n",
250
41.4M
                       fixed2float(dda_current(penum->dda.pixel0.x)),
251
41.4M
                       fixed2float(dda_current(penum->dda.pixel0.y)));
252
41.4M
            code = (*penum->render)(penum, buffer, sourcex + x_used,
253
41.4M
                                    width_spp - x_used * penum->spp, 1, dev);
254
41.4M
            if (code < 0) {
255
                /* Error or interrupt, restore original state. */
256
0
                penum->used.x += x_used;
257
0
                if (!penum->used.y) {
258
0
                    dda_previous(penum->dda.row.x);
259
0
                    dda_previous(penum->dda.row.y);
260
0
                    dda_translate(penum->dda.strip.x,
261
0
                                  penum->prev.x - penum->cur.x);
262
0
                    dda_translate(penum->dda.strip.y,
263
0
                                  penum->prev.y - penum->cur.y);
264
0
                }
265
0
                goto out;
266
0
            }
267
41.4M
            penum->prev = penum->cur;
268
41.4M
        }
269
58.0M
    }
270
47.2M
    if (penum->y < penum->rect.h) {
271
45.0M
        code = 0;
272
45.0M
    } else {
273
        /* End of input data.  Render any left-over buffered data. */
274
2.25M
        code = gx_image1_flush(info);
275
2.25M
        if (code >= 0)
276
2.25M
            code = 1;
277
2.25M
    }
278
47.2M
out:
279
    /* Note that caller must call end_image */
280
    /* for both error and normal termination. */
281
47.2M
    *rows_used = penum->y - y;
282
47.2M
    return code;
283
47.2M
}
284
285
/* Flush any buffered data. */
286
int
287
gx_image1_flush(gx_image_enum_common_t * info)
288
4.60M
{
289
4.60M
    gx_image_enum *penum = (gx_image_enum *)info;
290
4.60M
    int width_spp = penum->rect.w * penum->spp;
291
4.60M
    fixed adjust = penum->adjust;
292
293
4.60M
    penum->cur.x = dda_current(penum->dda.row.x);
294
4.60M
    penum->cur.y = dda_current(penum->dda.row.y);
295
4.60M
    switch (penum->posture) {
296
4.19M
        case image_portrait:
297
4.19M
            {
298
4.19M
                fixed yc = penum->cur.y;
299
300
4.19M
                penum->yci = fixed2int_rounded(yc - adjust);
301
4.19M
                penum->hci = fixed2int_rounded(yc + adjust) - penum->yci;
302
4.19M
            }
303
4.19M
            break;
304
184k
        case image_landscape:
305
184k
            {
306
184k
                fixed xc = penum->cur.x;
307
308
184k
                penum->xci = fixed2int_rounded(xc - adjust);
309
184k
                penum->wci = fixed2int_rounded(xc + adjust) - penum->xci;
310
184k
            }
311
184k
            break;
312
230k
        case image_skewed:  /* pacify compilers */
313
230k
            ;
314
4.60M
    }
315
4.60M
    update_strip(penum);
316
4.60M
    penum->prev = penum->cur;
317
4.60M
    return (*penum->render)(penum, NULL, 0, width_spp, 0,
318
4.60M
                            setup_image_device(penum));
319
4.60M
}
320
321
/* Update the strip DDA when moving to a new row. */
322
static void
323
update_strip(gx_image_enum *penum)
324
46.0M
{
325
326
46.0M
#if 1
327
    /* Old code. */
328
46.0M
    dda_translate(penum->dda.strip.x, penum->cur.x - penum->prev.x);
329
46.0M
    dda_translate(penum->dda.strip.y, penum->cur.y - penum->prev.y);
330
46.0M
    penum->dda.pixel0 = penum->dda.strip;
331
#else
332
    /* A better precision with stromng dda_advance -
333
       doesn't work becauae gx_image1_plane_data
334
       doesn't call it at each step. */
335
    gx_dda_fixed_point temp;
336
337
    temp.x.state = penum->dda.strip.x.state;
338
    temp.y.state = penum->dda.strip.y.state;
339
    temp.x.step = penum->dda.row.x.step;
340
    temp.y.step = penum->dda.row.y.step;
341
    dda_next(temp.x);
342
    dda_next(temp.y);
343
    penum->dda.strip.x.state = temp.x.state;
344
    penum->dda.strip.y.state = temp.y.state;
345
    penum->dda.pixel0 = penum->dda.strip;
346
#endif
347
46.0M
}
348
349
/*
350
 * Repack 1 to 8 individual bit planes into 8-bit samples.
351
 * buffer is aligned, and includes padding to an 8-byte boundary.
352
 * This procedure repacks one row, so the only relevant members of
353
 * src_planes are data and data_x (not raster).
354
 */
355
static void
356
repack_bit_planes(const gx_image_plane_t *src_planes, const ulong *offsets,
357
                  int num_planes, byte *buffer, int width,
358
                  const sample_lookup_t * ptab, int spread)
359
0
{
360
0
    gx_image_plane_t planes[8];
361
0
    byte *zeros = 0;
362
0
    byte *dest = buffer;
363
0
    int any_data_x = 0;
364
0
    bool direct = (spread == 1 && ptab->lookup8[0] == 0 &&
365
0
                   ptab->lookup8[255] == 255);
366
0
    int pi, x;
367
0
    gx_image_plane_t *pp;
368
369
    /*
370
     * Set up the row pointers, taking data_x and null planes into account.
371
     * If there are any null rows, we need to create a block of zeros in
372
     * order to avoid tests in the loop.
373
     */
374
0
    for (pi = 0, pp = planes; pi < num_planes; ++pi, ++pp)
375
0
        if (src_planes[pi].data == 0) {
376
0
            if (!zeros) {
377
0
                zeros = buffer + width - ((width + 7) >> 3);
378
0
            }
379
0
            pp->data = zeros;
380
0
            pp->data_x = 0;
381
0
        } else {
382
0
            int dx = src_planes[pi].data_x;
383
384
0
            pp->data = src_planes[pi].data + (dx >> 3) + offsets[pi];
385
0
            any_data_x |= (pp->data_x = dx & 7);
386
0
        }
387
0
    if (zeros)
388
0
        memset(zeros, 0, buffer + width - zeros);
389
390
    /*
391
     * Now process the data, in blocks of one input byte column
392
     * (8 output bytes).
393
     */
394
0
    for (x = 0; x < width; x += 8) {
395
0
        bits32 w0 = 0, w1 = 0;
396
#if ARCH_IS_BIG_ENDIAN
397
        static const bits32 expand[16] = {
398
            0x00000000, 0x00000001, 0x00000100, 0x00000101,
399
            0x00010000, 0x00010001, 0x00010100, 0x00010101,
400
            0x01000000, 0x01000001, 0x01000100, 0x01000101,
401
            0x01010000, 0x01010001, 0x01010100, 0x01010101
402
        };
403
#else
404
0
        static const bits32 expand[16] = {
405
0
            0x00000000, 0x01000000, 0x00010000, 0x01010000,
406
0
            0x00000100, 0x01000100, 0x00010100, 0x01010100,
407
0
            0x00000001, 0x01000001, 0x00010001, 0x01010001,
408
0
            0x00000101, 0x01000101, 0x00010101, 0x01010101
409
0
        };
410
0
#endif
411
412
0
        if (any_data_x) {
413
0
            for (pi = 0, pp = planes; pi < num_planes; ++pi, ++pp) {
414
0
                uint b = *(pp->data++);
415
0
                int dx = pp->data_x;
416
417
0
                if (dx) {
418
0
                    b <<= dx;
419
0
                    if (x + 8 - dx < width)
420
0
                        b += *pp->data >> (8 - dx);
421
0
                }
422
0
                w0 = (w0 << 1) | expand[b >> 4];
423
0
                w1 = (w1 << 1) | expand[b & 0xf];
424
0
            }
425
0
        } else {
426
0
            for (pi = 0, pp = planes; pi < num_planes; ++pi, ++pp) {
427
0
                uint b = *(pp->data++);
428
429
0
                w0 = (w0 << 1) | expand[b >> 4];
430
0
                w1 = (w1 << 1) | expand[b & 0xf];
431
0
            }
432
0
        }
433
        /*
434
         * We optimize spread == 1 and identity ptab together, although
435
         * we could subdivide these 2 cases into 4 if we wanted.
436
         */
437
0
        if (direct) {
438
0
            ((bits32 *)dest)[0] = w0;
439
0
            ((bits32 *)dest)[1] = w1;
440
0
            dest += 8;
441
0
        } else {
442
0
#define MAP_BYTE(v) (ptab->lookup8[(byte)(v)])
443
0
            dest[0] = MAP_BYTE(w0 >> 24); dest += spread;
444
0
            dest[1] = MAP_BYTE(w0 >> 16); dest += spread;
445
0
            dest[2] = MAP_BYTE(w0 >> 8); dest += spread;
446
0
            dest[3] = MAP_BYTE(w0); dest += spread;
447
0
            dest[4] = MAP_BYTE(w1 >> 24); dest += spread;
448
0
            dest[5] = MAP_BYTE(w1 >> 16); dest += spread;
449
0
            dest[6] = MAP_BYTE(w1 >> 8); dest += spread;
450
0
            dest[7] = MAP_BYTE(w1); dest += spread;
451
0
#undef MAP_BYTE
452
0
        }
453
0
    }
454
0
}
455
456
/* Set up the device for drawing an image. */
457
static gx_device *
458
setup_image_device(const gx_image_enum *penum)
459
51.8M
{
460
51.8M
    gx_device *dev = penum->dev;
461
462
51.8M
    if (penum->clip_dev) {
463
31.7M
        gx_device_clip *cdev = penum->clip_dev;
464
465
31.7M
        gx_device_set_target((gx_device_forward *)cdev, dev);
466
31.7M
        dev = (gx_device *) cdev;
467
31.7M
    }
468
51.8M
    if (penum->rop_dev) {
469
0
        gx_device_rop_texture *rtdev = penum->rop_dev;
470
471
0
        gx_device_set_target((gx_device_forward *)rtdev, dev);
472
0
        dev = (gx_device *) rtdev;
473
0
    }
474
51.8M
    return dev;
475
51.8M
}
476
477
/* Clean up by releasing the buffers. */
478
/* Currently we ignore draw_last. */
479
int
480
gx_image1_end_image(gx_image_enum_common_t * info, bool draw_last)
481
2.28M
{
482
2.28M
    gx_image_enum *penum = (gx_image_enum *) info;
483
2.28M
    gs_memory_t *mem = penum->memory;
484
2.28M
    stream_image_scale_state *scaler = penum->scaler;
485
486
2.28M
    if_debug2m('b', penum->memory, "[b]%send_image, y=%d\n",
487
2.28M
               (penum->y < penum->rect.h ? "premature " : ""), penum->y);
488
2.28M
    if (draw_last) {
489
2.27M
        int code = gx_image_flush(info);
490
491
2.27M
        if (code < 0)
492
0
            return code;
493
2.27M
    }
494
495
2.28M
    if (penum->tpr_state != NULL) {
496
927k
        transform_pixel_region_data data;
497
927k
        gx_device *dev = penum->dev;
498
927k
        if (penum->clip_dev)
499
152k
            dev = (gx_device *)penum->clip_dev;
500
927k
        if (penum->rop_dev)
501
0
            dev = (gx_device *)penum->rop_dev;
502
927k
        data.state = penum->tpr_state;
503
927k
        dev_proc(dev, transform_pixel_region)(dev, transform_pixel_region_end, &data);
504
927k
    }
505
    /* release the reference to the target */
506
2.28M
    if ( penum->rop_dev )
507
0
        gx_device_set_target((gx_device_forward *)penum->rop_dev, NULL);
508
    /* it is not clear (to me) why these are freed explicitly instead
509
       of using reference counting */
510
2.28M
    gs_free_object(mem, penum->rop_dev, "image RasterOp");
511
512
    /* We do now reference count the clip device, see bug #706771 */
513
2.28M
    rc_decrement(penum->clip_dev, "gx_image1_end_image");
514
2.28M
    penum->clip_dev = NULL;
515
516
2.28M
    if (scaler != 0) {
517
0
        (*scaler->templat->release) ((stream_state *) scaler);
518
0
        gs_free_object(mem, scaler, "image scaler state");
519
0
    }
520
2.28M
    if (penum->icc_link != NULL) {
521
1.37M
        gsicc_release_link(penum->icc_link);
522
1.37M
    }
523
2.28M
    if (penum->color_cache != NULL) {
524
2.00k
        gs_free_object(mem, penum->color_cache->device_contone,
525
2.00k
                        "device_contone");
526
2.00k
        gs_free_object(mem, penum->color_cache->is_transparent,
527
2.00k
                       "image is_transparent");
528
2.00k
        gs_free_object(mem, penum->color_cache, "image color cache");
529
2.00k
    }
530
2.28M
    if (penum->thresh_buffer != NULL) {
531
149k
        gs_free_object(mem, penum->thresh_buffer, "image thresh_buffer");
532
149k
    }
533
2.28M
    if (penum->ht_buffer != NULL) {
534
149k
        gs_free_object(mem, penum->ht_buffer, "image ht_buffer");
535
149k
    }
536
2.28M
    if (penum->clues != NULL) {
537
992k
        gs_free_object(mem,penum->clues, "image clues");
538
992k
    }
539
540
    /* decrement this ref that was incremented in gx_image_enum_begin() */
541
2.28M
    rc_decrement_only(penum->pcs, "pcs");
542
2.28M
    penum->pcs = NULL;
543
544
2.28M
    gs_free_object(mem, penum->line, "image line");
545
2.28M
    gs_free_object(mem, penum->buffer, "image buffer");
546
547
#ifdef WITH_CAL
548
    if (penum->cal_ht != NULL) {
549
        cal_halftone_fin(penum->cal_ht, mem->non_gc_memory);
550
    }
551
#endif
552
2.28M
    gx_image_free_enum(&info);
553
2.28M
    return 0;
554
2.28M
}