xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
1 /*
2 
3 Copyright (c) 2002-2003, Marcin 'Shard' Konicki
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9     * Redistributions of source code must retain the above copyright notice,
10       this list of conditions and the following disclaimer.
11     * Redistributions in binary form must reproduce the above copyright notice,
12       this list of conditions and the following disclaimer in the documentation and/or
13       other materials provided with the distribution.
14     * Name "Marcin Konicki", "Shard" or any combination of them,
15       must not be used to endorse or promote products derived from this
16       software without specific prior written permission from Marcin Konicki.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 */
31 
32 
33 #include "JPEGTranslator.h"
34 
35 #include <syslog.h>
36 
37 #include <Alignment.h>
38 #include <Catalog.h>
39 #include <LayoutBuilder.h>
40 #include <TabView.h>
41 #include <TextView.h>
42 
43 #include "be_jerror.h"
44 #include "exif_parser.h"
45 #include "TranslatorWindow.h"
46 
47 
48 #undef B_TRANSLATION_CONTEXT
49 #define B_TRANSLATION_CONTEXT "JPEGTranslator"
50 
51 #define MARKER_EXIF	0xe1
52 
53 // Set these accordingly
54 #define JPEG_ACRONYM "JPEG"
55 #define JPEG_FORMAT 'JPEG'
56 #define JPEG_MIME_STRING "image/jpeg"
57 #define JPEG_DESCRIPTION "JPEG image"
58 
59 // The translation kit's native file type
60 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap"
61 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap Format (JPEGTranslator)"
62 
63 
64 static const int32 sTranslatorVersion = B_TRANSLATION_MAKE_VERSION(1, 2, 0);
65 
66 static const char* sTranslatorName = B_TRANSLATE("JPEG images");
67 static const char* sTranslatorInfo = B_TRANSLATE("©2002-2003, Marcin Konicki\n"
68 	"©2005-2007, Haiku\n"
69 	"\n"
70 	"Based on IJG library ©  1994-2009, Thomas G. Lane, Guido Vollbeding.\n"
71 	"\thttp://www.ijg.org/files/\n"
72 	"\n"
73 	"with \"lossless\" encoding support patch by Ken Murchison\n"
74 	"\thttp://www.oceana.com/ftp/ljpeg/\n"
75 	"\n"
76 	"With some colorspace conversion routines by Magnus Hellman\n"
77 	"\thttp://www.bebits.com/app/802\n");
78 
79 // Define the formats we know how to read
80 static const translation_format sInputFormats[] = {
81 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
82 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
83 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
84 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }
85 };
86 
87 // Define the formats we know how to write
88 static const translation_format sOutputFormats[] = {
89 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
90 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
91 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
92 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }
93 };
94 
95 
96 static const TranSetting sDefaultSettings[] = {
97 	{JPEG_SET_SMOOTHING, TRAN_SETTING_INT32, 0},
98 	{JPEG_SET_QUALITY, TRAN_SETTING_INT32, 95},
99 	{JPEG_SET_PROGRESSIVE, TRAN_SETTING_BOOL, true},
100 	{JPEG_SET_OPT_COLORS, TRAN_SETTING_BOOL, true},
101 	{JPEG_SET_SMALL_FILES, TRAN_SETTING_BOOL, false},
102 	{JPEG_SET_GRAY1_AS_RGB24, TRAN_SETTING_BOOL, false},
103 	{JPEG_SET_ALWAYS_RGB32, TRAN_SETTING_BOOL, true},
104 	{JPEG_SET_PHOTOSHOP_CMYK, TRAN_SETTING_BOOL, true},
105 	{JPEG_SET_SHOWREADWARNING, TRAN_SETTING_BOOL, true}
106 };
107 
108 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
109 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
110 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
111 
112 
113 namespace conversion {
114 
115 
116 static bool
117 x_flipped(int32 orientation)
118 {
119 	return orientation == 2 || orientation == 3
120 		|| orientation == 6 || orientation == 7;
121 }
122 
123 
124 static bool
125 y_flipped(int32 orientation)
126 {
127 	return orientation == 3 || orientation == 4
128 		|| orientation == 7 || orientation == 8;
129 }
130 
131 
132 static int32
133 dest_index(uint32 width, uint32 height, uint32 x, uint32 y, int32 orientation)
134 {
135 	if (orientation > 4) {
136 		uint32 temp = x;
137 		x = y;
138 		y = temp;
139 	}
140 	if (y_flipped(orientation))
141 		y = height - 1 - y;
142 	if (x_flipped(orientation))
143 		x = width - 1 - x;
144 
145 	return y * width + x;
146 }
147 
148 
149 //	#pragma mark - conversion for compression
150 
151 
152 inline void
153 convert_from_gray1_to_gray8(uint8* in, uint8* out, int32 inRowBytes)
154 {
155 	int32 index = 0;
156 	int32 index2 = 0;
157 	while (index < inRowBytes) {
158 		unsigned char c = in[index++];
159 		for (int b = 128; b; b = b>>1) {
160 			unsigned char color;
161 			if (c & b)
162 				color = 0;
163 			else
164 				color = 255;
165 			out[index2++] = color;
166 		}
167 	}
168 }
169 
170 
171 inline void
172 convert_from_gray1_to_24(uint8* in, uint8* out, int32 inRowBytes)
173 {
174 	int32 index = 0;
175 	int32 index2 = 0;
176 	while (index < inRowBytes) {
177 		unsigned char c = in[index++];
178 		for (int b = 128; b; b = b>>1) {
179 			unsigned char color;
180 			if (c & b)
181 				color = 0;
182 			else
183 				color = 255;
184 			out[index2++] = color;
185 			out[index2++] = color;
186 			out[index2++] = color;
187 		}
188 	}
189 }
190 
191 
192 inline void
193 convert_from_cmap8_to_24(uint8* in, uint8* out, int32 inRowBytes)
194 {
195 	const color_map * map = system_colors();
196 	int32 index = 0;
197 	int32 index2 = 0;
198 	while (index < inRowBytes) {
199 		rgb_color color = map->color_list[in[index++]];
200 
201 		out[index2++] = color.red;
202 		out[index2++] = color.green;
203 		out[index2++] = color.blue;
204 	}
205 }
206 
207 
208 inline void
209 convert_from_15_to_24(uint8* in, uint8* out, int32 inRowBytes)
210 {
211 	int32 index = 0;
212 	int32 index2 = 0;
213 	int16 in_pixel;
214 	while (index < inRowBytes) {
215 		in_pixel = in[index] | (in[index + 1] << 8);
216 		index += 2;
217 
218 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
219 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
220 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
221 	}
222 }
223 
224 
225 inline void
226 convert_from_15b_to_24(uint8* in, uint8* out, int32 inRowBytes)
227 {
228 	int32 index = 0;
229 	int32 index2 = 0;
230 	int16 in_pixel;
231 	while (index < inRowBytes) {
232 		in_pixel = in[index + 1] | (in[index] << 8);
233 		index += 2;
234 
235 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
236 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
237 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
238 	}
239 }
240 
241 
242 inline void
243 convert_from_16_to_24(uint8* in, uint8* out, int32 inRowBytes)
244 {
245 	int32 index = 0;
246 	int32 index2 = 0;
247 	int16 in_pixel;
248 	while (index < inRowBytes) {
249 		in_pixel = in[index] | (in[index + 1] << 8);
250 		index += 2;
251 
252 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
253 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
254 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
255 	}
256 }
257 
258 
259 inline void
260 convert_from_16b_to_24(uint8* in, uint8* out, int32 inRowBytes)
261 {
262 	int32 index = 0;
263 	int32 index2 = 0;
264 	int16 in_pixel;
265 	while (index < inRowBytes) {
266 		in_pixel = in[index + 1] | (in[index] << 8);
267 		index += 2;
268 
269 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
270 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
271 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
272 	}
273 }
274 
275 
276 inline void
277 convert_from_24_to_24(uint8* in, uint8* out, int32 inRowBytes)
278 {
279 	int32 index = 0;
280 	int32 index2 = 0;
281 	while (index < inRowBytes) {
282 		out[index2++] = in[index + 2];
283 		out[index2++] = in[index + 1];
284 		out[index2++] = in[index];
285 		index+=3;
286 	}
287 }
288 
289 
290 inline void
291 convert_from_32_to_24(uint8* in, uint8* out, int32 inRowBytes)
292 {
293 	inRowBytes /= 4;
294 
295 	for (int32 i = 0; i < inRowBytes; i++) {
296 		out[0] = in[2];
297 		out[1] = in[1];
298 		out[2] = in[0];
299 
300 		in += 4;
301 		out += 3;
302 	}
303 }
304 
305 
306 inline void
307 convert_from_32b_to_24(uint8* in, uint8* out, int32 inRowBytes)
308 {
309 	inRowBytes /= 4;
310 
311 	for (int32 i = 0; i < inRowBytes; i++) {
312 		out[0] = in[1];
313 		out[1] = in[2];
314 		out[2] = in[3];
315 
316 		in += 4;
317 		out += 3;
318 	}
319 }
320 
321 
322 //	#pragma mark - conversion for decompression
323 
324 
325 inline void
326 convert_from_CMYK_to_32_photoshop(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
327 {
328 	for (int32 i = 0; i < inRowBytes; i += 4) {
329 		int32 black = in[3];
330 		out[0] = in[2] * black / 255;
331 		out[1] = in[1] * black / 255;
332 		out[2] = in[0] * black / 255;
333 		out[3] = 255;
334 
335 		in += 4;
336 		out += xStep;
337 	}
338 }
339 
340 
341 //!	!!! UNTESTED !!!
342 inline void
343 convert_from_CMYK_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
344 {
345 	for (int32 i = 0; i < inRowBytes; i += 4) {
346 		int32 black = 255 - in[3];
347 		out[0] = ((255 - in[2]) * black) / 255;
348 		out[1] = ((255 - in[1]) * black) / 255;
349 		out[2] = ((255 - in[0]) * black) / 255;
350 		out[3] = 255;
351 
352 		in += 4;
353 		out += xStep;
354 	}
355 }
356 
357 
358 //!	RGB24 8:8:8 to xRGB 8:8:8:8
359 inline void
360 convert_from_24_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
361 {
362 	for (int32 i = 0; i < inRowBytes; i += 3) {
363 		out[0] = in[2];
364 		out[1] = in[1];
365 		out[2] = in[0];
366 		out[3] = 255;
367 
368 		in += 3;
369 		out += xStep;
370 	}
371 }
372 
373 
374 //! 8-bit to 8-bit, only need when rotating the image
375 void
376 translate_8(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
377 {
378 	for (int32 i = 0; i < inRowBytes; i++) {
379 		out[0] = in[0];
380 
381 		in++;
382 		out += xStep;
383 	}
384 }
385 
386 
387 } // namespace conversion
388 
389 
390 //	#pragma mark -
391 
392 
393 SSlider::SSlider(const char* name, const char* label,
394 		BMessage* message, int32 minValue, int32 maxValue, orientation posture,
395 		thumb_style thumbType, uint32 flags)
396 	: BSlider(name, label, message, minValue, maxValue,
397 		posture, thumbType, flags)
398 {
399 	rgb_color barColor = { 0, 0, 229, 255 };
400 	UseFillColor(true, &barColor);
401 }
402 
403 
404 //!	Update status string - show actual value
405 const char*
406 SSlider::UpdateText() const
407 {
408 	snprintf(fStatusLabel, sizeof(fStatusLabel), "%" B_PRId32, Value());
409 	return fStatusLabel;
410 }
411 
412 
413 //	#pragma mark -
414 
415 
416 TranslatorReadView::TranslatorReadView(const char* name,
417 	TranslatorSettings* settings)
418 	:
419 	BView(name, 0, new BGroupLayout(B_HORIZONTAL)),
420 	fSettings(settings)
421 		// settings should already be Acquired()
422 {
423 	fAlwaysRGB32 = new BCheckBox("alwaysrgb32",
424 		B_TRANSLATE("Read greyscale images as RGB32"),
425 		new BMessage(VIEW_MSG_SET_ALWAYSRGB32));
426 	if (fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL))
427 		fAlwaysRGB32->SetValue(B_CONTROL_ON);
428 
429 	fPhotoshopCMYK = new BCheckBox("photoshopCMYK",
430 		B_TRANSLATE("Use CMYK code with 0 for 100% ink coverage"),
431 		new BMessage(VIEW_MSG_SET_PHOTOSHOPCMYK));
432 	if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK, NULL))
433 		fPhotoshopCMYK->SetValue(B_CONTROL_ON);
434 
435 	fShowErrorBox = new BCheckBox("error",
436 		B_TRANSLATE("Show warning messages"),
437 		new BMessage(VIEW_MSG_SET_SHOWREADERRORBOX));
438 	if (fSettings->SetGetBool(JPEG_SET_SHOWREADWARNING, NULL))
439 		fShowErrorBox->SetValue(B_CONTROL_ON);
440 
441 	BLayoutBuilder::Group<>(this, B_VERTICAL)
442 		.SetInsets(B_USE_DEFAULT_SPACING)
443 		.Add(fAlwaysRGB32)
444 		.Add(fPhotoshopCMYK)
445 		.Add(fShowErrorBox)
446 		.AddGlue();
447 }
448 
449 
450 TranslatorReadView::~TranslatorReadView()
451 {
452 	fSettings->Release();
453 }
454 
455 
456 void
457 TranslatorReadView::AttachedToWindow()
458 {
459 	BView::AttachedToWindow();
460 
461 	fAlwaysRGB32->SetTarget(this);
462 	fPhotoshopCMYK->SetTarget(this);
463 	fShowErrorBox->SetTarget(this);
464 }
465 
466 
467 void
468 TranslatorReadView::MessageReceived(BMessage* message)
469 {
470 	switch (message->what) {
471 		case VIEW_MSG_SET_ALWAYSRGB32:
472 		{
473 			int32 value;
474 			if (message->FindInt32("be:value", &value) == B_OK) {
475 				bool boolValue = value;
476 				fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, &boolValue);
477 				fSettings->SaveSettings();
478 			}
479 			break;
480 		}
481 		case VIEW_MSG_SET_PHOTOSHOPCMYK:
482 		{
483 			int32 value;
484 			if (message->FindInt32("be:value", &value) == B_OK) {
485 				bool boolValue = value;
486 				fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK, &boolValue);
487 				fSettings->SaveSettings();
488 			}
489 			break;
490 		}
491 		case VIEW_MSG_SET_SHOWREADERRORBOX:
492 		{
493 			int32 value;
494 			if (message->FindInt32("be:value", &value) == B_OK) {
495 				bool boolValue = value;
496 				fSettings->SetGetBool(JPEG_SET_SHOWREADWARNING, &boolValue);
497 				fSettings->SaveSettings();
498 			}
499 			break;
500 		}
501 		default:
502 			BView::MessageReceived(message);
503 			break;
504 	}
505 }
506 
507 
508 //	#pragma mark - TranslatorWriteView
509 
510 
511 TranslatorWriteView::TranslatorWriteView(const char* name,
512 	TranslatorSettings* settings)
513 	:
514 	BView(name, 0, new BGroupLayout(B_VERTICAL)),
515 	fSettings(settings)
516 		// settings should already be Acquired()
517 {
518 	fQualitySlider = new SSlider("quality", B_TRANSLATE("Output quality"),
519 		new BMessage(VIEW_MSG_SET_QUALITY), 0, 100);
520 	fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
521 	fQualitySlider->SetHashMarkCount(10);
522 	fQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High"));
523 	fQualitySlider->SetValue(fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL));
524 
525 	fSmoothingSlider = new SSlider("smoothing",
526 		B_TRANSLATE("Output smoothing strength"),
527 		new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100);
528 	fSmoothingSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
529 	fSmoothingSlider->SetHashMarkCount(10);
530 	fSmoothingSlider->SetLimitLabels(B_TRANSLATE("None"), B_TRANSLATE("High"));
531 	fSmoothingSlider->SetValue(
532 		fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL));
533 
534 	fProgress = new BCheckBox("progress",
535 		B_TRANSLATE("Use progressive compression"),
536 		new BMessage(VIEW_MSG_SET_PROGRESSIVE));
537 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
538 		fProgress->SetValue(B_CONTROL_ON);
539 
540 	fSmallerFile = new BCheckBox("smallerfile",
541 		B_TRANSLATE("Make file smaller (sligthtly worse quality)"),
542 		new BMessage(VIEW_MSG_SET_SMALLERFILE));
543 	if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
544 		fSmallerFile->SetValue(B_CONTROL_ON);
545 
546 	fOptimizeColors = new BCheckBox("optimizecolors",
547 		B_TRANSLATE("Prevent colors 'washing out'"),
548 		new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS));
549 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL))
550 		fOptimizeColors->SetValue(B_CONTROL_ON);
551 	else
552 		fSmallerFile->SetEnabled(false);
553 
554 	fGrayAsRGB24 = new BCheckBox("gray1asrgb24",
555 		B_TRANSLATE("Write black-and-white images as RGB24"),
556 		new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
557 	if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24))
558 		fGrayAsRGB24->SetValue(B_CONTROL_ON);
559 
560 	BLayoutBuilder::Group<>(this, B_VERTICAL)
561 		.SetInsets(B_USE_DEFAULT_SPACING)
562 		.Add(fQualitySlider)
563 		.Add(fSmoothingSlider)
564 		.Add(fProgress)
565 		.Add(fOptimizeColors)
566 		.Add(fSmallerFile)
567 		.Add(fGrayAsRGB24)
568 		.AddGlue();
569 }
570 
571 
572 TranslatorWriteView::~TranslatorWriteView()
573 {
574 	fSettings->Release();
575 }
576 
577 
578 void
579 TranslatorWriteView::AttachedToWindow()
580 {
581 	BView::AttachedToWindow();
582 
583 	fQualitySlider->SetTarget(this);
584 	fSmoothingSlider->SetTarget(this);
585 	fProgress->SetTarget(this);
586 	fOptimizeColors->SetTarget(this);
587 	fSmallerFile->SetTarget(this);
588 	fGrayAsRGB24->SetTarget(this);
589 }
590 
591 
592 void
593 TranslatorWriteView::MessageReceived(BMessage* message)
594 {
595 	switch (message->what) {
596 		case VIEW_MSG_SET_QUALITY:
597 		{
598 			int32 value;
599 			if (message->FindInt32("be:value", &value) == B_OK) {
600 				fSettings->SetGetInt32(JPEG_SET_QUALITY, &value);
601 				fSettings->SaveSettings();
602 			}
603 			break;
604 		}
605 		case VIEW_MSG_SET_SMOOTHING:
606 		{
607 			int32 value;
608 			if (message->FindInt32("be:value", &value) == B_OK) {
609 				fSettings->SetGetInt32(JPEG_SET_SMOOTHING, &value);
610 				fSettings->SaveSettings();
611 			}
612 			break;
613 		}
614 		case VIEW_MSG_SET_PROGRESSIVE:
615 		{
616 			int32 value;
617 			if (message->FindInt32("be:value", &value) == B_OK) {
618 				bool boolValue = value;
619 				fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, &boolValue);
620 				fSettings->SaveSettings();
621 			}
622 			break;
623 		}
624 		case VIEW_MSG_SET_OPTIMIZECOLORS:
625 		{
626 			int32 value;
627 			if (message->FindInt32("be:value", &value) == B_OK) {
628 				bool boolValue = value;
629 				fSettings->SetGetBool(JPEG_SET_OPT_COLORS, &boolValue);
630 				fSmallerFile->SetEnabled(value);
631 				fSettings->SaveSettings();
632 			}
633 			break;
634 		}
635 		case VIEW_MSG_SET_SMALLERFILE:
636 		{
637 			int32 value;
638 			if (message->FindInt32("be:value", &value) == B_OK) {
639 				bool boolValue = value;
640 				fSettings->SetGetBool(JPEG_SET_SMALL_FILES, &boolValue);
641 				fSettings->SaveSettings();
642 			}
643 			break;
644 		}
645 		case VIEW_MSG_SET_GRAY1ASRGB24:
646 		{
647 			int32 value;
648 			if (message->FindInt32("be:value", &value) == B_OK) {
649 				bool boolValue = value;
650 				fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, &boolValue);
651 				fSettings->SaveSettings();
652 			}
653 			break;
654 		}
655 		default:
656 			BView::MessageReceived(message);
657 			break;
658 	}
659 }
660 
661 
662 //	#pragma mark -
663 
664 
665 TranslatorAboutView::TranslatorAboutView(const char* name)
666 	:
667 	BView(name, 0, new BGroupLayout(B_VERTICAL))
668 {
669 	BAlignment labelAlignment = BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP);
670 	BStringView* title = new BStringView("Title", sTranslatorName);
671 	title->SetFont(be_bold_font);
672 	title->SetExplicitAlignment(labelAlignment);
673 
674 	char versionString[100];
675 	snprintf(versionString, sizeof(versionString),
676 		B_TRANSLATE("Version %d.%d.%d"),
677 		(int)(sTranslatorVersion >> 8),
678 		(int)((sTranslatorVersion >> 4) & 0xf),
679 		(int)(sTranslatorVersion & 0xf));
680 
681 	BStringView* version = new BStringView("Version", versionString);
682 	version->SetExplicitAlignment(labelAlignment);
683 
684 	BTextView* infoView = new BTextView("info");
685 	infoView->SetText(sTranslatorInfo);
686 	infoView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
687 	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
688 	infoView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
689 	infoView->MakeEditable(false);
690 
691 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
692 		.SetInsets(B_USE_DEFAULT_SPACING)
693 		.Add(title)
694 		.Add(version)
695 		.Add(infoView);
696 }
697 
698 
699 TranslatorView::TranslatorView(const char* name, TranslatorSettings* settings)
700 	:
701 	BTabView(name, B_WIDTH_FROM_LABEL)
702 {
703 	SetBorder(B_NO_BORDER);
704 
705 	AddTab(new TranslatorWriteView(B_TRANSLATE("Write"), settings->Acquire()));
706 	AddTab(new TranslatorReadView(B_TRANSLATE("Read"), settings->Acquire()));
707 	AddTab(new TranslatorAboutView(B_TRANSLATE("About")));
708 
709 	settings->Release();
710 
711  	BFont font;
712  	GetFont(&font);
713  	SetExplicitPreferredSize(
714 		BSize((font.Size() * 380) / 12, (font.Size() * 250) / 12));
715 }
716 
717 
718 //	#pragma mark - Translator Add-On
719 
720 
721 BView*
722 JPEGTranslator::NewConfigView(TranslatorSettings* settings)
723 {
724 	BView* configView = new TranslatorView("TranslatorView", settings);
725 	return configView;
726 }
727 
728 
729 /*! Determine whether or not we can handle this data */
730 status_t
731 JPEGTranslator::DerivedIdentify(BPositionIO* inSource,
732 	const translation_format* inFormat, BMessage* ioExtension,
733 	translator_info* outInfo, uint32 outType)
734 {
735 	if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT)
736 		return B_NO_TRANSLATOR;
737 
738 	// !!! You might need to make this buffer bigger to test for your native format
739 	off_t position = inSource->Position();
740 	char header[sizeof(TranslatorBitmap)];
741 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
742 	inSource->Seek(position, SEEK_SET);
743 	if (err < B_OK)
744 		return err;
745 
746 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
747 		if (PopulateInfoFromFormat(outInfo, B_TRANSLATOR_BITMAP) != B_OK)
748 			return B_NO_TRANSLATOR;
749 	} else {
750 		// First 3 bytes in jpg files are always the same from what i've seen so far
751 		// check them
752 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
753 			if (PopulateInfoFromFormat(outInfo, JPEG_FORMAT) != B_OK)
754 				return B_NO_TRANSLATOR;
755 
756 		} else
757 			return B_NO_TRANSLATOR;
758 	}
759 
760 	return B_OK;
761 }
762 
763 
764 status_t
765 JPEGTranslator::DerivedTranslate(BPositionIO* inSource,
766 	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
767 	BPositionIO* outDestination, int32 baseType)
768 {
769 	// Setup a "breakpoint" since throwing exceptions does not seem to work
770 	// at all in an add-on. (?)
771 	// In the be_jerror.cpp we implement a handler for critical library errors
772 	// (be_error_exit()) and there we use the longjmp() function to return to
773 	// this place. If this happens, it is as if the setjmp() call is called
774 	// a second time, but this time the return value will be 1. The first
775 	// invokation will return 0.
776 	jmp_buf longJumpBuffer;
777 	int jmpRet = setjmp(longJumpBuffer);
778 	if (jmpRet == 1)
779 		return B_ERROR;
780 
781 	try {
782 		// What action to take, based on the findings of Identify()
783 		if (outType == inInfo->type) {
784 			return Copy(inSource, outDestination);
785 		} else if (inInfo->type == B_TRANSLATOR_BITMAP
786 				&& outType == JPEG_FORMAT) {
787 			return Compress(inSource, outDestination, &longJumpBuffer);
788 		} else if (inInfo->type == JPEG_FORMAT
789 				&& (outType == B_TRANSLATOR_BITMAP || outType == 0)) {
790 			// This is the default if no specific outType was requested.
791 			return Decompress(inSource, outDestination, ioExtension,
792 				&longJumpBuffer);
793 		}
794 	} catch (...) {
795 		syslog(LOG_ERR, "libjpeg encountered a critical error (caught C++ "
796 			"exception).\n");
797 		return B_ERROR;
798 	}
799 
800 	return B_NO_TRANSLATOR;
801 }
802 
803 
804 /*!	The user has requested the same format for input and output, so just copy */
805 status_t
806 JPEGTranslator::Copy(BPositionIO* in, BPositionIO* out)
807 {
808 	int block_size = 65536;
809 	void* buffer = malloc(block_size);
810 	char temp[1024];
811 	if (buffer == NULL) {
812 		buffer = temp;
813 		block_size = 1024;
814 	}
815 	status_t err = B_OK;
816 
817 	// Read until end of file or error
818 	while (1) {
819 		ssize_t to_read = block_size;
820 		err = in->Read(buffer, to_read);
821 		// Explicit check for EOF
822 		if (err == -1) {
823 			if (buffer != temp) free(buffer);
824 			return B_OK;
825 		}
826 		if (err <= B_OK) break;
827 		to_read = err;
828 		err = out->Write(buffer, to_read);
829 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
830 		if (err < B_OK) break;
831 	}
832 
833 	if (buffer != temp) free(buffer);
834 	return (err >= 0) ? B_OK : err;
835 }
836 
837 
838 /*!	Encode into the native format */
839 status_t
840 JPEGTranslator::Compress(BPositionIO* in, BPositionIO* out,
841 	const jmp_buf* longJumpBuffer)
842 {
843 	using namespace conversion;
844 
845 	// Read info about bitmap
846 	TranslatorBitmap header;
847 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
848 	if (err < B_OK)
849 		return err;
850 	else if (err < (int)sizeof(TranslatorBitmap))
851 		return B_ERROR;
852 
853 	// Grab dimension, color space, and size information from the stream
854 	BRect bounds;
855 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
856 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
857 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
858 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
859 
860 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
861 
862 	int width = bounds.IntegerWidth() + 1;
863 	int height = bounds.IntegerHeight() + 1;
864 
865 	// Function pointer to convert function
866 	// It will point to proper function if needed
867 	void (*converter)(uchar* inscanline, uchar* outscanline,
868 		int32 inRowBytes) = NULL;
869 
870 	// Default color info
871 	J_COLOR_SPACE jpg_color_space = JCS_RGB;
872 	int jpg_input_components = 3;
873 	int32 out_row_bytes;
874 	int padding = 0;
875 
876 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) {
877 		case B_CMAP8:
878 			converter = convert_from_cmap8_to_24;
879 			padding = in_row_bytes - width;
880 			break;
881 
882 		case B_GRAY1:
883 			if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, NULL)) {
884 				converter = convert_from_gray1_to_24;
885 			} else {
886 				jpg_input_components = 1;
887 				jpg_color_space = JCS_GRAYSCALE;
888 				converter = convert_from_gray1_to_gray8;
889 			}
890 			padding = in_row_bytes - (width / 8);
891 			break;
892 
893 		case B_GRAY8:
894 			jpg_input_components = 1;
895 			jpg_color_space = JCS_GRAYSCALE;
896 			padding = in_row_bytes - width;
897 			break;
898 
899 		case B_RGB15:
900 		case B_RGBA15:
901 			converter = convert_from_15_to_24;
902 			padding = in_row_bytes - (width * 2);
903 			break;
904 
905 		case B_RGB15_BIG:
906 		case B_RGBA15_BIG:
907 			converter = convert_from_15b_to_24;
908 			padding = in_row_bytes - (width * 2);
909 			break;
910 
911 		case B_RGB16:
912 			converter = convert_from_16_to_24;
913 			padding = in_row_bytes - (width * 2);
914 			break;
915 
916 		case B_RGB16_BIG:
917 			converter = convert_from_16b_to_24;
918 			padding = in_row_bytes - (width * 2);
919 			break;
920 
921 		case B_RGB24:
922 			converter = convert_from_24_to_24;
923 			padding = in_row_bytes - (width * 3);
924 			break;
925 
926 		case B_RGB24_BIG:
927 			padding = in_row_bytes - (width * 3);
928 			break;
929 
930 		case B_RGB32:
931 		case B_RGBA32:
932 			converter = convert_from_32_to_24;
933 			padding = in_row_bytes - (width * 4);
934 			break;
935 
936 		case B_RGB32_BIG:
937 		case B_RGBA32_BIG:
938 			converter = convert_from_32b_to_24;
939 			padding = in_row_bytes - (width * 4);
940 			break;
941 
942 		case B_CMYK32:
943 			jpg_color_space = JCS_CMYK;
944 			jpg_input_components = 4;
945 			padding = in_row_bytes - (width * 4);
946 			break;
947 
948 		default:
949 			syslog(LOG_ERR, "Wrong type: Color space not implemented.\n");
950 			return B_ERROR;
951 	}
952 	out_row_bytes = jpg_input_components * width;
953 
954 	// Set basic things needed for jpeg writing
955 	struct jpeg_compress_struct cinfo;
956 	struct be_jpeg_error_mgr jerr;
957 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
958 	jpeg_create_compress(&cinfo);
959 	be_jpeg_stdio_dest(&cinfo, out);
960 
961 	// Set basic values
962 	cinfo.image_width = width;
963 	cinfo.image_height = height;
964 	cinfo.input_components = jpg_input_components;
965 	cinfo.in_color_space = jpg_color_space;
966 	jpeg_set_defaults(&cinfo);
967 
968 	// Set better accuracy
969 	cinfo.dct_method = JDCT_ISLOW;
970 
971 	// This is needed to prevent some colors loss
972 	// With it generated jpegs are as good as from Fireworks (at last! :D)
973 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL)) {
974 		int index = 0;
975 		while (index < cinfo.num_components) {
976 			cinfo.comp_info[index].h_samp_factor = 1;
977 			cinfo.comp_info[index].v_samp_factor = 1;
978 			// This will make file smaller, but with worse quality more or less
979 			// like with 93%-94% (but it's subjective opinion) on tested images
980 			// but with smaller size (between 92% and 93% on tested images)
981 			if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
982 				cinfo.comp_info[index].quant_tbl_no = 1;
983 			// This will make bigger file, but also better quality ;]
984 			// from my tests it seems like useless - better quality with smaller
985 			// can be acheived without this
986 //			cinfo.comp_info[index].dc_tbl_no = 1;
987 //			cinfo.comp_info[index].ac_tbl_no = 1;
988 			index++;
989 		}
990 	}
991 
992 	// Set quality
993 	jpeg_set_quality(&cinfo, fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL), true);
994 
995 	// Set progressive compression if needed
996 	// if not, turn on optimizing in libjpeg
997 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
998 		jpeg_simple_progression(&cinfo);
999 	else
1000 		cinfo.optimize_coding = TRUE;
1001 
1002 	// Set smoothing (effect like Blur)
1003 	cinfo.smoothing_factor = fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL);
1004 
1005 	// Initialize compression
1006 	jpeg_start_compress(&cinfo, TRUE);
1007 
1008 	// Declare scanlines
1009 	JSAMPROW in_scanline = NULL;
1010 	JSAMPROW out_scanline = NULL;
1011 	JSAMPROW writeline;
1012 		// Pointer to in_scanline (default) or out_scanline (if there will be conversion)
1013 
1014 	// Allocate scanline
1015 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1016 	in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1017 		JPOOL_PERMANENT, in_row_bytes);
1018 
1019 	// We need 2nd scanline storage ony for conversion
1020 	if (converter != NULL) {
1021 		// There will be conversion, allocate second scanline...
1022 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1023 	    out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1024 	    	JPOOL_PERMANENT, out_row_bytes);
1025 		// ... and make it the one to write to file
1026 		writeline = out_scanline;
1027 	} else
1028 		writeline = in_scanline;
1029 
1030 	while (cinfo.next_scanline < cinfo.image_height) {
1031 		// Read scanline
1032 		err = in->Read(in_scanline, in_row_bytes);
1033 		if (err < in_row_bytes)
1034 			return err < B_OK ? Error((j_common_ptr)&cinfo, err)
1035 				: Error((j_common_ptr)&cinfo, B_ERROR);
1036 
1037 		// Convert if needed
1038 		if (converter != NULL)
1039 			converter(in_scanline, out_scanline, in_row_bytes - padding);
1040 
1041 		// Write scanline
1042 	   	jpeg_write_scanlines(&cinfo, &writeline, 1);
1043 	}
1044 
1045 	jpeg_finish_compress(&cinfo);
1046 	jpeg_destroy_compress(&cinfo);
1047 	return B_OK;
1048 }
1049 
1050 
1051 /*!	Decode the native format */
1052 status_t
1053 JPEGTranslator::Decompress(BPositionIO* in, BPositionIO* out,
1054 	BMessage* ioExtension, const jmp_buf* longJumpBuffer)
1055 {
1056 	using namespace conversion;
1057 
1058 	// Set basic things needed for jpeg reading
1059 	struct jpeg_decompress_struct cinfo;
1060 	struct be_jpeg_error_mgr jerr;
1061 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
1062 	jpeg_create_decompress(&cinfo);
1063 	be_jpeg_stdio_src(&cinfo, in);
1064 
1065 	jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
1066 		// make sure the EXIF tag is stored
1067 
1068 	// Read info about image
1069 	jpeg_read_header(&cinfo, TRUE);
1070 
1071 	BMessage exif;
1072 
1073 	// parse EXIF data and add it ioExtension, if any
1074 	jpeg_marker_struct* marker = cinfo.marker_list;
1075 	while (marker != NULL) {
1076 		if (marker->marker == MARKER_EXIF
1077 			&& !strncmp((char*)marker->data, "Exif", 4)) {
1078 			if (ioExtension != NULL) {
1079 				// Strip EXIF header from TIFF data
1080 				ioExtension->AddData("exif", B_RAW_TYPE,
1081 					(uint8*)marker->data + 6, marker->data_length - 6);
1082 			}
1083 
1084 			BMemoryIO io(marker->data + 6, marker->data_length - 6);
1085 			convert_exif_to_message(io, exif);
1086 		}
1087 		marker = marker->next;
1088 	}
1089 
1090 	// Default color info
1091 	color_space outColorSpace = B_RGB32;
1092 	int outColorComponents = 4;
1093 
1094 	// Function pointer to convert function
1095 	// It will point to proper function if needed
1096 	void (*converter)(uchar* inScanLine, uchar* outScanLine,
1097 		int32 inRowBytes, int32 xStep) = convert_from_24_to_32;
1098 
1099 	// If color space isn't rgb
1100 	if (cinfo.out_color_space != JCS_RGB) {
1101 		switch (cinfo.out_color_space) {
1102 			case JCS_UNKNOWN:		/* error/unspecified */
1103 				syslog(LOG_ERR, "From Type: Jpeg uses unknown color type\n");
1104 				break;
1105 			case JCS_GRAYSCALE:		/* monochrome */
1106 				// Check if user wants to read only as RGB32 or not
1107 				if (!fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL)) {
1108 					// Grayscale
1109 					outColorSpace = B_GRAY8;
1110 					outColorComponents = 1;
1111 					converter = translate_8;
1112 				} else {
1113 					// RGB
1114 					cinfo.out_color_space = JCS_RGB;
1115 					cinfo.output_components = 3;
1116 					converter = convert_from_24_to_32;
1117 				}
1118 				break;
1119 			case JCS_YCbCr:		/* Y/Cb/Cr (also known as YUV) */
1120 				cinfo.out_color_space = JCS_RGB;
1121 				converter = convert_from_24_to_32;
1122 				break;
1123 			case JCS_YCCK:		/* Y/Cb/Cr/K */
1124 				// Let libjpeg convert it to CMYK
1125 				cinfo.out_color_space = JCS_CMYK;
1126 				// Fall through to CMYK since we need the same settings
1127 			case JCS_CMYK:		/* C/M/Y/K */
1128 				// Use proper converter
1129 				if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK))
1130 					converter = convert_from_CMYK_to_32_photoshop;
1131 				else
1132 					converter = convert_from_CMYK_to_32;
1133 				break;
1134 			default:
1135 				syslog(LOG_ERR,
1136 						"From Type: Jpeg uses hmm... i don't know really :(\n");
1137 				break;
1138 		}
1139 	}
1140 
1141 	// Initialize decompression
1142 	jpeg_start_decompress(&cinfo);
1143 
1144 	// retrieve orientation from settings/EXIF
1145 	int32 orientation;
1146 	if (ioExtension == NULL
1147 		|| ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) {
1148 		if (exif.FindInt32("Orientation", &orientation) != B_OK)
1149 			orientation = 1;
1150 	}
1151 
1152 	if (orientation != 1 && converter == NULL)
1153 		converter = translate_8;
1154 
1155 	int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width;
1156 	int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height;
1157 
1158 	int32 destOffset = dest_index(outputWidth, outputHeight,
1159 		0, 0, orientation) * outColorComponents;
1160 	int32 xStep = dest_index(outputWidth, outputHeight,
1161 		1, 0, orientation) * outColorComponents - destOffset;
1162 	int32 yStep = dest_index(outputWidth, outputHeight,
1163 		0, 1, orientation) * outColorComponents - destOffset;
1164 	bool needAll = orientation != 1;
1165 
1166 	// Initialize this bounds rect to the size of your image
1167 	BRect bounds(0, 0, outputWidth - 1, outputHeight - 1);
1168 
1169 #if 0
1170 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n",
1171 	destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height,
1172 	bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation);
1173 #endif
1174 
1175 	// Bytes count in one line of image (scanline)
1176 	int32 inRowBytes = cinfo.output_width * cinfo.output_components;
1177 	int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents;
1178 	int32 dataSize = cinfo.output_width * cinfo.output_height
1179 		* outColorComponents;
1180 
1181 	// Fill out the B_TRANSLATOR_BITMAP's header
1182 	TranslatorBitmap header;
1183 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1184 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1185 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1186 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1187 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1188 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
1189 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
1190 	header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize);
1191 
1192 	// Write out the header
1193 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1194 	if (err < B_OK)
1195 		return Error((j_common_ptr)&cinfo, err);
1196 	else if (err < (int)sizeof(TranslatorBitmap))
1197 		return Error((j_common_ptr)&cinfo, B_ERROR);
1198 
1199 	// Declare scanlines
1200 	JSAMPROW inScanLine = NULL;
1201 	uint8* dest = NULL;
1202 	uint8* destLine = NULL;
1203 
1204 	// Allocate scanline
1205 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1206     inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1207     	JPOOL_PERMANENT, inRowBytes);
1208 
1209 	// We need 2nd scanline storage only for conversion
1210 	if (converter != NULL) {
1211 		// There will be conversion, allocate second scanline...
1212 		// Use libjpeg memory allocation functions, so in case of error it will
1213 		// free them itself
1214 	    dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1215 	    	JPOOL_PERMANENT, needAll ? dataSize : rowBytes);
1216 	    destLine = dest + destOffset;
1217 	} else
1218 		destLine = inScanLine;
1219 
1220 	while (cinfo.output_scanline < cinfo.output_height) {
1221 		// Read scanline
1222 		jpeg_read_scanlines(&cinfo, &inScanLine, 1);
1223 
1224 		// Convert if needed
1225 		if (converter != NULL)
1226 			converter(inScanLine, destLine, inRowBytes, xStep);
1227 
1228 		if (!needAll) {
1229 	  		// Write the scanline buffer to the output stream
1230 			ssize_t bytesWritten = out->Write(destLine, rowBytes);
1231 			if (bytesWritten < rowBytes) {
1232 				return bytesWritten < B_OK
1233 					? Error((j_common_ptr)&cinfo, bytesWritten)
1234 					: Error((j_common_ptr)&cinfo, B_ERROR);
1235 			}
1236 		} else
1237 			destLine += yStep;
1238 	}
1239 
1240 	if (needAll) {
1241 		ssize_t bytesWritten = out->Write(dest, dataSize);
1242 		if (bytesWritten < dataSize) {
1243 			return bytesWritten < B_OK
1244 				? Error((j_common_ptr)&cinfo, bytesWritten)
1245 				: Error((j_common_ptr)&cinfo, B_ERROR);
1246 		}
1247 	}
1248 
1249 	jpeg_finish_decompress(&cinfo);
1250 	jpeg_destroy_decompress(&cinfo);
1251 	return B_OK;
1252 }
1253 
1254 /*! have the other PopulateInfoFromFormat() check both inputFormats & outputFormats */
1255 status_t
1256 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1257 	uint32 formatType, translator_id id)
1258 {
1259 	int32 formatCount;
1260 	const translation_format* formats = OutputFormats(&formatCount);
1261 	for (int i = 0; i <= 1 ;formats = InputFormats(&formatCount), i++) {
1262 		if (PopulateInfoFromFormat(info, formatType,
1263 			formats, formatCount) == B_OK) {
1264 			info->translator = id;
1265 			return B_OK;
1266 		}
1267 	}
1268 
1269 	return B_ERROR;
1270 }
1271 
1272 
1273 status_t
1274 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1275 	uint32 formatType, const translation_format* formats, int32 formatCount)
1276 {
1277 	for (int i = 0; i < formatCount; i++) {
1278 		if (formats[i].type == formatType) {
1279 			info->type = formatType;
1280 			info->group = formats[i].group;
1281 			info->quality = formats[i].quality;
1282 			info->capability = formats[i].capability;
1283 			BString str1(formats[i].name);
1284 			str1.ReplaceFirst("Be Bitmap Format (JPEGTranslator)",
1285 				B_TRANSLATE("Be Bitmap Format (JPEGTranslator)"));
1286 			strlcpy(info->name, str1.String(), sizeof(info->name));
1287 			strcpy(info->MIME,  formats[i].MIME);
1288 			return B_OK;
1289 		}
1290 	}
1291 
1292 	return B_ERROR;
1293 }
1294 
1295 /*!
1296 	Frees jpeg alocated memory
1297 	Returns given error (B_ERROR by default)
1298 */
1299 status_t
1300 JPEGTranslator::Error(j_common_ptr cinfo, status_t error)
1301 {
1302 	jpeg_destroy(cinfo);
1303 	return error;
1304 }
1305 
1306 
1307 JPEGTranslator::JPEGTranslator()
1308 	: BaseTranslator(sTranslatorName, sTranslatorInfo, sTranslatorVersion,
1309 		sInputFormats,  kNumInputFormats,
1310 		sOutputFormats, kNumOutputFormats,
1311 		SETTINGS_FILE,
1312 		sDefaultSettings, kNumDefaultSettings,
1313 		B_TRANSLATOR_BITMAP, JPEG_FORMAT)
1314 {}
1315 
1316 
1317 BTranslator*
1318 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
1319 {
1320 	if (n == 0)
1321 		return new JPEGTranslator();
1322 
1323 	return NULL;
1324 }
1325 
1326 
1327 int
1328 main(int, char**)
1329 {
1330 	BApplication app("application/x-vnd.Haiku-JPEGTranslator");
1331 	JPEGTranslator* translator = new JPEGTranslator();
1332 	if (LaunchTranslatorWindow(translator, sTranslatorName) == B_OK)
1333 		app.Run();
1334 
1335 	return 0;
1336 }
1337 
1338