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