png_set_filter(png_structrp png_ptr, int method, int filters)
{
   png_debug(1, "in png_set_filter");

   if (png_ptr == NULL)
      return;

#ifdef PNG_MNG_FEATURES_SUPPORTED
   if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
       (method == PNG_INTRAPIXEL_DIFFERENCING))
      method = PNG_FILTER_TYPE_BASE;

#endif
   if (method == PNG_FILTER_TYPE_BASE)
   {
      switch (filters & (PNG_ALL_FILTERS | 0x07))
      {
#ifdef PNG_WRITE_FILTER_SUPPORTED
         case 5:
         case 6:
         case 7: png_app_error(png_ptr, "Unknown row filter for method 0");
            /* FALL THROUGH */
#endif /* WRITE_FILTER */
         case PNG_FILTER_VALUE_NONE:
            png_ptr->do_filter = PNG_FILTER_NONE; break;

#ifdef PNG_WRITE_FILTER_SUPPORTED
         case PNG_FILTER_VALUE_SUB:
            png_ptr->do_filter = PNG_FILTER_SUB; break;

         case PNG_FILTER_VALUE_UP:
            png_ptr->do_filter = PNG_FILTER_UP; break;

         case PNG_FILTER_VALUE_AVG:
            png_ptr->do_filter = PNG_FILTER_AVG; break;

         case PNG_FILTER_VALUE_PAETH:
            png_ptr->do_filter = PNG_FILTER_PAETH; break;

         default:
            png_ptr->do_filter = (png_byte)filters; break;
#else
         default:
            png_app_error(png_ptr, "Unknown row filter for method 0");
#endif /* WRITE_FILTER */
      }

#ifdef PNG_WRITE_FILTER_SUPPORTED
      /* If we have allocated the row_buf, this means we have already started
       * with the image and we should have allocated all of the filter buffers
       * that have been selected.  If prev_row isn't already allocated, then
       * it is too late to start using the filters that need it, since we
       * will be missing the data in the previous row.  If an application
       * wants to start and stop using particular filters during compression,
       * it should start out with all of the filters, and then remove them
       * or add them back after the start of compression.
       *
       * NOTE: this is a nasty constraint on the code, because it means that the
       * prev_row buffer must be maintained even if there are currently no
       * 'prev_row' requiring filters active.
       */
      if (png_ptr->row_buf != NULL)
      {
         int num_filters;
         png_alloc_size_t buf_size;

         /* Repeat the checks in png_write_start_row; 1 pixel high or wide
          * images cannot benefit from certain filters.  If this isn't done here
          * the check below will fire on 1 pixel high images.
          */
         if (png_ptr->height == 1)
            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);

         if (png_ptr->width == 1)
            filters &= ~(PNG_FILTER_SUB|PNG_FILTER_AVG|PNG_FILTER_PAETH);

         if ((filters & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) != 0
            && png_ptr->prev_row == NULL)
         {
            /* This is the error case, however it is benign - the previous row
             * is not available so the filter can't be used.  Just warn here.
             */
            png_app_warning(png_ptr,
               "png_set_filter: UP/AVG/PAETH cannot be added after start");
            filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH);
         }

         num_filters = 0;

         if (filters & PNG_FILTER_SUB)
            num_filters++;

         if (filters & PNG_FILTER_UP)
            num_filters++;

         if (filters & PNG_FILTER_AVG)
            num_filters++;

         if (filters & PNG_FILTER_PAETH)
            num_filters++;

         /* Allocate needed row buffers if they have not already been
          * allocated.
          */
         buf_size = PNG_ROWBYTES(png_ptr->usr_channels * png_ptr->usr_bit_depth,
             png_ptr->width) + 1;

         if (png_ptr->try_row == NULL)
            png_ptr->try_row = png_voidcast(png_bytep,
               png_malloc(png_ptr, buf_size));

         if (num_filters > 1)
         {
            if (png_ptr->tst_row == NULL)
               png_ptr->tst_row = png_voidcast(png_bytep,
                  png_malloc(png_ptr, buf_size));
         }
      }
      png_ptr->do_filter = (png_byte)filters;
#endif
   }
   else
      png_error(png_ptr, "Unknown custom filter method");
}