xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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 	infoView->MakeEditable(false);
688 
689 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
690 		.SetInsets(B_USE_DEFAULT_SPACING)
691 		.Add(title)
692 		.Add(version)
693 		.Add(infoView);
694 }
695 
696 
697 TranslatorView::TranslatorView(const char* name, TranslatorSettings* settings)
698 	:
699 	BTabView(name, B_WIDTH_FROM_LABEL)
700 {
701 	SetBorder(B_NO_BORDER);
702 
703 	AddTab(new TranslatorWriteView(B_TRANSLATE("Write"), settings->Acquire()));
704 	AddTab(new TranslatorReadView(B_TRANSLATE("Read"), settings->Acquire()));
705 	AddTab(new TranslatorAboutView(B_TRANSLATE("About")));
706 
707 	settings->Release();
708 
709  	BFont font;
710  	GetFont(&font);
711  	SetExplicitPreferredSize(
712 		BSize((font.Size() * 380) / 12, (font.Size() * 250) / 12));
713 }
714 
715 
716 //	#pragma mark - Translator Add-On
717 
718 
719 BView*
720 JPEGTranslator::NewConfigView(TranslatorSettings* settings)
721 {
722 	BView* configView = new TranslatorView("TranslatorView", settings);
723 	return configView;
724 }
725 
726 
727 /*! Determine whether or not we can handle this data */
728 status_t
729 JPEGTranslator::DerivedIdentify(BPositionIO* inSource,
730 	const translation_format* inFormat, BMessage* ioExtension,
731 	translator_info* outInfo, uint32 outType)
732 {
733 	if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT)
734 		return B_NO_TRANSLATOR;
735 
736 	// !!! You might need to make this buffer bigger to test for your native format
737 	off_t position = inSource->Position();
738 	char header[sizeof(TranslatorBitmap)];
739 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
740 	inSource->Seek(position, SEEK_SET);
741 	if (err < B_OK)
742 		return err;
743 
744 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
745 		if (PopulateInfoFromFormat(outInfo, B_TRANSLATOR_BITMAP) != B_OK)
746 			return B_NO_TRANSLATOR;
747 	} else {
748 		// First 3 bytes in jpg files are always the same from what i've seen so far
749 		// check them
750 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
751 			if (PopulateInfoFromFormat(outInfo, JPEG_FORMAT) != B_OK)
752 				return B_NO_TRANSLATOR;
753 
754 		} else
755 			return B_NO_TRANSLATOR;
756 	}
757 
758 	return B_OK;
759 }
760 
761 
762 status_t
763 JPEGTranslator::DerivedTranslate(BPositionIO* inSource,
764 	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
765 	BPositionIO* outDestination, int32 baseType)
766 {
767 	// If no specific type was requested, convert to the interchange format
768 	if (outType == 0)
769 		outType = B_TRANSLATOR_BITMAP;
770 
771 	// Setup a "breakpoint" since throwing exceptions does not seem to work
772 	// at all in an add-on. (?)
773 	// In the be_jerror.cpp we implement a handler for critical library errors
774 	// (be_error_exit()) and there we use the longjmp() function to return to
775 	// this place. If this happens, it is as if the setjmp() call is called
776 	// a second time, but this time the return value will be 1. The first
777 	// invokation will return 0.
778 	jmp_buf longJumpBuffer;
779 	int jmpRet = setjmp(longJumpBuffer);
780 	if (jmpRet == 1)
781 		return B_ERROR;
782 
783 	try {
784 		// What action to take, based on the findings of Identify()
785 		if (outType == inInfo->type) {
786 			return Copy(inSource, outDestination);
787 		} else if (inInfo->type == B_TRANSLATOR_BITMAP
788 				&& outType == JPEG_FORMAT) {
789 			return Compress(inSource, outDestination, &longJumpBuffer);
790 		} else if (inInfo->type == JPEG_FORMAT
791 				&& outType == B_TRANSLATOR_BITMAP) {
792 			return Decompress(inSource, outDestination, ioExtension,
793 				&longJumpBuffer);
794 		}
795 	} catch (...) {
796 		syslog(LOG_ERR, "libjpeg encountered a critical error (caught C++ "
797 			"exception).\n");
798 		return B_ERROR;
799 	}
800 
801 	return B_NO_TRANSLATOR;
802 }
803 
804 
805 /*!	The user has requested the same format for input and output, so just copy */
806 status_t
807 JPEGTranslator::Copy(BPositionIO* in, BPositionIO* out)
808 {
809 	int block_size = 65536;
810 	void* buffer = malloc(block_size);
811 	char temp[1024];
812 	if (buffer == NULL) {
813 		buffer = temp;
814 		block_size = 1024;
815 	}
816 	status_t err = B_OK;
817 
818 	// Read until end of file or error
819 	while (1) {
820 		ssize_t to_read = block_size;
821 		err = in->Read(buffer, to_read);
822 		// Explicit check for EOF
823 		if (err == -1) {
824 			if (buffer != temp) free(buffer);
825 			return B_OK;
826 		}
827 		if (err <= B_OK) break;
828 		to_read = err;
829 		err = out->Write(buffer, to_read);
830 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
831 		if (err < B_OK) break;
832 	}
833 
834 	if (buffer != temp) free(buffer);
835 	return (err >= 0) ? B_OK : err;
836 }
837 
838 
839 /*!	Encode into the native format */
840 status_t
841 JPEGTranslator::Compress(BPositionIO* in, BPositionIO* out,
842 	const jmp_buf* longJumpBuffer)
843 {
844 	using namespace conversion;
845 
846 	// Read info about bitmap
847 	TranslatorBitmap header;
848 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
849 	if (err < B_OK)
850 		return err;
851 	else if (err < (int)sizeof(TranslatorBitmap))
852 		return B_ERROR;
853 
854 	// Grab dimension, color space, and size information from the stream
855 	BRect bounds;
856 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
857 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
858 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
859 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
860 
861 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
862 
863 	int width = bounds.IntegerWidth() + 1;
864 	int height = bounds.IntegerHeight() + 1;
865 
866 	// Function pointer to convert function
867 	// It will point to proper function if needed
868 	void (*converter)(uchar* inscanline, uchar* outscanline,
869 		int32 inRowBytes) = NULL;
870 
871 	// Default color info
872 	J_COLOR_SPACE jpg_color_space = JCS_RGB;
873 	int jpg_input_components = 3;
874 	int32 out_row_bytes;
875 	int padding = 0;
876 
877 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) {
878 		case B_CMAP8:
879 			converter = convert_from_cmap8_to_24;
880 			padding = in_row_bytes - width;
881 			break;
882 
883 		case B_GRAY1:
884 			if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, NULL)) {
885 				converter = convert_from_gray1_to_24;
886 			} else {
887 				jpg_input_components = 1;
888 				jpg_color_space = JCS_GRAYSCALE;
889 				converter = convert_from_gray1_to_gray8;
890 			}
891 			padding = in_row_bytes - (width / 8);
892 			break;
893 
894 		case B_GRAY8:
895 			jpg_input_components = 1;
896 			jpg_color_space = JCS_GRAYSCALE;
897 			padding = in_row_bytes - width;
898 			break;
899 
900 		case B_RGB15:
901 		case B_RGBA15:
902 			converter = convert_from_15_to_24;
903 			padding = in_row_bytes - (width * 2);
904 			break;
905 
906 		case B_RGB15_BIG:
907 		case B_RGBA15_BIG:
908 			converter = convert_from_15b_to_24;
909 			padding = in_row_bytes - (width * 2);
910 			break;
911 
912 		case B_RGB16:
913 			converter = convert_from_16_to_24;
914 			padding = in_row_bytes - (width * 2);
915 			break;
916 
917 		case B_RGB16_BIG:
918 			converter = convert_from_16b_to_24;
919 			padding = in_row_bytes - (width * 2);
920 			break;
921 
922 		case B_RGB24:
923 			converter = convert_from_24_to_24;
924 			padding = in_row_bytes - (width * 3);
925 			break;
926 
927 		case B_RGB24_BIG:
928 			padding = in_row_bytes - (width * 3);
929 			break;
930 
931 		case B_RGB32:
932 		case B_RGBA32:
933 			converter = convert_from_32_to_24;
934 			padding = in_row_bytes - (width * 4);
935 			break;
936 
937 		case B_RGB32_BIG:
938 		case B_RGBA32_BIG:
939 			converter = convert_from_32b_to_24;
940 			padding = in_row_bytes - (width * 4);
941 			break;
942 
943 		case B_CMYK32:
944 			jpg_color_space = JCS_CMYK;
945 			jpg_input_components = 4;
946 			padding = in_row_bytes - (width * 4);
947 			break;
948 
949 		default:
950 			syslog(LOG_ERR, "Wrong type: Color space not implemented.\n");
951 			return B_ERROR;
952 	}
953 	out_row_bytes = jpg_input_components * width;
954 
955 	// Set basic things needed for jpeg writing
956 	struct jpeg_compress_struct cinfo;
957 	struct be_jpeg_error_mgr jerr;
958 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
959 	jpeg_create_compress(&cinfo);
960 	be_jpeg_stdio_dest(&cinfo, out);
961 
962 	// Set basic values
963 	cinfo.image_width = width;
964 	cinfo.image_height = height;
965 	cinfo.input_components = jpg_input_components;
966 	cinfo.in_color_space = jpg_color_space;
967 	jpeg_set_defaults(&cinfo);
968 
969 	// Set better accuracy
970 	cinfo.dct_method = JDCT_ISLOW;
971 
972 	// This is needed to prevent some colors loss
973 	// With it generated jpegs are as good as from Fireworks (at last! :D)
974 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL)) {
975 		int index = 0;
976 		while (index < cinfo.num_components) {
977 			cinfo.comp_info[index].h_samp_factor = 1;
978 			cinfo.comp_info[index].v_samp_factor = 1;
979 			// This will make file smaller, but with worse quality more or less
980 			// like with 93%-94% (but it's subjective opinion) on tested images
981 			// but with smaller size (between 92% and 93% on tested images)
982 			if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
983 				cinfo.comp_info[index].quant_tbl_no = 1;
984 			// This will make bigger file, but also better quality ;]
985 			// from my tests it seems like useless - better quality with smaller
986 			// can be acheived without this
987 //			cinfo.comp_info[index].dc_tbl_no = 1;
988 //			cinfo.comp_info[index].ac_tbl_no = 1;
989 			index++;
990 		}
991 	}
992 
993 	// Set quality
994 	jpeg_set_quality(&cinfo, fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL), true);
995 
996 	// Set progressive compression if needed
997 	// if not, turn on optimizing in libjpeg
998 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
999 		jpeg_simple_progression(&cinfo);
1000 	else
1001 		cinfo.optimize_coding = TRUE;
1002 
1003 	// Set smoothing (effect like Blur)
1004 	cinfo.smoothing_factor = fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL);
1005 
1006 	// Initialize compression
1007 	jpeg_start_compress(&cinfo, TRUE);
1008 
1009 	// Declare scanlines
1010 	JSAMPROW in_scanline = NULL;
1011 	JSAMPROW out_scanline = NULL;
1012 	JSAMPROW writeline;
1013 		// Pointer to in_scanline (default) or out_scanline (if there will be conversion)
1014 
1015 	// Allocate scanline
1016 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1017 	in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1018 		JPOOL_PERMANENT, in_row_bytes);
1019 
1020 	// We need 2nd scanline storage ony for conversion
1021 	if (converter != NULL) {
1022 		// There will be conversion, allocate second scanline...
1023 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1024 	    out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1025 	    	JPOOL_PERMANENT, out_row_bytes);
1026 		// ... and make it the one to write to file
1027 		writeline = out_scanline;
1028 	} else
1029 		writeline = in_scanline;
1030 
1031 	while (cinfo.next_scanline < cinfo.image_height) {
1032 		// Read scanline
1033 		err = in->Read(in_scanline, in_row_bytes);
1034 		if (err < in_row_bytes)
1035 			return err < B_OK ? Error((j_common_ptr)&cinfo, err)
1036 				: Error((j_common_ptr)&cinfo, B_ERROR);
1037 
1038 		// Convert if needed
1039 		if (converter != NULL)
1040 			converter(in_scanline, out_scanline, in_row_bytes - padding);
1041 
1042 		// Write scanline
1043 	   	jpeg_write_scanlines(&cinfo, &writeline, 1);
1044 	}
1045 
1046 	jpeg_finish_compress(&cinfo);
1047 	jpeg_destroy_compress(&cinfo);
1048 	return B_OK;
1049 }
1050 
1051 
1052 /*!	Decode the native format */
1053 status_t
1054 JPEGTranslator::Decompress(BPositionIO* in, BPositionIO* out,
1055 	BMessage* ioExtension, const jmp_buf* longJumpBuffer)
1056 {
1057 	using namespace conversion;
1058 
1059 	// Set basic things needed for jpeg reading
1060 	struct jpeg_decompress_struct cinfo;
1061 	struct be_jpeg_error_mgr jerr;
1062 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
1063 	jpeg_create_decompress(&cinfo);
1064 	be_jpeg_stdio_src(&cinfo, in);
1065 
1066 	jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
1067 		// make sure the EXIF tag is stored
1068 
1069 	// Read info about image
1070 	jpeg_read_header(&cinfo, TRUE);
1071 
1072 	BMessage exif;
1073 
1074 	// parse EXIF data and add it ioExtension, if any
1075 	jpeg_marker_struct* marker = cinfo.marker_list;
1076 	while (marker != NULL) {
1077 		if (marker->marker == MARKER_EXIF
1078 			&& !strncmp((char*)marker->data, "Exif", 4)) {
1079 			if (ioExtension != NULL) {
1080 				// Strip EXIF header from TIFF data
1081 				ioExtension->AddData("exif", B_RAW_TYPE,
1082 					(uint8*)marker->data + 6, marker->data_length - 6);
1083 			}
1084 
1085 			BMemoryIO io(marker->data + 6, marker->data_length - 6);
1086 			convert_exif_to_message(io, exif);
1087 		}
1088 		marker = marker->next;
1089 	}
1090 
1091 	// Default color info
1092 	color_space outColorSpace = B_RGB32;
1093 	int outColorComponents = 4;
1094 
1095 	// Function pointer to convert function
1096 	// It will point to proper function if needed
1097 	void (*converter)(uchar* inScanLine, uchar* outScanLine,
1098 		int32 inRowBytes, int32 xStep) = convert_from_24_to_32;
1099 
1100 	// If color space isn't rgb
1101 	if (cinfo.out_color_space != JCS_RGB) {
1102 		switch (cinfo.out_color_space) {
1103 			case JCS_UNKNOWN:		/* error/unspecified */
1104 				syslog(LOG_ERR, "From Type: Jpeg uses unknown color type\n");
1105 				break;
1106 			case JCS_GRAYSCALE:		/* monochrome */
1107 				// Check if user wants to read only as RGB32 or not
1108 				if (!fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL)) {
1109 					// Grayscale
1110 					outColorSpace = B_GRAY8;
1111 					outColorComponents = 1;
1112 					converter = translate_8;
1113 				} else {
1114 					// RGB
1115 					cinfo.out_color_space = JCS_RGB;
1116 					cinfo.output_components = 3;
1117 					converter = convert_from_24_to_32;
1118 				}
1119 				break;
1120 			case JCS_YCbCr:		/* Y/Cb/Cr (also known as YUV) */
1121 				cinfo.out_color_space = JCS_RGB;
1122 				converter = convert_from_24_to_32;
1123 				break;
1124 			case JCS_YCCK:		/* Y/Cb/Cr/K */
1125 				// Let libjpeg convert it to CMYK
1126 				cinfo.out_color_space = JCS_CMYK;
1127 				// Fall through to CMYK since we need the same settings
1128 			case JCS_CMYK:		/* C/M/Y/K */
1129 				// Use proper converter
1130 				if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK))
1131 					converter = convert_from_CMYK_to_32_photoshop;
1132 				else
1133 					converter = convert_from_CMYK_to_32;
1134 				break;
1135 			default:
1136 				syslog(LOG_ERR,
1137 						"From Type: Jpeg uses hmm... i don't know really :(\n");
1138 				break;
1139 		}
1140 	}
1141 
1142 	// Initialize decompression
1143 	jpeg_start_decompress(&cinfo);
1144 
1145 	// retrieve orientation from settings/EXIF
1146 	int32 orientation;
1147 	if (ioExtension == NULL
1148 		|| ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) {
1149 		if (exif.FindInt32("Orientation", &orientation) != B_OK)
1150 			orientation = 1;
1151 	}
1152 
1153 	if (orientation != 1 && converter == NULL)
1154 		converter = translate_8;
1155 
1156 	int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width;
1157 	int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height;
1158 
1159 	int32 destOffset = dest_index(outputWidth, outputHeight,
1160 		0, 0, orientation) * outColorComponents;
1161 	int32 xStep = dest_index(outputWidth, outputHeight,
1162 		1, 0, orientation) * outColorComponents - destOffset;
1163 	int32 yStep = dest_index(outputWidth, outputHeight,
1164 		0, 1, orientation) * outColorComponents - destOffset;
1165 	bool needAll = orientation != 1;
1166 
1167 	// Initialize this bounds rect to the size of your image
1168 	BRect bounds(0, 0, outputWidth - 1, outputHeight - 1);
1169 
1170 #if 0
1171 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n",
1172 	destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height,
1173 	bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation);
1174 #endif
1175 
1176 	// Bytes count in one line of image (scanline)
1177 	int32 inRowBytes = cinfo.output_width * cinfo.output_components;
1178 	int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents;
1179 	int32 dataSize = cinfo.output_width * cinfo.output_height
1180 		* outColorComponents;
1181 
1182 	// Fill out the B_TRANSLATOR_BITMAP's header
1183 	TranslatorBitmap header;
1184 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1185 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1186 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1187 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1188 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1189 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
1190 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
1191 	header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize);
1192 
1193 	// Write out the header
1194 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1195 	if (err < B_OK)
1196 		return Error((j_common_ptr)&cinfo, err);
1197 	else if (err < (int)sizeof(TranslatorBitmap))
1198 		return Error((j_common_ptr)&cinfo, B_ERROR);
1199 
1200 	// Declare scanlines
1201 	JSAMPROW inScanLine = NULL;
1202 	uint8* dest = NULL;
1203 	uint8* destLine = NULL;
1204 
1205 	// Allocate scanline
1206 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1207     inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1208     	JPOOL_PERMANENT, inRowBytes);
1209 
1210 	// We need 2nd scanline storage only for conversion
1211 	if (converter != NULL) {
1212 		// There will be conversion, allocate second scanline...
1213 		// Use libjpeg memory allocation functions, so in case of error it will
1214 		// free them itself
1215 	    dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1216 	    	JPOOL_PERMANENT, needAll ? dataSize : rowBytes);
1217 	    destLine = dest + destOffset;
1218 	} else
1219 		destLine = inScanLine;
1220 
1221 	while (cinfo.output_scanline < cinfo.output_height) {
1222 		// Read scanline
1223 		jpeg_read_scanlines(&cinfo, &inScanLine, 1);
1224 
1225 		// Convert if needed
1226 		if (converter != NULL)
1227 			converter(inScanLine, destLine, inRowBytes, xStep);
1228 
1229 		if (!needAll) {
1230 	  		// Write the scanline buffer to the output stream
1231 			ssize_t bytesWritten = out->Write(destLine, rowBytes);
1232 			if (bytesWritten < rowBytes) {
1233 				return bytesWritten < B_OK
1234 					? Error((j_common_ptr)&cinfo, bytesWritten)
1235 					: Error((j_common_ptr)&cinfo, B_ERROR);
1236 			}
1237 		} else
1238 			destLine += yStep;
1239 	}
1240 
1241 	if (needAll) {
1242 		ssize_t bytesWritten = out->Write(dest, dataSize);
1243 		if (bytesWritten < dataSize) {
1244 			return bytesWritten < B_OK
1245 				? Error((j_common_ptr)&cinfo, bytesWritten)
1246 				: Error((j_common_ptr)&cinfo, B_ERROR);
1247 		}
1248 	}
1249 
1250 	jpeg_finish_decompress(&cinfo);
1251 	jpeg_destroy_decompress(&cinfo);
1252 	return B_OK;
1253 }
1254 
1255 /*! have the other PopulateInfoFromFormat() check both inputFormats & outputFormats */
1256 status_t
1257 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1258 	uint32 formatType, translator_id id)
1259 {
1260 	int32 formatCount;
1261 	const translation_format* formats = OutputFormats(&formatCount);
1262 	for (int i = 0; i <= 1 ;formats = InputFormats(&formatCount), i++) {
1263 		if (PopulateInfoFromFormat(info, formatType,
1264 			formats, formatCount) == B_OK) {
1265 			info->translator = id;
1266 			return B_OK;
1267 		}
1268 	}
1269 
1270 	return B_ERROR;
1271 }
1272 
1273 
1274 status_t
1275 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1276 	uint32 formatType, const translation_format* formats, int32 formatCount)
1277 {
1278 	for (int i = 0; i < formatCount; i++) {
1279 		if (formats[i].type == formatType) {
1280 			info->type = formatType;
1281 			info->group = formats[i].group;
1282 			info->quality = formats[i].quality;
1283 			info->capability = formats[i].capability;
1284 			BString str1(formats[i].name);
1285 			str1.ReplaceFirst("Be Bitmap Format (JPEGTranslator)",
1286 				B_TRANSLATE("Be Bitmap Format (JPEGTranslator)"));
1287 			strncpy(info->name, str1.String(), sizeof(info->name));
1288 			strcpy(info->MIME,  formats[i].MIME);
1289 			return B_OK;
1290 		}
1291 	}
1292 
1293 	return B_ERROR;
1294 }
1295 
1296 /*!
1297 	Frees jpeg alocated memory
1298 	Returns given error (B_ERROR by default)
1299 */
1300 status_t
1301 JPEGTranslator::Error(j_common_ptr cinfo, status_t error)
1302 {
1303 	jpeg_destroy(cinfo);
1304 	return error;
1305 }
1306 
1307 
1308 JPEGTranslator::JPEGTranslator()
1309 	: BaseTranslator(sTranslatorName, sTranslatorInfo, sTranslatorVersion,
1310 		sInputFormats,  kNumInputFormats,
1311 		sOutputFormats, kNumOutputFormats,
1312 		SETTINGS_FILE,
1313 		sDefaultSettings, kNumDefaultSettings,
1314 		B_TRANSLATOR_BITMAP, JPEG_FORMAT)
1315 {}
1316 
1317 
1318 BTranslator*
1319 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
1320 {
1321 	if (n == 0)
1322 		return new JPEGTranslator();
1323 
1324 	return NULL;
1325 }
1326 
1327 
1328 int
1329 main(int, char**)
1330 {
1331 	BApplication app("application/x-vnd.Haiku-JPEGTranslator");
1332 	JPEGTranslator* translator = new JPEGTranslator();
1333 	if (LaunchTranslatorWindow(translator, sTranslatorName) == B_OK)
1334 		app.Run();
1335 
1336 	return 0;
1337 }
1338 
1339