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 &params, 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 &params, 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