xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 	infoView->MakeEditable(false);
688 
689 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
690 		.SetInsets(B_USE_DEFAULT_SPACING)
691 		.Add(title)
692 		.Add(version)
693 		.Add(infoView);
694 }
695 
696 
697 TranslatorView::TranslatorView(const char* name, TranslatorSettings* settings)
698 	:
699 	BTabView(name, B_WIDTH_FROM_LABEL)
700 {
701 	SetBorder(B_NO_BORDER);
702 
703 	AddTab(new TranslatorWriteView(B_TRANSLATE("Write"), settings->Acquire()));
704 	AddTab(new TranslatorReadView(B_TRANSLATE("Read"), settings->Acquire()));
705 	AddTab(new TranslatorAboutView(B_TRANSLATE("About")));
706 
707 	settings->Release();
708 
709  	BFont font;
710  	GetFont(&font);
711  	SetExplicitPreferredSize(
712 		BSize((font.Size() * 380) / 12, (font.Size() * 250) / 12));
713 }
714 
715 
716 //	#pragma mark - Translator Add-On
717 
718 
719 BView*
720 JPEGTranslator::NewConfigView(TranslatorSettings* settings)
721 {
722 	BView* configView = new TranslatorView("TranslatorView", settings);
723 	return configView;
724 }
725 
726 
727 /*! Determine whether or not we can handle this data */
728 status_t
729 JPEGTranslator::DerivedIdentify(BPositionIO* inSource,
730 	const translation_format* inFormat, BMessage* ioExtension,
731 	translator_info* outInfo, uint32 outType)
732 {
733 	if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT)
734 		return B_NO_TRANSLATOR;
735 
736 	// !!! You might need to make this buffer bigger to test for your native format
737 	off_t position = inSource->Position();
738 	char header[sizeof(TranslatorBitmap)];
739 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
740 	inSource->Seek(position, SEEK_SET);
741 	if (err < B_OK)
742 		return err;
743 
744 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
745 		if (PopulateInfoFromFormat(outInfo, B_TRANSLATOR_BITMAP) != B_OK)
746 			return B_NO_TRANSLATOR;
747 	} else {
748 		// First 3 bytes in jpg files are always the same from what i've seen so far
749 		// check them
750 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
751 			if (PopulateInfoFromFormat(outInfo, JPEG_FORMAT) != B_OK)
752 				return B_NO_TRANSLATOR;
753 
754 		} else
755 			return B_NO_TRANSLATOR;
756 	}
757 
758 	return B_OK;
759 }
760 
761 
762 status_t
763 JPEGTranslator::DerivedTranslate(BPositionIO* inSource,
764 	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
765 	BPositionIO* outDestination, int32 baseType)
766 {
767 	// Setup a "breakpoint" since throwing exceptions does not seem to work
768 	// at all in an add-on. (?)
769 	// In the be_jerror.cpp we implement a handler for critical library errors
770 	// (be_error_exit()) and there we use the longjmp() function to return to
771 	// this place. If this happens, it is as if the setjmp() call is called
772 	// a second time, but this time the return value will be 1. The first
773 	// invokation will return 0.
774 	jmp_buf longJumpBuffer;
775 	int jmpRet = setjmp(longJumpBuffer);
776 	if (jmpRet == 1)
777 		return B_ERROR;
778 
779 	try {
780 		// What action to take, based on the findings of Identify()
781 		if (outType == inInfo->type) {
782 			return Copy(inSource, outDestination);
783 		} else if (inInfo->type == B_TRANSLATOR_BITMAP
784 				&& outType == JPEG_FORMAT) {
785 			return Compress(inSource, outDestination, &longJumpBuffer);
786 		} else if (inInfo->type == JPEG_FORMAT
787 				&& (outType == B_TRANSLATOR_BITMAP || outType == 0)) {
788 			// This is the default if no specific outType was requested.
789 			return Decompress(inSource, outDestination, ioExtension,
790 				&longJumpBuffer);
791 		}
792 	} catch (...) {
793 		syslog(LOG_ERR, "libjpeg encountered a critical error (caught C++ "
794 			"exception).\n");
795 		return B_ERROR;
796 	}
797 
798 	return B_NO_TRANSLATOR;
799 }
800 
801 
802 /*!	The user has requested the same format for input and output, so just copy */
803 status_t
804 JPEGTranslator::Copy(BPositionIO* in, BPositionIO* out)
805 {
806 	int block_size = 65536;
807 	void* buffer = malloc(block_size);
808 	char temp[1024];
809 	if (buffer == NULL) {
810 		buffer = temp;
811 		block_size = 1024;
812 	}
813 	status_t err = B_OK;
814 
815 	// Read until end of file or error
816 	while (1) {
817 		ssize_t to_read = block_size;
818 		err = in->Read(buffer, to_read);
819 		// Explicit check for EOF
820 		if (err == -1) {
821 			if (buffer != temp) free(buffer);
822 			return B_OK;
823 		}
824 		if (err <= B_OK) break;
825 		to_read = err;
826 		err = out->Write(buffer, to_read);
827 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
828 		if (err < B_OK) break;
829 	}
830 
831 	if (buffer != temp) free(buffer);
832 	return (err >= 0) ? B_OK : err;
833 }
834 
835 
836 /*!	Encode into the native format */
837 status_t
838 JPEGTranslator::Compress(BPositionIO* in, BPositionIO* out,
839 	const jmp_buf* longJumpBuffer)
840 {
841 	using namespace conversion;
842 
843 	// Read info about bitmap
844 	TranslatorBitmap header;
845 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
846 	if (err < B_OK)
847 		return err;
848 	else if (err < (int)sizeof(TranslatorBitmap))
849 		return B_ERROR;
850 
851 	// Grab dimension, color space, and size information from the stream
852 	BRect bounds;
853 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
854 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
855 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
856 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
857 
858 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
859 
860 	int width = bounds.IntegerWidth() + 1;
861 	int height = bounds.IntegerHeight() + 1;
862 
863 	// Function pointer to convert function
864 	// It will point to proper function if needed
865 	void (*converter)(uchar* inscanline, uchar* outscanline,
866 		int32 inRowBytes) = NULL;
867 
868 	// Default color info
869 	J_COLOR_SPACE jpg_color_space = JCS_RGB;
870 	int jpg_input_components = 3;
871 	int32 out_row_bytes;
872 	int padding = 0;
873 
874 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) {
875 		case B_CMAP8:
876 			converter = convert_from_cmap8_to_24;
877 			padding = in_row_bytes - width;
878 			break;
879 
880 		case B_GRAY1:
881 			if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24, NULL)) {
882 				converter = convert_from_gray1_to_24;
883 			} else {
884 				jpg_input_components = 1;
885 				jpg_color_space = JCS_GRAYSCALE;
886 				converter = convert_from_gray1_to_gray8;
887 			}
888 			padding = in_row_bytes - (width / 8);
889 			break;
890 
891 		case B_GRAY8:
892 			jpg_input_components = 1;
893 			jpg_color_space = JCS_GRAYSCALE;
894 			padding = in_row_bytes - width;
895 			break;
896 
897 		case B_RGB15:
898 		case B_RGBA15:
899 			converter = convert_from_15_to_24;
900 			padding = in_row_bytes - (width * 2);
901 			break;
902 
903 		case B_RGB15_BIG:
904 		case B_RGBA15_BIG:
905 			converter = convert_from_15b_to_24;
906 			padding = in_row_bytes - (width * 2);
907 			break;
908 
909 		case B_RGB16:
910 			converter = convert_from_16_to_24;
911 			padding = in_row_bytes - (width * 2);
912 			break;
913 
914 		case B_RGB16_BIG:
915 			converter = convert_from_16b_to_24;
916 			padding = in_row_bytes - (width * 2);
917 			break;
918 
919 		case B_RGB24:
920 			converter = convert_from_24_to_24;
921 			padding = in_row_bytes - (width * 3);
922 			break;
923 
924 		case B_RGB24_BIG:
925 			padding = in_row_bytes - (width * 3);
926 			break;
927 
928 		case B_RGB32:
929 		case B_RGBA32:
930 			converter = convert_from_32_to_24;
931 			padding = in_row_bytes - (width * 4);
932 			break;
933 
934 		case B_RGB32_BIG:
935 		case B_RGBA32_BIG:
936 			converter = convert_from_32b_to_24;
937 			padding = in_row_bytes - (width * 4);
938 			break;
939 
940 		case B_CMYK32:
941 			jpg_color_space = JCS_CMYK;
942 			jpg_input_components = 4;
943 			padding = in_row_bytes - (width * 4);
944 			break;
945 
946 		default:
947 			syslog(LOG_ERR, "Wrong type: Color space not implemented.\n");
948 			return B_ERROR;
949 	}
950 	out_row_bytes = jpg_input_components * width;
951 
952 	// Set basic things needed for jpeg writing
953 	struct jpeg_compress_struct cinfo;
954 	struct be_jpeg_error_mgr jerr;
955 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
956 	jpeg_create_compress(&cinfo);
957 	be_jpeg_stdio_dest(&cinfo, out);
958 
959 	// Set basic values
960 	cinfo.image_width = width;
961 	cinfo.image_height = height;
962 	cinfo.input_components = jpg_input_components;
963 	cinfo.in_color_space = jpg_color_space;
964 	jpeg_set_defaults(&cinfo);
965 
966 	// Set better accuracy
967 	cinfo.dct_method = JDCT_ISLOW;
968 
969 	// This is needed to prevent some colors loss
970 	// With it generated jpegs are as good as from Fireworks (at last! :D)
971 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL)) {
972 		int index = 0;
973 		while (index < cinfo.num_components) {
974 			cinfo.comp_info[index].h_samp_factor = 1;
975 			cinfo.comp_info[index].v_samp_factor = 1;
976 			// This will make file smaller, but with worse quality more or less
977 			// like with 93%-94% (but it's subjective opinion) on tested images
978 			// but with smaller size (between 92% and 93% on tested images)
979 			if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
980 				cinfo.comp_info[index].quant_tbl_no = 1;
981 			// This will make bigger file, but also better quality ;]
982 			// from my tests it seems like useless - better quality with smaller
983 			// can be acheived without this
984 //			cinfo.comp_info[index].dc_tbl_no = 1;
985 //			cinfo.comp_info[index].ac_tbl_no = 1;
986 			index++;
987 		}
988 	}
989 
990 	// Set quality
991 	jpeg_set_quality(&cinfo, fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL), true);
992 
993 	// Set progressive compression if needed
994 	// if not, turn on optimizing in libjpeg
995 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
996 		jpeg_simple_progression(&cinfo);
997 	else
998 		cinfo.optimize_coding = TRUE;
999 
1000 	// Set smoothing (effect like Blur)
1001 	cinfo.smoothing_factor = fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL);
1002 
1003 	// Initialize compression
1004 	jpeg_start_compress(&cinfo, TRUE);
1005 
1006 	// Declare scanlines
1007 	JSAMPROW in_scanline = NULL;
1008 	JSAMPROW out_scanline = NULL;
1009 	JSAMPROW writeline;
1010 		// Pointer to in_scanline (default) or out_scanline (if there will be conversion)
1011 
1012 	// Allocate scanline
1013 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1014 	in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1015 		JPOOL_PERMANENT, in_row_bytes);
1016 
1017 	// We need 2nd scanline storage ony for conversion
1018 	if (converter != NULL) {
1019 		// There will be conversion, allocate second scanline...
1020 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1021 	    out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1022 	    	JPOOL_PERMANENT, out_row_bytes);
1023 		// ... and make it the one to write to file
1024 		writeline = out_scanline;
1025 	} else
1026 		writeline = in_scanline;
1027 
1028 	while (cinfo.next_scanline < cinfo.image_height) {
1029 		// Read scanline
1030 		err = in->Read(in_scanline, in_row_bytes);
1031 		if (err < in_row_bytes)
1032 			return err < B_OK ? Error((j_common_ptr)&cinfo, err)
1033 				: Error((j_common_ptr)&cinfo, B_ERROR);
1034 
1035 		// Convert if needed
1036 		if (converter != NULL)
1037 			converter(in_scanline, out_scanline, in_row_bytes - padding);
1038 
1039 		// Write scanline
1040 	   	jpeg_write_scanlines(&cinfo, &writeline, 1);
1041 	}
1042 
1043 	jpeg_finish_compress(&cinfo);
1044 	jpeg_destroy_compress(&cinfo);
1045 	return B_OK;
1046 }
1047 
1048 
1049 /*!	Decode the native format */
1050 status_t
1051 JPEGTranslator::Decompress(BPositionIO* in, BPositionIO* out,
1052 	BMessage* ioExtension, const jmp_buf* longJumpBuffer)
1053 {
1054 	using namespace conversion;
1055 
1056 	// Set basic things needed for jpeg reading
1057 	struct jpeg_decompress_struct cinfo;
1058 	struct be_jpeg_error_mgr jerr;
1059 	cinfo.err = be_jpeg_std_error(&jerr, fSettings, longJumpBuffer);
1060 	jpeg_create_decompress(&cinfo);
1061 	be_jpeg_stdio_src(&cinfo, in);
1062 
1063 	jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
1064 		// make sure the EXIF tag is stored
1065 
1066 	// Read info about image
1067 	jpeg_read_header(&cinfo, TRUE);
1068 
1069 	BMessage exif;
1070 
1071 	// parse EXIF data and add it ioExtension, if any
1072 	jpeg_marker_struct* marker = cinfo.marker_list;
1073 	while (marker != NULL) {
1074 		if (marker->marker == MARKER_EXIF
1075 			&& !strncmp((char*)marker->data, "Exif", 4)) {
1076 			if (ioExtension != NULL) {
1077 				// Strip EXIF header from TIFF data
1078 				ioExtension->AddData("exif", B_RAW_TYPE,
1079 					(uint8*)marker->data + 6, marker->data_length - 6);
1080 			}
1081 
1082 			BMemoryIO io(marker->data + 6, marker->data_length - 6);
1083 			convert_exif_to_message(io, exif);
1084 		}
1085 		marker = marker->next;
1086 	}
1087 
1088 	// Default color info
1089 	color_space outColorSpace = B_RGB32;
1090 	int outColorComponents = 4;
1091 
1092 	// Function pointer to convert function
1093 	// It will point to proper function if needed
1094 	void (*converter)(uchar* inScanLine, uchar* outScanLine,
1095 		int32 inRowBytes, int32 xStep) = convert_from_24_to_32;
1096 
1097 	// If color space isn't rgb
1098 	if (cinfo.out_color_space != JCS_RGB) {
1099 		switch (cinfo.out_color_space) {
1100 			case JCS_UNKNOWN:		/* error/unspecified */
1101 				syslog(LOG_ERR, "From Type: Jpeg uses unknown color type\n");
1102 				break;
1103 			case JCS_GRAYSCALE:		/* monochrome */
1104 				// Check if user wants to read only as RGB32 or not
1105 				if (!fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL)) {
1106 					// Grayscale
1107 					outColorSpace = B_GRAY8;
1108 					outColorComponents = 1;
1109 					converter = translate_8;
1110 				} else {
1111 					// RGB
1112 					cinfo.out_color_space = JCS_RGB;
1113 					cinfo.output_components = 3;
1114 					converter = convert_from_24_to_32;
1115 				}
1116 				break;
1117 			case JCS_YCbCr:		/* Y/Cb/Cr (also known as YUV) */
1118 				cinfo.out_color_space = JCS_RGB;
1119 				converter = convert_from_24_to_32;
1120 				break;
1121 			case JCS_YCCK:		/* Y/Cb/Cr/K */
1122 				// Let libjpeg convert it to CMYK
1123 				cinfo.out_color_space = JCS_CMYK;
1124 				// Fall through to CMYK since we need the same settings
1125 			case JCS_CMYK:		/* C/M/Y/K */
1126 				// Use proper converter
1127 				if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK))
1128 					converter = convert_from_CMYK_to_32_photoshop;
1129 				else
1130 					converter = convert_from_CMYK_to_32;
1131 				break;
1132 			default:
1133 				syslog(LOG_ERR,
1134 						"From Type: Jpeg uses hmm... i don't know really :(\n");
1135 				break;
1136 		}
1137 	}
1138 
1139 	// Initialize decompression
1140 	jpeg_start_decompress(&cinfo);
1141 
1142 	// retrieve orientation from settings/EXIF
1143 	int32 orientation;
1144 	if (ioExtension == NULL
1145 		|| ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) {
1146 		if (exif.FindInt32("Orientation", &orientation) != B_OK)
1147 			orientation = 1;
1148 	}
1149 
1150 	if (orientation != 1 && converter == NULL)
1151 		converter = translate_8;
1152 
1153 	int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width;
1154 	int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height;
1155 
1156 	int32 destOffset = dest_index(outputWidth, outputHeight,
1157 		0, 0, orientation) * outColorComponents;
1158 	int32 xStep = dest_index(outputWidth, outputHeight,
1159 		1, 0, orientation) * outColorComponents - destOffset;
1160 	int32 yStep = dest_index(outputWidth, outputHeight,
1161 		0, 1, orientation) * outColorComponents - destOffset;
1162 	bool needAll = orientation != 1;
1163 
1164 	// Initialize this bounds rect to the size of your image
1165 	BRect bounds(0, 0, outputWidth - 1, outputHeight - 1);
1166 
1167 #if 0
1168 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n",
1169 	destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height,
1170 	bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation);
1171 #endif
1172 
1173 	// Bytes count in one line of image (scanline)
1174 	int32 inRowBytes = cinfo.output_width * cinfo.output_components;
1175 	int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents;
1176 	int32 dataSize = cinfo.output_width * cinfo.output_height
1177 		* outColorComponents;
1178 
1179 	// Fill out the B_TRANSLATOR_BITMAP's header
1180 	TranslatorBitmap header;
1181 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1182 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1183 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1184 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1185 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1186 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
1187 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
1188 	header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize);
1189 
1190 	// Write out the header
1191 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1192 	if (err < B_OK)
1193 		return Error((j_common_ptr)&cinfo, err);
1194 	else if (err < (int)sizeof(TranslatorBitmap))
1195 		return Error((j_common_ptr)&cinfo, B_ERROR);
1196 
1197 	// Declare scanlines
1198 	JSAMPROW inScanLine = NULL;
1199 	uint8* dest = NULL;
1200 	uint8* destLine = NULL;
1201 
1202 	// Allocate scanline
1203 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1204     inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1205     	JPOOL_PERMANENT, inRowBytes);
1206 
1207 	// We need 2nd scanline storage only for conversion
1208 	if (converter != NULL) {
1209 		// There will be conversion, allocate second scanline...
1210 		// Use libjpeg memory allocation functions, so in case of error it will
1211 		// free them itself
1212 	    dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
1213 	    	JPOOL_PERMANENT, needAll ? dataSize : rowBytes);
1214 	    destLine = dest + destOffset;
1215 	} else
1216 		destLine = inScanLine;
1217 
1218 	while (cinfo.output_scanline < cinfo.output_height) {
1219 		// Read scanline
1220 		jpeg_read_scanlines(&cinfo, &inScanLine, 1);
1221 
1222 		// Convert if needed
1223 		if (converter != NULL)
1224 			converter(inScanLine, destLine, inRowBytes, xStep);
1225 
1226 		if (!needAll) {
1227 	  		// Write the scanline buffer to the output stream
1228 			ssize_t bytesWritten = out->Write(destLine, rowBytes);
1229 			if (bytesWritten < rowBytes) {
1230 				return bytesWritten < B_OK
1231 					? Error((j_common_ptr)&cinfo, bytesWritten)
1232 					: Error((j_common_ptr)&cinfo, B_ERROR);
1233 			}
1234 		} else
1235 			destLine += yStep;
1236 	}
1237 
1238 	if (needAll) {
1239 		ssize_t bytesWritten = out->Write(dest, dataSize);
1240 		if (bytesWritten < dataSize) {
1241 			return bytesWritten < B_OK
1242 				? Error((j_common_ptr)&cinfo, bytesWritten)
1243 				: Error((j_common_ptr)&cinfo, B_ERROR);
1244 		}
1245 	}
1246 
1247 	jpeg_finish_decompress(&cinfo);
1248 	jpeg_destroy_decompress(&cinfo);
1249 	return B_OK;
1250 }
1251 
1252 /*! have the other PopulateInfoFromFormat() check both inputFormats & outputFormats */
1253 status_t
1254 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1255 	uint32 formatType, translator_id id)
1256 {
1257 	int32 formatCount;
1258 	const translation_format* formats = OutputFormats(&formatCount);
1259 	for (int i = 0; i <= 1 ;formats = InputFormats(&formatCount), i++) {
1260 		if (PopulateInfoFromFormat(info, formatType,
1261 			formats, formatCount) == B_OK) {
1262 			info->translator = id;
1263 			return B_OK;
1264 		}
1265 	}
1266 
1267 	return B_ERROR;
1268 }
1269 
1270 
1271 status_t
1272 JPEGTranslator::PopulateInfoFromFormat(translator_info* info,
1273 	uint32 formatType, const translation_format* formats, int32 formatCount)
1274 {
1275 	for (int i = 0; i < formatCount; i++) {
1276 		if (formats[i].type == formatType) {
1277 			info->type = formatType;
1278 			info->group = formats[i].group;
1279 			info->quality = formats[i].quality;
1280 			info->capability = formats[i].capability;
1281 			BString str1(formats[i].name);
1282 			str1.ReplaceFirst("Be Bitmap Format (JPEGTranslator)",
1283 				B_TRANSLATE("Be Bitmap Format (JPEGTranslator)"));
1284 			strlcpy(info->name, str1.String(), sizeof(info->name));
1285 			strcpy(info->MIME,  formats[i].MIME);
1286 			return B_OK;
1287 		}
1288 	}
1289 
1290 	return B_ERROR;
1291 }
1292 
1293 /*!
1294 	Frees jpeg alocated memory
1295 	Returns given error (B_ERROR by default)
1296 */
1297 status_t
1298 JPEGTranslator::Error(j_common_ptr cinfo, status_t error)
1299 {
1300 	jpeg_destroy(cinfo);
1301 	return error;
1302 }
1303 
1304 
1305 JPEGTranslator::JPEGTranslator()
1306 	: BaseTranslator(sTranslatorName, sTranslatorInfo, sTranslatorVersion,
1307 		sInputFormats,  kNumInputFormats,
1308 		sOutputFormats, kNumOutputFormats,
1309 		SETTINGS_FILE,
1310 		sDefaultSettings, kNumDefaultSettings,
1311 		B_TRANSLATOR_BITMAP, JPEG_FORMAT)
1312 {}
1313 
1314 
1315 BTranslator*
1316 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
1317 {
1318 	if (n == 0)
1319 		return new JPEGTranslator();
1320 
1321 	return NULL;
1322 }
1323 
1324 
1325 int
1326 main(int, char**)
1327 {
1328 	BApplication app("application/x-vnd.Haiku-JPEGTranslator");
1329 	JPEGTranslator* translator = new JPEGTranslator();
1330 	if (LaunchTranslatorWindow(translator, sTranslatorName) == B_OK)
1331 		app.Run();
1332 
1333 	return 0;
1334 }
1335 
1336