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