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