mirror of
				https://git.code.sf.net/p/libpng/code.git
				synced 2025-07-10 18:04:09 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			3770 lines
		
	
	
		
			113 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3770 lines
		
	
	
		
			113 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | |
| /* pngtrans.c - transforms the data in a row (used by both readers and writers)
 | |
|  *
 | |
|  * Last changed in libpng 1.7.0 [(PENDING RELEASE)]
 | |
|  * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
 | |
|  * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
 | |
|  * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
 | |
|  *
 | |
|  * This code is released under the libpng license.
 | |
|  * For conditions of distribution and use, see the disclaimer
 | |
|  * and license in png.h
 | |
|  */
 | |
| #include "pngpriv.h"
 | |
| #define PNG_SRC_FILE PNG_SRC_FILE_pngtrans
 | |
| 
 | |
| #ifdef _XOPEN_SOURCE
 | |
| #  include <unistd.h>
 | |
| #endif /* for swab */
 | |
| 
 | |
| /* Memory format enquiries */
 | |
| #ifdef PNG_GAMMA_SUPPORTED
 | |
| static png_fixed_point
 | |
| memory_gamma(png_const_structrp png_ptr)
 | |
| {
 | |
| #  ifdef PNG_READ_GAMMA_SUPPORTED
 | |
| #     ifdef PNG_TRANSFORM_MECH_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|             return png_ptr->row_gamma;
 | |
| #     endif /* TRANSFORM_MECH */
 | |
| #  endif /* READ_GAMMA */
 | |
| 
 | |
|    /* Else either no READ_GAMMA support or this is a write struct; in both
 | |
|     * cases there are no gamma transforms.  In the write case the set of the
 | |
|     * gamma in the info may not have been copied to the png_struct.
 | |
|     */
 | |
| #  if defined(PNG_GAMMA_SUPPORTED) && defined(PNG_READ_SUPPORTED)
 | |
|       if ((png_ptr->colorspace.flags &
 | |
|             (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
 | |
|          PNG_COLORSPACE_HAVE_GAMMA)
 | |
|          return png_ptr->colorspace.gamma;
 | |
| #  else /* !(GAMMA && READ) */
 | |
|       PNG_UNUSED(png_ptr)
 | |
| #  endif /* !(GAMMA && READ) */
 | |
| 
 | |
|    /* '0' means the value is not know: */
 | |
|    return 0;
 | |
| }
 | |
| #endif /* GAMMA */
 | |
| 
 | |
| unsigned int PNGAPI
 | |
| png_memory_format(png_structrp png_ptr)
 | |
| {
 | |
|    /* The in-memory format as a bitmask of PNG_FORMAT_FLAG_ values.  All the
 | |
|     * flags listed below are used.  If PNG_FORMAT_FLAG_INVALID is set the
 | |
|     * following caveats apply to the interpretation of PNG_FORMAT_FLAG_LINEAR:
 | |
|     *
 | |
|     *    The gamma may differ from the sRGB (!LINEAR) or 1.0 (LINEAR).  Call
 | |
|     *    png_memory_gamma to find the correct value.
 | |
|     *
 | |
|     *    The channel depth may differ from 8 (!LINEAR) or 16 (LINEAR).  Call
 | |
|     *    png_memory_channel_depth to find the correct value.
 | |
|     *
 | |
|     * It is only valid to call these APIS *after* either png_read_update_info
 | |
|     * or png_start_read_image on read or after the first row of an image has
 | |
|     * been written on write.
 | |
|     */
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
| #     ifdef PNG_TRANSFORM_MECH_SUPPORTED
 | |
|          unsigned int format = png_ptr->row_format;
 | |
| #     else /* !TRANSFORM_MECH */
 | |
|          unsigned int format = PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type);
 | |
| #     endif /* !TRANSFORM_MECH */
 | |
| 
 | |
|       if (png_ptr->read_struct) /* else no way to find the gamma! */
 | |
|       {
 | |
| #        ifdef PNG_GAMMA_SUPPORTED
 | |
| #           ifdef PNG_TRANSFORM_MECH_SUPPORTED
 | |
|                unsigned int bit_depth = png_ptr->row_bit_depth;
 | |
| #           else /* !TRANSFORM_MECH */
 | |
|                unsigned int bit_depth = png_ptr->bit_depth;
 | |
| #           endif /* !TRANSFORM_MECH */
 | |
| 
 | |
|             /* Now work out whether this is a valid simplified API format. */
 | |
|             switch (bit_depth)
 | |
|             {
 | |
|                case 8U:
 | |
|                   {
 | |
|                      png_fixed_point gamma = memory_gamma(png_ptr);
 | |
| 
 | |
|                      if (!PNG_GAMMA_IS_sRGB(gamma))
 | |
|                         format |= PNG_FORMAT_FLAG_INVALID;
 | |
|                   }
 | |
|                   break;
 | |
| 
 | |
|                case 16:
 | |
|                   if (memory_gamma(png_ptr) == PNG_GAMMA_LINEAR)
 | |
|                   {
 | |
|                      static const union
 | |
|                      {
 | |
|                         png_uint_16 u16;
 | |
|                         png_byte    u8[2];
 | |
|                      } sex = { 1U };
 | |
| 
 | |
|                      format |= PNG_FORMAT_FLAG_LINEAR;
 | |
| 
 | |
|                      /* But the memory layout of the 16-bit quantities must also
 | |
|                       * match; we need swapped data on LSB platforms.
 | |
|                       */
 | |
|                      if (sex.u8[0] == ((format & PNG_FORMAT_FLAG_SWAPPED) != 0))
 | |
|                         break; /* ok */
 | |
|                   }
 | |
| 
 | |
|                   /* FALL THROUGH*/
 | |
|                default: /* bit depth not supported for simplified API */
 | |
|                   format |= PNG_FORMAT_FLAG_INVALID;
 | |
|                   break;
 | |
|             }
 | |
| #        else /* !GAMMA */
 | |
|             /* We have no way of knowing if the gamma value matches that
 | |
|              * expected by the simplified API so mark the format as invalid:
 | |
|              */
 | |
|             format |= PNG_FORMAT_FLAG_INVALID;
 | |
| #        endif
 | |
|       } /* read_struct */
 | |
| 
 | |
|       return format;
 | |
|    }
 | |
| 
 | |
|    return 0;
 | |
| }
 | |
| 
 | |
| unsigned int PNGAPI png_memory_channel_depth(png_structrp png_ptr)
 | |
| {
 | |
|    /* The actual depth of each channel in the image. */
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
| #     ifdef PNG_TRANSFORM_MECH_SUPPORTED
 | |
|          return png_ptr->row_bit_depth;
 | |
| #     else
 | |
|          return png_ptr->bit_depth;
 | |
| #     endif
 | |
|    }
 | |
| 
 | |
|    return 0;
 | |
| }
 | |
| 
 | |
| #ifdef PNG_GAMMA_SUPPORTED
 | |
| png_fixed_point PNGAPI
 | |
| png_memory_gamma(png_structrp png_ptr)
 | |
| {
 | |
|    /* The actual gamma of the image data, scaled by 100,000.  This is the
 | |
|     * encoding gamma, e.g. 1/2.2 for sRGB.  If the gamma is unknown this will
 | |
|     * return 0.
 | |
|     *
 | |
|     * On write this invariably returns 0; libpng does not change the gamma of
 | |
|     * the data on write.
 | |
|     *
 | |
|     * Note that this is not always the exact inverse of the 'screen gamma'
 | |
|     * passed to png_set_gamma; internal optimizations remove attempts to make
 | |
|     * small changes to the gamma value.  This function returns the actual
 | |
|     * output value.
 | |
|     */
 | |
|    return (png_ptr != NULL) ? memory_gamma(png_ptr) : 0;
 | |
| }
 | |
| #endif /* GAMMA */
 | |
| 
 | |
| /* These are general purpose APIs that deal with the row buffer format in both
 | |
|  * the read and write case.  The png_struct::row_* members describe the
 | |
|  * in-memory format of the image data based on the transformations requested by
 | |
|  * the application.
 | |
|  */
 | |
| #ifdef PNG_TRANSFORM_MECH_SUPPORTED
 | |
| png_voidp /* PRIVATE */
 | |
| png_transform_cast_check(png_const_structp png_ptr, unsigned int src_line,
 | |
|    png_transformp tr, size_t size)
 | |
| {
 | |
|    /* Given a pointer to a transform, 'tr' validate that the underlying derived
 | |
|     * class has size 'size' using the tr->size field and return the same
 | |
|     * pointer.  If there is a size mismatch the function does an affirm using
 | |
|     * the given line number.
 | |
|     */
 | |
|    if (tr->size != size)
 | |
|       png_affirm(png_ptr, param_deb("transform upcast") src_line);
 | |
| 
 | |
|    return tr;
 | |
| }
 | |
| 
 | |
| void /* PRIAVE */
 | |
| png_transform_free(png_const_structrp png_ptr, png_transformp *list)
 | |
| {
 | |
|    if (*list != NULL)
 | |
|    {
 | |
|       png_transform_free(png_ptr, &(*list)-> next);
 | |
|       if ((*list)->free != NULL)
 | |
|          (*list)->free(png_ptr, *list);
 | |
|       png_free(png_ptr, *list);
 | |
|       *list = NULL;
 | |
|    }
 | |
| }
 | |
| 
 | |
| /* Utility to initialize a png_transform_control for read or write. */
 | |
| void /* PRIVATE */
 | |
| png_init_transform_control(png_transform_controlp tc, png_structp png_ptr)
 | |
| {
 | |
|    png_byte bd; /* bit depth of the row */
 | |
|    png_byte cd; /* bit depth of color information */
 | |
| 
 | |
|    memset(tc, 0, sizeof *tc);
 | |
|    tc->png_ptr = png_ptr; /* ALIAS */
 | |
|    tc->sp = tc->dp = NULL;
 | |
|    tc->width = 0;
 | |
| 
 | |
| #  ifdef PNG_READ_GAMMA_SUPPORTED
 | |
|       /* The file gamma is set by png_set_gamma, as well as being read from the
 | |
|        * input PNG gAMA chunk, if present, we don't have a png_info so can't get
 | |
|        * the set_gAMA value but this doesn't matter because on read the gamma
 | |
|        * value is in png_struct::colorspace and on write it isn't used.
 | |
|        */
 | |
|       if ((png_ptr->colorspace.flags &
 | |
|             (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
 | |
|          PNG_COLORSPACE_HAVE_GAMMA)
 | |
|       {
 | |
|          tc->gamma = png_ptr->colorspace.gamma;
 | |
|          debug(tc->gamma > 0);
 | |
|       }
 | |
| 
 | |
|       else
 | |
|       {
 | |
|          /* There is no input gamma, so there should be no overall gamma
 | |
|           * correction going on.  This test works because the various things
 | |
|           * that set an output gamma also default the input gamma.
 | |
|           */
 | |
|          debug(png_ptr->row_gamma == 0);
 | |
|       }
 | |
| #  endif
 | |
| 
 | |
|    /* Validate bit depth and color type here */
 | |
|    cd = bd = png_ptr->bit_depth;
 | |
| 
 | |
|    switch (png_ptr->color_type)
 | |
|    {
 | |
|       case PNG_COLOR_TYPE_GRAY:
 | |
|          affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U || bd == 16U);
 | |
|          tc->format = 0U;
 | |
|          break;
 | |
| 
 | |
|       case PNG_COLOR_TYPE_PALETTE:
 | |
|          affirm(bd == 1U || bd == 2U || bd == 4U || bd == 8U);
 | |
|          tc->format = PNG_FORMAT_FLAG_COLORMAP | PNG_FORMAT_FLAG_COLOR;
 | |
|          cd = 8U;
 | |
|          break;
 | |
| 
 | |
|       case PNG_COLOR_TYPE_GRAY_ALPHA:
 | |
|          affirm(bd == 8U || bd == 16U);
 | |
|          tc->format = PNG_FORMAT_FLAG_ALPHA;
 | |
|          break;
 | |
| 
 | |
|       case PNG_COLOR_TYPE_RGB:
 | |
|          affirm(bd == 8U || bd == 16U);
 | |
|          tc->format = PNG_FORMAT_FLAG_COLOR;
 | |
|          break;
 | |
| 
 | |
|       case PNG_COLOR_TYPE_RGB_ALPHA:
 | |
|          affirm(bd == 8U || bd == 16U);
 | |
|          tc->format = PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_ALPHA;
 | |
|          break;
 | |
| 
 | |
|       default:
 | |
|          impossible("PNG color type");
 | |
|    }
 | |
| 
 | |
|    tc->bit_depth = bd;
 | |
|    tc->range = 0;
 | |
| 
 | |
|    /* Preset the sBIT data to full precision/handled. */
 | |
|    tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = tc->sBIT_A = cd;
 | |
| #  ifdef PNG_READ_sBIT_SUPPORTED
 | |
|       {
 | |
|          int handled = 1;
 | |
| 
 | |
|          if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) != 0)
 | |
|          {
 | |
|             png_byte c = png_ptr->sig_bit.red;
 | |
|             if (c > 0 && c < cd)
 | |
|             {
 | |
|                tc->sBIT_R = c;
 | |
|                handled = 0;
 | |
|             }
 | |
| 
 | |
|             c = png_ptr->sig_bit.green;
 | |
|             if (c > 0 && c < cd)
 | |
|             {
 | |
|                tc->sBIT_G = c;
 | |
|                handled = 0;
 | |
|             }
 | |
| 
 | |
|             c = png_ptr->sig_bit.blue;
 | |
|             if (c > 0 && c < cd)
 | |
|             {
 | |
|                tc->sBIT_B = c;
 | |
|                handled = 0;
 | |
|             }
 | |
|          }
 | |
| 
 | |
|          else /* grayscale */
 | |
|          {
 | |
|             png_byte c = png_ptr->sig_bit.gray;
 | |
|             if (c > 0 && c < cd)
 | |
|             {
 | |
|                tc->sBIT_R = tc->sBIT_G = tc->sBIT_B = c;
 | |
|                handled = 0;
 | |
|             }
 | |
|          }
 | |
| 
 | |
|          /* The palette-mapped format doesn't store alpha information, an
 | |
|           * omission in the spec that is difficult to fix.  Notice that
 | |
|           * 'handled' is not cleared below, this is because the alpha channel is
 | |
|           * always linear, so the sBIT_A value can always be treated as a
 | |
|           * precision value.
 | |
|           */
 | |
|          if ((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0)
 | |
|          {
 | |
|             png_byte c = png_ptr->sig_bit.alpha;
 | |
|             if (c > 0 && c < cd)
 | |
|                tc->sBIT_A = c;
 | |
|          }
 | |
| 
 | |
|          /* If 'handled' did not get cleared there is no sBIT information. */
 | |
|          if (handled)
 | |
|             tc->invalid_info = PNG_INFO_sBIT;
 | |
|       }
 | |
| #  else /* !READ_sBIT */
 | |
|       /* No sBIT information */
 | |
|       tc->invalid_info = PNG_INFO_sBIT;
 | |
| #  endif /* !READ_sBIT */
 | |
| }
 | |
| 
 | |
| png_transformp /*PRIVATE*/
 | |
| png_add_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
 | |
|    unsigned int order)
 | |
| {
 | |
|    /* Add a transform.  This is a minimal implementation; the order is just
 | |
|     * controlled by 'order', the result is a point to the new transform, or
 | |
|     * to an existing one if one was already in the list.
 | |
|     */
 | |
|    png_transformp *p = &png_ptr->transform_list;
 | |
| 
 | |
|    while (*p != NULL && (*p)->order < order)
 | |
|       p = &(*p)->next;
 | |
| 
 | |
|    if (size == 0)
 | |
|       size = sizeof (png_transform);
 | |
| 
 | |
|    else
 | |
|       affirm(size >= sizeof (png_transform));
 | |
| 
 | |
|    if (*p == NULL || (*p)->order > order)
 | |
|    {
 | |
|       png_transformp t;
 | |
| 
 | |
|       t = png_voidcast(png_transformp, png_malloc(png_ptr, size));
 | |
|       memset(t, 0, size); /* zeros out the extra data too */
 | |
|       /* *p comes after the new entry, t: */
 | |
|       t->next = *p;
 | |
|       t->fn = fn;
 | |
|       t->free = NULL;
 | |
|       t->order = order;
 | |
|       t->size = 0xFFFFU & size;
 | |
|       *p = t;
 | |
|       return t;
 | |
|    }
 | |
| 
 | |
|    else /* (*p)->order matches order, return *p */
 | |
|    {
 | |
|        affirm((*p)->fn == fn && (*p)->order == order && (*p)->size == size);
 | |
|        return *p;
 | |
|    }
 | |
| }
 | |
| 
 | |
| png_transformp /* PRIVATE */
 | |
| png_push_transform(png_structrp png_ptr, size_t size, png_transform_fn fn,
 | |
|    png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    png_transformp tr = *transform;
 | |
|    unsigned int order = tr->order;
 | |
| 
 | |
|    /* Basic loop detection: */
 | |
|    affirm(fn != NULL && tr->fn != fn);
 | |
| 
 | |
|    /* Move the following transforms up: */
 | |
|    {
 | |
|       unsigned int old_order = order;
 | |
| 
 | |
|       do
 | |
|       {
 | |
|          tr->order = ++old_order;
 | |
|          tr = tr->next;
 | |
|       }
 | |
|       while (tr != NULL && tr->order == old_order);
 | |
| 
 | |
|       affirm(tr == NULL || tr->order > old_order);
 | |
|    }
 | |
| 
 | |
|    *transform = png_add_transform(png_ptr, size, fn, order);
 | |
| 
 | |
|    if (tc != NULL)
 | |
|       fn(transform, tc);
 | |
| 
 | |
|    return *transform;
 | |
| }
 | |
| 
 | |
| #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
 | |
| static png_transformp
 | |
| png_find_transform(png_const_structrp png_ptr, unsigned int order)
 | |
|    /* Find a transform with the given order, or return NULL.  Currently only
 | |
|     * used here.
 | |
|     */
 | |
| {
 | |
|    png_transformp p = png_ptr->transform_list;
 | |
| 
 | |
|    for (;;)
 | |
|    {
 | |
|       if (p == NULL || p->order > order)
 | |
|          return NULL;
 | |
| 
 | |
|       if (p->order == order)
 | |
|          return p;
 | |
| 
 | |
|       p = p->next;
 | |
|    }
 | |
| }
 | |
| #endif /* USER_TRANSFORM_PTR */
 | |
| 
 | |
| static void
 | |
| remove_transform(png_const_structp png_ptr, png_transformp *transform)
 | |
|    /* Remove a transform on a running list */
 | |
| {
 | |
|    png_transformp tp = *transform;
 | |
|    png_transformp next = tp->next;
 | |
| 
 | |
|    *transform = next;
 | |
|    tp->next = NULL;
 | |
|    png_transform_free(png_ptr, &tp);
 | |
| }
 | |
| 
 | |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED
 | |
| void /* PRIVATE */
 | |
| png_remove_transform(png_const_structp png_ptr, png_transformp *transform)
 | |
| {
 | |
|    remove_transform(png_ptr, transform);
 | |
| }
 | |
| #endif /* READ_TRANSFORMS */
 | |
| 
 | |
| static unsigned int
 | |
| run_transform_list_forwards(png_transform_controlp tc, png_transformp *start,
 | |
|    png_transformp end/*NULL for whole list*/)
 | |
|    /* Called from the init code and below, the caller must initialize 'tc' */
 | |
| {
 | |
|    png_const_structp png_ptr = tc->png_ptr;
 | |
|    unsigned int max_depth = PNG_TC_PIXEL_DEPTH(*tc);
 | |
| 
 | |
|    /* Caller guarantees that *start is non-NULL */
 | |
|    debug(*start != NULL);
 | |
| 
 | |
|    do
 | |
|    {
 | |
|       if ((*start)->fn != NULL)
 | |
|          (*start)->fn(start, tc);
 | |
| 
 | |
|       if ((*start)->fn == NULL) /* delete this transform */
 | |
|          remove_transform(png_ptr, start);
 | |
| 
 | |
|       else
 | |
|       {
 | |
|          /* Handle the initialization of the maximum pixel depth. */
 | |
|          unsigned int tc_depth = PNG_TC_PIXEL_DEPTH(*tc);
 | |
| 
 | |
|          if (tc_depth > max_depth)
 | |
|             max_depth = tc_depth;
 | |
| 
 | |
|          /* Advance to the next transform. */
 | |
|          start = &(*start)->next;
 | |
|       }
 | |
|    }
 | |
|    while (*start != NULL && *start != end);
 | |
| 
 | |
|    /* This only goes wrong if 'end' was non-NULL and not in the list: */
 | |
|    debug(*start == end);
 | |
| 
 | |
|    return max_depth;
 | |
| }
 | |
| 
 | |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED
 | |
| unsigned int /* PRIVATE */
 | |
| png_run_this_transform_list_forwards(png_transform_controlp tc,
 | |
|    png_transformp *start, png_transformp end)
 | |
| {
 | |
|    return run_transform_list_forwards(tc, start, end);
 | |
| }
 | |
| #endif /* READ_TRANSFORMS */
 | |
| 
 | |
| #ifdef PNG_READ_SUPPORTED
 | |
| unsigned int /* PRIVATE */
 | |
| png_run_transform_list_forwards(png_structp png_ptr, png_transform_controlp tc)
 | |
| {
 | |
|    if (png_ptr->transform_list != NULL)
 | |
|       return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL);
 | |
| 
 | |
|    else
 | |
|       return PNG_PIXEL_DEPTH(*png_ptr);
 | |
| }
 | |
| #endif /* READ */
 | |
| 
 | |
| #ifdef PNG_WRITE_SUPPORTED /* only used from pngwrite.c */
 | |
| static unsigned int
 | |
| run_transform_list_backwards(png_transform_controlp tc, png_transformp *list)
 | |
| {
 | |
|    png_const_structp png_ptr = tc->png_ptr;
 | |
|    unsigned int max_depth = 0;
 | |
| 
 | |
|    if ((*list)->next != NULL)
 | |
|       max_depth = run_transform_list_backwards(tc, &(*list)->next);
 | |
| 
 | |
|    /* Note that the above might change (*list)->next, but it can't change
 | |
|     * *list itself.
 | |
|     */
 | |
|    if ((*list)->fn != NULL)
 | |
|       (*list)->fn(list, tc);
 | |
| 
 | |
|    /* If that set 'fn' to NULL this transform must be removed; this is how
 | |
|     * (*list)->next gets changed in our caller:
 | |
|     */
 | |
|    if ((*list)->fn == NULL)
 | |
|       remove_transform(png_ptr, list);
 | |
| 
 | |
|    else
 | |
|    {
 | |
|       unsigned int depth = PNG_TC_PIXEL_DEPTH(*tc);
 | |
| 
 | |
|       if (depth > max_depth)
 | |
|          max_depth = depth;
 | |
|    }
 | |
| 
 | |
|    return max_depth;
 | |
| }
 | |
| 
 | |
| void /* PRIVATE */
 | |
| png_run_transform_list_backwards(png_structp png_ptr, png_transform_controlp tc)
 | |
| {
 | |
|    if (png_ptr->transform_list != NULL)
 | |
|    {
 | |
|       /* This doesn't take account of the base PNG depth, but that shouldn't
 | |
|        * matter, it's just a check:
 | |
|        */
 | |
|       unsigned int max_depth =
 | |
|          run_transform_list_backwards(tc, &png_ptr->transform_list);
 | |
| 
 | |
|       /* Better late than never (if this fires a memory overwrite has happened):
 | |
|        */
 | |
|       affirm(max_depth <= png_ptr->row_max_pixel_depth);
 | |
|    }
 | |
| }
 | |
| #endif /* WRITE */
 | |
| 
 | |
| static unsigned int
 | |
| init_transform_mech(png_structrp png_ptr, png_transform_control *tc, int start)
 | |
|    /* Called each time to run the transform list once during initialization. */
 | |
| {
 | |
|    png_init_transform_control(tc, png_ptr);
 | |
|    tc->init = start ? PNG_TC_INIT_FORMAT : PNG_TC_INIT_FINAL;
 | |
| #  ifdef PNG_READ_TRANSFORMS_SUPPORTED
 | |
|       if (png_ptr->read_struct)
 | |
|          return png_read_init_transform_mech(png_ptr, tc);
 | |
|       else
 | |
| #  endif
 | |
|    return run_transform_list_forwards(tc, &png_ptr->transform_list, NULL);
 | |
| }
 | |
| #endif /* TRANSFORM_MECH */
 | |
| 
 | |
| #ifdef PNG_PALETTE_MAX_SUPPORTED
 | |
| static int
 | |
| set_palette_max(png_structrp png_ptr, png_transformp tr, unsigned int max,
 | |
|       unsigned int format_max)
 | |
|    /* Called whenever a new maximum pixel value is found */
 | |
| {
 | |
|    /* One of these must be true: */
 | |
| #  ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
 | |
|       if (max >= tr->args && !png_ptr->palette_index_check_issued)
 | |
|       {
 | |
| #        ifdef PNG_READ_SUPPORTED
 | |
| #           ifdef PNG_WRITE_SUPPORTED
 | |
|                (png_ptr->read_struct ? png_chunk_benign_error : png_error)
 | |
| #           else /* !WRITE */
 | |
|                png_chunk_benign_error
 | |
| #           endif /* !WRITE */
 | |
| #        else /* !READ */
 | |
|             png_error
 | |
| #        endif /* !READ */
 | |
|             (png_ptr, "palette index too large");
 | |
|          png_ptr->palette_index_check_issued = 1;
 | |
|       }
 | |
| #  endif
 | |
| #  ifdef PNG_GET_PALETTE_MAX_SUPPORTED
 | |
|       png_ptr->palette_index_max = png_check_bits(png_ptr, max, 9);
 | |
| #  endif
 | |
| 
 | |
|    if (max == format_max)
 | |
|    {
 | |
|       tr->fn = NULL; /* no point continuing once the max has been seen */
 | |
|       return 1; /* stop */
 | |
|    }
 | |
| 
 | |
|    return 0; /* keep going */
 | |
| }
 | |
| 
 | |
| static void
 | |
| palette_max_1bpp(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    while (width >= 8)
 | |
|    {
 | |
|       if (*sp++) break;
 | |
|       width -= 8;
 | |
|    }
 | |
| 
 | |
|    if (width < 8)
 | |
|    {
 | |
|       if (width == 0 ||
 | |
|           (*sp & (((1U<<width)-1U) << (8-width))) == 0)
 | |
|          return; /* no '1' pixels */
 | |
|    }
 | |
| 
 | |
|    /* If the code reaches this point there is a set pixel */
 | |
|    (void)set_palette_max(tc->png_ptr, *tr, 1U, 1U);
 | |
| }
 | |
| 
 | |
| static void
 | |
| palette_max_2bpp(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_uint_32 width = tc->width;
 | |
|    unsigned int max = (*tr)->args; /* saved maximum */
 | |
| 
 | |
|    while (width > 0)
 | |
|    {
 | |
|       png_uint_32 input = 0U, test;
 | |
|       unsigned int new_max;
 | |
| 
 | |
|       /* This just skips 0 bytes: */
 | |
|       while (width > 0)
 | |
|       {
 | |
|          unsigned int next = *sp++;
 | |
| 
 | |
|          /* There may be partial pixels at the end, just remove the absent
 | |
|           * pixels with a right shift:
 | |
|           */
 | |
|          if (width >= 4)
 | |
|             width -= 4;
 | |
|          else
 | |
|             next >>= (4U-width) * 2U, width = 0;
 | |
| 
 | |
|          if (next)
 | |
|          {
 | |
|             input = (input << 8) | next;
 | |
|             if ((input & 0xFF000000U) != 0)
 | |
|                break;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       test = input & 0xAAAAAAAAU;
 | |
| 
 | |
|       if (test != 0)
 | |
|       {
 | |
|          if ((input & (test >> 1)) != 0)
 | |
|             new_max = 3U; /* both bits set in at least one pixel */
 | |
| 
 | |
|          else if (max < 2U)
 | |
|             new_max = 2U;
 | |
| 
 | |
|          else
 | |
|             continue; /* no change to max */
 | |
|       }
 | |
| 
 | |
|       else /* test is 0 */ if (input != 0 && max == 0)
 | |
|          new_max = 1U;
 | |
| 
 | |
|       else /* input is 0, or max is at least 1 */
 | |
|          continue;
 | |
| 
 | |
|       /* new_max is greater than max: */
 | |
|       if (set_palette_max(tc->png_ptr, *tr, new_max, 3U))
 | |
|          return;
 | |
| 
 | |
|       /* Record new_max: */
 | |
|       max = new_max;
 | |
|    }
 | |
| 
 | |
|    /* End of input, check the next line. */
 | |
|    (*tr)->args = max;
 | |
| }
 | |
| 
 | |
| static void
 | |
| palette_max_4bpp(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_uint_32 width = tc->width;
 | |
|    unsigned int max = (*tr)->args; /* saved maximum */
 | |
| 
 | |
|    while (width > 0)
 | |
|    {
 | |
|       unsigned int input = *sp++;
 | |
| 
 | |
|       if (width >= 2)
 | |
|          width -= 2;
 | |
|       else
 | |
|          input >>= 1, width = 0;
 | |
| 
 | |
|       if ((input & 0xFU) > max)
 | |
|          max = input & 0xFU;
 | |
| 
 | |
|       if (((input >> 4) & 0xFU) > max)
 | |
|          max = (input >> 4) & 0xFU;
 | |
|    }
 | |
| 
 | |
|    if (max > (*tr)->args)
 | |
|    {
 | |
|       if (set_palette_max(tc->png_ptr, *tr, max, 15U))
 | |
|          return;
 | |
| 
 | |
|       (*tr)->args = max;
 | |
|    }
 | |
| }
 | |
| 
 | |
| static void
 | |
| palette_max_8bpp(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_uint_32 width = tc->width;
 | |
|    unsigned int max = (*tr)->args; /* saved maximum */
 | |
| 
 | |
|    while (width > 0)
 | |
|    {
 | |
|       unsigned int input = *sp++;
 | |
| 
 | |
|       if (input > max)
 | |
|          max = input;
 | |
| 
 | |
|       --width;
 | |
|    }
 | |
| 
 | |
|    if (max > (*tr)->args)
 | |
|    {
 | |
|       if (set_palette_max(tc->png_ptr, *tr, max, 255U))
 | |
|          return;
 | |
| 
 | |
|       (*tr)->args = max;
 | |
|    }
 | |
| }
 | |
| 
 | |
| static void
 | |
| palette_max_init(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
 | |
|    {
 | |
|       if (tc->init == PNG_TC_INIT_FINAL)
 | |
|       {
 | |
|          /* Record the palette depth to check here: */
 | |
|          (*tr)->args = png_ptr->num_palette;
 | |
| 
 | |
|          switch (tc->bit_depth)
 | |
|          {
 | |
|             case 1: (*tr)->fn = palette_max_1bpp; break;
 | |
|             case 2: (*tr)->fn = palette_max_2bpp; break;
 | |
|             case 4: (*tr)->fn = palette_max_4bpp; break;
 | |
|             case 8: (*tr)->fn = palette_max_8bpp; break;
 | |
|             default:impossible("palette bit depth");
 | |
|          }
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    else
 | |
|       (*tr)->fn = NULL; /* not applicable */
 | |
| #  undef png_ptr
 | |
| }
 | |
| #endif /* PALETTE_MAX */
 | |
| 
 | |
| #ifdef PNG_GET_PALETTE_MAX_SUPPORTED
 | |
| int PNGAPI
 | |
| png_get_palette_max(png_const_structrp png_ptr, png_const_inforp info_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL
 | |
| #     ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
 | |
|          && !png_ptr->palette_index_check_disabled
 | |
| #     endif
 | |
|       )
 | |
|       return png_ptr->palette_index_max;
 | |
| 
 | |
|    /* This indicates to the caller that the information is not available: */
 | |
|    return -1;
 | |
|    PNG_UNUSED(info_ptr)
 | |
| }
 | |
| #endif /* GET_PALETTE_MAX */
 | |
| 
 | |
| #ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
 | |
|    /* Whether to report invalid palette index; added at libng-1.5.10.
 | |
|     * It is possible for an indexed (color-type==3) PNG file to contain
 | |
|     * pixels with invalid (out-of-range) indexes if the PLTE chunk has
 | |
|     * fewer entries than the image's bit-depth would allow. We recover
 | |
|     * from this gracefully by filling any incomplete palette with zeros
 | |
|     * (opaque black).  By default, when this occurs libpng will issue
 | |
|     * a benign error.  This API can be used to override that behavior.
 | |
|     */
 | |
| void PNGAPI
 | |
| png_set_check_for_invalid_index(png_structrp png_ptr, int enabled)
 | |
| {
 | |
|    /* This defaults to 0, therefore *on*: */
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       if (png_ptr->read_struct)
 | |
| #        ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
 | |
|             png_ptr->palette_index_check_disabled = enabled <= 0;
 | |
| #        else /* !READ_CHECK_FOR_INVALID_INDEX */
 | |
|             png_app_error(png_ptr, "no read palette check support");
 | |
| #        endif /* !READ_CHECK_FOR_INVALID_INDEX */
 | |
|       else /* write struct */
 | |
| #        ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
 | |
|             png_ptr->palette_index_check_disabled = enabled <= 0;
 | |
| #        else /* !WRITE_CHECK_FOR_INVALID_INDEX */
 | |
|             png_app_error(png_ptr, "no write palette check support");
 | |
| #        endif /* !WRITE_CHECK_FOR_INVALID_INDEX */
 | |
|    }
 | |
| }
 | |
| #endif /* CHECK_FOR_INVALID_INDEX */
 | |
| 
 | |
| void /* PRIVATE */
 | |
| png_init_row_info(png_structrp png_ptr)
 | |
| {
 | |
|    /* PNG pixels never exceed 64 bits in depth: */
 | |
|    const png_byte png_depth =
 | |
|       png_check_bits(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), 7U);
 | |
| 
 | |
| #  ifdef PNG_TRANSFORM_MECH_SUPPORTED
 | |
|       /* The palette index check stuff is *on* automatically.  To handle this
 | |
|        * add it here, if it is supported.
 | |
|        */
 | |
| #     ifdef PNG_PALETTE_MAX_SUPPORTED
 | |
|          /* The logic here is a little complex because of the plethora of
 | |
|           * #defines controlling this stuff.
 | |
|           */
 | |
|          if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && (
 | |
| #           if defined (PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\
 | |
|                defined (PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED)
 | |
|                (png_ptr->read_struct
 | |
| #              ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
 | |
|                   && !png_ptr->palette_index_check_disabled)
 | |
| #              endif /* READ_CHECK_FOR_INVALID_INDEX */
 | |
| #           else /* no READ support */
 | |
|                0
 | |
| #           endif /* READ checks */
 | |
|             ||
 | |
| #           if defined (PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\
 | |
|                defined (PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
 | |
|                (!png_ptr->read_struct
 | |
| #              ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
 | |
|                   && !png_ptr->palette_index_check_disabled)
 | |
| #              endif /* WRITE_CHECK_FOR_INVALID_INDEX */
 | |
| #           else /* no WRITE support */
 | |
|                0
 | |
| #           endif /* WRITE checks */
 | |
|             ))
 | |
|             png_add_transform(png_ptr, 0/*size*/, palette_max_init,
 | |
|                PNG_TR_CHECK_PALETTE);
 | |
| #     endif
 | |
| 
 | |
|       /* Application transforms may change the format of the data or, when
 | |
|        * producing interlaced images, the number of pixels in a line.  This code
 | |
|        * determines the maximum pixel depth required and allows transformations
 | |
|        * a chance to initialize themselves.
 | |
|        */
 | |
|       if (png_ptr->transform_list != NULL)
 | |
|       {
 | |
|          png_transform_control tc;
 | |
| 
 | |
|          (void)init_transform_mech(png_ptr, &tc, 1/*start*/);
 | |
| 
 | |
|          png_ptr->row_format = png_check_bits(png_ptr, tc.format, PNG_RF_BITS);
 | |
|          affirm(tc.bit_depth <= 32);
 | |
|          png_ptr->row_bit_depth = png_check_bits(png_ptr, tc.bit_depth, 6);
 | |
|          png_ptr->row_range = png_check_bits(png_ptr, tc.range, 3);
 | |
| #        ifdef PNG_READ_GAMMA_SUPPORTED
 | |
|             png_ptr->row_gamma = tc.gamma;
 | |
| #        endif /* READ_GAMMA */
 | |
| 
 | |
|          /* The above may have cancelled all the transforms in the list. */
 | |
|          if (png_ptr->transform_list != NULL)
 | |
|          {
 | |
|             /* Run the transform list again, also forward, and accumulate the
 | |
|              * maximum pixel depth.  At this point the transforms can swap
 | |
|              * out their initialization code.
 | |
|              */
 | |
|             unsigned int max_depth =
 | |
|                init_transform_mech(png_ptr, &tc, 0/*final*/);
 | |
| 
 | |
|             /* init_transform_mech is expected to take the input depth into
 | |
|              * account:
 | |
|              */
 | |
|             debug(max_depth >= png_depth);
 | |
|             if (max_depth < png_depth)
 | |
|                 max_depth = png_depth;
 | |
|             affirm(max_depth <= (png_ptr->read_struct ? 128U : 64U));
 | |
| 
 | |
| #           ifdef PNG_READ_TRANSFORMS_SUPPORTED
 | |
|                /* Set this now because it only gets resolved finally at this
 | |
|                 * point.
 | |
|                 */
 | |
|                png_ptr->invalid_info = tc.invalid_info;
 | |
| #           endif /* READ_TRANSFORMS */
 | |
| 
 | |
|             /* And check the transform fields: */
 | |
|             affirm(png_ptr->row_format == tc.format &&
 | |
|                png_ptr->row_range == tc.range &&
 | |
|                png_ptr->row_bit_depth == tc.bit_depth);
 | |
| #           ifdef PNG_READ_GAMMA_SUPPORTED
 | |
|                affirm(png_ptr->row_gamma == tc.gamma);
 | |
| #           endif /* READ_GAMMA */
 | |
| 
 | |
|             png_ptr->row_max_pixel_depth =
 | |
|                png_check_bits(png_ptr, max_depth, 8U);
 | |
| 
 | |
|             /* On 'read' input_depth is the PNG pixel depth and output_depth is
 | |
|              * the depth of the pixels passed to the application, but on 'write'
 | |
|              * the transform list is reversed so output_depth is the PNG depth
 | |
|              * and input_depth the application depth.
 | |
|              */
 | |
|             {
 | |
|                const png_byte app_depth =
 | |
|                   png_check_bits(png_ptr, PNG_TC_PIXEL_DEPTH(tc), 8U);
 | |
| 
 | |
|                affirm(app_depth <= max_depth);
 | |
| 
 | |
|                if (png_ptr->read_struct)
 | |
|                {
 | |
|                   png_ptr->row_input_pixel_depth = png_depth;
 | |
|                   png_ptr->row_output_pixel_depth = app_depth;
 | |
|                }
 | |
| 
 | |
|                else
 | |
|                {
 | |
|                   png_ptr->row_input_pixel_depth = app_depth;
 | |
|                   png_ptr->row_output_pixel_depth = png_depth;
 | |
|                }
 | |
| 
 | |
|                return; /* to skip the default settings below */
 | |
|             }
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       else /* png_ptr->transform_list == NULL */
 | |
|       {
 | |
|          png_ptr->row_format = png_check_bits(png_ptr,
 | |
|             PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type), PNG_RF_BITS);
 | |
|          png_ptr->row_bit_depth = png_check_bits(png_ptr, png_ptr->bit_depth,
 | |
|             6);
 | |
|          png_ptr->row_range = 0;
 | |
| #        ifdef PNG_READ_GAMMA_SUPPORTED
 | |
|             if ((png_ptr->colorspace.flags &
 | |
|                   (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) ==
 | |
|                  PNG_COLORSPACE_HAVE_GAMMA)
 | |
|                png_ptr->row_gamma = png_ptr->colorspace.gamma;
 | |
| #        endif /* READ_GAMMA */
 | |
| #        ifdef PNG_READ_TRANSFORMS_SUPPORTED
 | |
|             png_ptr->invalid_info = 0U;
 | |
| #        endif /* READ_TRANSFORMS */
 | |
|       }
 | |
| #  endif /* TRANSFORM_MECH */
 | |
| 
 | |
|    /* We get here if there are no transforms therefore no change to the pixel
 | |
|     * bit depths.
 | |
|     */
 | |
|    png_ptr->row_output_pixel_depth = png_ptr->row_max_pixel_depth =
 | |
|       png_ptr->row_input_pixel_depth = png_depth;
 | |
| }
 | |
| 
 | |
| #if defined(PNG_READ_INTERLACING_SUPPORTED) || \
 | |
|     defined(PNG_WRITE_INTERLACING_SUPPORTED)
 | |
| int PNGAPI
 | |
| png_set_interlace_handling(png_structrp png_ptr)
 | |
| {
 | |
|    png_debug(1, "in png_set_interlace handling");
 | |
| 
 | |
|    if (png_ptr != 0)
 | |
|    {
 | |
|       if (png_ptr->read_struct)
 | |
|       {
 | |
| #        ifdef PNG_READ_INTERLACING_SUPPORTED
 | |
|             if (png_ptr->interlaced)
 | |
|             {
 | |
|                png_ptr->do_interlace = 1;
 | |
|                return PNG_INTERLACE_ADAM7_PASSES;
 | |
|             }
 | |
| 
 | |
|             return 1;
 | |
| #        else /* !READ_INTERLACING */
 | |
|             png_app_error(png_ptr, "no de-interlace support");
 | |
|             /* return 0 below */
 | |
| #        endif /* !READ_INTERLACING */
 | |
|       }
 | |
| 
 | |
|       else /* write */
 | |
|       {
 | |
| #        ifdef PNG_WRITE_INTERLACING_SUPPORTED
 | |
|             if (png_ptr->interlaced)
 | |
|             {
 | |
|                png_ptr->do_interlace = 1;
 | |
|                return PNG_INTERLACE_ADAM7_PASSES;
 | |
|             }
 | |
| 
 | |
|             return 1;
 | |
| #        else /* !WRITE_INTERLACING */
 | |
|             png_app_error(png_ptr, "no interlace support");
 | |
|             /* return 0 below */
 | |
| #        endif /* !WRITE_INTERLACING */
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    /* API CHANGE: 1.7.0: returns 0 if called with a NULL png_ptr */
 | |
|    return 0;
 | |
| }
 | |
| #endif /* READ_INTERLACING || WRITE_INTERLACING */
 | |
| 
 | |
| #ifdef PNG_MNG_READ_FEATURES_SUPPORTED
 | |
| /* Undoes intrapixel differencing, this is called immediately after the PNG
 | |
|  * filter has been undone.
 | |
|  */
 | |
| static void
 | |
| png_do_read_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp
 | |
|     * are overwriting sp[]
 | |
|     */
 | |
|    do
 | |
|    {
 | |
|       *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */
 | |
|       *dp++ = *++sp; /* green */
 | |
|       *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */
 | |
|       sp += 2;
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_read_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    do
 | |
|    {
 | |
|       *dp++ = PNG_BYTE(sp[0] + sp[1]); /* red+green */
 | |
|       *dp++ = *++sp; /* green */
 | |
|       *dp++ = PNG_BYTE(sp[0] + sp[1]); /* green+blue */
 | |
|       sp += 2;
 | |
|       *dp++ = *sp++; /* alpha */
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_read_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    /* The input consists of 16-bit values and, by examination of the code
 | |
|     * (please, someone, check; I didn't read the spec) the differencing is done
 | |
|     * against the 16-bit green value.
 | |
|     */
 | |
|    do
 | |
|    {
 | |
|       unsigned int red   = png_get_uint_16(sp + 0);
 | |
|       unsigned int green = png_get_uint_16(sp + 2);
 | |
|       unsigned int blue  = png_get_uint_16(sp + 4);
 | |
|       sp += 6;
 | |
| 
 | |
|       red  += green;
 | |
|       blue += green;
 | |
| 
 | |
|       *dp++ = PNG_BYTE(red >> 8);
 | |
|       *dp++ = PNG_BYTE(red);
 | |
|       *dp++ = PNG_BYTE(green >> 8);
 | |
|       *dp++ = PNG_BYTE(green);
 | |
|       *dp++ = PNG_BYTE(blue >> 8);
 | |
|       *dp++ = PNG_BYTE(blue);
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_read_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    /* As above but copy the alpha over too. */
 | |
|    do
 | |
|    {
 | |
|       unsigned int red   = png_get_uint_16(sp + 0);
 | |
|       unsigned int green = png_get_uint_16(sp + 2);
 | |
|       unsigned int blue  = png_get_uint_16(sp + 4);
 | |
|       sp += 6;
 | |
| 
 | |
|       red  += green;
 | |
|       blue += green;
 | |
| 
 | |
|       *dp++ = PNG_BYTE(red >> 8);
 | |
|       *dp++ = PNG_BYTE(red);
 | |
|       *dp++ = PNG_BYTE(green >> 8);
 | |
|       *dp++ = PNG_BYTE(green);
 | |
|       *dp++ = PNG_BYTE(blue >> 8);
 | |
|       *dp++ = PNG_BYTE(blue);
 | |
|       *dp++ = *sp++;
 | |
|       *dp++ = *sp++; /* alpha */
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_init_read_intrapixel(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    /* Double check the permitted MNG features in case the app turned the feature
 | |
|     * on then off again.  Also make sure the color type is acceptable; it must
 | |
|     * be RGB or RGBA.
 | |
|     */
 | |
|    png_const_structp png_ptr = tc->png_ptr;
 | |
| 
 | |
|    if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
 | |
|        (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) &&
 | |
|        (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
 | |
|          PNG_FORMAT_FLAG_COLOR)
 | |
|    {
 | |
|       if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc))
 | |
|       {
 | |
|          case 24: (*tr)->fn = png_do_read_intrapixel_RGB8;   break;
 | |
|          case 32: (*tr)->fn = png_do_read_intrapixel_RGBA8;  break;
 | |
|          case 48: (*tr)->fn = png_do_read_intrapixel_RGB16;  break;
 | |
|          case 64: (*tr)->fn = png_do_read_intrapixel_RGBA16; break;
 | |
|          default: impossible("bit depth");
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    else
 | |
|       (*tr)->fn = NULL;
 | |
| }
 | |
| #endif /* MNG_READ_FEATURES_SUPPORTED */
 | |
| 
 | |
| #ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
 | |
| /* This is just the forward direction of the above:
 | |
|  *
 | |
|  *    red := red - green
 | |
|  *    blue:= blue- green
 | |
|  *
 | |
|  * Alpha is not changed.
 | |
|  */
 | |
| static void
 | |
| png_do_write_intrapixel_RGB8(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    /* TAKE CARE: dp and sp may be the same, in which case the assignments to *dp
 | |
|     * are overwriting sp[]
 | |
|     */
 | |
|    do
 | |
|    {
 | |
|       *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */
 | |
|       *dp++ = *++sp; /* green */
 | |
|       *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */
 | |
|       sp += 2;
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_write_intrapixel_RGBA8(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    do
 | |
|    {
 | |
|       *dp++ = PNG_BYTE(sp[0] - sp[1]); /* red-green */
 | |
|       *dp++ = *++sp; /* green */
 | |
|       *dp++ = PNG_BYTE(sp[0] - sp[1]); /* green-blue */
 | |
|       sp += 2;
 | |
|       *dp++ = *sp++; /* alpha */
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_write_intrapixel_RGB16(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    do
 | |
|    {
 | |
|       unsigned int red   = png_get_uint_16(sp + 0);
 | |
|       unsigned int green = png_get_uint_16(sp + 2);
 | |
|       unsigned int blue  = png_get_uint_16(sp + 4);
 | |
|       sp += 6;
 | |
| 
 | |
|       red  -= green;
 | |
|       blue -= green;
 | |
| 
 | |
|       *dp++ = PNG_BYTE(red >> 8);
 | |
|       *dp++ = PNG_BYTE(red);
 | |
|       *dp++ = PNG_BYTE(green >> 8);
 | |
|       *dp++ = PNG_BYTE(green);
 | |
|       *dp++ = PNG_BYTE(blue >> 8);
 | |
|       *dp++ = PNG_BYTE(blue);
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_write_intrapixel_RGBA16(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_uint_32 width = tc->width;
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    /* As above but copy the alpha over too. */
 | |
|    do
 | |
|    {
 | |
|       unsigned int red   = png_get_uint_16(sp + 0);
 | |
|       unsigned int green = png_get_uint_16(sp + 2);
 | |
|       unsigned int blue  = png_get_uint_16(sp + 4);
 | |
|       sp += 6;
 | |
| 
 | |
|       red  -= green;
 | |
|       blue -= green;
 | |
| 
 | |
|       *dp++ = PNG_BYTE(red >> 8);
 | |
|       *dp++ = PNG_BYTE(red);
 | |
|       *dp++ = PNG_BYTE(green >> 8);
 | |
|       *dp++ = PNG_BYTE(green);
 | |
|       *dp++ = PNG_BYTE(blue >> 8);
 | |
|       *dp++ = PNG_BYTE(blue);
 | |
|       *dp++ = *sp++;
 | |
|       *dp++ = *sp++; /* alpha */
 | |
|    }
 | |
|    while (--width > 0);
 | |
| 
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    UNTESTED
 | |
| #  undef png_ptr
 | |
|    PNG_UNUSED(tr)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_init_write_intrapixel(png_transformp *tr, png_transform_controlp tc)
 | |
| {
 | |
|    /* Write filter_method 64 (intrapixel differencing) only if:
 | |
|     *
 | |
|     * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED, and;
 | |
|     * 2. Libpng did not write a PNG signature (this filter_method is only
 | |
|     *    used in PNG datastreams that are embedded in MNG datastreams),
 | |
|     *    and;
 | |
|     * 3. The application called png_permit_mng_features with a mask that
 | |
|     *    included PNG_FLAG_MNG_FILTER_64, and;
 | |
|     * 4. The filter_method is 64, and;
 | |
|     * 5. The color_type is RGB or RGBA
 | |
|     */
 | |
|    png_const_structp png_ptr = tc->png_ptr;
 | |
| 
 | |
|    if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
 | |
|        (png_ptr->filter_method == PNG_INTRAPIXEL_DIFFERENCING) &&
 | |
|        (tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
 | |
|          PNG_FORMAT_FLAG_COLOR)
 | |
|    {
 | |
|       if (tc->init == PNG_TC_INIT_FINAL) switch (PNG_TC_PIXEL_DEPTH(*tc))
 | |
|       {
 | |
|          case 24: (*tr)->fn = png_do_write_intrapixel_RGB8;   break;
 | |
|          case 32: (*tr)->fn = png_do_write_intrapixel_RGBA8;  break;
 | |
|          case 48: (*tr)->fn = png_do_write_intrapixel_RGB16;  break;
 | |
|          case 64: (*tr)->fn = png_do_write_intrapixel_RGBA16; break;
 | |
|          default: impossible("bit depth");
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    else
 | |
|       (*tr)->fn = NULL;
 | |
| }
 | |
| #endif /* MNG_WRITE_FEATURES */
 | |
| 
 | |
| #ifdef PNG_MNG_FEATURES_SUPPORTED
 | |
| png_uint_32 PNGAPI
 | |
| png_permit_mng_features(png_structrp png_ptr, png_uint_32 mng_features)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
| #     ifdef PNG_MNG_READ_FEATURES_SUPPORTED
 | |
|          if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0)
 | |
|             png_add_transform(png_ptr, 0/*size*/, png_init_read_intrapixel,
 | |
|                PNG_TR_MNG_INTRAPIXEL);
 | |
| #     else /* !MNG_READ_FEATURES */
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "MNG not supported on read");
 | |
|             return;
 | |
|          }
 | |
| #     endif /* !MNG_READ_FEATURES */
 | |
| 
 | |
| #     ifdef PNG_MNG_WRITE_FEATURES_SUPPORTED
 | |
|          if ((mng_features & PNG_FLAG_MNG_FILTER_64) != 0)
 | |
|             png_add_transform(png_ptr, 0/*size*/, png_init_write_intrapixel,
 | |
|                PNG_TR_MNG_INTRAPIXEL);
 | |
| #     else /* !MNG_WRITE_FEATURES */
 | |
|          if (!png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "MNG not supported on write");
 | |
|             return;
 | |
|          }
 | |
| #     endif /* !MNG_WRITE_FEATURES */
 | |
| 
 | |
|       return png_ptr->mng_features_permitted =
 | |
|          mng_features & PNG_ALL_MNG_FEATURES;
 | |
|    }
 | |
| 
 | |
|    return 0;
 | |
| }
 | |
| #endif /* MNG_FEATURES */
 | |
| 
 | |
| #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) ||\
 | |
|     defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) ||\
 | |
|     defined(PNG_READ_SWAP_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_READ_FILLER_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_FILLER_SUPPORTED) ||\
 | |
|     defined(PNG_READ_STRIP_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_READ_STRIP_16_TO_8_SUPPORTED) ||\
 | |
|     defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) ||\
 | |
|     defined(PNG_READ_EXPAND_16_SUPPORTED) ||\
 | |
|     defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
 | |
| /* This is a generic transform which manipulates the bytes in an input row.  The
 | |
|  * manipulations supported are:
 | |
|  *
 | |
|  *    Channel addition (alpha or filler)
 | |
|  *    Channel removal (alpha or filler)
 | |
|  *    Channel swaps - RGB to BGR, alpha/filler from last to first and vice versa
 | |
|  *
 | |
|  * The output is described in blocks of output pixel size 4-bit codes encoded
 | |
|  * as follows:
 | |
|  *
 | |
|  *    0        Advance the source pointer by the source pixel size, start the
 | |
|  *             code list again.  This code doesn't actually exist; it is simply
 | |
|  *             the result of emptying the code list.
 | |
|  *    1..3     An error (ignored; treated like 0)
 | |
|  *    4..7     Put filler[code-4] into the output
 | |
|  *    8..15    Put source byte[code-8] in the output
 | |
|  *
 | |
|  * The codes are held in a png_uint_32 parameter.  transform->args is used by
 | |
|  * the init routine to work out the required codes.  The format change is a mask
 | |
|  * which is XORed with the tc format.  Note that the init routine works out
 | |
|  * whether to work from the beginning or end of the row and the codes are always
 | |
|  * stored LSB first in the order needed.
 | |
|  */
 | |
| typedef struct
 | |
| {
 | |
|    png_transform tr;
 | |
|    png_uint_32   codes;      /* As above */
 | |
|    unsigned int  format;     /* format after transform */
 | |
|    unsigned int  bit_depth;  /* bit depth after transform */
 | |
|    png_byte      filler[4];  /* Filler or alpha bytes, LSB first (see below) */
 | |
| }  png_transform_byte_op;
 | |
| 
 | |
| static void
 | |
| png_do_byte_ops_up(png_transformp *transform, png_transform_controlp tc)
 | |
|    /* Row width is unchanged or decreasing */
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_transform_byte_op *tr =
 | |
|       png_transform_cast(png_transform_byte_op, *transform);
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
 | |
|    const png_const_bytep ep = sp + PNG_TC_ROWBYTES(*tc);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
| 
 | |
|    debug(tc->bit_depth == 8 || tc->bit_depth == 16);
 | |
|    debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0);
 | |
| 
 | |
|    tc->sp = tc->dp;
 | |
|    tc->format = tr->format;
 | |
|    tc->bit_depth = tr->bit_depth;
 | |
| 
 | |
|    /* 'output' is a 32-byte buffer that is used to delay writes for 16 bytes,
 | |
|     * avoiding overwrite when source and destination buffers are the same.
 | |
|     * 'hwm' is either 32 or 16, initially '32', when the byte counter 'i'
 | |
|     * reaches 'hwm' the last-but-one 16 bytes are written; the bytes
 | |
|     * [hwm..hwm+15] modulo 32.  hwm is then swapped to hwm+16 mod 32 and i
 | |
|     * continues to advance.  i is always below hwm.
 | |
|     *
 | |
|     * At the end the whole remaining buffer from hwm to i is written.
 | |
|     */
 | |
|    {
 | |
|       const png_uint_32 codes = tr->codes;
 | |
|       png_uint_32 code = codes;
 | |
|       unsigned int i, hwm; /* buffer index and high-water-mark */
 | |
|       png_byte output[32];
 | |
| 
 | |
|       hwm = 32;
 | |
| 
 | |
|       for (i=0;;)
 | |
|       {
 | |
|          unsigned int next_code = code & 0xf;
 | |
| 
 | |
|          if (next_code >= 8)
 | |
|             output[i++] = sp[next_code-8];
 | |
| 
 | |
|          else if (next_code >= 4)
 | |
|             output[i++] = tr->filler[next_code - 4];
 | |
| 
 | |
|          else /* end code */
 | |
|          {
 | |
|             sp += sp_advance;
 | |
| 
 | |
|             if (sp >= ep)
 | |
|                break; /* i may be == hwm at this point. */
 | |
| 
 | |
|             code = codes;
 | |
|             continue; /* no ouput produced, skip the check */
 | |
|          }
 | |
| 
 | |
|          code >>= 4; /* find the next code */
 | |
| 
 | |
|          if (i == hwm)
 | |
|          {
 | |
|             hwm &= 0x10U; /* 0 or 16 */
 | |
|             memcpy(dp, output + hwm, 16);
 | |
|             dp += 16;
 | |
|             i = hwm; /* reset i if hwm was 32 */
 | |
|             /* hwm is only ever 16 or 32: */
 | |
|             hwm += 16;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       /* Write from hwm to (i-1), the delay means there is always something to
 | |
|        * write.
 | |
|        */
 | |
|       hwm &= 0x10U;
 | |
|       if (hwm == 16)
 | |
|       {
 | |
|          debug(i <= 16);
 | |
|          memcpy(dp, output + hwm, 16);
 | |
|          dp += 16;
 | |
|       }
 | |
| 
 | |
|       if (i > 0)
 | |
|          memcpy(dp, output, i);
 | |
| 
 | |
| #     ifndef PNG_RELEASE_BUILD
 | |
|          dp += i;
 | |
|          /* The macro expansion exceeded the limit on ANSI strings, so split it:
 | |
|           */
 | |
|          dp -= PNG_TC_ROWBYTES(*tc);
 | |
|          debug(dp == tc->dp);
 | |
| #     endif
 | |
|    }
 | |
| 
 | |
|    debug(sp == ep);
 | |
| #  undef png_ptr
 | |
| }
 | |
| 
 | |
| #ifdef PNG_READ_TRANSFORMS_SUPPORTED
 | |
| static void
 | |
| png_do_byte_ops_down(png_transformp *transform, png_transform_controlp tc)
 | |
|    /* Row width is increasing */
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_transform_byte_op *tr =
 | |
|       png_transform_cast(png_transform_byte_op, *transform);
 | |
|    const png_const_bytep ep = png_voidcast(png_const_bytep, tc->sp);
 | |
|    const unsigned int sp_advance = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
 | |
|    png_const_bytep sp = ep + PNG_TC_ROWBYTES(*tc);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_alloc_size_t dest_rowbytes;
 | |
| 
 | |
|    debug(tc->bit_depth == 8U || tc->bit_depth == 16U);
 | |
|    debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U);
 | |
| 
 | |
|    tc->sp = tc->dp;
 | |
|    tc->format = tr->format;
 | |
|    tc->bit_depth = tr->bit_depth;
 | |
|    dest_rowbytes = PNG_TC_ROWBYTES(*tc);
 | |
|    dp += dest_rowbytes;
 | |
| 
 | |
|    /* In this case the 32-byte buffer is written downwards with a writes delayed
 | |
|     * by 16 bytes as before.  'hwm' is lower than i; 0 or 16.
 | |
|     */
 | |
|    {
 | |
|       const png_uint_32 codes = tr->codes;
 | |
|       png_uint_32 code = codes;
 | |
|       unsigned int size, hwm, i;
 | |
|       png_byte output[32] = { 0 };
 | |
| 
 | |
|       /* This catches an empty codes array, which would cause all the input to
 | |
|        * be skipped and, potentially, a garbage output[] to be written (once) to
 | |
|        * *dp.
 | |
|        */
 | |
|       affirm((codes & 0xFU) >= 4U);
 | |
| 
 | |
|       /* Align the writes to a 16-byte multiple from the start of the
 | |
|        * destination buffer:
 | |
|        */
 | |
|       size = dest_rowbytes & 0xFU;
 | |
|       if (size == 0U) size = 16U;
 | |
|       i = size+16U;
 | |
|       sp -= sp_advance; /* Move 1 pixel back */
 | |
|       hwm = 0U;
 | |
| 
 | |
|       for (;;)
 | |
|       {
 | |
|          unsigned int next_code = code & 0xFU;
 | |
| 
 | |
|          if (next_code >= 8U)
 | |
|             output[--i] = sp[next_code-8U];
 | |
| 
 | |
|          else if (next_code >= 4U)
 | |
|             output[--i] = tr->filler[next_code - 4U];
 | |
| 
 | |
|          else /* end code */
 | |
|          {
 | |
|             sp -= sp_advance;
 | |
| 
 | |
|             if (sp < ep)
 | |
|                break;
 | |
| 
 | |
|             code = codes;
 | |
|             continue; /* no ouput produced, skip the check */
 | |
|          }
 | |
| 
 | |
|          code >>= 4; /* find the next code */
 | |
| 
 | |
|          if (i == hwm)
 | |
|          {
 | |
|             /* A partial copy comes at the beginning to align the copies to a
 | |
|              * 16-byte boundary.  The bytes to be written are the bytes
 | |
|              * i+16..(hwm-1) except that the partial buffer may reduce this.
 | |
|              */
 | |
|             dp -= size;
 | |
|             hwm ^= 0x10U; /* == i+16 mod 32 */
 | |
|             memcpy(dp, output + hwm, size);
 | |
|             size = 16U;
 | |
|             if (i == 0U) i = 32U;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       /* The loop above only exits with an exit code, so 'i' has been checked
 | |
|        * against 'hwm' before and, because of the alignment, i will always be
 | |
|        * either 16 or 32:
 | |
|        */
 | |
|       debug((i == 16U || i == 32U) & (((i & 0x10U)^0x10U) == hwm));
 | |
|       debug(sp+sp_advance == ep);
 | |
| 
 | |
|       /* At the end the bytes i..(hwm-1) need to be written, with the proviso
 | |
|        * that 'size' will be less than 16 for short rows.  If 'size' is still a
 | |
|        * short value then the range to be written is output[i..16+(size-1)],
 | |
|        * otherwise (size == 16) either this is the first write and a full 32
 | |
|        * bytes will be written (hwm == 0, i == 32) or 16 bytes need to be
 | |
|        * written.
 | |
|        */
 | |
|       if (size < 16U)
 | |
|       {
 | |
|          debug(i == 16U);
 | |
|          dp -= size;
 | |
|          memcpy(dp, output + i, size);
 | |
|       }
 | |
| 
 | |
|       else /* size == 16 */
 | |
|       {
 | |
|          debug(size == 16U);
 | |
| 
 | |
|          /* Write i..(hwm-1); 16 or 32 bytes, however if 32 bytes are written
 | |
|           * they are contiguous and i==0.
 | |
|           *
 | |
|           * hwm is 0 or 16, i is 16 or 32, swap 0 and 32:
 | |
|           */
 | |
|          if (hwm == 0U) hwm = 32U;
 | |
|          if (i == 32U) i = 0U;
 | |
|          affirm(i < hwm);
 | |
|          debug(hwm == i+16U || (i == 0U && hwm == 32U));
 | |
| 
 | |
|          hwm -= i;
 | |
|          dp -= hwm;
 | |
|          memcpy(dp, output+i, hwm);
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    debug(dp == png_upcast(png_bytep, tc->dp));
 | |
| 
 | |
| #  undef png_ptr
 | |
| }
 | |
| #endif /* READ_TRANSFORMS */
 | |
| 
 | |
| /* 16 bit byte swapping */
 | |
| static void
 | |
| png_do_bswap(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_transform_byte_op *tr =
 | |
|       png_transform_cast(png_transform_byte_op, *transform);
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    const png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
 | |
| 
 | |
|    tc->format = tr->format;
 | |
|    tc->bit_depth = tr->bit_depth;
 | |
|    tc->sp = dp;
 | |
| 
 | |
| #  ifdef _XOPEN_SOURCE
 | |
|       /* byte swapping often has incredibly fast implementations because of the
 | |
|        * importance in handling ethernet traffic.  X/Open defines swab() for
 | |
|        * this purpose and it is widely supported and normally incredibly fast:
 | |
|        */
 | |
|       debug((rowbytes & 1) == 0);
 | |
|       swab(sp, dp, rowbytes);
 | |
| #  else /* !_XOPEN_SOURCE */
 | |
|       {
 | |
|          const png_const_bytep ep = sp + rowbytes - 1;
 | |
| 
 | |
|          while (sp < ep)
 | |
|          {
 | |
|             png_byte b0 = *sp++;
 | |
|             *dp++ = *sp++;
 | |
|             *dp++ = b0;
 | |
|          }
 | |
| 
 | |
|          debug(sp == ep+1); /* even number of bytes */
 | |
|       }
 | |
| #  endif
 | |
| 
 | |
|    PNG_UNUSED(transform)
 | |
| #  undef png_ptr
 | |
| }
 | |
| 
 | |
| /* The following flags, store in tr->args, are set by the relevant PNGAPI
 | |
|  * png_set calls then resolved below.
 | |
|  */
 | |
| #define PNG_BO_STRIP_ALPHA  0x0001U /* Remove an alpha channel (read only) */
 | |
| #define PNG_BO_CHOP_16_TO_8 0x0002U /* Chop 16-bit to 8-bit channels */
 | |
| #define PNG_BO_GRAY_TO_RGB  0x0004U /* G <-> RGB; replicate channels */
 | |
| /* QUANTIZE happens here */
 | |
| #define PNG_BO_EXPAND_16    0x0008U /* Expand 8-bit channels to 16-bit */
 | |
| #define PNG_BO_BGR          0x0010U /* RGB <-> BGR */
 | |
| #define PNG_BO_FILLER       0x0020U /* Add a filler/alpha */
 | |
| #define PNG_BO_SWAP_ALPHA   0x0040U /* xA <-> Ax; alpha swap */
 | |
| #define PNG_BO_SWAP_16      0x0080U /* 16-bit channel byte swapping */
 | |
| 
 | |
| /* The following are additional flags to qualify the transforms: */
 | |
| #define PNG_BO_FILLER_ALPHA 0x4000U /* The filler is an alpha value */
 | |
| #define PNG_BO_FILLER_FIRST 0x8000U /* The filler comes first */
 | |
| 
 | |
| static void
 | |
| png_init_byte_ops(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    /* In the absence of png_set_quantize none of the above operations apply to a
 | |
|     * palette row except indirectly; they may apply if the palette was expanded,
 | |
|     * but this happens earlier in the pipeline.
 | |
|     *
 | |
|     * In the presence of png_set_quantize the rules are considerably more
 | |
|     * complex.  In libpng 1.6.0 the following operations occur before
 | |
|     * png_do_quantize:
 | |
|     *
 | |
|     *    PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, but only sometimes)
 | |
|     *    PNG_BO_STRIP_ALPHA (png_do_strip_channel; removes alpha)
 | |
|     *    encode_alpha
 | |
|     *    scale_16_to_8
 | |
|     *    PNG_BO_CHOP_16_TO_8 (png_do_chop)
 | |
|     *
 | |
|     * The following occur afterward:
 | |
|     *
 | |
|     *    PNG_BO_EXPAND_16 (png_do_expand_16)
 | |
|     *    PNG_BO_GRAY_TO_RGB (png_do_gray_to_rgb, normally)
 | |
|     *    PNG_BO_BGR (png_do_bgr)
 | |
|     *    PNG_BO_FILLER (png_do_read_filler)
 | |
|     *    PNG_BO_SWAP_ALPHA (png_do_read_swap_alpha)
 | |
|     *    PNG_BO_SWAP_16 (png_do_swap; 16-bit byte swap)
 | |
|     *
 | |
|     * The gray to RGB operation needs to occur early for GA or gray+tRNS images
 | |
|     * where the pixels are being composed on a non-gray value.  For the moment
 | |
|     * we assume that if this is necessary the following 'init' code will see RGB
 | |
|     * at this point.
 | |
|     *
 | |
|     * The quantize operation operates only if:
 | |
|     *
 | |
|     *    1) tc->bit_depth is 8
 | |
|     *    2) The color type exactly matches that required by the parameters to
 | |
|     *       png_set_quantize; it can be RGB, RGBA or palette, but
 | |
|     *       png_set_quantize (not the init routine) determines this.
 | |
|     *
 | |
|     * To avoid needing to know this here the two stage initialization is used
 | |
|     * with two transforms, one pre-quantization the other post.  In the first
 | |
|     * stage the correct row format and depth is set up.  In the second stage the
 | |
|     * pre-quantization transform looks for a post-quantization transform
 | |
|     * immediately following and, if it exists, transfers its flags to that.
 | |
|     */
 | |
|    png_structp png_ptr = tc->png_ptr;
 | |
|    png_transform_byte_op *tr =
 | |
|       png_transform_cast(png_transform_byte_op, *transform);
 | |
|    png_uint_32 args = tr->tr.args;
 | |
|    const unsigned int png_format = tc->format;
 | |
|    unsigned int format = png_format; /* memory format */
 | |
|    const unsigned int png_bit_depth = tc->bit_depth;
 | |
|    unsigned int bit_depth = png_bit_depth; /* memory bit depth */
 | |
| 
 | |
|    debug(tc->init);
 | |
| 
 | |
|    /* Channel swaps do not occur on COLORMAP format data at present because the
 | |
|     * COLORMAP is limited to 1 byte per pixel (so there is nothing to
 | |
|     * manipulate). Likewise for low bit depth gray, however the code below may
 | |
|     * widen 8-bit gray to RGB.
 | |
|     */
 | |
|    if ((png_format & PNG_FORMAT_FLAG_COLORMAP) != 0U || png_bit_depth < 8U)
 | |
|    {
 | |
|       tr->tr.fn = NULL;
 | |
|       return;
 | |
|    }
 | |
| 
 | |
|    /* This will normally happen in TC_INIT_FORMAT, but if there is a
 | |
|     * png_do_quantize operation which doesn't apply (this is unlikely) it will
 | |
|     * happen in TC_INIT_FINAL.
 | |
|     */
 | |
|    if (tr->tr.next != NULL && tr->tr.next->order == PNG_TR_CHANNEL_POSTQ)
 | |
|    {
 | |
|       debug(tr->tr.order == PNG_TR_CHANNEL_PREQ);
 | |
| 
 | |
|       /* So we can merge this transform into the next one, note that because the
 | |
|        * PNG_BO_FILLER operation is POSTQ we don't need to copy anything other
 | |
|        * than the flags.
 | |
|        */
 | |
|       debug((args & tr->tr.next->args) == 0U);
 | |
|       tr->tr.next->args |= args;
 | |
|       tr->tr.fn = NULL;
 | |
|       return;
 | |
|    }
 | |
| 
 | |
|    /* Else compact the flags for this transform - this is done in both
 | |
|     * TC_INIT_FORMAT and TC_INIT_FINAL because it is safer that way; the copy
 | |
|     * above shouldn't actually affect the results but might result in TO8 and
 | |
|     * TO16 cancelling each other because they are in separate transforms before
 | |
|     * the merge above.
 | |
|     *
 | |
|     * QUIET API CHANGE:
 | |
|     * For compatiblity with earlier versions of libpng these tests need to
 | |
|     * occur in the same order as the earlier transforms; 'TO8' combined with
 | |
|     * 'TO16' did actually do something to 16-bit data, however now it just
 | |
|     * preserves the original bit depth.
 | |
|     */
 | |
|    if ((args & PNG_BO_STRIP_ALPHA) != 0U)
 | |
|    {
 | |
|       if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U)
 | |
|          format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_ALPHA);
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_STRIP_ALPHA);
 | |
|    }
 | |
| 
 | |
|    if ((args & PNG_BO_CHOP_16_TO_8) != 0U)
 | |
|    {
 | |
|       /* This is the quiet API CHANGE; in fact it isn't necessary, but it
 | |
|        * seems likely that requesting both operations is a mistake:
 | |
|        */
 | |
|       if ((args & PNG_BO_EXPAND_16) != 0U)
 | |
|          args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8|PNG_BO_EXPAND_16);
 | |
| 
 | |
|       else if (bit_depth == 16U)
 | |
|       {
 | |
|          bit_depth = 8U;
 | |
| 
 | |
|          /* This also makes the tRNS chunk unusable: */
 | |
|          tc->invalid_info |= PNG_INFO_tRNS+PNG_INFO_hIST+PNG_INFO_pCAL;
 | |
|          /* These need further processing: PNG_INFO_sBIT, PNG_INFO_bKGD */
 | |
|       }
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_CHOP_16_TO_8);
 | |
|    }
 | |
| 
 | |
|    /* QUANTIZE happens here */
 | |
| 
 | |
|    if ((args & PNG_BO_EXPAND_16) != 0U)
 | |
|    {
 | |
|       /* This only does the 8 to 16-bit part of the expansion by multiply by
 | |
|        * 65535/255 (257) using byte replication.  The cases of low bit depth
 | |
|        * gray being expanded to 16-bit have to be handled separately.
 | |
|        */
 | |
|       if (bit_depth == 8U)
 | |
|          bit_depth = 16U;
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_EXPAND_16);
 | |
|    }
 | |
| 
 | |
|    if ((args & PNG_BO_GRAY_TO_RGB) != 0U)
 | |
|    {
 | |
|       if ((format & PNG_FORMAT_FLAG_COLOR) == 0U)
 | |
|          format |= PNG_FORMAT_FLAG_COLOR;
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_GRAY_TO_RGB);
 | |
|    }
 | |
| 
 | |
|    if ((args & PNG_BO_BGR) != 0U)
 | |
|    {
 | |
|       /* This does not happen on colormaps: */
 | |
|       if ((format & PNG_FORMAT_FLAG_COLOR) != 0U && !tc->palette)
 | |
|          format |= PNG_FORMAT_FLAG_BGR;
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_BGR);
 | |
|    }
 | |
| 
 | |
|    if ((args & PNG_BO_FILLER) != 0U)
 | |
|    {
 | |
|       if ((format & PNG_FORMAT_FLAG_ALPHA) == 0U)
 | |
|       {
 | |
|          format |= PNG_FORMAT_FLAG_ALPHA;
 | |
|          tc->channel_add = 1U;
 | |
|          /* And SWAP_ALPHA did not occur, because prior to 1.7.0 the filler op
 | |
|           * did not set ALPHA in the color type, so use SWAP_ALPHA to handle the
 | |
|           * before/after filler location.
 | |
|           *
 | |
|           * NOTE: this occurs twice, once in TC_START and once in TC_FINAL, but
 | |
|           * that is ok, the operations are idempotent.
 | |
|           *
 | |
|           * For colormaps (tc->palette set) the filler will just end up setting
 | |
|           * all the tRNS entries and PNG_BO_SWAP_ALPHA will be cancelled below.
 | |
|           */
 | |
|          if ((args & PNG_BO_FILLER_FIRST) != 0U)
 | |
|             args |= PNG_BO_SWAP_ALPHA;
 | |
| 
 | |
|          else
 | |
|             args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA);
 | |
| 
 | |
|          if (!(args & PNG_BO_FILLER_ALPHA)) /* filler is not alpha */
 | |
|             format |= PNG_FORMAT_FLAG_AFILLER;
 | |
|       }
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_FILLER);
 | |
|    }
 | |
| 
 | |
|    if ((args & PNG_BO_SWAP_ALPHA) != 0U)
 | |
|    {
 | |
|       /* This does not happen on color maps: */
 | |
|       if ((format & PNG_FORMAT_FLAG_ALPHA) != 0U && !tc->palette)
 | |
|          format |= PNG_FORMAT_FLAG_AFIRST;
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_SWAP_ALPHA);
 | |
|    }
 | |
| 
 | |
|    if ((args & PNG_BO_SWAP_16) != 0U)
 | |
|    {
 | |
|       if (bit_depth == 16U)
 | |
|          format |= PNG_FORMAT_FLAG_SWAPPED;
 | |
| 
 | |
|       else
 | |
|          args &= PNG_BIC_MASK(PNG_BO_SWAP_16);
 | |
|    }
 | |
| 
 | |
|    if (args != 0U)
 | |
|    {
 | |
|       /* At the end (TC_INIT_FINAL) work out the mapping array using the codes
 | |
|        * defined above and store the format and bit depth changes (as changes,
 | |
|        * so they will work either forward or backward).  The filler array must
 | |
|        * be set up by the png_set API.
 | |
|        */
 | |
|       if (tc->init == PNG_TC_INIT_FINAL)
 | |
|       {
 | |
|          const unsigned int png_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
 | |
| 
 | |
|          tc->format = format;
 | |
|          tc->bit_depth = bit_depth;
 | |
| 
 | |
|          {
 | |
|             const unsigned int memory_pixel_size = PNG_TC_PIXEL_DEPTH(*tc) >> 3;
 | |
|             unsigned int code_size, src_size;
 | |
|             int go_down;
 | |
|             png_byte codes[8];
 | |
| 
 | |
|             /* The codes array maps the PNG format into the memory format
 | |
|              * assuming the mapping works upwards in the address space.
 | |
|              * Initially ignore the bit depth and just work on the first four
 | |
|              * bytes.
 | |
|              */
 | |
|             codes[0] = 8U;
 | |
|             codes[1] = 9U;
 | |
|             codes[2] = 10U;
 | |
|             codes[3] = 11U;
 | |
|             codes[7] = codes[6] = codes[5] = codes[4] = 0U/*error*/;
 | |
| 
 | |
|             /* PNG_BO_STRIP_ALPHA: handled by memory_pixel_size */
 | |
|             /* PNG_BO_CHOP_16_TO_8: handled below */
 | |
|             /* PNG_BO_EXPAND_16: handled below */
 | |
| 
 | |
|             if ((args & PNG_BO_GRAY_TO_RGB) != 0U)
 | |
|             {
 | |
|                codes[3] = 9U; /* alpha, if present */
 | |
|                codes[2] = codes[1] = 8U;
 | |
| 
 | |
| #              ifdef PNG_READ_tRNS_SUPPORTED
 | |
|                   /* Gray to RGB, so copy the tRNS G value into r,g,b: */
 | |
|                   if (png_ptr->num_trans == 1U)
 | |
|                      png_ptr->trans_color.blue =
 | |
|                         png_ptr->trans_color.green =
 | |
|                         png_ptr->trans_color.red =
 | |
|                         png_ptr->trans_color.gray;
 | |
| #              endif /* READ_tRNS */
 | |
|             }
 | |
| 
 | |
|             /* 'BGR' and gray-to-RGB are mutually exclusive; with gray-to-RGB
 | |
|              * codes[0] == codes[2] == 8
 | |
|              */
 | |
|             else if ((args & PNG_BO_BGR) != 0U)
 | |
|             {
 | |
|                codes[0] = 10U;
 | |
|                codes[2] = 8U;
 | |
|             }
 | |
| 
 | |
|             if ((args & PNG_BO_FILLER) != 0U)
 | |
|             {
 | |
|                /* The filler alway goes after; for a 'before' filler the code
 | |
|                 * above turns on SWAP_ALPHA too.  The gray-to-RGB transform has
 | |
|                 * happened already, so the location of the filler channel is
 | |
|                 * given by 'format':
 | |
|                 */
 | |
|                if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
 | |
|                   codes[3] = 4U; /* low byte of filler */
 | |
| 
 | |
|                else
 | |
|                   codes[1] = 4U;
 | |
|             }
 | |
| 
 | |
|             if ((args & PNG_BO_SWAP_ALPHA) != 0U)
 | |
|             {
 | |
|                if ((format & PNG_FORMAT_FLAG_COLOR) != 0U)
 | |
|                {
 | |
|                   /* BGR may have swapped the early codes. gray-to-RGB may have
 | |
|                    * set them all to '8':
 | |
|                    */
 | |
|                   png_byte acode = codes[3];
 | |
|                   codes[3] = codes[2];
 | |
|                   codes[2] = codes[1];
 | |
|                   codes[1] = codes[0];
 | |
|                   codes[0] = acode;
 | |
|                }
 | |
| 
 | |
|                else /* GA format */
 | |
|                   codes[0] = codes[1], codes[1] = 8U;
 | |
|             }
 | |
| 
 | |
|             /* PNG_BO_SWAP_16: 16-bit only, handled below */
 | |
| 
 | |
|             /* Now the 16-bit dependent stuff. */
 | |
|             if ((args & PNG_BO_CHOP_16_TO_8) != 0U)
 | |
|             {
 | |
|                /* 16-bit input, 8-bit output, happens before FILLER so the
 | |
|                 * filler must be an 8-bit value.  Apart from a filler code (4 in
 | |
|                 * this case) the code must be adjusted from byte 'x' to byte
 | |
|                 * '2x' to select the MSB of each 16-bit channel.
 | |
|                 *
 | |
|                 * We must use PNG_FORMAT_CHANNELS here because the memory pixel
 | |
|                 * size might (in the future) include a TO16 operation.
 | |
|                 */
 | |
|                unsigned int i = PNG_FORMAT_CHANNELS(format);
 | |
| 
 | |
|                while (i > 0U)
 | |
|                {
 | |
|                   unsigned int code = codes[--i];
 | |
| 
 | |
|                   if (code > 8U) /* 8, 4 need not change */
 | |
|                      codes[i] = PNG_BYTE(8U+2U*(code-8U));
 | |
|                }
 | |
|             }
 | |
| 
 | |
|             if ((args & PNG_BO_EXPAND_16) != 0U)
 | |
|             {
 | |
|                /* Don't expect this with CHOP, but it will work, setting the low
 | |
|                 * 8-bits of each 16-bit value to the high bits.
 | |
|                 */
 | |
|                unsigned int i = PNG_FORMAT_CHANNELS(format);
 | |
| 
 | |
|                while (i > 0U)
 | |
|                {
 | |
|                   png_byte code = codes[--i];
 | |
| 
 | |
|                   /* BSWAP is after FILLER, however the data passed in is a
 | |
|                    * machine native png_uint_16.  We don't know until this init
 | |
|                    * routine whether the data is an 8 or 16-bit value because we
 | |
|                    * don't know the full set of transforms the app will apply
 | |
|                    * when the png_set_filler API is called.
 | |
|                    *
 | |
|                    * This means that the data in tr->filler[] needs to have the
 | |
|                    * low bits in a known place, so the code here puts the low 8
 | |
|                    * bits in filler[0], code 4.  Hence the following:
 | |
|                    */
 | |
|                   if (code == 4U)
 | |
|                      codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U;
 | |
| 
 | |
|                   else
 | |
|                      codes[2U*i] = codes[2U*i+1U] = code;
 | |
|                }
 | |
| 
 | |
| #              ifdef PNG_READ_tRNS_SUPPORTED
 | |
|                   /* We're just duplicating bytes, so the tRNS chunk can be
 | |
|                    * maintained if present.  If the tRNS is for a colormap this
 | |
|                    * produces garbage in trans_color, but it isn't used.
 | |
|                    */
 | |
|                   if (png_ptr->num_trans == 1U)
 | |
|                   {
 | |
| #                    define TO16(x) x = PNG_UINT_16((x & 0xFFU) * 0x101U)
 | |
|                      TO16(png_ptr->trans_color.gray);
 | |
|                      TO16(png_ptr->trans_color.red);
 | |
|                      TO16(png_ptr->trans_color.green);
 | |
|                      TO16(png_ptr->trans_color.blue);
 | |
| #                    undef TO16
 | |
|                   }
 | |
| #              endif /* READ_tRNS */
 | |
|             }
 | |
| 
 | |
|             else if (bit_depth == 16U)
 | |
|             {
 | |
|                /* 16-bit input and output. */
 | |
|                unsigned int i = PNG_FORMAT_CHANNELS(format);
 | |
| 
 | |
|                while (i > 0U)
 | |
|                {
 | |
|                   unsigned int code = codes[--i];
 | |
| 
 | |
|                   if (code == 4U) /* as above */
 | |
|                      codes[2U*i/*MSB*/] = 5U, codes[2U*i+1U/*LSB*/] = 4U;
 | |
| 
 | |
|                   else
 | |
|                   {
 | |
|                      codes[2U*i] = PNG_BYTE(8U+2U*(code-8U));
 | |
|                      codes[2U*i+1U] = PNG_BYTE(8U+2U*(code-8U)+1U);
 | |
|                   }
 | |
|                }
 | |
|             }
 | |
| 
 | |
|             if ((args & PNG_BO_SWAP_16) != 0U)
 | |
|             {
 | |
|                /* bswap the memory bytes. */
 | |
|                unsigned int i;
 | |
|                png_byte bswap_codes[sizeof codes];
 | |
| 
 | |
|                debug((memory_pixel_size & 1U) == 0U);
 | |
| 
 | |
|                for (i=0U; i<sizeof codes; ++i)
 | |
|                   bswap_codes[i] = codes[i ^ 1U];
 | |
| 
 | |
|                memcpy(codes, bswap_codes, sizeof codes);
 | |
|             }
 | |
| 
 | |
| #           ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
 | |
|                /* Handle the 'write' case; the codes[] array must be inverted,
 | |
|                 * it lists the PNG pixel for each memory pixel, we need it to
 | |
|                 * list the memory pixel for each PNG pixel.
 | |
|                 */
 | |
|                if (!png_ptr->read_struct)
 | |
|                {
 | |
|                   /* There are no write transforms that add data to the PNG
 | |
|                    * file; the 'filler' transform removes a channel, but that is
 | |
|                    * the limit of the changes.
 | |
|                    */
 | |
|                   unsigned int i = 0U;
 | |
|                   png_byte write_codes[8U];
 | |
| 
 | |
|                   memset(write_codes, 0, sizeof write_codes);
 | |
| 
 | |
|                   while (i<memory_pixel_size)
 | |
|                   {
 | |
|                      unsigned int code = codes[i];
 | |
| 
 | |
|                      if (code >= 8U) /* 8+index of PNG byte */
 | |
|                         write_codes[code-8U] = PNG_BYTE(8U+i);
 | |
|                      /* else this is a filler byte to be removed */
 | |
|                      else
 | |
|                         debug(code == 4U || code == 5U);
 | |
| 
 | |
|                      ++i;
 | |
|                   }
 | |
| 
 | |
|                   code_size = png_pixel_size;
 | |
|                   src_size = memory_pixel_size;
 | |
|                   tr->format = png_format;
 | |
|                   tr->bit_depth = png_bit_depth;
 | |
| 
 | |
|                   /* The PNG size should always be <= to the memory size, the
 | |
|                    * source pointer will be the memory, the destination the PNG
 | |
|                    * format, so it should always be possible to do the upwards
 | |
|                    * copy.
 | |
|                    */
 | |
|                   go_down = png_pixel_size > memory_pixel_size;
 | |
|                   affirm(!go_down);
 | |
|                   memcpy(codes, write_codes, sizeof codes);
 | |
|                }
 | |
| 
 | |
|                else
 | |
| #           endif /* WRITE_TRANSFORMS */
 | |
|             {
 | |
|                code_size = memory_pixel_size;
 | |
|                src_size = png_pixel_size;
 | |
|                tr->format = format;
 | |
|                tr->bit_depth = bit_depth;
 | |
|                go_down = png_pixel_size < memory_pixel_size;
 | |
|             }
 | |
| 
 | |
|             /* Record this for debugging: */
 | |
|             tr->tr.args = args;
 | |
| 
 | |
|             /* For the same-pixel-size case check for a bswap; this is available
 | |
|              * in heavily optimized forms and is a common operation (50% of the
 | |
|              * time) with 16-bit PNG data, particularly given the handling in
 | |
|              * the simplified API.
 | |
|              */
 | |
|             if (!go_down)
 | |
|             {
 | |
|                if (memory_pixel_size == png_pixel_size)
 | |
|                {
 | |
|                   int the_same = 1;
 | |
|                   int swapped = (memory_pixel_size & 1) == 0; /* even count */
 | |
|                   unsigned int i;
 | |
| 
 | |
|                   for (i=0U; i<memory_pixel_size; ++i)
 | |
|                   {
 | |
|                      if (codes[i] != 8U+i)
 | |
|                      {
 | |
|                         the_same = 0;
 | |
|                         if (codes[i] != 8U+(i^1U))
 | |
|                            swapped = 0;
 | |
|                         if (!swapped)
 | |
|                            break;
 | |
|                      }
 | |
| 
 | |
|                      else /* byte is copied, so it can't be swapped! */
 | |
|                      {
 | |
|                         swapped = 0;
 | |
|                         if (!the_same)
 | |
|                            break;
 | |
|                      }
 | |
|                   }
 | |
| 
 | |
|                   /* Use the 'bswap' routine if possible. */
 | |
|                   if (swapped)
 | |
|                   {
 | |
|                      tr->tr.fn = png_do_bswap;
 | |
|                      return;
 | |
|                   }
 | |
| 
 | |
|                   else if (the_same)
 | |
|                      impossible("not reached");
 | |
|                }
 | |
| 
 | |
|                tr->tr.fn = png_do_byte_ops_up;
 | |
| 
 | |
|                /* Construct the code, forwards: */
 | |
|                {
 | |
|                   unsigned int i = code_size;
 | |
|                   png_uint_32 code = 0U;
 | |
| 
 | |
|                   while (i > 0U)
 | |
|                   {
 | |
|                      unsigned int next = codes[--i];
 | |
| 
 | |
|                      code <<= 4U;
 | |
| 
 | |
|                      if ((next >= 8U && next < 8U+src_size) ||
 | |
|                            next == 4U || next == 5U)
 | |
|                         code += next;
 | |
| 
 | |
|                      else
 | |
|                         impossible("invalid code (up)");
 | |
|                   }
 | |
| 
 | |
|                   tr->codes = code;
 | |
|                }
 | |
|             }
 | |
| 
 | |
|             else /* go_down */
 | |
| #           ifdef PNG_READ_TRANSFORMS_SUPPORTED
 | |
|                {
 | |
|                   tr->tr.fn = png_do_byte_ops_down;
 | |
| 
 | |
|                   /* Construct the code, backwards: */
 | |
|                   {
 | |
|                      unsigned int i = 0U;
 | |
|                      png_uint_32 code = 0U;
 | |
| 
 | |
|                      while (i < code_size)
 | |
|                      {
 | |
|                         unsigned int next = codes[i++];
 | |
| 
 | |
|                         code <<= 4;
 | |
| 
 | |
|                         if ((next >= 8U && next < 8U+src_size) ||
 | |
|                               next == 4U || next == 5U)
 | |
|                            code += next;
 | |
| 
 | |
|                         else
 | |
|                            impossible("invalid code (down)");
 | |
|                      }
 | |
| 
 | |
|                      tr->codes = code;
 | |
|                   }
 | |
|                }
 | |
| #           else /* !READ_TRANSFORMS */
 | |
|                impossible("not reached"); /* because of the affirm above */
 | |
| #           endif /* !READ_TRANSFORMS */
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       else /* TC_INIT_FORMAT: just store modified 'args' */
 | |
|       {
 | |
|          tc->format = format;
 | |
|          tc->bit_depth = bit_depth;
 | |
|          tr->tr.args = args;
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    else /* the transform is not applicable */
 | |
|       tr->tr.fn = NULL;
 | |
| }
 | |
| #endif /* SWAP poo */
 | |
| 
 | |
| #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED
 | |
| static void
 | |
| png_init_rgb_to_gray_byte_ops(png_transformp *transform,
 | |
|    png_transform_controlp tc)
 | |
| {
 | |
|    /* This just delay initializes the function; all the transform initialization
 | |
|     * has been done below.
 | |
|     */
 | |
|    (*transform)->fn = png_do_byte_ops_up;
 | |
| 
 | |
|    /*  If this happens on a row do the transform immediately: */
 | |
|    if (!tc->init)
 | |
|       png_do_byte_ops_up(transform, tc);
 | |
| 
 | |
|    else
 | |
|    {
 | |
|       /* This doing the init - update the row information here */
 | |
| #     define png_ptr (tc->png_ptr)
 | |
|       png_transform_byte_op *tr =
 | |
|          png_transform_cast(png_transform_byte_op, *transform);
 | |
| 
 | |
|       debug(tc->bit_depth == 8U || tc->bit_depth == 16U);
 | |
|       debug((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0U &&
 | |
|             (tc->format & PNG_FORMAT_FLAG_COLOR) != 0U);
 | |
| 
 | |
|       tc->format = tr->format;
 | |
|       tc->bit_depth = tr->bit_depth;
 | |
| #     undef png_ptr
 | |
|    }
 | |
| }
 | |
| 
 | |
| void /* PRIVATE */
 | |
| png_add_rgb_to_gray_byte_ops(png_structrp png_ptr, png_transform_controlp tc,
 | |
|    unsigned int index, unsigned int order)
 | |
|    /* Add a byte_ops transform to convert RGB or RGBA data to 'gray' by
 | |
|     * selecting just the given change [index]  The transform added is added at
 | |
|     * 'order'.
 | |
|     */
 | |
| {
 | |
|    png_transform_byte_op *tr = png_transform_cast(png_transform_byte_op,
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|          png_init_rgb_to_gray_byte_ops, order));
 | |
| 
 | |
|    affirm((tc->format & (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_COLORMAP)) ==
 | |
|             PNG_FORMAT_FLAG_COLOR &&
 | |
|           index <= 2 && tc->init == PNG_TC_INIT_FINAL);
 | |
| 
 | |
|    tr->format = tc->format & PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR);
 | |
|    tr->bit_depth = tc->bit_depth;
 | |
| 
 | |
|    /* For 1 byte channel [index] plus, maybe, alpha: */
 | |
|    if (tc->bit_depth == 8)
 | |
|       tr->codes = 8U + index +
 | |
|          ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ? (8U+3U) << 4 : 0U);
 | |
| 
 | |
|    else
 | |
|    {
 | |
|       affirm(tc->bit_depth == 16);
 | |
| 
 | |
|       /* As above, but two bytes; [2*index] and [2*index+1] */
 | |
|       index *= 2U;
 | |
|       tr->codes = (8U + index) + ((9U + index) << 4) +
 | |
|          ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0 ?
 | |
|             ((8U+6U) + ((9U+6U) << 4)) << 8 : 0U);
 | |
|    }
 | |
| }
 | |
| #endif /* READ_RGB_TO_GRAY */
 | |
| 
 | |
| #if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) &&\
 | |
|     defined(PNG_READ_BACKGROUND_SUPPORTED)
 | |
| void /* PRIVATE */
 | |
| png_push_gray_to_rgb_byte_ops(png_transformp *transform,
 | |
|    png_transform_controlp tc)
 | |
|    /* This is an init-time utility to add appropriate byte ops to expand a
 | |
|     * grayscale PNG data set to RGB.
 | |
|     */
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_transformp tr = png_push_transform(png_ptr,
 | |
|       sizeof (png_transform_byte_op), png_init_byte_ops, transform, NULL);
 | |
| 
 | |
|    tr->args = PNG_BO_GRAY_TO_RGB;
 | |
|    debug(tr == *transform);
 | |
|    png_init_byte_ops(transform, tc);
 | |
| #  undef png_ptr
 | |
| }
 | |
| #endif /* GRAY_TO_RGB && READ_BACKGROUND */
 | |
| 
 | |
| #ifdef PNG_READ_STRIP_ALPHA_SUPPORTED
 | |
| void /* PRIVATE */
 | |
| png_add_strip_alpha_byte_ops(png_structrp png_ptr)
 | |
| {
 | |
|    png_add_transform(png_ptr, sizeof (png_transform_byte_op), png_init_byte_ops,
 | |
|       PNG_TR_CHANNEL_PREQ)->args |= PNG_BO_STRIP_ALPHA;
 | |
| }
 | |
| #endif /* READ_STRIP_ALPHA */
 | |
| 
 | |
| #ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED
 | |
| /* Chop 16-bit depth files to 8-bit depth */
 | |
| void PNGAPI
 | |
| png_set_strip_16(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|          png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |=
 | |
|          PNG_BO_CHOP_16_TO_8;
 | |
| }
 | |
| #endif /* READ_STRIP_16_TO_8 */
 | |
| 
 | |
| #ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED
 | |
| void PNGAPI
 | |
| png_set_gray_to_rgb(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       png_set_expand_gray_1_2_4_to_8(png_ptr);
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|          png_init_byte_ops, PNG_TR_CHANNEL_PREQ)->args |=
 | |
|          PNG_BO_GRAY_TO_RGB;
 | |
|    }
 | |
| }
 | |
| #endif /* READ_GRAY_TO_RGB */
 | |
| 
 | |
| /* QUANTIZE */
 | |
| 
 | |
| #ifdef PNG_READ_EXPAND_16_SUPPORTED
 | |
| /* Expand to 16-bit channels.  PNG_BO_EXPAND_16 also expands the tRNS chunk if
 | |
|  * it is present, but it requires low bit depth grayscale expanded first.  This
 | |
|  * must also force palette to RGB.
 | |
|  */
 | |
| void PNGAPI
 | |
| png_set_expand_16(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       png_set_expand_gray_1_2_4_to_8(png_ptr);
 | |
|       png_set_palette_to_rgb(png_ptr);
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|          png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
 | |
|          PNG_BO_EXPAND_16;
 | |
|    }
 | |
| }
 | |
| #endif /* READ_EXPAND_16 */
 | |
| 
 | |
| #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
 | |
| void PNGAPI
 | |
| png_set_bgr(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
| #     ifndef PNG_READ_BGR_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_bgr not supported on read");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| #     ifndef PNG_WRITE_BGR_SUPPORTED
 | |
|          if (!png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_bgr not supported on write");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| 
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|          png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
 | |
|          PNG_BO_BGR;
 | |
|    }
 | |
| }
 | |
| #endif /* BGR */
 | |
| 
 | |
| #if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
 | |
| /* This includes png_set_filler and png_set_add_alpha.  The only difference
 | |
|  * between the two is that the latter resulted in PNG_COLOR_MASK_ALPHA being
 | |
|  * added to the info_ptr color type, if png_read_update_info was called whereas
 | |
|  * the former did not.
 | |
|  *
 | |
|  * Regardless of whether the added channel resulted in the change to the
 | |
|  * png_info color type, the SWAP_ALPHA transform was not performed, even though
 | |
|  * it apparently occured after the add, because PNG_COLOR_MASK_ALPHA was never
 | |
|  * set in the 1.6 'row_info'.
 | |
|  *
 | |
|  * Consequently 'SWAP_ALPHA' and 'FILLER' were independent; one or the other
 | |
|  * would occur depending on the color type (not the number of channels) prior to
 | |
|  * the two transforms.
 | |
|  *
 | |
|  * Prior to 1.7.0 the app could obtain information about the memory format by
 | |
|  * calling png_read_update_info followed by png_get_color_type and
 | |
|  * png_get_channels.  The first would return PNG_COLOR_TYPE..._ALPHA if
 | |
|  * png_set_add_alpha was performed and the base type if png_set_filler was
 | |
|  * performed, however in both cases png_get_channels would return the extra
 | |
|  * channel; 2 or 4.
 | |
|  *
 | |
|  * The app could also insert a user transform callback and view the color type
 | |
|  * in the old "row_info" structure, however this resulted in an inconsistent
 | |
|  * color type because png_set_alpha did not add COLOR_MASK_ALPHA to the color
 | |
|  * type.
 | |
|  *
 | |
|  * Hence API CHANGE: 1.7.0, row transform callbacks now see the same color type
 | |
|  * as reported by png_get_color_type after png_read_update_info.
 | |
|  */
 | |
| /* Add a filler byte on read, or remove a filler or alpha byte on write.
 | |
|  * The filler type has changed in v0.95 to allow future 2-byte fillers
 | |
|  * for 48-bit input data, as well as to avoid problems with some compilers
 | |
|  * that don't like bytes as parameters.
 | |
|  */
 | |
| static void
 | |
| set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc, int alpha)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       if (filler_loc != PNG_FILLER_BEFORE && filler_loc != PNG_FILLER_AFTER)
 | |
|       {
 | |
|          png_app_error(png_ptr, "png_set_filler: invalid filler location");
 | |
|          return;
 | |
|       }
 | |
| 
 | |
| #     ifndef PNG_READ_SWAP_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_filler not supported on read");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| #     ifndef PNG_WRITE_SWAP_SUPPORTED
 | |
|          if (!png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_filler not supported on write");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| 
 | |
|       {
 | |
|          png_transform_byte_op *tr =
 | |
|             png_transform_cast(png_transform_byte_op,
 | |
|                png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|                   png_init_byte_ops, PNG_TR_CHANNEL_POSTQ));
 | |
|          png_uint_32 args = PNG_BO_FILLER;
 | |
| 
 | |
|          if (filler_loc == PNG_FILLER_BEFORE)
 | |
|             args |= PNG_BO_FILLER_FIRST;
 | |
| 
 | |
|          if (alpha)
 | |
|             args |= PNG_BO_FILLER_ALPHA;
 | |
| 
 | |
|          tr->tr.args |= args;
 | |
| 
 | |
|          /* The filler must be stored LSByte first: */
 | |
|          tr->filler[0] = PNG_BYTE(filler >>  0);
 | |
|          tr->filler[1] = PNG_BYTE(filler >>  8);
 | |
|          tr->filler[2] = PNG_BYTE(filler >> 16);
 | |
|          tr->filler[3] = PNG_BYTE(filler >> 24);
 | |
|       }
 | |
|    }
 | |
| }
 | |
| 
 | |
| void PNGAPI
 | |
| png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
 | |
| {
 | |
|    set_filler(png_ptr, filler, filler_loc, 0/*!alpha*/);
 | |
| }
 | |
| 
 | |
| /* Added to libpng-1.2.7 */
 | |
| void PNGAPI
 | |
| png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc)
 | |
| {
 | |
|    set_filler(png_ptr, filler, filler_loc, 1/*alpha*/);
 | |
| }
 | |
| #endif /* FILLER */
 | |
| 
 | |
| #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
 | |
|     defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
 | |
| void PNGAPI
 | |
| png_set_swap_alpha(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
| #     ifndef PNG_READ_SWAP_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_swap_alpha not supported on read");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| #     ifndef PNG_WRITE_SWAP_SUPPORTED
 | |
|          if (!png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_swap_alpha not supported on write");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| 
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|          png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
 | |
|          PNG_BO_SWAP_ALPHA;
 | |
|    }
 | |
| }
 | |
| #endif /* SWAP_ALPHA */
 | |
| 
 | |
| #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
 | |
| void PNGAPI
 | |
| png_set_swap(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
| #     ifndef PNG_READ_SWAP_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_swap not supported on read");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| #     ifndef PNG_WRITE_SWAP_SUPPORTED
 | |
|          if (!png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_swap not supported on write");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| 
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_byte_op),
 | |
|          png_init_byte_ops, PNG_TR_CHANNEL_POSTQ)->args |=
 | |
|          PNG_BO_SWAP_16;
 | |
|    }
 | |
| }
 | |
| #endif /* SWAP */
 | |
| 
 | |
| #if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_PACKSWAP_SUPPORTED) ||\
 | |
|     defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
 | |
| static png_alloc_size_t
 | |
| row_align(png_transform_controlp tc)
 | |
|    /* Utiltity to align the source row (sp) in a transform control; it does this
 | |
|     * by simply copying it to dp if it is not already aligned.  As a convenience
 | |
|     * the utility returns the number of bytes in the row.
 | |
|     */
 | |
| {
 | |
|    png_const_structp png_ptr = tc->png_ptr;
 | |
|    png_const_voidp sp = tc->sp;
 | |
|    png_voidp dp = tc->dp;
 | |
|    png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
 | |
| 
 | |
|    /* For alignment; if png_alignof is not supported by the compiler this will
 | |
|     * always do an initial memcpy if the source and destination are not the
 | |
|     * same.  We can only get here for write; the read case always uses locally
 | |
|     * allocated buffers, only write reads from the application data directly.
 | |
|     */
 | |
| #  ifdef png_alignof
 | |
|       debug(png_isaligned(dp, png_uint_32));
 | |
| #  endif
 | |
|    if (sp != dp && !png_ptr->read_struct && !png_isaligned(sp, png_uint_32))
 | |
|    {
 | |
|       UNTESTED
 | |
|       memcpy(dp, sp, rowbytes);
 | |
|       tc->sp = dp;
 | |
|    }
 | |
| 
 | |
|    return rowbytes;
 | |
| }
 | |
| #endif /* Stuff that needs row_align */
 | |
| 
 | |
| #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
 | |
| /* Bit-ops; invert bytes.  This works for mono inverts too because even the low
 | |
|  * bit depths can be handled as bytes (since there can be no intervening
 | |
|  * channels).
 | |
|  */
 | |
| #define PNG_B_INVERT_MONO  1U
 | |
| #define PNG_B_INVERT_RGB   2U /* not set, used below */
 | |
| #define PNG_B_INVERT_ALPHA 4U
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
|    png_transform tr;
 | |
|    unsigned int  step0; /* initial advance on sp and dp */
 | |
|    unsigned int  step;  /* advance after start */
 | |
|    png_uint_32   mask;  /* XOR mask */
 | |
| }  png_transform_bit_op;
 | |
| 
 | |
| static void
 | |
| png_do_invert_all(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    const png_const_structp png_ptr = tc->png_ptr;
 | |
|    /* Invert the whole row, quickly */
 | |
|    const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc);
 | |
|    png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
 | |
|    png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    if (png_ptr->read_struct)
 | |
|    {
 | |
|       tc->format |= PNG_FORMAT_FLAG_RANGE;
 | |
|       tc->range++;
 | |
|    }
 | |
| 
 | |
|    else if (--(tc->range) == 0)
 | |
|       tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
 | |
| 
 | |
|    while (png_upcast(void*,dp) < dp_end)
 | |
|       *dp++ = ~*sp++;
 | |
| 
 | |
|    PNG_UNUSED(transform);
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_invert_channel(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    const png_const_structp png_ptr = tc->png_ptr;
 | |
|    /* Invert just one channel in the row. */
 | |
|    const png_transform_bit_op * const tr =
 | |
|       png_transform_cast(png_transform_bit_op, *transform);
 | |
|    const png_uint_32 mask = tr->mask;
 | |
|    const unsigned int step = tr->step;
 | |
|    const unsigned int step0 = tr->step0;
 | |
|    const png_const_voidp dp_end = png_upcast(png_bytep, tc->dp) + row_align(tc);
 | |
|    png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
 | |
|    png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    if (png_ptr->read_struct)
 | |
|    {
 | |
|       tc->format |= PNG_FORMAT_FLAG_RANGE;
 | |
|       tc->range++;
 | |
|    }
 | |
| 
 | |
|    else if (--(tc->range) == 0)
 | |
|       tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
 | |
| 
 | |
|    if (sp == dp || step == 1)
 | |
|    {
 | |
|       sp += step0;
 | |
|       dp += step0;
 | |
| 
 | |
|       while (png_upcast(void*,dp) < dp_end)
 | |
|          *dp = *sp ^ mask, dp += step, sp += step;
 | |
|    }
 | |
| 
 | |
|    else /* step == 2, copy required */
 | |
|    {
 | |
|       if (step0) /* must be 1 */
 | |
|          *dp++ = *sp++;
 | |
| 
 | |
|       while (png_upcast(void*,dp) < dp_end)
 | |
|       {
 | |
|          *dp++ = *sp++ ^ mask;
 | |
|          if (!(png_upcast(void*,dp) < dp_end))
 | |
|             break;
 | |
|          *dp++ = *sp++;
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    PNG_UNUSED(transform);
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_init_invert(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_transform_bit_op *tr =
 | |
|       png_transform_cast(png_transform_bit_op, *transform);
 | |
|    png_uint_32 invert = tr->tr.args;
 | |
|    png_uint_32 present; /* channels present */
 | |
| 
 | |
|    if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) != 0)
 | |
|       present = 0;
 | |
| 
 | |
|    else /* not color mapped */
 | |
|    {
 | |
|       if ((tc->format & PNG_FORMAT_FLAG_COLOR) != 0)
 | |
|          present = PNG_B_INVERT_RGB;
 | |
|       else
 | |
|          present = PNG_B_INVERT_MONO;
 | |
| 
 | |
|       if ((tc->format & PNG_FORMAT_FLAG_ALPHA) != 0)
 | |
|          present |= PNG_B_INVERT_ALPHA;
 | |
|    }
 | |
| 
 | |
|    /* Cannot invert things that aren't there: */
 | |
|    invert &= present;
 | |
| 
 | |
|    /* If nothing can be inverted is present the transform is not applicable: */
 | |
|    if (invert == 0)
 | |
|       (*transform)->fn = NULL;
 | |
| 
 | |
|    else
 | |
|    {
 | |
|       tc->format |= PNG_FORMAT_FLAG_RANGE;
 | |
|       tc->range++;
 | |
| 
 | |
|       if (tc->init == PNG_TC_INIT_FINAL)
 | |
|       {
 | |
|          /* If everything that is present is to be inverted just invert the
 | |
|           * whole row:
 | |
|           */
 | |
|          if (invert == present)
 | |
|             (*transform)->fn = png_do_invert_all;
 | |
| 
 | |
|          else
 | |
|          {
 | |
|             /* One thing is to be inverted, G or A: */
 | |
|             unsigned int channels = PNG_TC_CHANNELS(*tc);
 | |
|             unsigned int channel =
 | |
|                (tc->format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels-1;
 | |
| 
 | |
|             affirm(channels == 2 || channels == 4);
 | |
| 
 | |
|             if (invert != PNG_B_INVERT_ALPHA)
 | |
|             {
 | |
|                debug(invert == PNG_B_INVERT_MONO && channels == 2 &&
 | |
|                      present == PNG_B_INVERT_MONO+PNG_B_INVERT_ALPHA);
 | |
|                channel = (channels-1) - channel;
 | |
|             }
 | |
| 
 | |
|             affirm(tc->bit_depth == 8 || tc->bit_depth == 16);
 | |
| 
 | |
|             /* So channels[channel] is to be inverted, make a mask: */
 | |
|             {
 | |
|                union
 | |
|                {
 | |
|                   png_byte bytes[8];
 | |
|                   png_uint_32 words[2];
 | |
|                } masks;
 | |
| 
 | |
|                memset(&masks, 0, sizeof masks);
 | |
| 
 | |
|                if (tc->bit_depth == 8)
 | |
|                {
 | |
|                   /* channels is 2 or 4, channel < 4. */
 | |
|                   masks.bytes[channel+channels] = masks.bytes[channel] = 0xff;
 | |
|                   tr->step = 1;
 | |
|                   tr->mask = masks.words[0];
 | |
|                   tr->step0 = 0;
 | |
|                }
 | |
| 
 | |
|                else /* tc->bit_depth == 16 */
 | |
|                {
 | |
|                   channel <<= 1; /* in bytes */
 | |
|                   masks.bytes[channel+1] = masks.bytes[channel] = 0xff;
 | |
| 
 | |
|                   if (channels == 2)
 | |
|                   {
 | |
|                      tr->step = 1;
 | |
|                      tr->mask = masks.words[0];
 | |
|                      tr->step0 = 0;
 | |
|                   }
 | |
| 
 | |
|                   else /* channels == 4 */
 | |
|                   {
 | |
|                      tr->step = 2;
 | |
| 
 | |
|                      if (masks.words[0] == 0)
 | |
|                      {
 | |
|                         tr->mask = masks.words[1];
 | |
|                         tr->step0 = 1;
 | |
|                      }
 | |
| 
 | |
|                      else
 | |
|                      {
 | |
|                         tr->mask = masks.words[0];
 | |
|                         tr->step0 = 0;
 | |
|                      }
 | |
|                   }
 | |
|                }
 | |
|             }
 | |
| 
 | |
|             (*transform)->fn = png_do_invert_channel;
 | |
|          }
 | |
|       }
 | |
|    }
 | |
| #  undef png_ptr
 | |
| }
 | |
| 
 | |
| #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
 | |
| void PNGAPI
 | |
| png_set_invert_alpha(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_bit_op),
 | |
|          png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_ALPHA;
 | |
| #     ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED
 | |
|          /* This is necessary to avoid palette processing on write; the only
 | |
|           * transform that applies to colormapped images is the tRNS chunk
 | |
|           * invert.
 | |
|           */
 | |
|          png_ptr->write_invert_alpha = 1U;
 | |
| #     endif
 | |
|    }
 | |
| }
 | |
| #endif /* INVERT_ALPHA */
 | |
| 
 | |
| #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
 | |
| void PNGAPI
 | |
| png_set_invert_mono(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|       png_add_transform(png_ptr, sizeof (png_transform_bit_op),
 | |
|          png_init_invert, PNG_TR_INVERT)->args |= PNG_B_INVERT_MONO;
 | |
| }
 | |
| #endif /* INVERT */
 | |
| #endif /* INVERT_ALPHA || INVERT */
 | |
| 
 | |
| /*
 | |
|  *   WARNING
 | |
|  *     WARNING
 | |
|  *       WARNING
 | |
|  *         WARNING
 | |
|  *           WARNING  The transforms below are temporary; they can and will be
 | |
|  *         WARNING    heavily optimized before release.
 | |
|  *       WARNING
 | |
|  *     WARNING
 | |
|  *   WARNING
 | |
|  */
 | |
| #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
 | |
| typedef struct
 | |
| {
 | |
|    png_transform  tr;
 | |
|    png_color_8    true_bits;
 | |
| }  png_transform_shift;
 | |
| 
 | |
| /* Shift pixel values to take advantage of whole range.  Pass the
 | |
|  * true number of bits in bit_depth.  The row should be packed
 | |
|  * according to tc->bit_depth.  Thus, if you had a row of
 | |
|  * bit depth 4, but the pixels only had values from 0 to 7, you
 | |
|  * would pass 3 as bit_depth, and this routine would translate the
 | |
|  * data to 0 to 15.
 | |
|  *
 | |
|  * NOTE: this is horrible complexity for no value.  Once people suggested they
 | |
|  * were selling 16-bit displays with 5:6:5 bits spread R:G:B but so far as I
 | |
|  * could determine these displays produced intermediate grey (uncolored) colors,
 | |
|  * which is impossible with a true 5:6:5, so most likely 5:6:5 was marketing.
 | |
|  */
 | |
| static unsigned int
 | |
| set_shifts(unsigned int format, unsigned int bit_depth,
 | |
|    png_const_color_8p true_bits, int *shift_start, int *shift_dec)
 | |
| {
 | |
|    unsigned int channels = 0;
 | |
| 
 | |
|    if ((format & (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST)) ==
 | |
|        (PNG_FORMAT_FLAG_ALPHA+PNG_FORMAT_FLAG_AFIRST))
 | |
|       ++channels; /* filled in below */
 | |
| 
 | |
|    if ((format & PNG_FORMAT_FLAG_COLOR) != 0)
 | |
|    {
 | |
|       unsigned int offset = /* 0 or 2 as appropriate for red */
 | |
|          ((format & PNG_FORMAT_FLAG_BGR) != 0) << 1;
 | |
| 
 | |
|       shift_start[channels+offset] = bit_depth - true_bits->red;
 | |
|       if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->red;
 | |
| 
 | |
|       shift_start[channels+1] = bit_depth - true_bits->green;
 | |
|       if (shift_dec != NULL) shift_dec[channels+1] = true_bits->green;
 | |
| 
 | |
|       offset ^= 2; /* for blue */
 | |
|       shift_start[channels+offset] = bit_depth - true_bits->blue;
 | |
|       if (shift_dec != NULL) shift_dec[channels+offset] = true_bits->blue;
 | |
| 
 | |
|       channels += 3;
 | |
|    }
 | |
| 
 | |
|    else /* no color: gray */
 | |
|    {
 | |
|       shift_start[channels] = bit_depth - true_bits->gray;
 | |
|       if (shift_dec != NULL) shift_dec[channels] = true_bits->gray;
 | |
|       ++channels;
 | |
|    }
 | |
| 
 | |
|    if ((format & PNG_FORMAT_FLAG_ALPHA) != 0)
 | |
|    {
 | |
|       const unsigned int offset =
 | |
|          (format & PNG_FORMAT_FLAG_AFIRST) != 0 ? 0 : channels++;
 | |
| 
 | |
|       shift_start[offset] = bit_depth - true_bits->alpha;
 | |
|       if (shift_dec != NULL) shift_dec[offset] = true_bits->alpha;
 | |
|    }
 | |
| 
 | |
|    return channels;
 | |
| }
 | |
| 
 | |
| #ifdef PNG_WRITE_SHIFT_SUPPORTED
 | |
| static void
 | |
| png_do_shift(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_transform_shift *tr =
 | |
|       png_transform_cast(png_transform_shift, *transform);
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc);
 | |
| 
 | |
|    png_debug(1, "in png_do_shift");
 | |
| 
 | |
|    if (--(tc->range) == 0)
 | |
|       tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
 | |
| 
 | |
|    tc->sp = dp;
 | |
| 
 | |
|    {
 | |
|       int shift_start[4], shift_dec[4];
 | |
|       unsigned int channels = set_shifts(tc->format, tc->bit_depth,
 | |
|          &tr->true_bits, shift_start, shift_dec);
 | |
| 
 | |
|       debug(PNG_TC_CHANNELS(*tc) == channels);
 | |
| 
 | |
|       /* With low res depths, could only be grayscale, so one channel */
 | |
|       if (tc->bit_depth < 8)
 | |
|       {
 | |
|          unsigned int mask;
 | |
| 
 | |
|          UNTESTED
 | |
|          affirm(channels == 1);
 | |
|          /* This doesn't matter but we expect to run before packswap: */
 | |
|          debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
 | |
| 
 | |
|          if (tr->true_bits.gray == 1 && tc->bit_depth == 2)
 | |
|             mask = 0x55;
 | |
| 
 | |
|          else if (tc->bit_depth == 4 && tr->true_bits.gray == 3)
 | |
|             mask = 0x11;
 | |
| 
 | |
|          else
 | |
|             mask = 0xff;
 | |
| 
 | |
|          while (dp < dp_end)
 | |
|          {
 | |
|             int j;
 | |
|             unsigned int v, out;
 | |
| 
 | |
|             v = *sp++;
 | |
|             out = 0;
 | |
| 
 | |
|             for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
 | |
|             {
 | |
|                if (j > 0)
 | |
|                   out |= v << j;
 | |
| 
 | |
|                else
 | |
|                   out |= (v >> (-j)) & mask;
 | |
|             }
 | |
| 
 | |
|             *dp++ = png_check_byte(png_ptr, out);
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       else if (tc->bit_depth == 8)
 | |
|       {
 | |
|          unsigned int c = 0;
 | |
| 
 | |
|          UNTESTED
 | |
|          while (dp < dp_end)
 | |
|          {
 | |
| 
 | |
|             int j;
 | |
|             unsigned int v, out;
 | |
| 
 | |
|             v = *sp++;
 | |
|             out = 0;
 | |
| 
 | |
|             for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
 | |
|             {
 | |
|                if (j > 0)
 | |
|                   out |= v << j;
 | |
| 
 | |
|                else
 | |
|                   out |= v >> (-j);
 | |
|             }
 | |
| 
 | |
|             *dp++ = png_check_byte(png_ptr, out);
 | |
|             if (++c == channels) c = 0;
 | |
|          }
 | |
|       }
 | |
| 
 | |
|       else /* tc->bit_depth == 16 */
 | |
|       {
 | |
|          unsigned int c = 0, s0, s1;
 | |
| 
 | |
|          UNTESTED
 | |
|          if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
 | |
|             s0 = 0, s1 = 8; /* LSB */
 | |
| 
 | |
|          else
 | |
|             s0 = 8, s1 = 0; /* MSB */
 | |
| 
 | |
|          while (dp < dp_end)
 | |
|          {
 | |
|             int j;
 | |
|             unsigned int value, v;
 | |
| 
 | |
|             v = *sp++ << s0;
 | |
|             v += *sp++ << s1;
 | |
|             value = 0;
 | |
| 
 | |
|             for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
 | |
|             {
 | |
|                if (j > 0)
 | |
|                   value |= v << j;
 | |
| 
 | |
|                else
 | |
|                   value |= v >> (-j);
 | |
|             }
 | |
| 
 | |
|             *dp++ = PNG_BYTE(value >> s0);
 | |
|             *dp++ = PNG_BYTE(value >> s1);
 | |
|          }
 | |
|       }
 | |
|    }
 | |
| #  undef png_ptr
 | |
| }
 | |
| #endif /* WRITE_SHIFT */
 | |
| 
 | |
| #ifdef PNG_READ_SHIFT_SUPPORTED
 | |
| /* Reverse the effects of png_do_shift.  This routine merely shifts the
 | |
|  * pixels back to their significant bits values.  Thus, if you have
 | |
|  * a row of bit depth 8, but only 5 are significant, this will shift
 | |
|  * the values back to 0 through 31.
 | |
|  */
 | |
| static void
 | |
| png_do_unshift(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_transform_shift *tr =
 | |
|       png_transform_cast(png_transform_shift, *transform);
 | |
|    png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
 | |
|    png_bytep dp = png_voidcast(png_bytep, tc->dp);
 | |
|    png_const_bytep dp_end = dp + PNG_TC_ROWBYTES(*tc);
 | |
| 
 | |
|    png_debug(1, "in png_do_unshift");
 | |
| 
 | |
|    tc->range++;
 | |
|    tc->format |= PNG_FORMAT_FLAG_RANGE;
 | |
| 
 | |
|    {
 | |
|       int shift[4];
 | |
|       unsigned int channels = set_shifts(tc->format, tc->bit_depth,
 | |
|          &tr->true_bits, shift, NULL);
 | |
| 
 | |
|       debug(PNG_TC_CHANNELS(*tc) == channels);
 | |
| 
 | |
|       {
 | |
|          unsigned int c, have_shift;
 | |
| 
 | |
|          for (c = have_shift = 0; c < channels; ++c)
 | |
|          {
 | |
|             /* A shift of more than the bit depth is an error condition but it
 | |
|              * gets ignored here.
 | |
|              */
 | |
|             if (shift[c] <= 0 || (unsigned)/*SAFE*/shift[c] >= tc->bit_depth)
 | |
|                shift[c] = 0;
 | |
| 
 | |
|             else
 | |
|                have_shift = 1;
 | |
|          }
 | |
| 
 | |
|          if (have_shift == 0)
 | |
|             return;
 | |
|       }
 | |
| 
 | |
|       /* The code below will copy sp to dp, so: */
 | |
|       tc->sp = dp;
 | |
| 
 | |
|       switch (tc->bit_depth)
 | |
|       {
 | |
|          default:
 | |
|             /* Must be 1bpp gray: should not be here! */
 | |
|             impossible("unshift bit depth");
 | |
|             /* NOTREACHED */
 | |
|             break;
 | |
| 
 | |
|          case 2:
 | |
|             /* Must be 2bpp gray */
 | |
|             debug(channels == 1 && shift[0] == 1);
 | |
|             debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
 | |
| 
 | |
|             while (dp < dp_end)
 | |
|                *dp++ = (*sp++ >> 1) & 0x55;
 | |
|             break;
 | |
| 
 | |
|          case 4:
 | |
|             /* Must be 4bpp gray */
 | |
|             debug(channels == 1);
 | |
|             debug(!(tc->format & PNG_FORMAT_FLAG_SWAPPED));
 | |
|             {
 | |
|                unsigned int gray_shift = shift[0];
 | |
|                unsigned int mask =  0xf >> gray_shift; /* <= 4 bits */
 | |
| 
 | |
|                mask |= mask << 4; /* <= 8 bits */
 | |
| 
 | |
|                while (dp < dp_end)
 | |
|                   *dp++ = (png_byte)/*SAFE*/((*sp++ >> gray_shift) & mask);
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|          case 8:
 | |
|             /* Single byte components, G, GA, RGB, RGBA */
 | |
|             {
 | |
|                unsigned int channel = 0;
 | |
| 
 | |
|                while (dp < dp_end)
 | |
|                {
 | |
|                   *dp++ = (png_byte)/*SAFE*/(*sp++ >> shift[channel]);
 | |
|                   if (++channel >= channels)
 | |
|                      channel = 0;
 | |
|                }
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|          case 16:
 | |
|             /* Double byte components, G, GA, RGB, RGBA */
 | |
|             {
 | |
|                unsigned int channel = 0;
 | |
|                unsigned int s0, s1;
 | |
| 
 | |
|                if ((tc->format & PNG_FORMAT_FLAG_SWAPPED) != 0)
 | |
|                   s0 = 0, s1 = 8; /* LSB */
 | |
| 
 | |
|                else
 | |
|                   s0 = 8, s1 = 0; /* MSB */
 | |
| 
 | |
|                while (dp < dp_end)
 | |
|                {
 | |
|                   unsigned int value = *sp++ << s0;
 | |
| 
 | |
|                   value += *sp++ << s1; /* <= 16 bits */
 | |
| 
 | |
|                   value >>= shift[channel];
 | |
|                   if (++channel >= channels) channel = 0;
 | |
|                   *dp++ = PNG_BYTE(value >> s0);
 | |
|                   *dp++ = PNG_BYTE(value >> s1);
 | |
|                }
 | |
|             }
 | |
|             break;
 | |
|       }
 | |
|    }
 | |
| 
 | |
| #  undef png_ptr
 | |
| }
 | |
| #endif /* READ_SHIFT */
 | |
| 
 | |
| static void
 | |
| init_shift(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    png_const_structp png_ptr = tc->png_ptr;
 | |
| 
 | |
|    /* These shifts apply to the component value, not the pixel index, so skip
 | |
|     * palette data.  In addition there is no *write* shift for palette entries;
 | |
|     * only a read one, so skip the write/palette case too.
 | |
|     */
 | |
|    if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
 | |
|        (png_ptr->read_struct || !tc->palette))
 | |
|    {
 | |
|       /* The only change to the format is to mark the data as having a non-PNG
 | |
|        * range.
 | |
|        */
 | |
|       tc->range++;
 | |
|       tc->format |= PNG_FORMAT_FLAG_RANGE;
 | |
| 
 | |
|       if (tc->init == PNG_TC_INIT_FINAL)
 | |
|       {
 | |
| #     ifdef PNG_READ_SHIFT_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             (*transform)->fn = png_do_unshift;
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| #     ifdef PNG_WRITE_SHIFT_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             (*transform)->fn = png_do_shift;
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    else /* transform not applicable */
 | |
|       (*transform)->fn = NULL;
 | |
| }
 | |
| 
 | |
| void PNGAPI
 | |
| png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits)
 | |
| {
 | |
|    if (png_ptr != NULL && true_bits != NULL)
 | |
|    {
 | |
| #     ifndef PNG_READ_SHIFT_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_shift not supported on read");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| #     ifndef PNG_WRITE_SHIFT_SUPPORTED
 | |
|          if (!png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_shift not supported on write");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| 
 | |
|       {
 | |
|          png_transform_shift *trs = png_transform_cast(png_transform_shift,
 | |
|             png_add_transform(png_ptr, sizeof (png_transform_shift),
 | |
|                init_shift, PNG_TR_SHIFT));
 | |
|          trs->true_bits = *true_bits;
 | |
|       }
 | |
|    }
 | |
| }
 | |
| #endif /* SHIFT */
 | |
| 
 | |
| #if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
 | |
| /* Turn on pixel packing */
 | |
| void PNGAPI
 | |
| png_set_packing(png_structrp png_ptr)
 | |
| {
 | |
|    /* The transforms aren't symmetric, so even though there is one API there are
 | |
|     * two internal init functions, one for read, the other write:
 | |
|     */
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       if (png_ptr->read_struct)
 | |
|       {
 | |
| #        ifdef PNG_READ_PACK_SUPPORTED
 | |
|             png_add_transform(png_ptr, 0/*size*/, png_init_read_pack,
 | |
|                PNG_TR_PACK);
 | |
| #        else
 | |
|             png_app_error(png_ptr, "png_set_packing not supported on read");
 | |
| #        endif
 | |
|       }
 | |
| 
 | |
|       else
 | |
|       {
 | |
| #        ifdef PNG_WRITE_PACK_SUPPORTED
 | |
|             png_add_transform(png_ptr, 0/*size*/, png_init_write_pack,
 | |
|                PNG_TR_PACK);
 | |
| #        else
 | |
|             png_app_error(png_ptr, "png_set_packing not supported on write");
 | |
| #        endif
 | |
|       }
 | |
|    }
 | |
| }
 | |
| #endif /* PACK */
 | |
| 
 | |
| #if defined(PNG_READ_PACKSWAP_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_PACKSWAP_SUPPORTED)
 | |
| /* Turn on pixel-swapping within a byte, this is symmetric - doing the swap
 | |
|  * twice produces the original value, so only one implementation is required for
 | |
|  * either read or write.
 | |
|  *
 | |
|  * Used to be refered to as "packswap", but pixel-swap seems more
 | |
|  * self-documenting.
 | |
|  */
 | |
| static void
 | |
| png_do_swap_1bit(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
 | |
|    png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
 | |
|    png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
 | |
| 
 | |
|    tc->sp = dp;
 | |
|    tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
 | |
| 
 | |
|    for (;;)
 | |
|    {
 | |
|       png_uint_32 s = *sp++;
 | |
|       s = ((s >> 1) & 0x55555555) | ((s & 0x55555555) << 1);
 | |
|       s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2);
 | |
|       s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
 | |
|       *dp++ = s;
 | |
|       if (rowbytes <= 4) break;
 | |
|       rowbytes -= 4;
 | |
|    }
 | |
| 
 | |
|    PNG_UNUSED(transform)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_swap_2bit(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
 | |
|    png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
 | |
|    png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
 | |
| 
 | |
|    tc->sp = dp;
 | |
|    tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
 | |
| 
 | |
|    for (;;)
 | |
|    {
 | |
|       png_uint_32 s = *sp++;
 | |
|       s = ((s >> 2) & 0x33333333) | ((s & 0x33333333) << 2);
 | |
|       s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
 | |
|       *dp++ = s;
 | |
|       if (rowbytes <= 4) break;
 | |
|       rowbytes -= 4;
 | |
|    }
 | |
| 
 | |
|    PNG_UNUSED(transform)
 | |
| }
 | |
| 
 | |
| static void
 | |
| png_do_swap_4bit(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    png_alloc_size_t rowbytes = row_align(tc); /* may change tc->sp */
 | |
|    png_const_uint_32p sp = png_voidcast(png_const_uint_32p, tc->sp);
 | |
|    png_uint_32p dp = png_voidcast(png_uint_32p, tc->dp);
 | |
| 
 | |
|    tc->sp = dp;
 | |
|    tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
 | |
| 
 | |
|    for (;;)
 | |
|    {
 | |
|       png_uint_32 s = *sp++;
 | |
|       s = ((s >> 4) & 0x0f0f0f0f) | ((s & 0x0f0f0f0f) << 4);
 | |
|       *dp++ = s;
 | |
|       if (rowbytes <= 4) break;
 | |
|       rowbytes -= 4;
 | |
|    }
 | |
| 
 | |
|    PNG_UNUSED(transform)
 | |
| }
 | |
| 
 | |
| static void
 | |
| init_packswap(png_transformp *transform, png_transform_controlp tc)
 | |
| {
 | |
|    png_transform_fn fn;
 | |
| 
 | |
| #  define png_ptr tc->png_ptr
 | |
|    debug(tc->init);
 | |
| #  undef png_ptr
 | |
| 
 | |
|    switch (tc->bit_depth)
 | |
|    {
 | |
|       case 1: fn = png_do_swap_1bit; break;
 | |
|       case 2: fn = png_do_swap_2bit; break;
 | |
|       case 4: fn = png_do_swap_4bit; break;
 | |
| 
 | |
|       default: /* transform not applicable */
 | |
|          (*transform)->fn = NULL;
 | |
|          return;
 | |
|    }
 | |
| 
 | |
|    tc->format ^= PNG_FORMAT_FLAG_SWAPPED;
 | |
|    if (tc->init == PNG_TC_INIT_FINAL)
 | |
|       (*transform)->fn = fn;
 | |
| }
 | |
| 
 | |
| void PNGAPI
 | |
| png_set_packswap(png_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
| #     ifndef PNG_READ_PACKSWAP_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_packswap not supported on read");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| #     ifndef PNG_WRITE_PACKSWAP_SUPPORTED
 | |
|          if (!png_ptr->read_struct)
 | |
|          {
 | |
|             png_app_error(png_ptr, "png_set_packswap not supported on write");
 | |
|             return;
 | |
|          }
 | |
| #     endif
 | |
| 
 | |
|       png_add_transform(png_ptr, 0/*size*/, init_packswap, PNG_TR_PIXEL_SWAP);
 | |
|    }
 | |
| }
 | |
| #endif /* PACKSWAP */
 | |
| 
 | |
| /* User transform handling */
 | |
| #ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED
 | |
| png_uint_32 PNGAPI
 | |
| png_get_current_row_number(png_const_structrp png_ptr)
 | |
| {
 | |
|    /* See the comments in png.h - this is the sub-image row when reading an
 | |
|     * interlaced image.
 | |
|     */
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       /* In the read case png_struct::row_number is the row in the final image,
 | |
|        * not the pass, this will return the previous row number if the row isn't
 | |
|        * in the pass:
 | |
|        */
 | |
|       if (png_ptr->read_struct)
 | |
|          return PNG_PASS_ROWS(png_ptr->row_number+1, png_ptr->pass)-1U;
 | |
| 
 | |
|       else
 | |
|          return png_ptr->row_number;
 | |
|    }
 | |
| 
 | |
|    return PNG_UINT_32_MAX; /* help the app not to fail silently */
 | |
| }
 | |
| 
 | |
| png_byte PNGAPI
 | |
| png_get_current_pass_number(png_const_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|       return png_ptr->pass;
 | |
|    return 8; /* invalid */
 | |
| }
 | |
| #endif /* USER_TRANSFORM_INFO */
 | |
| 
 | |
| #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) ||\
 | |
|     defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
 | |
| typedef struct
 | |
| {
 | |
|    png_transform          tr;
 | |
|    png_user_transform_ptr user_fn;
 | |
| #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
 | |
|    png_voidp              user_ptr;
 | |
| #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
 | |
|    unsigned int           user_depth;
 | |
|    unsigned int           user_channels;
 | |
| #endif
 | |
| #endif
 | |
| }  png_user_transform, *png_user_transformp;
 | |
| 
 | |
| typedef const png_user_transform *png_const_user_transformp;
 | |
| 
 | |
| static png_user_transformp
 | |
| get_user_transform(png_structrp png_ptr)
 | |
| {
 | |
|    /* Note that in an added transform the whole transform is memset to 0, so we
 | |
|     * don't need to initialize anything.
 | |
|     */
 | |
|    return png_transform_cast(png_user_transform, png_add_transform(png_ptr,
 | |
|       sizeof (png_user_transform), NULL/*function*/, PNG_TR_USER));
 | |
| }
 | |
| #endif /* READ_USER_TRANSFORM || WRITE_USER_TRANSFORM */
 | |
| 
 | |
| #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
 | |
| png_voidp PNGAPI
 | |
| png_get_user_transform_ptr(png_const_structrp png_ptr)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       png_transformp tr = png_find_transform(png_ptr, PNG_TR_USER);
 | |
| 
 | |
|       if (tr != NULL)
 | |
|       {
 | |
|          png_user_transformp tru = png_transform_cast(png_user_transform, tr);
 | |
|          return tru->user_ptr;
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    return NULL;
 | |
| }
 | |
| #endif /* USER_TRANSFORM_PTR */
 | |
| 
 | |
| #ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
 | |
| void PNGAPI
 | |
| png_set_user_transform_info(png_structrp png_ptr, png_voidp ptr, int depth,
 | |
|    int channels)
 | |
| {
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       /* NOTE: this function only sets the user transform pointer on write, i.e.
 | |
|        * the depth and channels arguments are ignored.
 | |
|        */
 | |
|       png_user_transformp tr = get_user_transform(png_ptr);
 | |
| 
 | |
|       tr->user_ptr = ptr;
 | |
| 
 | |
| #     ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
 | |
|          if (png_ptr->read_struct)
 | |
|          {
 | |
|             if (png_ptr->row_bit_depth == 0)
 | |
|             {
 | |
|                if (depth > 0 && depth <= 32 && channels > 0 && channels <= 4 &&
 | |
|                    (-depth & depth) == depth /* power of 2 */)
 | |
|                {
 | |
|                   tr->user_depth = png_check_bits(png_ptr, depth, 6);
 | |
|                   tr->user_channels = png_check_bits(png_ptr, channels, 3);
 | |
|                }
 | |
| 
 | |
|                else
 | |
|                   png_app_error(png_ptr, "unsupported bit-depth or channels");
 | |
|             }
 | |
|             else
 | |
|                png_app_error(png_ptr,
 | |
|                   "cannot change user info after image start");
 | |
|          }
 | |
| #     else /* !READ_USER_TRANSFORM */
 | |
|          PNG_UNUSED(depth)
 | |
|          PNG_UNUSED(channels)
 | |
| #     endif /* !READ_USER_TRANSFORM */
 | |
|    }
 | |
| }
 | |
| #endif /* USER_TRANSFORM_PTR */
 | |
| 
 | |
| #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED
 | |
| static void
 | |
| png_do_read_user_transform(png_transformp *trIn, png_transform_controlp tc)
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    png_user_transformp tr = png_transform_cast(png_user_transform, *trIn);
 | |
| 
 | |
|    if (!tc->init && tr->user_fn != NULL)
 | |
|    {
 | |
|       png_row_info row_info;
 | |
| 
 | |
|       row_info.width = tc->width;
 | |
|       row_info.rowbytes = PNG_TC_ROWBYTES(*tc);
 | |
|       row_info.color_type = png_check_byte(png_ptr,
 | |
|          PNG_COLOR_TYPE_FROM_FORMAT(tc->format));
 | |
|       row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth);
 | |
|       row_info.channels = png_check_byte(png_ptr,
 | |
|          PNG_FORMAT_CHANNELS(tc->format));
 | |
|       row_info.bit_depth = png_check_byte(png_ptr,
 | |
|          PNG_TC_PIXEL_DEPTH(*tc));
 | |
| 
 | |
|       /* TODO: fix this API, but for the moment we have to copy the row data to
 | |
|        * the working buffer.  This is an unnecessary perf overhead when a user
 | |
|        * transform is used to read information without a transform or when it is
 | |
|        * used on write.
 | |
|        */
 | |
|       if (tc->sp != tc->dp)
 | |
|       {
 | |
|          memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc));
 | |
|          tc->sp = tc->dp;
 | |
|       }
 | |
| 
 | |
|       tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp));
 | |
|    }
 | |
| 
 | |
| #  ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED
 | |
|       if (tr->user_depth > 0)
 | |
|       {
 | |
|          /* The read transform can modify the bit depth and number of
 | |
|           * channels; the interface doesn't permit anything else to be
 | |
|           * changed.  If the information isn't set the user callback has to
 | |
|           * produce pixels with the correct pixel depth (otherwise the
 | |
|           * de-interlace won't work) but there really is no other constraint.
 | |
|           */
 | |
|          tc->bit_depth = tr->user_depth;
 | |
| 
 | |
|          /* The API is very restricted in functionality; the user_channels
 | |
|           * can be changed, but the color_type can't, so the format is simply
 | |
|           * fixed up to match the channels.
 | |
|           */
 | |
|          if (tr->user_channels != PNG_FORMAT_CHANNELS(tc->format))
 | |
|             switch (tr->user_channels)
 | |
|          {
 | |
|             case 1:
 | |
|                tc->format &=
 | |
|                   PNG_BIC_MASK(PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
 | |
|                break;
 | |
| 
 | |
|             case 2: /* has to be GA */
 | |
|                tc->format &=
 | |
|                   PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP+PNG_FORMAT_FLAG_COLOR);
 | |
|                tc->format |= PNG_FORMAT_FLAG_ALPHA;
 | |
|                break;
 | |
| 
 | |
|             case 3: /* has to be RGB */
 | |
|                tc->format &=
 | |
|                   PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP|PNG_FORMAT_FLAG_ALPHA);
 | |
|                tc->format |= PNG_FORMAT_FLAG_COLOR;
 | |
|                break;
 | |
| 
 | |
|             case 4: /* has to be RGBA */
 | |
|                tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_COLORMAP);
 | |
|                tc->format |= (PNG_FORMAT_FLAG_COLOR+PNG_FORMAT_FLAG_ALPHA);
 | |
|                break;
 | |
| 
 | |
|             default: /* checked before */
 | |
|                impossible("user channels");
 | |
|          }
 | |
| 
 | |
|          debug(PNG_FORMAT_CHANNELS(tc->format) == tr->user_channels);
 | |
|       }
 | |
| #  endif /* USER_TRANSFORM_PTR */
 | |
| #  undef png_ptr
 | |
| }
 | |
| 
 | |
| void PNGAPI
 | |
| png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
 | |
|     read_user_transform_fn)
 | |
| {
 | |
|    /* There is no 'init' function, just the callback above to handle the
 | |
|     * transform.
 | |
|     */
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       if (png_ptr->read_struct)
 | |
|       {
 | |
|          png_user_transformp tr = get_user_transform(png_ptr);
 | |
| 
 | |
|          tr->user_fn = read_user_transform_fn;
 | |
|          tr->tr.fn = png_do_read_user_transform;
 | |
|       }
 | |
| 
 | |
|       else
 | |
|          png_app_error(png_ptr, "cannot set a read transform on write");
 | |
|    }
 | |
| }
 | |
| #endif /* READ_USER_TRANSFORM */
 | |
| 
 | |
| #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED
 | |
| static void
 | |
| png_do_write_user_transform(png_transformp *trIn, png_transform_controlp tc)
 | |
| {
 | |
| #  define png_ptr (tc->png_ptr)
 | |
|    /* The write side is pretty simple; call the call-back, it will make the row
 | |
|     * data right.
 | |
|     *
 | |
|     * BUG: we need to copy the input row (it would be a non-quiet API change
 | |
|     * otherwise) and we don't know how big it is because the information passed
 | |
|     * to png_set_user_transform_info never was used on write, but that's fine
 | |
|     * because the write code never did get this right, so presumably all the
 | |
|     * apps have worked round it...
 | |
|     */
 | |
|    if (!tc->init)
 | |
|    {
 | |
|       png_user_transformp tr = png_transform_cast(png_user_transform, *trIn);
 | |
|       png_row_info row_info;
 | |
| 
 | |
|       if (tc->sp != tc->dp) /* no interlace */
 | |
|       {
 | |
|          memcpy(tc->dp, tc->sp, PNG_TC_ROWBYTES(*tc));
 | |
|          tc->sp = tc->dp;
 | |
|       }
 | |
| 
 | |
|       row_info.width = tc->width;
 | |
|       row_info.rowbytes = PNG_TC_ROWBYTES(*tc);
 | |
|       row_info.color_type = png_check_byte(png_ptr,
 | |
|          PNG_COLOR_TYPE_FROM_FORMAT(tc->format));
 | |
|       row_info.bit_depth = png_check_byte(png_ptr, tc->bit_depth);
 | |
|       row_info.channels = png_check_byte(png_ptr,
 | |
|          PNG_FORMAT_CHANNELS(tc->format));
 | |
|       row_info.bit_depth = png_check_byte(png_ptr,
 | |
|          PNG_TC_PIXEL_DEPTH(*tc));
 | |
| 
 | |
|       /* The user function promises to give us this format: */
 | |
|       tr->user_fn(png_ptr, &row_info, png_voidcast(png_bytep, tc->dp));
 | |
|    }
 | |
| #  undef png_ptr
 | |
| }
 | |
| 
 | |
| void PNGAPI
 | |
| png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr
 | |
|     write_user_transform_fn)
 | |
| {
 | |
| 
 | |
|    if (png_ptr != NULL)
 | |
|    {
 | |
|       if (!png_ptr->read_struct)
 | |
|       {
 | |
|          png_user_transformp tr = get_user_transform(png_ptr);
 | |
| 
 | |
|          tr->user_fn = write_user_transform_fn;
 | |
|          tr->tr.fn = png_do_write_user_transform;
 | |
|       }
 | |
| 
 | |
|       else
 | |
|          png_app_error(png_ptr, "cannot set a write transform on read");
 | |
|    }
 | |
| }
 | |
| #endif /* WRITE_USER_TRANSFORM */
 | 
