1 /****************************************************************************
2 *
3 * pngHandler.cc: Joint Photographic Experts Group (JPEG) format handler
4 * This is part of the yafray package
5 * Copyright (C) 2010 Rodrigo Placencia Vazquez
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23 #include <core_api/imagehandler.h>
24 #include <core_api/logging.h>
25 #include <core_api/session.h>
26 #include <core_api/environment.h>
27 #include <core_api/params.h>
28 #include <utilities/math_utils.h>
29 #include <core_api/file.h>
30
31 extern "C"
32 {
33 #include <setjmp.h>
34 #include <jpeglib.h>
35 }
36
37 __BEGIN_YAFRAY
38
39 #define inv8 0.00392156862745098039f // 1 / 255
40
41 // error handlers for libJPEG,
42
METHODDEF(void)43 METHODDEF(void) jpgErrorMessage(j_common_ptr info)
44 {
45 char buffer[JMSG_LENGTH_MAX];
46 (*info->err->format_message)(info, buffer);
47 Y_ERROR << "JPEG Library Error: " << buffer << yendl;
48 }
49
50 struct jpgErrorManager
51 {
52 struct jpeg_error_mgr pub;
53 jmp_buf setjmp_buffer;
54 };
55
56 // JPEG error manager pointer
57 typedef struct jpgErrorManager *error_ptr;
58
jpgExitOnError(j_common_ptr info)59 void jpgExitOnError(j_common_ptr info)
60 {
61 error_ptr myerr = (error_ptr)info->err;
62 (*info->err->output_message)(info);
63 longjmp(myerr->setjmp_buffer, 1);
64 }
65
66 // The actual class
67
68 class jpgHandler_t: public imageHandler_t
69 {
70 public:
71 jpgHandler_t();
72 ~jpgHandler_t();
73 bool loadFromFile(const std::string &name);
74 bool saveToFile(const std::string &name, int imgIndex = 0);
75 static imageHandler_t *factory(paraMap_t ¶ms, renderEnvironment_t &render);
76 };
77
jpgHandler_t()78 jpgHandler_t::jpgHandler_t()
79 {
80 m_hasAlpha = false;
81 m_MultiLayer = false;
82
83 handlerName = "JPEGHandler";
84 }
85
~jpgHandler_t()86 jpgHandler_t::~jpgHandler_t()
87 {
88 clearImgBuffers();
89 }
90
saveToFile(const std::string & name,int imgIndex)91 bool jpgHandler_t::saveToFile(const std::string &name, int imgIndex)
92 {
93 int h = getHeight(imgIndex);
94 int w = getWidth(imgIndex);
95
96 std::string nameWithoutTmp = name;
97 nameWithoutTmp.erase(nameWithoutTmp.length()-4);
98 if(session.renderInProgress()) Y_INFO << handlerName << ": Autosaving partial render (" << RoundFloatPrecision(session.currentPassPercent(), 0.01) << "% of pass " << session.currentPass() << " of " << session.totalPasses() << ") RGB" << " file as \"" << nameWithoutTmp << "\"... " << getDenoiseParams() << yendl;
99 else Y_INFO << handlerName << ": Saving RGB" << " file as \"" << nameWithoutTmp << "\"... " << getDenoiseParams() << yendl;
100
101 struct jpeg_compress_struct info;
102 struct jpgErrorManager jerr;
103 int x, y, ix;
104 yByte *scanline = nullptr;
105
106 FILE * fp = file_t::open(name, "wb");
107
108 if (!fp)
109 {
110 Y_ERROR << handlerName << ": Cannot open file for writing " << name << yendl;
111 return false;
112 }
113
114 info.err = jpeg_std_error(&jerr.pub);
115 info.err->output_message = jpgErrorMessage;
116 jerr.pub.error_exit = jpgExitOnError;
117
118 jpeg_create_compress(&info);
119 jpeg_stdio_dest(&info, fp);
120
121 info.image_width = w;
122 info.image_height = h;
123 info.in_color_space = JCS_RGB;
124 info.input_components = 3;
125
126 jpeg_set_defaults(&info);
127
128 info.dct_method = JDCT_FLOAT;
129 jpeg_set_quality(&info, 100, TRUE);
130
131 jpeg_start_compress(&info, TRUE);
132
133 scanline = new yByte[ w * 3 ];
134
135 //The denoise functionality will only work if YafaRay is built with OpenCV support
136 #ifdef HAVE_OPENCV
137 if(m_Denoise) {
138 imageBuffer_t denoised_buffer = imgBuffer.at(imgIndex)->getDenoisedLDRBuffer(m_DenoiseHCol, m_DenoiseHLum, m_DenoiseMix);
139 for(y = 0; y < h; y++)
140 {
141 for (x = 0; x < w; x++)
142 {
143 ix = x * 3;
144 colorA_t col = denoised_buffer.getColor(x, y);
145 col.clampRGBA01();
146 scanline[ix] = (yByte) (col.getR() * 255);
147 scanline[ix+1] = (yByte) (col.getG() * 255);
148 scanline[ix+2] = (yByte) (col.getB() * 255);
149 }
150
151 jpeg_write_scanlines(&info, &scanline, 1);
152 }
153 }
154 else
155 #endif //HAVE_OPENCV
156 {
157 for (y = 0; y < h; y++) {
158 for (x = 0; x < w; x++) {
159 ix = x * 3;
160 colorA_t col = imgBuffer.at(imgIndex)->getColor(x, y);
161 col.clampRGBA01();
162 scanline[ix] = (yByte) (col.getR() * 255);
163 scanline[ix + 1] = (yByte) (col.getG() * 255);
164 scanline[ix + 2] = (yByte) (col.getB() * 255);
165 }
166
167 jpeg_write_scanlines(&info, &scanline, 1);
168 }
169 }
170
171 delete [] scanline;
172
173 jpeg_finish_compress(&info);
174 jpeg_destroy_compress(&info);
175
176 file_t::close(fp);
177
178 if(m_hasAlpha)
179 {
180 std::string alphaname = name.substr(0, name.size() - 4) + "_alpha.jpg";
181 if(session.renderInProgress()) Y_INFO << handlerName << ": Autosaving partial render (" << RoundFloatPrecision(session.currentPassPercent(), 0.01) << "% of pass " << session.currentPass() << " of " << session.totalPasses() << ") Alpha channel as \"" << alphaname << "\"... " << getDenoiseParams() << yendl;
182 else Y_INFO << handlerName << ": Saving Alpha channel as \"" << alphaname << "\"... " << getDenoiseParams() << yendl;
183
184 fp = file_t::open(alphaname, "wb");
185
186 if (!fp)
187 {
188 Y_ERROR << handlerName << ": Cannot open file for writing " << alphaname << yendl;
189 return false;
190 }
191
192 info.err = jpeg_std_error(&jerr.pub);
193 info.err->output_message = jpgErrorMessage;
194 jerr.pub.error_exit = jpgExitOnError;
195
196 jpeg_create_compress(&info);
197 jpeg_stdio_dest(&info, fp);
198
199 info.image_width = w;
200 info.image_height = h;
201 info.in_color_space = JCS_GRAYSCALE;
202 info.input_components = 1;
203
204 jpeg_set_defaults(&info);
205
206 info.dct_method = JDCT_FLOAT;
207 jpeg_set_quality(&info, 100, TRUE);
208
209 jpeg_start_compress(&info, TRUE);
210
211 scanline = new yByte[ w ];
212
213 for(y = 0; y < h; y++)
214 {
215 for (x = 0; x < w; x++)
216 {
217 float col = std::max(0.f, std::min(1.f, imgBuffer.at(imgIndex)->getColor(x, y).getA()));
218
219 scanline[x] = (yByte)(col * 255);
220 }
221
222 jpeg_write_scanlines(&info, &scanline, 1);
223 }
224
225 delete [] scanline;
226
227 jpeg_finish_compress(&info);
228 jpeg_destroy_compress(&info);
229
230 file_t::close(fp);
231 }
232
233 Y_VERBOSE << handlerName << ": Done." << yendl;
234
235 return true;
236 }
237
loadFromFile(const std::string & name)238 bool jpgHandler_t::loadFromFile(const std::string &name)
239 {
240 jpeg_decompress_struct info;
241 jpgErrorManager jerr;
242
243 FILE *fp = file_t::open(name, "rb");
244
245 Y_INFO << handlerName << ": Loading image \"" << name << "\"..." << yendl;
246
247 if(!fp)
248 {
249 Y_ERROR << handlerName << ": Cannot open file " << name << yendl;
250 return false;
251 }
252
253 info.err = jpeg_std_error(&jerr.pub);
254 info.err->output_message = jpgErrorMessage;
255 jerr.pub.error_exit = jpgExitOnError;
256
257 if (setjmp(jerr.setjmp_buffer))
258 {
259 jpeg_destroy_decompress(&info);
260
261 file_t::close(fp);
262
263 return false;
264 }
265
266 jpeg_create_decompress(&info);
267 jpeg_stdio_src(&info, fp);
268 jpeg_read_header(&info, TRUE);
269
270 jpeg_start_decompress(&info);
271
272 bool isGray = ((info.out_color_space == JCS_GRAYSCALE) & (info.output_components == 1));
273 bool isRGB = ((info.out_color_space == JCS_RGB) & (info.output_components == 3));
274 bool isCMYK = ((info.out_color_space == JCS_CMYK) & (info.output_components == 4));// TODO: findout if blender's non-standard jpeg + alpha comply with this or not, the code for conversion is below
275
276 if ((!isGray) && (!isRGB) && (!isCMYK))
277 {
278 Y_ERROR << handlerName << ": Unsupported color space: " << info.out_color_space << "| Color components: " << info.output_components << yendl;
279
280 jpeg_finish_decompress(&info);
281 jpeg_destroy_decompress(&info);
282
283 file_t::close(fp);
284
285 return false;
286 }
287
288 m_hasAlpha = false;
289 m_width = info.output_width;
290 m_height = info.output_height;
291
292 clearImgBuffers();
293
294 int nChannels = 3;
295 if(m_grayscale) nChannels = 1;
296 else if(m_hasAlpha) nChannels = 4;
297
298 imgBuffer.push_back(new imageBuffer_t(m_width, m_height, nChannels, getTextureOptimization()));
299
300 yByte* scanline = new yByte[m_width * info.output_components];
301
302 int y = 0;
303 int ix = 0;
304
305 while ( info.output_scanline < info.output_height )
306 {
307 jpeg_read_scanlines(&info, &scanline, 1);
308
309 for (int x = 0; x < m_width; x++)
310 {
311 colorA_t color;
312
313 if (isGray)
314 {
315 float colscan = scanline[x] * inv8;
316 color.set(colscan, colscan, colscan, 1.f);
317 }
318 else if(isRGB)
319 {
320 ix = x * 3;
321 color.set( scanline[ix] * inv8,
322 scanline[ix+1] * inv8,
323 scanline[ix+2] * inv8,
324 1.f);
325 }
326 else if(isCMYK)
327 {
328 ix = x * 4;
329 float K = scanline[ix+3] * inv8;
330 float iK = 1.f - K;
331
332 color.set( 1.f - std::max((scanline[ix] * inv8 * iK) + K, 1.f),
333 1.f - std::max((scanline[ix+1] * inv8 * iK) + K, 1.f),
334 1.f - std::max((scanline[ix+2] * inv8 * iK) + K, 1.f),
335 1.f);
336 }
337 else // this is probabbly (surely) never executed, i need to research further; this assumes blender non-standard jpeg + alpha
338 {
339 ix = x * 4;
340 float A = scanline[ix+3] * inv8;
341 float iA = 1.f - A;
342 color.set( std::max(0.f, std::min((scanline[ix] * inv8) - iA, 1.f)),
343 std::max(0.f, std::min((scanline[ix+1] * inv8) - iA, 1.f)),
344 std::max(0.f, std::min((scanline[ix+2] * inv8) - iA, 1.f)),
345 A);
346 }
347
348 imgBuffer.at(0)->setColor(x, y, color, m_colorSpace, m_gamma);
349 }
350 y++;
351 }
352
353 delete [] scanline;
354
355 jpeg_finish_decompress(&info);
356 jpeg_destroy_decompress(&info);
357
358 file_t::close(fp);
359
360 Y_VERBOSE << handlerName << ": Done." << yendl;
361
362 return true;
363 }
364
365
factory(paraMap_t & params,renderEnvironment_t & render)366 imageHandler_t *jpgHandler_t::factory(paraMap_t ¶ms, renderEnvironment_t &render)
367 {
368 int width = 0;
369 int height = 0;
370 bool withAlpha = false;
371 bool forOutput = true;
372 bool img_grayscale = false;
373 bool denoiseEnabled = false;
374 int denoiseHLum = 3;
375 int denoiseHCol = 3;
376 float denoiseMix = 0.8f;
377
378 params.getParam("width", width);
379 params.getParam("height", height);
380 params.getParam("alpha_channel", withAlpha);
381 params.getParam("for_output", forOutput);
382 params.getParam("denoiseEnabled", denoiseEnabled);
383 params.getParam("denoiseHLum", denoiseHLum);
384 params.getParam("denoiseHCol", denoiseHCol);
385 params.getParam("denoiseMix", denoiseMix);
386 params.getParam("img_grayscale", img_grayscale);
387
388 imageHandler_t *ih = new jpgHandler_t();
389
390 if(forOutput)
391 {
392 if(yafLog.getUseParamsBadge()) height += yafLog.getBadgeHeight();
393 ih->initForOutput(width, height, render.getRenderPasses(), denoiseEnabled, denoiseHLum, denoiseHCol, denoiseMix, withAlpha, false, img_grayscale);
394 }
395
396 return ih;
397 }
398
399 extern "C"
400 {
401
registerPlugin(renderEnvironment_t & render)402 YAFRAYPLUGIN_EXPORT void registerPlugin(renderEnvironment_t &render)
403 {
404 render.registerImageHandler("jpg", "jpg jpeg", "JPEG [Joint Photographic Experts Group]", jpgHandler_t::factory);
405 }
406
407 }
408
409 __END_YAFRAY
410