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