1/*
2
3Copyright (c) 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 "JPEG2000Translator.h"
34#include "TranslatorWindow.h"
35
36#include <syslog.h>
37
38#include <LayoutBuilder.h>
39#include <TabView.h>
40#include <TextView.h>
41
42
43#undef B_TRANSLATION_CONTEXT
44#define B_TRANSLATION_CONTEXT "JPEG2000Translator"
45
46// Set these accordingly
47#define JP2_ACRONYM "JP2"
48#define JP2_FORMAT 'JP2 '
49#define JP2_MIME_STRING "image/jp2"
50#define JP2_DESCRIPTION "JPEG2000 image"
51
52// The translation kit's native file type
53#define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap"
54#define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap Format (JPEG2000Translator)"
55
56static int32 sTranslatorVersion = B_TRANSLATION_MAKE_VERSION(1, 0, 0);
57
58static const char* sTranslatorName = B_TRANSLATE("JPEG2000 images");
59static const char* sTranslatorInfo = B_TRANSLATE("��2002-2003, Shard\n"
60	"��2005-2006, Haiku\n"
61	"\n"
62	"Based on JasPer library:\n"
63	"�� 1999-2000, Image Power, Inc. and\n"
64	"the University of British Columbia, Canada.\n"
65	"�� 2001-2003 Michael David Adams.\n"
66	"\thttp://www.ece.uvic.ca/~mdadams/jasper/\n"
67	"\n"
68	"ImageMagick's jp2 codec was used as \"tutorial\".\n"
69	"\thttp://www.imagemagick.org/\n");
70
71static const translation_format sInputFormats[] = {
72	{ JP2_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
73		JP2_MIME_STRING, JP2_DESCRIPTION },
74	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
75		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
76};
77
78static const translation_format sOutputFormats[] = {
79	{ JP2_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
80		JP2_MIME_STRING, JP2_DESCRIPTION },
81	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
82		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
83};
84
85
86static const TranSetting sDefaultSettings[] = {
87	{JP2_SET_QUALITY, TRAN_SETTING_INT32, 25},
88	{JP2_SET_JPC, TRAN_SETTING_BOOL, false},
89	{JP2_SET_GRAY1_AS_B_RGB24, TRAN_SETTING_BOOL, false},
90	{JP2_SET_GRAY8_AS_B_RGB32, TRAN_SETTING_BOOL, true}
91};
92
93const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
94const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
95const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
96
97
98#define	JP2_BOX_JP		0x6a502020
99#define	JPC_MS_SOC		0xff4f
100
101
102namespace conversion{
103//!	Make RGB32 scanline from *pixels[3]
104inline void
105read_rgb24_to_rgb32(jas_matrix_t** pixels, uchar* scanline, int width)
106{
107	int32 index = 0;
108	int32 x = 0;
109	while (x < width) {
110		scanline[index++] = (uchar)jas_matrix_getv(pixels[2], x);
111		scanline[index++] = (uchar)jas_matrix_getv(pixels[1], x);
112		scanline[index++] = (uchar)jas_matrix_getv(pixels[0], x);
113		scanline[index++] = 255;
114		x++;
115	}
116}
117
118
119//!	Make gray scanline from *pixels[1]
120inline void
121read_gray_to_rgb32(jas_matrix_t** pixels, uchar* scanline, int width)
122{
123	int32 index = 0;
124	int32 x = 0;
125	uchar color = 0;
126	while (x < width) {
127		color = (uchar)jas_matrix_getv(pixels[0], x++);
128		scanline[index++] = color;
129		scanline[index++] = color;
130		scanline[index++] = color;
131		scanline[index++] = 255;
132	}
133}
134
135
136/*!
137	Make RGBA32 scanline from *pixels[4]
138	(just read data to scanline)
139*/
140inline void
141read_rgba32(jas_matrix_t** pixels, uchar *scanline, int width)
142{
143	int32 index = 0;
144	int32 x = 0;
145	while (x < width) {
146		scanline[index++] = (uchar)jas_matrix_getv(pixels[2], x);
147		scanline[index++] = (uchar)jas_matrix_getv(pixels[1], x);
148		scanline[index++] = (uchar)jas_matrix_getv(pixels[0], x);
149		scanline[index++] = (uchar)jas_matrix_getv(pixels[3], x);
150		x++;
151	}
152}
153
154
155/*!
156	Make gray scanline from *pixels[1]
157	(just read data to scanline)
158*/
159inline void
160read_gray(jas_matrix_t** pixels, uchar* scanline, int width)
161{
162	int32 x = 0;
163	while (x < width) {
164		scanline[x] = (uchar)jas_matrix_getv(pixels[0], x);
165		x++;
166	}
167}
168
169
170//!	Make *pixels[1] from gray1 scanline
171inline void
172write_gray1_to_gray(jas_matrix_t** pixels, uchar* scanline, int width)
173{
174	int32 x = 0;
175	int32 index = 0;
176	while (x < (width/8)) {
177		unsigned char c = scanline[x++];
178		for (int b = 128; b; b = b >> 1) {
179			if (c & b)
180				jas_matrix_setv(pixels[0], index++, 0);
181			else
182				jas_matrix_setv(pixels[0], index++, 255);
183		}
184	}
185}
186
187
188//!	Make *pixels[3] from gray1 scanline
189inline void
190write_gray1_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
191{
192	int32 x = 0;
193	int32 index = 0;
194	while (x < (width / 8)) {
195		unsigned char c = scanline[x++];
196		for (int b = 128; b; b = b >> 1) {
197			if (c & b) {
198				jas_matrix_setv(pixels[0], index, 0);
199				jas_matrix_setv(pixels[1], index, 0);
200				jas_matrix_setv(pixels[2], index, 0);
201			} else {
202				jas_matrix_setv(pixels[0], index, 255);
203				jas_matrix_setv(pixels[1], index, 255);
204				jas_matrix_setv(pixels[2], index, 255);
205			}
206			index++;
207		}
208	}
209}
210
211
212//!	Make *pixels[3] from cmap8 scanline
213inline void
214write_cmap8_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
215{
216	const color_map* map = system_colors();
217	int32 x = 0;
218	while (x < width) {
219		rgb_color color = map->color_list[scanline[x]];
220
221		jas_matrix_setv(pixels[0], x, color.red);
222		jas_matrix_setv(pixels[1], x, color.green);
223		jas_matrix_setv(pixels[2], x, color.blue);
224		x++;
225	}
226}
227
228
229/*!
230	Make *pixels[1] from gray scanline
231	(just write data to pixels)
232*/
233inline void
234write_gray(jas_matrix_t** pixels, uchar* scanline, int width)
235{
236	int32 x = 0;
237	while (x < width) {
238		jas_matrix_setv(pixels[0], x, scanline[x]);
239		x++;
240	}
241}
242
243
244/*!
245	Make *pixels[3] from RGB15/RGBA15 scanline
246	(just write data to pixels)
247*/
248inline void
249write_rgb15_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
250{
251	int32 x = 0;
252	int32 index = 0;
253	int16 in_pixel;
254	while (x < width) {
255		in_pixel = scanline[index] | (scanline[index+1] << 8);
256		index += 2;
257
258		jas_matrix_setv(pixels[0], x, (char)(((in_pixel & 0x7c00)) >> 7)
259			| (((in_pixel & 0x7c00)) >> 12));
260		jas_matrix_setv(pixels[1], x, (char)(((in_pixel & 0x3e0)) >> 2)
261			| (((in_pixel & 0x3e0)) >> 7));
262		jas_matrix_setv(pixels[2], x, (char)(((in_pixel & 0x1f)) << 3)
263			| (((in_pixel & 0x1f)) >> 2));
264		x++;
265	}
266}
267
268
269/*!
270	Make *pixels[3] from RGB15/RGBA15 bigendian scanline
271	(just write data to pixels)
272*/
273inline void
274write_rgb15b_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
275{
276	int32 x = 0;
277	int32 index = 0;
278	int16 in_pixel;
279	while (x < width) {
280		in_pixel = scanline[index + 1] | (scanline[index] << 8);
281		index += 2;
282
283		jas_matrix_setv(pixels[0], x, (char)(((in_pixel & 0x7c00)) >> 7)
284			| (((in_pixel & 0x7c00)) >> 12));
285		jas_matrix_setv(pixels[1], x, (char)(((in_pixel & 0x3e0)) >> 2)
286			| (((in_pixel & 0x3e0)) >> 7));
287		jas_matrix_setv(pixels[2], x, (char)(((in_pixel & 0x1f)) << 3)
288			| (((in_pixel & 0x1f)) >> 2));
289		x++;
290	}
291}
292
293
294/*!
295	Make *pixels[3] from RGB16/RGBA16 scanline
296	(just write data to pixels)
297*/
298inline void
299write_rgb16_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
300{
301	int32 x = 0;
302	int32 index = 0;
303	int16 in_pixel;
304	while (x < width) {
305		in_pixel = scanline[index] | (scanline[index+1] << 8);
306		index += 2;
307
308		jas_matrix_setv(pixels[0], x, (char)(((in_pixel & 0xf800)) >> 8)
309			| (((in_pixel & 0x7c00)) >> 12));
310		jas_matrix_setv(pixels[1], x, (char)(((in_pixel & 0x7e0)) >> 3)
311			| (((in_pixel & 0x7e0)) >> 9));
312		jas_matrix_setv(pixels[2], x, (char)(((in_pixel & 0x1f)) << 3)
313			| (((in_pixel & 0x1f)) >> 2));
314		x++;
315	}
316}
317
318
319/*!
320	Make *pixels[3] from RGB16/RGBA16 bigendian scanline
321	(just write data to pixels)
322*/
323inline void
324write_rgb16b_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
325{
326	int32 x = 0;
327	int32 index = 0;
328	int16 in_pixel;
329	while (x < width) {
330		in_pixel = scanline[index + 1] | (scanline[index] << 8);
331		index += 2;
332
333		jas_matrix_setv(pixels[0], x, (char)(((in_pixel & 0xf800)) >> 8)
334			| (((in_pixel & 0xf800)) >> 13));
335		jas_matrix_setv(pixels[1], x, (char)(((in_pixel & 0x7e0)) >> 3)
336			| (((in_pixel & 0x7e0)) >> 9));
337		jas_matrix_setv(pixels[2], x, (char)(((in_pixel & 0x1f)) << 3)
338			| (((in_pixel & 0x1f)) >> 2));
339		x++;
340	}
341}
342
343
344/*!
345	Make *pixels[3] from RGB24 scanline
346	(just write data to pixels)
347*/
348inline void
349write_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
350{
351	int32 index = 0;
352	int32 x = 0;
353	while (x < width) {
354		jas_matrix_setv(pixels[2], x, scanline[index++]);
355		jas_matrix_setv(pixels[1], x, scanline[index++]);
356		jas_matrix_setv(pixels[0], x, scanline[index++]);
357		x++;
358	}
359}
360
361
362/*!
363	Make *pixels[3] from RGB24 bigendian scanline
364	(just write data to pixels)
365*/
366inline void
367write_rgb24b(jas_matrix_t** pixels, uchar* scanline, int width)
368{
369	int32 index = 0;
370	int32 x = 0;
371	while (x < width) {
372		jas_matrix_setv(pixels[0], x, scanline[index++]);
373		jas_matrix_setv(pixels[1], x, scanline[index++]);
374		jas_matrix_setv(pixels[2], x, scanline[index++]);
375		x++;
376	}
377}
378
379
380/*!
381	Make *pixels[3] from RGB32 scanline
382	(just write data to pixels)
383*/
384inline void
385write_rgb32_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
386{
387	int32 index = 0;
388	int32 x = 0;
389	while (x < width) {
390		jas_matrix_setv(pixels[2], x, scanline[index++]);
391		jas_matrix_setv(pixels[1], x, scanline[index++]);
392		jas_matrix_setv(pixels[0], x, scanline[index++]);
393		index++;
394		x++;
395	}
396}
397
398
399/*!
400	Make *pixels[3] from RGB32 bigendian scanline
401	(just write data to pixels)
402*/
403inline void
404write_rgb32b_to_rgb24(jas_matrix_t** pixels, uchar* scanline, int width)
405{
406	int32 index = 0;
407	int32 x = 0;
408	while (x < width) {
409		index++;
410		jas_matrix_setv(pixels[0], x, scanline[index++]);
411		jas_matrix_setv(pixels[1], x, scanline[index++]);
412		jas_matrix_setv(pixels[2], x, scanline[index++]);
413		x++;
414	}
415}
416
417
418/*!
419	Make *pixels[4] from RGBA32 scanline
420	(just write data to pixels)
421	!!! UNTESTED !!!
422*/
423inline void
424write_rgba32(jas_matrix_t** pixels, uchar* scanline, int width)
425{
426	int32 index = 0;
427	int32 x = 0;
428	while (x < width) {
429		jas_matrix_setv(pixels[3], x, scanline[index++]);
430		jas_matrix_setv(pixels[2], x, scanline[index++]);
431		jas_matrix_setv(pixels[1], x, scanline[index++]);
432		jas_matrix_setv(pixels[0], x, scanline[index++]);
433		x++;
434	}
435}
436
437
438/*!
439	Make *pixels[4] from RGBA32 bigendian scanline
440	(just write data to pixels)
441	!!! UNTESTED !!!
442*/
443inline void
444write_rgba32b(jas_matrix_t** pixels, uchar* scanline, int width)
445{
446	int32 index = 0;
447	int32 x = 0;
448	while (x < width) {
449		jas_matrix_setv(pixels[0], x, scanline[index++]);
450		jas_matrix_setv(pixels[1], x, scanline[index++]);
451		jas_matrix_setv(pixels[2], x, scanline[index++]);
452		jas_matrix_setv(pixels[3], x, scanline[index++]);
453		x++;
454	}
455}
456
457
458}// end namespace conversion
459
460
461//	#pragma mark -	jasper I/O
462
463
464static int
465Read(jas_stream_obj_t* object, char* buffer, const int length)
466{
467	return (*(BPositionIO**)object)->Read(buffer, length);
468}
469
470
471static int
472Write(jas_stream_obj_t* object, char* buffer, const int length)
473{
474	return (*(BPositionIO**)object)->Write(buffer, length);
475}
476
477
478static long
479Seek(jas_stream_obj_t* object, long offset, int origin)
480{
481	return (*(BPositionIO**)object)->Seek(offset, origin);
482}
483
484
485static int
486Close(jas_stream_obj_t* object)
487{
488	return 0;
489}
490
491
492static jas_stream_ops_t positionIOops = {
493	Read,
494	Write,
495	Seek,
496	Close
497};
498
499
500static jas_stream_t*
501jas_stream_positionIOopen(BPositionIO *positionIO)
502{
503	jas_stream_t* stream;
504
505	stream = (jas_stream_t *)malloc(sizeof(jas_stream_t));
506	if (stream == (jas_stream_t *)NULL)
507		return (jas_stream_t *)NULL;
508
509	memset(stream, 0, sizeof(jas_stream_t));
510	stream->rwlimit_ = -1;
511	stream->obj_=(jas_stream_obj_t *)malloc(sizeof(BPositionIO*));
512	if (stream->obj_ == (jas_stream_obj_t *)NULL) {
513		free(stream);
514		return (jas_stream_t *)NULL;
515	}
516
517	*((BPositionIO**)stream->obj_) = positionIO;
518	stream->ops_ = (&positionIOops);
519	stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
520	stream->bufbase_ = stream->tinybuf_;
521	stream->bufsize_ = 1;
522	stream->bufstart_ = (&stream->bufbase_[JAS_STREAM_MAXPUTBACK]);
523	stream->ptr_ = stream->bufstart_;
524	stream->bufmode_ |= JAS_STREAM_UNBUF & JAS_STREAM_BUFMODEMASK;
525
526	return stream;
527}
528
529
530//	#pragma mark -
531
532
533SSlider::SSlider(const char* name, const char* label,
534		BMessage* message, int32 minValue, int32 maxValue, orientation posture,
535		thumb_style thumbType, uint32 flags)
536	:
537	BSlider(name, label, message, minValue, maxValue,
538		posture, thumbType, flags)
539{
540	rgb_color barColor = { 0, 0, 229, 255 };
541	UseFillColor(true, &barColor);
542}
543
544
545//!	Update status string - show actual value
546const char*
547SSlider::UpdateText() const
548{
549	snprintf(fStatusLabel, sizeof(fStatusLabel), "%" B_PRId32, Value());
550	return fStatusLabel;
551}
552
553
554//	#pragma mark -
555
556
557TranslatorReadView::TranslatorReadView(const char* name,
558	TranslatorSettings* settings)
559	:
560	BView(name, 0, new BGroupLayout(B_VERTICAL)),
561	fSettings(settings)
562{
563	fGrayAsRGB32 = new BCheckBox("grayasrgb32",
564		B_TRANSLATE("Read greyscale images as RGB32"),
565		new BMessage(VIEW_MSG_SET_GRAYASRGB32));
566	if (fSettings->SetGetBool(JP2_SET_GRAY8_AS_B_RGB32))
567		fGrayAsRGB32->SetValue(B_CONTROL_ON);
568
569	float padding = 10.0f;
570	BLayoutBuilder::Group<>(this, B_VERTICAL)
571		.SetInsets(padding)
572		.Add(fGrayAsRGB32)
573		.AddGlue();
574}
575
576
577TranslatorReadView::~TranslatorReadView()
578{
579	fSettings->Release();
580}
581
582
583void
584TranslatorReadView::AttachedToWindow()
585{
586	fGrayAsRGB32->SetTarget(this);
587}
588
589
590void
591TranslatorReadView::MessageReceived(BMessage* message)
592{
593	switch (message->what) {
594		case VIEW_MSG_SET_GRAYASRGB32:
595		{
596			int32 value;
597			if (message->FindInt32("be:value", &value) == B_OK) {
598				bool boolValue = value;
599				fSettings->SetGetBool(JP2_SET_GRAY8_AS_B_RGB32, &boolValue);
600				fSettings->SaveSettings();
601			}
602			break;
603		}
604		default:
605			BView::MessageReceived(message);
606			break;
607	}
608}
609
610
611//	#pragma mark - TranslatorWriteView
612
613
614TranslatorWriteView::TranslatorWriteView(const char* name,
615	TranslatorSettings* settings)
616	:
617	BView(name, 0, new BGroupLayout(B_VERTICAL)),
618	fSettings(settings)
619{
620	fQualitySlider = new SSlider("quality", B_TRANSLATE("Output quality"),
621		new BMessage(VIEW_MSG_SET_QUALITY), 0, 100);
622	fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
623	fQualitySlider->SetHashMarkCount(10);
624	fQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High"));
625	fQualitySlider->SetValue(fSettings->SetGetInt32(JP2_SET_QUALITY));
626
627	fGrayAsRGB24 = new BCheckBox("gray1asrgb24",
628		B_TRANSLATE("Write black-and-white images as RGB24"),
629		new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
630	if (fSettings->SetGetBool(JP2_SET_GRAY1_AS_B_RGB24))
631		fGrayAsRGB24->SetValue(B_CONTROL_ON);
632
633	fCodeStreamOnly = new BCheckBox("codestreamonly",
634		B_TRANSLATE("Output only codestream (.jpc)"),
635		new BMessage(VIEW_MSG_SET_JPC));
636	if (fSettings->SetGetBool(JP2_SET_JPC))
637		fCodeStreamOnly->SetValue(B_CONTROL_ON);
638
639	BLayoutBuilder::Group<>(this, B_VERTICAL)
640		.SetInsets(B_USE_DEFAULT_SPACING)
641		.Add(fQualitySlider)
642		.Add(fGrayAsRGB24)
643		.Add(fCodeStreamOnly)
644		.AddGlue();
645}
646
647
648TranslatorWriteView::~TranslatorWriteView()
649{
650	fSettings->Release();
651}
652
653
654void
655TranslatorWriteView::AttachedToWindow()
656{
657	fQualitySlider->SetTarget(this);
658	fGrayAsRGB24->SetTarget(this);
659	fCodeStreamOnly->SetTarget(this);
660}
661
662
663void
664TranslatorWriteView::MessageReceived(BMessage* message)
665{
666	switch (message->what) {
667		case VIEW_MSG_SET_QUALITY:
668		{
669			int32 value;
670			if (message->FindInt32("be:value", &value) == B_OK) {
671				fSettings->SetGetInt32(JP2_SET_QUALITY, &value);
672				fSettings->SaveSettings();
673			}
674			break;
675		}
676		case VIEW_MSG_SET_GRAY1ASRGB24:
677		{
678			int32 value;
679			if (message->FindInt32("be:value", &value) == B_OK) {
680				bool boolValue = value;
681				fSettings->SetGetBool(JP2_SET_GRAY1_AS_B_RGB24, &boolValue);
682				fSettings->SaveSettings();
683			}
684			break;
685		}
686		case VIEW_MSG_SET_JPC:
687		{
688			int32 value;
689			if (message->FindInt32("be:value", &value) == B_OK) {
690				bool boolValue = value;
691				fSettings->SetGetBool(JP2_SET_JPC, &boolValue);
692				fSettings->SaveSettings();
693			}
694			break;
695		}
696		default:
697			BView::MessageReceived(message);
698			break;
699	}
700}
701
702
703//	#pragma mark -
704
705
706TranslatorAboutView::TranslatorAboutView(const char* name)
707	:
708	BView(name, 0, new BGroupLayout(B_VERTICAL))
709{
710	BAlignment labelAlignment = BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP);
711	BStringView* title = new BStringView("Title", sTranslatorName);
712	title->SetFont(be_bold_font);
713	title->SetExplicitAlignment(labelAlignment);
714
715	char versionString[100];
716	snprintf(versionString, sizeof(versionString),
717		B_TRANSLATE("Version %d.%d.%d"),
718		static_cast<int>(sTranslatorVersion >> 8),
719		static_cast<int>((sTranslatorVersion >> 4) & 0xf),
720		static_cast<int>(sTranslatorVersion & 0xf));
721
722	BStringView* version = new BStringView("Version", versionString);
723	version->SetExplicitAlignment(labelAlignment);
724
725	BTextView* infoView = new BTextView("info");
726	infoView->SetText(sTranslatorInfo);
727	infoView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
728	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
729	infoView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
730	infoView->MakeEditable(false);
731
732	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
733		.SetInsets(B_USE_DEFAULT_SPACING)
734		.Add(title)
735		.Add(version)
736		.Add(infoView);
737}
738
739
740//	#pragma mark -
741
742
743TranslatorView::TranslatorView(const char* name, TranslatorSettings* settings)
744	:
745	BTabView(name, B_WIDTH_FROM_LABEL)
746{
747	SetBorder(B_NO_BORDER);
748
749	AddTab(new TranslatorWriteView(B_TRANSLATE("Write"),
750		settings->Acquire()));
751	AddTab(new TranslatorReadView(B_TRANSLATE("Read"),
752		settings->Acquire()));
753	AddTab(new TranslatorAboutView(B_TRANSLATE("About")));
754
755	settings->Release();
756}
757
758
759//	#pragma mark -
760
761BView*
762JP2Translator::NewConfigView(TranslatorSettings* settings)
763{
764	BView* outView = new TranslatorView("TranslatorView", settings);
765	return outView;
766}
767
768
769JP2Translator::JP2Translator()
770	: BaseTranslator(sTranslatorName, sTranslatorInfo, sTranslatorVersion,
771		sInputFormats, kNumInputFormats,
772		sOutputFormats, kNumOutputFormats,
773		JP2_SETTINGS_FILE,
774		sDefaultSettings, kNumDefaultSettings,
775		B_TRANSLATOR_BITMAP, JP2_FORMAT)
776{
777}
778
779
780//!	Determine whether or not we can handle this data
781status_t
782JP2Translator::DerivedIdentify(BPositionIO* inSource,
783	const translation_format* inFormat, BMessage* ioExtension,
784	translator_info* outInfo, uint32 outType)
785{
786	if ((outType != 0) && (outType != B_TRANSLATOR_BITMAP)
787		&& outType != JP2_FORMAT)
788		return B_NO_TRANSLATOR;
789
790	// !!! You might need to make this buffer bigger to test for your
791	// native format
792	off_t position = inSource->Position();
793	uint8 header[sizeof(TranslatorBitmap)];
794	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
795	inSource->Seek(position, SEEK_SET);
796	if (err < B_OK)
797		return err;
798
799	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic)
800		== B_TRANSLATOR_BITMAP) {
801		if (PopulateInfoFromFormat(outInfo, B_TRANSLATOR_BITMAP) != B_OK)
802			return B_NO_TRANSLATOR;
803	} else {
804		if ((((header[4] << 24) | (header[5] << 16) | (header[6] << 8)
805			| header[7]) == JP2_BOX_JP) // JP2
806			|| (header[0] == (JPC_MS_SOC >> 8) && header[1]
807			== (JPC_MS_SOC & 0xff)))	// JPC
808		{
809			if (PopulateInfoFromFormat(outInfo, JP2_FORMAT) != B_OK)
810				return B_NO_TRANSLATOR;
811		} else
812			return B_NO_TRANSLATOR;
813	}
814
815	return B_OK;
816}
817
818
819status_t
820JP2Translator::DerivedTranslate(BPositionIO* inSource,
821	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
822	BPositionIO* outDestination, int32 baseType)
823{
824	// If no specific type was requested, convert to the interchange format
825	if (outType == 0)
826		outType = B_TRANSLATOR_BITMAP;
827
828	// What action to take, based on the findings of Identify()
829	if (outType == inInfo->type)
830		return Copy(inSource, outDestination);
831	if (inInfo->type == B_TRANSLATOR_BITMAP && outType == JP2_FORMAT)
832		return Compress(inSource, outDestination);
833	if (inInfo->type == JP2_FORMAT && outType == B_TRANSLATOR_BITMAP)
834		return Decompress(inSource, outDestination);
835
836	return B_NO_TRANSLATOR;
837}
838
839
840//!	The user has requested the same format for input and output, so just copy
841status_t
842JP2Translator::Copy(BPositionIO* in, BPositionIO* out)
843{
844	int block_size = 65536;
845	void* buffer = malloc(block_size);
846	char temp[1024];
847	if (buffer == NULL) {
848		buffer = temp;
849		block_size = 1024;
850	}
851	status_t err = B_OK;
852
853	// Read until end of file or error
854	while (1) {
855		ssize_t to_read = block_size;
856		err = in->Read(buffer, to_read);
857		// Explicit check for EOF
858		if (err == -1) {
859			if (buffer != temp)
860				free(buffer);
861			return B_OK;
862		}
863		if (err <= B_OK) break;
864		to_read = err;
865		err = out->Write(buffer, to_read);
866		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
867		if (err < B_OK) break;
868	}
869
870	if (buffer != temp)
871		free(buffer);
872	return (err >= 0) ? B_OK : err;
873}
874
875
876//!	Encode into the native format
877status_t
878JP2Translator::Compress(BPositionIO* in, BPositionIO* out)
879{
880	using namespace conversion;
881
882	// Read info about bitmap
883	TranslatorBitmap header;
884	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
885	if (err < B_OK)
886		return err;
887	if (err < (int)sizeof(TranslatorBitmap))
888		return B_ERROR;
889
890	// Grab dimension, color space, and size information from the stream
891	BRect bounds;
892	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
893	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
894	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
895	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
896
897	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
898
899	int width = bounds.IntegerWidth() + 1;
900	int height = bounds.IntegerHeight() + 1;
901
902	// Function pointer to write function
903	// It MUST point to proper function
904	void (*converter)(jas_matrix_t** pixels, uchar* inscanline,
905		int width) = write_rgba32;
906
907	// Default color info
908	int out_color_space = JAS_CLRSPC_SRGB;
909	int out_color_components = 3;
910
911	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) {
912		case B_GRAY1:
913			if (fSettings->SetGetBool(JP2_SET_GRAY1_AS_B_RGB24)) {
914				converter = write_gray1_to_rgb24;
915			} else {
916				out_color_components = 1;
917				out_color_space = JAS_CLRSPC_SGRAY;
918				converter = write_gray1_to_gray;
919			}
920			break;
921
922		case B_CMAP8:
923			converter = write_cmap8_to_rgb24;
924			break;
925
926		case B_GRAY8:
927			out_color_components = 1;
928			out_color_space = JAS_CLRSPC_SGRAY;
929			converter = write_gray;
930			break;
931
932		case B_RGB15:
933		case B_RGBA15:
934			converter = write_rgb15_to_rgb24;
935			break;
936
937		case B_RGB15_BIG:
938		case B_RGBA15_BIG:
939			converter = write_rgb15b_to_rgb24;
940			break;
941
942		case B_RGB16:
943			converter = write_rgb16_to_rgb24;
944			break;
945
946		case B_RGB16_BIG:
947			converter = write_rgb16b_to_rgb24;
948			break;
949
950		case B_RGB24:
951			converter = write_rgb24;
952			break;
953
954		case B_RGB24_BIG:
955			converter = write_rgb24b;
956			break;
957
958		case B_RGB32:
959			converter = write_rgb32_to_rgb24;
960			break;
961
962		case B_RGB32_BIG:
963			converter = write_rgb32b_to_rgb24;
964			break;
965
966		case B_RGBA32:
967		/*
968			// In theory it should be possible to write 4 color components
969			// to jp2, so it should be possible to have transparency.
970			// Unfortunetly libjasper does not agree with that
971			// For now i don't know how to modify it :(
972
973			out_color_components = 4;
974			converter = write_rgba32;
975		*/
976			converter = write_rgb32_to_rgb24;
977			break;
978
979		case B_RGBA32_BIG:
980		/*
981			// In theory it should be possible to write 4 color components
982			// to jp2, so it should be possible to have transparency.
983			// Unfortunetly libjasper does not agree with that
984			// For now i don't know how to modify it :(
985
986			out_color_components = 4;
987			converter = write_rgba32b;
988		*/
989			converter = write_rgb32b_to_rgb24;
990			break;
991
992		default:
993			syslog(LOG_ERR, "Unknown color space.\n");
994			return B_ERROR;
995	}
996
997	jas_image_t* image;
998	jas_stream_t* outs;
999	jas_matrix_t* pixels[4];
1000	jas_image_cmptparm_t component_info[4];
1001
1002	if (jas_init())
1003		return B_ERROR;
1004
1005	if (!(outs = jas_stream_positionIOopen(out)))
1006		return B_ERROR;
1007
1008	int32 i = 0;
1009	for (i = 0; i < (long)out_color_components; i++) {
1010		(void) memset(component_info + i, 0, sizeof(jas_image_cmptparm_t));
1011		component_info[i].hstep = 1;
1012		component_info[i].vstep = 1;
1013		component_info[i].width = (unsigned int)width;
1014		component_info[i].height = (unsigned int)height;
1015		component_info[i].prec = (unsigned int)8;
1016	}
1017
1018	image = jas_image_create((short)out_color_components, component_info,
1019		out_color_space);
1020	if (image == (jas_image_t *)NULL)
1021		return Error(outs, NULL, NULL, 0, NULL, B_ERROR);
1022
1023	uchar *in_scanline = (uchar*) malloc(in_row_bytes);
1024	if (in_scanline == NULL)
1025		return Error(outs, image, NULL, 0, NULL, B_ERROR);
1026
1027	for (i = 0; i < (long)out_color_components; i++) {
1028		pixels[i] = jas_matrix_create(1, (unsigned int)width);
1029		if (pixels[i] == (jas_matrix_t *)NULL)
1030			return Error(outs, image, pixels, i+1, in_scanline, B_ERROR);
1031	}
1032
1033	int32 y = 0;
1034	for (y = 0; y < (long)height; y++) {
1035		err = in->Read(in_scanline, in_row_bytes);
1036		if (err < in_row_bytes) {
1037			return (err < B_OK) ? Error(outs, image, pixels,
1038					out_color_components, in_scanline, err)
1039				: Error(outs, image, pixels, out_color_components, in_scanline,
1040					B_ERROR);
1041		}
1042
1043		converter(pixels, in_scanline, width);
1044
1045		for (i = 0; i < (long)out_color_components; i++) {
1046			(void)jas_image_writecmpt(image, (short)i, 0, (unsigned int)y,
1047				(unsigned int)width, 1, pixels[i]);
1048		}
1049	}
1050
1051	char opts[16];
1052	sprintf(opts, "rate=%1f",
1053		(float)fSettings->SetGetInt32(JP2_SET_QUALITY) / 100.0);
1054
1055	if (jas_image_encode(image, outs, jas_image_strtofmt(
1056			fSettings->SetGetBool(JP2_SET_JPC) ?
1057				(char*)"jpc" : (char*)"jp2"), opts)) {
1058		return Error(outs, image, pixels,
1059			out_color_components, in_scanline, err);
1060	}
1061
1062	free(in_scanline);
1063
1064	for (i = 0; i < (long)out_color_components; i++)
1065		jas_matrix_destroy(pixels[i]);
1066
1067	jas_stream_close(outs);
1068	jas_image_destroy(image);
1069	jas_image_clearfmts();
1070
1071	return B_OK;
1072}
1073
1074
1075//!	Decode the native format
1076status_t
1077JP2Translator::Decompress(BPositionIO* in, BPositionIO* out)
1078{
1079	using namespace conversion;
1080
1081	jas_image_t* image;
1082	jas_stream_t* ins;
1083	jas_matrix_t* pixels[4];
1084
1085	if (jas_init())
1086		return B_ERROR;
1087
1088	if (!(ins = jas_stream_positionIOopen(in)))
1089		return B_ERROR;
1090
1091	if (!(image = jas_image_decode(ins, -1, 0)))
1092		return Error(ins, NULL, NULL, 0, NULL, B_ERROR);
1093
1094	// Default color info
1095	color_space out_color_space;
1096	int out_color_components;
1097	int	in_color_components = jas_image_numcmpts(image);
1098
1099	// Function pointer to read function
1100	// It MUST point to proper function
1101	void (*converter)(jas_matrix_t** pixels, uchar* outscanline,
1102		int width) = NULL;
1103
1104	switch (jas_clrspc_fam(jas_image_clrspc(image))) {
1105		case JAS_CLRSPC_FAM_RGB:
1106			out_color_components = 4;
1107			if (in_color_components == 3) {
1108				out_color_space = B_RGB32;
1109				converter = read_rgb24_to_rgb32;
1110			} else if (in_color_components == 4) {
1111				out_color_space = B_RGBA32;
1112				converter = read_rgba32;
1113			} else {
1114				syslog(LOG_ERR, "Other than RGB with 3 or 4 color "
1115					"components not implemented.\n");
1116				return Error(ins, image, NULL, 0, NULL, B_ERROR);
1117			}
1118			break;
1119		case JAS_CLRSPC_FAM_GRAY:
1120			if (fSettings->SetGetBool(JP2_SET_GRAY8_AS_B_RGB32)) {
1121				out_color_space = B_RGB32;
1122				out_color_components = 4;
1123				converter = read_gray_to_rgb32;
1124			} else {
1125				out_color_space = B_GRAY8;
1126				out_color_components = 1;
1127				converter = read_gray;
1128			}
1129			break;
1130		case JAS_CLRSPC_FAM_YCBCR:
1131			syslog(LOG_ERR, "Color space YCBCR not implemented yet.\n");
1132			return Error(ins, image, NULL, 0, NULL, B_ERROR);
1133			break;
1134		case JAS_CLRSPC_UNKNOWN:
1135		default:
1136			syslog(LOG_ERR, "Color space unknown. \n");
1137			return Error(ins, image, NULL, 0, NULL, B_ERROR);
1138			break;
1139	}
1140
1141	float width = (float)jas_image_width(image);
1142	float height = (float)jas_image_height(image);
1143
1144	// Bytes count in one line of image (scanline)
1145	int64 out_row_bytes = (int32)width * out_color_components;
1146		// NOTE: things will go wrong if "out_row_bytes" wouldn't fit into 32 bits
1147
1148	// !!! Initialize this bounds rect to the size of your image
1149	BRect bounds(0, 0, width - 1, height - 1);
1150
1151
1152	// Fill out the B_TRANSLATOR_BITMAP's header
1153	TranslatorBitmap header;
1154	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1155	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1156	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1157	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1158	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1159	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(out_color_space);
1160	header.rowBytes = B_HOST_TO_BENDIAN_INT32(out_row_bytes);
1161	header.dataSize = B_HOST_TO_BENDIAN_INT32((int32)(out_row_bytes * height));
1162
1163	// Write out the header
1164	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1165	if (err < B_OK)
1166		return Error(ins, image, NULL, 0, NULL, err);
1167	if (err < (int)sizeof(TranslatorBitmap))
1168		return Error(ins, image, NULL, 0, NULL, B_ERROR);
1169
1170	uchar *out_scanline = (uchar*) malloc(out_row_bytes);
1171	if (out_scanline == NULL)
1172		return Error(ins, image, NULL, 0, NULL, B_ERROR);
1173
1174	int32 i = 0;
1175	for (i = 0; i < (long)in_color_components; i++) {
1176		pixels[i] = jas_matrix_create(1, (unsigned int)width);
1177		if (pixels[i] == (jas_matrix_t *)NULL)
1178			return Error(ins, image, pixels, i + 1, out_scanline, B_ERROR);
1179	}
1180
1181	int32 y = 0;
1182	for (y = 0; y < (long)height; y++) {
1183		for (i = 0; i < (long)in_color_components; i++) {
1184			(void)jas_image_readcmpt(image, (short)i, 0, (unsigned int)y,
1185				(unsigned int)width, 1, pixels[i]);
1186		}
1187
1188		converter(pixels, out_scanline, (int32)width);
1189
1190		err = out->Write(out_scanline, out_row_bytes);
1191		if (err < out_row_bytes) {
1192			return (err < B_OK) ? Error(ins, image, pixels, in_color_components,
1193				out_scanline, err)
1194				: Error(ins, image, pixels, in_color_components, out_scanline,
1195					B_ERROR);
1196		}
1197	}
1198
1199	free(out_scanline);
1200
1201	for (i = 0; i < (long)in_color_components; i++)
1202		jas_matrix_destroy(pixels[i]);
1203
1204	jas_stream_close(ins);
1205	jas_image_destroy(image);
1206	jas_image_clearfmts();
1207
1208	return B_OK;
1209}
1210
1211
1212/*! searches in both inputFormats & outputFormats */
1213status_t
1214JP2Translator::PopulateInfoFromFormat(translator_info* info,
1215	uint32 formatType, translator_id id)
1216{
1217	int32 formatCount;
1218	const translation_format* formats = OutputFormats(&formatCount);
1219
1220	for (int i = 0; i <= 1; formats = InputFormats(&formatCount), i++) {
1221		if (PopulateInfoFromFormat(info, formatType,
1222			formats, formatCount) == B_OK) {
1223			info->translator = id;
1224			return B_OK;
1225		}
1226	}
1227
1228	return B_ERROR;
1229}
1230
1231
1232status_t
1233JP2Translator::PopulateInfoFromFormat(translator_info* info,
1234	uint32 formatType, const translation_format* formats, int32 formatCount)
1235{
1236	for (int i = 0; i < formatCount; i++) {
1237		if (formats[i].type == formatType) {
1238			info->type = formatType;
1239			info->group = formats[i].group;
1240			info->quality = formats[i].quality;
1241			info->capability = formats[i].capability;
1242			if (strcmp(formats[i].name, B_TRANSLATOR_BITMAP_DESCRIPTION)
1243				== 0) {
1244				strlcpy(info->name,
1245					B_TRANSLATE(B_TRANSLATOR_BITMAP_DESCRIPTION),
1246					sizeof(info->name));
1247			} else {
1248				strlcpy(info->name, formats[i].name, sizeof(info->name));
1249			}
1250			strlcpy(info->MIME, formats[i].MIME, sizeof(info->MIME));
1251			return B_OK;
1252		}
1253	}
1254
1255	return B_ERROR;
1256}
1257
1258
1259/*!
1260	Frees jpeg alocated memory
1261	Returns given error (B_ERROR by default)
1262*/
1263status_t
1264Error(jas_stream_t* stream, jas_image_t* image, jas_matrix_t** pixels,
1265	int32 pixels_count, uchar* scanline, status_t error)
1266{
1267	if (pixels) {
1268		int32 i;
1269		for (i = 0; i < (long)pixels_count; i++) {
1270			if (pixels[i] != NULL)
1271				jas_matrix_destroy(pixels[i]);
1272		}
1273	}
1274	if (stream)
1275		jas_stream_close(stream);
1276	if (image)
1277		jas_image_destroy(image);
1278
1279	jas_image_clearfmts();
1280	free(scanline);
1281
1282	return error;
1283}
1284
1285
1286//	#pragma mark -
1287
1288BTranslator*
1289make_nth_translator(int32 n, image_id you, uint32 flags, ...)
1290{
1291	if (!n)
1292		return new JP2Translator();
1293
1294	return NULL;
1295}
1296
1297
1298int
1299main()
1300{
1301	BApplication app("application/x-vnd.Haiku-JPEG2000Translator");
1302	JP2Translator* translator = new JP2Translator();
1303	if (LaunchTranslatorWindow(translator, sTranslatorName) == B_OK)
1304		app.Run();
1305
1306	return 0;
1307}
1308
1309