xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision 020cbad9d40235a2c50a81a42d69912a5ff8fbc4)
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 "exif_parser.h"
36 
37 #include <TabView.h>
38 
39 
40 #define MARKER_EXIF	0xe1
41 
42 // Set these accordingly
43 #define JPEG_ACRONYM "JPEG"
44 #define JPEG_FORMAT 'JPEG'
45 #define JPEG_MIME_STRING "image/jpeg"
46 #define JPEG_DESCRIPTION "JPEG image"
47 
48 // The translation kit's native file type
49 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap"
50 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap image"
51 
52 // Translation Kit required globals
53 char translatorName[] = "JPEG Images";
54 char translatorInfo[] =
55 	"©2002-2003, Marcin Konicki\n"
56 	"©2005-2007, Haiku\n"
57 	"\n"
58 	"Based on IJG library © 1991-1998, Thomas G. Lane\n"
59 	"          http://www.ijg.org/files/\n"
60 	"with \"Lossless\" encoding support patch by Ken Murchison\n"
61 	"          http://www.oceana.com/ftp/ljpeg/\n"
62 	"\n"
63 	"With some colorspace conversion routines by Magnus Hellman\n"
64 	"          http://www.bebits.com/app/802\n";
65 
66 int32 translatorVersion = 0x120;
67 
68 // Define the formats we know how to read
69 translation_format inputFormats[] = {
70 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
71 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
72 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
73 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
74 	{}
75 };
76 
77 // Define the formats we know how to write
78 translation_format outputFormats[] = {
79 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
80 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
81 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
82 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
83 	{}
84 };
85 
86 // Main functions of translator :)
87 static status_t Copy(BPositionIO *in, BPositionIO *out);
88 static status_t Compress(BPositionIO *in, BPositionIO *out,
89 	const jmp_buf* longJumpBuffer);
90 static status_t Decompress(BPositionIO *in, BPositionIO *out,
91 	BMessage* ioExtension, const jmp_buf* longJumpBuffer);
92 static status_t Error(j_common_ptr cinfo, status_t error = B_ERROR);
93 
94 
95 bool gAreSettingsRunning = false;
96 
97 
98 //!	Make settings to defaults
99 void
100 LoadDefaultSettings(jpeg_settings *settings)
101 {
102 	settings->Smoothing = 0;
103 	settings->Quality = 95;
104 	settings->Progressive = true;
105 	settings->OptimizeColors = true;
106 	settings->SmallerFile = false;
107 	settings->B_GRAY1_as_B_RGB24 = false;
108 	settings->Always_B_RGB32 = true;
109 	settings->PhotoshopCMYK = true;
110 	settings->ShowReadWarningBox = true;
111 }
112 
113 
114 //!	Save settings to config file
115 void
116 SaveSettings(jpeg_settings *settings)
117 {
118 	// Make path to settings file
119 	BPath path;
120 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
121 		return;
122 
123 	path.Append(SETTINGS_FILE);
124 
125 	// Open settings file (create it if there's no file) and write settings
126 	FILE *file = NULL;
127 	if ((file = fopen(path.Path(), "wb+"))) {
128 		fwrite(settings, sizeof(jpeg_settings), 1, file);
129 		fclose(file);
130 	}
131 }
132 
133 
134 //!	Return true if settings were run, false if not
135 bool
136 SettingsChangedAlert()
137 {
138 	// If settings view wasn't already initialized (settings not running)
139 	// and user wants to run settings
140 	if (!gAreSettingsRunning
141 		&& (new BAlert("Different settings file",
142 				"JPEG settings were set to default because of incompatible settings file.",
143 				"Configure settings", "OK", NULL, B_WIDTH_AS_USUAL,
144 				B_WARNING_ALERT))->Go() == 0) {
145 		// Create settings window (with no quit on close!), launch
146 		// it and wait until it's closed
147 		TranslatorWindow *window = new TranslatorWindow(false);
148 		window->Show();
149 
150 		status_t err;
151 		wait_for_thread(window->Thread(), &err);
152 		return true;
153 	}
154 
155 	return false;
156 }
157 
158 
159 /*!
160 	Load settings from config file
161 	If can't find it make them default and try to save
162 */
163 void
164 LoadSettings(jpeg_settings *settings)
165 {
166 	// Make path to settings file
167 	BPath path;
168 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) {
169 		LoadDefaultSettings(settings);
170 		return;
171 	}
172 
173 	path.Append(SETTINGS_FILE);
174 
175 	// Open settings file (create it if there's no file) and write settings
176 	FILE *file = NULL;
177 	if ((file = fopen(path.Path(), "rb")) != NULL) {
178 		if (!fread(settings, sizeof(jpeg_settings), 1, file)) {
179 			// settings struct has changed size
180 			// Load default settings, and Save them
181 			fclose(file);
182 			LoadDefaultSettings(settings);
183 			SaveSettings(settings);
184 			// Tell user settings were changed to default, and ask to run settings panel or not
185 			if (SettingsChangedAlert())
186 				// User configured settings, load them again
187 				LoadSettings(settings);
188 		} else
189 			fclose(file);
190 	} else if ((file = fopen(path.Path(), "wb+")) != NULL) {
191 		LoadDefaultSettings(settings);
192 		fwrite(settings, sizeof(jpeg_settings), 1, file);
193 		fclose(file);
194 	}
195 }
196 
197 
198 static bool
199 x_flipped(int32 orientation)
200 {
201 	return orientation == 2 || orientation == 3
202 		|| orientation == 6 || orientation == 7;
203 }
204 
205 
206 static bool
207 y_flipped(int32 orientation)
208 {
209 	return orientation == 3 || orientation == 4
210 		|| orientation == 7 || orientation == 8;
211 }
212 
213 
214 static int32
215 dest_index(uint32 width, uint32 height, uint32 x, uint32 y, int32 orientation)
216 {
217 	if (orientation > 4) {
218 		uint32 temp = x;
219 		x = y;
220 		y = temp;
221 	}
222 	if (y_flipped(orientation))
223 		y = height - 1 - y;
224 	if (x_flipped(orientation))
225 		x = width - 1 - x;
226 
227 	return y * width + x;
228 }
229 
230 
231 //	#pragma mark - conversion for compression
232 
233 
234 inline void
235 convert_from_gray1_to_gray8(uint8* in, uint8* out, int32 inRowBytes)
236 {
237 	int32 index = 0;
238 	int32 index2 = 0;
239 	while (index < inRowBytes) {
240 		unsigned char c = in[index++];
241 		for (int b = 128; b; b = b>>1) {
242 			unsigned char color;
243 			if (c & b)
244 				color = 0;
245 			else
246 				color = 255;
247 			out[index2++] = color;
248 		}
249 	}
250 }
251 
252 
253 inline void
254 convert_from_gray1_to_24(uint8* in, uint8* out, int32 inRowBytes)
255 {
256 	int32 index = 0;
257 	int32 index2 = 0;
258 	while (index < inRowBytes) {
259 		unsigned char c = in[index++];
260 		for (int b = 128; b; b = b>>1) {
261 			unsigned char color;
262 			if (c & b)
263 				color = 0;
264 			else
265 				color = 255;
266 			out[index2++] = color;
267 			out[index2++] = color;
268 			out[index2++] = color;
269 		}
270 	}
271 }
272 
273 
274 inline void
275 convert_from_cmap8_to_24(uint8* in, uint8* out, int32 inRowBytes)
276 {
277 	const color_map * map = system_colors();
278 	int32 index = 0;
279 	int32 index2 = 0;
280 	while (index < inRowBytes) {
281 		rgb_color color = map->color_list[in[index++]];
282 
283 		out[index2++] = color.red;
284 		out[index2++] = color.green;
285 		out[index2++] = color.blue;
286 	}
287 }
288 
289 
290 inline void
291 convert_from_15_to_24(uint8* in, uint8* out, int32 inRowBytes)
292 {
293 	int32 index = 0;
294 	int32 index2 = 0;
295 	int16 in_pixel;
296 	while (index < inRowBytes) {
297 		in_pixel = in[index] | (in[index+1] << 8);
298 		index += 2;
299 
300 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
301 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
302 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
303 	}
304 }
305 
306 
307 inline void
308 convert_from_15b_to_24(uint8* in, uint8* out, int32 inRowBytes)
309 {
310 	int32 index = 0;
311 	int32 index2 = 0;
312 	int16 in_pixel;
313 	while (index < inRowBytes) {
314 		in_pixel = in[index+1] | (in[index] << 8);
315 		index += 2;
316 
317 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
318 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
319 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
320 	}
321 }
322 
323 
324 inline void
325 convert_from_16_to_24(uint8* in, uint8* out, int32 inRowBytes)
326 {
327 	int32 index = 0;
328 	int32 index2 = 0;
329 	int16 in_pixel;
330 	while (index < inRowBytes) {
331 		in_pixel = in[index] | (in[index+1] << 8);
332 		index += 2;
333 
334 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
335 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
336 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
337 	}
338 }
339 
340 
341 inline void
342 convert_from_16b_to_24(uint8* in, uint8* out, int32 inRowBytes)
343 {
344 	int32 index = 0;
345 	int32 index2 = 0;
346 	int16 in_pixel;
347 	while (index < inRowBytes) {
348 		in_pixel = in[index+1] | (in[index] << 8);
349 		index += 2;
350 
351 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
352 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
353 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
354 	}
355 }
356 
357 
358 inline void
359 convert_from_24_to_24(uint8* in, uint8* out, int32 inRowBytes)
360 {
361 	int32 index = 0;
362 	int32 index2 = 0;
363 	while (index < inRowBytes) {
364 		out[index2++] = in[index+2];
365 		out[index2++] = in[index+1];
366 		out[index2++] = in[index];
367 		index+=3;
368 	}
369 }
370 
371 
372 inline void
373 convert_from_32_to_24(uint8* in, uint8* out, int32 inRowBytes)
374 {
375 	inRowBytes /= 4;
376 
377 	for (int32 i = 0; i < inRowBytes; i++) {
378 		out[0] = in[2];
379 		out[1] = in[1];
380 		out[2] = in[0];
381 
382 		in += 4;
383 		out += 3;
384 	}
385 }
386 
387 
388 inline void
389 convert_from_32b_to_24(uint8* in, uint8* out, int32 inRowBytes)
390 {
391 	inRowBytes /= 4;
392 
393 	for (int32 i = 0; i < inRowBytes; i++) {
394 		out[0] = in[1];
395 		out[1] = in[2];
396 		out[2] = in[3];
397 
398 		in += 4;
399 		out += 3;
400 	}
401 }
402 
403 
404 //	#pragma mark - conversion for decompression
405 
406 
407 inline void
408 convert_from_CMYK_to_32_photoshop(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
409 {
410 	for (int32 i = 0; i < inRowBytes; i += 4) {
411 		int32 black = in[3];
412 		out[0] = in[2] * black / 255;
413 		out[1] = in[1] * black / 255;
414 		out[2] = in[0] * black / 255;
415 		out[3] = 255;
416 
417 		in += 4;
418 		out += xStep;
419 	}
420 }
421 
422 
423 //!	!!! UNTESTED !!!
424 inline void
425 convert_from_CMYK_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
426 {
427 	for (int32 i = 0; i < inRowBytes; i += 4) {
428 		int32 black = 255 - in[3];
429 		out[0] = ((255 - in[2]) * black) / 255;
430 		out[1] = ((255 - in[1]) * black) / 255;
431 		out[2] = ((255 - in[0]) * black) / 255;
432 		out[3] = 255;
433 
434 		in += 4;
435 		out += xStep;
436 	}
437 }
438 
439 
440 //!	RGB24 8:8:8 to xRGB 8:8:8:8
441 inline void
442 convert_from_24_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
443 {
444 	for (int32 i = 0; i < inRowBytes; i += 3) {
445 		out[0] = in[2];
446 		out[1] = in[1];
447 		out[2] = in[0];
448 		out[3] = 255;
449 
450 		in += 3;
451 		out += xStep;
452 	}
453 }
454 
455 
456 //! 8-bit to 8-bit, only need when rotating the image
457 void
458 translate_8(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
459 {
460 	for (int32 i = 0; i < inRowBytes; i++) {
461 		out[0] = in[0];
462 
463 		in++;
464 		out += xStep;
465 	}
466 }
467 
468 
469 //	#pragma mark - SView
470 
471 
472 SView::SView(const char *name, float x, float y)
473 	: BView(BRect(x, y, x, y), name, B_FOLLOW_NONE, B_WILL_DRAW)
474 {
475 	fPreferredWidth = 0;
476 	fPreferredHeight = 0;
477 
478 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
479 	SetLowColor(ViewColor());
480 
481 	SetFont(be_plain_font);
482 }
483 
484 
485 void
486 SView::GetPreferredSize(float* _width, float* _height)
487 {
488 	if (_width)
489 		*_width = fPreferredWidth;
490 	if (_height)
491 		*_height = fPreferredHeight;
492 }
493 
494 
495 void
496 SView::ResizeToPreferred()
497 {
498 	ResizeTo(fPreferredWidth, fPreferredHeight);
499 }
500 
501 
502 void
503 SView::ResizePreferredBy(float width, float height)
504 {
505 	fPreferredWidth += width;
506 	fPreferredHeight += height;
507 }
508 
509 
510 void
511 SView::AddChild(BView *child, BView *before)
512 {
513 	BView::AddChild(child, before);
514 	child->ResizeToPreferred();
515 	BRect frame = child->Frame();
516 
517 	if (frame.right > fPreferredWidth)
518 		fPreferredWidth = frame.right;
519 	if (frame.bottom > fPreferredHeight)
520 		fPreferredHeight = frame.bottom;
521 }
522 
523 
524 //	#pragma mark -
525 
526 
527 SSlider::SSlider(BRect frame, const char *name, const char *label,
528 		BMessage *message, int32 minValue, int32 maxValue, orientation posture,
529 		thumb_style thumbType, uint32 resizingMode, uint32 flags)
530 	: BSlider(frame, name, label, message, minValue, maxValue,
531 		posture, thumbType, resizingMode, flags)
532 {
533 	rgb_color barColor = { 0, 0, 229, 255 };
534 	UseFillColor(true, &barColor);
535 }
536 
537 
538 //!	Update status string - show actual value
539 char*
540 SSlider::UpdateText() const
541 {
542 	snprintf(fStatusLabel, sizeof(fStatusLabel), "%ld", Value());
543 	return fStatusLabel;
544 }
545 
546 
547 //!	BSlider::ResizeToPreferred + Resize width if it's too small to show label and status
548 void
549 SSlider::ResizeToPreferred()
550 {
551 	int32 width = (int32)ceil(StringWidth(Label()) + StringWidth("9999"));
552 	if (width < 230)
553 		width = 230;
554 
555 	float w, h;
556 	GetPreferredSize(&w, &h);
557 	ResizeTo(width, h);
558 }
559 
560 
561 //	#pragma mark -
562 
563 
564 TranslatorReadView::TranslatorReadView(const char *name, jpeg_settings *settings,
565 		float x, float y)
566 	: SView(name, x, y),
567 	fSettings(settings)
568 {
569 	fAlwaysRGB32 = new BCheckBox(BRect(10, GetPreferredHeight(), 10,
570 		GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_ALWAYSRGB32,
571 		new BMessage(VIEW_MSG_SET_ALWAYSRGB32));
572 	fAlwaysRGB32->SetFont(be_plain_font);
573 	if (fSettings->Always_B_RGB32)
574 		fAlwaysRGB32->SetValue(1);
575 
576 	AddChild(fAlwaysRGB32);
577 
578 	fPhotoshopCMYK = new BCheckBox(BRect(10, GetPreferredHeight(), 10,
579 		GetPreferredHeight()), "photoshopCMYK", VIEW_LABEL_PHOTOSHOPCMYK,
580 		new BMessage(VIEW_MSG_SET_PHOTOSHOPCMYK));
581 	fPhotoshopCMYK->SetFont(be_plain_font);
582 	if (fSettings->PhotoshopCMYK)
583 		fPhotoshopCMYK->SetValue(1);
584 
585 	AddChild(fPhotoshopCMYK);
586 
587 	fShowErrorBox = new BCheckBox(BRect(10, GetPreferredHeight(), 10,
588 		GetPreferredHeight()), "error", VIEW_LABEL_SHOWREADERRORBOX,
589 		new BMessage(VIEW_MSG_SET_SHOWREADERRORBOX));
590 	fShowErrorBox->SetFont(be_plain_font);
591 	if (fSettings->ShowReadWarningBox)
592 		fShowErrorBox->SetValue(1);
593 
594 	AddChild(fShowErrorBox);
595 
596 	ResizeToPreferred();
597 }
598 
599 
600 void
601 TranslatorReadView::AttachedToWindow()
602 {
603 	fAlwaysRGB32->SetTarget(this);
604 	fPhotoshopCMYK->SetTarget(this);
605 	fShowErrorBox->SetTarget(this);
606 }
607 
608 
609 void
610 TranslatorReadView::MessageReceived(BMessage* message)
611 {
612 	switch (message->what) {
613 		case VIEW_MSG_SET_ALWAYSRGB32:
614 		{
615 			int32 value;
616 			if (message->FindInt32("be:value", &value) == B_OK) {
617 				fSettings->Always_B_RGB32 = value;
618 				SaveSettings(fSettings);
619 			}
620 			break;
621 		}
622 		case VIEW_MSG_SET_PHOTOSHOPCMYK:
623 		{
624 			int32 value;
625 			if (message->FindInt32("be:value", &value) == B_OK) {
626 				fSettings->PhotoshopCMYK = value;
627 				SaveSettings(fSettings);
628 			}
629 			break;
630 		}
631 		case VIEW_MSG_SET_SHOWREADERRORBOX:
632 		{
633 			int32 value;
634 			if (message->FindInt32("be:value", &value) == B_OK) {
635 				fSettings->ShowReadWarningBox = value;
636 				SaveSettings(fSettings);
637 			}
638 			break;
639 		}
640 		default:
641 			BView::MessageReceived(message);
642 			break;
643 	}
644 }
645 
646 
647 //	#pragma mark - TranslatorWriteView
648 
649 
650 TranslatorWriteView::TranslatorWriteView(const char *name, jpeg_settings *settings,
651 		float x, float y)
652 	: SView(name, x, y),
653 	fSettings(settings)
654 {
655 	fQualitySlider = new SSlider(BRect(10, GetPreferredHeight(), 10,
656 		GetPreferredHeight()), "quality", VIEW_LABEL_QUALITY,
657 		new BMessage(VIEW_MSG_SET_QUALITY), 0, 100);
658 	fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
659 	fQualitySlider->SetHashMarkCount(10);
660 	fQualitySlider->SetLimitLabels("Low", "High");
661 	fQualitySlider->SetFont(be_plain_font);
662 	fQualitySlider->SetValue(fSettings->Quality);
663 	AddChild(fQualitySlider);
664 
665 	fSmoothingSlider = new SSlider(BRect(10, GetPreferredHeight()+10, 10,
666 		GetPreferredHeight()), "smoothing", VIEW_LABEL_SMOOTHING,
667 		new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100);
668 	fSmoothingSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
669 	fSmoothingSlider->SetHashMarkCount(10);
670 	fSmoothingSlider->SetLimitLabels("None", "High");
671 	fSmoothingSlider->SetFont(be_plain_font);
672 	fSmoothingSlider->SetValue(fSettings->Smoothing);
673 	AddChild(fSmoothingSlider);
674 
675 	fProgress = new BCheckBox(BRect(10, GetPreferredHeight()+10, 10,
676 		GetPreferredHeight()), "progress", VIEW_LABEL_PROGRESSIVE,
677 		new BMessage(VIEW_MSG_SET_PROGRESSIVE));
678 	fProgress->SetFont(be_plain_font);
679 	if (fSettings->Progressive)
680 		fProgress->SetValue(1);
681 
682 	AddChild(fProgress);
683 
684 	fOptimizeColors = new BCheckBox(BRect(10, GetPreferredHeight()+5, 10,
685 		GetPreferredHeight() + 5), "optimizecolors", VIEW_LABEL_OPTIMIZECOLORS,
686 		new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS));
687 	fOptimizeColors->SetFont(be_plain_font);
688 	if (fSettings->OptimizeColors)
689 		fOptimizeColors->SetValue(1);
690 
691 	AddChild(fOptimizeColors);
692 
693 	fSmallerFile = new BCheckBox(BRect(25, GetPreferredHeight()+5, 25,
694 		GetPreferredHeight() + 5), "smallerfile", VIEW_LABEL_SMALLERFILE,
695 		new BMessage(VIEW_MSG_SET_SMALLERFILE));
696 	fSmallerFile->SetFont(be_plain_font);
697 	if (fSettings->SmallerFile)
698 		fSmallerFile->SetValue(1);
699 	if (!fSettings->OptimizeColors)
700 		fSmallerFile->SetEnabled(false);
701 
702 	AddChild(fSmallerFile);
703 
704 	fGrayAsRGB24 = new BCheckBox(BRect(10, GetPreferredHeight()+5, 25,
705 		GetPreferredHeight()+5), "gray1asrgb24", VIEW_LABEL_GRAY1ASRGB24,
706 		new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
707 	fGrayAsRGB24->SetFont(be_plain_font);
708 	if (fSettings->B_GRAY1_as_B_RGB24)
709 		fGrayAsRGB24->SetValue(1);
710 
711 	AddChild(fGrayAsRGB24);
712 
713 	ResizeToPreferred();
714 }
715 
716 
717 void
718 TranslatorWriteView::AttachedToWindow()
719 {
720 	fQualitySlider->SetTarget(this);
721 	fSmoothingSlider->SetTarget(this);
722 	fProgress->SetTarget(this);
723 	fOptimizeColors->SetTarget(this);
724 	fSmallerFile->SetTarget(this);
725 	fGrayAsRGB24->SetTarget(this);
726 }
727 
728 
729 void
730 TranslatorWriteView::MessageReceived(BMessage *message)
731 {
732 	switch (message->what) {
733 		case VIEW_MSG_SET_QUALITY:
734 		{
735 			int32 value;
736 			if (message->FindInt32("be:value", &value) == B_OK) {
737 				fSettings->Quality = value;
738 				SaveSettings(fSettings);
739 			}
740 			break;
741 		}
742 		case VIEW_MSG_SET_SMOOTHING:
743 		{
744 			int32 value;
745 			if (message->FindInt32("be:value", &value) == B_OK) {
746 				fSettings->Smoothing = value;
747 				SaveSettings(fSettings);
748 			}
749 			break;
750 		}
751 		case VIEW_MSG_SET_PROGRESSIVE:
752 		{
753 			int32 value;
754 			if (message->FindInt32("be:value", &value) == B_OK) {
755 				fSettings->Progressive = value;
756 				SaveSettings(fSettings);
757 			}
758 			break;
759 		}
760 		case VIEW_MSG_SET_OPTIMIZECOLORS:
761 		{
762 			int32 value;
763 			if (message->FindInt32("be:value", &value) == B_OK) {
764 				fSettings->OptimizeColors = value;
765 				SaveSettings(fSettings);
766 			}
767 			fSmallerFile->SetEnabled(fSettings->OptimizeColors);
768 			break;
769 		}
770 		case VIEW_MSG_SET_SMALLERFILE:
771 		{
772 			int32 value;
773 			if (message->FindInt32("be:value", &value) == B_OK) {
774 				fSettings->SmallerFile = value;
775 				SaveSettings(fSettings);
776 			}
777 			break;
778 		}
779 		case VIEW_MSG_SET_GRAY1ASRGB24:
780 		{
781 			int32 value;
782 			if (message->FindInt32("be:value", &value) == B_OK) {
783 				fSettings->B_GRAY1_as_B_RGB24 = value;
784 				SaveSettings(fSettings);
785 			}
786 			break;
787 		}
788 		default:
789 			BView::MessageReceived(message);
790 			break;
791 	}
792 }
793 
794 
795 //	#pragma mark -
796 
797 
798 TranslatorAboutView::TranslatorAboutView(const char *name, float x, float y)
799 	: SView(name, x, y)
800 {
801 	BStringView *title = new BStringView(BRect(10, 0, 10, 0), "Title",
802 		translatorName);
803 	title->SetFont(be_bold_font);
804 
805 	AddChild(title);
806 
807 	BRect rect = title->Bounds();
808 	float space = title->StringWidth("    ");
809 
810 	char versionString[16];
811 	sprintf(versionString, "v%d.%d.%d", (int)(translatorVersion >> 8),
812 		(int)((translatorVersion >> 4) & 0xf), (int)(translatorVersion & 0xf));
813 
814 	BStringView *version = new BStringView(BRect(rect.right+space, rect.top,
815 		rect.right+space, rect.top), "Version", versionString);
816 	version->SetFont(be_plain_font);
817 	version->SetFontSize(9);
818 	// Make version be in the same line as title
819 	version->ResizeToPreferred();
820 	version->MoveBy(0, rect.bottom-version->Frame().bottom);
821 
822 	AddChild(version);
823 
824 	// Now for each line in translatorInfo add a BStringView
825 	char* current = translatorInfo;
826 	int32 index = 1;
827 	while (current != NULL && current[0]) {
828 		char text[128];
829 		char* newLine = strchr(current, '\n');
830 		if (newLine == NULL) {
831 			strlcpy(text, current, sizeof(text));
832 			current = NULL;
833 		} else {
834 			strlcpy(text, current, min_c((int32)sizeof(text), newLine + 1 - current));
835 			current = newLine + 1;
836 		}
837 
838 		BStringView* string = new BStringView(BRect(10, GetPreferredHeight(),
839 			10, GetPreferredHeight()), "copyright", text);
840 		if (index > 3)
841 			string->SetFontSize(9);
842 		AddChild(string);
843 
844 		index++;
845 	}
846 
847 	ResizeToPreferred();
848 }
849 
850 
851 //	#pragma mark -
852 
853 
854 TranslatorView::TranslatorView(const char *name)
855 	: SView(name),
856 	fTabWidth(30),
857 	fActiveChild(0)
858 {
859 	// Set global var to true
860 	gAreSettingsRunning = true;
861 
862 	// Load settings to global settings struct
863 	LoadSettings(&fSettings);
864 
865 	font_height fontHeight;
866 	GetFontHeight(&fontHeight);
867 	fTabHeight = (int32)ceilf(fontHeight.ascent + fontHeight.descent + fontHeight.leading) + 7;
868 	// Add left and top margins
869 	float top = fTabHeight + 20;
870 	float left = 0;
871 
872 	// This will remember longest string width
873 	int32 nameWidth = 0;
874 
875 	SView *view = new TranslatorWriteView("Write", &fSettings, left, top);
876 	AddChild(view);
877 	nameWidth = (int32)StringWidth(view->Name());
878 	fTabs.AddItem(new BTab(view));
879 
880 	view = new TranslatorReadView("Read", &fSettings, left, top);
881 	AddChild(view);
882 	if (nameWidth < StringWidth(view->Name()))
883 		nameWidth = (int32)StringWidth(view->Name());
884 	fTabs.AddItem(new BTab(view));
885 
886 	view = new TranslatorAboutView("About", left, top);
887 	AddChild(view);
888 	if (nameWidth < StringWidth(view->Name()))
889 		nameWidth = (int32)StringWidth(view->Name());
890 	fTabs.AddItem(new BTab(view));
891 
892 	fTabWidth += nameWidth;
893 	if (fTabWidth * CountChildren() > GetPreferredWidth())
894 		ResizePreferredBy((fTabWidth * CountChildren()) - GetPreferredWidth(), 0);
895 
896 	// Add right and bottom margins
897 	ResizePreferredBy(10, 15);
898 
899 	ResizeToPreferred();
900 
901 	// Make TranslatorView resize itself with parent
902 	SetFlags(Flags() | B_FOLLOW_ALL);
903 }
904 
905 
906 TranslatorView::~TranslatorView()
907 {
908 	gAreSettingsRunning = false;
909 
910 	BTab* tab;
911 	while ((tab = (BTab*)fTabs.RemoveItem((int32)0)) != NULL) {
912 		delete tab;
913 	}
914 }
915 
916 
917 //!	Attached to window - resize parent to preferred
918 void
919 TranslatorView::AttachedToWindow()
920 {
921 	// Hide all children except first one
922 	BView *child;
923 	int32 index = 1;
924 	while ((child = ChildAt(index++)) != NULL)
925 		child->Hide();
926 
927 }
928 
929 
930 BRect
931 TranslatorView::_TabFrame(int32 index) const
932 {
933 	return BRect(index * fTabWidth, 10, (index + 1) * fTabWidth, 10 + fTabHeight);
934 }
935 
936 
937 void
938 TranslatorView::Draw(BRect updateRect)
939 {
940 	// This is needed because DataTranslations app hides children
941 	// after user changes translator
942 	if (ChildAt(fActiveChild)->IsHidden())
943 		ChildAt(fActiveChild)->Show();
944 
945 	// Clear
946 	SetHighColor(ViewColor());
947 	BRect frame = _TabFrame(0);
948 	FillRect(BRect(frame.left, frame.top, Bounds().right, frame.bottom - 1));
949 
950 	int32 index = 0;
951 	BTab* tab;
952 	while ((tab = (BTab*)fTabs.ItemAt(index)) != NULL) {
953 		tab_position position;
954 		if (fActiveChild == index)
955 			position = B_TAB_FRONT;
956 		else if (index == 0)
957 			position = B_TAB_FIRST;
958 		else
959 			position = B_TAB_ANY;
960 
961 		tab->DrawTab(this, _TabFrame(index), position, index + 1 != fActiveChild);
962 		index++;
963 	}
964 
965 	// Draw bottom edge
966 	SetHighColor(tint_color(ViewColor(), B_LIGHTEN_MAX_TINT));
967 
968 	BRect selectedFrame = _TabFrame(fActiveChild);
969 	float offset = ceilf(frame.Height() / 2.0);
970 
971 	if (selectedFrame.left > frame.left) {
972 		StrokeLine(BPoint(frame.left, frame.bottom),
973 			BPoint(selectedFrame.left, frame.bottom));
974 	}
975 	if (selectedFrame.right + offset < Bounds().right) {
976 		StrokeLine(BPoint(selectedFrame.right + offset, frame.bottom),
977 			BPoint(Bounds().right, frame.bottom));
978 	}
979 }
980 
981 
982 //!	MouseDown, check if on tab, if so change tab if needed
983 void
984 TranslatorView::MouseDown(BPoint where)
985 {
986 	BRect frame = _TabFrame(fTabs.CountItems() - 1);
987 	frame.left = 0;
988 	if (!frame.Contains(where))
989 		return;
990 
991 	for (int32 index = fTabs.CountItems(); index-- > 0;) {
992 		if (!_TabFrame(index).Contains(where))
993 			continue;
994 
995 		if (fActiveChild != index) {
996 			// Hide current visible child
997 			ChildAt(fActiveChild)->Hide();
998 
999 			// This loop is needed because it looks like in DataTranslations
1000 			// view gets hidden more than one time when user changes translator
1001 			while (ChildAt(index)->IsHidden()) {
1002 				ChildAt(index)->Show();
1003 			}
1004 
1005 			// Remember which one is currently visible
1006 			fActiveChild = index;
1007 			Invalidate(frame);
1008 			break;
1009 		}
1010 	}
1011 }
1012 
1013 
1014 //	#pragma mark -
1015 
1016 
1017 TranslatorWindow::TranslatorWindow(bool quitOnClose)
1018 	: BWindow(BRect(100, 100, 100, 100), "JPEG Settings", B_TITLED_WINDOW,
1019 		B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS)
1020 {
1021 	BRect extent(0, 0, 0, 0);
1022 	BView *config = NULL;
1023 	MakeConfig(NULL, &config, &extent);
1024 
1025 	AddChild(config);
1026 	ResizeTo(extent.Width(), extent.Height());
1027 
1028 	// Make application quit after this window close
1029 	if (quitOnClose)
1030 		SetFlags(Flags() | B_QUIT_ON_WINDOW_CLOSE);
1031 }
1032 
1033 
1034 //	#pragma mark - Translator Add-On
1035 
1036 
1037 
1038 /*! Hook to create and return our configuration view */
1039 status_t
1040 MakeConfig(BMessage *ioExtension, BView **outView, BRect *outExtent)
1041 {
1042 	*outView = new TranslatorView("TranslatorView");
1043 	*outExtent = (*outView)->Frame();
1044 	return B_OK;
1045 }
1046 
1047 /*! Determine whether or not we can handle this data */
1048 status_t
1049 Identify(BPositionIO *inSource, const translation_format *inFormat,
1050 	BMessage *ioExtension, translator_info *outInfo, uint32 outType)
1051 {
1052 	if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT)
1053 		return B_NO_TRANSLATOR;
1054 
1055 	// !!! You might need to make this buffer bigger to test for your native format
1056 	off_t position = inSource->Position();
1057 	char header[sizeof(TranslatorBitmap)];
1058 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
1059 	inSource->Seek(position, SEEK_SET);
1060 	if (err < B_OK)
1061 		return err;
1062 
1063 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
1064 		outInfo->type = inputFormats[1].type;
1065 		outInfo->translator = 0;
1066 		outInfo->group = inputFormats[1].group;
1067 		outInfo->quality = inputFormats[1].quality;
1068 		outInfo->capability = inputFormats[1].capability;
1069 		strcpy(outInfo->name, inputFormats[1].name);
1070 		strcpy(outInfo->MIME, inputFormats[1].MIME);
1071 	} else {
1072 		// First 3 bytes in jpg files are always the same from what i've seen so far
1073 		// check them
1074 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
1075 		/* this below would be safer but it slows down whole thing
1076 
1077 			struct jpeg_decompress_struct cinfo;
1078 			struct jpeg_error_mgr jerr;
1079 			cinfo.err = jpeg_std_error(&jerr);
1080 			jpeg_create_decompress(&cinfo);
1081 			be_jpeg_stdio_src(&cinfo, inSource);
1082 			// now try to read header
1083 			// it can't be read before checking first 3 bytes
1084 			// because it will hang up if there is no header (not jpeg file)
1085 			int result = jpeg_read_header(&cinfo, FALSE);
1086 			jpeg_destroy_decompress(&cinfo);
1087 			if (result == JPEG_HEADER_OK) {
1088 		*/		outInfo->type = inputFormats[0].type;
1089 				outInfo->translator = 0;
1090 				outInfo->group = inputFormats[0].group;
1091 				outInfo->quality = inputFormats[0].quality;
1092 				outInfo->capability = inputFormats[0].capability;
1093 				strcpy(outInfo->name, inputFormats[0].name);
1094 				strcpy(outInfo->MIME, inputFormats[0].MIME);
1095 				return B_OK;
1096 		/*	} else
1097 				return B_NO_TRANSLATOR;
1098 		*/
1099 		} else
1100 			return B_NO_TRANSLATOR;
1101 	}
1102 
1103 	return B_OK;
1104 }
1105 
1106 /*!	Arguably the most important method in the add-on */
1107 status_t
1108 Translate(BPositionIO *inSource, const translator_info *inInfo,
1109 	BMessage *ioExtension, uint32 outType, BPositionIO *outDestination)
1110 {
1111 	// If no specific type was requested, convert to the interchange format
1112 	if (outType == 0)
1113 		outType = B_TRANSLATOR_BITMAP;
1114 
1115 	// Setup a "breakpoint" since throwing exceptions does not seem to work
1116 	// at all in an add-on. (?)
1117 	// In the be_jerror.cpp we implement a handler for critical library errors
1118 	// (be_error_exit()) and there we use the longjmp() function to return to
1119 	// this place. If this happens, it is as if the setjmp() call is called
1120 	// a second time, but this time the return value will be 1. The first
1121 	// invokation will return 0.
1122 	jmp_buf longJumpBuffer;
1123 	int jmpRet = setjmp(longJumpBuffer);
1124 	if (jmpRet == 1)
1125 		return B_ERROR;
1126 
1127 	try {
1128 		// What action to take, based on the findings of Identify()
1129 		if (outType == inInfo->type) {
1130 			return Copy(inSource, outDestination);
1131 		} else if (inInfo->type == B_TRANSLATOR_BITMAP
1132 				&& outType == JPEG_FORMAT) {
1133 			return Compress(inSource, outDestination, &longJumpBuffer);
1134 		} else if (inInfo->type == JPEG_FORMAT
1135 				&& outType == B_TRANSLATOR_BITMAP) {
1136 			return Decompress(inSource, outDestination, ioExtension,
1137 				&longJumpBuffer);
1138 		}
1139 	} catch (...) {
1140 		fprintf(stderr, "libjpeg encoutered a critical error "
1141 			"(caught C++ exception).\n");
1142 		return B_ERROR;
1143 	}
1144 
1145 	return B_NO_TRANSLATOR;
1146 }
1147 
1148 /*!	The user has requested the same format for input and output, so just copy */
1149 static status_t
1150 Copy(BPositionIO *in, BPositionIO *out)
1151 {
1152 	int block_size = 65536;
1153 	void *buffer = malloc(block_size);
1154 	char temp[1024];
1155 	if (buffer == NULL) {
1156 		buffer = temp;
1157 		block_size = 1024;
1158 	}
1159 	status_t err = B_OK;
1160 
1161 	// Read until end of file or error
1162 	while (1) {
1163 		ssize_t to_read = block_size;
1164 		err = in->Read(buffer, to_read);
1165 		// Explicit check for EOF
1166 		if (err == -1) {
1167 			if (buffer != temp) free(buffer);
1168 			return B_OK;
1169 		}
1170 		if (err <= B_OK) break;
1171 		to_read = err;
1172 		err = out->Write(buffer, to_read);
1173 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
1174 		if (err < B_OK) break;
1175 	}
1176 
1177 	if (buffer != temp) free(buffer);
1178 	return (err >= 0) ? B_OK : err;
1179 }
1180 
1181 
1182 /*!	Encode into the native format */
1183 static status_t
1184 Compress(BPositionIO *in, BPositionIO *out, const jmp_buf* longJumpBuffer)
1185 {
1186 	// Load Settings
1187 	jpeg_settings settings;
1188 	LoadSettings(&settings);
1189 
1190 	// Read info about bitmap
1191 	TranslatorBitmap header;
1192 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
1193 	if (err < B_OK)
1194 		return err;
1195 	else if (err < (int)sizeof(TranslatorBitmap))
1196 		return B_ERROR;
1197 
1198 	// Grab dimension, color space, and size information from the stream
1199 	BRect bounds;
1200 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
1201 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
1202 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
1203 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
1204 
1205 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
1206 
1207 	int width = bounds.IntegerWidth() + 1;
1208 	int height = bounds.IntegerHeight() + 1;
1209 
1210 	// Function pointer to convert function
1211 	// It will point to proper function if needed
1212 	void (*converter)(uchar *inscanline, uchar *outscanline,
1213 		int32 inRowBytes) = NULL;
1214 
1215 	// Default color info
1216 	J_COLOR_SPACE jpg_color_space = JCS_RGB;
1217 	int jpg_input_components = 3;
1218 	int32 out_row_bytes;
1219 	int padding = 0;
1220 
1221 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) {
1222 		case B_CMAP8:
1223 			converter = convert_from_cmap8_to_24;
1224 			padding = in_row_bytes - width;
1225 			break;
1226 
1227 		case B_GRAY1:
1228 			if (settings.B_GRAY1_as_B_RGB24) {
1229 				converter = convert_from_gray1_to_24;
1230 			} else {
1231 				jpg_input_components = 1;
1232 				jpg_color_space = JCS_GRAYSCALE;
1233 				converter = convert_from_gray1_to_gray8;
1234 			}
1235 			padding = in_row_bytes - (width/8);
1236 			break;
1237 
1238 		case B_GRAY8:
1239 			jpg_input_components = 1;
1240 			jpg_color_space = JCS_GRAYSCALE;
1241 			padding = in_row_bytes - width;
1242 			break;
1243 
1244 		case B_RGB15:
1245 		case B_RGBA15:
1246 			converter = convert_from_15_to_24;
1247 			padding = in_row_bytes - (width * 2);
1248 			break;
1249 
1250 		case B_RGB15_BIG:
1251 		case B_RGBA15_BIG:
1252 			converter = convert_from_15b_to_24;
1253 			padding = in_row_bytes - (width * 2);
1254 			break;
1255 
1256 		case B_RGB16:
1257 			converter = convert_from_16_to_24;
1258 			padding = in_row_bytes - (width * 2);
1259 			break;
1260 
1261 		case B_RGB16_BIG:
1262 			converter = convert_from_16b_to_24;
1263 			padding = in_row_bytes - (width * 2);
1264 			break;
1265 
1266 		case B_RGB24:
1267 			converter = convert_from_24_to_24;
1268 			padding = in_row_bytes - (width * 3);
1269 			break;
1270 
1271 		case B_RGB24_BIG:
1272 			padding = in_row_bytes - (width * 3);
1273 			break;
1274 
1275 		case B_RGB32:
1276 		case B_RGBA32:
1277 			converter = convert_from_32_to_24;
1278 			padding = in_row_bytes - (width * 4);
1279 			break;
1280 
1281 		case B_RGB32_BIG:
1282 		case B_RGBA32_BIG:
1283 			converter = convert_from_32b_to_24;
1284 			padding = in_row_bytes - (width * 4);
1285 			break;
1286 
1287 		case B_CMYK32:
1288 			jpg_color_space = JCS_CMYK;
1289 			jpg_input_components = 4;
1290 			padding = in_row_bytes - (width * 4);
1291 			break;
1292 
1293 		default:
1294 			fprintf(stderr, "Wrong type: Color space not implemented.\n");
1295 			return B_ERROR;
1296 	}
1297 	out_row_bytes = jpg_input_components * width;
1298 
1299 	// Set basic things needed for jpeg writing
1300 	struct jpeg_compress_struct cinfo;
1301 	struct jpeg_error_mgr jerr;
1302 	cinfo.err = be_jpeg_std_error(&jerr, &settings, longJumpBuffer);
1303 	jpeg_create_compress(&cinfo);
1304 	be_jpeg_stdio_dest(&cinfo, out);
1305 
1306 	// Set basic values
1307 	cinfo.image_width = width;
1308 	cinfo.image_height = height;
1309 	cinfo.input_components = jpg_input_components;
1310 	cinfo.in_color_space = jpg_color_space;
1311 	jpeg_set_defaults(&cinfo);
1312 
1313 	// Set better accuracy
1314 	cinfo.dct_method = JDCT_ISLOW;
1315 
1316 	// This is needed to prevent some colors loss
1317 	// With it generated jpegs are as good as from Fireworks (at last! :D)
1318 	if (settings.OptimizeColors) {
1319 		int index = 0;
1320 		while (index < cinfo.num_components) {
1321 			cinfo.comp_info[index].h_samp_factor = 1;
1322 			cinfo.comp_info[index].v_samp_factor = 1;
1323 			// This will make file smaller, but with worse quality more or less
1324 			// like with 93%-94% (but it's subjective opinion) on tested images
1325 			// but with smaller size (between 92% and 93% on tested images)
1326 			if (settings.SmallerFile)
1327 				cinfo.comp_info[index].quant_tbl_no = 1;
1328 			// This will make bigger file, but also better quality ;]
1329 			// from my tests it seems like useless - better quality with smaller
1330 			// can be acheived without this
1331 //			cinfo.comp_info[index].dc_tbl_no = 1;
1332 //			cinfo.comp_info[index].ac_tbl_no = 1;
1333 			index++;
1334 		}
1335 	}
1336 
1337 	// Set quality
1338 	jpeg_set_quality(&cinfo, settings.Quality, true);
1339 
1340 	// Set progressive compression if needed
1341 	// if not, turn on optimizing in libjpeg
1342 	if (settings.Progressive)
1343 		jpeg_simple_progression(&cinfo);
1344 	else
1345 		cinfo.optimize_coding = TRUE;
1346 
1347 	// Set smoothing (effect like Blur)
1348 	cinfo.smoothing_factor = settings.Smoothing;
1349 
1350 	// Initialize compression
1351 	jpeg_start_compress(&cinfo, TRUE);
1352 
1353 	// Declare scanlines
1354 	JSAMPROW in_scanline = NULL;
1355 	JSAMPROW out_scanline = NULL;
1356 	JSAMPROW writeline;	// Pointer to in_scanline (default) or out_scanline (if there will be conversion)
1357 
1358 	// Allocate scanline
1359 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1360 	in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1361 		JPOOL_PERMANENT, in_row_bytes);
1362 
1363 	// We need 2nd scanline storage ony for conversion
1364 	if (converter != NULL) {
1365 		// There will be conversion, allocate second scanline...
1366 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1367 	    out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1368 	    	JPOOL_PERMANENT, out_row_bytes);
1369 		// ... and make it the one to write to file
1370 		writeline = out_scanline;
1371 	} else
1372 		writeline = in_scanline;
1373 
1374 	while (cinfo.next_scanline < cinfo.image_height) {
1375 		// Read scanline
1376 		err = in->Read(in_scanline, in_row_bytes);
1377 		if (err < in_row_bytes)
1378 			return err < B_OK ? Error((j_common_ptr)&cinfo, err)
1379 				: Error((j_common_ptr)&cinfo, B_ERROR);
1380 
1381 		// Convert if needed
1382 		if (converter != NULL)
1383 			converter(in_scanline, out_scanline, in_row_bytes - padding);
1384 
1385 		// Write scanline
1386 	   	jpeg_write_scanlines(&cinfo, &writeline, 1);
1387 	}
1388 
1389 	jpeg_finish_compress(&cinfo);
1390 	jpeg_destroy_compress(&cinfo);
1391 	return B_OK;
1392 }
1393 
1394 
1395 /*!	Decode the native format */
1396 static status_t
1397 Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension,
1398 	const jmp_buf* longJumpBuffer)
1399 {
1400 	// Load Settings
1401 	jpeg_settings settings;
1402 	LoadSettings(&settings);
1403 
1404 	// Set basic things needed for jpeg reading
1405 	struct jpeg_decompress_struct cinfo;
1406 	struct jpeg_error_mgr jerr;
1407 	cinfo.err = be_jpeg_std_error(&jerr, &settings, longJumpBuffer);
1408 	jpeg_create_decompress(&cinfo);
1409 	be_jpeg_stdio_src(&cinfo, in);
1410 
1411 	jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
1412 		// make sure the EXIF tag is stored
1413 
1414 	// Read info about image
1415 	jpeg_read_header(&cinfo, TRUE);
1416 
1417 	BMessage exif;
1418 
1419 	// parse EXIF data and add it ioExtension, if any
1420 	jpeg_marker_struct* marker = cinfo.marker_list;
1421 	while (marker != NULL) {
1422 		if (marker->marker == MARKER_EXIF
1423 			&& !strncmp((char*)marker->data, "Exif", 4)) {
1424 			if (ioExtension != NULL) {
1425 				// Strip EXIF header from TIFF data
1426 				ioExtension->AddData("exif", B_RAW_TYPE,
1427 					(uint8 *)marker->data + 6, marker->data_length - 6);
1428 			}
1429 
1430 			BMemoryIO io(marker->data + 6, marker->data_length - 6);
1431 			convert_exif_to_message(io, exif);
1432 		}
1433 		marker = marker->next;
1434 	}
1435 
1436 	// Default color info
1437 	color_space outColorSpace = B_RGB32;
1438 	int outColorComponents = 4;
1439 
1440 	// Function pointer to convert function
1441 	// It will point to proper function if needed
1442 	void (*converter)(uchar *inScanLine, uchar *outScanLine,
1443 		int32 inRowBytes, int32 xStep) = convert_from_24_to_32;
1444 
1445 	// If color space isn't rgb
1446 	if (cinfo.out_color_space != JCS_RGB) {
1447 		switch (cinfo.out_color_space) {
1448 			case JCS_UNKNOWN:		/* error/unspecified */
1449 				fprintf(stderr, "From Type: Jpeg uses unknown color type\n");
1450 				break;
1451 			case JCS_GRAYSCALE:		/* monochrome */
1452 				// Check if user wants to read only as RGB32 or not
1453 				if (!settings.Always_B_RGB32) {
1454 					// Grayscale
1455 					outColorSpace = B_GRAY8;
1456 					outColorComponents = 1;
1457 					converter = translate_8;
1458 				} else {
1459 					// RGB
1460 					cinfo.out_color_space = JCS_RGB;
1461 					cinfo.output_components = 3;
1462 					converter = convert_from_24_to_32;
1463 				}
1464 				break;
1465 			case JCS_YCbCr:		/* Y/Cb/Cr (also known as YUV) */
1466 				cinfo.out_color_space = JCS_RGB;
1467 				converter = convert_from_24_to_32;
1468 				break;
1469 			case JCS_YCCK:		/* Y/Cb/Cr/K */
1470 				// Let libjpeg convert it to CMYK
1471 				cinfo.out_color_space = JCS_CMYK;
1472 				// Fall through to CMYK since we need the same settings
1473 			case JCS_CMYK:		/* C/M/Y/K */
1474 				// Use proper converter
1475 				if (settings.PhotoshopCMYK)
1476 					converter = convert_from_CMYK_to_32_photoshop;
1477 				else
1478 					converter = convert_from_CMYK_to_32;
1479 				break;
1480 			default:
1481 				fprintf(stderr, "From Type: Jpeg uses hmm... i don't know really :(\n");
1482 				break;
1483 		}
1484 	}
1485 
1486 	// Initialize decompression
1487 	jpeg_start_decompress(&cinfo);
1488 
1489 	// retrieve orientation from settings/EXIF
1490 	int32 orientation;
1491 	if (ioExtension == NULL
1492 		|| ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) {
1493 		if (exif.FindInt32("Orientation", &orientation) != B_OK)
1494 			orientation = 1;
1495 	}
1496 
1497 	if (orientation != 1 && converter == NULL)
1498 		converter = translate_8;
1499 
1500 	int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width;
1501 	int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height;
1502 
1503 	int32 destOffset = dest_index(outputWidth, outputHeight,
1504 		0, 0, orientation) * outColorComponents;
1505 	int32 xStep = dest_index(outputWidth, outputHeight,
1506 		1, 0, orientation) * outColorComponents - destOffset;
1507 	int32 yStep = dest_index(outputWidth, outputHeight,
1508 		0, 1, orientation) * outColorComponents - destOffset;
1509 	bool needAll = orientation != 1;
1510 
1511 	// Initialize this bounds rect to the size of your image
1512 	BRect bounds(0, 0, outputWidth - 1, outputHeight - 1);
1513 
1514 #if 0
1515 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n",
1516 	destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height,
1517 	bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation);
1518 #endif
1519 
1520 	// Bytes count in one line of image (scanline)
1521 	int32 inRowBytes = cinfo.output_width * cinfo.output_components;
1522 	int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents;
1523 	int32 dataSize = cinfo.output_width * cinfo.output_height
1524 		* outColorComponents;
1525 
1526 	// Fill out the B_TRANSLATOR_BITMAP's header
1527 	TranslatorBitmap header;
1528 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1529 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1530 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1531 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1532 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1533 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
1534 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
1535 	header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize);
1536 
1537 	// Write out the header
1538 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1539 	if (err < B_OK)
1540 		return Error((j_common_ptr)&cinfo, err);
1541 	else if (err < (int)sizeof(TranslatorBitmap))
1542 		return Error((j_common_ptr)&cinfo, B_ERROR);
1543 
1544 	// Declare scanlines
1545 	JSAMPROW inScanLine = NULL;
1546 	uint8* dest = NULL;
1547 	uint8* destLine = NULL;
1548 
1549 	// Allocate scanline
1550 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1551     inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1552     	JPOOL_PERMANENT, inRowBytes);
1553 
1554 	// We need 2nd scanline storage only for conversion
1555 	if (converter != NULL) {
1556 		// There will be conversion, allocate second scanline...
1557 		// Use libjpeg memory allocation functions, so in case of error it will free
1558 		// them itself
1559 	    dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1560 	    	JPOOL_PERMANENT, needAll ? dataSize : rowBytes);
1561 	    destLine = dest + destOffset;
1562 	} else
1563 		destLine = inScanLine;
1564 
1565 	while (cinfo.output_scanline < cinfo.output_height) {
1566 		// Read scanline
1567 		jpeg_read_scanlines(&cinfo, &inScanLine, 1);
1568 
1569 		// Convert if needed
1570 		if (converter != NULL)
1571 			converter(inScanLine, destLine, inRowBytes, xStep);
1572 
1573 		if (!needAll) {
1574 	  		// Write the scanline buffer to the output stream
1575 			ssize_t bytesWritten = out->Write(destLine, rowBytes);
1576 			if (bytesWritten < rowBytes) {
1577 				return bytesWritten < B_OK
1578 					? Error((j_common_ptr)&cinfo, bytesWritten)
1579 					: Error((j_common_ptr)&cinfo, B_ERROR);
1580 			}
1581 		} else
1582 			destLine += yStep;
1583 	}
1584 
1585 	if (needAll) {
1586 		ssize_t bytesWritten = out->Write(dest, dataSize);
1587 		if (bytesWritten < dataSize) {
1588 			return bytesWritten < B_OK
1589 				? Error((j_common_ptr)&cinfo, bytesWritten)
1590 				: Error((j_common_ptr)&cinfo, B_ERROR);
1591 		}
1592 	}
1593 
1594 	jpeg_finish_decompress(&cinfo);
1595 	jpeg_destroy_decompress(&cinfo);
1596 	return B_OK;
1597 }
1598 
1599 /*!
1600 	Frees jpeg alocated memory
1601 	Returns given error (B_ERROR by default)
1602 */
1603 static status_t
1604 Error(j_common_ptr cinfo, status_t error)
1605 {
1606 	jpeg_destroy(cinfo);
1607 	return error;
1608 }
1609 
1610 
1611 //	#pragma mark -
1612 
1613 
1614 int
1615 main(int, char**)
1616 {
1617 	BApplication app("application/x-vnd.Haiku-JPEGTranslator");
1618 
1619 	TranslatorWindow *window = new TranslatorWindow();
1620 	window->Show();
1621 
1622 	app.Run();
1623 	return 0;
1624 }
1625 
1626