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