void
image_upsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
{
	int32_t vx, vy;
#if !defined __i386__ && !defined __x86_64__
	int32_t rx, ry;
	pix vcol;

	if((pdest == NULL) || (psrc == NULL))
		return;

	for(vy = 0; vy < height; vy++)
	{
		for(vx = 0; vx < width; vx++)
		{
			rx = ((vx * psrc->width) / width);
			ry = ((vy * psrc->height) / height);
			vcol = get_pix(psrc, rx, ry);
#else
	pix   vcol,vcol1,vcol2,vcol3,vcol4;
	float rx,ry;
	float width_scale, height_scale;
	float x_dist, y_dist;

	width_scale  = (float)psrc->width  / (float)width;
	height_scale = (float)psrc->height / (float)height;

	for(vy = 0;vy < height; vy++)
	{
		for(vx = 0;vx < width; vx++)
		{
			rx = vx * width_scale;
			ry = vy * height_scale;
			vcol1 = get_pix(psrc, (int32_t)rx, (int32_t)ry);
			vcol2 = get_pix(psrc, ((int32_t)rx)+1, (int32_t)ry);
			vcol3 = get_pix(psrc, (int32_t)rx, ((int32_t)ry)+1);
			vcol4 = get_pix(psrc, ((int32_t)rx)+1, ((int32_t)ry)+1);

			x_dist = rx - ((float)((int32_t)rx));
			y_dist = ry - ((float)((int32_t)ry));
			vcol = COL_FULL( (uint8_t)((COL_RED(vcol1)*(1.0-x_dist)
			                  + COL_RED(vcol2)*(x_dist))*(1.0-y_dist)
			                  + (COL_RED(vcol3)*(1.0-x_dist)
			                  + COL_RED(vcol4)*(x_dist))*(y_dist)),
			                 (uint8_t)((COL_GREEN(vcol1)*(1.0-x_dist)
			                  + COL_GREEN(vcol2)*(x_dist))*(1.0-y_dist)
			                  + (COL_GREEN(vcol3)*(1.0-x_dist)
			                  + COL_GREEN(vcol4)*(x_dist))*(y_dist)),
			                 (uint8_t)((COL_BLUE(vcol1)*(1.0-x_dist)
			                  + COL_BLUE(vcol2)*(x_dist))*(1.0-y_dist)
			                  + (COL_BLUE(vcol3)*(1.0-x_dist)
			                  + COL_BLUE(vcol4)*(x_dist))*(y_dist)),
			                 (uint8_t)((COL_ALPHA(vcol1)*(1.0-x_dist)
			                  + COL_ALPHA(vcol2)*(x_dist))*(1.0-y_dist)
			                  + (COL_ALPHA(vcol3)*(1.0-x_dist)
			                  + COL_ALPHA(vcol4)*(x_dist))*(y_dist))
			               );
#endif
			put_pix_alpha_replace(pdest, vx, vy, vcol);
		}
	}
}

void
image_downsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height)
{
	int32_t vx, vy;
	pix vcol;
	int32_t i, j;
#if !defined __i386__ && !defined __x86_64__
	int32_t rx, ry, rx_next, ry_next;
	int red, green, blue, alpha;
	int factor;

	if((pdest == NULL) || (psrc == NULL))
		return;

	for(vy = 0; vy < height; vy++)
	{
		for(vx = 0; vx < width; vx++)
		{

			rx = ((vx * psrc->width) / width);
			ry = ((vy * psrc->height) / height);

			red = green = blue = alpha = 0;

			rx_next = rx + (psrc->width / width);
			ry_next = ry + (psrc->width / width);
			factor = 0;

			for( j = rx; j < rx_next; j++)
			{
				for( i = ry; i < ry_next; i++)
				{
					factor += 1;
					vcol = get_pix(psrc, j, i);

					red   += COL_RED(vcol);
					green += COL_GREEN(vcol);
					blue  += COL_BLUE(vcol);
					alpha += COL_ALPHA(vcol);
				}
			}

			red   /= factor;
			green /= factor;
			blue  /= factor;
			alpha /= factor;

			/* on sature les valeurs */
			red   = (red   > 255) ? 255 : ((red   < 0) ? 0 : red  );
			green = (green > 255) ? 255 : ((green < 0) ? 0 : green);
			blue  = (blue  > 255) ? 255 : ((blue  < 0) ? 0 : blue );
			alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha);
#else
	float rx,ry;
	float width_scale, height_scale;
	float red, green, blue, alpha;
	int32_t half_square_width, half_square_height;
	float round_width, round_height;

	if( (pdest == NULL) || (psrc == NULL) )
		return;

	width_scale  = (float)psrc->width  / (float)width;
	height_scale = (float)psrc->height / (float)height;

	half_square_width  = (int32_t)(width_scale  / 2.0);
	half_square_height = (int32_t)(height_scale / 2.0);
	round_width  = (width_scale  / 2.0) - (float)half_square_width;
	round_height = (height_scale / 2.0) - (float)half_square_height;
	if(round_width  > 0.0)
		half_square_width++;
	else
		round_width = 1.0;
	if(round_height > 0.0)
		half_square_height++;
	else
		round_height = 1.0;

	for(vy = 0;vy < height; vy++)  
	{
		for(vx = 0;vx < width; vx++)
		{
			rx = vx * width_scale;
			ry = vy * height_scale;
			vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry);

			red = green = blue = alpha = 0.0;

			for(j=0;j<half_square_height<<1;j++)
			{
				for(i=0;i<half_square_width<<1;i++)
				{
					vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i,
					                     ((int32_t)ry)-half_square_height+j);
          
					if(((j == 0) || (j == (half_square_height<<1)-1)) && 
					   ((i == 0) || (i == (half_square_width<<1)-1)))
					{
						red   += round_width*round_height*(float)COL_RED  (vcol);
						green += round_width*round_height*(float)COL_GREEN(vcol);
						blue  += round_width*round_height*(float)COL_BLUE (vcol);
						alpha += round_width*round_height*(float)COL_ALPHA(vcol);
					}
					else if((j == 0) || (j == (half_square_height<<1)-1))
					{
						red   += round_height*(float)COL_RED  (vcol);
						green += round_height*(float)COL_GREEN(vcol);
						blue  += round_height*(float)COL_BLUE (vcol);
						alpha += round_height*(float)COL_ALPHA(vcol);
					}
					else if((i == 0) || (i == (half_square_width<<1)-1))
					{
						red   += round_width*(float)COL_RED  (vcol);
						green += round_width*(float)COL_GREEN(vcol);
						blue  += round_width*(float)COL_BLUE (vcol);
						alpha += round_width*(float)COL_ALPHA(vcol);
					}
					else
					{
						red   += (float)COL_RED  (vcol);
						green += (float)COL_GREEN(vcol);
						blue  += (float)COL_BLUE (vcol);
						alpha += (float)COL_ALPHA(vcol);
					}
				}
			}
      
			red   /= width_scale*height_scale;
			green /= width_scale*height_scale;
			blue  /= width_scale*height_scale;
			alpha /= width_scale*height_scale;
      
			/* on sature les valeurs */
			red   = (red   > 255.0)? 255.0 : ((red   < 0.0)? 0.0:red  );
			green = (green > 255.0)? 255.0 : ((green < 0.0)? 0.0:green);
			blue  = (blue  > 255.0)? 255.0 : ((blue  < 0.0)? 0.0:blue );
			alpha = (alpha > 255.0)? 255.0 : ((alpha < 0.0)? 0.0:alpha);
#endif
			put_pix_alpha_replace(pdest, vx, vy,
					      COL_FULL((uint8_t)red, (uint8_t)green, (uint8_t)blue, (uint8_t)alpha));
		}
	}
}

image_s *
image_resize(image_s * src_image, int32_t width, int32_t height)
{
	image_s * dst_image;

	dst_image = image_new(width, height);
	if( !dst_image )
		return NULL;
	if( (src_image->width < width) || (src_image->height < height) )
		image_upsize(dst_image, src_image, width, height);
	else
		image_downsize(dst_image, src_image, width, height);

	return dst_image;
}


unsigned char *
image_save_to_jpeg_buf(image_s * pimage, int * size)
{
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	JSAMPROW row_pointer[1];
	int row_stride;
	char *data;
	int i, x;
	struct my_dst_mgr dst;

	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);
	jpeg_memory_dest(&cinfo, &dst);
	cinfo.image_width = pimage->width;
	cinfo.image_height = pimage->height;
	cinfo.input_components = 3;
	cinfo.in_color_space = JCS_RGB;
	jpeg_set_defaults(&cinfo);
	jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
	jpeg_start_compress(&cinfo, TRUE);
	row_stride = cinfo.image_width * 3;
	if((data = malloc(row_stride)) == NULL)
	{
		DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
		if (dst.buf)
			free(dst.buf);
		jpeg_destroy_compress(&cinfo);
		return NULL;
	}
	i = 0;
	while(cinfo.next_scanline < cinfo.image_height)
	{
		for(x = 0; x < pimage->width; x++)
		{
			data[x * 3]     = COL_RED(pimage->buf[i]);
			data[x * 3 + 1] = COL_GREEN(pimage->buf[i]);
			data[x * 3 + 2] = COL_BLUE(pimage->buf[i]);
			i++;
		}
		row_pointer[0] = (unsigned char *)data;
		jpeg_write_scanlines(&cinfo, row_pointer, 1);
	}
	jpeg_finish_compress(&cinfo);
	*size = dst.used;
	free(data);
	jpeg_destroy_compress(&cinfo);

	return dst.buf;
}

char *
image_save_to_jpeg_file(image_s * pimage, char * path)
{
	int nwritten, size = 0;
	unsigned char * buf;
	FILE * dst_file;

	buf = image_save_to_jpeg_buf(pimage, &size);
	if( !buf )
		return NULL;
 	dst_file = fopen(path, "w");
	if( !dst_file )
	{
		free(buf);
		return NULL;
	}
	nwritten = fwrite(buf, 1, size, dst_file);
	fclose(dst_file);
	free(buf);

	return (nwritten == size) ? path : NULL;
}
Exemple #2
0
void
image_downsize_gd(image *im)
{
  int x, y;
  float sy1, sy2, sx1, sx2;
  int dstX = 0, dstY = 0, srcX = 0, srcY = 0;
  float width_scale, height_scale;
  
  int dstW = im->target_width;
  int dstH = im->target_height;
  int srcW = im->width;
  int srcH = im->height;
  
  if (im->height_padding) {
    dstY = im->height_padding;
    dstH = im->height_inner;
  }
  
  if (im->width_padding) {
    dstX = im->width_padding;
    dstW = im->width_inner;
  }
  
  width_scale = (float)srcW / dstW;
  height_scale = (float)srcH / dstH;
  
  for (y = dstY; (y < dstY + dstH); y++) {
    sy1 = (float)(y - dstY) * height_scale;
    sy2 = (float)((y + 1) - dstY) * height_scale;
    
    for (x = dstX; (x < dstX + dstW); x++) {
      float sx, sy;
  	  float spixels = 0;
  	  float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
  	  
  	  if (!im->has_alpha)
        alpha = 255.0;
  	  
  	  sx1 = (float)(x - dstX) * width_scale;
  	  sx2 = (float)((x + 1) - dstX) * width_scale;
  	  sy = sy1;
  	  
      //DEBUG_TRACE("sx1 %.2f, sx2 %.2f, sy1 %.2f, sy2 %.2f\n", sx1, sx2, sy1, sy2); 
  	  
  	  do {
        float yportion;
        
        //DEBUG_TRACE("  yportion(sy %.2f, sy1 %.2f, sy2 %.2f) = ", sy, sy1, sy2);
        if (floor2(sy) == floor2(sy1)) {
          yportion = 1.0 - (sy - floor2(sy));
    		  if (yportion > sy2 - sy1) {
            yportion = sy2 - sy1;
    		  }
    		  sy = floor2(sy);
    		}
    		else if (sy == floor2(sy2)) {
          yportion = sy2 - floor2(sy2);
        }
        else {
          yportion = 1.0;
        }
        //DEBUG_TRACE("%.2f\n", yportion);
        
        sx = sx1;
        
        do {
          float xportion = 1.0;
    		  float pcontribution;
    		  pix p;

    		  //DEBUG_TRACE("  xportion(sx %.2f, sx1 %.2f, sx2 %.2f) = ", sx, sx1, sx2);
    		  if (floor2(sx) == floor2(sx1)) {
    	      xportion = 1.0 - (sx - floor2(sx));
    	      if (xportion > sx2 - sx1)	{
              xportion = sx2 - sx1;
    			  }
    		    sx = floor2(sx);
    		  }
    		  else if (sx == floor2(sx2)) {
            xportion = sx2 - floor2(sx2);
          }
    		  else {
    		    xportion = 1.0;
    		  }
    		  //DEBUG_TRACE("%.2f\n", xportion);
  		    
    		  pcontribution = xportion * yportion;
  		  
    		  p = get_pix(im, (int32_t)sx + srcX, (int32_t)sy + srcY);
    		  
    		  /*
    		  DEBUG_TRACE("  merging with pix %d, %d: src %x (%d %d %d %d), pcontribution %.2f\n",
            (int32_t)sx + srcX, (int32_t)sy + srcY,
            p, COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p), pcontribution);
          */
  		    
    		  red   += COL_RED(p)   * pcontribution;
    		  green += COL_GREEN(p) * pcontribution;
    		  blue  += COL_BLUE(p)  * pcontribution;
    		  
    		  if (im->has_alpha)
    		    alpha += COL_ALPHA(p) * pcontribution;
    		  
    		  spixels += pcontribution;
    		  sx += 1.0;
    		} while (sx < sx2);
    		
    		sy += 1.0;
      } while (sy < sy2);
      
      if (spixels != 0.0) {
        //DEBUG_TRACE("  rgba (%.2f %.2f %.2f %.2f) spixels %.2f\n", red, green, blue, alpha, spixels);
        spixels = 1 / spixels;
	      red   *= spixels;
	      green *= spixels;
	      blue  *= spixels;
	      
	      if (im->has_alpha)
	        alpha *= spixels;
	    }
	    
	    /* Clamping to allow for rounding errors above */
      if (red > 255.0)   red = 255.0;
      if (green > 255.0) green = 255.0;
      if (blue > 255.0)  blue = 255.0;
      if (im->has_alpha && alpha > 255.0) alpha = 255.0;
      
      /*
      DEBUG_TRACE("  -> %d, %d %x (%d %d %d %d)\n",
        x, y, COL_FULL((int)red, (int)green, (int)blue, (int)alpha),
        (int)red, (int)green, (int)blue, (int)alpha);
      */
      
      if (im->orientation != ORIENTATION_NORMAL) {
        int ox, oy; // new destination pixel coordinates after rotating
        
        image_get_rotated_coords(im, x, y, &ox, &oy);
        
        if (im->orientation >= 5) {
          // 90 and 270 rotations, width/height are swapped so we have to use alternate put_pix method
          put_pix_rotated(
            im, ox, oy, im->target_height,
            COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
          );
        }
        else {
          put_pix(
            im, ox, oy,
            COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
          );
        }
      }
      else {
        put_pix(
          im, x, y,
          COL_FULL(ROUND_FLOAT_TO_INT(red), ROUND_FLOAT_TO_INT(green), ROUND_FLOAT_TO_INT(blue), ROUND_FLOAT_TO_INT(alpha))
        );
      }
    }
	}
}
Exemple #3
0
void
image_downsize_gd_fixed_point(image *im)
{
  int x, y;
  fixed_t sy1, sy2, sx1, sx2;
  int dstX = 0, dstY = 0, srcX = 0, srcY = 0;
  fixed_t width_scale, height_scale;
  
  int dstW = im->target_width;
  int dstH = im->target_height;
  int srcW = im->width;
  int srcH = im->height;
  
  if (im->height_padding) {
    dstY = im->height_padding;
    dstH = im->height_inner;
  }
  
  if (im->width_padding) {
    dstX = im->width_padding;
    dstW = im->width_inner;
  }
  
  width_scale = fixed_div(int_to_fixed(srcW), int_to_fixed(dstW));
  height_scale = fixed_div(int_to_fixed(srcH), int_to_fixed(dstH));
  
  for (y = dstY; (y < dstY + dstH); y++) {
    sy1 = fixed_mul(int_to_fixed(y - dstY), height_scale);
    sy2 = fixed_mul(int_to_fixed((y + 1) - dstY), height_scale);
    
    for (x = dstX; (x < dstX + dstW); x++) {
      fixed_t sx, sy;
  	  fixed_t spixels = 0;
  	  fixed_t red = 0, green = 0, blue = 0, alpha = 0;
  	  
  	  if (!im->has_alpha)
        alpha = FIXED_255;
  	  
      sx1 = fixed_mul(int_to_fixed(x - dstX), width_scale);
      sx2 = fixed_mul(int_to_fixed((x + 1) - dstX), width_scale);  	  
  	  sy = sy1;
  	  
  	  /*
      DEBUG_TRACE("sx1 %f, sx2 %f, sy1 %f, sy2 %f\n",
        fixed_to_float(sx1), fixed_to_float(sx2), fixed_to_float(sy1), fixed_to_float(sy2));
      */
  	  
  	  do {
        fixed_t yportion;
        
        //DEBUG_TRACE("  yportion(sy %f, sy1 %f, sy2 %f) = ", fixed_to_float(sy), fixed_to_float(sy1), fixed_to_float(sy2));
        
        if (fixed_floor(sy) == fixed_floor(sy1)) {
          yportion = FIXED_1 - (sy - fixed_floor(sy));
    		  if (yportion > sy2 - sy1) {
            yportion = sy2 - sy1;
    		  }
    		  sy = fixed_floor(sy);
    		}
    		else if (sy == fixed_floor(sy2)) {
          yportion = sy2 - fixed_floor(sy2);
        }
        else {
          yportion = FIXED_1;
        }
        
        //DEBUG_TRACE("%f\n", fixed_to_float(yportion));
        
        sx = sx1;
        
        do {
          fixed_t xportion;
    		  fixed_t pcontribution;
    		  pix p;
    		  
    		  //DEBUG_TRACE("  xportion(sx %f, sx1 %f, sx2 %f) = ", fixed_to_float(sx), fixed_to_float(sx1), fixed_to_float(sx2));
  		  
    		  if (fixed_floor(sx) == fixed_floor(sx1)) {
    	      xportion = FIXED_1 - (sx - fixed_floor(sx));
    	      if (xportion > sx2 - sx1)	{
              xportion = sx2 - sx1;
    			  }
    		    sx = fixed_floor(sx);
    		  }
    		  else if (sx == fixed_floor(sx2)) {
            xportion = sx2 - fixed_floor(sx2);
          }
    		  else {
    		    xportion = FIXED_1;
    		  }
    		  
    		  //DEBUG_TRACE("%f\n", fixed_to_float(xportion));
  		  
    		  pcontribution = fixed_mul(xportion, yportion);
  		  
    		  p = get_pix(im, fixed_to_int(sx + srcX), fixed_to_int(sy + srcY));
    		  
    		  /*
    		  DEBUG_TRACE("  merging with pix %d, %d: src %x (%d %d %d %d), pcontribution %f\n",
            fixed_to_int(sx + srcX), fixed_to_int(sy + srcY),
            p, COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p), fixed_to_float(pcontribution));
          */
  		    
          red   += fixed_mul(int_to_fixed(COL_RED(p)), pcontribution);
          green += fixed_mul(int_to_fixed(COL_GREEN(p)), pcontribution);
    		  blue  += fixed_mul(int_to_fixed(COL_BLUE(p)), pcontribution);
    		  
    		  if (im->has_alpha)
    		    alpha += fixed_mul(int_to_fixed(COL_ALPHA(p)), pcontribution);
    		  
    		  spixels += pcontribution;
    		  sx += FIXED_1;
    		} while (sx < sx2);
    		
        sy += FIXED_1;
      } while (sy < sy2);
      
  	  // If rgba get too large for the fixed-point representation, fallback to the floating point routine
		  // This should only happen with very large images
		  if (red < 0 || green < 0 || blue < 0 || alpha < 0) {
        warn("fixed-point overflow: %d %d %d %d\n", red, green, blue, alpha);
        return image_downsize_gd(im);
      }
      
      if (spixels != 0) {
        /*
        DEBUG_TRACE("  rgba (%f %f %f %f) spixels %f\n",
          fixed_to_float(red), fixed_to_float(green), fixed_to_float(blue), fixed_to_float(alpha), fixed_to_float(spixels));
        */
        
        spixels = fixed_div(FIXED_1, spixels);
        
        red   = fixed_mul(red, spixels);
        green = fixed_mul(green, spixels);
        blue  = fixed_mul(blue, spixels);
        
        if (im->has_alpha)
          alpha = fixed_mul(alpha, spixels);
	    }
	    
	    /* Clamping to allow for rounding errors above */
      if (red > FIXED_255)   red = FIXED_255;
      if (green > FIXED_255) green = FIXED_255;
      if (blue > FIXED_255)  blue = FIXED_255;
      if (im->has_alpha && alpha > FIXED_255) alpha = FIXED_255;
      
      /*
      DEBUG_TRACE("  -> %d, %d %x (%d %d %d %d)\n",
        x, y, COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha)),
        fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha));
      */
      
      if (im->orientation != ORIENTATION_NORMAL) {
        int ox, oy; // new destination pixel coordinates after rotating
        
        image_get_rotated_coords(im, x, y, &ox, &oy);
        
        if (im->orientation >= 5) {
          // 90 and 270 rotations, width/height are swapped so we have to use alternate put_pix method
          put_pix_rotated(
            im, ox, oy, im->target_height,
            COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
          );
        }
        else {
          put_pix(
            im, ox, oy,
            COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
          );
        }
      }
      else {
        put_pix(
          im, x, y,
          COL_FULL(fixed_to_int(red), fixed_to_int(green), fixed_to_int(blue), fixed_to_int(alpha))
        );
      }
	  }
	}
}
int image_png_compress(MediaScanImage *i, MediaScanThumbSpec *spec) {
  int j, x, y;
  int color_space = PNG_COLOR_TYPE_RGB_ALPHA;
  volatile unsigned char *ptr = NULL;
  png_structp png_ptr;
  png_infop info_ptr;
  Buffer *buf;

  if (!i->_pixbuf_size) {
    LOG_WARN("PNG compression requires pixbuf data (%s)\n", i->path);
    return 0;
  }

  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (!png_ptr) {
    FATAL("Could not initialize libpng\n");
  }

  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_write_struct(&png_ptr, NULL);
    FATAL("Could not initialize libpng\n");
  }

  // Initialize buffer for compressed data
  buf = (Buffer *)malloc(sizeof(Buffer));
  buffer_init(buf, BUF_SIZE);
  i->_dbuf = (void *)buf;

  png_set_write_fn(png_ptr, buf, image_png_write_buf, image_png_flush_buf);

  if (setjmp(png_jmpbuf(png_ptr))) {
    if (ptr != NULL)
      free((void *)ptr);
    return 0;
  }

  // Match output color space with input file
  switch (i->channels) {
    case 4:
    case 3:
      LOG_DEBUG("PNG output color space set to RGBA\n");
      color_space = PNG_COLOR_TYPE_RGB_ALPHA;
      break;
    case 2:
    case 1:
      LOG_DEBUG("PNG output color space set to gray alpha\n");
      color_space = PNG_COLOR_TYPE_GRAY_ALPHA;
      break;
  }

  png_set_IHDR(png_ptr, info_ptr, spec->width, spec->height, 8, color_space,
               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

  png_write_info(png_ptr, info_ptr);

  ptr = (unsigned char *)malloc(png_get_rowbytes(png_ptr, info_ptr));

  j = 0;

  if (color_space == PNG_COLOR_TYPE_GRAY_ALPHA) {
    for (y = 0; y < spec->height; y++) {
      for (x = 0; x < spec->width; x++) {
        ptr[x * 2] = COL_BLUE(i->_pixbuf[j]);
        ptr[x * 2 + 1] = COL_ALPHA(i->_pixbuf[j]);
        j++;
      }
      png_write_row(png_ptr, (png_bytep) ptr);
    }
  }
  else {                        // RGB
    for (y = 0; y < spec->height; y++) {
      for (x = 0; x < spec->width; x++) {
        ptr[x * 4] = COL_RED(i->_pixbuf[j]);
        ptr[x * 4 + 1] = COL_GREEN(i->_pixbuf[j]);
        ptr[x * 4 + 2] = COL_BLUE(i->_pixbuf[j]);
        ptr[x * 4 + 3] = COL_ALPHA(i->_pixbuf[j]);
        j++;
      }
      png_write_row(png_ptr, (png_bytep) ptr);
    }
  }

  free((void *)ptr);

  png_write_end(png_ptr, info_ptr);

  png_destroy_write_struct(&png_ptr, &info_ptr);

  return 1;
}
// Compress the data from i->_pixbuf to i->data.
// Uses libjpeg-turbo if available (JCS_EXTENSIONS) for better performance
int image_jpeg_compress(MediaScanImage *i, MediaScanThumbSpec *spec) {
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  struct buf_dst_mgr dst;
  int quality = spec->jpeg_quality;
  int x;
#ifdef JCS_EXTENSIONS
  JSAMPROW *data = NULL;
#else
  volatile unsigned char *data = NULL;  // volatile = won't be rolled back if longjmp is called
  JSAMPROW row_pointer[1];
  int y, row_stride;
#endif

  if (!i->_pixbuf_size) {
    LOG_WARN("JPEG compression requires pixbuf data (%s)\n", i->path);
    exit(-1);
    return 0;
  }

  if (!quality)
    quality = DEFAULT_JPEG_QUALITY;

  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
  image_jpeg_buf_dest(&cinfo, &dst);

  cinfo.image_width = i->width;
  cinfo.image_height = i->height;
  cinfo.input_components = 3;
  cinfo.in_color_space = JCS_RGB; // output is always RGB even if source was grayscale

  if (setjmp(setjmp_buffer)) {
    if (data != NULL) {
      LOG_MEM("destroy JPEG data row @ %p\n", data);
      free((void *)data);
    }
    return 0;
  }

#ifdef JCS_EXTENSIONS
  // Use libjpeg-turbo support for direct reading from source buffer
  cinfo.input_components = 4;
  cinfo.in_color_space = JCS_EXT_XBGR;
#endif

  jpeg_set_defaults(&cinfo);
  jpeg_set_quality(&cinfo, quality, TRUE);
  jpeg_start_compress(&cinfo, TRUE);

#ifdef JCS_EXTENSIONS
  data = (JSAMPROW *)malloc(i->height * sizeof(JSAMPROW));
  LOG_MEM("new JPEG data row @ %p\n", data);

  for (x = 0; x < i->height; x++)
    data[x] = (JSAMPROW)&i->_pixbuf[x * i->width];

  while (cinfo.next_scanline < cinfo.image_height) {
    jpeg_write_scanlines(&cinfo, &data[cinfo.next_scanline], cinfo.image_height - cinfo.next_scanline);
  }

#else
  // Normal libjpeg
  row_stride = cinfo.image_width * 3;
  data = (unsigned char *)malloc(row_stride);
  LOG_MEM("new JPEG data row @ %p\n", data);

  y = 0;
  while (cinfo.next_scanline < cinfo.image_height) {
    for (x = 0; x < cinfo.image_width; x++) {
      data[x + x + x] = COL_RED(i->_pixbuf[y]);
      data[x + x + x + 1] = COL_GREEN(i->_pixbuf[y]);
      data[x + x + x + 2] = COL_BLUE(i->_pixbuf[y]);
      y++;
    }
    row_pointer[0] = (unsigned char *)data;
    jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }
#endif

  jpeg_finish_compress(&cinfo);

  LOG_MEM("destroy JPEG data row @ %p\n", data);
  free((void *)data);

  jpeg_destroy_compress(&cinfo);

  // Attach compressed buffer to image
  i->_dbuf = (void *)dst.dbuf;

  return 1;
}
void write_png_file(char* file_name) {
  int j, x, y;
  int color_space = PNG_COLOR_TYPE_RGB_ALPHA;
  volatile unsigned char *ptr = NULL;
  png_structp png_ptr;
  png_infop info_ptr;
//  Buffer *buf;
	FILE *fp = fopen(file_name, "wb");

	if (!result._thumbs[0]->_pixbuf_size) {
    LOG_WARN("PNG compression requires pixbuf data\n");
    return;
  }

  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (!png_ptr) {
    FATAL("Could not initialize libpng\n");
  }

  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    png_destroy_write_struct(&png_ptr, NULL);
    FATAL("Could not initialize libpng\n");
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    if (ptr != NULL)
      free((void *)ptr);
    return;
  }

  png_init_io(png_ptr, fp);

	/* write header */
	if (setjmp(png_jmpbuf(png_ptr)))
				abort_("[write_png_file] Error during writing header");


  // Match output color space with input file
	switch (result._thumbs[0]->channels) {
    case 4:
    case 3:
      LOG_DEBUG("PNG output color space set to RGBA\n");
      color_space = PNG_COLOR_TYPE_RGB_ALPHA;
      break;
    case 2:
    case 1:
      LOG_DEBUG("PNG output color space set to gray alpha\n");
      color_space = PNG_COLOR_TYPE_GRAY_ALPHA;
      break;
  }

  png_set_IHDR(png_ptr, info_ptr, result._thumbs[0]->width, result._thumbs[0]->height, 8, color_space,
               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

  png_write_info(png_ptr, info_ptr);

	/* write bytes */
	if (setjmp(png_jmpbuf(png_ptr)))
				abort_("[write_png_file] Error during writing bytes");


  ptr = (unsigned char *)malloc(png_get_rowbytes(png_ptr, info_ptr));

  j = 0;

  if (color_space == PNG_COLOR_TYPE_GRAY_ALPHA) {
    for (y = 0; y < result._thumbs[0]->height; y++) {
      for (x = 0; x < result._thumbs[0]->width; x++) {
        ptr[x * 2] = COL_BLUE(result._thumbs[0]->_pixbuf[j]);
        ptr[x * 2 + 1] = COL_ALPHA(result._thumbs[0]->_pixbuf[j]);
        j++;
      }
      png_write_row(png_ptr, (png_bytep) ptr);
    }
  }
  else {                        // RGB  
    for (y = 0; y < result._thumbs[0]->height; y++) {
      for (x = 0; x < result._thumbs[0]->width; x++) {
        ptr[x * 4] = COL_RED(result._thumbs[0]->_pixbuf[j]);
				ptr[x * 4 + 1] = COL_GREEN(result._thumbs[0]->_pixbuf[j]);
        ptr[x * 4 + 2] = COL_BLUE(result._thumbs[0]->_pixbuf[j]);
        ptr[x * 4 + 3] = COL_ALPHA(result._thumbs[0]->_pixbuf[j]);
        j++;
      }
      png_write_row(png_ptr, (png_bytep) ptr);
    }
  }

  free((void *)ptr);

	/* end write */
	if (setjmp(png_jmpbuf(png_ptr)))
				abort_("[write_png_file] Error during end of write");


  png_write_end(png_ptr, info_ptr);

  png_destroy_write_struct(&png_ptr, &info_ptr);

	fclose(fp);
}