1/*
2
3Copyright (c) 2002-2003, Marcin 'Shard' Konicki
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, 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
18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28EVEN 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
61static const int32 sTranslatorVersion = B_TRANSLATION_MAKE_VERSION(1, 2, 0);
62
63static const char* sTranslatorName = B_TRANSLATE("JPEG images");
64static 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
77static 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
85static 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
93static 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
105const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
106const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
107const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
108
109
110namespace conversion {
111
112
113static bool
114x_flipped(int32 orientation)
115{
116	return orientation == 2 || orientation == 3
117		|| orientation == 6 || orientation == 7;
118}
119
120
121static bool
122y_flipped(int32 orientation)
123{
124	return orientation == 3 || orientation == 4
125		|| orientation == 7 || orientation == 8;
126}
127
128
129static int32
130dest_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
149inline void
150convert_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
168inline void
169convert_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
189inline void
190convert_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
205inline void
206convert_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
222inline void
223convert_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
239inline void
240convert_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
256inline void
257convert_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
273inline void
274convert_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
287inline void
288convert_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
303inline void
304convert_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
322inline void
323convert_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 !!!
339inline void
340convert_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
356inline void
357convert_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
372void
373translate_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
390SSlider::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
402const char*
403SSlider::UpdateText() const
404{
405	snprintf(fStatusLabel, sizeof(fStatusLabel), "%ld", Value());
406	return fStatusLabel;
407}
408
409
410//	#pragma mark -
411
412
413TranslatorReadView::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
449TranslatorReadView::~TranslatorReadView()
450{
451	fSettings->Release();
452}
453
454
455void
456TranslatorReadView::AttachedToWindow()
457{
458	BView::AttachedToWindow();
459
460	fAlwaysRGB32->SetTarget(this);
461	fPhotoshopCMYK->SetTarget(this);
462	fShowErrorBox->SetTarget(this);
463}
464
465
466void
467TranslatorReadView::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
510TranslatorWriteView::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
572TranslatorWriteView::~TranslatorWriteView()
573{
574	fSettings->Release();
575}
576
577
578void
579TranslatorWriteView::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
592void
593TranslatorWriteView::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
665TranslatorAboutView::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
698TranslatorView::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
718BView*
719JPEGTranslator::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 */
727status_t
728JPEGTranslator::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
761status_t
762JPEGTranslator::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 */
805status_t
806JPEGTranslator::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 */
839status_t
840JPEGTranslator::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 */
1052status_t
1053JPEGTranslator::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
1170printf("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 */
1255status_t
1256JPEGTranslator::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
1273status_t
1274JPEGTranslator::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*/
1299status_t
1300JPEGTranslator::Error(j_common_ptr cinfo, status_t error)
1301{
1302	jpeg_destroy(cinfo);
1303	return error;
1304}
1305
1306
1307JPEGTranslator::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
1317BTranslator*
1318make_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
1327int
1328main(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