/*
 *  dither.c:           Various dithering routines
 *
 *  Adapted by:         Ullrich Hafner
 *
 *  This file is part of FIASCO (Fractal Image And Sequence COdec)
 *  Copyright (C) 1994-2000 Ullrich Hafner
 */

/*
 * Copyright (c) 1995 Erik Corry
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL ERIK CORRY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
 * THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIK CORRY HAS BEEN ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ERIK CORRY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
 * BASIS, AND ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 *  $Date: 2000/11/27 20:22:51 $
 *  $Author: hafner $
 *  $Revision: 5.3 $
 *  $State: Exp $
 */

#include "pm_config.h"
#include "config.h"

#include <string.h>
#include <stdlib.h>

#include "types.h"
#include "macros.h"
#include "error.h"

#include "fiasco.h"
#include "image.h"
#include "misc.h"
#include "dither.h"

/*****************************************************************************

                                prototypes

*****************************************************************************/

static int
display_16_bit (const struct fiasco_renderer *this, unsigned char *ximage,
                const fiasco_image_t *fiasco_image);

static int
display_24_bit_bgr (const struct fiasco_renderer *this, unsigned char *ximage,
                    const fiasco_image_t *fiasco_image);

static int
display_24_bit_rgb (const struct fiasco_renderer *this, unsigned char *ximage,
                    const fiasco_image_t *fiasco_image);

static int
display_32_bit (const struct fiasco_renderer *this, unsigned char *ximage,
                const fiasco_image_t *fiasco_image);

static int
free_bits_at_bottom (unsigned long a);

static int
number_of_bits_set (unsigned long a);

/*****************************************************************************

                                public code

*****************************************************************************/

fiasco_renderer_t *
fiasco_renderer_new (unsigned long red_mask, unsigned long green_mask,
                     unsigned long blue_mask, unsigned bpp,
                     int double_resolution)
/*
 *  FIASCO renderer constructor.
 *  Allocate memory for the FIASCO renderer structure and
 *  initialize values.
 *  `red_mask', `green_mask', and `blue_mask' are the corresponding masks
 *  of the X11R6 XImage structure.
 *  `bpp' gives the depth of the image in bits per pixel (16, 24, or 32).
 *  If `double_resolution' is not 0 the the image width and height is doubled.
 *  (fast pixel doubling, no interpolation!)
 *
 *  Return value:
 *      pointer to the new structure or NULL on error
 */
{
   if (bpp != 16 && bpp != 24 && bpp !=32)
   {
      set_error (_("Rendering depth of XImage must be 16, 24, or 32 bpp."));
      return NULL;
   }
   else
   {
      fiasco_renderer_t  *render    = calloc (1, sizeof (fiasco_renderer_t));
      renderer_private_t *private   = calloc (1, sizeof (renderer_private_t));
      bool_t              twopixels = (bpp == 16 && double_resolution);
      int                 crval, cbval, i; /* counter */

      if (!render || !private)
      {
         set_error (_("Out of memory."));
         return NULL;
      }
      switch (bpp)
      {
         case 16:
            render->render = display_16_bit;
            break;
         case 24:
            if (red_mask > green_mask)
               render->render = display_24_bit_rgb;
            else
               render->render = display_24_bit_bgr;
            break;
         case 32:
            render->render = display_32_bit;
            break;
         default:
            break;                      /* does not happen */
      }
      render->private = private;
      render->delete  = fiasco_renderer_delete;

      private->double_resolution = double_resolution;
      private->Cr_r_tab = calloc (256 + 2 * 1024, sizeof (int));
      private->Cr_g_tab = calloc (256 + 2 * 1024, sizeof (int));
      private->Cb_g_tab = calloc (256 + 2 * 1024, sizeof (int));
      private->Cb_b_tab = calloc (256 + 2 * 1024, sizeof (int));

      if (!private->Cr_r_tab || !private->Cr_g_tab
          || !private->Cb_b_tab || !private->Cb_g_tab)
      {
         set_error (_("Out of memory."));
         return NULL;
      }

      for (i = 1024; i < 1024 + 256; i++)
      {
         cbval = crval  = i - 128 - 1024;

         private->Cr_r_tab [i] =  1.4022 * crval + 0.5;
         private->Cr_g_tab [i] = -0.7145 * crval + 0.5;
         private->Cb_g_tab [i] = -0.3456 * cbval + 0.5;
         private->Cb_b_tab [i] =  1.7710 * cbval + 0.5;
      }
      for (i = 0; i < 1024; i++)
      {
         private->Cr_r_tab [i] = private->Cr_r_tab [1024];
         private->Cr_g_tab [i] = private->Cr_g_tab [1024];
         private->Cb_g_tab [i] = private->Cb_g_tab [1024];
         private->Cb_b_tab [i] = private->Cb_b_tab [1024];
      }
      for (i = 1024 + 256; i < 2048 + 256; i++)
      {
         private->Cr_r_tab [i] = private->Cr_r_tab [1024 + 255];
         private->Cr_g_tab [i] = private->Cr_g_tab [1024 + 255];
         private->Cb_g_tab [i] = private->Cb_g_tab [1024 + 255];
         private->Cb_b_tab [i] = private->Cb_b_tab [1024 + 255];
      }

      private->Cr_r_tab += 1024 + 128;
      private->Cr_g_tab += 1024 + 128;
      private->Cb_g_tab += 1024 + 128;
      private->Cb_b_tab += 1024 + 128;

      /*
       *  Set up entries 0-255 in rgb-to-pixel value tables.
       */
      private->r_table = calloc (256 + 2 * 1024, sizeof (unsigned int));
      private->g_table = calloc (256 + 2 * 1024, sizeof (unsigned int));
      private->b_table = calloc (256 + 2 * 1024, sizeof (unsigned int));
      private->y_table = calloc (256 + 2 * 1024, sizeof (unsigned int));

      if (!private->r_table || !private->g_table
          || !private->b_table || !private->y_table)
      {
         set_error (_("Out of memory."));
         return NULL;
      }

      for (i = 0; i < 256; i++)
      {
         private->r_table [i + 1024]
            = i >> (8 - number_of_bits_set(red_mask));
         private->r_table [i + 1024]
            <<= free_bits_at_bottom (red_mask);
         private->g_table [i + 1024]
            = i >> (8 - number_of_bits_set (green_mask));
         private->g_table [i + 1024]
            <<= free_bits_at_bottom (green_mask);
         private->b_table [i + 1024]
            <<= free_bits_at_bottom (blue_mask);
         private->b_table [i + 1024]
            = i >> (8 - number_of_bits_set (blue_mask));
         if (twopixels)
         {
            private->r_table [i + 1024] = ((private->r_table [i + 1024] << 16)
                                           | private->r_table [i + 1024]);
            private->g_table [i + 1024] = ((private->g_table [i + 1024] << 16)
                                           | private->g_table [i + 1024]);
            private->b_table [i + 1024] = ((private->b_table [i + 1024] << 16)
                                           | private->b_table [i + 1024]);
         }
         private->y_table [i + 1024] = (private->r_table [i + 1024]
                                        | private->g_table [i + 1024]
                                        | private->b_table [i + 1024]);
      }

      /*
       * Spread out the values we have to the rest of the array so that
       * we do not need to check for overflow.
       */
      for (i = 0; i < 1024; i++)
      {
         private->r_table [i]              = private->r_table [1024];
         private->r_table [i + 1024 + 256] = private->r_table [1024 + 255];
         private->g_table [i]              = private->g_table [1024];
         private->g_table [i + 1024 + 256] = private->g_table [1024 + 255];
         private->b_table [i]              = private->b_table [1024];
         private->b_table [i + 1024 + 256] = private->b_table [1024 + 255];
         private->y_table [i]              = private->y_table [1024];
         private->y_table [i + 1024 + 256] = private->y_table [1024 + 255];
      }

      private->r_table += 1024;
      private->g_table += 1024;
      private->b_table += 1024;
      private->y_table += 1024 + 128;

      return render;
   }

}

void
fiasco_renderer_delete (fiasco_renderer_t *renderer)
/*
 *  FIASCO renderer destructor:
 *  Free memory of 'renderer' structure.
 *
 *  No return value.
 *
 *  Side effects:
 *      structure 'renderer' is discarded.
 */
{
   if (!renderer)
      return;
   else
   {
      renderer_private_t *private = (renderer_private_t *) renderer->private;

      Free (private->Cr_g_tab - (1024 + 128));
      Free (private->Cr_r_tab - (1024 + 128));
      Free (private->Cb_g_tab - (1024 + 128));
      Free (private->Cb_b_tab - (1024 + 128));
      Free (private->r_table - 1024);
      Free (private->g_table - 1024);
      Free (private->b_table - 1024);
      Free (private->y_table - (1024 + 128));

      Free (private);
      Free (renderer);
   }
}

int
fiasco_renderer_render (const fiasco_renderer_t *renderer,
                        unsigned char *ximage,
                        const fiasco_image_t *fiasco_image)
{
   if (!renderer)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "renderer");
      return 0;
   }
   else
      return renderer->render (renderer, ximage, fiasco_image);
}

/*****************************************************************************

                                private code

*****************************************************************************/

/*
 *  Erik Corry's multi-byte dither routines.
 *
 *  The basic idea is that the Init generates all the necessary
 *  tables.  The tables incorporate the information about the layout
 *  of pixels in the XImage, so that it should be able to cope with
 *  16-bit 24-bit (non-packed) and 32-bit (10-11 bits per
 *  color!) screens.  At present it cannot cope with 24-bit packed
 *  mode, since this involves getting down to byte level again. It is
 *  assumed that the bits for each color are contiguous in the
 *  longword.
 *
 *  Writing to memory is done in shorts or ints. (Unfortunately, short
 *  is not very fast on Alpha, so there is room for improvement
 *  here). There is no dither time check for overflow - instead the
 *  tables have slack at each end. This is likely to be faster than an
 *  'if' test as many modern architectures are really bad at
 *  ifs. Potentially, each '&&' causes a pipeline flush!
 *
 *  There is no shifting and fixed point arithmetic, as I really doubt
 *  you can see the difference, and it costs. This may be just my
 *  bias, since I heard that Intel is really bad at shifting.
 */

static int
number_of_bits_set (unsigned long a)
/*
 *  How many 1 bits are there in the longword.
 *  Low performance, do not call often.
 */
{
   if (!a)
      return 0;
   if (a & 1)
      return 1 + number_of_bits_set (a >> 1);
   else
      return (number_of_bits_set (a >> 1));
}

static int
free_bits_at_bottom (unsigned long a)
/*
 *  How many 0 bits are there at least significant end of longword.
 *  Low performance, do not call often.
 */
{
   /* assume char is 8 bits */
   if (!a)
      return sizeof (unsigned long) * 8;
   else if(((long) a) & 1l)
      return 0;
   else
      return 1 + free_bits_at_bottom ( a >> 1);
}

static int
display_16_bit (const struct fiasco_renderer *this, unsigned char *ximage,
                const fiasco_image_t *fiasco_image)
/*
 *  Convert 'image' to 16 bit color bitmap.
 *  If 'double_resolution' is true then double image size in both directions.
 *
 *  No return value.
 *
 *  Side effects:
 *      'out[]' is filled with dithered image
 */
{
   const image_t      *image;
   renderer_private_t *private;
   byte_t             *out;

   if (!this)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "this");
      return 0;
   }
   if (!ximage)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
      return 0;
   }
   if (!fiasco_image)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
      return 0;
   }

   out     = (byte_t *) ximage;
   image   = cast_image ((fiasco_image_t *) fiasco_image);
   if (!image)
      return 0;
   private = (renderer_private_t *) this->private;

   if (image->color)
   {
      word_t       *cbptr, *crptr;      /* pointer to chroma bands */
      word_t       *yptr;               /* pointers to lumincance band */
      int           yval, crval, cbval; /* pixel value in YCbCr color space */
      int           R, G, B;            /* pixel value in RGB color space */
      int           n;                  /* pixel counter */
      int           x, y;               /* pixel coordinates */
      int          *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
      unsigned int *r_table, *g_table, *b_table;

      Cr_g_tab = private->Cr_g_tab;
      Cr_r_tab = private->Cr_r_tab;
      Cb_b_tab = private->Cb_b_tab;
      Cb_g_tab = private->Cb_g_tab;
      r_table  = private->r_table;
      g_table  = private->g_table;
      b_table  = private->b_table;
      yptr     = image->pixels [Y];
      cbptr    = image->pixels [Cb];
      crptr    = image->pixels [Cr];

      if (image->format == FORMAT_4_2_0)
      {
         u_word_t *dst, *dst2;          /* pointers to dithered pixels */
         word_t   *yptr2;               /* pointers to lumincance band */

         if (private->double_resolution)
         {
            yptr2 = yptr + image->width;
            dst   = (u_word_t *) out;
            dst2  = dst + 4 * image->width;
            for (y = image->height / 2; y; y--)
            {
               for (x = image->width / 2; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  crval = *crptr++ >> 4;
                  cbval = *cbptr++ >> 4;
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  crval = *crptr++ / 16;
                  cbval = *cbptr++ / 16;
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
               }
               memcpy (dst, dst - 2 * image->width,
                       2 * image->width * sizeof (u_word_t));
               memcpy (dst2, dst2 - 2 * image->width,
                       2 * image->width * sizeof (u_word_t));
               yptr  += image->width;
               yptr2 += image->width;
               dst   += 3 * image->width * 2;
               dst2  += 3 * image->width * 2;
            }
         }
         else
         {
            yptr2 = yptr + image->width;
            dst  = (u_word_t *) out;
            dst2 = dst + image->width;

            for (y = image->height / 2; y; y--)
            {
               for (x = image->width / 2; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  crval = *crptr++ >> 4;
                  cbval = *cbptr++ >> 4;
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  crval = *crptr++ / 16;
                  cbval = *cbptr++ / 16;
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
               }
               yptr  += image->width;
               yptr2 += image->width;
               dst   += image->width;
               dst2  += image->width;
            }
         }
      }
      else                              /* 4:4:4 format */
      {
         if (private->double_resolution)
         {
            unsigned int *dst;          /* pointer to dithered pixels */

            dst  = (unsigned int *) out;

            for (y = image->height; y; y--)
            {
               for (x = image->width; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  crval = *crptr++ >> 4;
                  cbval = *cbptr++ >> 4;
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  crval = *crptr++ / 16;
                  cbval = *cbptr++ / 16;
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];

                  *dst++ = r_table [R] | g_table [G] | b_table [B];
               }
               memcpy (dst, dst - image->width,
                       image->width * sizeof (unsigned int));
               dst += image->width;
            }
         }
         else
         {
            u_word_t *dst;              /* pointer to dithered pixels */

            dst  = (u_word_t *) out;

            for (n = image->width * image->height; n; n--)
            {
#ifdef HAVE_SIGNED_SHIFT
               crval = *crptr++ >> 4;
               cbval = *cbptr++ >> 4;
               yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
               crval = *crptr++ / 16;
               cbval = *cbptr++ / 16;
               yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
               R = yval + Cr_r_tab [crval];
               G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
               B = yval + Cb_b_tab [cbval];

               *dst++ = r_table [R] | g_table [G] | b_table [B];
            }
         }
      }
   }
   else
   {
      unsigned int *dst;                /* pointer to dithered pixels */
      word_t       *src;                /* current pixel of frame */
      unsigned int *y_table;

      y_table = private->y_table;
      dst     = (unsigned int *) out;
      src     = image->pixels [GRAY];

      if (private->double_resolution)
      {
         int x, y;                      /* pixel coordinates */

         for (y = image->height; y; y--)
         {
            for (x = image->width; x; x--)
            {
               int value;

#ifdef HAVE_SIGNED_SHIFT
               value = y_table [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
               value = y_table [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */
               *dst++ = (value << 16) | value;
            }

            memcpy (dst, dst - image->width,
                    image->width * sizeof (unsigned int));
            dst += image->width;
         }
      }
      else
      {
         int n;                         /* pixel counter */

         for (n = image->width * image->height / 2; n; n--, src += 2)
#ifdef HAVE_SIGNED_SHIFT
#       if BYTE_ORDER == LITTLE_ENDIAN
            *dst++ = (y_table [src [1] >> 4] << 16) | y_table [src [0] >> 4];
#       else
            *dst++ = (y_table [src [0] >> 4] << 16) | y_table [src [1] >> 4];
#       endif
#else /* not HAVE_SIGNED_SHIFT */
#       if BYTE_ORDER == LITTLE_ENDIAN
            *dst++ = (y_table [src [1] / 16] << 16) | y_table [src [0] / 16];
#       else
            *dst++ = (y_table [src [0] / 16] << 16) | y_table [src [1] / 16];
#       endif
#endif /* not HAVE_SIGNED_SHIFT */
      }
   }

   return 1;
}

static int
display_24_bit_bgr (const struct fiasco_renderer *this, unsigned char *ximage,
                    const fiasco_image_t *fiasco_image)
/*
 *  Convert 'image' to 16 bit color bitmap.
 *  If 'double_resolution' is true then double image size in both directions.
 *
 *  No return value.
 *
 *  Side effects:
 *      'out[]' is filled with dithered image
 */
{
   unsigned           *gray_clip = init_clipping ();
   const image_t      *image;
   renderer_private_t *private;
   byte_t             *out;

   if (!gray_clip)
      return 0;
   if (!this)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "this");
      return 0;
   }
   if (!ximage)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
      return 0;
   }
   if (!fiasco_image)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
      return 0;
   }

   out     = (byte_t *) ximage;
   image   = cast_image ((fiasco_image_t *) fiasco_image);
   if (!image)
      return 0;
   private = (renderer_private_t *) this->private;

   if (image->color)
   {
      word_t       *cbptr, *crptr;      /* pointer to chroma bands */
      word_t       *yptr;               /* pointers to lumincance band */
      int          *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;

      Cr_g_tab = private->Cr_g_tab;
      Cr_r_tab = private->Cr_r_tab;
      Cb_b_tab = private->Cb_b_tab;
      Cb_g_tab = private->Cb_g_tab;
      yptr     = image->pixels [Y];
      cbptr    = image->pixels [Cb];
      crptr    = image->pixels [Cr];

      if (image->format == FORMAT_4_2_0)
      {
         if (private->double_resolution)
         {
            int           yval1;        /* lumincance pixel */
            int           crval1, cbval1; /* chroma pixels */
            int           yval2;        /* pixel in YCbCr color space */
            unsigned int  R1, G1, B1;   /* pixel in RGB color space */
            unsigned int  R2, G2, B2;   /* pixel in RGB color space */
            int           x, y;         /* pixel counter */
            unsigned int *dst;          /* pointer to dithered pixels */
            unsigned int *dst2;         /* pointers to dithered pixels */
            word_t       *yptr2;        /* pointers to lumincance band */

            dst   = (unsigned int *) out;
            dst2  = dst + (image->width >> 1) * 3 * 2;
            yptr2 = yptr + image->width;

            for (y = image->height >> 1; y; y--)
            {
               for (x = image->width >> 1; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B1 << 24);
                  *dst++ = G1 | (R1 << 8) | (B2 << 16) | (G2 << 24);
                  *dst++ = R2 | (B2 << 8) | (G2 << 16) | (R2 << 24);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr2++ >> 4) + 128;
                  yval2  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr2++  / 16 + 128;
                  yval2  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst2++ = B1 | (G1 << 8) | (R1 << 16) | (B1 << 24);
                  *dst2++ = G1 | (R1 << 8) | (B2 << 16) | (G2 << 24);
                  *dst2++ = R2 | (B2 << 8) | (G2 << 16) | (R2 << 24);
               }
               memcpy (dst, dst - (image->width >> 1) * 3,
                       (image->width >> 1) * 3 * sizeof (unsigned int));
               memcpy (dst2, dst2 - (image->width >> 1) * 3,
                       (image->width >> 1) * 3 * sizeof (unsigned int));
               dst   += (image->width >> 1) * 3 * 3;
               dst2  += (image->width >> 1) * 3 * 3;
               yptr  += image->width;
               yptr2 += image->width;
            }
         }
         else
         {
            int           yval1;        /* lumincance pixel */
            int           crval1, cbval1; /* chroma pixels */
            int           yval2;        /* pixel in YCbCr color space */
            unsigned int  R1, G1, B1;   /* pixel in RGB color space */
            unsigned int  R2, G2, B2;   /* pixel in RGB color space */
            int           x, y;         /* pixel counter */
            unsigned int *dst;          /* pointer to dithered pixels */
            unsigned int *dst2;         /* pointers to dithered pixels */
            word_t       *yptr2;        /* pointers to lumincance band */

            dst   = (unsigned int *) out;
            dst2  = dst + (image->width >> 2) * 3;
            yptr2 = yptr + image->width;

            for (y = image->height >> 1; y; y--)
            {
               for (x = image->width >> 2; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B2 << 24);
                  *dst   = G2 | (R2 << 8);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr2++ >> 4) + 128;
                  yval2  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr2++  / 16 + 128;
                  yval2  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst2++ = B1 | (G1 << 8) | (R1 << 16) | (B2 << 24);
                  *dst2   = G2 | (R2 << 8);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  crval2 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
                  cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst++ |= (B1 << 16) | (G1 << 24);
                  *dst++ = R1 | (B2 << 8) | (G2 << 16) | (R2 << 24);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr2++ >> 4) + 128;
                  yval2  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr2++  / 16 + 128;
                  yval2  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst2++ |= (B1 << 16) | (G1 << 24);
                  *dst2++ = R1 | (B2 << 8) | (G2 << 16) | (R2 << 24);
               }
               dst   += (image->width >> 2) * 3;
               dst2  += (image->width >> 2) * 3;
               yptr  += image->width;
               yptr2 += image->width;
            }
         }
      }
      else                              /* 4:4:4 format */
      {
         if (private->double_resolution)
         {
            unsigned int R1, G1, B1;            /* pixel1 in RGB color space */
            unsigned int R2, G2, B2;            /* pixel2 in RGB color space */
            int          yval1, crval1, cbval1; /* pixel1 in YCbCr space */
            int          yval2, crval2, cbval2; /* pixel2 in YCbCr space */
            int          x, y;                  /* pixel counter */
            unsigned int *dst;                  /* dithered pixel pointer */

            dst = (unsigned int *) out;

            for (y = image->height; y; y--)
            {
               for (x = image->width >> 1; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  crval2 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
                  cbval2 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  crval2 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
                  cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                 + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval2]
                                 + Cb_g_tab [cbval2]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];

                  *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B1 << 24);
                  *dst++ = G1 | (R1 << 8) | (B2 << 16) | (G2 << 24);
                  *dst++ = R2 | (B2 << 8) | (G2 << 16) | (R2 << 24);
               }
               memcpy (dst, dst - 3 * (image->width >> 1),
                       3 * (image->width >> 1) * sizeof (unsigned int));
               dst += 3 * (image->width >> 1);
            }
         }
         else
         {
            unsigned int R1, G1, B1;            /* pixel in RGB color space */
            unsigned int R2, G2, B2;            /* pixel in RGB color space */
            int          yval1, crval1, cbval1; /* pixel1 in YCbCr space */
            int          yval2, crval2, cbval2; /* pixel2 in YCbCr space */
            int          n;                     /* pixel counter */
            unsigned int *dst;                  /* dithered pixel pointer */

            dst = (unsigned int *) out;

            for (n = (image->width * image->height) >> 2; n; n--)
            {
#ifdef HAVE_SIGNED_SHIFT
               yval1  = (*yptr++ >> 4) + 128;
               yval2  = (*yptr++ >> 4) + 128;
               crval1 = *crptr++ >> 4;
               crval2 = *crptr++ >> 4;
               cbval1 = *cbptr++ >> 4;
               cbval2 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
               yval1  = *yptr++  / 16 + 128;
               yval2  = *yptr++  / 16 + 128;
               crval1 = *crptr++ / 16;
               crval2 = *crptr++ / 16;
               cbval1 = *cbptr++ / 16;
               cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

               R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
               G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
               B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
               R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
               G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
               B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];

               *dst++ = B1 | (G1 << 8) | (R1 << 16) | (B2 << 24);
               *dst   = G2 | (R2 << 8);

#ifdef HAVE_SIGNED_SHIFT
               yval1  = (*yptr++ >> 4) + 128;
               yval2  = (*yptr++ >> 4) + 128;
               crval1 = *crptr++ >> 4;
               crval2 = *crptr++ >> 4;
               cbval1 = *cbptr++ >> 4;
               cbval2 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
               yval1  = *yptr++  / 16 + 128;
               yval2  = *yptr++  / 16 + 128;
               crval1 = *crptr++ / 16;
               crval2 = *crptr++ / 16;
               cbval1 = *cbptr++ / 16;
               cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

               R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
               G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
               B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
               R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
               G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
               B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];

               *dst++ |= (B1 << 16) | (G1 << 24);
               *dst++ = R1 | (B2 << 8) | (G2 << 16) | (R2 << 24);
            }
         }
      }
   }
   else
   {
      unsigned int *dst;                /* pointer to dithered pixels */
      word_t       *src;                /* current pixel of frame */

      dst     = (unsigned int *) out;
      src     = image->pixels [GRAY];

      if (private->double_resolution)
      {
         int       x, y;                /* pixel counter */
         unsigned *shift_clipping = gray_clip + 128;

         for (y = image->height; y; y--)
         {
            for (x = image->width >> 1; x; x--)
            {
               unsigned int val1, val2;
#ifdef HAVE_SIGNED_SHIFT
               val1 = shift_clipping [*src++ >> 4];
               val2 = shift_clipping [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
               val1 = shift_clipping [*src++ / 16];
               val2 = shift_clipping [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */

               *dst++ = val1 | (val1 << 8) | (val1 << 16) | (val1 << 24);
               *dst++ = val1 | (val1 << 8) | (val2 << 16) | (val2 << 24);
               *dst++ = val2 | (val2 << 8) | (val2 << 16) | (val2 << 24);
            }

            memcpy (dst, dst - 3 * (image->width >> 1),
                    3 * (image->width >> 1) * sizeof (unsigned int));
            dst += 3 * (image->width >> 1);
         }
      }
      else
      {
         int       n;                   /* pixel counter */
         unsigned *shift_clipping = gray_clip + 128;

         for (n = (image->width * image->height) >> 2; n; n--)
         {
            unsigned int val1, val2;

#ifdef HAVE_SIGNED_SHIFT
            val1 = shift_clipping [*src++ >> 4];
            val2 = shift_clipping [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
            val1 = shift_clipping [*src++ / 16];
            val2 = shift_clipping [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */

            *dst++ = val1 | (val1 << 8)
                     | (val1 << 16) | (val2 << 24);  /* RGBR */
            *dst   = val2 | (val2 << 8);             /* GB-- */

#ifdef HAVE_SIGNED_SHIFT
            val1 = shift_clipping [*src++ >> 4];
            val2 = shift_clipping [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
            val1 = shift_clipping [*src++ / 16];
            val2 = shift_clipping [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */

            *dst++ |= (val1 << 16) | (val1 << 24);   /* --RG */
            *dst++  = val1 | (val2 << 8)
                      | (val2 << 16) | (val2 << 24); /* BRGB */
         }
      }
   }

   return 1;
}

static int
display_24_bit_rgb (const struct fiasco_renderer *this, unsigned char *ximage,
                    const fiasco_image_t *fiasco_image)
/*
 *  Convert 'image' to 16 bit color bitmap.
 *  If 'double_resolution' is true then double image size in both directions.
 *
 *  No return value.
 *
 *  Side effects:
 *      'out[]' is filled with dithered image
 */
{
   unsigned           *gray_clip = init_clipping ();
   const image_t      *image;
   renderer_private_t *private;
   byte_t             *out;

   if (!gray_clip)
      return 0;
   if (!this)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "this");
      return 0;
   }
   if (!ximage)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
      return 0;
   }
   if (!fiasco_image)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
      return 0;
   }

   out     = (byte_t *) ximage;
   image   = cast_image ((fiasco_image_t *) fiasco_image);
   if (!image)
      return 0;
   private = (renderer_private_t *) this->private;

   if (image->color)
   {
      word_t       *cbptr, *crptr;      /* pointer to chroma bands */
      word_t       *yptr;               /* pointers to lumincance band */
      int          *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;

      Cr_g_tab = private->Cr_g_tab;
      Cr_r_tab = private->Cr_r_tab;
      Cb_b_tab = private->Cb_b_tab;
      Cb_g_tab = private->Cb_g_tab;
      yptr     = image->pixels [Y];
      cbptr    = image->pixels [Cb];
      crptr    = image->pixels [Cr];

      if (image->format == FORMAT_4_2_0)
      {
         if (private->double_resolution)
         {
            int           yval1;        /* lumincance pixel */
            int           crval1, cbval1; /* chroma pixels */
            int           yval2;        /* pixel in YCbCr color space */
            unsigned int  R1, G1, B1;   /* pixel in RGB color space */
            unsigned int  R2, G2, B2;   /* pixel in RGB color space */
            int           x, y;         /* pixel counter */
            unsigned int *dst;          /* pointer to dithered pixels */
            unsigned int *dst2;         /* pointers to dithered pixels */
            word_t       *yptr2;        /* pointers to lumincance band */

            dst   = (unsigned int *) out;
            dst2  = dst + (image->width >> 1) * 3 * 2;
            yptr2 = yptr + image->width;

            for (y = image->height >> 1; y; y--)
            {
               for (x = image->width >> 1; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R1 << 24);
                  *dst++ = G1 | (B1 << 8) | (R2 << 16) | (G2 << 24);
                  *dst++ = B2 | (R2 << 8) | (G2 << 16) | (B2 << 24);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr2++ >> 4) + 128;
                  yval2  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr2++  / 16 + 128;
                  yval2  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst2++ = R1 | (G1 << 8) | (B1 << 16) | (R1 << 24);
                  *dst2++ = G1 | (B1 << 8) | (R2 << 16) | (G2 << 24);
                  *dst2++ = B2 | (R2 << 8) | (G2 << 16) | (B2 << 24);
               }
               memcpy (dst, dst - (image->width >> 1) * 3,
                       (image->width >> 1) * 3 * sizeof (unsigned int));
               memcpy (dst2, dst2 - (image->width >> 1) * 3,
                       (image->width >> 1) * 3 * sizeof (unsigned int));
               dst   += (image->width >> 1) * 3 * 3;
               dst2  += (image->width >> 1) * 3 * 3;
               yptr  += image->width;
               yptr2 += image->width;
            }
         }
         else
         {
            int           yval1;        /* lumincance pixel */
            int           crval1, cbval1; /* chroma pixels */
            int           yval2;        /* pixel in YCbCr color space */
            unsigned int  R1, G1, B1;   /* pixel in RGB color space */
            unsigned int  R2, G2, B2;   /* pixel in RGB color space */
            int           x, y;         /* pixel counter */
            unsigned int *dst;          /* pointer to dithered pixels */
            unsigned int *dst2;         /* pointers to dithered pixels */
            word_t       *yptr2;        /* pointers to lumincance band */

            dst   = (unsigned int *) out;
            dst2  = dst + (image->width >> 2) * 3;
            yptr2 = yptr + image->width;

            for (y = image->height >> 1; y; y--)
            {
               for (x = image->width >> 2; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R2 << 24);
                  *dst   = G2 | (B2 << 8);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr2++ >> 4) + 128;
                  yval2  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr2++  / 16 + 128;
                  yval2  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst2++ = R1 | (G1 << 8) | (B1 << 16) | (R2 << 24);
                  *dst2   = G2 | (B2 << 8);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  crval2 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
                  cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst++ |= (R1 << 16) | (G1 << 24);
                  *dst++ = B1 | (R2 << 8) | (G2 << 16) | (B2 << 24);

#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr2++ >> 4) + 128;
                  yval2  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr2++  / 16 + 128;
                  yval2  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval1]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval1]];

                  *dst2++ |= (R1 << 16) | (G1 << 24);
                  *dst2++ = B1 | (R2 << 8) | (G2 << 16) | (B2 << 24);
               }
               dst   += (image->width >> 2) * 3;
               dst2  += (image->width >> 2) * 3;
               yptr  += image->width;
               yptr2 += image->width;
            }
         }
      }
      else                              /* 4:4:4 format */
      {
         if (private->double_resolution)
         {
            unsigned int R1, G1, B1;            /* pixel1 in RGB color space */
            unsigned int R2, G2, B2;            /* pixel2 in RGB color space */
            int          yval1, crval1, cbval1; /* pixel1 in YCbCr space */
            int          yval2, crval2, cbval2; /* pixel2 in YCbCr space */
            int          x, y;                  /* pixel counter */
            unsigned int *dst;                  /* dithered pixel pointer */

            dst = (unsigned int *) out;

            for (y = image->height; y; y--)
            {
               for (x = image->width >> 1; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  yval1  = (*yptr++ >> 4) + 128;
                  yval2  = (*yptr++ >> 4) + 128;
                  crval1 = *crptr++ >> 4;
                  crval2 = *crptr++ >> 4;
                  cbval1 = *cbptr++ >> 4;
                  cbval2 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
                  yval1  = *yptr++  / 16 + 128;
                  yval2  = *yptr++  / 16 + 128;
                  crval1 = *crptr++ / 16;
                  crval2 = *crptr++ / 16;
                  cbval1 = *cbptr++ / 16;
                  cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

                  R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
                  G1 = gray_clip [yval1 + Cr_g_tab [crval1]
                                  + Cb_g_tab [cbval1]];
                  B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
                  R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
                  G2 = gray_clip [yval2 + Cr_g_tab [crval2]
                                  + Cb_g_tab [cbval2]];
                  B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];

                  *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R1 << 24);
                  *dst++ = G1 | (B1 << 8) | (R2 << 16) | (G2 << 24);
                  *dst++ = B2 | (R2 << 8) | (G2 << 16) | (B2 << 24);
               }
               memcpy (dst, dst - 3 * (image->width >> 1),
                       3 * (image->width >> 1) * sizeof (unsigned int));
               dst += 3 * (image->width >> 1);
            }
         }
         else
         {
            unsigned int R1, G1, B1;            /* pixel in RGB color space */
            unsigned int R2, G2, B2;            /* pixel in RGB color space */
            int          yval1, crval1, cbval1; /* pixel1 in YCbCr space */
            int          yval2, crval2, cbval2; /* pixel2 in YCbCr space */
            int          n;                     /* pixel counter */
            unsigned int *dst;                  /* dithered pixel pointer */

            dst = (unsigned int *) out;

            for (n = (image->width * image->height) >> 2; n; n--)
            {
#ifdef HAVE_SIGNED_SHIFT
               yval1  = (*yptr++ >> 4) + 128;
               yval2  = (*yptr++ >> 4) + 128;
               crval1 = *crptr++ >> 4;
               crval2 = *crptr++ >> 4;
               cbval1 = *cbptr++ >> 4;
               cbval2 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
               yval1  = *yptr++  / 16 + 128;
               yval2  = *yptr++  / 16 + 128;
               crval1 = *crptr++ / 16;
               crval2 = *crptr++ / 16;
               cbval1 = *cbptr++ / 16;
               cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

               R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
               G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
               B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
               R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
               G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
               B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];

               *dst++ = R1 | (G1 << 8) | (B1 << 16) | (R2 << 24);
               *dst   = G2 | (B2 << 8);

#ifdef HAVE_SIGNED_SHIFT
               yval1  = (*yptr++ >> 4) + 128;
               yval2  = (*yptr++ >> 4) + 128;
               crval1 = *crptr++ >> 4;
               crval2 = *crptr++ >> 4;
               cbval1 = *cbptr++ >> 4;
               cbval2 = *cbptr++ >> 4;
#else /* not HAVE_SIGNED_SHIFT */
               yval1  = *yptr++  / 16 + 128;
               yval2  = *yptr++  / 16 + 128;
               crval1 = *crptr++ / 16;
               crval2 = *crptr++ / 16;
               cbval1 = *cbptr++ / 16;
               cbval2 = *cbptr++ / 16;
#endif /* not HAVE_SIGNED_SHIFT */

               R1 = gray_clip [yval1 + Cr_r_tab [crval1]];
               G1 = gray_clip [yval1 + Cr_g_tab [crval1] + Cb_g_tab [cbval1]];
               B1 = gray_clip [yval1 + Cb_b_tab [cbval1]];
               R2 = gray_clip [yval2 + Cr_r_tab [crval2]];
               G2 = gray_clip [yval2 + Cr_g_tab [crval2] + Cb_g_tab [cbval2]];
               B2 = gray_clip [yval2 + Cb_b_tab [cbval2]];

               *dst++ |= (R1 << 16) | (G1 << 24);
               *dst++ = B1 | (R2 << 8) | (G2 << 16) | (B2 << 24);
            }
         }
      }
   }
   else
   {
      unsigned int *dst;                /* pointer to dithered pixels */
      word_t       *src;                /* current pixel of frame */

      dst     = (unsigned int *) out;
      src     = image->pixels [GRAY];

      if (private->double_resolution)
      {
         int       x, y;                /* pixel counter */
         unsigned *shift_clipping = gray_clip + 128;

         for (y = image->height; y; y--)
         {
            for (x = image->width >> 1; x; x--)
            {
               unsigned int val1, val2;
#ifdef HAVE_SIGNED_SHIFT
               val1 = shift_clipping [*src++ >> 4];
               val2 = shift_clipping [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
               val1 = shift_clipping [*src++ / 16];
               val2 = shift_clipping [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */

               *dst++ = val1 | (val1 << 8) | (val1 << 16) | (val1 << 24);
               *dst++ = val1 | (val1 << 8) | (val2 << 16) | (val2 << 24);
               *dst++ = val2 | (val2 << 8) | (val2 << 16) | (val2 << 24);
            }

            memcpy (dst, dst - 3 * (image->width >> 1),
                    3 * (image->width >> 1) * sizeof (unsigned int));
            dst += 3 * (image->width >> 1);
         }
      }
      else
      {
         int       n;                   /* pixel counter */
         unsigned *shift_clipping = gray_clip + 128;

         for (n = (image->width * image->height) >> 2; n; n--)
         {
            unsigned int val1, val2;

#ifdef HAVE_SIGNED_SHIFT
            val1 = shift_clipping [*src++ >> 4];
            val2 = shift_clipping [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
            val1 = shift_clipping [*src++ / 16];
            val2 = shift_clipping [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */

            *dst++ = val1 | (val1 << 8)
                     | (val1 << 16) | (val2 << 24);  /* RGBR */
            *dst   = val2 | (val2 << 8);             /* GB-- */

#ifdef HAVE_SIGNED_SHIFT
            val1 = shift_clipping [*src++ >> 4];
            val2 = shift_clipping [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
            val1 = shift_clipping [*src++ / 16];
            val2 = shift_clipping [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */

            *dst++ |= (val1 << 16) | (val1 << 24);   /* --RG */
            *dst++  = val1 | (val2 << 8)
                      | (val2 << 16) | (val2 << 24); /* BRGB */
         }
      }
   }

   return 1;
}

static int
display_32_bit (const struct fiasco_renderer *this, unsigned char *ximage,
                const fiasco_image_t *fiasco_image)
/*
 *  Convert 'image' to 16 bit color bitmap.
 *  If 'double_resolution' is true then double image size in both directions.
 *
 *  No return value.
 *
 *  Side effects:
 *      'out[]' is filled with dithered image
 */
{
   const image_t      *image;
   renderer_private_t *private;
   byte_t             *out;

   if (!this)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "this");
      return 0;
   }
   if (!ximage)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "ximage");
      return 0;
   }
   if (!fiasco_image)
   {
      set_error (_("Parameter `%s' not defined (NULL)."), "fiasco_image");
      return 0;
   }

   out     = (byte_t *) ximage;
   private = (renderer_private_t *) this->private;
   image   = cast_image ((fiasco_image_t *) fiasco_image);
   if (!image)
      return 0;

   if (image->color)
   {
      word_t       *cbptr, *crptr;      /* pointer to chroma bands */
      word_t       *yptr;               /* pointers to lumincance band */
      int           yval, crval, cbval; /* pixel value in YCbCr color space */
      int           R, G, B;            /* pixel value in RGB color space */
      int           n;                  /* pixel counter */
      int           x, y;               /* pixel coordinates */
      int          *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
      unsigned int *r_table, *g_table, *b_table;

      Cr_g_tab = private->Cr_g_tab;
      Cr_r_tab = private->Cr_r_tab;
      Cb_b_tab = private->Cb_b_tab;
      Cb_g_tab = private->Cb_g_tab;
      r_table  = private->r_table;
      g_table  = private->g_table;
      b_table  = private->b_table;
      yptr  = image->pixels [Y];
      cbptr = image->pixels [Cb];
      crptr = image->pixels [Cr];

      if (image->format == FORMAT_4_2_0)
      {
         unsigned int   *dst, *dst2;    /* pointers to dithered pixels */
         word_t         *yptr2;         /* pointers to lumincance band */

         if (private->double_resolution)
         {
            yptr2 = yptr + image->width;
            dst  = (unsigned int *) out;
            dst2 = dst + 4 * image->width;
            for (y = image->height / 2; y; y--)
            {
               for (x = image->width / 2; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  crval = *crptr++ >> 4;
                  cbval = *cbptr++ >> 4;
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  crval = *crptr++ / 16;
                  cbval = *cbptr++ / 16;
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
               }
               memcpy (dst, dst - 2 * image->width,
                       2 * image->width * sizeof (unsigned int));
               memcpy (dst2, dst2 - 2 * image->width,
                       2 * image->width * sizeof (unsigned int));
               yptr  += image->width;
               yptr2 += image->width;
               dst   += 3 * image->width * 2;
               dst2  += 3 * image->width * 2;
            }
         }
         else
         {
            yptr2 = yptr + image->width;
            dst   = (unsigned int *) out;
            dst2  = dst + image->width;

            for (y = image->height / 2; y; y--)
            {
               for (x = image->width / 2; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  crval = *crptr++ >> 4;
                  cbval = *cbptr++ >> 4;
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  crval = *crptr++ / 16;
                  cbval = *cbptr++ / 16;
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];

#ifdef HAVE_SIGNED_SHIFT
                  yval  = (*yptr2++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  yval  = *yptr2++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];
                  *dst2++ = r_table [R] | g_table [G] | b_table [B];
               }
               yptr  += image->width;
               yptr2 += image->width;
               dst   += image->width;
               dst2  += image->width;
            }
         }
      }
      else                              /* 4:4:4 format */
      {
         if (private->double_resolution)
         {
            unsigned int *dst;          /* pointer to dithered pixels */

            dst = (unsigned int *) out;

            for (y = image->height; y; y--)
            {
               for (x = image->width; x; x--)
               {
#ifdef HAVE_SIGNED_SHIFT
                  crval = *crptr++ >> 4;
                  cbval = *cbptr++ >> 4;
                  yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
                  crval = *crptr++ / 16;
                  cbval = *cbptr++ / 16;
                  yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
                  R = yval + Cr_r_tab [crval];
                  G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
                  B = yval + Cb_b_tab [cbval];

                  *dst++ = r_table [R] | g_table [G] | b_table [B];
                  *dst++ = r_table [R] | g_table [G] | b_table [B];
               }
               memcpy (dst, dst - 2 * image->width,
                       2 * image->width * sizeof (unsigned int));
               dst += image->width * 2;
            }
         }
         else
         {
            unsigned int *dst;          /* pointer to dithered pixels */

            dst  = (unsigned int *) out;

            for (n = image->width * image->height; n; n--)
            {
#ifdef HAVE_SIGNED_SHIFT
               crval = *crptr++ >> 4;
               cbval = *cbptr++ >> 4;
               yval  = (*yptr++ >> 4) + 128;
#else /* not HAVE_SIGNED_SHIFT */
               crval = *crptr++ / 16;
               cbval = *cbptr++ / 16;
               yval  = *yptr++  / 16 + 128;
#endif /* not HAVE_SIGNED_SHIFT */
               R = yval + Cr_r_tab [crval];
               G = yval + Cr_g_tab [crval] + Cb_g_tab [cbval];
               B = yval + Cb_b_tab [cbval];

               *dst++ = r_table [R] | g_table [G] | b_table [B];
            }
         }
      }
   }
   else
   {
      unsigned int *dst;                /* pointer to dithered pixels */
      word_t       *src;                /* current pixel of frame */
      unsigned int *y_table;

      y_table = private->y_table;
      dst     = (unsigned int *) out;
      src     = image->pixels [GRAY];

      if (private->double_resolution)
      {
         int x, y;                      /* pixel coordinates */

         for (y = image->height; y; y--)
         {
            for (x = image->width; x; x--)
            {
               int value;

#ifdef HAVE_SIGNED_SHIFT
               value = y_table [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
               value = y_table [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */
               *dst++ = value;
               *dst++ = value;
            }

            memcpy (dst, dst - 2 * image->width,
                    2 * image->width * sizeof (unsigned int));
            dst += 2 * image->width;
         }
      }
      else
      {
         int n;                         /* pixel counter */

         for (n = image->width * image->height; n; n--)
#ifdef HAVE_SIGNED_SHIFT
            *dst++ = y_table [*src++ >> 4];
#else /* not HAVE_SIGNED_SHIFT */
            *dst++ = y_table [*src++ / 16];
#endif /* not HAVE_SIGNED_SHIFT */
      }
   }

   return 1;
}



