xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 	BLayoutBuilder::Group<>(this, B_VERTICAL)
442 		.SetInsets(B_USE_DEFAULT_SPACING)
443 		.Add(fAlwaysRGB32)
444 		.Add(fPhotoshopCMYK)
445 		.Add(fShowErrorBox)
446 		.AddGlue();
447 }
448 
449 
450 TranslatorReadView::~TranslatorReadView()
451 {
452 	fSettings->Release();
453 }
454 
455 
456 void
457 TranslatorReadView::AttachedToWindow()
458 {
459 	BView::AttachedToWindow();
460 
461 	fAlwaysRGB32->SetTarget(this);
462 	fPhotoshopCMYK->SetTarget(this);
463 	fShowErrorBox->SetTarget(this);
464 }
465 
466 
467 void
468 TranslatorReadView::MessageReceived(BMessage* message)
469 {
470 	switch (message->what) {
471 		case VIEW_MSG_SET_ALWAYSRGB32:
472 		{
473 			int32 value;
474 			if (message->FindInt32("be:value", &value) == B_OK) {
475 				bool boolValue = value;
476 				fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, &boolValue);
477 				fSettings->SaveSettings();
478 			}
479 			break;
480 		}
481 		case VIEW_MSG_SET_PHOTOSHOPCMYK:
482 		{
483 			int32 value;
484 			if (message->FindInt32("be:value", &value) == B_OK) {
485 				bool boolValue = value;
486 				fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK, &boolValue);
487 				fSettings->SaveSettings();
488 			}
489 			break;
490 		}
491 		case VIEW_MSG_SET_SHOWREADERRORBOX:
492 		{
493 			int32 value;
494 			if (message->FindInt32("be:value", &value) == B_OK) {
495 				bool boolValue = value;
496 				fSettings->SetGetBool(JPEG_SET_SHOWREADWARNING, &boolValue);
497 				fSettings->SaveSettings();
498 			}
499 			break;
500 		}
501 		default:
502 			BView::MessageReceived(message);
503 			break;
504 	}
505 }
506 
507 
508 //	#pragma mark - TranslatorWriteView
509 
510 
511 TranslatorWriteView::TranslatorWriteView(const char* name,
512 	TranslatorSettings* settings)
513 	:
514 	BView(name, 0, new BGroupLayout(B_VERTICAL)),
515 	fSettings(settings)
516 		// settings should already be Acquired()
517 {
518 	fQualitySlider = new SSlider("quality", B_TRANSLATE("Output quality"),
519 		new BMessage(VIEW_MSG_SET_QUALITY), 0, 100);
520 	fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
521 	fQualitySlider->SetHashMarkCount(10);
522 	fQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High"));
523 	fQualitySlider->SetValue(fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL));
524 
525 	fSmoothingSlider = new SSlider("smoothing",
526 		B_TRANSLATE("Output smoothing strength"),
527 		new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100);
528 	fSmoothingSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
529 	fSmoothingSlider->SetHashMarkCount(10);
530 	fSmoothingSlider->SetLimitLabels(B_TRANSLATE("None"), B_TRANSLATE("High"));
531 	fSmoothingSlider->SetValue(
532 		fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL));
533 
534 	fProgress = new BCheckBox("progress",
535 		B_TRANSLATE("Use progressive compression"),
536 		new BMessage(VIEW_MSG_SET_PROGRESSIVE));
537 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
538 		fProgress->SetValue(B_CONTROL_ON);
539 
540 	fSmallerFile = new BCheckBox("smallerfile",
541 		B_TRANSLATE("Make file smaller (sligthtly worse quality)"),
542 		new BMessage(VIEW_MSG_SET_SMALLERFILE));
543 	if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
544 		fSmallerFile->SetValue(B_CONTROL_ON);
545 
546 	fOptimizeColors = new BCheckBox("optimizecolors",
547 		B_TRANSLATE("Prevent colors 'washing out'"),
548 		new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS));
549 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL))
550 		fOptimizeColors->SetValue(B_CONTROL_ON);
551 	else
552 		fSmallerFile->SetEnabled(false);
553 
554 	fGrayAsRGB24 = new BCheckBox("gray1asrgb24",
555 		B_TRANSLATE("Write black-and-white images as RGB24"),
556 		new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
557 	if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24))
558 		fGrayAsRGB24->SetValue(B_CONTROL_ON);
559 
560 	BLayoutBuilder::Group<>(this, B_VERTICAL)
561 		.SetInsets(B_USE_DEFAULT_SPACING)
562 		.Add(fQualitySlider)
563 		.Add(fSmoothingSlider)
564 		.Add(fProgress)
565 		.Add(fOptimizeColors)
566 		.Add(fSmallerFile)
567 		.Add(fGrayAsRGB24)
568 		.AddGlue();
569 }
570 
571 
572 TranslatorWriteView::~TranslatorWriteView()
573 {
574 	fSettings->Release();
575 }
576 
577 
578 void
579 TranslatorWriteView::AttachedToWindow()
580 {
581 	BView::AttachedToWindow();
582 
583 	fQualitySlider->SetTarget(this);
584 	fSmoothingSlider->SetTarget(this);
585 	fProgress->SetTarget(this);
586 	fOptimizeColors->SetTarget(this);
587 	fSmallerFile->SetTarget(this);
588 	fGrayAsRGB24->SetTarget(this);
589 }
590 
591 
592 void
593 TranslatorWriteView::MessageReceived(BMessage* message)
594 {
595 	switch (message->what) {
596 		case VIEW_MSG_SET_QUALITY:
597 		{
598 			int32 value;
599 			if (message->FindInt32("be:value", &value) == B_OK) {
600 				fSettings->SetGetInt32(JPEG_SET_QUALITY, &value);
601 				fSettings->SaveSettings();
602 			}
603 			break;
604 		}
605 		case VIEW_MSG_SET_SMOOTHING:
606 		{
607 			int32 value;
608 			if (message->FindInt32("be:value", &value) == B_OK) {
609 				fSettings->SetGetInt32(JPEG_SET_SMOOTHING, &value);
610 				fSettings->SaveSettings();
611 			}
612 			break;
613 		}
614 		case VIEW_MSG_SET_PROGRESSIVE:
615 		{
616 			int32 value;
617 			if (message->FindInt32("be:value", &value) == B_OK) {
618 				bool boolValue = value;
619 				fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, &boolValue);
620 				fSettings->SaveSettings();
621 			}
622 			break;
623 		}
624 		case VIEW_MSG_SET_OPTIMIZECOLORS:
625 		{
626 			int32 value;
627 			if (message->FindInt32("be:value", &value) == B_OK) {
628 				bool boolValue = value;
629 				fSettings->SetGetBool(JPEG_SET_OPT_COLORS, &boolValue);
630 				fSmallerFile->SetEnabled(value);
631 				fSettings->SaveSettings();
632 			}
633 			break;
634 		}
635 		case VIEW_MSG_SET_SMALLERFILE:
636 		{
637 			int32 value;
638 			if (message->FindInt32("be:value", &value) == B_OK) {
639 				bool boolValue = value;
640 				fSettings->SetGetBool(JPEG_SET_SMALL_FILES, &boolValue);
641 				fSettings->SaveSettings();
642 			}
643 			break;
644 		}
645 		case VIEW_MSG_SET_GRAY1ASRGB24:
646 		{
647 			int32 value;
648 			if (message->FindInt32("be:value", &value) == B_OK) {
649 				bool boolValue = value;
650 				fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, &boolValue);
651 				fSettings->SaveSettings();
652 			}
653 			break;
654 		}
655 		default:
656 			BView::MessageReceived(message);
657 			break;
658 	}
659 }
660 
661 
662 //	#pragma mark -
663 
664 
665 TranslatorAboutView::TranslatorAboutView(const char* name)
666 	:
667 	BView(name, 0, new BGroupLayout(B_VERTICAL))
668 {
669 	BAlignment labelAlignment = BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP);
670 	BStringView* title = new BStringView("Title", sTranslatorName);
671 	title->SetFont(be_bold_font);
672 	title->SetExplicitAlignment(labelAlignment);
673 
674 	char versionString[100];
675 	snprintf(versionString, sizeof(versionString),
676 		B_TRANSLATE("Version %d.%d.%d"),
677 		(int)(sTranslatorVersion >> 8),
678 		(int)((sTranslatorVersion >> 4) & 0xf),
679 		(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->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
687 	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
688 	infoView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
689 	infoView->MakeEditable(false);
690 
691 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
692 		.SetInsets(B_USE_DEFAULT_SPACING)
693 		.Add(title)
694 		.Add(version)
695 		.Add(infoView);
696 }
697 
698 
699 TranslatorView::TranslatorView(const char* name, TranslatorSettings* settings)
700 	:
701 	BTabView(name, B_WIDTH_FROM_LABEL)
702 {
703 	SetBorder(B_NO_BORDER);
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 
712 
713 //	#pragma mark - Translator Add-On
714 
715 
716 BView*
717 JPEGTranslator::NewConfigView(TranslatorSettings* settings)
718 {
719 	BView* configView = new TranslatorView("TranslatorView", settings);
720 	return configView;
721 }
722 
723 
724 /*! Determine whether or not we can handle this data */
725 status_t
726 JPEGTranslator::DerivedIdentify(BPositionIO* inSource,
727 	const translation_format* inFormat, BMessage* ioExtension,
728 	translator_info* outInfo, uint32 outType)
729 {
730 	if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT)
731 		return B_NO_TRANSLATOR;
732 
733 	// !!! You might need to make this buffer bigger to test for your native format
734 	off_t position = inSource->Position();
735 	char header[sizeof(TranslatorBitmap)];
736 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
737 	inSource->Seek(position, SEEK_SET);
738 	if (err < B_OK)
739 		return err;
740 
741 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
742 		if (PopulateInfoFromFormat(outInfo, B_TRANSLATOR_BITMAP) != B_OK)
743 			return B_NO_TRANSLATOR;
744 	} else {
745 		// First 3 bytes in jpg files are always the same from what i've seen so far
746 		// check them
747 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
748 			if (PopulateInfoFromFormat(outInfo, JPEG_FORMAT) != B_OK)
749 				return B_NO_TRANSLATOR;
750 
751 		} else
752 			return B_NO_TRANSLATOR;
753 	}
754 
755 	return B_OK;
756 }
757 
758 
759 status_t
760 JPEGTranslator::DerivedTranslate(BPositionIO* inSource,
761 	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
762 	BPositionIO* outDestination, int32 baseType)
763 {
764 	// Setup a "breakpoint" since throwing exceptions does not seem to work
765 	// at all in an add-on. (?)
766 	// In the be_jerror.cpp we implement a handler for critical library errors
767 	// (be_error_exit()) and there we use the longjmp() function to return to
768 	// this place. If this happens, it is as if the setjmp() call is called
769 	// a second time, but this time the return value will be 1. The first
770 	// invokation will return 0.
771 	jmp_buf longJumpBuffer;
772 	int jmpRet = setjmp(longJumpBuffer);
773 	if (jmpRet == 1)
774 		return B_ERROR;
775 
776 	try {
777 		// What action to take, based on the findings of Identify()
778 		if (outType == inInfo->type) {
779 			return Copy(inSource, outDestination);
780 		} else if (inInfo->type == B_TRANSLATOR_BITMAP
781 				&& outType == JPEG_FORMAT) {
782 			return Compress(inSource, outDestination, &longJumpBuffer);
783 		} else if (inInfo->type == JPEG_FORMAT
784 				&& (outType == B_TRANSLATOR_BITMAP || outType == 0)) {
785 			// This is the default if no specific outType was requested.
786 			return Decompress(inSource, outDestination, ioExtension,
787 				&longJumpBuffer);
788 		}
789 	} catch (...) {
790 		syslog(LOG_ERR, "libjpeg encountered a critical error (caught C++ "
791 			"exception).\n");
792 		return B_ERROR;
793 	}
794 
795 	return B_NO_TRANSLATOR;
796 }
797 
798 
799 /*!	The user has requested the same format for input and output, so just copy */
800 status_t
801 JPEGTranslator::Copy(BPositionIO* in, BPositionIO* out)
802 {
803 	int block_size = 65536;
804 	void* buffer = malloc(block_size);
805 	char temp[1024];
806 	if (buffer == NULL) {
807 		buffer = temp;
808 		block_size = 1024;
809 	}
810 	status_t err = B_OK;
811 
812 	// Read until end of file or error
813 	while (1) {
814 		ssize_t to_read = block_size;
815 		err = in->Read(buffer, to_read);
816 		// Explicit check for EOF
817 		if (err == -1) {
818 			if (buffer != temp) free(buffer);
819 			return B_OK;
820 		}
821 		if (err <= B_OK) break;
822 		to_read = err;
823 		err = out->Write(buffer, to_read);
824 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
825 		if (err < B_OK) break;
826 	}
827 
828 	if (buffer != temp) free(buffer);
829 	return (err >= 0) ? B_OK : err;
830 }
831 
832 
833 /*!	Encode into the native format */
834 status_t
835 JPEGTranslator::Compress(BPositionIO* in, BPositionIO* out,
836 	const jmp_buf* longJumpBuffer)
837 {
838 	using namespace conversion;
839 
840 	// Read info about bitmap
841 	TranslatorBitmap header;
842 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
843 	if (err < B_OK)
844 		return err;
845 	else if (err < (int)sizeof(TranslatorBitmap))
846 		return B_ERROR;
847 
848 	// Grab dimension, color space, and size information from the stream
849 	BRect bounds;
850 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
851 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
852 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
853 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
854 
855 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
856 
857 	int width = bounds.IntegerWidth() + 1;
858 	int height = bounds.IntegerHeight() + 1;
859 
860 	// Function pointer to convert function
861 	// It will point to proper function if needed
862 	void (*converter)(uchar* inscanline, uchar* outscanline,
863 		int32 inRowBytes) = NULL;
864 
865 	// Default color info
866 	J_COLOR_SPACE jpg_color_space = JCS_RGB;
867 	int jpg_input_components = 3;
868 	int32 out_row_bytes;
869 	int padding = 0;
870 
871 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) {
872 		case B_CMAP8:
873 			converter = convert_from_cmap8_to_24;
874 			padding = in_row_bytes - width;
875 			break;
876 
877 		case B_GRAY1:
878 			if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, NULL)) {
879 				converter = convert_from_gray1_to_24;
880 			} else {
881 				jpg_input_components = 1;
882 				jpg_color_space = JCS_GRAYSCALE;
883 				converter = convert_from_gray1_to_gray8;
884 			}
885 			padding = in_row_bytes - (width / 8);
886 			break;
887 
888 		case B_GRAY8:
889 			jpg_input_components = 1;
890 			jpg_color_space = JCS_GRAYSCALE;
891 			padding = in_row_bytes - width;
892 			break;
893 
894 		case B_RGB15:
895 		case B_RGBA15:
896 			converter = convert_from_15_to_24;
897 			padding = in_row_bytes - (width * 2);
898 			break;
899 
900 		case B_RGB15_BIG:
901 		case B_RGBA15_BIG:
902 			converter = convert_from_15b_to_24;
903 			padding = in_row_bytes - (width * 2);
904 			break;
905 
906 		case B_RGB16:
907 			converter = convert_from_16_to_24;
908 			padding = in_row_bytes - (width * 2);
909 			break;
910 
911 		case B_RGB16_BIG:
912 			converter = convert_from_16b_to_24;
913 			padding = in_row_bytes - (width * 2);
914 			break;
915 
916 		case B_RGB24:
917 			converter = convert_from_24_to_24;
918 			padding = in_row_bytes - (width * 3);
919 			break;
920 
921 		case B_RGB24_BIG:
922 			padding = in_row_bytes - (width * 3);
923 			break;
924 
925 		case B_RGB32:
926 		case B_RGBA32:
927 			converter = convert_from_32_to_24;
928 			padding = in_row_bytes - (width * 4);
929 			break;
930 
931 		case B_RGB32_BIG:
932 		case B_RGBA32_BIG:
933 			converter = convert_from_32b_to_24;
934 			padding = in_row_bytes - (width * 4);
935 			break;
936 
937 		case B_CMYK32:
938 			jpg_color_space = JCS_CMYK;
939 			jpg_input_components = 4;
940 			padding = in_row_bytes - (width * 4);
941 			break;
942 
943 		default:
944 			syslog(LOG_ERR, "Wrong type: Color space not implemented.\n");
945 			return B_ERROR;
946 	}
947 	out_row_bytes = jpg_input_components * width;
948 
949 	// Set basic things needed for jpeg writing
950 	struct jpeg_compress_struct cinfo;
951 	struct be_jpeg_error_mgr jerr;
952 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
953 	jpeg_create_compress(&cinfo);
954 	be_jpeg_stdio_dest(&cinfo, out);
955 
956 	// Set basic values
957 	cinfo.image_width = width;
958 	cinfo.image_height = height;
959 	cinfo.input_components = jpg_input_components;
960 	cinfo.in_color_space = jpg_color_space;
961 	jpeg_set_defaults(&cinfo);
962 
963 	// Set better accuracy
964 	cinfo.dct_method = JDCT_ISLOW;
965 
966 	// This is needed to prevent some colors loss
967 	// With it generated jpegs are as good as from Fireworks (at last! :D)
968 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL)) {
969 		int index = 0;
970 		while (index < cinfo.num_components) {
971 			cinfo.comp_info[index].h_samp_factor = 1;
972 			cinfo.comp_info[index].v_samp_factor = 1;
973 			// This will make file smaller, but with worse quality more or less
974 			// like with 93%-94% (but it's subjective opinion) on tested images
975 			// but with smaller size (between 92% and 93% on tested images)
976 			if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
977 				cinfo.comp_info[index].quant_tbl_no = 1;
978 			// This will make bigger file, but also better quality ;]
979 			// from my tests it seems like useless - better quality with smaller
980 			// can be acheived without this
981 //			cinfo.comp_info[index].dc_tbl_no = 1;
982 //			cinfo.comp_info[index].ac_tbl_no = 1;
983 			index++;
984 		}
985 	}
986 
987 	// Set quality
988 	jpeg_set_quality(&cinfo, fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL), true);
989 
990 	// Set progressive compression if needed
991 	// if not, turn on optimizing in libjpeg
992 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
993 		jpeg_simple_progression(&cinfo);
994 	else
995 		cinfo.optimize_coding = TRUE;
996 
997 	// Set smoothing (effect like Blur)
998 	cinfo.smoothing_factor = fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL);
999 
1000 	// Initialize compression
1001 	jpeg_start_compress(&cinfo, TRUE);
1002 
1003 	// Declare scanlines
1004 	JSAMPROW in_scanline = NULL;
1005 	JSAMPROW out_scanline = NULL;
1006 	JSAMPROW writeline;
1007 		// Pointer to in_scanline (default) or out_scanline (if there will be conversion)
1008 
1009 	// Allocate scanline
1010 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1011 	in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1012 		JPOOL_PERMANENT, in_row_bytes);
1013 
1014 	// We need 2nd scanline storage ony for conversion
1015 	if (converter != NULL) {
1016 		// There will be conversion, allocate second scanline...
1017 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1018 	    out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1019 	    	JPOOL_PERMANENT, out_row_bytes);
1020 		// ... and make it the one to write to file
1021 		writeline = out_scanline;
1022 	} else
1023 		writeline = in_scanline;
1024 
1025 	while (cinfo.next_scanline < cinfo.image_height) {
1026 		// Read scanline
1027 		err = in->Read(in_scanline, in_row_bytes);
1028 		if (err < in_row_bytes)
1029 			return err < B_OK ? Error((j_common_ptr)&cinfo, err)
1030 				: Error((j_common_ptr)&cinfo, B_ERROR);
1031 
1032 		// Convert if needed
1033 		if (converter != NULL)
1034 			converter(in_scanline, out_scanline, in_row_bytes - padding);
1035 
1036 		// Write scanline
1037 	   	jpeg_write_scanlines(&cinfo, &writeline, 1);
1038 	}
1039 
1040 	jpeg_finish_compress(&cinfo);
1041 	jpeg_destroy_compress(&cinfo);
1042 	return B_OK;
1043 }
1044 
1045 
1046 /*!	Decode the native format */
1047 status_t
1048 JPEGTranslator::Decompress(BPositionIO* in, BPositionIO* out,
1049 	BMessage* ioExtension, const jmp_buf* longJumpBuffer)
1050 {
1051 	using namespace conversion;
1052 
1053 	// Set basic things needed for jpeg reading
1054 	struct jpeg_decompress_struct cinfo;
1055 	struct be_jpeg_error_mgr jerr;
1056 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
1057 	jpeg_create_decompress(&cinfo);
1058 	be_jpeg_stdio_src(&cinfo, in);
1059 
1060 	jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
1061 		// make sure the EXIF tag is stored
1062 
1063 	// Read info about image
1064 	jpeg_read_header(&cinfo, TRUE);
1065 
1066 	BMessage exif;
1067 
1068 	// parse EXIF data and add it ioExtension, if any
1069 	jpeg_marker_struct* marker = cinfo.marker_list;
1070 	while (marker != NULL) {
1071 		if (marker->marker == MARKER_EXIF
1072 			&& !strncmp((char*)marker->data, "Exif", 4)) {
1073 			if (ioExtension != NULL) {
1074 				// Strip EXIF header from TIFF data
1075 				ioExtension->AddData("exif", B_RAW_TYPE,
1076 					(uint8*)marker->data + 6, marker->data_length - 6);
1077 			}
1078 
1079 			BMemoryIO io(marker->data + 6, marker->data_length - 6);
1080 			convert_exif_to_message(io, exif);
1081 		}
1082 		marker = marker->next;
1083 	}
1084 
1085 	// Default color info
1086 	color_space outColorSpace = B_RGB32;
1087 	int outColorComponents = 4;
1088 
1089 	// Function pointer to convert function
1090 	// It will point to proper function if needed
1091 	void (*converter)(uchar* inScanLine, uchar* outScanLine,
1092 		int32 inRowBytes, int32 xStep) = convert_from_24_to_32;
1093 
1094 	// If color space isn't rgb
1095 	if (cinfo.out_color_space != JCS_RGB) {
1096 		switch (cinfo.out_color_space) {
1097 			case JCS_UNKNOWN:		/* error/unspecified */
1098 				syslog(LOG_ERR, "From Type: Jpeg uses unknown color type\n");
1099 				break;
1100 			case JCS_GRAYSCALE:		/* monochrome */
1101 				// Check if user wants to read only as RGB32 or not
1102 				if (!fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL)) {
1103 					// Grayscale
1104 					outColorSpace = B_GRAY8;
1105 					outColorComponents = 1;
1106 					converter = translate_8;
1107 				} else {
1108 					// RGB
1109 					cinfo.out_color_space = JCS_RGB;
1110 					cinfo.output_components = 3;
1111 					converter = convert_from_24_to_32;
1112 				}
1113 				break;
1114 			case JCS_YCbCr:		/* Y/Cb/Cr (also known as YUV) */
1115 				cinfo.out_color_space = JCS_RGB;
1116 				converter = convert_from_24_to_32;
1117 				break;
1118 			case JCS_YCCK:		/* Y/Cb/Cr/K */
1119 				// Let libjpeg convert it to CMYK
1120 				cinfo.out_color_space = JCS_CMYK;
1121 				// Fall through to CMYK since we need the same settings
1122 			case JCS_CMYK:		/* C/M/Y/K */
1123 				// Use proper converter
1124 				if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK))
1125 					converter = convert_from_CMYK_to_32_photoshop;
1126 				else
1127 					converter = convert_from_CMYK_to_32;
1128 				break;
1129 			default:
1130 				syslog(LOG_ERR,
1131 						"From Type: Jpeg uses hmm... i don't know really :(\n");
1132 				break;
1133 		}
1134 	}
1135 
1136 	// Initialize decompression
1137 	jpeg_start_decompress(&cinfo);
1138 
1139 	// retrieve orientation from settings/EXIF
1140 	int32 orientation;
1141 	if (ioExtension == NULL
1142 		|| ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) {
1143 		if (exif.FindInt32("Orientation", &orientation) != B_OK)
1144 			orientation = 1;
1145 	}
1146 
1147 	if (orientation != 1 && converter == NULL)
1148 		converter = translate_8;
1149 
1150 	int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width;
1151 	int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height;
1152 
1153 	int32 destOffset = dest_index(outputWidth, outputHeight,
1154 		0, 0, orientation) * outColorComponents;
1155 	int32 xStep = dest_index(outputWidth, outputHeight,
1156 		1, 0, orientation) * outColorComponents - destOffset;
1157 	int32 yStep = dest_index(outputWidth, outputHeight,
1158 		0, 1, orientation) * outColorComponents - destOffset;
1159 	bool needAll = orientation != 1;
1160 
1161 	// Initialize this bounds rect to the size of your image
1162 	BRect bounds(0, 0, outputWidth - 1, outputHeight - 1);
1163 
1164 #if 0
1165 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n",
1166 	destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height,
1167 	bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation);
1168 #endif
1169 
1170 	// Bytes count in one line of image (scanline)
1171 	int32 inRowBytes = cinfo.output_width * cinfo.output_components;
1172 	int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents;
1173 	int32 dataSize = cinfo.output_width * cinfo.output_height
1174 		* outColorComponents;
1175 
1176 	// Fill out the B_TRANSLATOR_BITMAP's header
1177 	TranslatorBitmap header;
1178 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1179 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1180 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1181 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1182 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1183 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
1184 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
1185 	header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize);
1186 
1187 	// Write out the header
1188 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1189 	if (err < B_OK)
1190 		return Error((j_common_ptr)&cinfo, err);
1191 	else if (err < (int)sizeof(TranslatorBitmap))
1192 		return Error((j_common_ptr)&cinfo, B_ERROR);
1193 
1194 	// Declare scanlines
1195 	JSAMPROW inScanLine = NULL;
1196 	uint8* dest = NULL;
1197 	uint8* destLine = NULL;
1198 
1199 	// Allocate scanline
1200 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1201     inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1202     	JPOOL_PERMANENT, inRowBytes);
1203 
1204 	// We need 2nd scanline storage only for conversion
1205 	if (converter != NULL) {
1206 		// There will be conversion, allocate second scanline...
1207 		// Use libjpeg memory allocation functions, so in case of error it will
1208 		// free them itself
1209 	    dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1210 	    	JPOOL_PERMANENT, needAll ? dataSize : rowBytes);
1211 	    destLine = dest + destOffset;
1212 	} else
1213 		destLine = inScanLine;
1214 
1215 	while (cinfo.output_scanline < cinfo.output_height) {
1216 		// Read scanline
1217 		jpeg_read_scanlines(&cinfo, &inScanLine, 1);
1218 
1219 		// Convert if needed
1220 		if (converter != NULL)
1221 			converter(inScanLine, destLine, inRowBytes, xStep);
1222 
1223 		if (!needAll) {
1224 	  		// Write the scanline buffer to the output stream
1225 			ssize_t bytesWritten = out->Write(destLine, rowBytes);
1226 			if (bytesWritten < rowBytes) {
1227 				return bytesWritten < B_OK
1228 					? Error((j_common_ptr)&cinfo, bytesWritten)
1229 					: Error((j_common_ptr)&cinfo, B_ERROR);
1230 			}
1231 		} else
1232 			destLine += yStep;
1233 	}
1234 
1235 	if (needAll) {
1236 		ssize_t bytesWritten = out->Write(dest, dataSize);
1237 		if (bytesWritten < dataSize) {
1238 			return bytesWritten < B_OK
1239 				? Error((j_common_ptr)&cinfo, bytesWritten)
1240 				: Error((j_common_ptr)&cinfo, B_ERROR);
1241 		}
1242 	}
1243 
1244 	jpeg_finish_decompress(&cinfo);
1245 	jpeg_destroy_decompress(&cinfo);
1246 	return B_OK;
1247 }
1248 
1249 /*! have the other PopulateInfoFromFormat() check both inputFormats & outputFormats */
1250 status_t
1251 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1252 	uint32 formatType, translator_id id)
1253 {
1254 	int32 formatCount;
1255 	const translation_format* formats = OutputFormats(&formatCount);
1256 	for (int i = 0; i <= 1 ;formats = InputFormats(&formatCount), i++) {
1257 		if (PopulateInfoFromFormat(info, formatType,
1258 			formats, formatCount) == B_OK) {
1259 			info->translator = id;
1260 			return B_OK;
1261 		}
1262 	}
1263 
1264 	return B_ERROR;
1265 }
1266 
1267 
1268 status_t
1269 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1270 	uint32 formatType, const translation_format* formats, int32 formatCount)
1271 {
1272 	for (int i = 0; i < formatCount; i++) {
1273 		if (formats[i].type == formatType) {
1274 			info->type = formatType;
1275 			info->group = formats[i].group;
1276 			info->quality = formats[i].quality;
1277 			info->capability = formats[i].capability;
1278 			BString str1(formats[i].name);
1279 			str1.ReplaceFirst("Be Bitmap Format (JPEGTranslator)",
1280 				B_TRANSLATE("Be Bitmap Format (JPEGTranslator)"));
1281 			strlcpy(info->name, str1.String(), sizeof(info->name));
1282 			strcpy(info->MIME,  formats[i].MIME);
1283 			return B_OK;
1284 		}
1285 	}
1286 
1287 	return B_ERROR;
1288 }
1289 
1290 /*!
1291 	Frees jpeg alocated memory
1292 	Returns given error (B_ERROR by default)
1293 */
1294 status_t
1295 JPEGTranslator::Error(j_common_ptr cinfo, status_t error)
1296 {
1297 	jpeg_destroy(cinfo);
1298 	return error;
1299 }
1300 
1301 
1302 JPEGTranslator::JPEGTranslator()
1303 	: BaseTranslator(sTranslatorName, sTranslatorInfo, sTranslatorVersion,
1304 		sInputFormats,  kNumInputFormats,
1305 		sOutputFormats, kNumOutputFormats,
1306 		SETTINGS_FILE,
1307 		sDefaultSettings, kNumDefaultSettings,
1308 		B_TRANSLATOR_BITMAP, JPEG_FORMAT)
1309 {}
1310 
1311 
1312 BTranslator*
1313 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
1314 {
1315 	if (n == 0)
1316 		return new JPEGTranslator();
1317 
1318 	return NULL;
1319 }
1320 
1321 
1322 int
1323 main(int, char**)
1324 {
1325 	BApplication app("application/x-vnd.Haiku-JPEGTranslator");
1326 	JPEGTranslator* translator = new JPEGTranslator();
1327 	if (LaunchTranslatorWindow(translator, sTranslatorName) == B_OK)
1328 		app.Run();
1329 
1330 	return 0;
1331 }
1332 
1333