xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 #include "TranslatorWindow.h"
35 #include "exif_parser.h"
36 
37 #include <syslog.h>
38 
39 #include <Alignment.h>
40 #include <Catalog.h>
41 #include <LayoutBuilder.h>
42 #include <TabView.h>
43 #include <TextView.h>
44 
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "JPEGTranslator"
47 
48 #define MARKER_EXIF	0xe1
49 
50 // Set these accordingly
51 #define JPEG_ACRONYM "JPEG"
52 #define JPEG_FORMAT 'JPEG'
53 #define JPEG_MIME_STRING "image/jpeg"
54 #define JPEG_DESCRIPTION "JPEG image"
55 
56 // The translation kit's native file type
57 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap"
58 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap Format (JPEGTranslator)"
59 
60 
61 static const int32 sTranslatorVersion = B_TRANSLATION_MAKE_VERSION(1, 2, 0);
62 
63 static const char* sTranslatorName = B_TRANSLATE("JPEG images");
64 static const char* sTranslatorInfo = B_TRANSLATE("©2002-2003, Marcin Konicki\n"
65 	"©2005-2007, Haiku\n"
66 	"\n"
67 	"Based on IJG library ©  1994-2009, Thomas G. Lane, Guido Vollbeding.\n"
68 	"\thttp://www.ijg.org/files/\n"
69 	"\n"
70 	"with \"lossless\" encoding support patch by Ken Murchison\n"
71 	"\thttp://www.oceana.com/ftp/ljpeg/\n"
72 	"\n"
73 	"With some colorspace conversion routines by Magnus Hellman\n"
74 	"\thttp://www.bebits.com/app/802\n");
75 
76 // Define the formats we know how to read
77 static const translation_format sInputFormats[] = {
78 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
79 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
80 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
81 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }
82 };
83 
84 // Define the formats we know how to write
85 static const translation_format sOutputFormats[] = {
86 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
87 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
88 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
89 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }
90 };
91 
92 
93 static const TranSetting sDefaultSettings[] = {
94 	{JPEG_SET_SMOOTHING, TRAN_SETTING_INT32, 0},
95 	{JPEG_SET_QUALITY, TRAN_SETTING_INT32, 95},
96 	{JPEG_SET_PROGRESSIVE, TRAN_SETTING_BOOL, true},
97 	{JPEG_SET_OPT_COLORS, TRAN_SETTING_BOOL, true},
98 	{JPEG_SET_SMALL_FILES, TRAN_SETTING_BOOL, false},
99 	{JPEG_SET_GRAY1_AS_RGB24, TRAN_SETTING_BOOL, false},
100 	{JPEG_SET_ALWAYS_RGB32, TRAN_SETTING_BOOL, true},
101 	{JPEG_SET_PHOTOSHOP_CMYK, TRAN_SETTING_BOOL, true},
102 	{JPEG_SET_SHOWREADWARNING, TRAN_SETTING_BOOL, true}
103 };
104 
105 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
106 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
107 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
108 
109 
110 namespace conversion {
111 
112 
113 static bool
114 x_flipped(int32 orientation)
115 {
116 	return orientation == 2 || orientation == 3
117 		|| orientation == 6 || orientation == 7;
118 }
119 
120 
121 static bool
122 y_flipped(int32 orientation)
123 {
124 	return orientation == 3 || orientation == 4
125 		|| orientation == 7 || orientation == 8;
126 }
127 
128 
129 static int32
130 dest_index(uint32 width, uint32 height, uint32 x, uint32 y, int32 orientation)
131 {
132 	if (orientation > 4) {
133 		uint32 temp = x;
134 		x = y;
135 		y = temp;
136 	}
137 	if (y_flipped(orientation))
138 		y = height - 1 - y;
139 	if (x_flipped(orientation))
140 		x = width - 1 - x;
141 
142 	return y * width + x;
143 }
144 
145 
146 //	#pragma mark - conversion for compression
147 
148 
149 inline void
150 convert_from_gray1_to_gray8(uint8* in, uint8* out, int32 inRowBytes)
151 {
152 	int32 index = 0;
153 	int32 index2 = 0;
154 	while (index < inRowBytes) {
155 		unsigned char c = in[index++];
156 		for (int b = 128; b; b = b>>1) {
157 			unsigned char color;
158 			if (c & b)
159 				color = 0;
160 			else
161 				color = 255;
162 			out[index2++] = color;
163 		}
164 	}
165 }
166 
167 
168 inline void
169 convert_from_gray1_to_24(uint8* in, uint8* out, int32 inRowBytes)
170 {
171 	int32 index = 0;
172 	int32 index2 = 0;
173 	while (index < inRowBytes) {
174 		unsigned char c = in[index++];
175 		for (int b = 128; b; b = b>>1) {
176 			unsigned char color;
177 			if (c & b)
178 				color = 0;
179 			else
180 				color = 255;
181 			out[index2++] = color;
182 			out[index2++] = color;
183 			out[index2++] = color;
184 		}
185 	}
186 }
187 
188 
189 inline void
190 convert_from_cmap8_to_24(uint8* in, uint8* out, int32 inRowBytes)
191 {
192 	const color_map * map = system_colors();
193 	int32 index = 0;
194 	int32 index2 = 0;
195 	while (index < inRowBytes) {
196 		rgb_color color = map->color_list[in[index++]];
197 
198 		out[index2++] = color.red;
199 		out[index2++] = color.green;
200 		out[index2++] = color.blue;
201 	}
202 }
203 
204 
205 inline void
206 convert_from_15_to_24(uint8* in, uint8* out, int32 inRowBytes)
207 {
208 	int32 index = 0;
209 	int32 index2 = 0;
210 	int16 in_pixel;
211 	while (index < inRowBytes) {
212 		in_pixel = in[index] | (in[index + 1] << 8);
213 		index += 2;
214 
215 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
216 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
217 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
218 	}
219 }
220 
221 
222 inline void
223 convert_from_15b_to_24(uint8* in, uint8* out, int32 inRowBytes)
224 {
225 	int32 index = 0;
226 	int32 index2 = 0;
227 	int16 in_pixel;
228 	while (index < inRowBytes) {
229 		in_pixel = in[index + 1] | (in[index] << 8);
230 		index += 2;
231 
232 		out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12);
233 		out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7);
234 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
235 	}
236 }
237 
238 
239 inline void
240 convert_from_16_to_24(uint8* in, uint8* out, int32 inRowBytes)
241 {
242 	int32 index = 0;
243 	int32 index2 = 0;
244 	int16 in_pixel;
245 	while (index < inRowBytes) {
246 		in_pixel = in[index] | (in[index + 1] << 8);
247 		index += 2;
248 
249 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
250 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
251 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
252 	}
253 }
254 
255 
256 inline void
257 convert_from_16b_to_24(uint8* in, uint8* out, int32 inRowBytes)
258 {
259 	int32 index = 0;
260 	int32 index2 = 0;
261 	int16 in_pixel;
262 	while (index < inRowBytes) {
263 		in_pixel = in[index + 1] | (in[index] << 8);
264 		index += 2;
265 
266 		out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13);
267 		out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9);
268 		out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2);
269 	}
270 }
271 
272 
273 inline void
274 convert_from_24_to_24(uint8* in, uint8* out, int32 inRowBytes)
275 {
276 	int32 index = 0;
277 	int32 index2 = 0;
278 	while (index < inRowBytes) {
279 		out[index2++] = in[index + 2];
280 		out[index2++] = in[index + 1];
281 		out[index2++] = in[index];
282 		index+=3;
283 	}
284 }
285 
286 
287 inline void
288 convert_from_32_to_24(uint8* in, uint8* out, int32 inRowBytes)
289 {
290 	inRowBytes /= 4;
291 
292 	for (int32 i = 0; i < inRowBytes; i++) {
293 		out[0] = in[2];
294 		out[1] = in[1];
295 		out[2] = in[0];
296 
297 		in += 4;
298 		out += 3;
299 	}
300 }
301 
302 
303 inline void
304 convert_from_32b_to_24(uint8* in, uint8* out, int32 inRowBytes)
305 {
306 	inRowBytes /= 4;
307 
308 	for (int32 i = 0; i < inRowBytes; i++) {
309 		out[0] = in[1];
310 		out[1] = in[2];
311 		out[2] = in[3];
312 
313 		in += 4;
314 		out += 3;
315 	}
316 }
317 
318 
319 //	#pragma mark - conversion for decompression
320 
321 
322 inline void
323 convert_from_CMYK_to_32_photoshop(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
324 {
325 	for (int32 i = 0; i < inRowBytes; i += 4) {
326 		int32 black = in[3];
327 		out[0] = in[2] * black / 255;
328 		out[1] = in[1] * black / 255;
329 		out[2] = in[0] * black / 255;
330 		out[3] = 255;
331 
332 		in += 4;
333 		out += xStep;
334 	}
335 }
336 
337 
338 //!	!!! UNTESTED !!!
339 inline void
340 convert_from_CMYK_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
341 {
342 	for (int32 i = 0; i < inRowBytes; i += 4) {
343 		int32 black = 255 - in[3];
344 		out[0] = ((255 - in[2]) * black) / 255;
345 		out[1] = ((255 - in[1]) * black) / 255;
346 		out[2] = ((255 - in[0]) * black) / 255;
347 		out[3] = 255;
348 
349 		in += 4;
350 		out += xStep;
351 	}
352 }
353 
354 
355 //!	RGB24 8:8:8 to xRGB 8:8:8:8
356 inline void
357 convert_from_24_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
358 {
359 	for (int32 i = 0; i < inRowBytes; i += 3) {
360 		out[0] = in[2];
361 		out[1] = in[1];
362 		out[2] = in[0];
363 		out[3] = 255;
364 
365 		in += 3;
366 		out += xStep;
367 	}
368 }
369 
370 
371 //! 8-bit to 8-bit, only need when rotating the image
372 void
373 translate_8(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
374 {
375 	for (int32 i = 0; i < inRowBytes; i++) {
376 		out[0] = in[0];
377 
378 		in++;
379 		out += xStep;
380 	}
381 }
382 
383 
384 } // namespace conversion
385 
386 
387 //	#pragma mark -
388 
389 
390 SSlider::SSlider(const char* name, const char* label,
391 		BMessage* message, int32 minValue, int32 maxValue, orientation posture,
392 		thumb_style thumbType, uint32 flags)
393 	: BSlider(name, label, message, minValue, maxValue,
394 		posture, thumbType, flags)
395 {
396 	rgb_color barColor = { 0, 0, 229, 255 };
397 	UseFillColor(true, &barColor);
398 }
399 
400 
401 //!	Update status string - show actual value
402 const char*
403 SSlider::UpdateText() const
404 {
405 	snprintf(fStatusLabel, sizeof(fStatusLabel), "%ld", Value());
406 	return fStatusLabel;
407 }
408 
409 
410 //	#pragma mark -
411 
412 
413 TranslatorReadView::TranslatorReadView(const char* name,
414 	TranslatorSettings* settings)
415 	:
416 	BView(name, 0, new BGroupLayout(B_HORIZONTAL)),
417 	fSettings(settings)
418 		// settings should already be Acquired()
419 {
420 	fAlwaysRGB32 = new BCheckBox("alwaysrgb32",
421 		B_TRANSLATE("Read greyscale images as RGB32"),
422 		new BMessage(VIEW_MSG_SET_ALWAYSRGB32));
423 	if (fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, NULL))
424 		fAlwaysRGB32->SetValue(B_CONTROL_ON);
425 
426 	fPhotoshopCMYK = new BCheckBox("photoshopCMYK",
427 		B_TRANSLATE("Use CMYK code with 0 for 100% ink coverage"),
428 		new BMessage(VIEW_MSG_SET_PHOTOSHOPCMYK));
429 	if (fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK, NULL))
430 		fPhotoshopCMYK->SetValue(B_CONTROL_ON);
431 
432 	fShowErrorBox = new BCheckBox("error",
433 		B_TRANSLATE("Show warning messages"),
434 		new BMessage(VIEW_MSG_SET_SHOWREADERRORBOX));
435 	if (fSettings->SetGetBool(JPEG_SET_SHOWREADWARNING, NULL))
436 		fShowErrorBox->SetValue(B_CONTROL_ON);
437 
438 	float padding = 5.0f;
439 
440 	BLayoutBuilder::Group<>(this, B_VERTICAL, padding)
441 		.SetInsets(padding)
442 		.Add(fAlwaysRGB32)
443 		.Add(fPhotoshopCMYK)
444 		.Add(fShowErrorBox)
445 		.AddGlue();
446 }
447 
448 
449 TranslatorReadView::~TranslatorReadView()
450 {
451 	fSettings->Release();
452 }
453 
454 
455 void
456 TranslatorReadView::AttachedToWindow()
457 {
458 	BView::AttachedToWindow();
459 
460 	fAlwaysRGB32->SetTarget(this);
461 	fPhotoshopCMYK->SetTarget(this);
462 	fShowErrorBox->SetTarget(this);
463 }
464 
465 
466 void
467 TranslatorReadView::MessageReceived(BMessage* message)
468 {
469 	switch (message->what) {
470 		case VIEW_MSG_SET_ALWAYSRGB32:
471 		{
472 			int32 value;
473 			if (message->FindInt32("be:value", &value) == B_OK) {
474 				bool boolValue = value;
475 				fSettings->SetGetBool(JPEG_SET_ALWAYS_RGB32, &boolValue);
476 				fSettings->SaveSettings();
477 			}
478 			break;
479 		}
480 		case VIEW_MSG_SET_PHOTOSHOPCMYK:
481 		{
482 			int32 value;
483 			if (message->FindInt32("be:value", &value) == B_OK) {
484 				bool boolValue = value;
485 				fSettings->SetGetBool(JPEG_SET_PHOTOSHOP_CMYK, &boolValue);
486 				fSettings->SaveSettings();
487 			}
488 			break;
489 		}
490 		case VIEW_MSG_SET_SHOWREADERRORBOX:
491 		{
492 			int32 value;
493 			if (message->FindInt32("be:value", &value) == B_OK) {
494 				bool boolValue = value;
495 				fSettings->SetGetBool(JPEG_SET_SHOWREADWARNING, &boolValue);
496 				fSettings->SaveSettings();
497 			}
498 			break;
499 		}
500 		default:
501 			BView::MessageReceived(message);
502 			break;
503 	}
504 }
505 
506 
507 //	#pragma mark - TranslatorWriteView
508 
509 
510 TranslatorWriteView::TranslatorWriteView(const char* name,
511 	TranslatorSettings* settings)
512 	:
513 	BView(name, 0, new BGroupLayout(B_VERTICAL)),
514 	fSettings(settings)
515 		// settings should already be Acquired()
516 {
517 	fQualitySlider = new SSlider("quality", B_TRANSLATE("Output quality"),
518 		new BMessage(VIEW_MSG_SET_QUALITY), 0, 100);
519 	fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
520 	fQualitySlider->SetHashMarkCount(10);
521 	fQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High"));
522 	fQualitySlider->SetValue(fSettings->SetGetInt32(JPEG_SET_QUALITY, NULL));
523 
524 	fSmoothingSlider = new SSlider("smoothing",
525 		B_TRANSLATE("Output smoothing strength"),
526 		new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100);
527 	fSmoothingSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
528 	fSmoothingSlider->SetHashMarkCount(10);
529 	fSmoothingSlider->SetLimitLabels(B_TRANSLATE("None"), B_TRANSLATE("High"));
530 	fSmoothingSlider->SetValue(
531 		fSettings->SetGetInt32(JPEG_SET_SMOOTHING, NULL));
532 
533 	fProgress = new BCheckBox("progress",
534 		B_TRANSLATE("Use progressive compression"),
535 		new BMessage(VIEW_MSG_SET_PROGRESSIVE));
536 	if (fSettings->SetGetBool(JPEG_SET_PROGRESSIVE, NULL))
537 		fProgress->SetValue(B_CONTROL_ON);
538 
539 	fSmallerFile = new BCheckBox("smallerfile",
540 		B_TRANSLATE("Make file smaller (sligthtly worse quality)"),
541 		new BMessage(VIEW_MSG_SET_SMALLERFILE));
542 	if (fSettings->SetGetBool(JPEG_SET_SMALL_FILES))
543 		fSmallerFile->SetValue(B_CONTROL_ON);
544 
545 	fOptimizeColors = new BCheckBox("optimizecolors",
546 		B_TRANSLATE("Prevent colors 'washing out'"),
547 		new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS));
548 	if (fSettings->SetGetBool(JPEG_SET_OPT_COLORS, NULL))
549 		fOptimizeColors->SetValue(B_CONTROL_ON);
550 	else
551 		fSmallerFile->SetEnabled(false);
552 
553 	fGrayAsRGB24 = new BCheckBox("gray1asrgb24",
554 		B_TRANSLATE("Write black-and-white images as RGB24"),
555 		new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
556 	if (fSettings->SetGetBool(JPEG_SET_GRAY1_AS_RGB24))
557 		fGrayAsRGB24->SetValue(B_CONTROL_ON);
558 
559 	float padding = 5.0f;
560 	BLayoutBuilder::Group<>(this, B_VERTICAL, padding)
561 		.SetInsets(padding)
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[16];
675 	sprintf(versionString, "v%d.%d.%d", (int)(sTranslatorVersion >> 8),
676 		(int)((sTranslatorVersion >> 4) & 0xf), (int)(sTranslatorVersion & 0xf));
677 
678 	BStringView* version = new BStringView("Version", versionString);
679 	version->SetExplicitAlignment(labelAlignment);
680 
681 	BTextView* infoView = new BTextView("info");
682 	infoView->SetText(sTranslatorInfo);
683 	infoView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
684 	infoView->MakeEditable(false);
685 
686 	float padding = 5.0f;
687 	BLayoutBuilder::Group<>(this, B_VERTICAL, padding)
688 		.SetInsets(padding)
689 		.AddGroup(B_HORIZONTAL, padding)
690 			.Add(title)
691 			.Add(version)
692 			.AddGlue()
693 		.End()
694 		.Add(infoView);
695 }
696 
697 
698 TranslatorView::TranslatorView(const char* name, TranslatorSettings* settings)
699 	:
700 	BTabView(name)
701 {
702 	AddTab(new TranslatorWriteView(B_TRANSLATE("Write"), settings->Acquire()));
703 	AddTab(new TranslatorReadView(B_TRANSLATE("Read"), settings->Acquire()));
704 	AddTab(new TranslatorAboutView(B_TRANSLATE("About")));
705 
706 	settings->Release();
707 
708  	BFont font;
709  	GetFont(&font);
710  	SetExplicitPreferredSize(
711 		BSize((font.Size() * 380) / 12, (font.Size() * 250) / 12));
712 }
713 
714 
715 //	#pragma mark - Translator Add-On
716 
717 
718 BView*
719 JPEGTranslator::NewConfigView(TranslatorSettings* settings)
720 {
721 	BView* configView = new TranslatorView("TranslatorView", settings);
722 	return configView;
723 }
724 
725 
726 /*! Determine whether or not we can handle this data */
727 status_t
728 JPEGTranslator::DerivedIdentify(BPositionIO* inSource,
729 	const translation_format* inFormat, BMessage* ioExtension,
730 	translator_info* outInfo, uint32 outType)
731 {
732 	if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT)
733 		return B_NO_TRANSLATOR;
734 
735 	// !!! You might need to make this buffer bigger to test for your native format
736 	off_t position = inSource->Position();
737 	char header[sizeof(TranslatorBitmap)];
738 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
739 	inSource->Seek(position, SEEK_SET);
740 	if (err < B_OK)
741 		return err;
742 
743 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
744 		if (PopulateInfoFromFormat(outInfo, B_TRANSLATOR_BITMAP) != B_OK)
745 			return B_NO_TRANSLATOR;
746 	} else {
747 		// First 3 bytes in jpg files are always the same from what i've seen so far
748 		// check them
749 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
750 			if (PopulateInfoFromFormat(outInfo, JPEG_FORMAT) != B_OK)
751 				return B_NO_TRANSLATOR;
752 
753 		} else
754 			return B_NO_TRANSLATOR;
755 	}
756 
757 	return B_OK;
758 }
759 
760 
761 status_t
762 JPEGTranslator::DerivedTranslate(BPositionIO* inSource,
763 	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
764 	BPositionIO* outDestination, int32 baseType)
765 {
766 	// If no specific type was requested, convert to the interchange format
767 	if (outType == 0)
768 		outType = B_TRANSLATOR_BITMAP;
769 
770 	// Setup a "breakpoint" since throwing exceptions does not seem to work
771 	// at all in an add-on. (?)
772 	// In the be_jerror.cpp we implement a handler for critical library errors
773 	// (be_error_exit()) and there we use the longjmp() function to return to
774 	// this place. If this happens, it is as if the setjmp() call is called
775 	// a second time, but this time the return value will be 1. The first
776 	// invokation will return 0.
777 	jmp_buf longJumpBuffer;
778 	int jmpRet = setjmp(longJumpBuffer);
779 	if (jmpRet == 1)
780 		return B_ERROR;
781 
782 	try {
783 		// What action to take, based on the findings of Identify()
784 		if (outType == inInfo->type) {
785 			return Copy(inSource, outDestination);
786 		} else if (inInfo->type == B_TRANSLATOR_BITMAP
787 				&& outType == JPEG_FORMAT) {
788 			return Compress(inSource, outDestination, &longJumpBuffer);
789 		} else if (inInfo->type == JPEG_FORMAT
790 				&& outType == B_TRANSLATOR_BITMAP) {
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 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 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 			strncpy(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