shithub: jbig2

ref: 3f91590d98fe9c341779dcfb2d13cab320073e31
dir: /jbig2_generic.c/

View raw version
/* Copyright (C) 2001-2019 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.,  1305 Grant Avenue - Suite 200, Novato,
   CA 94945, U.S.A., +1(415)492-9861, for further information.
*/

/*
    jbig2dec
*/

/**
 * Generic region handlers.
 **/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "os_types.h"

#include <stddef.h>
#include <string.h>             /* memcpy(), memset() */

#ifdef OUTPUT_PBM
#include <stdio.h>
#endif

#include "jbig2.h"
#include "jbig2_priv.h"
#include "jbig2_arith.h"
#include "jbig2_generic.h"
#include "jbig2_image.h"
#include "jbig2_mmr.h"
#include "jbig2_page.h"
#include "jbig2_segment.h"

#if !defined (UINT32_MAX)
#define UINT32_MAX 0xffffffff
#endif

/*
This is an explanation of the unoptimized and optimized generic
region decoder implementations below, wherein we try to explain
all the magic numbers.

The generic region decoders decode the output pixels one row at a
time, top to bottom. Within each row the pixels are decoded left
to right. The input for the arithmetic integer decoder used to
decode each pixel is a context consisting of up to 16 previously
decoded pixels. These pixels are chosen according to a predefined
template placed relative to the location of the pixel to be
decoded (6.2.5.3 figures 3, 4, 5 and 6). There are four different
template that may be used (6.2.5.3). The template to use is
determined by GBTEMPLATE. GBTEMPLATE is set in the symbol
dictionary (6.5.8.1), generic region (7.4.6.4), or when decoding
a halftone region's gray-scale image (annex C.5).

Most of the pixels in each template have fixed locations relative
to the pixel to be decoded. However, all templates have at least
one adaptive pixel. The adaptive pixels have nominal locations,
but these locations may be changed by GBAT. GBAT is set in the
symbol dictionary (7.4.2.1.2), generic region (7.4.6.1), or hard
coded as for halftone patterns (6.7.5).

Adaptive pixels are restricted to fall within a field of
previously decoded pixels relative to the pixel to be decoded
(figure 7). The relative Y-coordinate for these adaptive pixels
may vary between -128 and 0. The relative X-coordinate may vary
between -128 and +127 (however, if the Y-coordinate is 0 the
range of the X-coordinate is further restricted to -128 to -1
since the pixels at locations 0 to +127 have not yet been
decoded). If a template refers to a pixel location that reside
outside of the image boundaries its value is assumed to be 0.

UNOPTIMIZED DECODER

The unoptimized decoders first check the contents of GBAT. If
GBAT specifies that any of the adaptive pixels reside outside the
allowed field the decoding is aborted. Next, each row is
processed top to bottom, left to right, one pixel at a time. For
each pixel a context is created containing the bit values of the
pixels that fall inside the template.

The order these bits are stored in the context is implementation
dependent (6.2.5.3). We store the bit values in the CONTEXT
variable from LSB to MSB, starting with the value of the pixel to
the left of the current pixel, continuing right to left, bottom
to top following the template. Using the CONTEXT created from
these pixel values, the arithmetic integer decoder retrieves the
pixel value, which is then written into the output image.

Example when GBTEMPLATE is 2:

The figure below represents a pixel grid of the output image.
Each pixel is a single bit in the image. The pixel "OO" in the
figure below is about to be decoded. The pixels "??" have not
been decoded yet. The CONTEXT variable is constructed by
combining the bit values from the pixels referred to by the
template, shifted to their corresponding bit position.

     .    .    .    .    .    .    .    .
     .    .    .    .    .    .    .    .
  ...+----+----+----+----+----+----+----+...
     |    |    | X9 | X8 | X7 |    |    |
  ...+----+----+----+----+----+----+----+...
     |    | X6 | X5 | X4 | X3 | A1 |    |
  ...+----+----+----+----+----+----+----+...
     |    | X2 | X1 | OO | ?? | ?? | ?? |
  ...+----+----+----+----+----+----+----+...
     .    .    .    .    .    .    .    .
     .    .    .    .    .    .    .    .

In the table below pixel OO is assumed to be at coordinate (x, y).

Bit 9: Pixel at location (x-1, y-2) (This is fixed pixel X9)
Bit 8: Pixel at location (x  , y-2) (This is fixed pixel X8)
Bit 7: Pixel at location (x+1, y-2) (This is fixed pixel X7)
Bit 6: Pixel at location (x-2, y-1) (This is fixed pixel X6)
Bit 5: Pixel at location (x-1, y-1) (This is fixed pixel X5)
Bit 4: Pixel at location (x  , y-1) (This is fixed pixel X4)
Bit 3: Pixel at location (x+1, y-1) (This is fixed pixel X3)
Bit 2: Pixel at location (x+2, y-1) (This is adaptive pixel A1)
Bit 1: Pixel at location (x-2, y  ) (This is fixed pixel X2)
Bit 0: Pixel at location (x-1, y  ) (This is fixed pixel X1)

The location of adaptive pixel A1 may not always be at the
nominal location (x+2, y-1). It could be at any pixel location to
the left or above OO as specified by GBAT, e.g. at the location
(x-128, y+127).

OPTIMIZED DECODER

The optimized decoders work differently. They strive to avoid
recreating the arithmetic integer decoder context from scratch
for every pixel decoded. Instead they reuse part of the CONTEXT
used to compute the previous pixel (the pixel to left of the one
now being decoded). They also keep two sliding windows of pixel
bit values from the two rows of pixels immediately above the
pixel to be decoded. These are stored in the 32-bit variables
line_m1 (row above the pixel to be decoded) and line_m2 (row
above that of line_m1). These optimized decoders ONLY work for
the nominal adaptive pixel locations since these locations are
hard-coded into the implementation.

The bit ordering in the CONTEXT variable is identical to the
unoptimized case described above.

The optimized decoders decode the output pixels one row at a
time, top to bottom. Within each row the pixels are decoded in
batches of up to eight pixels at a time (except possibly the
right most batch which may be less than eight pixels). The
batches in a row are decoded in sequence from left to right.
Within each such batch the pixels are decoded in sequence from
left to right.

Before decoding the pixels in a row the two sliding windows of
pixel values are reset. The first eight pixels of the row above
the pixel to be decoded is stored in line_m1, while line_m2
stores the first eight pixels of the row above that of line_m1.

The figure below illustrates the situation where the template has
been placed so that the decoded pixel OO is the very first pixel
of a row. It also gives labels to various pixels that we will
refer to below.

             .    .    .    .    .    .    .    .    .    .    .
             |    .    .    .    .    .    .    .    .    .    .
   +    +    +----+----+----+----+----+----+----+----+----+----+...
          X9 | X8 | X7 | m1 | m2 | m3 | m4 | m5 | m6 | m7 |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+...
     X6   X5 | X4 | X3 | A1 | n1 | n2 | n3 | n4 | n5 | n6 | n7 |
   +    +    +----+----+----+----+----+----+----+----+----+----+...
     X2   X1 | OO |    |    |    |    |    |    |    |    |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+...
             |    .    .    .    .    .    .    .    .    .    .
             .    .    .    .    .    .    .    .    .    .    .

The pixels X1, X2, X5, X6 and X9 all reside outside the left edge
of the image. These pixels (like all others outside the image)
can according to 6.2.5.2 be assumed to be 0. line_m1 stores n5
through n1 as well as A1, and X3 through X6. line_m2 stores m6
through m1 as well as X7 through X9. The bits in line_m2 are also
shifted left four bits as seen below.

15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 | bit position
------------------------------------------------+------------------
 0  0  0  0  0  0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5 | line_m1
 0  0  0 X9 X8 X7 m1 m2 m3 m4 m5 m6  0  0  0  0 | line_m2

The way line_m1 and line_m2 are stored means we can simply shift
them by the same amount to move the sliding window.

The bit order in line_m1 and line_m2 matches the ordering in the
CONTEXT variable. Each bit for the 'A' and 'X' pixels in line_m1
and line_m2 correspond to the equivalent bits in CONTEXT, only
shifted right by 3 bits. Thus X3 is bit 3 in CONTEXT and bit 6 in
line_m1, etc.

The initial arithmetic integer decoder context is created and
stored in the CONTEXT variable by masking, shifting, and bitwise
ORing the contents of line_m1 and line_m2. The "CONTEXT contents"
row is only shown for clarity, it is not present in the code.

15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 | bit position
------------------------------------------------+---------------------------
 0  0  0  0  0  0  0  0  0 X6 X5 X4 X3 A1 n1 n2 | line_m1 >> 3
 0  0  0  0  0  0  0  0  0  1  1  1  1  1  0  0 | mask for line_m1 (0x7c)
 0  0  0  0  0  0  0  0  0 X6 X5 X4 X3 A1  0  0 | line_m1 AND mask
------------------------------------------------+---------------------------
 0  0  0  0  0  0 X9 X8 X7 m1 m2 m3 m4 m5 m6  0 | line_m2 >> 3
 0  0  0  0  0  0  1  1  1  0  0  0  0  0  0  0 | mask for line_m2 (0x380)
 0  0  0  0  0  0 X9 X8 X7  0  0  0  0  0  0  0 | line_m2 AND mask
------------------------------------------------+---------------------------
 0  0  0  0  0  0 X9 X8 X7 X6 X5 X4 X3 A1  0  0 | CONTEXT = line_m1 OR line_m2
------------------------------------------------+---------------------------
 0  0  0  0  0  0 X9 X8 X7 X6 X5 X4 X3 A1 X2 X1 | CONTEXT contents

Each batch is normally 8 bits, but at the right edge of the image
we may have fewer pixels to decode. The minor_width is how many
pixels the current batch should decode, with a counter variable
x_minor to keep track of the current pixel being decoded.

In order to process a new batch of pixels, unless we're at the
rightmost batch of pixels, we need to refill the sliding window
variables with eight new bits. Looking at the diagram above we
can see that in order to decode eight pixels starting with O0
we'll need to have bits up to pixel 'n7' for line_m1 and 'm7' for
line_m2 available (A1 and X7 moved right 7 times). To do this
simply and quickly, we shift line_m1 left by 8 bits, and OR in
the next byte from corresponding row. Likewise for line_m2, but
the next byte from the image is also shifted left by 4 bits to
compensate for line_m2 having the four least significant bits
unused.

These new eight bits contain the bit values of the eight pixels
to the right of those already present in line_m1 and line_m2. We
call these new bits m7 through mE, and n6 through nD, as
illustrated below.

23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 | bit position
------------------------------------------------------------------------+-------------
 0  0  0  0  0  0  0  0  0  0  0  0  0  0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5 | original line_m1
 0  0  0  0  0  0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5  0  0  0  0  0  0  0  0 | line_m1 shifted left by 8
 0  0  0  0  0  0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5 n6 n7 n8 n9 nA nB nC nD | line_m1 with new bits ORed in
------------------------------------------------------------------------+-------------
 0  0  0  0  0  0  0  0  0  0  0 X9 X8 X7 m1 m2 m3 m4 m5 m6  0  0  0  0 | original line_m2
 0  0  0 X9 X8 X7 m1 m2 m3 m4 m5 m6  0  0  0  0  0  0  0  0  0  0  0  0 | line_m2 shifted left by 8
 0  0  0 X9 X8 X7 m1 m2 m3 m4 m5 m6 m7 m8 m9 mA mB mC mD mE  0  0  0  0 | line_m2 with new bits ORed in

             .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .
             |    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
          X9 | X8 | X7 | m1 | m2 | m3 | m4 | m5 | m6 | m7 | m8 | m9 | mA | mB | mC | mD | mE |    |    |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
     X6   X5 | X4 | X3 | A1 | n1 | n2 | n3 | n4 | n5 | n6 | n7 | n8 | n9 | nA | nB | nC | nD |    |    |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
     X2   X1 | OO |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
             |    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .
             .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .

CONTEXT, line_m1 and line_m2 now contain all necessary bits to
decode a full batch of eight pixels.

The first pixel in the batch is decoded using this CONTEXT. After
that, for each following pixel we need to update the CONTEXT
using both the last decoded pixel value and new bits from line_m1
and line_m2.

             .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .
             |    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
         (X9)|_X8_|_X7_|>m1<| m2 | m3 | m4 | m5 | m6 | m7 | m8 | m9 | mA | mB | mC | mD | mE |    |    |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
    (X6) _X5_|_X4_|_X3_|_A1_|>n1<| n2 | n3 | n4 | n5 | n6 | n7 | n8 | n9 | nA | nB | nC | nD |    |    |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
    (X2) _X1_|>OO<| oo |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
   +    +    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+...
             |    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .
             .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .

This figure illustrates what happens when the same template is
overlaid on itself shifted one pixel to the right in order to
decode the next pixel. Pixels marked with _  _ are pixels that
are present in both templates' CONTEXTs and can be reused. Pixels
marked with (  ) are pixels from the first template that are no
longer necessary and can be removed from CONTEXT. Pixels marked
with >  < are new pixels that were not part of the original
CONTEXT, and so need to be moved into the CONTEXT at the
appropriate locations. In general the leftmost pixels of each
template row can be forgotten, while new pixels are needed at the
right most location of each row.

The CONTEXT corresponding to the current pixel OO and how it is
masked is shown below. Note how the left most pixel of each row
of the template is NOT propagated to the CONTEXT, these pixels
are X2, X6 and X9. This is done by having the mask being 0 at the
corresponding locations.

 9  8  7  6  5  4  3  2  1  0 | bit position
------------------------------+-------------
X9 X8 X7 X6 X5 X4 X3 A1 X2 X1 | pixel values from CONTEXT
 0  1  1  0  1  1  1  1  0  1 | reused pixel bit value mask (0x1bd)
 0 X8 X7  0 X5 X4 X3 A1  0 X1 | reused pixel values from CONTEXT

Next the CONTEXT is shifted left by one bit to make it reference
the next pixel to be decoded. The pixel bit value we just decoded
is then written into the bit corresponding to X1. The sliding
windows in line_m1 and line_m2 are both shifted (10 - x_minor)
bits to the right to make the needed pixels' bit values appear at
the correct positions to be ORed into CONTEXT. Note that this
shift amount depends on which bit in the batch is currently being
computed, as is given by the x_minor counter. In the example
below we assume that x_minor is 0.

 9  8  7  6  5  4  3  2  1  0 | bit position
------------------------------+--------------
 0 X8 X7  0 X5 X4 X3 A1  0  0 | reused pixels from CONTEXT
X8 X7  0 X5 X4 X3 A1  0  0  0 | reused pixels shifted left 1 bit
------------------------------+--------------
X8 X7  0 X5 X4 X3 A1  0 X1 OO | new CONTEXT with current pixel at LSB
------------------------------+--------------
 0  0 X6 X5 X4 X3 A1 n1 n2 n3 | line_m1 shifted (10 - x_minor) bits to the right
 0  0  0  0  0  0  0  1  0  0 | mask for new adaptive pixel one row above (0x4)
X8 X7  0 X5 X4 X3 A1 n1 X1 OO | new CONTEXT with new adaptive pixel
------------------------------+--------------
X8 X7 m1 m2 m3 m4 m5 m6 m7 m8 | line_m2 with new bits ORed in
 0  0  1  0  0  0  0  0  0  0 | mask for new pixel two rows above (0x80)
X8 X7 m1 X5 X4 X3 A1 n1 X1 OO | new CONTEXT with new pixel

This makes the computation of the new CONTEXT be:

NEWCONTEXT = (CONTEXT & 0x1bd) << 1
NEWCONTEXT |= newbit;
NEWCONTEXT |= (line_m1 >> (10-x_minor)) & 0x4;
NEWCONTEXT |= (line_m2 >> (10-x_minor)) & 0x80;

The optimized decoding functions for GBTEMPLATE 0, 1 and 3 all
work similarly. */

/* return the appropriate context size for the given template */
int
jbig2_generic_stats_size(Jbig2Ctx *ctx, int template)
{
    int stats_size = template == 0 ? 1 << 16 : template == 1 ? 1 << 13 : 1 << 10;

    return stats_size;
}

static int
jbig2_decode_generic_template0(Jbig2Ctx *ctx,
                               Jbig2Segment *segment,
                               const Jbig2GenericRegionParams *params, Jbig2ArithState *as,
                               Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    const uint32_t rowstride = image->stride;
    uint32_t x, y;
    byte *line2 = NULL;
    byte *line1 = NULL;
    byte *gbreg_line = (byte *) image->data;

#ifdef OUTPUT_PBM
    printf("P4\n%d %d\n", GBW, GBH);
#endif

    if (GBW <= 0)
        return 0;

    for (y = 0; y < GBH; y++) {
        uint32_t CONTEXT;
        uint32_t line_m1;
        uint32_t line_m2;
        uint32_t padded_width = (GBW + 7) & -8;
        int code = 0;

        line_m1 = line1 ? line1[0] : 0;
        line_m2 = line2 ? line2[0] << 6 : 0;
        CONTEXT = (line_m1 & 0x7f0) | (line_m2 & 0xf800);

        /* 6.2.5.7 3d */
        for (x = 0; x < padded_width; x += 8) {
            byte result = 0;
            int x_minor;
            int minor_width = GBW - x > 8 ? 8 : GBW - x;

            if (line1)
                line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0);

            if (line2)
                line_m2 = (line_m2 << 8) | (x + 8 < GBW ? line2[(x >> 3) + 1] << 6 : 0);

            /* This is the speed-critical inner loop. */
            for (x_minor = 0; x_minor < minor_width; x_minor++) {
                bool bit;

                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 optimized");
                result |= bit << (7 - x_minor);
                CONTEXT = ((CONTEXT & 0x7bf7) << 1) | bit | ((line_m1 >> (7 - x_minor)) & 0x10) | ((line_m2 >> (7 - x_minor)) & 0x800);
            }
            gbreg_line[x >> 3] = result;
        }
#ifdef OUTPUT_PBM
        fwrite(gbreg_line, 1, rowstride, stdout);
#endif
        line2 = line1;
        line1 = gbreg_line;
        gbreg_line += rowstride;
    }

    return 0;
}

#define pixel_outside_field(x, y) \
    ((y) < -128 || (y) > 0 || (x) < -128 || ((y) < 0 && (x) > 127) || ((y) == 0 && (x) >= 0))

static int
jbig2_decode_generic_template0_unopt(Jbig2Ctx *ctx,
                                     Jbig2Segment *segment,
                                     const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]) ||
        pixel_outside_field(params->gbat[2], params->gbat[3]) ||
        pixel_outside_field(params->gbat[4], params->gbat[5]) ||
        pixel_outside_field(params->gbat[6], params->gbat[7]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    /* this version is generic and easy to understand, but very slow */

    for (y = 0; y < GBH; y++) {
        for (x = 0; x < GBW; x++) {
            if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                jbig2_image_set_pixel(image, x, y, 0);
                continue;
            }
            CONTEXT = 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
            CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2;
            CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3;
            CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4;
            CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 5;
            CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 6;
            CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 7;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 8;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 9;
            CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[2], y + params->gbat[3]) << 10;
            CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[4], y + params->gbat[5]) << 11;
            CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 12;
            CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 2) << 13;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 14;
            CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[6], y + params->gbat[7]) << 15;
            bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
            if (code)
                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 unoptimized");
            jbig2_image_set_pixel(image, x, y, bit);
        }
    }
    return 0;
}

static int
jbig2_decode_generic_template1_unopt(Jbig2Ctx *ctx,
                                     Jbig2Segment *segment,
                                     const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    /* this version is generic and easy to understand, but very slow */

    for (y = 0; y < GBH; y++) {
        for (x = 0; x < GBW; x++) {
            if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                jbig2_image_set_pixel(image, x, y, 0);
                continue;
            }
            CONTEXT = 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
            CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2;
            CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 3;
            CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 4;
            CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5;
            CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 6;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8;
            CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 2) << 9;
            CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 10;
            CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 2) << 11;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 12;
            bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
            if (code)
                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 unoptimized");
            jbig2_image_set_pixel(image, x, y, bit);
        }
    }
    return 0;
}

static int
jbig2_decode_generic_template1(Jbig2Ctx *ctx,
                               Jbig2Segment *segment,
                               const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    const uint32_t rowstride = image->stride;
    uint32_t x, y;
    byte *line2 = NULL;
    byte *line1 = NULL;
    byte *gbreg_line = (byte *) image->data;
    int code = 0;

#ifdef OUTPUT_PBM
    printf("P4\n%d %d\n", GBW, GBH);
#endif

    if (GBW <= 0)
        return 0;

    for (y = 0; y < GBH; y++) {
        uint32_t CONTEXT;
        uint32_t line_m1;
        uint32_t line_m2;
        uint32_t padded_width = (GBW + 7) & -8;

        line_m1 = line1 ? line1[0] : 0;
        line_m2 = line2 ? line2[0] << 5 : 0;
        CONTEXT = ((line_m1 >> 1) & 0x1f8) | ((line_m2 >> 1) & 0x1e00);

        /* 6.2.5.7 3d */
        for (x = 0; x < padded_width; x += 8) {
            byte result = 0;
            int x_minor;
            int minor_width = GBW - x > 8 ? 8 : GBW - x;

            if (line1)
                line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0);

            if (line2)
                line_m2 = (line_m2 << 8) | (x + 8 < GBW ? line2[(x >> 3) + 1] << 5 : 0);

            /* This is the speed-critical inner loop. */
            for (x_minor = 0; x_minor < minor_width; x_minor++) {
                bool bit;

                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 optimized");
                result |= bit << (7 - x_minor);
                CONTEXT = ((CONTEXT & 0xefb) << 1) | bit | ((line_m1 >> (8 - x_minor)) & 0x8) | ((line_m2 >> (8 - x_minor)) & 0x200);
            }
            gbreg_line[x >> 3] = result;
        }
#ifdef OUTPUT_PBM
        fwrite(gbreg_line, 1, rowstride, stdout);
#endif
        line2 = line1;
        line1 = gbreg_line;
        gbreg_line += rowstride;
    }

    return 0;
}

static int
jbig2_decode_generic_template2_unopt(Jbig2Ctx *ctx,
                               Jbig2Segment *segment,
                               const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    /* this version is generic and easy to understand, but very slow */

    for (y = 0; y < GBH; y++) {
        for (x = 0; x < GBW; x++) {
            if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                jbig2_image_set_pixel(image, x, y, 0);
                continue;
            }
            CONTEXT = 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
            CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 2;
            CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 3;
            CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 4;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 5;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 6;
            CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 7;
            CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 2) << 8;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 9;
            bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
            if (code)
                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 unoptimized");
            jbig2_image_set_pixel(image, x, y, bit);
        }
    }

    return 0;
}

static int
jbig2_decode_generic_template2(Jbig2Ctx *ctx,
                                Jbig2Segment *segment,
                                const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    const uint32_t rowstride = image->stride;
    uint32_t x, y;
    byte *line2 = NULL;
    byte *line1 = NULL;
    byte *gbreg_line = (byte *) image->data;
    int code = 0;

#ifdef OUTPUT_PBM
    printf("P4\n%d %d\n", GBW, GBH);
#endif

    if (GBW <= 0)
        return 0;

    for (y = 0; y < GBH; y++) {
        uint32_t CONTEXT;
        uint32_t line_m1;
        uint32_t line_m2;
        uint32_t padded_width = (GBW + 7) & -8;

        line_m1 = line1 ? line1[0] : 0;
        line_m2 = line2 ? line2[0] << 4 : 0;
        CONTEXT = ((line_m1 >> 3) & 0x7c) | ((line_m2 >> 3) & 0x380);

        /* 6.2.5.7 3d */
        for (x = 0; x < padded_width; x += 8) {
            byte result = 0;
            int x_minor;
            int minor_width = GBW - x > 8 ? 8 : GBW - x;

            if (line1)
                line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0);

            if (line2)
                line_m2 = (line_m2 << 8) | (x + 8 < GBW ? line2[(x >> 3) + 1] << 4 : 0);

            /* This is the speed-critical inner loop. */
            for (x_minor = 0; x_minor < minor_width; x_minor++) {
                bool bit;

                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 optimized");
                result |= bit << (7 - x_minor);
                CONTEXT = ((CONTEXT & 0x1bd) << 1) | bit | ((line_m1 >> (10 - x_minor)) & 0x4) | ((line_m2 >> (10 - x_minor)) & 0x80);
            }
            gbreg_line[x >> 3] = result;
        }
#ifdef OUTPUT_PBM
        fwrite(gbreg_line, 1, rowstride, stdout);
#endif
        line2 = line1;
        line1 = gbreg_line;
        gbreg_line += rowstride;
    }

    return 0;
}

static int
jbig2_decode_generic_template3(Jbig2Ctx *ctx,
                               Jbig2Segment *segment,
                               const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    const uint32_t rowstride = image->stride;
    byte *line1 = NULL;
    byte *gbreg_line = (byte *) image->data;
    uint32_t x, y;
    int code;

#ifdef OUTPUT_PBM
    printf("P4\n%d %d\n", GBW, GBH);
#endif

    if (GBW <= 0)
        return 0;

    for (y = 0; y < GBH; y++) {
        uint32_t CONTEXT;
        uint32_t line_m1;
        uint32_t padded_width = (GBW + 7) & -8;

        line_m1 = line1 ? line1[0] : 0;
        CONTEXT = (line_m1 >> 1) & 0x3f0;

        /* 6.2.5.7 3d */
        for (x = 0; x < padded_width; x += 8) {
            byte result = 0;
            int x_minor;
            int minor_width = GBW - x > 8 ? 8 : GBW - x;

            if (line1)
                line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0);

            /* This is the speed-critical inner loop. */
            for (x_minor = 0; x_minor < minor_width; x_minor++) {
                bool bit;

                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 optimized");
                result |= bit << (7 - x_minor);
                CONTEXT = ((CONTEXT & 0x1f7) << 1) | bit | ((line_m1 >> (8 - x_minor)) & 0x10);
            }
            gbreg_line[x >> 3] = result;
        }
#ifdef OUTPUT_PBM
        fwrite(gbreg_line, 1, rowstride, stdout);
#endif
        line1 = gbreg_line;
        gbreg_line += rowstride;
    }

    return 0;
}

static int
jbig2_decode_generic_template3_unopt(Jbig2Ctx *ctx,
                                     Jbig2Segment *segment,
                                     const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    /* this version is generic and easy to understand, but very slow */

    for (y = 0; y < GBH; y++) {
        for (x = 0; x < GBW; x++) {
            if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                jbig2_image_set_pixel(image, x, y, 0);
                continue;
            }
            CONTEXT = 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
            CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2;
            CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3;
            CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4;
            CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5;
            CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 6;
            CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7;
            CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8;
            CONTEXT |= jbig2_image_get_pixel(image, x - 3, y - 1) << 9;
            bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
            if (code)
                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 unoptimized");
            jbig2_image_set_pixel(image, x, y, bit);
        }
    }
    return 0;
}

static void
copy_prev_row(Jbig2Image *image, int row)
{
    if (!row) {
        /* no previous row */
        memset(image->data, 0, image->stride);
    } else {
        /* duplicate data from the previous row */
        uint8_t *src = image->data + (row - 1) * image->stride;

        memcpy(src + image->stride, src, image->stride);
    }
}

static int
jbig2_decode_generic_template0_TPGDON(Jbig2Ctx *ctx,
                                      Jbig2Segment *segment,
                                      const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int LTP = 0;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]) ||
        pixel_outside_field(params->gbat[2], params->gbat[3]) ||
        pixel_outside_field(params->gbat[4], params->gbat[5]) ||
        pixel_outside_field(params->gbat[6], params->gbat[7]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    for (y = 0; y < GBH; y++) {
        LTP ^= jbig2_arith_decode(as, &GB_stats[0x9B25], &code);
        if (code)
            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON1");
        if (!LTP) {
            for (x = 0; x < GBW; x++) {
                if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                    jbig2_image_set_pixel(image, x, y, 0);
                    continue;
                }
                CONTEXT = jbig2_image_get_pixel(image, x - 1, y);
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
                CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2;
                CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4;
                CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 5;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 6;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 7;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 8;
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 9;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[2], y + params->gbat[3]) << 10;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[4], y + params->gbat[5]) << 11;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 12;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 2) << 13;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 14;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[6], y + params->gbat[7]) << 15;
                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON2");
                jbig2_image_set_pixel(image, x, y, bit);
            }
        } else {
            copy_prev_row(image, y);
        }
    }

    return 0;
}

static int
jbig2_decode_generic_template1_TPGDON(Jbig2Ctx *ctx,
                                      Jbig2Segment *segment,
                                      const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int LTP = 0;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    for (y = 0; y < GBH; y++) {
        LTP ^= jbig2_arith_decode(as, &GB_stats[0x0795], &code);
        if (code)
            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 TPGDON1");
        if (!LTP) {
            for (x = 0; x < GBW; x++) {
                if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                    jbig2_image_set_pixel(image, x, y, 0);
                    continue;
                }
                CONTEXT = jbig2_image_get_pixel(image, x - 1, y);
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
                CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 3;
                CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 4;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 6;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7;
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8;
                CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 2) << 9;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 10;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 2) << 11;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 12;
                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 TPGDON2");
                jbig2_image_set_pixel(image, x, y, bit);
            }
        } else {
            copy_prev_row(image, y);
        }
    }

    return 0;
}

static int
jbig2_decode_generic_template2_TPGDON(Jbig2Ctx *ctx,
                                      Jbig2Segment *segment,
                                      const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int LTP = 0;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    for (y = 0; y < GBH; y++) {
        LTP ^= jbig2_arith_decode(as, &GB_stats[0xE5], &code);
        if (code)
            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 TPGDON1");
        if (!LTP) {
            for (x = 0; x < GBW; x++) {
                if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                    jbig2_image_set_pixel(image, x, y, 0);
                    continue;
                }
                CONTEXT = jbig2_image_get_pixel(image, x - 1, y);
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 2;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 3;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 4;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 5;
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 6;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 7;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 2) << 8;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 9;
                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 TPGDON2");
                jbig2_image_set_pixel(image, x, y, bit);
            }
        } else {
            copy_prev_row(image, y);
        }
    }

    return 0;
}

static int
jbig2_decode_generic_template3_TPGDON(Jbig2Ctx *ctx,
                                      Jbig2Segment *segment,
                                      const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const uint32_t GBW = image->width;
    const uint32_t GBH = image->height;
    uint32_t CONTEXT;
    uint32_t x, y;
    bool bit;
    int LTP = 0;
    int code = 0;

    if (pixel_outside_field(params->gbat[0], params->gbat[1]))
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "adaptive template pixel is out of field");

    for (y = 0; y < GBH; y++) {
        LTP ^= jbig2_arith_decode(as, &GB_stats[0x0195], &code);
        if (code)
            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 TPGDON1");
        if (!LTP) {
            for (x = 0; x < GBW; x++) {
                if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) {
                    jbig2_image_set_pixel(image, x, y, 0);
                    continue;
                }
                CONTEXT = jbig2_image_get_pixel(image, x - 1, y);
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
                CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2;
                CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 6;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7;
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8;
                CONTEXT |= jbig2_image_get_pixel(image, x - 3, y - 1) << 9;
                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code);
                if (code)
                    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 TPGDON2");
                jbig2_image_set_pixel(image, x, y, bit);
            }
        } else {
            copy_prev_row(image, y);
        }
    }

    return 0;
}

static int
jbig2_decode_generic_region_TPGDON(Jbig2Ctx *ctx,
                                   Jbig2Segment *segment,
                                   const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    switch (params->GBTEMPLATE) {
    case 0:
        return jbig2_decode_generic_template0_TPGDON(ctx, segment, params, as, image, GB_stats);
    case 1:
        return jbig2_decode_generic_template1_TPGDON(ctx, segment, params, as, image, GB_stats);
    case 2:
        return jbig2_decode_generic_template2_TPGDON(ctx, segment, params, as, image, GB_stats);
    case 3:
        return jbig2_decode_generic_template3_TPGDON(ctx, segment, params, as, image, GB_stats);
    }

    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unsupported GBTEMPLATE (%d)", params->GBTEMPLATE);
}

/**
 * jbig2_decode_generic_region: Decode a generic region.
 * @ctx: The context for allocation and error reporting.
 * @segment: A segment reference for error reporting.
 * @params: Decoding parameter set.
 * @as: Arithmetic decoder state.
 * @image: Where to store the decoded data.
 * @GB_stats: Arithmetic stats.
 *
 * Decodes a generic region, according to section 6.2. The caller should
 * pass an already allocated Jbig2Image object for @image
 *
 * Because this API is based on an arithmetic decoding state, it is
 * not suitable for MMR decoding.
 *
 * Return code: 0 on success.
 **/
int
jbig2_decode_generic_region(Jbig2Ctx *ctx,
                            Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const int8_t *gbat = params->gbat;

    if (image->stride * image->height > (1 << 26) && segment->data_length < image->stride * image->height / (1 << 16)) {
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number,
                           "region is far larger than data provided (%d << %d), aborting to prevent DOS", segment->data_length, image->stride * image->height);
    }

    if (!params->MMR && params->TPGDON)
        return jbig2_decode_generic_region_TPGDON(ctx, segment, params, as, image, GB_stats);

    if (!params->MMR && params->GBTEMPLATE == 0) {
        if (!params->USESKIP && gbat[0] == +3 && gbat[1] == -1 && gbat[2] == -3 && gbat[3] == -1 && gbat[4] == +2 && gbat[5] == -2 && gbat[6] == -2 && gbat[7] == -2)
            return jbig2_decode_generic_template0(ctx, segment, params, as, image, GB_stats);
        else
            return jbig2_decode_generic_template0_unopt(ctx, segment, params, as, image, GB_stats);
    } else if (!params->MMR && params->GBTEMPLATE == 1) {
        if (!params->USESKIP && gbat[0] == +3 && gbat[1] == -1)
            return jbig2_decode_generic_template1(ctx, segment, params, as, image, GB_stats);
        else
            return jbig2_decode_generic_template1_unopt(ctx, segment, params, as, image, GB_stats);
    }
    else if (!params->MMR && params->GBTEMPLATE == 2) {
        if (!params->USESKIP && gbat[0] == 2 && gbat[1] == -1)
            return jbig2_decode_generic_template2(ctx, segment, params, as, image, GB_stats);
        else
            return jbig2_decode_generic_template2_unopt(ctx, segment, params, as, image, GB_stats);
    } else if (!params->MMR && params->GBTEMPLATE == 3) {
        if (!params->USESKIP && gbat[0] == 2 && gbat[1] == -1)
            return jbig2_decode_generic_template3(ctx, segment, params, as, image, GB_stats);
        else
            return jbig2_decode_generic_template3_unopt(ctx, segment, params, as, image, GB_stats);
    }

    {
        int i;

        for (i = 0; i < 8; i++)
            jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "gbat[%d] = %d", i, params->gbat[i]);
    }

    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unsupported generic region (MMR=%d, GBTEMPLATE=%d)", params->MMR, params->GBTEMPLATE);
}

/**
 * Handler for immediate generic region segments
 */
int
jbig2_immediate_generic_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data)
{
    Jbig2RegionSegmentInfo rsi;
    byte seg_flags;
    int8_t gbat[8];
    int offset;
    uint32_t gbat_bytes = 0;
    Jbig2GenericRegionParams params;
    int code = 0;
    Jbig2Image *image = NULL;
    Jbig2WordStream *ws = NULL;
    Jbig2ArithState *as = NULL;
    Jbig2ArithCx *GB_stats = NULL;
    uint32_t height;
    Jbig2Page *page = &ctx->pages[ctx->current_page];

    /* 7.4.6 */
    if (segment->data_length < 18)
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short");

    jbig2_get_region_segment_info(&rsi, segment_data);
    jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "generic region: %u x %u @ (%u, %u), flags = %02x", rsi.width, rsi.height, rsi.x, rsi.y, rsi.flags);

    /* 7.4.6.4 */
    height = rsi.height;
    if (segment->rows != UINT32_MAX) {
        if (segment->rows > rsi.height)
            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment contains more rows than stated in header");
        height = segment->rows;
    }

    /* 7.4.6.2 */
    seg_flags = segment_data[17];
    jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "segment flags = %02x", seg_flags);
    if ((seg_flags & 1) && (seg_flags & 6))
        jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "MMR is 1, but GBTEMPLATE is not 0");

    /* 7.4.6.3 */
    if (!(seg_flags & 1)) {
        gbat_bytes = (seg_flags & 6) ? 2 : 8;
        if (18 + gbat_bytes > segment->data_length)
            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short");
        memcpy(gbat, segment_data + 18, gbat_bytes);
        jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "gbat: %d, %d", gbat[0], gbat[1]);
    }

    offset = 18 + gbat_bytes;

    /* Check for T.88 amendment 2 */
    if ((seg_flags >> 5) & 1)
        return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment uses 12 adaptive template pixels (NYI)");

    /* Table 34 */
    params.MMR = seg_flags & 1;
    params.GBTEMPLATE = (seg_flags & 6) >> 1;
    params.TPGDON = (seg_flags & 8) >> 3;
    params.USESKIP = 0;
    memcpy(params.gbat, gbat, gbat_bytes);

    if (page->height == 0xffffffff && page->striped && page->stripe_size > 0) {
        if (rsi.y >= page->end_row + page->stripe_size) {
            jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "ignoring %u x %u region at (%u, %u) outside of stripe at row %u covering %u rows, on page of height %u", rsi.width, rsi.height, rsi.x, rsi.y, page->end_row, page->stripe_size, page->image->height);
            return 0;
        }
        if (height > page->end_row + page->stripe_size) {
            height = page->end_row + page->stripe_size;
        }
    } else {
        if (rsi.y >= page->height) {
            jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "ignoring %u x %u region at (%u, %u) outside of page of height %u", rsi.width, rsi.height, rsi.x, rsi.y, page->height);
            return 0;
        }
        if (height > page->height - rsi .y) {
            height = page->height - rsi.y;
        }
    }
    if (height == 0) {
        jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "nothing remains of region, ignoring");
        return 0;
    }

    image = jbig2_image_new(ctx, rsi.width, height);
    if (image == NULL)
        return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate generic image");
    jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "allocated %d x %d image buffer for region decode results", rsi.width, height);

    if (params.MMR) {
        code = jbig2_decode_generic_mmr(ctx, segment, &params, segment_data + offset, segment->data_length - offset, image);
        if (code < 0) {
            code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode MMR-coded generic region");
            goto cleanup;
        }
    } else {
        int stats_size = jbig2_generic_stats_size(ctx, params.GBTEMPLATE);

        GB_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size);
        if (GB_stats == NULL) {
            code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate arithmetic decoder states when handling immediate generic region");
            goto cleanup;
        }
        memset(GB_stats, 0, stats_size);

        ws = jbig2_word_stream_buf_new(ctx, segment_data + offset, segment->data_length - offset);
        if (ws == NULL) {
            code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocated word stream when handling immediate generic region");
            goto cleanup;
        }
        as = jbig2_arith_new(ctx, ws);
        if (as == NULL) {
            code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding state when handling immediate generic region");
            goto cleanup;
        }
        code = jbig2_decode_generic_region(ctx, segment, &params, as, image, GB_stats);
        if (code < 0) {
            code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode immediate generic region");
            goto cleanup;
        }
    }

    code = jbig2_page_add_result(ctx, &ctx->pages[ctx->current_page], image, rsi.x, rsi.y, rsi.op);
    if (code < 0)
        code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to add result to page");

cleanup:
    jbig2_free(ctx->allocator, as);
    jbig2_word_stream_buf_free(ctx, ws);
    jbig2_free(ctx->allocator, GB_stats);
    jbig2_image_release(ctx, image);

    return code;
}