diff mupdf-source/thirdparty/jbig2dec/jbig2_image.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/jbig2dec/jbig2_image.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,506 @@
+/* Copyright (C) 2001-2023 Artifex Software, Inc.
+   All Rights Reserved.
+
+   This software is provided AS-IS with no warranty, either express or
+   implied.
+
+   This software is distributed under license and may not be copied,
+   modified or distributed except as expressly authorized under the terms
+   of the license contained in the file LICENSE in this distribution.
+
+   Refer to licensing information at http://www.artifex.com or contact
+   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
+   CA 94129, USA, for further information.
+*/
+
+/*
+    jbig2dec
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "os_types.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>             /* memcpy() */
+
+#include "jbig2.h"
+#include "jbig2_priv.h"
+#include "jbig2_image.h"
+
+/* allocate a Jbig2Image structure and its associated bitmap */
+Jbig2Image *
+jbig2_image_new(Jbig2Ctx *ctx, uint32_t width, uint32_t height)
+{
+    Jbig2Image *image;
+    uint32_t stride;
+
+    if (width == 0 || height == 0) {
+        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to create zero sized image");
+        return NULL;
+    }
+
+    image = jbig2_new(ctx, Jbig2Image, 1);
+    if (image == NULL) {
+        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate image");
+        return NULL;
+    }
+
+    stride = ((width - 1) >> 3) + 1;    /* generate a byte-aligned stride */
+
+    /* check for integer multiplication overflow */
+    if (height > (INT32_MAX / stride)) {
+        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "integer multiplication overflow (stride=%u, height=%u)", stride, height);
+        jbig2_free(ctx->allocator, image);
+        return NULL;
+    }
+    image->data = jbig2_new(ctx, uint8_t, (size_t) height * stride);
+    if (image->data == NULL) {
+        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate image data buffer (stride=%u, height=%u)", stride, height);
+        jbig2_free(ctx->allocator, image);
+        return NULL;
+    }
+
+    image->width = width;
+    image->height = height;
+    image->stride = stride;
+    image->refcount = 1;
+
+    return image;
+}
+
+/* bump the reference count for an image pointer */
+Jbig2Image *
+jbig2_image_reference(Jbig2Ctx *ctx, Jbig2Image *image)
+{
+    if (image)
+        image->refcount++;
+    return image;
+}
+
+/* release an image pointer, freeing it it appropriate */
+void
+jbig2_image_release(Jbig2Ctx *ctx, Jbig2Image *image)
+{
+    if (image == NULL)
+        return;
+    image->refcount--;
+    if (image->refcount == 0)
+        jbig2_image_free(ctx, image);
+}
+
+/* free a Jbig2Image structure and its associated memory */
+void
+jbig2_image_free(Jbig2Ctx *ctx, Jbig2Image *image)
+{
+    if (image != NULL) {
+        jbig2_free(ctx->allocator, image->data);
+        jbig2_free(ctx->allocator, image);
+    }
+}
+
+/* resize a Jbig2Image */
+Jbig2Image *
+jbig2_image_resize(Jbig2Ctx *ctx, Jbig2Image *image, uint32_t width, uint32_t height, int value)
+{
+    if (width == image->width) {
+        uint8_t *data;
+
+        /* check for integer multiplication overflow */
+        if (image->height > (INT32_MAX / image->stride)) {
+            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "integer multiplication overflow during resize (stride=%u, height=%u)", image->stride, height);
+            return NULL;
+        }
+        /* use the same stride, just change the length */
+        data = jbig2_renew(ctx, image->data, uint8_t, (size_t) height * image->stride);
+        if (data == NULL) {
+            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to reallocate image");
+            return NULL;
+        }
+        image->data = data;
+        if (height > image->height) {
+            const uint8_t fill = value ? 0xFF : 0x00;
+            memset(image->data + (size_t) image->height * image->stride, fill, ((size_t) height - image->height) * image->stride);
+        }
+        image->height = height;
+
+    } else {
+        Jbig2Image *newimage;
+        int code;
+
+        /* Unoptimized implementation, but it works. */
+
+        newimage = jbig2_image_new(ctx, width, height);
+        if (newimage == NULL) {
+            jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to allocate resized image");
+            return NULL;
+        }
+        jbig2_image_clear(ctx, newimage, value);
+
+        code = jbig2_image_compose(ctx, newimage, image, 0, 0, JBIG2_COMPOSE_REPLACE);
+        if (code < 0) {
+            jbig2_error(ctx, JBIG2_SEVERITY_WARNING, JBIG2_UNKNOWN_SEGMENT_NUMBER, "failed to compose image buffers when resizing");
+            jbig2_image_release(ctx, newimage);
+            return NULL;
+        }
+
+        /* if refcount > 1 the original image, its pointer must
+        be kept, so simply replaces its innards, and throw away
+        the empty new image shell. */
+        jbig2_free(ctx->allocator, image->data);
+        image->width = newimage->width;
+        image->height = newimage->height;
+        image->stride = newimage->stride;
+        image->data = newimage->data;
+        jbig2_free(ctx->allocator, newimage);
+    }
+
+    return image;
+}
+
+static inline void
+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)
+{
+    int i;
+    uint32_t j;
+    int bytewidth = (int)bytewidth_;
+
+    if (bytewidth == 1) {
+        for (j = 0; j < h; j++) {
+            /* Only 1 byte! */
+            uint8_t v = (((early ? 0 : ss[0]<<8) | (late ? 0 : ss[1]))>>shift);
+            if (op == JBIG2_COMPOSE_OR)
+                *dd |= v & leftmask;
+            else if (op == JBIG2_COMPOSE_AND)
+                *dd &= (v & leftmask) | ~leftmask;
+            else if (op == JBIG2_COMPOSE_XOR)
+                *dd ^= v & leftmask;
+            else if (op == JBIG2_COMPOSE_XNOR)
+                *dd ^= (~v) & leftmask;
+            else /* Replace */
+                *dd = (v & leftmask) | (*dd & ~leftmask);
+            dd += dstride;
+            ss += sstride;
+        }
+        return;
+    }
+    bytewidth -= 2;
+    if (shift == 0) {
+        ss++;
+        for (j = 0; j < h; j++) {
+            /* Left byte */
+            const uint8_t * JBIG2_RESTRICT s = ss;
+            uint8_t * JBIG2_RESTRICT d = dd;
+            if (op == JBIG2_COMPOSE_OR)
+                *d++ |= *s++ & leftmask;
+            else if (op == JBIG2_COMPOSE_AND)
+                *d++ &= (*s++ & leftmask) | ~leftmask;
+            else if (op == JBIG2_COMPOSE_XOR)
+                *d++ ^= *s++ & leftmask;
+            else if (op == JBIG2_COMPOSE_XNOR)
+                *d++ ^= (~*s++) & leftmask;
+            else /* Replace */
+                *d = (*s++ & leftmask) | (*d & ~leftmask), d++;
+            /* Central run */
+            for (i = bytewidth; i != 0; i--) {
+                if (op == JBIG2_COMPOSE_OR)
+                    *d++ |= *s++;
+                else if (op == JBIG2_COMPOSE_AND)
+                    *d++ &= *s++;
+                else if (op == JBIG2_COMPOSE_XOR)
+                    *d++ ^= *s++;
+                else if (op == JBIG2_COMPOSE_XNOR)
+                    *d++ ^= ~*s++;
+                else /* Replace */
+                    *d++ = *s++;
+            }
+            /* Right byte */
+            if (op == JBIG2_COMPOSE_OR)
+                *d |= *s & rightmask;
+            else if (op == JBIG2_COMPOSE_AND)
+                *d &= (*s & rightmask) | ~rightmask;
+            else if (op == JBIG2_COMPOSE_XOR)
+                *d ^= *s & rightmask;
+            else if (op == JBIG2_COMPOSE_XNOR)
+                *d ^= (~*s) & rightmask;
+            else /* Replace */
+                *d = (*s & rightmask) | (*d & ~rightmask);
+            dd += dstride;
+            ss += sstride;
+        }
+    } else {
+        for (j = 0; j < h; j++) {
+            /* Left byte */
+            const uint8_t * JBIG2_RESTRICT s = ss;
+            uint8_t * JBIG2_RESTRICT d = dd;
+            uint8_t s0, s1, v;
+            s0 = early ? 0 : *s;
+            s++;
+            s1 = *s++;
+            v = ((s0<<8) | s1)>>shift;
+            if (op == JBIG2_COMPOSE_OR)
+                *d++ |= v & leftmask;
+            else if (op == JBIG2_COMPOSE_AND)
+                *d++ &= (v & leftmask) | ~leftmask;
+            else if (op == JBIG2_COMPOSE_XOR)
+                *d++ ^= v & leftmask;
+            else if (op == JBIG2_COMPOSE_XNOR)
+                *d++ ^= (~v) & leftmask;
+            else /* Replace */
+                *d = (v & leftmask) | (*d & ~leftmask), d++;
+            /* Central run */
+            for (i = bytewidth; i > 0; i--) {
+                s0 = s1; s1 = *s++;
+                v = ((s0<<8) | s1)>>shift;
+                if (op == JBIG2_COMPOSE_OR)
+                    *d++ |= v;
+                else if (op == JBIG2_COMPOSE_AND)
+                    *d++ &= v;
+                else if (op == JBIG2_COMPOSE_XOR)
+                    *d++ ^= v;
+                else if (op == JBIG2_COMPOSE_XNOR)
+                    *d++ ^= ~v;
+                else /* Replace */
+                    *d++ = v;
+            }
+            /* Right byte */
+            s0 = s1; s1 = (late ? 0 : *s);
+            v = (((s0<<8) | s1)>>shift);
+            if (op == JBIG2_COMPOSE_OR)
+                *d |= v & rightmask;
+            else if (op == JBIG2_COMPOSE_AND)
+                *d &= (v & rightmask) | ~rightmask;
+            else if (op == JBIG2_COMPOSE_XOR)
+                *d ^= v & rightmask;
+            else if (op == JBIG2_COMPOSE_XNOR)
+                *d ^= ~v & rightmask;
+            else /* Replace */
+                *d = (v & rightmask) | (*d & ~rightmask);
+            dd += dstride;
+            ss += sstride;
+        }
+    }
+}
+
+static void
+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)
+{
+    if (early || late)
+        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_OR);
+    else
+        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_OR);
+}
+
+static void
+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)
+{
+    if (early || late)
+        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_AND);
+    else
+        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_AND);
+}
+
+static void
+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)
+{
+    if (early || late)
+        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XOR);
+    else
+        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XOR);
+}
+
+static void
+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)
+{
+    if (early || late)
+        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XNOR);
+    else
+        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XNOR);
+}
+
+static void
+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)
+{
+    if (early || late)
+        template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_REPLACE);
+    else
+        template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_REPLACE);
+}
+
+/* composite one jbig2_image onto another */
+int
+jbig2_image_compose(Jbig2Ctx *ctx, Jbig2Image *dst, Jbig2Image *src, int x, int y, Jbig2ComposeOp op)
+{
+    uint32_t w, h;
+    uint32_t shift;
+    uint32_t leftbyte;
+    uint8_t *ss;
+    uint8_t *dd;
+    uint8_t leftmask, rightmask;
+    int early = x >= 0;
+    int late;
+    uint32_t bytewidth;
+    uint32_t syoffset = 0;
+
+    if (src == NULL)
+        return 0;
+
+    if ((UINT32_MAX - src->width  < (uint32_t) (x > 0 ? x : -x)) ||
+        (UINT32_MAX - src->height < (uint32_t) (y > 0 ? y : -y)))
+    {
+#ifdef JBIG2_DEBUG
+        jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "overflow in compose_image");
+#endif
+        return 0;
+    }
+
+    /* This code takes a src image and combines it onto dst at offset (x,y), with operation op. */
+
+    /* Data is packed msb first within a byte, so with bits numbered: 01234567.
+     * Second byte is: 89abcdef. So to combine into a run, we use:
+     *       (s[0]<<8) | s[1] == 0123456789abcdef.
+     * To read from src into dst at offset 3, we need to read:
+     *    read:      0123456789abcdef...
+     *    write:  0123456798abcdef...
+     * In general, to read from src and write into dst at offset x, we need to shift
+     * down by (x&7) bits to allow for bit alignment. So shift = x&7.
+     * So the 'central' part of our runs will see us doing:
+     *   *d++ op= ((s[0]<<8)|s[1])>>shift;
+     * with special cases on the left and right edges of the run to mask.
+     * With the left hand edge, we have to be careful not to 'underread' the start of
+     * the src image; this is what the early flag is about. Similarly we have to be
+     * careful not to read off the right hand edge; this is what the late flag is for.
+     */
+
+    /* clip */
+    w = src->width;
+    h = src->height;
+    shift = (x & 7);
+    ss = src->data - early;
+
+    if (x < 0) {
+        if (w < (uint32_t) -x)
+            w = 0;
+        else
+            w += x;
+        ss += (-x-1)>>3;
+        x = 0;
+    }
+    if (y < 0) {
+        if (h < (uint32_t) -y)
+            h = 0;
+        else
+            h += y;
+        syoffset = -y * src->stride;
+        y = 0;
+    }
+    if ((uint32_t)x + w > dst->width)
+    {
+        if (dst->width < (uint32_t)x)
+            w = 0;
+        else
+            w = dst->width - x;
+    }
+    if ((uint32_t)y + h > dst->height)
+    {
+        if (dst->height < (uint32_t)y)
+            h = 0;
+        else
+            h = dst->height - y;
+    }
+#ifdef JBIG2_DEBUG
+    jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "compositing %dx%d at (%d, %d) after clipping", w, h, x, y);
+#endif
+
+    /* check for zero clipping region */
+    if ((w <= 0) || (h <= 0)) {
+#ifdef JBIG2_DEBUG
+        jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, JBIG2_UNKNOWN_SEGMENT_NUMBER, "zero clipping region");
+#endif
+        return 0;
+    }
+
+    leftbyte = (uint32_t) x >> 3;
+    dd = dst->data + y * dst->stride + leftbyte;
+    bytewidth = (((uint32_t) x + w - 1) >> 3) - leftbyte + 1;
+    leftmask = 255>>(x&7);
+    rightmask = (((x+w)&7) == 0) ? 255 : ~(255>>((x+w)&7));
+    if (bytewidth == 1)
+        leftmask &= rightmask;
+    late = (ss + bytewidth >= src->data + ((src->width+7)>>3));
+    ss += syoffset;
+
+    switch(op)
+    {
+    case JBIG2_COMPOSE_OR:
+        jbig2_image_compose_opt_OR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
+        break;
+    case JBIG2_COMPOSE_AND:
+        jbig2_image_compose_opt_AND(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
+        break;
+    case JBIG2_COMPOSE_XOR:
+        jbig2_image_compose_opt_XOR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
+        break;
+    case JBIG2_COMPOSE_XNOR:
+        jbig2_image_compose_opt_XNOR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
+        break;
+    case JBIG2_COMPOSE_REPLACE:
+        jbig2_image_compose_opt_REPLACE(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride);
+        break;
+    }
+
+    return 0;
+}
+
+/* initialize an image bitmap to a constant value */
+void
+jbig2_image_clear(Jbig2Ctx *ctx, Jbig2Image *image, int value)
+{
+    const uint8_t fill = value ? 0xFF : 0x00;
+
+    memset(image->data, fill, image->stride * image->height);
+}
+
+/* look up a pixel value in an image.
+   returns 0 outside the image frame for the convenience of
+   the template code
+*/
+int
+jbig2_image_get_pixel(Jbig2Image *image, int x, int y)
+{
+    const int w = image->width;
+    const int h = image->height;
+    const int byte = (x >> 3) + y * image->stride;
+    const int bit = 7 - (x & 7);
+
+    if ((x < 0) || (x >= w))
+        return 0;
+    if ((y < 0) || (y >= h))
+        return 0;
+
+    return ((image->data[byte] >> bit) & 1);
+}
+
+/* set an individual pixel value in an image */
+void
+jbig2_image_set_pixel(Jbig2Image *image, int x, int y, bool value)
+{
+    const int w = image->width;
+    const int h = image->height;
+    int scratch, mask;
+    int bit, byte;
+
+    if ((x < 0) || (x >= w))
+        return;
+    if ((y < 0) || (y >= h))
+        return;
+
+    byte = (x >> 3) + y * image->stride;
+    bit = 7 - (x & 7);
+    mask = (1 << bit) ^ 0xff;
+
+    scratch = image->data[byte] & mask;
+    image->data[byte] = scratch | (value << bit);
+}