mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-10-31 10:07:15 +08:00 
			
		
		
		
	 c1834faa23
			
		
	
	c1834faa23
	
	
	
		
			
			We revamped the plugin so it doesn't have extra dependencies. The issue is when images have external dependencies, forcing users to use something like docker to satisfy those but as we see these days, we can ship the viewer for heic as a wasm plugin existing client side and can focus on building a smaller footprint server
		
			
				
	
	
		
			189 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdint.h>
 | |
| #include <stdbool.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <jpeglib.h>
 | |
| #include <setjmp.h>
 | |
| 
 | |
| static bool write_preview(const uint8_t *buf, size_t len, int output);
 | |
| static bool write_thumbnail(const uint8_t *buf, size_t len, int output, int targetSize);
 | |
| 
 | |
| void raw_to_jpeg(int inputDesc, int outputDesc, int targetSize) {
 | |
|     FILE *in = fdopen(inputDesc, "rb");
 | |
|     if (!in) { perror("fdopen"); return; }
 | |
| 
 | |
|     uint8_t  chunk[4096];
 | |
|     uint8_t  last = 0, curr;
 | |
|     bool     dumping = false;
 | |
| 
 | |
|     uint8_t *cur_buf = NULL;
 | |
|     size_t   cur_len = 0, cur_cap = 0;
 | |
| 
 | |
|     while (!feof(in)) {
 | |
|         size_t n = fread(chunk, 1, sizeof(chunk), in);
 | |
|         if (n == 0) break;
 | |
| 
 | |
|         for (size_t i = 0; i < n; i++) {
 | |
|             curr = chunk[i];
 | |
| 
 | |
|             if (dumping == true && cur_len + 1 > cur_cap) {
 | |
|                 cur_cap = (cur_cap == 0 ? 4096 : cur_cap * 2);
 | |
|                 cur_buf = realloc(cur_buf, cur_cap);
 | |
|                 if (!cur_buf) {
 | |
|                     free(cur_buf);
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // start of jpeg
 | |
|             if (dumping == false && last == 0xFF && curr == 0xD8) {
 | |
|                 dumping = true;
 | |
|                 cur_cap = 4096;
 | |
|                 cur_len = 0;
 | |
|                 cur_buf = malloc(cur_cap);
 | |
|                 if (!cur_buf) return;
 | |
|                 cur_buf[cur_len++] = 0xFF;
 | |
|                 cur_buf[cur_len++] = 0xD8;
 | |
|             }
 | |
|             // end of jpeg
 | |
|             else if (dumping == true && last == 0xFF && curr == 0xD9) {
 | |
|                 cur_buf[cur_len++] = curr;
 | |
|                 if (targetSize > 0 && write_preview(cur_buf, cur_len, outputDesc)) {
 | |
|                     free(cur_buf);
 | |
|                     return;
 | |
|                 } else if (targetSize <= 0 && write_thumbnail(cur_buf, cur_len, outputDesc, -targetSize)) {
 | |
|                     free(cur_buf);
 | |
|                     return;
 | |
|                 }
 | |
|                 cur_buf = NULL;
 | |
|                 cur_len = cur_cap = 0;
 | |
|                 dumping = false;
 | |
|             }
 | |
|             // body of jpeg
 | |
|             else if (dumping == true) {
 | |
|                 cur_buf[cur_len++] = curr;
 | |
|             }
 | |
|             last = curr;
 | |
|         }
 | |
|     }
 | |
|     free(cur_buf);
 | |
| }
 | |
| 
 | |
| typedef struct filestash_raw_error_mgr {
 | |
|     struct jpeg_error_mgr pub;
 | |
|     jmp_buf jmp;
 | |
| } *filestash_raw_error_ptr;
 | |
| 
 | |
| static void filestash_raw_error_exit (j_common_ptr cinfo) {
 | |
|     filestash_raw_error_ptr filestash_err = (filestash_raw_error_ptr) cinfo->err;
 | |
|     longjmp(filestash_err->jmp, 1);
 | |
| }
 | |
| 
 | |
| static bool write_preview(const uint8_t *buf, size_t len, int output) {
 | |
|     struct jpeg_decompress_struct  cinfo;
 | |
|     struct filestash_raw_error_mgr jerr;
 | |
|     bool                           ok = true;
 | |
| 
 | |
|     jpeg_create_decompress(&cinfo);
 | |
|     jpeg_mem_src(&cinfo, buf, len);
 | |
|     cinfo.err = jpeg_std_error(&jerr.pub);
 | |
|     jerr.pub.error_exit = filestash_raw_error_exit;
 | |
|     if (setjmp(jerr.jmp)) {
 | |
|         jpeg_destroy_decompress(&cinfo);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) ok = false;
 | |
|     else if (cinfo.image_width < 700) ok = false;
 | |
| 
 | |
|     jpeg_destroy_decompress(&cinfo);
 | |
|     if (ok == true && write(output, buf, len) != (ssize_t)len) {
 | |
|         perror("write");
 | |
|     }
 | |
|     return ok;
 | |
| }
 | |
| 
 | |
| static bool write_thumbnail(const uint8_t *buf, size_t len, int output, int targetSize) {
 | |
|     struct jpeg_decompress_struct dinfo;
 | |
|     struct filestash_raw_error_mgr jerr;
 | |
|     bool ok = true;
 | |
| 
 | |
|     jpeg_create_decompress(&dinfo);
 | |
|     jpeg_mem_src(&dinfo, buf, len);
 | |
|     dinfo.err           = jpeg_std_error(&jerr.pub);
 | |
|     jerr.pub.error_exit = filestash_raw_error_exit;
 | |
|     if (setjmp(jerr.jmp)) {
 | |
|         jpeg_destroy_decompress(&dinfo);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (jpeg_read_header(&dinfo, TRUE) != JPEG_HEADER_OK || dinfo.image_width < 500) {
 | |
|         jpeg_destroy_decompress(&dinfo);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (dinfo.image_width / 8 >= targetSize) {
 | |
|         dinfo.scale_num = 1;
 | |
|         dinfo.scale_denom = 8;
 | |
|     } else if (dinfo.image_width * 2 / 8 >= targetSize) {
 | |
|         dinfo.scale_num = 1;
 | |
|         dinfo.scale_denom = 4;
 | |
|     } else if (dinfo.image_width * 3 / 8 >= targetSize) {
 | |
|         dinfo.scale_num = 3;
 | |
|         dinfo.scale_denom = 8;
 | |
|     } else if (dinfo.image_width * 4 / 8 >= targetSize) {
 | |
|         dinfo.scale_num = 4;
 | |
|         dinfo.scale_denom = 8;
 | |
|     } else if (dinfo.image_width * 5 / 8 >= targetSize) {
 | |
|         dinfo.scale_num = 5;
 | |
|         dinfo.scale_denom = 8;
 | |
|     } else if (dinfo.image_width * 6 / 8 >= targetSize) {
 | |
|         dinfo.scale_num = 6;
 | |
|         dinfo.scale_denom = 8;
 | |
|     } else if (dinfo.image_width * 7 / 8 >= targetSize) {
 | |
|         dinfo.scale_num = 7;
 | |
|         dinfo.scale_denom = 8;
 | |
|     }
 | |
| 
 | |
|     jpeg_start_decompress(&dinfo);
 | |
|     size_t stride = dinfo.output_width * dinfo.output_components;
 | |
|     JSAMPARRAY rowbuf = dinfo.mem->alloc_sarray((j_common_ptr)&dinfo, JPOOL_IMAGE, stride, 1);
 | |
| 
 | |
|     struct jpeg_compress_struct cinfo;
 | |
|     struct jpeg_error_mgr       cerr;
 | |
| 
 | |
|     cinfo.err = jpeg_std_error(&cerr);
 | |
|     jpeg_create_compress(&cinfo);
 | |
| 
 | |
|     unsigned char *outbuf = NULL;
 | |
|     unsigned long  outlen = 0;
 | |
|     jpeg_mem_dest(&cinfo, &outbuf, &outlen);
 | |
| 
 | |
|     cinfo.image_width      = dinfo.output_width;
 | |
|     cinfo.image_height     = dinfo.output_height;
 | |
|     cinfo.input_components = dinfo.output_components;
 | |
|     cinfo.in_color_space   = dinfo.out_color_space;
 | |
| 
 | |
|     jpeg_set_defaults(&cinfo);
 | |
|     jpeg_set_quality(&cinfo, 70, TRUE);
 | |
|     jpeg_start_compress(&cinfo, TRUE);
 | |
|     while (cinfo.next_scanline < cinfo.image_height) {
 | |
|         jpeg_read_scanlines(&dinfo, rowbuf, 1);
 | |
|         jpeg_write_scanlines(&cinfo, rowbuf, 1);
 | |
|     }
 | |
|     jpeg_finish_compress(&cinfo);
 | |
|     jpeg_destroy_compress(&cinfo);
 | |
| 
 | |
|     jpeg_finish_decompress(&dinfo);
 | |
|     jpeg_destroy_decompress(&dinfo);
 | |
| 
 | |
|     if (write(output, outbuf, outlen) != (ssize_t)outlen) {
 | |
|         perror("write");
 | |
|         ok = false;
 | |
|     }
 | |
|     free(outbuf);
 | |
|     return ok;
 | |
| }
 |