template_compose_group(byte *gs_restrict tos_ptr, bool tos_isolated,
                       int tos_planestride, int tos_rowstride,
                       byte alpha, byte shape, gs_blend_mode_t blend_mode,
                       bool tos_has_shape, int tos_shape_offset,
                       int tos_alpha_g_offset, int tos_tag_offset,
                       bool tos_has_tag, byte *gs_restrict nos_ptr,
                       bool nos_isolated, int nos_planestride,
                       int nos_rowstride, byte *gs_restrict nos_alpha_g_ptr,
                       bool nos_knockout, int nos_shape_offset,
                       int nos_tag_offset, byte *gs_restrict mask_row_ptr,
                       int has_mask, pdf14_buf *gs_restrict maskbuf,
                       byte mask_bg_alpha, const byte *gs_restrict mask_tr_fn,
                       byte *gs_restrict backdrop_ptr, bool has_matte,
                       int n_chan, bool additive, int num_spots,
                       bool overprint, gx_color_index drawn_comps,
                       int x0, int y0, int x1, int y1,
                       const pdf14_nonseparable_blending_procs_t *pblend_procs,
                       pdf14_device *pdev, int has_alpha)
{
    byte *gs_restrict mask_curr_ptr = NULL;
    int width = x1 - x0;
    int x, y;
    int i;
    byte tos_pixel[PDF14_MAX_PLANES];
    byte nos_pixel[PDF14_MAX_PLANES];
    byte back_drop[PDF14_MAX_PLANES];
    gx_color_index comps;
    bool in_mask_rect_y;
    bool in_mask_rect;
    byte pix_alpha;
    byte matte_alpha = 0xff;
    int first_spot = n_chan - num_spots;
    int first_blend_spot = n_chan;
    bool has_mask2 = has_mask;
    byte *gs_restrict dst;
    byte global_shape = (byte)(255 * pdev->shape + 0.5);

    if (!nos_knockout && num_spots > 0 && !blend_valid_for_spot(blend_mode)) {
        first_blend_spot = first_spot;
    }
    if (blend_mode == BLEND_MODE_Normal)
        first_blend_spot = 0;
    if (!nos_isolated && backdrop_ptr != NULL)
        has_mask2 = false;

    for (y = y1 - y0; y > 0; --y) {
        mask_curr_ptr = mask_row_ptr;
        in_mask_rect_y = (has_mask && y1 - y >= maskbuf->rect.p.y && y1 - y < maskbuf->rect.q.y);
        for (x = 0; x < width; x++) {
            in_mask_rect = (in_mask_rect_y && x0 + x >= maskbuf->rect.p.x && x0 + x < maskbuf->rect.q.x);
            pix_alpha = alpha;
            /* If we have a soft mask, then we have some special handling of the
               group alpha value */
            if (maskbuf != NULL) {
                if (!in_mask_rect) {
                    /* Special case where we have a soft mask but are outside
                       the range of the soft mask and must use the background
                       alpha value */
                    pix_alpha = mask_bg_alpha;
                    matte_alpha = 0xff;
                } else {
                    if (has_matte)
                        matte_alpha = mask_tr_fn[*mask_curr_ptr];
                }
            }

            /* Matte present, need to undo premultiplied alpha prior to blend */
            if (has_matte && matte_alpha != 0 && matte_alpha < 0xff) {
                for (i = 0; i < n_chan; i++) {
                    /* undo */
                    byte matte = maskbuf->matte[i]>>8;
                    int val = tos_ptr[i * tos_planestride] - matte;
                    int temp = ((((val * 0xff) << 8) / matte_alpha) >> 8) + matte;

                    /* clip */
                    if (temp > 0xff)
                        tos_pixel[i] = 0xff;
                    else if (temp < 0)
                        tos_pixel[i] = 0;
                    else
                        tos_pixel[i] = temp;

                    if (!additive) {
                        /* Pure subtractive */
                        tos_pixel[i] = 255 - tos_pixel[i];
                        nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
                    } else {
                        /* additive or hybrid */
                        if (i >= first_spot)
                            nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
                        else
                            nos_pixel[i] = nos_ptr[i * nos_planestride];
                    }
                }
            } else {
                /* No matte present */
                if (!additive) {
                    /* Pure subtractive */
                    for (i = 0; i < n_chan; ++i) {
                        tos_pixel[i] = 255 - tos_ptr[i * tos_planestride];
                        nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
                    }
                } else {
                    /* Additive or hybrid */
                    for (i = 0; i < first_spot; ++i) {
                        tos_pixel[i] = tos_ptr[i * tos_planestride];
                        nos_pixel[i] = nos_ptr[i * nos_planestride];
                    }
                    for (; i < n_chan; i++) {
                        tos_pixel[i] = 255 - tos_ptr[i * tos_planestride];
                        nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
                    }
                }
            }
            /* alpha */
            tos_pixel[n_chan] = has_alpha ? tos_ptr[n_chan * tos_planestride] : 255;
            nos_pixel[n_chan] = has_alpha ? nos_ptr[n_chan * nos_planestride] : 255;

            if (mask_curr_ptr != NULL) {
                if (in_mask_rect) {
                    byte mask = mask_tr_fn[*mask_curr_ptr++];
                    int tmp = pix_alpha * mask + 0x80;
                    pix_alpha = (tmp + (tmp >> 8)) >> 8;
                } else {
                    mask_curr_ptr++;
                }
            }

            dst = nos_pixel;
            if (nos_knockout) {
                /* We need to be knocking out what ever is on the nos, but may
                   need to combine with it's backdrop */
                byte tos_shape = 255;

                if (tos_has_shape)
                    tos_shape = tos_ptr[tos_shape_offset];

                if (nos_isolated || backdrop_ptr == NULL) {
                    /* We do not need to compose with the backdrop */
                    back_drop[n_chan] = 0;
                    /* FIXME: The blend here can be simplified */
                } else {
                    /* Per the PDF spec, since the tos is not isolated and we are
                       going onto a knock out group, we do the composition with
                       the nos initial backdrop. */
                    if (additive) {
                        /* additive or hybrid */
                        for (i = 0; i < first_spot; ++i) {
                            back_drop[i] = backdrop_ptr[i * nos_planestride];
                        }
                        for (; i < n_chan; i++) {
                            back_drop[i] = 255 - backdrop_ptr[i * nos_planestride];
                        }
                    } else {
                        /* pure subtractive */
                        for (i = 0; i < n_chan; ++i) {
                            back_drop[i] = 255 - backdrop_ptr[i * nos_planestride];
                        }
                    }
                    /* alpha */
                    back_drop[n_chan] = backdrop_ptr[n_chan * nos_planestride];
                }
                art_pdf_composite_knockout_group_8(back_drop, tos_shape,
                                                   nos_pixel, nos_alpha_g_ptr,
                                                   tos_pixel, n_chan, pix_alpha,
                                                   blend_mode, pblend_procs,
                                                   pdev, has_mask2);
            } else if (tos_isolated ?
                       art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
                                                 tos_pixel, n_chan, pix_alpha) :
                       art_pdf_recomposite_group_8(&dst, nos_alpha_g_ptr,
                           tos_pixel, has_alpha ? tos_ptr[tos_alpha_g_offset] : 255, n_chan,
                                                   pix_alpha, blend_mode, first_blend_spot,
                                                   pblend_procs, pdev)) {
                dst = art_pdf_composite_pixel_alpha_8_inline(nos_pixel, tos_pixel, n_chan,
                                                blend_mode, first_blend_spot,
                                                pblend_procs, pdev);
            }
            if (nos_shape_offset && pix_alpha != 0) {
                nos_ptr[nos_shape_offset] =
                    art_pdf_union_mul_8 (nos_ptr[nos_shape_offset],
                                         has_alpha ? tos_ptr[tos_shape_offset] : global_shape,
                                         shape);
            }
            if (dst)
            {
                /* Complement the results for subtractive color spaces.  Again,
                 * if we are in an additive blending color space, we are not
                 * going to be fooling with overprint of spot colors */
                if (additive) {
                    /* additive or hybrid */
                    for (i = 0; i < first_spot; ++i) {
                        nos_ptr[i * nos_planestride] = dst[i];
                    }
                    for (; i < n_chan; i++) {
                        nos_ptr[i * nos_planestride] = 255 - dst[i];
                    }
                } else {
                    /* Pure subtractive */
                    /* If we were running in the compatible overprint blend mode
                    * and popping the group, we don't need to fool with the
                    * drawn components as that should have already have been
                    * handled during the blending within our special non-isolated
                    * group.  So in other words, if the blend mode is normal
                    * (or compatible) and we are doing overprint, the overprint
                    * has NOT been handled by compatible overprint mode and we
                    * need to take care of it now */
                    if (overprint) {
                        for (i = 0, comps = drawn_comps; comps != 0; ++i, comps >>= 1) {
                            if ((comps & 0x1) != 0) {
                                nos_ptr[i * nos_planestride] = 255 - dst[i];
                            }
                        }
                    } else {
                        for (i = 0; i < n_chan; ++i)
                            nos_ptr[i * nos_planestride] = 255 - dst[i];
                    }
                }
                /* alpha */
                nos_ptr[n_chan * nos_planestride] = dst[n_chan];
            }
            /* tags */
            if (nos_tag_offset && tos_has_tag) {
                nos_ptr[nos_tag_offset] |= tos_ptr[tos_tag_offset];
             }

            if (nos_alpha_g_ptr != NULL)
                ++nos_alpha_g_ptr;
            if (backdrop_ptr != NULL)
                ++backdrop_ptr;
            ++tos_ptr;
            ++nos_ptr;
        }
        tos_ptr += tos_rowstride - width;
        nos_ptr += nos_rowstride - width;
        if (nos_alpha_g_ptr != NULL)
            nos_alpha_g_ptr += nos_rowstride - width;
        if (mask_row_ptr != NULL)
            mask_row_ptr += maskbuf->rowstride;
        if (backdrop_ptr != NULL)
            backdrop_ptr += nos_rowstride - width;
    }
}