Coverage Report

Created: 2026-10-13 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/thirdparty/jbig2dec/jbig2_image.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
    jbig2dec
18
*/
19
20
#ifdef HAVE_CONFIG_H
21
#include "config.h"
22
#endif
23
#include "os_types.h"
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>             /* memcpy() */
28
29
#include "jbig2.h"
30
#include "jbig2_priv.h"
31
#include "jbig2_image.h"
32
33
/* allocate a Jbig2Image structure and its associated bitmap */
34
Jbig2Image *
35
jbig2_image_new(Jbig2Ctx *ctx, uint32_t width, uint32_t height)
36
0
{
37
0
    Jbig2Image *image;
38
0
    uint32_t stride;
39
40
0
    if (width == 0 || height == 0) {
41
0
        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to create zero sized image");
42
0
        return NULL;
43
0
    }
44
45
0
    image = jbig2_new(ctx, Jbig2Image, 1);
46
0
    if (image == NULL) {
47
0
        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate image");
48
0
        return NULL;
49
0
    }
50
51
0
    stride = ((width - 1) >> 3) + 1;    /* generate a byte-aligned stride */
52
53
    /* check for integer multiplication overflow */
54
0
    if (height > (INT32_MAX / stride)) {
55
0
        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "integer multiplication overflow (stride=%u, height=%u)", stride, height);
56
0
        jbig2_free(ctx->allocator, image);
57
0
        return NULL;
58
0
    }
59
0
    image->data = jbig2_new(ctx, uint8_t, (size_t) height * stride);
60
0
    if (image->data == NULL) {
61
0
        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate image data buffer (stride=%u, height=%u)", stride, height);
62
0
        jbig2_free(ctx->allocator, image);
63
0
        return NULL;
64
0
    }
65
66
0
    image->width = width;
67
0
    image->height = height;
68
0
    image->stride = stride;
69
0
    image->refcount = 1;
70
71
0
    return image;
72
0
}
73
74
/* bump the reference count for an image pointer */
75
Jbig2Image *
76
jbig2_image_reference(Jbig2Ctx *ctx, Jbig2Image *image)
77
0
{
78
0
    (void) ctx;
79
0
    if (image)
80
0
        image->refcount++;
81
0
    return image;
82
0
}
83
84
/* release an image pointer, freeing it it appropriate */
85
void
86
jbig2_image_release(Jbig2Ctx *ctx, Jbig2Image *image)
87
0
{
88
0
    if (image == NULL)
89
0
        return;
90
0
    image->refcount--;
91
0
    if (image->refcount == 0)
92
0
        jbig2_image_free(ctx, image);
93
0
}
94
95
/* free a Jbig2Image structure and its associated memory */
96
void
97
jbig2_image_free(Jbig2Ctx *ctx, Jbig2Image *image)
98
0
{
99
0
    if (image != NULL) {
100
0
        jbig2_free(ctx->allocator, image->data);
101
0
        jbig2_free(ctx->allocator, image);
102
0
    }
103
0
}
104
105
/* resize a Jbig2Image */
106
Jbig2Image *
107
jbig2_image_resize(Jbig2Ctx *ctx, Jbig2Image *image, uint32_t width, uint32_t height, int value)
108
0
{
109
0
    if (width == image->width) {
110
0
        uint8_t *data;
111
112
        /* check for integer multiplication overflow */
113
0
        if (image->height > (INT32_MAX / image->stride)) {
114
0
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "integer multiplication overflow during resize (stride=%u, height=%u)", image->stride, height);
115
0
            return NULL;
116
0
        }
117
        /* use the same stride, just change the length */
118
0
        data = jbig2_renew(ctx, image->data, uint8_t, (size_t) height * image->stride);
119
0
        if (data == NULL) {
120
0
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to reallocate image");
121
0
            return NULL;
122
0
        }
123
0
        image->data = data;
124
0
        if (height > image->height) {
125
0
            const uint8_t fill = value ? 0xFF : 0x00;
126
0
            memset(image->data + (size_t) image->height * image->stride, fill, ((size_t) height - image->height) * image->stride);
127
0
        }
128
0
        image->height = height;
129
130
0
    } else {
131
0
        Jbig2Image *newimage;
132
0
        int code;
133
134
        /* Unoptimized implementation, but it works. */
135
136
0
        newimage = jbig2_image_new(ctx, width, height);
137
0
        if (newimage == NULL) {
138
0
            jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate resized image");
139
0
            return NULL;
140
0
        }
141
0
        jbig2_image_clear(ctx, newimage, value);
142
143
0
        code = jbig2_image_compose(ctx, newimage, image, 0, 0, JBIG2_COMPOSE_REPLACE);
144
0
        if (code < 0) {
145
0
            jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to compose image buffers when resizing");
146
0
            jbig2_image_release(ctx, newimage);
147
0
            return NULL;
148
0
        }
149
150
        /* if refcount > 1 the original image, its pointer must
151
        be kept, so simply replaces its innards, and throw away
152
        the empty new image shell. */
153
0
        jbig2_free(ctx->allocator, image->data);
154
0
        image->width = newimage->width;
155
0
        image->height = newimage->height;
156
0
        image->stride = newimage->stride;
157
0
        image->data = newimage->data;
158
0
        jbig2_free(ctx->allocator, newimage);
159
0
    }
160
161
0
    return image;
162
0
}
163
164
static inline void
165
template_image_compose_opt(const uint8_t * JBIG2_RESTRICT ss, uint8_t * JBIG2_RESTRICT dd, int early, int late, uint8_t leftmask, uint8_t rightmask, uint32_t bytewidth_, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride, Jbig2ComposeOp op)
166
0
{
167
0
    int i;
168
0
    uint32_t j;
169
0
    int bytewidth = (int)bytewidth_;
170
171
0
    if (bytewidth == 1) {
172
0
        for (j = 0; j < h; j++) {
173
            /* Only 1 byte! */
174
0
            uint8_t v = (((early ? 0 : ss[0]<<8) | (late ? 0 : ss[1]))>>shift);
175
0
            if (op == JBIG2_COMPOSE_OR)
176
0
                *dd |= v & leftmask;
177
0
            else if (op == JBIG2_COMPOSE_AND)
178
0
                *dd &= (v & leftmask) | ~leftmask;
179
0
            else if (op == JBIG2_COMPOSE_XOR)
180
0
                *dd ^= v & leftmask;
181
0
            else if (op == JBIG2_COMPOSE_XNOR)
182
0
                *dd ^= (~v) & leftmask;
183
0
            else /* Replace */
184
0
                *dd = (v & leftmask) | (*dd & ~leftmask);
185
0
            dd += dstride;
186
0
            ss += sstride;
187
0
        }
188
0
        return;
189
0
    }
190
0
    bytewidth -= 2;
191
0
    if (shift == 0) {
192
0
        ss++;
193
0
        for (j = 0; j < h; j++) {
194
            /* Left byte */
195
0
            const uint8_t * JBIG2_RESTRICT s = ss;
196
0
            uint8_t * JBIG2_RESTRICT d = dd;
197
0
            if (op == JBIG2_COMPOSE_OR)
198
0
                *d++ |= *s++ & leftmask;
199
0
            else if (op == JBIG2_COMPOSE_AND)
200
0
                *d++ &= (*s++ & leftmask) | ~leftmask;
201
0
            else if (op == JBIG2_COMPOSE_XOR)
202
0
                *d++ ^= *s++ & leftmask;
203
0
            else if (op == JBIG2_COMPOSE_XNOR)
204
0
                *d++ ^= (~*s++) & leftmask;
205
0
            else /* Replace */
206
0
                *d = (*s++ & leftmask) | (*d & ~leftmask), d++;
207
            /* Central run */
208
0
            for (i = bytewidth; i != 0; i--) {
209
0
                if (op == JBIG2_COMPOSE_OR)
210
0
                    *d++ |= *s++;
211
0
                else if (op == JBIG2_COMPOSE_AND)
212
0
                    *d++ &= *s++;
213
0
                else if (op == JBIG2_COMPOSE_XOR)
214
0
                    *d++ ^= *s++;
215
0
                else if (op == JBIG2_COMPOSE_XNOR)
216
0
                    *d++ ^= ~*s++;
217
0
                else /* Replace */
218
0
                    *d++ = *s++;
219
0
            }
220
            /* Right byte */
221
0
            if (op == JBIG2_COMPOSE_OR)
222
0
                *d |= *s & rightmask;
223
0
            else if (op == JBIG2_COMPOSE_AND)
224
0
                *d &= (*s & rightmask) | ~rightmask;
225
0
            else if (op == JBIG2_COMPOSE_XOR)
226
0
                *d ^= *s & rightmask;
227
0
            else if (op == JBIG2_COMPOSE_XNOR)
228
0
                *d ^= (~*s) & rightmask;
229
0
            else /* Replace */
230
0
                *d = (*s & rightmask) | (*d & ~rightmask);
231
0
            dd += dstride;
232
0
            ss += sstride;
233
0
        }
234
0
    } else {
235
0
        for (j = 0; j < h; j++) {
236
            /* Left byte */
237
0
            const uint8_t * JBIG2_RESTRICT s = ss;
238
0
            uint8_t * JBIG2_RESTRICT d = dd;
239
0
            uint8_t s0, s1, v;
240
0
            s0 = early ? 0 : *s;
241
0
            s++;
242
0
            s1 = *s++;
243
0
            v = ((s0<<8) | s1)>>shift;
244
0
            if (op == JBIG2_COMPOSE_OR)
245
0
                *d++ |= v & leftmask;
246
0
            else if (op == JBIG2_COMPOSE_AND)
247
0
                *d++ &= (v & leftmask) | ~leftmask;
248
0
            else if (op == JBIG2_COMPOSE_XOR)
249
0
                *d++ ^= v & leftmask;
250
0
            else if (op == JBIG2_COMPOSE_XNOR)
251
0
                *d++ ^= (~v) & leftmask;
252
0
            else /* Replace */
253
0
                *d = (v & leftmask) | (*d & ~leftmask), d++;
254
            /* Central run */
255
0
            for (i = bytewidth; i > 0; i--) {
256
0
                s0 = s1; s1 = *s++;
257
0
                v = ((s0<<8) | s1)>>shift;
258
0
                if (op == JBIG2_COMPOSE_OR)
259
0
                    *d++ |= v;
260
0
                else if (op == JBIG2_COMPOSE_AND)
261
0
                    *d++ &= v;
262
0
                else if (op == JBIG2_COMPOSE_XOR)
263
0
                    *d++ ^= v;
264
0
                else if (op == JBIG2_COMPOSE_XNOR)
265
0
                    *d++ ^= ~v;
266
0
                else /* Replace */
267
0
                    *d++ = v;
268
0
            }
269
            /* Right byte */
270
0
            s0 = s1; s1 = (late ? 0 : *s);
271
0
            v = (((s0<<8) | s1)>>shift);
272
0
            if (op == JBIG2_COMPOSE_OR)
273
0
                *d |= v & rightmask;
274
0
            else if (op == JBIG2_COMPOSE_AND)
275
0
                *d &= (v & rightmask) | ~rightmask;
276
0
            else if (op == JBIG2_COMPOSE_XOR)
277
0
                *d ^= v & rightmask;
278
0
            else if (op == JBIG2_COMPOSE_XNOR)
279
0
                *d ^= ~v & rightmask;
280
0
            else /* Replace */
281
0
                *d = (v & rightmask) | (*d & ~rightmask);
282
0
            dd += dstride;
283
0
            ss += sstride;
284
0
        }
285
0
    }
286
0
}
287
288
static void
289
jbig2_image_compose_opt_OR(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride)
290
0
{
291
0
    if (early || late)
292
0
        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_OR);
293
0
    else
294
0
        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_OR);
295
0
}
296
297
static void
298
jbig2_image_compose_opt_AND(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride)
299
0
{
300
0
    if (early || late)
301
0
        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_AND);
302
0
    else
303
0
        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_AND);
304
0
}
305
306
static void
307
jbig2_image_compose_opt_XOR(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride)
308
0
{
309
0
    if (early || late)
310
0
        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XOR);
311
0
    else
312
0
        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XOR);
313
0
}
314
315
static void
316
jbig2_image_compose_opt_XNOR(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride)
317
0
{
318
0
    if (early || late)
319
0
        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XNOR);
320
0
    else
321
0
        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XNOR);
322
0
}
323
324
static void
325
jbig2_image_compose_opt_REPLACE(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride)
326
0
{
327
0
    if (early || late)
328
0
        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_REPLACE);
329
0
    else
330
0
        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_REPLACE);
331
0
}
332
333
/* composite one jbig2_image onto another */
334
int
335
jbig2_image_compose(Jbig2Ctx *ctx, Jbig2Image *dst, Jbig2Image *src, int64_t x, int64_t y, Jbig2ComposeOp op)
336
0
{
337
0
    uint32_t w, h;
338
0
    uint32_t shift;
339
0
    uint32_t leftbyte;
340
0
    uint8_t *ss;
341
0
    uint8_t *dd;
342
0
    uint8_t leftmask, rightmask;
343
0
    int early = x >= 0;
344
0
    int late;
345
0
    uint32_t bytewidth;
346
0
    uint32_t syoffset = 0;
347
348
0
    (void) ctx;
349
350
0
    if (src == NULL)
351
0
        return 0;
352
353
    /* Detect if src image has no overlap with the dst image.
354
     * Because the widths/heights are of type uint32_t their theoretical
355
     * maximum size is UINT32_MAX. Therefore this check also rejects any
356
     * x/y values outside the range [-UINT32_MAX + 1, UINT32_MAX - 1].
357
     * And after this if-statement x/y must necessarily fall within this
358
     * closed range.
359
     */
360
0
    if (
361
0
            (x <= -((int64_t) src->width)) || (x >= (int64_t) dst->width) ||
362
0
            (y <= -((int64_t) src->height)) || (y >= (int64_t) dst->height))
363
0
    {
364
0
        jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "src image entirely outside dst image in compose_image");
365
0
        return 0;
366
0
    }
367
368
    /* This code takes a src image and combines it onto dst at offset (x,y), with operation op. */
369
370
    /* Data is packed msb first within a byte, so with bits numbered: 01234567.
371
     * Second byte is: 89abcdef. So to combine into a run, we use:
372
     *       (s[0]<<8) | s[1] == 0123456789abcdef.
373
     * To read from src into dst at offset 3, we need to read:
374
     *    read:      0123456789abcdef...
375
     *    write:  0123456798abcdef...
376
     * In general, to read from src and write into dst at offset x, we need to shift
377
     * down by (x&7) bits to allow for bit alignment. So shift = x&7.
378
     * So the 'central' part of our runs will see us doing:
379
     *   *d++ op= ((s[0]<<8)|s[1])>>shift;
380
     * with special cases on the left and right edges of the run to mask.
381
     * With the left hand edge, we have to be careful not to 'underread' the start of
382
     * the src image; this is what the early flag is about. Similarly we have to be
383
     * careful not to read off the right hand edge; this is what the late flag is for.
384
     */
385
386
0
    w = src->width;
387
0
    h = src->height;
388
0
    shift = (uint32_t) (x & 7);
389
0
    ss = src->data - early;
390
391
    /* Since we know that x/y are now limited to [-UINT32_MAX + 1, UINT32_MAX - 1],
392
     * we know that we can't accidentally negate an INT64_MIN. Moreover, we know
393
     * that their negated values fit within an uint32_t, so casting to uint32_t is
394
     * safe.
395
     */
396
397
    /* clip left/top */
398
0
    if (x < 0) {
399
0
        uint32_t negx = (uint32_t) (-x);
400
0
        w -= negx;
401
0
        ss += (negx-1)>>3;
402
0
        x = 0;
403
0
    }
404
0
    if (y < 0) {
405
0
        uint32_t negy = (uint32_t) (-y);
406
0
        h -= negy;
407
0
        syoffset = negy * src->stride;
408
0
        y = 0;
409
0
    }
410
411
    /* clip right/bottom */
412
0
    if ((uint32_t)x + w > dst->width)
413
0
        w = dst->width - ((uint32_t) x);
414
0
    if ((uint32_t)y + h > dst->height)
415
0
        h = dst->height - ((uint32_t) y);
416
#ifdef JBIG2_DEBUG
417
    jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "compositing %ux%u at (%u, %u) after clipping",
418
        w, h, (uint32_t) x, (uint32_t) y);
419
#endif
420
421
    /* check for zero clipping region */
422
0
    if ((w <= 0) || (h <= 0)) {
423
#ifdef JBIG2_DEBUG
424
        jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "zero clipping region");
425
#endif
426
0
        return 0;
427
0
    }
428
429
0
    leftbyte = (uint32_t) x >> 3;
430
0
    dd = dst->data + y * dst->stride + leftbyte;
431
0
    bytewidth = (((uint32_t) x + w - 1) >> 3) - leftbyte + 1;
432
0
    leftmask = 255>>(x&7);
433
0
    rightmask = (((x+w)&7) == 0) ? 255 : ~(255>>((x+w)&7));
434
0
    if (bytewidth == 1)
435
0
        leftmask &= rightmask;
436
0
    late = (ss + bytewidth >= src->data + ((src->width+7)>>3));
437
0
    ss += syoffset;
438
439
0
    switch(op)
440
0
    {
441
0
    case JBIG2_COMPOSE_OR:
442
0
        jbig2_image_compose_opt_OR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
443
0
        break;
444
0
    case JBIG2_COMPOSE_AND:
445
0
        jbig2_image_compose_opt_AND(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
446
0
        break;
447
0
    case JBIG2_COMPOSE_XOR:
448
0
        jbig2_image_compose_opt_XOR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
449
0
        break;
450
0
    case JBIG2_COMPOSE_XNOR:
451
0
        jbig2_image_compose_opt_XNOR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
452
0
        break;
453
0
    case JBIG2_COMPOSE_REPLACE:
454
0
        jbig2_image_compose_opt_REPLACE(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
455
0
        break;
456
0
    }
457
458
0
    return 0;
459
0
}
460
461
/* initialize an image bitmap to a constant value */
462
void
463
jbig2_image_clear(Jbig2Ctx *ctx, Jbig2Image *image, int value)
464
0
{
465
0
    const uint8_t fill = value ? 0xFF : 0x00;
466
467
0
    (void) ctx;
468
469
0
    memset(image->data, fill, image->stride * image->height);
470
0
}
471
472
/* look up a pixel value in an image.
473
   returns 0 outside the image frame for the convenience of
474
   the template code
475
*/
476
int
477
jbig2_image_get_pixel(Jbig2Image *image, int64_t x, int64_t y)
478
0
{
479
0
    const int64_t w = image->width;
480
0
    const int64_t h = image->height;
481
0
    size_t sx, sy;
482
0
    size_t byte;
483
0
    int bit;
484
485
0
    if ((x < 0) || (x >= w))
486
0
        return 0;
487
0
    if ((y < 0) || (y >= h))
488
0
        return 0;
489
490
0
    sx = (size_t) x;
491
0
    sy = (size_t) y;
492
493
0
    byte = (sx >> 3) + sy * image->stride;
494
0
    bit = 7 - ((int) (sx & 7));
495
496
0
    return ((image->data[byte] >> bit) & 1);
497
0
}
498
499
/* set an individual pixel value in an image */
500
void
501
jbig2_image_set_pixel(Jbig2Image *image, int64_t x, int64_t y, bool value)
502
0
{
503
0
    const int64_t w = image->width;
504
0
    const int64_t h = image->height;
505
0
    uint8_t scratch, mask;
506
0
    size_t sx, sy;
507
0
    size_t byte;
508
0
    int bit;
509
510
0
    if ((x < 0) || (x >= w))
511
0
        return;
512
0
    if ((y < 0) || (y >= h))
513
0
        return;
514
515
0
    sx = (size_t) x;
516
0
    sy = (size_t) y;
517
518
0
    byte = (sx >> 3) + sy * image->stride;
519
0
    bit = 7 - ((int) (sx & 7));
520
0
    mask = (uint8_t) ((1 << bit) ^ 0xff);
521
522
0
    scratch = image->data[byte] & mask;
523
0
    image->data[byte] = scratch | (value << bit);
524
0
}