xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
1 /*
2 
3 Copyright (c) 2002-2003, Marcin 'Shard' Konicki
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9     * Redistributions of source code must retain the above copyright notice,
10       this list of conditions and the following disclaimer.
11     * Redistributions in binary form must reproduce the above copyright notice,
12       this list of conditions and the following disclaimer in the documentation and/or
13       other materials provided with the distribution.
14     * Name "Marcin Konicki", "Shard" or any combination of them,
15       must not be used to endorse or promote products derived from this
16       software without specific prior written permission from Marcin Konicki.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 */
31 
32 
33 #include "JPEGTranslator.h"
34 #include "TranslatorWindow.h"
35 #include "exif_parser.h"
36 
37 #include <syslog.h>
38 
39 #include <Alignment.h>
40 #include <Catalog.h>
41 #include <GridLayoutBuilder.h>
42 #include <GroupLayoutBuilder.h>
43 #include <TabView.h>
44 #include <TextView.h>
45 
46 #undef B_TRANSLATE_CONTEXT
47 #define B_TRANSLATE_CONTEXT "JPEGTranslator"
48 
49 #define MARKER_EXIF	0xe1
50 
51 // Set these accordingly
52 #define JPEG_ACRONYM "JPEG"
53 #define JPEG_FORMAT 'JPEG'
54 #define JPEG_MIME_STRING "image/jpeg"
55 #define JPEG_DESCRIPTION "JPEG image"
56 
57 // The translation kit's native file type
58 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap"
59 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap Format (JPEGTranslator)"
60 
61 
62 static const int32 sTranslatorVersion = B_TRANSLATION_MAKE_VERSION(1, 2, 0);
63 
64 static const char* sTranslatorName = B_TRANSLATE("JPEG images");
65 static const char* sTranslatorInfo = B_TRANSLATE("©2002-2003, Marcin Konicki\n"
66 	"©2005-2007, Haiku\n"
67 	"\n"
68 	"Based on IJG library ©  1994-2009, Thomas G. Lane, Guido Vollbeding.\n"
69 	"\thttp://www.ijg.org/files/\n"
70 	"\n"
71 	"with \"lossless\" encoding support patch by Ken Murchison\n"
72 	"\thttp://www.oceana.com/ftp/ljpeg/\n"
73 	"\n"
74 	"With some colorspace conversion routines by Magnus Hellman\n"
75 	"\thttp://www.bebits.com/app/802\n");
76 
77 // Define the formats we know how to read
78 static const translation_format sInputFormats[] = {
79 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
80 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
81 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
82 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }
83 };
84 
85 // Define the formats we know how to write
86 static const translation_format sOutputFormats[] = {
87 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
88 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
89 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
90 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }
91 };
92 
93 
94 static const TranSetting sDefaultSettings[] = {
95 	{JPEG_SET_SMOOTHING, TRAN_SETTING_INT32, 0},
96 	{JPEG_SET_QUALITY, TRAN_SETTING_INT32, 95},
97 	{JPEG_SET_PROGRESSIVE, TRAN_SETTING_BOOL, true},
98 	{JPEG_SET_OPT_COLORS, TRAN_SETTING_BOOL, true},
99 	{JPEG_SET_SMALL_FILES, TRAN_SETTING_BOOL, false},
100 	{JPEG_SET_GRAY1_AS_RGB24, TRAN_SETTING_BOOL, false},
101 	{JPEG_SET_ALWAYS_RGB32, TRAN_SETTING_BOOL, true},
102 	{JPEG_SET_PHOTOSHOP_CMYK, TRAN_SETTING_BOOL, true},
103 	{JPEG_SET_SHOWREADWARNING, TRAN_SETTING_BOOL, true}
104 };
105 
106 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
107 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
108 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
109 
110 
111 namespace conversion {
112 
113 
114 static bool
115 x_flipped(int32 orientation)
116 {
117 	return orientation == 2 || orientation == 3
118 		|| orientation == 6 || orientation == 7;
119 }
120 
121 
122 static bool
123 y_flipped(int32 orientation)
124 {
125 	return orientation == 3 || orientation == 4
126 		|| orientation == 7 || orientation == 8;
127 }
128 
129 
130 static int32
131 dest_index(uint32 width, uint32 height, uint32 x, uint32 y, int32 orientation)
132 {
133 	if (orientation > 4) {
134 		uint32 temp = x;
135 		x = y;
136 		y = temp;
137 	}
138 	if (y_flipped(orientation))
139 		y = height - 1 - y;
140 	if (x_flipped(orientation))
141 		x = width - 1 - x;
142 
143 	return y * width + x;
144 }
145 
146 
147 //	#pragma mark - conversion for compression
148 
149 
150 inline void
151 convert_from_gray1_to_gray8(uint8* in, uint8* out, int32 inRowBytes)
152 {
153 	int32 index = 0;
154 	int32 index2 = 0;
155 	while (index < inRowBytes) {
156 		unsigned char c = in[index++];
157 		for (int b = 128; b; b = b>>1) {
158 			unsigned char color;
159 			if (c & b)
160 				color = 0;
161 			else
162 				color = 255;
163 			out[index2++] = color;
164 		}
165 	}
166 }
167 
168 
169 inline void
170 convert_from_gray1_to_24(uint8* in, uint8* out, int32 inRowBytes)
171 {
172 	int32 index = 0;
173 	int32 index2 = 0;
174 	while (index < inRowBytes) {
175 		unsigned char c = in[index++];
176 		for (int b = 128; b; b = b>>1) {
177 			unsigned char color;
178 			if (c & b)
179 				color = 0;
180 			else
181 				color = 255;
182 			out[index2++] = color;
183 			out[index2++] = color;
184 			out[index2++] = color;
185 		}
186 	}
187 }
188 
189 
190 inline void
191 convert_from_cmap8_to_24(uint8* in, uint8* out, int32 inRowBytes)
192 {
193 	const color_map * map = system_colors();
194 	int32 index = 0;
195 	int32 index2 = 0;
196 	while (index < inRowBytes) {
197 		rgb_color color = map->color_list[in[index++]];
198 
199 		out[index2++] = color.red;
200 		out[index2++] = color.green;
201 		out[index2++] = color.blue;
202 	}
203 }
204 
205 
206 inline void
207 convert_from_15_to_24(uint8* in, uint8* out, int32 inRowBytes)
208 {
209 	int32 index = 0;
210 	int32 index2 = 0;
211 	int16 in_pixel;
212 	while (index < inRowBytes) {
213 		in_pixel = in[index] | (in[index + 1] << 8);
214 		index += 2;
215 
216 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
217 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
218 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
219 	}
220 }
221 
222 
223 inline void
224 convert_from_15b_to_24(uint8* in, uint8* out, int32 inRowBytes)
225 {
226 	int32 index = 0;
227 	int32 index2 = 0;
228 	int16 in_pixel;
229 	while (index < inRowBytes) {
230 		in_pixel = in[index + 1] | (in[index] << 8);
231 		index += 2;
232 
233 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
234 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
235 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
236 	}
237 }
238 
239 
240 inline void
241 convert_from_16_to_24(uint8* in, uint8* out, int32 inRowBytes)
242 {
243 	int32 index = 0;
244 	int32 index2 = 0;
245 	int16 in_pixel;
246 	while (index < inRowBytes) {
247 		in_pixel = in[index] | (in[index + 1] << 8);
248 		index += 2;
249 
250 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
251 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
252 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
253 	}
254 }
255 
256 
257 inline void
258 convert_from_16b_to_24(uint8* in, uint8* out, int32 inRowBytes)
259 {
260 	int32 index = 0;
261 	int32 index2 = 0;
262 	int16 in_pixel;
263 	while (index < inRowBytes) {
264 		in_pixel = in[index + 1] | (in[index] << 8);
265 		index += 2;
266 
267 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
268 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
269 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
270 	}
271 }
272 
273 
274 inline void
275 convert_from_24_to_24(uint8* in, uint8* out, int32 inRowBytes)
276 {
277 	int32 index = 0;
278 	int32 index2 = 0;
279 	while (index < inRowBytes) {
280 		out[index2++] = in[index + 2];
281 		out[index2++] = in[index + 1];
282 		out[index2++] = in[index];
283 		index+=3;
284 	}
285 }
286 
287 
288 inline void
289 convert_from_32_to_24(uint8* in, uint8* out, int32 inRowBytes)
290 {
291 	inRowBytes /= 4;
292 
293 	for (int32 i = 0; i < inRowBytes; i++) {
294 		out[0] = in[2];
295 		out[1] = in[1];
296 		out[2] = in[0];
297 
298 		in += 4;
299 		out += 3;
300 	}
301 }
302 
303 
304 inline void
305 convert_from_32b_to_24(uint8* in, uint8* out, int32 inRowBytes)
306 {
307 	inRowBytes /= 4;
308 
309 	for (int32 i = 0; i < inRowBytes; i++) {
310 		out[0] = in[1];
311 		out[1] = in[2];
312 		out[2] = in[3];
313 
314 		in += 4;
315 		out += 3;
316 	}
317 }
318 
319 
320 //	#pragma mark - conversion for decompression
321 
322 
323 inline void
324 convert_from_CMYK_to_32_photoshop(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
325 {
326 	for (int32 i = 0; i < inRowBytes; i += 4) {
327 		int32 black = in[3];
328 		out[0] = in[2] * black / 255;
329 		out[1] = in[1] * black / 255;
330 		out[2] = in[0] * black / 255;
331 		out[3] = 255;
332 
333 		in += 4;
334 		out += xStep;
335 	}
336 }
337 
338 
339 //!	!!! UNTESTED !!!
340 inline void
341 convert_from_CMYK_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
342 {
343 	for (int32 i = 0; i < inRowBytes; i += 4) {
344 		int32 black = 255 - in[3];
345 		out[0] = ((255 - in[2]) * black) / 255;
346 		out[1] = ((255 - in[1]) * black) / 255;
347 		out[2] = ((255 - in[0]) * black) / 255;
348 		out[3] = 255;
349 
350 		in += 4;
351 		out += xStep;
352 	}
353 }
354 
355 
356 //!	RGB24 8:8:8 to xRGB 8:8:8:8
357 inline void
358 convert_from_24_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
359 {
360 	for (int32 i = 0; i < inRowBytes; i += 3) {
361 		out[0] = in[2];
362 		out[1] = in[1];
363 		out[2] = in[0];
364 		out[3] = 255;
365 
366 		in += 3;
367 		out += xStep;
368 	}
369 }
370 
371 
372 //! 8-bit to 8-bit, only need when rotating the image
373 void
374 translate_8(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
375 {
376 	for (int32 i = 0; i < inRowBytes; i++) {
377 		out[0] = in[0];
378 
379 		in++;
380 		out += xStep;
381 	}
382 }
383 
384 
385 } // namespace conversion
386 
387 
388 //	#pragma mark -
389 
390 
391 SSlider::SSlider(const char* name, const char* label,
392 		BMessage* message, int32 minValue, int32 maxValue, orientation posture,
393 		thumb_style thumbType, uint32 flags)
394 	: BSlider(name, label, message, minValue, maxValue,
395 		posture, thumbType, flags)
396 {
397 	rgb_color barColor = { 0, 0, 229, 255 };
398 	UseFillColor(true, &barColor);
399 }
400 
401 
402 //!	Update status string - show actual value
403 const char*
404 SSlider::UpdateText() const
405 {
406 	snprintf(fStatusLabel, sizeof(fStatusLabel), "%ld", Value());
407 	return fStatusLabel;
408 }
409 
410 
411 //	#pragma mark -
412 
413 
414 TranslatorReadView::TranslatorReadView(const char* name,
415 	TranslatorSettings* settings)
416 	:
417 	BView(name, 0, new BGroupLayout(B_HORIZONTAL)),
418 	fSettings(settings)
419 		// settings should already be Acquired()
420 {
421 	fAlwaysRGB32 = new BCheckBox("alwaysrgb32",
422 		B_TRANSLATE("Read greyscale images as RGB32"),
423 		new BMessage(VIEW_MSG_SET_ALWAYSRGB32));
424 	if (fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL))
425 		fAlwaysRGB32->SetValue(B_CONTROL_ON);
426 
427 	fPhotoshopCMYK = new BCheckBox("photoshopCMYK",
428 		B_TRANSLATE("Use CMYK code with 0 for 100% ink coverage"),
429 		new BMessage(VIEW_MSG_SET_PHOTOSHOPCMYK));
430 	if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK, NULL))
431 		fPhotoshopCMYK->SetValue(B_CONTROL_ON);
432 
433 	fShowErrorBox = new BCheckBox("error",
434 		B_TRANSLATE("Show warning messages"),
435 		new BMessage(VIEW_MSG_SET_SHOWREADERRORBOX));
436 	if (fSettings->SetGetBool(JPEG_SET_SHOWREADWARNING, NULL))
437 		fShowErrorBox->SetValue(B_CONTROL_ON);
438 
439 	float padding = 5.0f;
440 	AddChild(BGroupLayoutBuilder(B_VERTICAL, padding)
441 		.Add(fAlwaysRGB32)
442 		.Add(fPhotoshopCMYK)
443 		.Add(fShowErrorBox)
444 		.AddGlue()
445 		.SetInsets(padding, padding, padding, padding)
446 	);
447 
448 }
449 
450 
451 TranslatorReadView::~TranslatorReadView()
452 {
453 	fSettings->Release();
454 }
455 
456 
457 void
458 TranslatorReadView::AttachedToWindow()
459 {
460 	BView::AttachedToWindow();
461 
462 	fAlwaysRGB32->SetTarget(this);
463 	fPhotoshopCMYK->SetTarget(this);
464 	fShowErrorBox->SetTarget(this);
465 }
466 
467 
468 void
469 TranslatorReadView::MessageReceived(BMessage* message)
470 {
471 	switch (message->what) {
472 		case VIEW_MSG_SET_ALWAYSRGB32:
473 		{
474 			int32 value;
475 			if (message->FindInt32("be:value", &value) == B_OK) {
476 				bool boolValue = value;
477 				fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, &boolValue);
478 				fSettings->SaveSettings();
479 			}
480 			break;
481 		}
482 		case VIEW_MSG_SET_PHOTOSHOPCMYK:
483 		{
484 			int32 value;
485 			if (message->FindInt32("be:value", &value) == B_OK) {
486 				bool boolValue = value;
487 				fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK, &boolValue);
488 				fSettings->SaveSettings();
489 			}
490 			break;
491 		}
492 		case VIEW_MSG_SET_SHOWREADERRORBOX:
493 		{
494 			int32 value;
495 			if (message->FindInt32("be:value", &value) == B_OK) {
496 				bool boolValue = value;
497 				fSettings->SetGetBool(JPEG_SET_SHOWREADWARNING, &boolValue);
498 				fSettings->SaveSettings();
499 			}
500 			break;
501 		}
502 		default:
503 			BView::MessageReceived(message);
504 			break;
505 	}
506 }
507 
508 
509 //	#pragma mark - TranslatorWriteView
510 
511 
512 TranslatorWriteView::TranslatorWriteView(const char* name,
513 	TranslatorSettings* settings)
514 	:
515 	BView(name, 0, new BGroupLayout(B_VERTICAL)),
516 	fSettings(settings)
517 		// settings should already be Acquired()
518 {
519 	fQualitySlider = new SSlider("quality", B_TRANSLATE("Output quality"),
520 		new BMessage(VIEW_MSG_SET_QUALITY), 0, 100);
521 	fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
522 	fQualitySlider->SetHashMarkCount(10);
523 	fQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High"));
524 	fQualitySlider->SetValue(fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL));
525 
526 	fSmoothingSlider = new SSlider("smoothing",
527 		B_TRANSLATE("Output smoothing strength"),
528 		new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100);
529 	fSmoothingSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
530 	fSmoothingSlider->SetHashMarkCount(10);
531 	fSmoothingSlider->SetLimitLabels(B_TRANSLATE("None"), B_TRANSLATE("High"));
532 	fSmoothingSlider->SetValue(
533 		fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL));
534 
535 	fProgress = new BCheckBox("progress",
536 		B_TRANSLATE("Use progressive compression"),
537 		new BMessage(VIEW_MSG_SET_PROGRESSIVE));
538 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
539 		fProgress->SetValue(B_CONTROL_ON);
540 
541 	fSmallerFile = new BCheckBox("smallerfile",
542 		B_TRANSLATE("Make file smaller (sligthtly worse quality)"),
543 		new BMessage(VIEW_MSG_SET_SMALLERFILE));
544 	if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
545 		fSmallerFile->SetValue(B_CONTROL_ON);
546 
547 	fOptimizeColors = new BCheckBox("optimizecolors",
548 		B_TRANSLATE("Prevent colors 'washing out'"),
549 		new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS));
550 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL))
551 		fOptimizeColors->SetValue(B_CONTROL_ON);
552 	else
553 		fSmallerFile->SetEnabled(false);
554 
555 	fGrayAsRGB24 = new BCheckBox("gray1asrgb24",
556 		B_TRANSLATE("Write black-and-white images as RGB24"),
557 		new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
558 	if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24))
559 		fGrayAsRGB24->SetValue(B_CONTROL_ON);
560 
561 	float padding = 5.0f;
562 	AddChild(BGroupLayoutBuilder(B_VERTICAL, padding)
563 		.Add(fQualitySlider)
564 		.Add(fSmoothingSlider)
565 		.Add(fProgress)
566 		.Add(fOptimizeColors)
567 		.Add(fSmallerFile)
568 		.Add(fGrayAsRGB24)
569 		.AddGlue()
570 		.SetInsets(padding, padding, padding, padding)
571 	);
572 }
573 
574 
575 TranslatorWriteView::~TranslatorWriteView()
576 {
577 	fSettings->Release();
578 }
579 
580 
581 void
582 TranslatorWriteView::AttachedToWindow()
583 {
584 	BView::AttachedToWindow();
585 
586 	fQualitySlider->SetTarget(this);
587 	fSmoothingSlider->SetTarget(this);
588 	fProgress->SetTarget(this);
589 	fOptimizeColors->SetTarget(this);
590 	fSmallerFile->SetTarget(this);
591 	fGrayAsRGB24->SetTarget(this);
592 }
593 
594 
595 void
596 TranslatorWriteView::MessageReceived(BMessage* message)
597 {
598 	switch (message->what) {
599 		case VIEW_MSG_SET_QUALITY:
600 		{
601 			int32 value;
602 			if (message->FindInt32("be:value", &value) == B_OK) {
603 				fSettings->SetGetInt32(JPEG_SET_QUALITY, &value);
604 				fSettings->SaveSettings();
605 			}
606 			break;
607 		}
608 		case VIEW_MSG_SET_SMOOTHING:
609 		{
610 			int32 value;
611 			if (message->FindInt32("be:value", &value) == B_OK) {
612 				fSettings->SetGetInt32(JPEG_SET_SMOOTHING, &value);
613 				fSettings->SaveSettings();
614 			}
615 			break;
616 		}
617 		case VIEW_MSG_SET_PROGRESSIVE:
618 		{
619 			int32 value;
620 			if (message->FindInt32("be:value", &value) == B_OK) {
621 				bool boolValue = value;
622 				fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, &boolValue);
623 				fSettings->SaveSettings();
624 			}
625 			break;
626 		}
627 		case VIEW_MSG_SET_OPTIMIZECOLORS:
628 		{
629 			int32 value;
630 			if (message->FindInt32("be:value", &value) == B_OK) {
631 				bool boolValue = value;
632 				fSettings->SetGetBool(JPEG_SET_OPT_COLORS, &boolValue);
633 				fSmallerFile->SetEnabled(value);
634 				fSettings->SaveSettings();
635 			}
636 			break;
637 		}
638 		case VIEW_MSG_SET_SMALLERFILE:
639 		{
640 			int32 value;
641 			if (message->FindInt32("be:value", &value) == B_OK) {
642 				bool boolValue = value;
643 				fSettings->SetGetBool(JPEG_SET_SMALL_FILES, &boolValue);
644 				fSettings->SaveSettings();
645 			}
646 			break;
647 		}
648 		case VIEW_MSG_SET_GRAY1ASRGB24:
649 		{
650 			int32 value;
651 			if (message->FindInt32("be:value", &value) == B_OK) {
652 				bool boolValue = value;
653 				fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, &boolValue);
654 				fSettings->SaveSettings();
655 			}
656 			break;
657 		}
658 		default:
659 			BView::MessageReceived(message);
660 			break;
661 	}
662 }
663 
664 
665 //	#pragma mark -
666 
667 
668 TranslatorAboutView::TranslatorAboutView(const char* name)
669 	:
670 	BView(name, 0, new BGroupLayout(B_VERTICAL))
671 {
672 	BAlignment labelAlignment = BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP);
673 	BStringView* title = new BStringView("Title", sTranslatorName);
674 	title->SetFont(be_bold_font);
675 	title->SetExplicitAlignment(labelAlignment);
676 
677 	char versionString[16];
678 	sprintf(versionString, "v%d.%d.%d", (int)(sTranslatorVersion >> 8),
679 		(int)((sTranslatorVersion >> 4) & 0xf), (int)(sTranslatorVersion & 0xf));
680 
681 	BStringView* version = new BStringView("Version", versionString);
682 	version->SetExplicitAlignment(labelAlignment);
683 
684 	BTextView* infoView = new BTextView("info");
685 	infoView->SetText(sTranslatorInfo);
686 	infoView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
687 	infoView->MakeEditable(false);
688 
689 	float padding = 5.0f;
690 	AddChild(BGroupLayoutBuilder(B_VERTICAL, padding)
691 		.Add(BGroupLayoutBuilder(B_HORIZONTAL, padding)
692 			.Add(title)
693 			.Add(version)
694 			.AddGlue()
695 		)
696 		.Add(infoView)
697 		.SetInsets(padding, padding, padding, padding)
698 	);
699 }
700 
701 
702 TranslatorView::TranslatorView(const char* name, TranslatorSettings* settings)
703 	:
704 	BTabView(name)
705 {
706 	AddTab(new TranslatorWriteView(B_TRANSLATE("Write"), settings->Acquire()));
707 	AddTab(new TranslatorReadView(B_TRANSLATE("Read"), settings->Acquire()));
708 	AddTab(new TranslatorAboutView(B_TRANSLATE("About")));
709 
710 	settings->Release();
711 
712  	BFont font;
713  	GetFont(&font);
714  	SetExplicitPreferredSize(
715 		BSize((font.Size() * 380) / 12, (font.Size() * 250) / 12));
716 }
717 
718 
719 //	#pragma mark - Translator Add-On
720 
721 
722 BView*
723 JPEGTranslator::NewConfigView(TranslatorSettings* settings)
724 {
725 	BView* configView = new TranslatorView("TranslatorView", settings);
726 	return configView;
727 }
728 
729 
730 /*! Determine whether or not we can handle this data */
731 status_t
732 JPEGTranslator::DerivedIdentify(BPositionIO* inSource,
733 	const translation_format* inFormat, BMessage* ioExtension,
734 	translator_info* outInfo, uint32 outType)
735 {
736 	if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT)
737 		return B_NO_TRANSLATOR;
738 
739 	// !!! You might need to make this buffer bigger to test for your native format
740 	off_t position = inSource->Position();
741 	char header[sizeof(TranslatorBitmap)];
742 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
743 	inSource->Seek(position, SEEK_SET);
744 	if (err < B_OK)
745 		return err;
746 
747 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
748 		if (PopulateInfoFromFormat(outInfo, B_TRANSLATOR_BITMAP) != B_OK)
749 			return B_NO_TRANSLATOR;
750 	} else {
751 		// First 3 bytes in jpg files are always the same from what i've seen so far
752 		// check them
753 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
754 			if (PopulateInfoFromFormat(outInfo, JPEG_FORMAT) != B_OK)
755 				return B_NO_TRANSLATOR;
756 
757 		} else
758 			return B_NO_TRANSLATOR;
759 	}
760 
761 	return B_OK;
762 }
763 
764 
765 status_t
766 JPEGTranslator::DerivedTranslate(BPositionIO* inSource,
767 	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
768 	BPositionIO* outDestination, int32 baseType)
769 {
770 	// If no specific type was requested, convert to the interchange format
771 	if (outType == 0)
772 		outType = B_TRANSLATOR_BITMAP;
773 
774 	// Setup a "breakpoint" since throwing exceptions does not seem to work
775 	// at all in an add-on. (?)
776 	// In the be_jerror.cpp we implement a handler for critical library errors
777 	// (be_error_exit()) and there we use the longjmp() function to return to
778 	// this place. If this happens, it is as if the setjmp() call is called
779 	// a second time, but this time the return value will be 1. The first
780 	// invokation will return 0.
781 	jmp_buf longJumpBuffer;
782 	int jmpRet = setjmp(longJumpBuffer);
783 	if (jmpRet == 1)
784 		return B_ERROR;
785 
786 	try {
787 		// What action to take, based on the findings of Identify()
788 		if (outType == inInfo->type) {
789 			return Copy(inSource, outDestination);
790 		} else if (inInfo->type == B_TRANSLATOR_BITMAP
791 				&& outType == JPEG_FORMAT) {
792 			return Compress(inSource, outDestination, &longJumpBuffer);
793 		} else if (inInfo->type == JPEG_FORMAT
794 				&& outType == B_TRANSLATOR_BITMAP) {
795 			return Decompress(inSource, outDestination, ioExtension,
796 				&longJumpBuffer);
797 		}
798 	} catch (...) {
799 		syslog(LOG_ERR, "libjpeg encountered a critical error (caught C++ "
800 			"exception).\n");
801 		return B_ERROR;
802 	}
803 
804 	return B_NO_TRANSLATOR;
805 }
806 
807 
808 /*!	The user has requested the same format for input and output, so just copy */
809 status_t
810 JPEGTranslator::Copy(BPositionIO* in, BPositionIO* out)
811 {
812 	int block_size = 65536;
813 	void* buffer = malloc(block_size);
814 	char temp[1024];
815 	if (buffer == NULL) {
816 		buffer = temp;
817 		block_size = 1024;
818 	}
819 	status_t err = B_OK;
820 
821 	// Read until end of file or error
822 	while (1) {
823 		ssize_t to_read = block_size;
824 		err = in->Read(buffer, to_read);
825 		// Explicit check for EOF
826 		if (err == -1) {
827 			if (buffer != temp) free(buffer);
828 			return B_OK;
829 		}
830 		if (err <= B_OK) break;
831 		to_read = err;
832 		err = out->Write(buffer, to_read);
833 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
834 		if (err < B_OK) break;
835 	}
836 
837 	if (buffer != temp) free(buffer);
838 	return (err >= 0) ? B_OK : err;
839 }
840 
841 
842 /*!	Encode into the native format */
843 status_t
844 JPEGTranslator::Compress(BPositionIO* in, BPositionIO* out,
845 	const jmp_buf* longJumpBuffer)
846 {
847 	using namespace conversion;
848 
849 	// Read info about bitmap
850 	TranslatorBitmap header;
851 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
852 	if (err < B_OK)
853 		return err;
854 	else if (err < (int)sizeof(TranslatorBitmap))
855 		return B_ERROR;
856 
857 	// Grab dimension, color space, and size information from the stream
858 	BRect bounds;
859 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
860 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
861 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
862 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
863 
864 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
865 
866 	int width = bounds.IntegerWidth() + 1;
867 	int height = bounds.IntegerHeight() + 1;
868 
869 	// Function pointer to convert function
870 	// It will point to proper function if needed
871 	void (*converter)(uchar* inscanline, uchar* outscanline,
872 		int32 inRowBytes) = NULL;
873 
874 	// Default color info
875 	J_COLOR_SPACE jpg_color_space = JCS_RGB;
876 	int jpg_input_components = 3;
877 	int32 out_row_bytes;
878 	int padding = 0;
879 
880 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) {
881 		case B_CMAP8:
882 			converter = convert_from_cmap8_to_24;
883 			padding = in_row_bytes - width;
884 			break;
885 
886 		case B_GRAY1:
887 			if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, NULL)) {
888 				converter = convert_from_gray1_to_24;
889 			} else {
890 				jpg_input_components = 1;
891 				jpg_color_space = JCS_GRAYSCALE;
892 				converter = convert_from_gray1_to_gray8;
893 			}
894 			padding = in_row_bytes - (width / 8);
895 			break;
896 
897 		case B_GRAY8:
898 			jpg_input_components = 1;
899 			jpg_color_space = JCS_GRAYSCALE;
900 			padding = in_row_bytes - width;
901 			break;
902 
903 		case B_RGB15:
904 		case B_RGBA15:
905 			converter = convert_from_15_to_24;
906 			padding = in_row_bytes - (width * 2);
907 			break;
908 
909 		case B_RGB15_BIG:
910 		case B_RGBA15_BIG:
911 			converter = convert_from_15b_to_24;
912 			padding = in_row_bytes - (width * 2);
913 			break;
914 
915 		case B_RGB16:
916 			converter = convert_from_16_to_24;
917 			padding = in_row_bytes - (width * 2);
918 			break;
919 
920 		case B_RGB16_BIG:
921 			converter = convert_from_16b_to_24;
922 			padding = in_row_bytes - (width * 2);
923 			break;
924 
925 		case B_RGB24:
926 			converter = convert_from_24_to_24;
927 			padding = in_row_bytes - (width * 3);
928 			break;
929 
930 		case B_RGB24_BIG:
931 			padding = in_row_bytes - (width * 3);
932 			break;
933 
934 		case B_RGB32:
935 		case B_RGBA32:
936 			converter = convert_from_32_to_24;
937 			padding = in_row_bytes - (width * 4);
938 			break;
939 
940 		case B_RGB32_BIG:
941 		case B_RGBA32_BIG:
942 			converter = convert_from_32b_to_24;
943 			padding = in_row_bytes - (width * 4);
944 			break;
945 
946 		case B_CMYK32:
947 			jpg_color_space = JCS_CMYK;
948 			jpg_input_components = 4;
949 			padding = in_row_bytes - (width * 4);
950 			break;
951 
952 		default:
953 			syslog(LOG_ERR, "Wrong type: Color space not implemented.\n");
954 			return B_ERROR;
955 	}
956 	out_row_bytes = jpg_input_components * width;
957 
958 	// Set basic things needed for jpeg writing
959 	struct jpeg_compress_struct cinfo;
960 	struct jpeg_error_mgr jerr;
961 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
962 	jpeg_create_compress(&cinfo);
963 	be_jpeg_stdio_dest(&cinfo, out);
964 
965 	// Set basic values
966 	cinfo.image_width = width;
967 	cinfo.image_height = height;
968 	cinfo.input_components = jpg_input_components;
969 	cinfo.in_color_space = jpg_color_space;
970 	jpeg_set_defaults(&cinfo);
971 
972 	// Set better accuracy
973 	cinfo.dct_method = JDCT_ISLOW;
974 
975 	// This is needed to prevent some colors loss
976 	// With it generated jpegs are as good as from Fireworks (at last! :D)
977 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL)) {
978 		int index = 0;
979 		while (index < cinfo.num_components) {
980 			cinfo.comp_info[index].h_samp_factor = 1;
981 			cinfo.comp_info[index].v_samp_factor = 1;
982 			// This will make file smaller, but with worse quality more or less
983 			// like with 93%-94% (but it's subjective opinion) on tested images
984 			// but with smaller size (between 92% and 93% on tested images)
985 			if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
986 				cinfo.comp_info[index].quant_tbl_no = 1;
987 			// This will make bigger file, but also better quality ;]
988 			// from my tests it seems like useless - better quality with smaller
989 			// can be acheived without this
990 //			cinfo.comp_info[index].dc_tbl_no = 1;
991 //			cinfo.comp_info[index].ac_tbl_no = 1;
992 			index++;
993 		}
994 	}
995 
996 	// Set quality
997 	jpeg_set_quality(&cinfo, fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL), true);
998 
999 	// Set progressive compression if needed
1000 	// if not, turn on optimizing in libjpeg
1001 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
1002 		jpeg_simple_progression(&cinfo);
1003 	else
1004 		cinfo.optimize_coding = TRUE;
1005 
1006 	// Set smoothing (effect like Blur)
1007 	cinfo.smoothing_factor = fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL);
1008 
1009 	// Initialize compression
1010 	jpeg_start_compress(&cinfo, TRUE);
1011 
1012 	// Declare scanlines
1013 	JSAMPROW in_scanline = NULL;
1014 	JSAMPROW out_scanline = NULL;
1015 	JSAMPROW writeline;
1016 		// Pointer to in_scanline (default) or out_scanline (if there will be conversion)
1017 
1018 	// Allocate scanline
1019 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1020 	in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1021 		JPOOL_PERMANENT, in_row_bytes);
1022 
1023 	// We need 2nd scanline storage ony for conversion
1024 	if (converter != NULL) {
1025 		// There will be conversion, allocate second scanline...
1026 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1027 	    out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1028 	    	JPOOL_PERMANENT, out_row_bytes);
1029 		// ... and make it the one to write to file
1030 		writeline = out_scanline;
1031 	} else
1032 		writeline = in_scanline;
1033 
1034 	while (cinfo.next_scanline < cinfo.image_height) {
1035 		// Read scanline
1036 		err = in->Read(in_scanline, in_row_bytes);
1037 		if (err < in_row_bytes)
1038 			return err < B_OK ? Error((j_common_ptr)&cinfo, err)
1039 				: Error((j_common_ptr)&cinfo, B_ERROR);
1040 
1041 		// Convert if needed
1042 		if (converter != NULL)
1043 			converter(in_scanline, out_scanline, in_row_bytes - padding);
1044 
1045 		// Write scanline
1046 	   	jpeg_write_scanlines(&cinfo, &writeline, 1);
1047 	}
1048 
1049 	jpeg_finish_compress(&cinfo);
1050 	jpeg_destroy_compress(&cinfo);
1051 	return B_OK;
1052 }
1053 
1054 
1055 /*!	Decode the native format */
1056 status_t
1057 JPEGTranslator::Decompress(BPositionIO* in, BPositionIO* out,
1058 	BMessage* ioExtension, const jmp_buf* longJumpBuffer)
1059 {
1060 	using namespace conversion;
1061 
1062 	// Set basic things needed for jpeg reading
1063 	struct jpeg_decompress_struct cinfo;
1064 	struct jpeg_error_mgr jerr;
1065 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
1066 	jpeg_create_decompress(&cinfo);
1067 	be_jpeg_stdio_src(&cinfo, in);
1068 
1069 	jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
1070 		// make sure the EXIF tag is stored
1071 
1072 	// Read info about image
1073 	jpeg_read_header(&cinfo, TRUE);
1074 
1075 	BMessage exif;
1076 
1077 	// parse EXIF data and add it ioExtension, if any
1078 	jpeg_marker_struct* marker = cinfo.marker_list;
1079 	while (marker != NULL) {
1080 		if (marker->marker == MARKER_EXIF
1081 			&& !strncmp((char*)marker->data, "Exif", 4)) {
1082 			if (ioExtension != NULL) {
1083 				// Strip EXIF header from TIFF data
1084 				ioExtension->AddData("exif", B_RAW_TYPE,
1085 					(uint8*)marker->data + 6, marker->data_length - 6);
1086 			}
1087 
1088 			BMemoryIO io(marker->data + 6, marker->data_length - 6);
1089 			convert_exif_to_message(io, exif);
1090 		}
1091 		marker = marker->next;
1092 	}
1093 
1094 	// Default color info
1095 	color_space outColorSpace = B_RGB32;
1096 	int outColorComponents = 4;
1097 
1098 	// Function pointer to convert function
1099 	// It will point to proper function if needed
1100 	void (*converter)(uchar* inScanLine, uchar* outScanLine,
1101 		int32 inRowBytes, int32 xStep) = convert_from_24_to_32;
1102 
1103 	// If color space isn't rgb
1104 	if (cinfo.out_color_space != JCS_RGB) {
1105 		switch (cinfo.out_color_space) {
1106 			case JCS_UNKNOWN:		/* error/unspecified */
1107 				syslog(LOG_ERR, "From Type: Jpeg uses unknown color type\n");
1108 				break;
1109 			case JCS_GRAYSCALE:		/* monochrome */
1110 				// Check if user wants to read only as RGB32 or not
1111 				if (!fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL)) {
1112 					// Grayscale
1113 					outColorSpace = B_GRAY8;
1114 					outColorComponents = 1;
1115 					converter = translate_8;
1116 				} else {
1117 					// RGB
1118 					cinfo.out_color_space = JCS_RGB;
1119 					cinfo.output_components = 3;
1120 					converter = convert_from_24_to_32;
1121 				}
1122 				break;
1123 			case JCS_YCbCr:		/* Y/Cb/Cr (also known as YUV) */
1124 				cinfo.out_color_space = JCS_RGB;
1125 				converter = convert_from_24_to_32;
1126 				break;
1127 			case JCS_YCCK:		/* Y/Cb/Cr/K */
1128 				// Let libjpeg convert it to CMYK
1129 				cinfo.out_color_space = JCS_CMYK;
1130 				// Fall through to CMYK since we need the same settings
1131 			case JCS_CMYK:		/* C/M/Y/K */
1132 				// Use proper converter
1133 				if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK))
1134 					converter = convert_from_CMYK_to_32_photoshop;
1135 				else
1136 					converter = convert_from_CMYK_to_32;
1137 				break;
1138 			default:
1139 				syslog(LOG_ERR,
1140 						"From Type: Jpeg uses hmm... i don't know really :(\n");
1141 				break;
1142 		}
1143 	}
1144 
1145 	// Initialize decompression
1146 	jpeg_start_decompress(&cinfo);
1147 
1148 	// retrieve orientation from settings/EXIF
1149 	int32 orientation;
1150 	if (ioExtension == NULL
1151 		|| ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) {
1152 		if (exif.FindInt32("Orientation", &orientation) != B_OK)
1153 			orientation = 1;
1154 	}
1155 
1156 	if (orientation != 1 && converter == NULL)
1157 		converter = translate_8;
1158 
1159 	int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width;
1160 	int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height;
1161 
1162 	int32 destOffset = dest_index(outputWidth, outputHeight,
1163 		0, 0, orientation) * outColorComponents;
1164 	int32 xStep = dest_index(outputWidth, outputHeight,
1165 		1, 0, orientation) * outColorComponents - destOffset;
1166 	int32 yStep = dest_index(outputWidth, outputHeight,
1167 		0, 1, orientation) * outColorComponents - destOffset;
1168 	bool needAll = orientation != 1;
1169 
1170 	// Initialize this bounds rect to the size of your image
1171 	BRect bounds(0, 0, outputWidth - 1, outputHeight - 1);
1172 
1173 #if 0
1174 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n",
1175 	destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height,
1176 	bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation);
1177 #endif
1178 
1179 	// Bytes count in one line of image (scanline)
1180 	int32 inRowBytes = cinfo.output_width * cinfo.output_components;
1181 	int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents;
1182 	int32 dataSize = cinfo.output_width * cinfo.output_height
1183 		* outColorComponents;
1184 
1185 	// Fill out the B_TRANSLATOR_BITMAP's header
1186 	TranslatorBitmap header;
1187 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1188 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1189 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1190 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1191 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1192 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
1193 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
1194 	header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize);
1195 
1196 	// Write out the header
1197 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1198 	if (err < B_OK)
1199 		return Error((j_common_ptr)&cinfo, err);
1200 	else if (err < (int)sizeof(TranslatorBitmap))
1201 		return Error((j_common_ptr)&cinfo, B_ERROR);
1202 
1203 	// Declare scanlines
1204 	JSAMPROW inScanLine = NULL;
1205 	uint8* dest = NULL;
1206 	uint8* destLine = NULL;
1207 
1208 	// Allocate scanline
1209 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1210     inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1211     	JPOOL_PERMANENT, inRowBytes);
1212 
1213 	// We need 2nd scanline storage only for conversion
1214 	if (converter != NULL) {
1215 		// There will be conversion, allocate second scanline...
1216 		// Use libjpeg memory allocation functions, so in case of error it will
1217 		// free them itself
1218 	    dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1219 	    	JPOOL_PERMANENT, needAll ? dataSize : rowBytes);
1220 	    destLine = dest + destOffset;
1221 	} else
1222 		destLine = inScanLine;
1223 
1224 	while (cinfo.output_scanline < cinfo.output_height) {
1225 		// Read scanline
1226 		jpeg_read_scanlines(&cinfo, &inScanLine, 1);
1227 
1228 		// Convert if needed
1229 		if (converter != NULL)
1230 			converter(inScanLine, destLine, inRowBytes, xStep);
1231 
1232 		if (!needAll) {
1233 	  		// Write the scanline buffer to the output stream
1234 			ssize_t bytesWritten = out->Write(destLine, rowBytes);
1235 			if (bytesWritten < rowBytes) {
1236 				return bytesWritten < B_OK
1237 					? Error((j_common_ptr)&cinfo, bytesWritten)
1238 					: Error((j_common_ptr)&cinfo, B_ERROR);
1239 			}
1240 		} else
1241 			destLine += yStep;
1242 	}
1243 
1244 	if (needAll) {
1245 		ssize_t bytesWritten = out->Write(dest, dataSize);
1246 		if (bytesWritten < dataSize) {
1247 			return bytesWritten < B_OK
1248 				? Error((j_common_ptr)&cinfo, bytesWritten)
1249 				: Error((j_common_ptr)&cinfo, B_ERROR);
1250 		}
1251 	}
1252 
1253 	jpeg_finish_decompress(&cinfo);
1254 	jpeg_destroy_decompress(&cinfo);
1255 	return B_OK;
1256 }
1257 
1258 /*! have the other PopulateInfoFromFormat() check both inputFormats & outputFormats */
1259 status_t
1260 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1261 	uint32 formatType, translator_id id)
1262 {
1263 	int32 formatCount;
1264 	const translation_format* formats = OutputFormats(&formatCount);
1265 	for (int i = 0; i <= 1 ;formats = InputFormats(&formatCount), i++) {
1266 		if (PopulateInfoFromFormat(info, formatType,
1267 			formats, formatCount) == B_OK) {
1268 			info->translator = id;
1269 			return B_OK;
1270 		}
1271 	}
1272 
1273 	return B_ERROR;
1274 }
1275 
1276 
1277 status_t
1278 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1279 	uint32 formatType, const translation_format* formats, int32 formatCount)
1280 {
1281 	for (int i = 0; i < formatCount; i++) {
1282 		if (formats[i].type == formatType) {
1283 			info->type = formatType;
1284 			info->group = formats[i].group;
1285 			info->quality = formats[i].quality;
1286 			info->capability = formats[i].capability;
1287 			BString str1(formats[i].name);
1288 			str1.ReplaceFirst("Be Bitmap Format (JPEGTranslator)",
1289 				B_TRANSLATE("Be Bitmap Format (JPEGTranslator)"));
1290 			strncpy(info->name, str1.String(), sizeof(info->name));
1291 			strcpy(info->MIME,  formats[i].MIME);
1292 			return B_OK;
1293 		}
1294 	}
1295 
1296 	return B_ERROR;
1297 }
1298 
1299 /*!
1300 	Frees jpeg alocated memory
1301 	Returns given error (B_ERROR by default)
1302 */
1303 status_t
1304 JPEGTranslator::Error(j_common_ptr cinfo, status_t error)
1305 {
1306 	jpeg_destroy(cinfo);
1307 	return error;
1308 }
1309 
1310 
1311 JPEGTranslator::JPEGTranslator()
1312 	: BaseTranslator(sTranslatorName, sTranslatorInfo, sTranslatorVersion,
1313 		sInputFormats,  kNumInputFormats,
1314 		sOutputFormats, kNumOutputFormats,
1315 		SETTINGS_FILE,
1316 		sDefaultSettings, kNumDefaultSettings,
1317 		B_TRANSLATOR_BITMAP, JPEG_FORMAT)
1318 {}
1319 
1320 
1321 BTranslator*
1322 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
1323 {
1324 	if (n == 0)
1325 		return new JPEGTranslator();
1326 
1327 	return NULL;
1328 }
1329 
1330 
1331 int
1332 main(int, char**)
1333 {
1334 	BApplication app("application/x-vnd.Haiku-JPEGTranslator");
1335 	JPEGTranslator* translator = new JPEGTranslator();
1336 	if (LaunchTranslatorWindow(translator, sTranslatorName) == B_OK)
1337 		app.Run();
1338 
1339 	return 0;
1340 }
1341 
1342