Ejemplo n.º 1
0
struct png_info compute_write_info(struct png_info read, int width, int height)
{
    struct png_info write;

    /* If either width or height is -1, user is requesting
       us to preserve the aspect ratio:

       Set write.width, write.height so that:
       1. read.width/read.height approx= write.width/write.height
       2. write.width <= width
       3. write.height <= height
       4. Image is large as possible */
    if (width == -1 && height > 0) {
        write.height = height;
        write.width = ROUND_DIV(write.height * read.width, read.height);
    } else if (width > 0 && height == -1) {
        write.width = width;
        write.height = ROUND_DIV(write.width * read.height, read.width);
    } else if (width <= 0 || height <= 0) {
        abort_("Invalid width/height");
    } else {
        write.width = width;
        write.height = height;
    }

    if (write.width == 0) {
        write.width = 1;
    }
    if (write.height == 0) {
        write.height = 1;
    }
    write.bit_depth = 8;
    write.color_type = read.color_type & ~PNG_COLOR_MASK_PALETTE;
    return write;
}
Ejemplo n.º 2
0
Archivo: main.c Proyecto: kerichsen/asf
void echo_cancel(const short *out_buf, const short *in, size_t block_size, int *filter_q16, size_t num_taps, short *echo_cancelled_in)
{
    const short *cur_out = &out_buf[block_size-1];
    const short *cur_in = &in[block_size-1];
    short *cur_in_ec = &echo_cancelled_in[block_size-1];

    const short *out_p;
    int out_norm_sq_p16; /* The value is * 2^16 */

    /* Calculate initial norm squared of output vector */
    out_norm_sq_p16 = 0;
    for (out_p = &out_buf[block_size+num_taps-1]; out_p != &out_buf[block_size-1]; --out_p)
        out_norm_sq_p16 += SQ(ROUND_DIV((int) *out_p, 1 << 8));

    do
    {
        int *filter_q16_p;
        int echo_est_q16;
        short echo_est;

        /* Calculate echo estimate for this sample using output data  */
        echo_est_q16 = 0;
        for (out_p = &cur_out[num_taps-1], filter_q16_p = &filter_q16[num_taps-1]; out_p != cur_out; --out_p, --filter_q16_p)
            echo_est_q16 += (int) (*out_p)*(*filter_q16_p);

        echo_est = ROUND_DIV(echo_est_q16, 1 << 16);

        /* Echo cancelled input is simply input minus echo estimate
                       * Round echo_est_q16 to nearest int when converting to short
                       * This can also be interpreted as the error term, which
                       * is used for the NLMS correction below
                       */
        *cur_in_ec = *cur_in - echo_est;

        /* Update norm squared of output vector */
        out_norm_sq_p16 += SQ(ROUND_DIV((int) cur_out[0], 1<<8)) - SQ(ROUND_DIV((int) cur_out[num_taps], 1<<8));

        /* Update filter tap weights using NLMS correction */
        if (out_norm_sq_p16 != 0)
        {
            for (out_p = &cur_out[num_taps-1], filter_q16_p = &filter_q16[num_taps-1]; out_p != cur_out; --out_p, --filter_q16_p)
                *filter_q16_p += ROUND_DIV((int) *cur_in_ec * (int) *out_p, out_norm_sq_p16);
        }
    } while (cur_out--, cur_in_ec--, cur_in-- != in);
}
Ejemplo n.º 3
0
static void do_display(struct timeval const *now)
{
    if (! refresh_rate) return;

    printf(TOPLEFT CLEAR);
    printf("Duplicogram - Every " BRIGHT "%.2fs" NORMAL " - " BRIGHT "%s" NORMAL, refresh_rate / 1000000., ctime(&now->tv_sec));
    printf(BRIGHT "dups" NORMAL ":  %12"PRIu64"/%-12"PRIu64" (%6.2f%%)\n", nb_dups, nb_dups+nb_nodups, 100.*(double)nb_dups/(nb_dups+nb_nodups));
    printf(BRIGHT "bytes" NORMAL ": %12"PRIu64"/%-12"PRIu64" (%6.2f%%)\n", sz_dups, sz_dups+sz_nodups, 100.*(double)sz_dups/(sz_dups+sz_nodups));

    unsigned lines, columns;
    get_window_size(&columns, &lines);

    if (lines <= 5) return;

    mutex_lock(&dup_lock);
    if (! dups) {
        printf("no data yet\n");
        mutex_unlock(&dup_lock);
        return;
    }

    // look for max dups
    static unsigned dups_max = 0;
    unsigned cur_dups_max = 0;
    for (unsigned b = 0; b < nb_buckets; b++) {
        if (dups[b] > cur_dups_max) cur_dups_max = dups[b];
    }
    if (dups_max == 0) {
        dups_max = cur_dups_max;
    } else {
        if (cur_dups_max > dups_max) {
            dups_max = cur_dups_max;
        } else if (cur_dups_max < dups_max/2) {
            dups_max = cur_dups_max;
        }
    }

    unsigned prev_y_label = ~0U;
    unsigned no_y_tick = 0;
    for (unsigned y = lines - 5; y > 0; y--) {
        unsigned const y_label = ROUND_DIV(dups_max*y, lines-3);
        if (no_y_tick++ == 5 && y_label != prev_y_label) {
            printf("%5u|", y_label);
            prev_y_label = y_label;
            no_y_tick = 0;
        } else {
            printf("     |");
        }
        for (unsigned x = 0; x < columns-6; x++) {
            printf(dups[x] >= y_label ? "*":" ");
        }
        puts("");
    }

    // we are done with dups
    mutex_unlock(&dup_lock);

#   define X_TICK 16    // one tick every X_TICK chars (must be power of 2)
    printf("     ");
    unsigned x;
    for (x = 0; x < columns-7; x++) printf(x & (X_TICK-1) ? "-" : "+");
    printf(">\n     ");
    unsigned x_label = 0;
    for (x = 0; x < columns-7-X_TICK; x+=X_TICK, x_label += X_TICK*bucket_width) {
        if (x > 0) printf("           "); // X_TICK-5 spaces long
        printf("%-5u", x_label);
    }
    for (; x < columns-8; x++) printf(" ");
    printf("           us");  // X_TICK-5 spaces long

    fflush(stdout);
}
Ejemplo n.º 4
0
/***************************************************************************
 * sab_baud:      Function to compute the best register value to achieve
 *                a given baudrate.
 *                
 *
 *     Parameters   : 
 *                  port:    The port being used  (in only)
 *                  encbaud: 2* the baudrate. We use the
 *                           double value so as to support 134.5 (in only)
 *                  bgr      Value of reg BGR for baudrate(output)
 *                  ccr2     Value of reg CCR2 for baudrate (output)
 *                  ccr4     Value of reg CCR4 for baudrate (output)
 *                  truebaud The actual baudrate achieved (output).
 *
 *
 *     Return value : Return TRUE if the vaudrate can be set, FALSE otherwise 
 *
 *     Prerequisite : The various ports must have been initialized
 *
 *     Remark       : Stolen from the Aurora ase driver.
 *
 *     Author       : fw
 *
 *     Revision     : Oct 9 2000, creation
 ***************************************************************************/
unsigned int 
sab8253x_baud(sab_port_t *port, unsigned long encbaud,
	      unsigned char *bgr, unsigned char *ccr2,
	      unsigned char *ccr4, unsigned long *truebaudp)
{
	unsigned char	 bgr_std, bgr_enh, ccr2_std, ccr2_enh, ccr4_enh;
	unsigned int		 ok_std, ok_enh;
	unsigned long	 truebaud_std, truebaud_enh, truebaud,clkspeed;
	
	bgr_std = bgr_enh = 0;
	ccr2_std = ccr2_enh = 0;
	ccr4_enh = 0;
	
	/*
	 * the port/chip/board structure will tell us:
	 *  1) clock speed
	 *  2) chip revision (to figure out if the enhanced method is
	 *     available.
	 */
	
	clkspeed = port->chip->c_cim ? port->chip->c_cim->ci_clkspeed :  port->board->b_clkspeed;
	
#ifdef NODEBUGGING
	printk("With clk speed %ld, baud rate = %ld\n",clkspeed, encbaud);
#endif
	
	ok_std = sab8253x_baudstd(encbaud, clkspeed, &bgr_std,
				  &ccr2_std, &truebaud_std);
#ifdef NODEBUGGING
	printk("Std gives bgr = 0x%x, ccr2=0x%x for speed %ld\n",bgr_std,ccr2_std,truebaud_std);
#endif
	if(port->chip->c_revision >= SAB82532_VSTR_VN_3_2) 
	{
		ok_enh = sab8253x_baudenh(encbaud, clkspeed,
					  &bgr_enh, &ccr2_enh, &truebaud_enh);
#ifdef NODEBUGGING
		printk("Enh gives bgr = 0x%x, ccr2=0x%x for speed %ld\n",bgr_enh,ccr2_enh,truebaud_enh);
#endif
	} 
	else 
		ok_enh = FALSE;
	
	/*
	 * Did both methods return values?
	 */
	if (ok_std && ok_enh) 
	{
		/*
		 * Find the closest of the two.
		 */
		if (ABSDIF((truebaud_enh<<1), encbaud) <
		    ABSDIF((truebaud_std<<1), encbaud)) 
		{
			ok_std = FALSE;
		}
		else 
		{
			ok_enh = FALSE;
		}
	}
	
	/*
	 * Now return the values.
	 */
	
	if (ok_std || ok_enh) 
	{
		truebaud = ok_std ? truebaud_std : truebaud_enh;
		
		/*
		 * If the true baud rate is off by more than 5%, then
		 *  we don't support it.
		 */
		if (ROUND_DIV(ABSDIF((truebaud<<1), encbaud), encbaud) != 0) 
		{
			/*
			 * We're not even in the right ballpark.  This
			 *  test is here to deal with overflow conditions.
			 */
			return FALSE;
		}
		else if (ROUND_DIV(ABSDIF((truebaud<<1), encbaud) * 100,
				   encbaud) >= 5) 
		{
			return FALSE;
		}
		
		*truebaudp = truebaud;
		
		if (ok_enh) 
		{
			*ccr4 |= SAB82532_CCR4_EBRG;
			*ccr2 = ccr2_enh;
			*bgr = bgr_enh;
#ifdef DEBUGGING
			printk("Enhanced Baud at %ld, ccr4 = 0x%x, ccr2 = 9x%x, bgr = 0x%x\n",
			       truebaud,*ccr4,*ccr2,*bgr);
#endif
		} 
		else 
		{
			*ccr4 &= ~SAB82532_CCR4_EBRG;
			*ccr2 = ccr2_std;
			*bgr = bgr_std;
#ifdef DEBUGGING
			printk("Standard Baud at %ld, ccr4 = 0x%x, ccr2 = 9x%x, bgr = 0x%x\n",
			       truebaud,*ccr4,*ccr2,*bgr);
#endif
		}
		
		return TRUE;
	}
	else 
	{
		return FALSE;
	}
}
Ejemplo n.º 5
0
/***************************************************************************
 * sab_baudstd:      Function to compute the "standard " baudrate.
 *                
 *
 *     Parameters   : 
 *                  encbaud  2* the baudrate. We use the
 *                           double value so as to support 134.5 (in only)
 *                  clkspeed The board clock speed in Hz.
 *                  bgr      Value of reg BGR for baudrate(output)
 *                  ccr2     Value of reg CCR2 for baudrate (output)
 *                  ccr4     Value of reg CCR4 for baudrate (output)
 *                  truebaud The actual baudrate achieved (output).
 *
 *
 *     Return value : Return FALSE the parameters could not be computed, 
 *
 *     Prerequisite : The various ports must have been initialized
 *
 *     Remark       : Stolen from the Aurora ase driver.
 *
 *     Author       : fw
 *
 *     Revision     : Oct 9 2000, creation
 ***************************************************************************/
static unsigned int
sab8253x_baudstd(unsigned long encbaud, unsigned long clk_speed,
		 unsigned char *bgr, unsigned char *ccr2,
		 unsigned long *truebaudp)
{
  register unsigned short	 quot;
  register unsigned char	 ccr2tmp;
  
  if (encbaud == 0) 
  {
	  return FALSE;
  }
  
  /*
   * This divisor algorithm is a little strange.  The
   *  divisors are all multiples of 2, except for the
   *  magic value of 1.
   *
   * What we do is do most of the algorithm for multiples
   *  of 1, and then switch at the last minute to multiples
   *  of 2.
   */
  
  /*
   * Will we lose any information by left shifting encbaud?
   *  If so, then right shift clk_speed instead.
   */
  if (!HIZERO(encbaud, 3)) 
  {
	  quot = (unsigned short) ROUND_DIV(ROUND_SHIFT(clk_speed, 3),
					    encbaud);
	  /* quot = (clk_speed / 8) / (baud * 2) = clk_speed / (16 * baud) */
  }
  else 
  {
	  /* encbaud isn't a multiple of 2^29 (baud not mult. of 2^28) */
	  quot = (unsigned short) ROUND_DIV(clk_speed, encbaud << 3);
  }
  
  /* quot = clk_speed / (baud * 16) */
  if (quot < 2) 
  {
	  /* bgr and ccr2 should be initialized to 0 */
	  *truebaudp = ROUND_SHIFT(clk_speed, 4);
	  return TRUE;
  }
  
  /*
   * Divide the quotient by two.
   */
  quot = ROUND_SHIFT(quot, 1);
  
  if (quot <= 0x400) 
  {
	  /* quot = [1, 0x400]  -> (quot << 5) != 0 */
	  *truebaudp = ROUND_DIV(clk_speed, ((unsigned long) quot << 5));
	  quot--;
	  
	  ccr2tmp = SAB82532_CCR2_BDF;
	  if ((quot & 0x200) != 0) 
	  {
		  ccr2tmp |= SAB82532_CCR2_BR9;
	  }
	  if ((quot & 0x100) != 0) 
	  {
		  ccr2tmp |=SAB82532_CCR2_BR8;
	  }
	  
	  *ccr2 = ccr2tmp | (*ccr2 & ~(SAB82532_CCR2_BDF|SAB82532_CCR2_BR8|SAB82532_CCR2_BR9));
	  *bgr = (unsigned char) quot;
  }
  else 
  {			/* the baud rate is too small. */
	  return FALSE;
  }
  
  return TRUE;
}
Ejemplo n.º 6
0
static unsigned int
sab8253x_baudenh(unsigned long encbaud, unsigned long clk_speed,
		 unsigned char *bgr, unsigned char *ccr2,
		 unsigned long *truebaudp)
{
	register unsigned short	 tmp;
	register unsigned char	 ccr2tmp;
	unsigned long		 power2, mant;
	unsigned int			 fastclock;
	
	if (encbaud == 0) {
		return FALSE;
	}
	
	/*
	 * Keep dividing quotien by two until it is between the value of 1 and 64,
	 *  inclusive.
	 */
	
	fastclock = (clk_speed >= 10000000);	/* >= 10 MHz */
	
	for (power2 = 0; power2 < 16; power2++) 
	{
		/* divisor = baud * 2^M * 16 */
		if (!HIZERO(encbaud, power2 + 3)) 
		{
			if (!HIZERO(encbaud, power2)) 
			{	/* baud rate still too big? */
				mant = ROUND_DIV(ROUND_SHIFT(clk_speed, power2 + 3), encbaud);
				
				/* mant = (clk_speed / (8 * 2^M)) / (baud * 2) */
				/*	= clk_speed / (baud * 16 * 2^M) */
			}
			else 
			{
				mant = ROUND_DIV(ROUND_SHIFT(clk_speed, 3), encbaud << power2);
				/* mant = (clk_speed / 8) / (baud * 2 * 2^M) */
				/*	= clk_speed / (baud * 16 * 2^M) */
			}
		}
		else 
		{
			mant = ROUND_DIV(clk_speed, encbaud << (power2 + 3));
			/* mant = clk_speed / (baud * 2 * 8 * 2^M) */
			/*	    = clk_speed / (baud * 16 * 2^M) */
		}
		
		/* mant = clk_speed / (baud * 2^M * 16) */
		
		if (mant < 2
		    || (mant <= 64 && (!fastclock || power2 != 0))) 
		{
			break;
		}
	}
	
	/*
	 * Did we not succeed?  (Baud rate is too small)
	 */
	if (mant > 64) 
	{
		return FALSE;
	}
	
	/*
	 * Now, calculate the true baud rate.
	 */
	
	if (mant < 1 || (mant == 1 && power2 == 0)) 
	{
		/* bgr and ccr2 should be initialized to 0 */
		*truebaudp = ROUND_SHIFT(clk_speed, 4);
	}
	else 
	{
		*truebaudp = ROUND_DIV(clk_speed, mant << (4 + power2));
		/* divisor is not zero because mant is [1, 64] */
		mant--; /* now [0, 63] */
		
		/*
		 * Encode the N and M values into the bgr and ccr2 registers.
		 */
		
		tmp = ((unsigned short) mant) | ((unsigned short) power2 << 6);
		
		ccr2tmp = SAB82532_CCR2_BDF;
		if ((tmp & 0x200) != 0) 
		{
			ccr2tmp |= SAB82532_CCR2_BR9;
		}
		if ((tmp & 0x100) != 0) 
		{
			ccr2tmp |= SAB82532_CCR2_BR8;
		}
		
		*ccr2 = ccr2tmp | (*ccr2 & ~(SAB82532_CCR2_BDF|SAB82532_CCR2_BR8|SAB82532_CCR2_BR9));
		*bgr = (unsigned char) tmp;
	}
	
	return TRUE;
}
Ejemplo n.º 7
0
static int
xencomm_privcmd_domctl(privcmd_hypercall_t *hypercall)
{
	xen_domctl_t kern_op;
	xen_domctl_t __user *user_op;
	struct xencomm_handle *op_desc;
	struct xencomm_handle *desc = NULL;
	int ret = 0;

	user_op = (xen_domctl_t __user *)hypercall->arg[0];

	if (copy_from_user(&kern_op, user_op, sizeof(xen_domctl_t)))
		return -EFAULT;

	if (kern_op.interface_version != XEN_DOMCTL_INTERFACE_VERSION)
		return -EACCES;

	op_desc = xencomm_create_inline(&kern_op);

	switch (kern_op.cmd) {
	case XEN_DOMCTL_createdomain:
	case XEN_DOMCTL_destroydomain:
	case XEN_DOMCTL_pausedomain:
	case XEN_DOMCTL_unpausedomain:
	case XEN_DOMCTL_getdomaininfo:
		break;
	case XEN_DOMCTL_getmemlist:
	{
		unsigned long nr_pages = kern_op.u.getmemlist.max_pfns;

		ret = xencomm_create(
			xen_guest_handle(kern_op.u.getmemlist.buffer),
			nr_pages * sizeof(unsigned long),
			&desc, GFP_KERNEL);
		set_xen_guest_handle(kern_op.u.getmemlist.buffer,
		                     (void *)desc);
		break;
	}
	case XEN_DOMCTL_getpageframeinfo:
		break;
	case XEN_DOMCTL_getpageframeinfo2:
		ret = xencomm_create(
			xen_guest_handle(kern_op.u.getpageframeinfo2.array),
			kern_op.u.getpageframeinfo2.num,
			&desc, GFP_KERNEL);
		set_xen_guest_handle(kern_op.u.getpageframeinfo2.array,
		                     (void *)desc);
		break;
	case XEN_DOMCTL_shadow_op:
		ret = xencomm_create(
			xen_guest_handle(kern_op.u.shadow_op.dirty_bitmap),
			ROUND_DIV(kern_op.u.shadow_op.pages, 8),
			&desc, GFP_KERNEL);
		set_xen_guest_handle(kern_op.u.shadow_op.dirty_bitmap,
		                     (void *)desc);
		break;
	case XEN_DOMCTL_max_mem:
		break;
	case XEN_DOMCTL_setvcpucontext:
	case XEN_DOMCTL_getvcpucontext:
		ret = xencomm_create(
			xen_guest_handle(kern_op.u.vcpucontext.ctxt),
			sizeof(vcpu_guest_context_t),
			&desc, GFP_KERNEL);
		set_xen_guest_handle(kern_op.u.vcpucontext.ctxt, (void *)desc);
		break;
	case XEN_DOMCTL_getvcpuinfo:
		break;
	case XEN_DOMCTL_setvcpuaffinity:
	case XEN_DOMCTL_getvcpuaffinity:
		ret = xencomm_create(
			xen_guest_handle(kern_op.u.vcpuaffinity.cpumap.bitmap),
			ROUND_DIV(kern_op.u.vcpuaffinity.cpumap.nr_cpus, 8),
			&desc, GFP_KERNEL);
		set_xen_guest_handle(kern_op.u.vcpuaffinity.cpumap.bitmap,
		                     (void *)desc);
		break;
	case XEN_DOMCTL_max_vcpus:
	case XEN_DOMCTL_scheduler_op:
	case XEN_DOMCTL_setdomainhandle:
	case XEN_DOMCTL_setdebugging:
	case XEN_DOMCTL_irq_permission:
	case XEN_DOMCTL_iomem_permission:
	case XEN_DOMCTL_ioport_permission:
	case XEN_DOMCTL_hypercall_init:
	case XEN_DOMCTL_arch_setup:
	case XEN_DOMCTL_settimeoffset:
	case XEN_DOMCTL_sendtrigger:
	case XEN_DOMCTL_set_address_size:
	case XEN_DOMCTL_get_address_size:
		break;
	default:
		printk("%s: unknown domctl cmd %d\n", __func__, kern_op.cmd);
		return -ENOSYS;
	}

	if (ret) {
		/* error mapping the nested pointer */
		return ret;
	}

	ret = xencomm_arch_hypercall_domctl(op_desc);
	if (kern_op.cmd == XEN_DOMCTL_destroydomain) {
		while (ret == -EAGAIN) {
			schedule(); /* prevent softlock up message */
			ret = xencomm_arch_hypercall_domctl(op_desc);
		}
	}

	/* FIXME: should we restore the handle?  */
	if (copy_to_user(user_op, &kern_op, sizeof(xen_domctl_t)))
		ret = -EFAULT;

	if (desc)
		xencomm_free(desc);
	return ret;
}
Ejemplo n.º 8
0
void scale_png_down_no_alpha(struct png_info read, struct png_info write)
{
    int x, y, c;

    /* Read and write pixels */
    png_bytep read_row_pointer = (png_byte*) malloc(read.rowbytes);
    if (!read_row_pointer) {
        abort_("Failed to allocate memory to hold one row of input PNG image");
    }

    uint64_t* write_row_sums_pointer = (uint64_t*) malloc(sizeof(uint64_t) * write.width * write.channels);
    uint64_t* write_next_row_sums_pointer = (uint64_t*) malloc(sizeof(uint64_t) * write.width * write.channels);
    uint64_t read_area = ((uint64_t)read.width) * read.height;
    if (!write_row_sums_pointer || !write_next_row_sums_pointer) {
        abort_("Failed to allocate memory - need enough to hold five rows of output PNG image");
    }

    png_bytep write_row_pointer = (png_byte*) malloc(write.rowbytes);
    if (!write_row_pointer) {
        abort_("Failed to allocate memory to hold one row of output PNG image");
    }
    
    memset(write_row_sums_pointer, 0, sizeof(uint64_t) * write.width * write.channels);
    memset(write_next_row_sums_pointer, 0, sizeof(uint64_t) * write.width * write.channels);
    int y_frac = 0;
    for (y=0; y < read.height; y++) {
        png_read_row(read.png_ptr, read_row_pointer, NULL);

        int end_of_row = 0;
        unsigned int fraction_in_current_row = write.height; /* Proportion represented by integer between 0 and write.height */
        unsigned int fraction_in_next_row = 0;
        y_frac += write.height;
        if (y_frac >= read.height) {
            /* We've reached a boundary between output image rows. */
            end_of_row = 1;
            y_frac -= read.height;
            fraction_in_current_row = write.height - y_frac;
            fraction_in_next_row = y_frac;
        }

        int write_x = 0;
        int x_frac = 0;
        for (x=0; x < read.width; x++) {
            int end_of_col = 0;
            unsigned int fraction_in_current_col = write.width; /* Proportion represented by integer between 0 and write.width */
            unsigned int fraction_in_next_col = 0;
            x_frac += write.width;
            if (x_frac >= read.width) {
                /* We've reached a boundary between output image columns. */
                end_of_col = 1;
                x_frac -= read.width;
                fraction_in_current_col = write.width - x_frac;
                fraction_in_next_col = x_frac;
            }

            png_byte* read_ptr = &(read_row_pointer[x*read.channels]);
            for (c=0; c < write.channels; c++) {
                uint64_t value = read_ptr[c];
                write_row_sums_pointer[write.channels*write_x + c] +=
                    value * fraction_in_current_col * fraction_in_current_row;
                if (fraction_in_next_col) {
                    write_row_sums_pointer[write.channels*(write_x + 1) + c] +=
                        value * fraction_in_next_col * fraction_in_current_row;
                }
                if (fraction_in_next_row) {
                    write_next_row_sums_pointer[write.channels*write_x + c] +=
                        value * fraction_in_current_col * fraction_in_next_row;
                }
                if (fraction_in_next_col && fraction_in_next_row) {
                    write_next_row_sums_pointer[write.channels*(write_x + 1) + c] +=
                        value * fraction_in_next_col * fraction_in_next_row;
                }
            }

            if (end_of_col) {
                write_x++;
                assert (write_x < write.width || x == read.width - 1);
            }
        }

        if (end_of_row) {
            for (x=0; x < write.width; x++) {
                png_byte* write_ptr = &(write_row_pointer[x*write.channels]);
                uint64_t* write_sums_ptr = &(write_row_sums_pointer[x*write.channels]);
                for (c=0; c < write.channels; c++) {
                    write_ptr[c] = ROUND_DIV(write_sums_ptr[c], read_area);
                }
            }

            png_write_row(write.png_ptr, write_row_pointer);
            SWAP(write_row_sums_pointer, write_next_row_sums_pointer, uint64_t*);
            memset(write_next_row_sums_pointer, 0, sizeof(uint64_t) * write.width * write.channels);
        }
    }

    close_read_png(read);
    close_write_png(write);
}