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