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