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