Exemple #1
0
int img_convert(struct img_pixmap *img, enum img_fmt tofmt)
{
	struct pixel pbuf[8];
	int bufsz = (img->width & 7) == 0 ? 8 : ((img->width & 3) == 0 ? 4 : 1);
	int i, num_pix = img->width * img->height;
	int num_iter = num_pix / bufsz;
	char *sptr, *dptr;
	struct img_pixmap nimg;

	if(img->fmt == tofmt) {
		return 0;	/* nothing to do */
	}

	img_init(&nimg);
	if(img_set_pixels(&nimg, img->width, img->height, tofmt, 0) == -1) {
		img_destroy(&nimg);
		return -1;
	}

	sptr = img->pixels;
	dptr = nimg.pixels;

	for(i=0; i<num_iter; i++) {
		unpack[img->fmt](pbuf, sptr, bufsz);
		pack[tofmt](dptr, pbuf, bufsz);

		sptr += bufsz * img->pixelsz;
		dptr += bufsz * nimg.pixelsz;
	}

	img_copy(img, &nimg);
	img_destroy(&nimg);
	return 0;
}
Exemple #2
0
static int write(struct img_pixmap *img, struct img_io *io)
{
	struct img_pixmap fimg;

	img_init(&fimg);
	if(img_copy(&fimg, img) == -1) {
		img_destroy(&fimg);
		return -1;
	}
	if(img_convert(&fimg, IMG_FMT_RGBF) == -1) {
		img_destroy(&fimg);
		return -1;
	}

	if(rgbe_write_header(io, fimg.width, fimg.height, 0) == -1) {
		img_destroy(&fimg);
		return -1;
	}
	if(rgbe_write_pixels_rle(io, fimg.pixels, fimg.width, fimg.height) == -1) {
		img_destroy(&fimg);
		return -1;
	}
	img_destroy(&fimg);
	return 0;
}
Exemple #3
0
static int write(struct img_pixmap *img, struct img_io *io)
{
	int i, nlines = 0;
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	struct dst_mgr dest;
	struct img_pixmap tmpimg;
	unsigned char **scanlines;

	img_init(&tmpimg);

	if(img->fmt != IMG_FMT_RGB24) {
		if(img_copy(&tmpimg, img) == -1) {
			return -1;
		}
		if(img_convert(&tmpimg, IMG_FMT_RGB24) == -1) {
			img_destroy(&tmpimg);
			return -1;
		}
		img = &tmpimg;
	}

	if(!(scanlines = malloc(img->height * sizeof *scanlines))) {
		img_destroy(&tmpimg);
		return -1;
	}
	scanlines[0] = img->pixels;
	for(i=1; i<img->height; i++) {
		scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz;
	}

	cinfo.err = jpeg_std_error(&jerr);	/* XXX */
	jpeg_create_compress(&cinfo);

	dest.pub.init_destination = init_destination;
	dest.pub.empty_output_buffer = empty_output_buffer;
	dest.pub.term_destination = term_destination;
	dest.io = io;
	cinfo.dest = (struct jpeg_destination_mgr*)&dest;

	cinfo.image_width = img->width;
	cinfo.image_height = img->height;
	cinfo.input_components = 3;
	cinfo.in_color_space = JCS_RGB;

	jpeg_set_defaults(&cinfo);

	jpeg_start_compress(&cinfo, 1);
	while(nlines < img->height) {
		int res = jpeg_write_scanlines(&cinfo, scanlines + nlines, img->height - nlines);
		nlines += res;
	}
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	free(scanlines);
	img_destroy(&tmpimg);
	return 0;
}
int GGPROTO::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam)
{
	switch( eventType ) {
	case EV_PROTO_ONLOAD:
		{
			HookProtoEvent(ME_OPT_INITIALISE, &GGPROTO::options_init);
			HookProtoEvent(ME_USERINFO_INITIALISE, &GGPROTO::details_init);

			// Init misc stuff
			gg_icolib_init();
			initpopups();
			gc_init();
			keepalive_init();
			img_init();
			block_init();

			// Try to fetch user avatar
			getOwnAvatar();
			break;
		}
	case EV_PROTO_ONEXIT:
		// Stop avatar request thread
		pth_avatar.dwThreadId = 0;

		// Stop main connection session thread
		pth_sess.dwThreadId = 0;

		img_shutdown();
		sessions_closedlg();
		break;

	case EV_PROTO_ONOPTIONS:
		return options_init(wParam, lParam);

	case EV_PROTO_ONMENU:
		menus_init();
		break;

	case EV_PROTO_ONRENAME:
		if (hMenuRoot) {
			CLISTMENUITEM mi = { sizeof(mi) };
			mi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
			mi.ptszName = m_tszUserName;
			Menu_ModifyItem(hMenuRoot, &mi);
		}
		break;

	case EV_PROTO_ONCONTACTDELETED:
		return contactdeleted(wParam, lParam);

	case EV_PROTO_DBSETTINGSCHANGED:
		return dbsettingchanged(wParam, lParam);
	}
	return TRUE;
}
Exemple #5
0
void	core(t_env *e)
{
	error((int)(e->mlx = mlx_init()));
	error((int)(e->win.adr = mlx_new_window(e->mlx, e->win.w, e->win.h,
	e->arg.file_scene)));
//	ft_strdel(&e->arg.file_scene);
	img_init(e);
	raytracing(e);
//	viewer_export(e);
	mlx_hook(e->win.adr, 2, (1L << 0), key_pressed, e);
	mlx_expose_hook(e->win.adr, expose_hook, e);
	mlx_loop_hook(e->mlx, loop_hook, e);
	mlx_loop(e->mlx);
}
Exemple #6
0
void	*eb_mlx(int	shm_id, t_share *shared)
{
	t_win		*env;
	t_img		*img;

	shared = shmat(shm_id, (void *)0, 0);
	env = env_init();
	img = img_init();
	mlx_key_hook(env->win, eb_mlx_key_hook, NULL);
	mlx_expose_hook(env->win, eb_expose_hook, shared);
	mlx_loop_hook(env->mlx, eb_loop_hook, shared);
	mlx_loop(env->mlx);
	return (NULL);
}
Exemple #7
0
unsigned int img_gltexture_read(struct img_io *io)
{
	struct img_pixmap img;
	unsigned int tex;

	img_init(&img);
	if(img_read(&img, io) == -1) {
		img_destroy(&img);
		return 0;
	}

	tex = img_gltexture(&img);
	img_destroy(&img);
	return tex;
}
Exemple #8
0
unsigned int img_gltexture_read_file(FILE *fp)
{
	struct img_pixmap img;
	unsigned int tex;

	img_init(&img);
	if(img_read_file(&img, fp) == -1) {
		img_destroy(&img);
		return 0;
	}

	tex = img_gltexture(&img);
	img_destroy(&img);
	return tex;
}
Exemple #9
0
unsigned int img_gltexture_load(const char *fname)
{
	struct img_pixmap img;
	unsigned int tex;

	img_init(&img);
	if(img_load(&img, fname) == -1) {
		img_destroy(&img);
		return 0;
	}

	tex = img_gltexture(&img);
	img_destroy(&img);
	return tex;
}
Exemple #10
0
int		expose_hook(t_data *data)
{
	img_init(data);
	if (data->fractal == 1)
		mandel_process(data);
	if (data->fractal == 2)
		julia_process(data);
	if (data->fractal == 3)
		burning_process(data);
	if (data->fractal == 4)
		tree_process(data);
	if (data->fractal == 5)
		galaxy_process(data);
	mlx_put_image_to_window(data->mlx, data->win, data->img.adr, 0, 0);
	return (0);
}
Exemple #11
0
int		eb_mlx_key_hook(int keycode)
{
	t_win	*env;
	t_img	*img;

	if (keycode == KEY_ESC)
	{
		img = img_init();
		env = env_init();
		if (img->img)
			mlx_destroy_image(env->mlx, img->img);
		free(img);
		free(env);
		exit(0);
	}
	return (0);
}
Exemple #12
0
t_win	*env_init(void)
{
	static t_win	*win = NULL;
	t_data			*d;

	d = data_init();
	if (win == NULL)
	{
		if (!(win = (t_win *)malloc(sizeof(t_win))))
			ft_error("erreur malloc");
		win->mlx = mlx_init();
		win->win = mlx_new_window(win->mlx, d->win_size_x, d->win_size_y
			, d->scene_name);
		win->img = img_init();
	}
	return (win);
}
Exemple #13
0
int GGPROTO::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam)
{
	switch( eventType ) {
	case EV_PROTO_ONLOAD:
		HookProtoEvent(ME_OPT_INITIALISE, &GGPROTO::options_init);
		HookProtoEvent(ME_USERINFO_INITIALISE, &GGPROTO::details_init);

		// Init misc stuff
		gg_icolib_init();
		initpopups();
		gc_init();
		keepalive_init();
		img_init();
		block_init();

		// Try to fetch user avatar
		getOwnAvatar();
		break;

	case EV_PROTO_ONEXIT:
		// Stop avatar request thread
		pth_avatar.dwThreadId = 0;

		// Stop main connection session thread
		pth_sess.dwThreadId = 0;

		img_shutdown();
		sessions_closedlg();
		break;

	case EV_PROTO_ONOPTIONS:
		return options_init(wParam, lParam);

	case EV_PROTO_ONMENU:
		menus_init();
		break;

	case EV_PROTO_ONCONTACTDELETED:
		return contactdeleted(wParam, lParam);

	case EV_PROTO_DBSETTINGSCHANGED:
		return dbsettingchanged(wParam, lParam);
	}
	return TRUE;
}
Exemple #14
0
/**
 * Initialize the templ theme
 * @param hue [0..360] hue value from HSV color space to define the theme's base color
 * @param font pointer to a font (NULL to use the default)
 * @return pointer to the initialized theme
 */
lv_theme_t * lv_theme_templ_init(uint16_t hue, lv_font_t *font)
{
    if(font == NULL) font = LV_FONT_DEFAULT;

    _hue = hue;
    _font = font;

    /*For backward compatibility initialize all theme elements with a default style */
    uint16_t i;
    lv_style_t **style_p = (lv_style_t**) &theme;
    for(i = 0; i < sizeof(lv_theme_t) / sizeof(lv_style_t*); i++) {
        *style_p = &def;
        style_p++;
    }

    basic_init();
    cont_init();
    btn_init();
    label_init();
    img_init();
    line_init();
    led_init();
    bar_init();
    slider_init();
    sw_init();
    lmeter_init();
    gauge_init();
    chart_init();
    cb_init();
    btnm_init();
    kb_init();
    mbox_init();
    page_init();
    ta_init();
    list_init();
    ddlist_init();
    roller_init();
    tabview_init();
    win_init();

    return &theme;
}
Exemple #15
0
void		init(t_env *env, char **av, int ac)
{
	env->color = NULL;
	env->pixel = NULL;
	env->coeff = NULL;
	env->t = NULL;
	env->fract = NULL;
	if ((init_args(env, av, ac)))
		ft_exit(env, "Wrong arg !");
	if (!(img_init(env)))
		ft_exit(env, "Unable to init mlx.");
	if (!(env->color = rgbmap()))
		ft_exit(env, "Unable to creat rgbmap");
	if (!(env->pixel = pixelmap()))
		ft_exit(env, "Unable to create pixelmap");
	if (!(env->coeff = coeffmap()))
		ft_exit(env, "Unable to create coeffmap");
	if (!(env->t = threadmap(env)))
		ft_exit(env, "Unable to create threadmap");
}
Exemple #16
0
void		draw(t_env *e)
{
	e->i = 0;
	img_init(e);
	while (e->i + 1 < e->p.lenmax)
	{
		iso_init(e);
		if (e->a[e->i]->y == e->a[e->i + 1]->y)
		{
			iso_converth(e);
			draw_line(e);
		}
		if (e->i + e->p.lenx < e->p.lenmax)
		{
			iso_convertv(e);
			draw_line(e);
		}
		e->i++;
	}
	mlx_hook(e->win, 2, 1, keyfnc, e);
	mlx_put_image_to_window(e->mlx, e->win, e->img.adr, 0, 0);
	optionsdisplay(e);
	mlx_loop(e->mlx);
}
Exemple #17
0
int parse_args(int argc, char **argv)
{
	int i;
	char *endp;
	struct list_node *head = 0, *tail = 0;

	for(i=1; i<argc; i++) {
		if(argv[i][0] == '-' && argv[i][2] == 0) {
			switch(argv[i][1]) {
			case 'f':
				fullscreen = !fullscreen;
				break;

			case 's':
				stereo = !stereo;
				break;

			case 't':
				threshold = strtod(argv[++i], &endp);
				if(endp == argv[i]) {
					fprintf(stderr, "-t must be followed by a number\n");
					return -1;
				}
				break;

			case 'h':
				printf("usage: %s [opt]\n", argv[0]);
				printf("options:\n");
				printf("  -f    start in fullscreen\n");
				printf("  -s    enable stereoscopic rendering\n");
				printf("  -h    print usage and exit\n");
				exit(0);

			default:
				fprintf(stderr, "unrecognized option: %s\n", argv[i]);
				return -1;
			}
		} else {
			struct list_node *slice;

			if(!(slice = malloc(sizeof *slice))) {
				fprintf(stderr, "failed to allocate volume slice: %d\n", num_slices);
				return -1;
			}
			slice->next = 0;

			img_init(&slice->img);
			if(img_load(&slice->img, argv[i]) == -1) {
				fprintf(stderr, "failed to load volume slice %d: %s\n", num_slices, argv[i]);
				free(slice);
				return -1;
			}
			img_convert(&slice->img, IMG_FMT_GREY8);

			if(num_slices > 0 && (xres != slice->img.width || yres != slice->img.height)) {
				fprintf(stderr, "error: slice %d (%s) is %dx%d, up to now we had %dx%d images\n", num_slices, argv[i],
						slice->img.width, slice->img.height, xres, yres);
				img_destroy(&slice->img);
				free(slice);
				return -1;
			}
			xres = slice->img.width;
			yres = slice->img.height;

			if(head) {
				tail->next = slice;
				tail = slice;
			} else {
				head = tail = slice;
			}
			printf("loaded volume slice %d: %s\n", num_slices++, argv[i]);
		}
	}

	if(!head) {
		fprintf(stderr, "you must specify a list of images for the volume data slices\n");
		return -1;
	}

	if(!(volume = malloc(num_slices * sizeof *volume))) {
		fprintf(stderr, "failed to allocate volume data (%d slices)\n", num_slices);
		return -1;
	}

	for(i=0; i<num_slices; i++) {
		void *tmp;

		assert(head);
		volume[i] = head->img;

		tmp = head;
		head = head->next;
		free(tmp);
	}

	return 0;
}
Exemple #18
0
/**
 * Initialize all structures.
 *
 * @param tld learning structures
 */
void tldInit(TldStruct& tld) {

	// initialize lucas kanade
	lkInit();

	// get initial bounding box
	Eigen::Vector4d bb;
	bb = tld.cfg->init;

	Eigen::Vector2i imsize;
	imsize(0) = tld.imgsize.m;
	imsize(1) = tld.imgsize.n;
	bb_scan(tld, bb, imsize, tld.model->min_win);

	// Features
	tldGenerateFeatures(tld, tld.model->num_trees, tld.model->num_features);

	// Initialize Detector
	fern0();

	ImgType im0;

	img_init(*(tld.cfg));

	im0.input = img_get();

	im0.blur = cvCloneImage(im0.input);
	im0.blur = img_blur(im0.blur);

	// allocate structures
	fern1(im0.input, tld.grid, tld.features, tld.scales);

	// Temporal structures
	Tmp temporal;
	temporal.conf = Eigen::VectorXd::Zero(tld.nGrid);
	temporal.patt = Eigen::Matrix<double, 10, Eigen::Dynamic>::Zero(tld.model->num_trees, tld.nGrid);
	tld.tmp = temporal;

	// RESULTS =================================================================

	// Initialize Trajectory

	tld.prevBB = Eigen::Vector4d::Constant(
			std::numeric_limits<double>::quiet_NaN());
	tld.currentImg = im0;
	tld.currentBB = bb;
	tld.conf = 1;
	tld.currentValid = 1;
	tld.size = 1;

	// TRAIN DETECTOR ==========================================================

	// Initialize structures
	tld.imgsize.m = DIMY;
	tld.imgsize.n = DIMX;

	Eigen::RowVectorXd overlap = bb_overlap(tld.currentBB, tld.nGrid, tld.grid.topRows(4));

	tld.target = img_patch(tld.currentImg.input, tld.currentBB);

	// Generate Positive Examples
	Eigen::Matrix<double, NTREES, Eigen::Dynamic> pX; // pX: 10 rows
	Eigen::Matrix<double, (PATCHSIZE * PATCHSIZE), Eigen::Dynamic> pEx;
	tld.currentBB = tldGeneratePositiveData(tld, overlap, tld.currentImg,
			tld.p_par_init, pX, pEx);

	Eigen::MatrixXd pY = Eigen::MatrixXd::Ones(1, pX.cols());
	// Generate Negative Examples
	Eigen::Matrix<double, NTREES, Eigen::Dynamic> nX; // nX: 10 rows
	Eigen::Matrix<double, (PATCHSIZE * PATCHSIZE), Eigen::Dynamic> nEx;
	tldGenerateNegativeData(tld, overlap, tld.currentImg, nX, nEx);

	// Split Negative Data to Training set and Validation set
	Eigen::Matrix<double, NTREES, Eigen::Dynamic> spnX;
	Eigen::Matrix<double, (PATCHSIZE * PATCHSIZE), Eigen::Dynamic> spnEx;
	tldSplitNegativeData(nX, nEx, spnX, spnEx);

	Eigen::MatrixXd nY1 = Eigen::MatrixXd::Zero(1, spnX.cols() / 2);

	Eigen::MatrixXd xCombined(pX.rows(), pX.cols() + spnX.cols() / 2);
	xCombined << pX, spnX.leftCols(spnX.cols() / 2);
	Eigen::MatrixXd yCombined(pY.rows(), pY.cols() + nY1.cols());
	yCombined << pY, nY1;
	Eigen::RowVectorXd idx(xCombined.cols());
	for (int i = 0; i < xCombined.cols(); i++)
		idx(i) = i;

	idx = permutate_cols(idx);

	Eigen::MatrixXd permX(xCombined.rows(), xCombined.cols());
	Eigen::VectorXd permY(yCombined.cols());
	for (int i = 0; i < idx.cols(); i++) {
		permX.col(i) = xCombined.col(idx(i));
		permY(i) = yCombined(0, idx(i));
	}

	// Train using training set ------------------------------------------------

	// Fern
	unsigned char bootstrap = 2;
	Eigen::VectorXd dummy(1);
	dummy(0) = -1;
	fern2(permX, permY, tld.model->thr_fern, bootstrap, dummy);

	// Nearest Neighbour
	tld.npex = 0;
	tld.nnex = 0;

	tldTrainNN(pEx, spnEx.leftCols(spnEx.cols() / 2), tld);
	tld.model->num_init = tld.npex;

	// Estimate thresholds on validation set  ----------------------------------

	// Fern
	unsigned int ferninsize = spnX.cols() / 2;
	Eigen::RowVectorXd conf_fern(ferninsize);
	Eigen::Matrix<double, 10, Eigen::Dynamic> fernin(10, ferninsize);
	fernin.leftCols(ferninsize) = spnX.rightCols(ferninsize);
	conf_fern = fern3(fernin, ferninsize);
	tld.model->thr_fern = std::max(conf_fern.maxCoeff() / tld.model->num_trees,
			tld.model->thr_fern);

	// Nearest neighbor
	Eigen::MatrixXd conf_nn(3, 3);
	conf_nn = tldNN(spnEx.rightCols(spnEx.cols() / 2), tld);

	tld.model->thr_nn = std::max(tld.model->thr_nn, conf_nn.block(0, 0, 1,
			conf_nn.cols() / 3).maxCoeff());
	tld.model->thr_nn_valid = std::max(tld.model->thr_nn_valid,
			tld.model->thr_nn);
}
Exemple #19
0
void			generate_stereoscopy(t_env *e)
{
	e->cam->stereo = img_init(e);
	compute_cyan(&e->cam->stereo, &e->cam->img);
	compute_red(&e->cam->stereo, &e->cam->twin->img);
}
Exemple #20
0
t_npc		*init_all(t_image *img, t_player *player, t_map *map)
{
  img_init(img);
  init(player, map, img);
  return (get_npc_monsters(map, img));
}
Exemple #21
0
int export_img(struct play_s *play)
{
	/*
	 Export img uses following pipeline:

	 file -(uncompressed_buffer)->     reads data from stream file
	 unpack -(uncompressed_buffer)->   decompresses lzo/quicklz packets
	 rgb -(rgb)->               does conversion to BGR
	 scale -(scale)->           does rescaling
	 color -(color)->           applies color correction
	 img                        writes separate image files for each frame
	*/

	ps_bufferattr_t attr;
	ps_buffer_t uncompressed_buffer, compressed_buffer,
		    rgb_buffer, color_buffer, scale_buffer;
	img_t img;
	color_t color;
	scale_t scale;
	unpack_t unpack;
	rgb_t rgb;
	int ret = 0;

	if ((ret = ps_bufferattr_init(&attr)))
		goto err;

	/* buffers */
	if ((ret = ps_bufferattr_setsize(&attr, play->compressed_size)))
		goto err;
	if ((ret = ps_buffer_init(&compressed_buffer, &attr)))
		goto err;

	if ((ret = ps_bufferattr_setsize(&attr, play->uncompressed_size)))
		goto err;
	if ((ret = ps_buffer_init(&uncompressed_buffer, &attr)))
		goto err;
	if ((ret = ps_buffer_init(&color_buffer, &attr)))
		goto err;
	if ((ret = ps_buffer_init(&rgb_buffer, &attr)))
		goto err;
	if ((ret = ps_buffer_init(&scale_buffer, &attr)))
		goto err;

	if ((ret = ps_bufferattr_destroy(&attr)))
		goto err;

	/* filters */
	if ((ret = unpack_init(&unpack, &play->glc)))
		goto err;
	if ((ret = rgb_init(&rgb, &play->glc)))
		goto err;
	if ((ret = scale_init(&scale, &play->glc)))
		goto err;
	if (play->scale_width && play->scale_height)
		scale_set_size(scale, play->scale_width, play->scale_height);
	else
		scale_set_scale(scale, play->scale_factor);
	if ((ret = color_init(&color, &play->glc)))
		goto err;
	if (play->override_color_correction)
		color_override(color, play->brightness, play->contrast,
			       play->red_gamma, play->green_gamma, play->blue_gamma);
	if ((ret = img_init(&img, &play->glc)))
		goto err;
	img_set_filename(img, play->export_filename_format);
	img_set_stream_id(img, play->export_video_id);
	img_set_format(img, play->img_format);
	img_set_fps(img, play->fps);

	/* pipeline... */
	if ((ret = unpack_process_start(unpack, &compressed_buffer, &uncompressed_buffer)))
		goto err;
	if ((ret = rgb_process_start(rgb, &uncompressed_buffer, &rgb_buffer)))
		goto err;
	if ((ret = scale_process_start(scale, &rgb_buffer, &scale_buffer)))
		goto err;
	if ((ret = color_process_start(color, &scale_buffer, &color_buffer)))
		goto err;
	if ((ret = img_process_start(img, &color_buffer)))
		goto err;

	/* ok, read the file */
	if ((ret = file_read(play->file, &compressed_buffer)))
		goto err;

	/* wait 'till its done and clean up the mess... */
	if ((ret = img_process_wait(img)))
		goto err;
	if ((ret = color_process_wait(color)))
		goto err;
	if ((ret = scale_process_wait(scale)))
		goto err;
	if ((ret = rgb_process_wait(rgb)))
		goto err;
	if ((ret = unpack_process_wait(unpack)))
		goto err;

	unpack_destroy(unpack);
	rgb_destroy(rgb);
	scale_destroy(scale);
	color_destroy(color);
	img_destroy(img);

	ps_buffer_destroy(&compressed_buffer);
	ps_buffer_destroy(&uncompressed_buffer);
	ps_buffer_destroy(&color_buffer);
	ps_buffer_destroy(&scale_buffer);
	ps_buffer_destroy(&rgb_buffer);

	return 0;
err:
	fprintf(stderr, "exporting images failed: %s (%d)\n", strerror(ret), ret);
	return ret;
}
Exemple #22
0
static int write_file(struct img_pixmap *img, struct img_io *io)
{
	png_struct *png;
	png_info *info;
	png_text txt;
	struct img_pixmap tmpimg;
	unsigned char **rows;
	unsigned char *pixptr;
	int i, coltype;

	img_init(&tmpimg);

	if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
		return -1;
	}
	if(!(info = png_create_info_struct(png))) {
		png_destroy_write_struct(&png, 0);
		return -1;
	}

	/* if the input image is floating-point, we need to convert it to integer */
	if(img_is_float(img)) {
		if(img_copy(&tmpimg, img) == -1) {
			return -1;
		}
		if(img_to_integer(&tmpimg) == -1) {
			img_destroy(&tmpimg);
			return -1;
		}
		img = &tmpimg;
	}

	txt.compression = PNG_TEXT_COMPRESSION_NONE;
	txt.key = "Software";
	txt.text = "libimago2";
	txt.text_length = 0;

	if(setjmp(png_jmpbuf(png))) {
		png_destroy_write_struct(&png, &info);
		img_destroy(&tmpimg);
		return -1;
	}
	png_set_write_fn(png, io, write_func, flush_func);

	coltype = fmt_to_png_type(img->fmt);
	png_set_IHDR(png, info, img->width, img->height, 8, coltype, PNG_INTERLACE_NONE,
			PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
	png_set_text(png, info, &txt, 1);

	if(!(rows = malloc(img->height * sizeof *rows))) {
		png_destroy_write_struct(&png, &info);
		img_destroy(&tmpimg);
		return -1;
	}

	pixptr = img->pixels;
	for(i=0; i<img->height; i++) {
		rows[i] = pixptr;
		pixptr += img->width * img->pixelsz;
	}
	png_set_rows(png, info, rows);

	png_write_png(png, info, 0, 0);
	png_write_end(png, info);
	png_destroy_write_struct(&png, &info);

	free(rows);

	img_destroy(&tmpimg);
	return 0;
}