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