1 /*
2    This file is part of harvid
3 
4    Copyright (C) 2008-2014 Robin Gareus <robin@gareus.org>
5 
6    This file contains some GPL source from vgrabbj by
7 	 Jens Gecius <devel@gecius.de>
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2, or (at your option)
12    any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 #ifdef HAVE_WINDOWS
23 #include <windows.h>
24 #define HAVE_BOOLEAN 1
25 #endif
26 
27 #include <sys/types.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <jpeglib.h>
32 #include <png.h>
33 
34 #include <dlog.h>
35 #include <vinfo.h> // harvid.h
36 #include "enums.h"
37 
38 #define JPEG_QUALITY 75
39 
write_jpeg(VInfo * ji,uint8_t * buffer,int quality,FILE * x)40 static int write_jpeg(VInfo *ji, uint8_t *buffer, int quality, FILE *x) {
41   uint8_t *line;
42   int n, y = 0, i, line_width;
43 
44   struct jpeg_compress_struct cjpeg;
45   struct jpeg_error_mgr jerr;
46   JSAMPROW row_ptr[1];
47 
48   line = malloc(ji->out_width * 3);
49   if (!line) {
50     dlog(DLOG_CRIT, "IMF: OUT OF MEMORY, Exiting...\n");
51     exit(1);
52   }
53   cjpeg.err = jpeg_std_error(&jerr);
54   jpeg_create_compress (&cjpeg);
55   cjpeg.image_width  = ji->out_width;
56   cjpeg.image_height = ji->out_height;
57   cjpeg.input_components = 3;
58   //cjpeg.smoothing_factor = 0; // 0..100
59   cjpeg.in_color_space = JCS_RGB;
60 
61   jpeg_set_defaults (&cjpeg);
62   jpeg_set_quality (&cjpeg, quality, TRUE);
63   cjpeg.dct_method = quality > 90? JDCT_DEFAULT : JDCT_FASTEST;
64 
65   jpeg_simple_progression(&cjpeg);
66 
67   jpeg_stdio_dest (&cjpeg, x);
68   jpeg_start_compress (&cjpeg, TRUE);
69   row_ptr[0] = line;
70   line_width = ji->out_width * 3;
71   n = 0;
72 
73   for (y = 0; y < ji->out_height; y++)
74     {
75       for (i = 0; i< line_width; i += 3)
76 	{
77 	  line[i]   = buffer[n];
78 	  line[i+1] = buffer[n+1];
79 	  line[i+2] = buffer[n+2];
80 	  n += 3;
81 	}
82       jpeg_write_scanlines (&cjpeg, row_ptr, 1);
83     }
84   jpeg_finish_compress (&cjpeg);
85   jpeg_destroy_compress (&cjpeg);
86   free(line);
87   return(0);
88 }
89 
write_png(VInfo * ji,uint8_t * image,FILE * x)90 static int write_png(VInfo *ji, uint8_t *image, FILE *x) {
91   register int y;
92   png_bytep rowpointers[ji->out_height];
93   png_infop info_ptr;
94   png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
95 						 NULL, NULL, NULL);
96 
97   if (!png_ptr)
98     return(1);
99   info_ptr = png_create_info_struct (png_ptr);
100   if (!info_ptr) {
101     png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
102     return(1);
103   }
104   if (setjmp(png_jmpbuf(png_ptr))) {
105   /* If we get here, we had a problem reading the file */
106     png_destroy_write_struct(&png_ptr, &info_ptr);
107     return (1);
108   }
109 
110   png_init_io (png_ptr, x);
111   png_set_IHDR (png_ptr, info_ptr, ji->out_width, ji->out_height,
112 		8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
113 		PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
114   png_write_info (png_ptr, info_ptr);
115   for (y = 0; y < ji->out_height; y++) {
116     rowpointers[y] = image + y*ji->out_width*3;
117   }
118   png_write_image(png_ptr, rowpointers);
119   png_write_end (png_ptr, info_ptr);
120   png_destroy_write_struct (&png_ptr, &info_ptr);
121   return(0);
122 }
123 
write_ppm(VInfo * ji,uint8_t * image,FILE * x)124 static int write_ppm(VInfo *ji, uint8_t *image, FILE *x) {
125 
126   fprintf(x, "P6\n%d %d\n255\n", ji->out_width, ji->out_height);
127   fwrite(image, ji->out_height, 3*ji->out_width, x);
128 
129   return(0);
130 }
131 
open_outfile(char * filename)132 static FILE *open_outfile(char *filename) {
133   if (!strcmp(filename, "-")) return stdout;
134   return fopen(filename, "w+");
135 }
136 
format_image(uint8_t ** out,int render_fmt,int misc_int,VInfo * ji,uint8_t * buf)137 size_t format_image(uint8_t **out, int render_fmt, int misc_int, VInfo *ji, uint8_t *buf) {
138 #ifdef HAVE_WINDOWS
139   char tfn[64] = "";
140 #endif
141 #ifdef __USE_XOPEN2K8
142   size_t rs = 0;
143   FILE *x = open_memstream((char**) out, &rs);
144 #else
145   FILE *x = tmpfile();
146 #endif
147 #ifdef HAVE_WINDOWS
148   if (!x) {
149     // wine and ancient version of windows don't support tmpfile()
150     // srand is per thread :(
151     struct timeval tv;
152     gettimeofday(&tv, NULL);
153     srand(tv.tv_sec * tv.tv_usec * 100000);
154     snprintf(tfn, sizeof(tfn), "harvid.tmp.%d", rand());
155     x = fopen(tfn, "w+b"); // we should really use open(tfn, O_RDWR | O_CREAT | O_EXCL, 0600); and fdopen
156   }
157 #endif
158   if (!x) {
159     dlog(LOG_ERR, "IMF: tmpfile() creation failed.\n");
160     return(0);
161   }
162 
163   switch (render_fmt) {
164     case 1:
165       {
166         if (misc_int < 5 || misc_int > 100)
167           misc_int = JPEG_QUALITY;
168       if (write_jpeg(ji, buf, misc_int, x))
169         dlog(LOG_ERR, "IMF: Could not write jpeg\n");
170       break;
171       }
172     case 2:
173       if (write_png(ji, buf, x))
174         dlog(LOG_ERR, "IMF: Could not write png\n");
175       break;
176     case 3:
177       if (write_ppm(ji, buf, x))
178         dlog(LOG_ERR, "IMF: Could not write ppm\n");
179       break;
180     default:
181         dlog(LOG_ERR, "IMF: Unknown outformat %d\n", render_fmt);
182         fclose(x);
183         return 0;
184       break;
185   }
186 #ifdef __USE_XOPEN2K8
187   fclose(x);
188   return rs;
189 #elif defined HAVE_WINDOWS
190   if (strlen(tfn) > 0) {
191     fclose(x);
192     x = fopen(tfn, "rb");
193   } else {
194     fflush(x);
195   }
196 #else
197   fflush(x);
198 #endif
199   /* re-read image from tmp-file */
200   fseek (x , 0 , SEEK_END);
201   long int rsize = ftell (x);
202   rewind(x);
203   if (fseek(x, 0L, SEEK_SET) < 0) {
204     dlog(LOG_WARNING, "IMF: fseek failed\n");
205   }
206   fflush(x);
207   *out = (uint8_t*) malloc(rsize*sizeof(uint8_t));
208   if (fread(*out, sizeof(char), rsize, x) != rsize) {
209     dlog(LOG_WARNING, "IMF: short read. - possibly incomplete image\n");
210   }
211   fclose(x);
212 #ifdef HAVE_WINDOWS
213   if (strlen(tfn) > 0) {
214     unlink(tfn);
215   }
216 #endif
217   return (rsize);
218 }
219 
write_image(char * file_name,int render_fmt,VInfo * ji,uint8_t * buf)220 void write_image(char *file_name, int render_fmt, VInfo *ji, uint8_t *buf) {
221   FILE *x;
222   if ((x = open_outfile(file_name))) {
223     switch (render_fmt) {
224       case FMT_JPG:
225 	if (write_jpeg(ji, buf, JPEG_QUALITY, x))
226 	  dlog(LOG_ERR, "IMF: Could not write jpeg: %s\n", file_name);
227 	break;
228       case FMT_PNG:
229 	if (write_png(ji, buf, x))
230 	  dlog(LOG_ERR, "IMF: Could not write png: %s\n", file_name);
231 	break;
232       case FMT_PPM:
233 	if (write_ppm(ji, buf, x))
234 	  dlog(LOG_ERR, "IMF: Could not write ppm: %s\n", file_name);
235 	break;
236       default:
237 	dlog(LOG_ERR, "IMF: Unknown outformat %d\n", render_fmt);
238 	break;
239     }
240     if (strcmp(file_name, "-")) fclose(x);
241     dlog(LOG_INFO, "IMF: Outputfile %s closed\n", file_name);
242   }
243   else
244     dlog(LOG_ERR, "IMF: Could not open outfile: %s\n", file_name);
245   return;
246 }
247 
248 // vim:sw=2 sts=2 ts=8 et:
249