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