mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-10-31 10:07:15 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			133 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <png.h>
 | |
| #include <webp/encode.h>
 | |
| #include "utils.h"
 | |
| 
 | |
| void png_read_error(png_structp png_ptr, png_const_charp error_msg) {
 | |
|   longjmp(png_jmpbuf(png_ptr), 1);
 | |
| }
 | |
| 
 | |
| void png_read_warning(png_structp png_ptr, png_const_charp warning_msg) {
 | |
|   longjmp(png_jmpbuf(png_ptr), 1);
 | |
| }
 | |
| 
 | |
| int png_to_webp(int inputDesc, int outputDesc, int targetSize) {
 | |
| #ifdef HAS_DEBUG
 | |
|   clock_t t;
 | |
|   t = clock();
 | |
| #endif
 | |
|   if (targetSize < 0 ) {
 | |
|     targetSize = -targetSize;
 | |
|   }
 | |
|   int status = 0;
 | |
|   FILE* input = fdopen(inputDesc, "rb");
 | |
|   FILE* output = fdopen(outputDesc, "wb");
 | |
|   if (!input || !output) {
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   // STEP1: setup png
 | |
|   png_structp png_ptr = NULL;
 | |
|   png_infop info_ptr = NULL;
 | |
|   if(!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_read_error, png_read_warning))) {
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT;
 | |
|   }
 | |
|   if (!(info_ptr = png_create_info_struct(png_ptr))) {
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT_A;
 | |
|   }
 | |
|   if (setjmp(png_jmpbuf(png_ptr))) {
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT_B;
 | |
|   }
 | |
|   png_init_io(png_ptr, input);
 | |
|   png_read_info(png_ptr, info_ptr);
 | |
|   png_set_strip_alpha(png_ptr);
 | |
|   png_uint_32 width = png_get_image_width(png_ptr, info_ptr);
 | |
|   png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
 | |
|   png_byte color_type = png_get_color_type(png_ptr, info_ptr);
 | |
|   png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);
 | |
|   if (color_type == PNG_COLOR_TYPE_PALETTE) {
 | |
|     png_set_palette_to_rgb(png_ptr);
 | |
|   }
 | |
|   if (color_type == PNG_COLOR_TYPE_GRAY) {
 | |
|     png_set_expand_gray_1_2_4_to_8(png_ptr);
 | |
|   }
 | |
|   if (color_type & PNG_COLOR_MASK_ALPHA) {
 | |
|     png_set_strip_alpha(png_ptr);
 | |
|   }
 | |
|   png_read_update_info(png_ptr, info_ptr);
 | |
|   DEBUG("after png construct");
 | |
| 
 | |
|   // STEP2: process the image
 | |
|   int scale_factor = height > targetSize ? height / targetSize : 1;
 | |
|   png_uint_32 thumb_width = width / scale_factor;
 | |
|   png_uint_32 thumb_height = height / scale_factor;
 | |
| 
 | |
|   if (thumb_width == 0 || thumb_height == 0) {
 | |
|     ERROR("0 dimensions");
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT_B;
 | |
|   }
 | |
|   uint8_t* webp_image_data = (uint8_t*)malloc(thumb_width * thumb_height * 3);
 | |
|   if (!webp_image_data) {
 | |
|     ERROR("malloc error");
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT_B;
 | |
|   }
 | |
|   png_bytep row = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
 | |
|   if (!row) {
 | |
|     ERROR("malloc error");
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT_B;
 | |
|   }
 | |
|   DEBUG("after png malloc");
 | |
|   for (png_uint_32 y = 0; y < height; y++) {
 | |
|     png_read_row(png_ptr, row, NULL);
 | |
|     if (y % scale_factor == 0 && (y / scale_factor < thumb_height)) {
 | |
|       for (png_uint_32 x = 0; x < width; x += scale_factor) {
 | |
|         if (x / scale_factor < thumb_width) {
 | |
|           png_uint_32 thumb_x = x / scale_factor;
 | |
|           png_uint_32 thumb_y = y / scale_factor;
 | |
|           memcpy(webp_image_data + (thumb_y * thumb_width + thumb_x) * 3, row + x * 3, 3);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   DEBUG("after png process");
 | |
|   free(row);
 | |
|   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 | |
|   DEBUG("after png cleanup");
 | |
| 
 | |
|   // STEP3: save as webp
 | |
|   uint8_t* webp_output_data = NULL;
 | |
|   size_t webp_output_size = WebPEncodeRGB(webp_image_data, thumb_width, thumb_height, thumb_width * 3, 75, &webp_output_data);
 | |
|   free(webp_image_data);
 | |
|   DEBUG("after webp init");
 | |
|   if (webp_output_data == NULL) {
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT_B;
 | |
|   } else if (webp_output_size == 0) {
 | |
|     status = 1;
 | |
|     goto CLEANUP_AND_ABORT_C;
 | |
|   }
 | |
|   fwrite(webp_output_data, webp_output_size, 1, output);
 | |
|   fflush(output);
 | |
|   DEBUG("after webp written");
 | |
| 
 | |
|  CLEANUP_AND_ABORT_C:
 | |
|   if (webp_output_data != NULL) WebPFree(webp_output_data);
 | |
| 
 | |
|  CLEANUP_AND_ABORT_B:
 | |
|   if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
 | |
| 
 | |
|  CLEANUP_AND_ABORT_A:
 | |
|   if (png_ptr != NULL) png_destroy_read_struct(&png_ptr, (info_ptr != NULL) ? &info_ptr : NULL, NULL);
 | |
| 
 | |
|  CLEANUP_AND_ABORT:
 | |
|   return status;
 | |
| }
 | 
