xref: /haiku/src/add-ons/translators/jpeg2000/JPEG2000Translator.cpp (revision cbe35e2031cb2bfb757422f35006bb9bd382bed1)
1 /*
2 
3 Copyright (c) 2003, Marcin 'Shard' Konicki
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9     * Redistributions of source code must retain the above copyright notice,
10       this list of conditions and the following disclaimer.
11     * Redistributions in binary form must reproduce the above copyright notice,
12       this list of conditions and the following disclaimer in the documentation and/or
13       other materials provided with the distribution.
14     * Name "Marcin Konicki", "Shard" or any combination of them,
15       must not be used to endorse or promote products derived from this
16       software without specific prior written permission from Marcin Konicki.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 */
31 
32 //----------------------------------------------------------------------------
33 //
34 //	Include
35 //
36 //----------------------------------------------------------------------------
37 
38 #include "JPEG2000Translator.h"
39 #include "jp2_cod.h"
40 #include "jpc_cs.h"
41 
42 //----------------------------------------------------------------------------
43 //
44 //	Global variables initialization
45 //
46 //----------------------------------------------------------------------------
47 
48 // Set these accordingly
49 #define JP2_ACRONYM "JP2"
50 #define JP2_FORMAT 'JP2 '
51 #define JP2_MIME_STRING "image/jp2"
52 #define JP2_DESCRIPTION "JPEG2000 image"
53 
54 // The translation kit's native file type
55 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap"
56 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap image"
57 
58 // Translation Kit required globals
59 char translatorName[] = "JPEG2000 translator";
60 char translatorInfo[] = "© 2002-2003, Shard
61 
62 Based on JasPer library:
63 © 1999-2000, Image Power, Inc. and
64 the University of British Columbia, Canada.
65 © 2001-2003 Michael David Adams.
66           http://www.ece.uvic.ca/~mdadams/jasper/
67 
68 ImageMagick's jp2 codec was used as \"tutorial\".
69           http://www.imagemagick.org/
70 ";
71 int32 translatorVersion = 256;	// 256 = v1.0.0
72 
73 // Define the formats we know how to read
74 translation_format inputFormats[] = {
75 	{ JP2_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
76 		JP2_MIME_STRING, JP2_DESCRIPTION },
77 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
78 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
79 	{ 0, 0, 0, 0, 0, 0 },
80 };
81 
82 // Define the formats we know how to write
83 translation_format outputFormats[] = {
84 	{ JP2_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
85 		JP2_MIME_STRING, JP2_DESCRIPTION },
86 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
87 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
88 	{ 0, 0, 0, 0, 0, 0 },
89 };
90 
91 bool AreSettingsRunning = false;
92 
93 //----------------------------------------------------------------------------
94 //
95 //	Functions :: jas_stream for BPositionIO
96 //
97 //----------------------------------------------------------------------------
98 
99 static int Read(jas_stream_obj_t *object,char *buffer,const int length)
100 {
101 	return (*(BPositionIO**) object)->Read(buffer, length);
102 }
103 
104 static int Write(jas_stream_obj_t *object,char *buffer,const int length)
105 {
106 	return (*(BPositionIO**) object)->Write(buffer, length);
107 }
108 
109 static long Seek(jas_stream_obj_t *object,long offset,int origin)
110 {
111   return (*(BPositionIO**) object)->Seek(offset, origin);
112 }
113 
114 static int Close(jas_stream_obj_t *object)
115 {
116   return(0);
117 }
118 
119 static jas_stream_ops_t
120 positionIOops =
121 {
122 	Read,
123 	Write,
124 	Seek,
125 	Close
126 };
127 
128 static jas_stream_t *jas_stream_positionIOopen(BPositionIO *positionIO)
129 {
130 	jas_stream_t *stream;
131 
132 	stream=(jas_stream_t *) malloc( sizeof(jas_stream_t));
133 	if (stream == (jas_stream_t *) NULL)
134 		return((jas_stream_t *) NULL);
135 	(void) memset(stream, 0, sizeof(jas_stream_t));
136 	stream->rwlimit_=(-1);
137 	stream->obj_=(jas_stream_obj_t *) malloc( sizeof(BPositionIO*));
138 	if (stream->obj_ == (jas_stream_obj_t *) NULL)
139 		return((jas_stream_t *) NULL);
140 	*((BPositionIO**)stream->obj_) = positionIO;
141 	stream->ops_=(&positionIOops);
142 	stream->openmode_=JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
143 	stream->bufbase_=stream->tinybuf_;
144 	stream->bufsize_=1;
145 	stream->bufstart_=(&stream->bufbase_[JAS_STREAM_MAXPUTBACK]);
146 	stream->ptr_=stream->bufstart_;
147 	stream->bufmode_|=JAS_STREAM_UNBUF & JAS_STREAM_BUFMODEMASK;
148 	return(stream);
149 }
150 
151 //----------------------------------------------------------------------------
152 //
153 //	Functions :: SSlider
154 //
155 //----------------------------------------------------------------------------
156 
157 //---------------------------------------------------
158 //	Constructor
159 //---------------------------------------------------
160 SSlider::SSlider(BRect frame, const char *name, const char *label, BMessage *message, int32 minValue, int32 maxValue, orientation posture, thumb_style thumbType, uint32 resizingMode, uint32 flags)
161 :	BSlider(frame, name, label, message, minValue, maxValue, posture, thumbType, resizingMode, flags)
162 {
163 	rgb_color bar_color = { 0, 0, 229, 255 };
164 	UseFillColor(true, &bar_color);
165 }
166 
167 //---------------------------------------------------
168 //	Update status string - show actual value
169 //---------------------------------------------------
170 char*
171 SSlider::UpdateText() const
172 {
173 	sprintf( (char*)statusLabel, "%ld", Value());
174 	return (char*)statusLabel;
175 }
176 
177 //---------------------------------------------------
178 //	BSlider::ResizeToPreferred + Resize width if it's too small to show label and status
179 //---------------------------------------------------
180 void
181 SSlider::ResizeToPreferred()
182 {
183 	int32 width = (int32)ceil(StringWidth( Label()) + StringWidth("9999"));
184 	if (width < 230) width = 230;
185 	float w, h;
186 	GetPreferredSize(&w, &h);
187 	ResizeTo(width, h);
188 }
189 
190 
191 //----------------------------------------------------------------------------
192 //
193 //	Functions :: TranslatorReadView
194 //
195 //----------------------------------------------------------------------------
196 
197 //---------------------------------------------------
198 //	Constructor
199 //---------------------------------------------------
200 TranslatorReadView::TranslatorReadView(const char *name, SETTINGS *settings, float x, float y)
201 :	SView(name, x, y),
202 	Settings(settings)
203 {
204 	grayasrgb32 = new BCheckBox( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_GRAYASRGB32, new BMessage(VIEW_MSG_SET_GRAYASRGB32));
205 	grayasrgb32->SetFont(be_plain_font);
206 	if (Settings->B_GRAY8_as_B_RGB32)
207 		grayasrgb32->SetValue(1);
208 
209 	AddChild(grayasrgb32);
210 
211 	ResizeToPreferred();
212 }
213 
214 //---------------------------------------------------
215 //	Attached to window - set children target
216 //---------------------------------------------------
217 void
218 TranslatorReadView::AttachedToWindow()
219 {
220 	grayasrgb32->SetTarget(this);
221 }
222 
223 //---------------------------------------------------
224 //	MessageReceived - receive GUI changes, save settings
225 //---------------------------------------------------
226 void
227 TranslatorReadView::MessageReceived(BMessage *message)
228 {
229 	switch (message->what)
230 	{
231 		case VIEW_MSG_SET_GRAYASRGB32:
232 		{
233 			int32 value;
234 			if (message->FindInt32("be:value", &value) == B_OK) {
235 				Settings->B_GRAY8_as_B_RGB32 = value;
236 				SaveSettings(Settings);
237 			}
238 			break;
239 		}
240 		default:
241 			BView::MessageReceived(message);
242 			break;
243 	}
244 }
245 
246 
247 //----------------------------------------------------------------------------
248 //
249 //	Functions :: TranslatorWriteView
250 //
251 //----------------------------------------------------------------------------
252 
253 //---------------------------------------------------
254 //	Constructor
255 //---------------------------------------------------
256 TranslatorWriteView::TranslatorWriteView(const char *name, SETTINGS *settings, float x, float y)
257 :	SView(name, x, y),
258 	Settings(settings)
259 {
260 	quality = new SSlider( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "quality", VIEW_LABEL_QUALITY, new BMessage(VIEW_MSG_SET_QUALITY), 1, 100);
261 	quality->SetHashMarks(B_HASH_MARKS_BOTTOM);
262 	quality->SetHashMarkCount(10);
263 	quality->SetLimitLabels("Low", "High");
264 	quality->SetFont(be_plain_font);
265 	quality->SetValue(Settings->Quality);
266 
267 	AddChild(quality);
268 
269 	gray1asrgb24 = new BCheckBox( BRect(10, GetPreferredHeight()+10, 10, GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_GRAY1ASRGB24, new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
270 	gray1asrgb24->SetFont(be_plain_font);
271 	if (Settings->B_GRAY1_as_B_RGB24)
272 		gray1asrgb24->SetValue(1);
273 
274 	AddChild(gray1asrgb24);
275 
276 	jpc = new BCheckBox( BRect(10, GetPreferredHeight()+5, 10, GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_JPC, new BMessage(VIEW_MSG_SET_JPC));
277 	jpc->SetFont(be_plain_font);
278 	if (Settings->JPC)
279 		jpc->SetValue(1);
280 
281 	AddChild(jpc);
282 
283 	ResizeToPreferred();
284 }
285 
286 //---------------------------------------------------
287 //	Attached to window - set children target
288 //---------------------------------------------------
289 void
290 TranslatorWriteView::AttachedToWindow()
291 {
292 	quality->SetTarget(this);
293 	gray1asrgb24->SetTarget(this);
294 	jpc->SetTarget(this);
295 }
296 
297 //---------------------------------------------------
298 //	MessageReceived - receive GUI changes, save settings
299 //---------------------------------------------------
300 void
301 TranslatorWriteView::MessageReceived(BMessage *message)
302 {
303 	switch (message->what)
304 	{
305 		case VIEW_MSG_SET_QUALITY:
306 		{
307 			int32 value;
308 			if (message->FindInt32("be:value", &value) == B_OK) {
309 				Settings->Quality = value;
310 				SaveSettings(Settings);
311 			}
312 			break;
313 		}
314 		case VIEW_MSG_SET_GRAY1ASRGB24:
315 		{
316 			int32 value;
317 			if (message->FindInt32("be:value", &value) == B_OK) {
318 				Settings->B_GRAY1_as_B_RGB24 = value;
319 				SaveSettings(Settings);
320 			}
321 			break;
322 		}
323 		case VIEW_MSG_SET_JPC:
324 		{
325 			int32 value;
326 			if (message->FindInt32("be:value", &value) == B_OK) {
327 				Settings->JPC = value;
328 				SaveSettings(Settings);
329 			}
330 			break;
331 		}
332 		default:
333 			BView::MessageReceived(message);
334 			break;
335 	}
336 }
337 
338 
339 //----------------------------------------------------------------------------
340 //
341 //	Functions :: TranslatorAboutView
342 //
343 //----------------------------------------------------------------------------
344 
345 //---------------------------------------------------
346 //	Constructor
347 //---------------------------------------------------
348 TranslatorAboutView::TranslatorAboutView(const char *name, float x, float y)
349 :	SView(name, x, y)
350 {
351 	BStringView *title = new BStringView( BRect(10, 0, 10, 0), "Title", translatorName);
352 	title->SetFont(be_bold_font);
353 
354 	AddChild(title);
355 
356 	BRect rect = title->Bounds();
357 	float space = title->StringWidth("    ");
358 
359 	char versionString[16];
360 	sprintf(versionString, "v%d.%d.%d", (int)(translatorVersion >> 8), (int)((translatorVersion >> 4) & 0xf), (int)(translatorVersion & 0xf));
361 
362 	BStringView *version = new BStringView( BRect(rect.right+space, rect.top, rect.right+space, rect.top), "Version", versionString);
363 	version->SetFont(be_plain_font);
364 	version->SetFontSize( 9);
365 	// Make version be in the same line as title
366 	version->ResizeToPreferred();
367 	version->MoveBy(0, rect.bottom-version->Frame().bottom);
368 
369 	AddChild(version);
370 
371 	// Now for each line in translatorInfo add BStringView
372 	BStringView *copyright;
373 	const char *current = translatorInfo;
374 	char *temp = translatorInfo;
375 	while (*current != 0) {
376 		// Find next line char
377 		temp = strchr(current, 0x0a);
378 		// If found replace it with 0 so current will look like ending here
379 		if (temp)
380 			*temp = 0;
381 		// Add BStringView showing what's under current
382 		copyright = new BStringView( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "Copyright", current);
383 		copyright->SetFont(be_plain_font);
384 		copyright->SetFontSize( 9);
385 		AddChild(copyright);
386 
387 		// If there was next line, move current there and put next line char back
388 		if (temp) {
389 			current = temp+1;
390 			*temp = 0x0a;
391 		} else
392 		// If there was no next line char break loop
393 			break;
394 	}
395 
396 	ResizeToPreferred();
397 }
398 
399 
400 //----------------------------------------------------------------------------
401 //
402 //	Functions :: TranslatorView
403 //
404 //----------------------------------------------------------------------------
405 
406 //---------------------------------------------------
407 //	Constructor
408 //---------------------------------------------------
409 TranslatorView::TranslatorView(const char *name)
410 	: SView(name),
411 	  tabWidth(30),
412 	  tabHeight((int32)ceilf(7 + be_plain_font->Size())),
413 	  activeChild(0)
414 {
415 	// Set global var to true
416 	AreSettingsRunning = true;
417 
418 	// Without this strings are not correctly aliased
419 	// THX to Jack Burton for info :)
420 	SetLowColor( ViewColor());
421 
422 	// Load settings to global Settings struct
423 	LoadSettings(&Settings);
424 
425 	// Add left and top margins
426 	float top = tabHeight+15;
427 	float left = 0;
428 
429 	// This will remember longest string width
430 	float nameWidth = 0;
431 
432 	SView *view = new TranslatorWriteView("Write", &Settings, left, top);
433 	AddChild(view);
434 	nameWidth = StringWidth(view->Name());
435 
436 	view = new TranslatorReadView("Read", &Settings, left, top);
437 	AddChild(view);
438 	if (nameWidth < StringWidth(view->Name()))
439 		nameWidth = StringWidth(view->Name());
440 
441 	view = new TranslatorAboutView("About", left, top);
442 	AddChild(view);
443 	if (nameWidth < StringWidth(view->Name()))
444 		nameWidth = StringWidth(view->Name());
445 
446 	tabWidth += (int32)ceilf(nameWidth);
447 	if (tabWidth * CountChildren() > GetPreferredWidth())
448 		ResizePreferredBy((tabWidth * CountChildren()) - GetPreferredWidth(), 0);
449 
450 	// Add right and bottom margins
451 	ResizePreferredBy(10, 15);
452 
453 	ResizeToPreferred();
454 
455 	// Make TranslatorView resize itself with parent
456 	SetFlags( Flags() | B_FOLLOW_ALL);
457 }
458 
459 //---------------------------------------------------
460 //	Attached to window - resize parent to preferred
461 //---------------------------------------------------
462 void
463 TranslatorView::AttachedToWindow()
464 {
465 	// Hide all children except first one
466 	BView *child = NULL;
467 	int32 index = 1;
468 	while ((child = ChildAt(index++)))
469 		child->Hide();
470 
471 	// Hack for DataTranslations which doesn't resize visible area to requested by view
472 	// which makes some parts of bigger than usual translationviews out of visible area
473 	// so if it was loaded to DataTranslations resize window if needed
474 	BWindow *window = Window();
475 	if (!strcmp(window->Name(), "DataTranslations")) {
476 		BView *view = Parent();
477 		if (view) {
478 			BRect frame = view->Frame();
479 			if (frame.Width() < GetPreferredWidth() || (frame.Height()-48) < GetPreferredHeight()) {
480 				float x = ceil(GetPreferredWidth() - frame.Width());
481 				float y = ceil(GetPreferredHeight() - (frame.Height()-48));
482 				if (x < 0) x = 0;
483 				if (y < 0) y = 0;
484 
485 				// DataTranslations has main view called "Background"
486 				// change it's resizing mode so it will always resize with window
487 				// also make sure view will be redrawed after resize
488 				view = window->FindView("Background");
489 				if (view) {
490 					view->SetResizingMode(B_FOLLOW_ALL);
491 					view->SetFlags(B_FULL_UPDATE_ON_RESIZE);
492 				}
493 
494 				// The same with "Info..." button, except redrawing, which isn't needed
495 				view = window->FindView("Info…");
496 				if (view)
497 					view->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
498 
499 				window->ResizeBy( x, y);
500 
501 				// Let user resize window if resizing option is not already there...
502 				uint32 flags = window->Flags();
503 				if (flags & B_NOT_RESIZABLE) {
504 					// ...but first prevent too small window (so "Info..." button will not look strange ;)
505 					// max will be 800x600 which should be enough for now
506 					window->SetSizeLimits(400, 800, 66, 600);
507 
508 					flags ^= B_NOT_RESIZABLE;
509 					window->SetFlags(flags);
510 				}
511 			}
512 		}
513 	}
514 }
515 
516 //---------------------------------------------------
517 //	DrawTabs
518 //---------------------------------------------------
519 void
520 TranslatorView::Draw(BRect updateRect)
521 {
522 	// This is needed because DataTranslations app hides children
523 	// after user changes translator
524 	if (ChildAt(activeChild)->IsHidden())
525 		ChildAt(activeChild)->Show();
526 
527 	// Prepare colors used for drawing "tabs"
528 	rgb_color dark_line_color = tint_color( ViewColor(), B_DARKEN_2_TINT);
529 	rgb_color darkest_line_color = tint_color( ViewColor(), B_DARKEN_3_TINT);
530 	rgb_color light_line_color = tint_color( ViewColor(), B_LIGHTEN_MAX_TINT);
531 	rgb_color text_color = ui_color(B_MENU_ITEM_TEXT_COLOR);
532 
533 	int32 index = 0;
534 	BView *child = NULL;
535 	float left = 0;
536 
537 	// Clear
538 	SetHighColor( ViewColor());
539 	FillRect( BRect(0, 0, Frame().right, tabHeight));
540 
541 	while ((child = ChildAt(index))) {
542 		// Draw outline
543 		SetHighColor(dark_line_color);
544 		StrokeLine( BPoint(left, 10), BPoint(left, tabHeight));
545 		StrokeArc( BPoint(left+10, 10), 10, 10, 90, 90);
546 		StrokeLine( BPoint(left+10, 0), BPoint(left+tabWidth-10, 0));
547 		StrokeArc( BPoint(left+tabWidth-10, 10), 9, 10, 0, 90);
548 		StrokeLine( BPoint(left+tabWidth-1, 10), BPoint(left+tabWidth-1, tabHeight));
549 		// Draw "shadow" on the right side
550 		SetHighColor(darkest_line_color);
551 		StrokeArc( BPoint(left+tabWidth-10, 10), 10, 10, 0, 50);
552 		StrokeLine( BPoint(left+tabWidth, 10), BPoint(left+tabWidth, tabHeight-1));
553 		// Draw label
554 		SetHighColor(text_color);
555 		DrawString( child->Name(), BPoint(left+(tabWidth/2)-(StringWidth(child->Name())/2), 3+be_plain_font->Size()));
556 		// Draw "light" on left and top side
557 		SetHighColor(light_line_color);
558 		StrokeArc( BPoint(left+10, 10), 9, 9, 90, 90);
559 		StrokeLine( BPoint(left+1, 10), BPoint(left+1, tabHeight));
560 		StrokeLine( BPoint(left+10, 1), BPoint(left+tabWidth-8, 1));
561 		// Draw bottom edge
562 		if (activeChild != index)
563 			StrokeLine( BPoint(left-2,tabHeight), BPoint(left+tabWidth,tabHeight));
564 		else
565 			StrokeLine( BPoint(left-2,tabHeight), BPoint(left+1,tabHeight));
566 
567 		left += tabWidth+2;
568 		index++;
569 	}
570 	// Draw bottom edge to the rigth side
571 	StrokeLine( BPoint(left-2,tabHeight), BPoint(Bounds().Width(),tabHeight));
572 }
573 
574 //---------------------------------------------------
575 //	MouseDown, check if on tab, if so change tab if needed
576 //---------------------------------------------------
577 void
578 TranslatorView::MouseDown(BPoint where)
579 {
580 	// If user clicked on tabs part of view
581 	if (where.y <= tabHeight)
582 		// If there is a tab (not whole width is occupied by tabs)
583 		if (where.x < tabWidth*CountChildren()) {
584 			// Which tab was selected?
585 			int32 index = (int32)(where.x / tabWidth);
586 			if (activeChild != index) {
587 				// Hide current visible child
588 				ChildAt(activeChild)->Hide();
589 				// This loop is needed because it looks like in DataTranslations
590 				// view gets hidden more than one time when user changes translator
591 				while (ChildAt(index)->IsHidden())
592 					ChildAt(index)->Show();
593 				// Remember which one is currently visible
594 				activeChild = index;
595 				// Redraw
596 				Draw( Frame());
597 			}
598 		}
599 }
600 
601 
602 //----------------------------------------------------------------------------
603 //
604 //	Functions :: TranslatorWindow
605 //
606 //----------------------------------------------------------------------------
607 
608 //---------------------------------------------------
609 //	Constructor
610 //---------------------------------------------------
611 TranslatorWindow::TranslatorWindow(bool quit_on_close)
612 :	BWindow(BRect(100, 100, 100, 100), "JPEG2000 Settings", B_TITLED_WINDOW, B_NOT_ZOOMABLE)
613 {
614 	BRect extent(0, 0, 0, 0);
615 	BView *config = NULL;
616 	MakeConfig(NULL, &config, &extent);
617 
618 	AddChild(config);
619 	ResizeTo(extent.Width(), extent.Height());
620 
621 	// Make application quit after this window close
622 	if (quit_on_close)
623 		SetFlags(Flags() | B_QUIT_ON_WINDOW_CLOSE);
624 }
625 
626 
627 //----------------------------------------------------------------------------
628 //
629 //	Functions :: main
630 //
631 //----------------------------------------------------------------------------
632 
633 //---------------------------------------------------
634 //	main function
635 //---------------------------------------------------
636 int
637 main() {
638 	BApplication app("application/x-vnd.Shard.JPEG2000Translator");
639 
640 	TranslatorWindow *window = new TranslatorWindow();
641 	window->Show();
642 
643 	app.Run();
644 	return 0;
645 }
646 
647 //---------------------------------------------------
648 //	Hook to create and return our configuration view
649 //---------------------------------------------------
650 status_t
651 MakeConfig(BMessage *ioExtension, BView **outView, BRect *outExtent)
652 {
653 	*outView = new TranslatorView("TranslatorView");
654 	*outExtent = (*outView)->Frame();
655 	return B_OK;
656 }
657 
658 //---------------------------------------------------
659 //	Determine whether or not we can handle this data
660 //---------------------------------------------------
661 status_t
662 Identify(BPositionIO *inSource, const translation_format *inFormat, BMessage *ioExtension, translator_info *outInfo, uint32 outType)
663 {
664 
665 	if ((outType != 0) && (outType != B_TRANSLATOR_BITMAP) && outType != JP2_FORMAT)
666 		return B_NO_TRANSLATOR;
667 
668 	// !!! You might need to make this buffer bigger to test for your native format
669 	off_t position = inSource->Position();
670 	uint8 header[sizeof(TranslatorBitmap)];
671 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
672 	inSource->Seek( position, SEEK_SET);
673 	if (err < B_OK) return err;
674 
675 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
676 		outInfo->type = inputFormats[1].type;
677 		outInfo->translator = 0;
678 		outInfo->group = inputFormats[1].group;
679 		outInfo->quality = inputFormats[1].quality;
680 		outInfo->capability = inputFormats[1].capability;
681 		strcpy(outInfo->name, inputFormats[1].name);
682 		strcpy(outInfo->MIME, inputFormats[1].MIME);
683 	} else {
684 		if ((((header[4] << 24) | (header[5] << 16) | (header[6] << 8) | header[7]) == JP2_BOX_JP) ||	// JP2
685 			(header[0] == (JPC_MS_SOC >> 8) && header[1] == (JPC_MS_SOC & 0xff)))	// JPC
686 		{
687 			outInfo->type = inputFormats[0].type;
688 			outInfo->translator = 0;
689 			outInfo->group = inputFormats[0].group;
690 			outInfo->quality = inputFormats[0].quality;
691 			outInfo->capability = inputFormats[0].capability;
692 			strcpy(outInfo->name, inputFormats[0].name);
693 			strcpy(outInfo->MIME, inputFormats[0].MIME);
694 			return B_OK;
695 		} else
696 			return B_NO_TRANSLATOR;
697 	}
698 
699 	return B_OK;
700 }
701 
702 //---------------------------------------------------
703 //	Arguably the most important method in the add-on
704 //---------------------------------------------------
705 status_t
706 Translate(BPositionIO *inSource, const translator_info *inInfo, BMessage *ioExtension, uint32 outType, BPositionIO *outDestination)
707 {
708 	// If no specific type was requested, convert to the interchange format
709 	if (outType == 0) outType = B_TRANSLATOR_BITMAP;
710 
711 	// What action to take, based on the findings of Identify()
712 	if (outType == inInfo->type) {
713 		return Copy(inSource, outDestination);
714 	} else if (inInfo->type == B_TRANSLATOR_BITMAP && outType == JP2_FORMAT) {
715 		return Compress(inSource, outDestination);
716 	} else if (inInfo->type == JP2_FORMAT && outType == B_TRANSLATOR_BITMAP) {
717 		return Decompress(inSource, outDestination);
718 	}
719 
720 	return B_NO_TRANSLATOR;
721 }
722 
723 //---------------------------------------------------
724 //	The user has requested the same format for input and output, so just copy
725 //---------------------------------------------------
726 status_t
727 Copy(BPositionIO *in, BPositionIO *out)
728 {
729 	int block_size = 65536;
730 	void *buffer = malloc(block_size);
731 	char temp[1024];
732 	if (buffer == NULL) {
733 		buffer = temp;
734 		block_size = 1024;
735 	}
736 	status_t err = B_OK;
737 
738 	// Read until end of file or error
739 	while (1) {
740 		ssize_t to_read = block_size;
741 		err = in->Read(buffer, to_read);
742 		// Explicit check for EOF
743 		if (err == -1) {
744 			if (buffer != temp) free(buffer);
745 			return B_OK;
746 		}
747 		if (err <= B_OK) break;
748 		to_read = err;
749 		err = out->Write(buffer, to_read);
750 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
751 		if (err < B_OK) break;
752 	}
753 
754 	if (buffer != temp) free(buffer);
755 	return (err >= 0) ? B_OK : err;
756 }
757 
758 //---------------------------------------------------
759 //	Encode into the native format
760 //---------------------------------------------------
761 status_t
762 Compress(BPositionIO *in, BPositionIO *out)
763 {
764 	// Load Settings
765 	SETTINGS Settings;
766 	LoadSettings(&Settings);
767 
768 	// Read info about bitmap
769 	TranslatorBitmap header;
770 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
771 	if (err < B_OK) return err;
772 	else if (err < (int)sizeof(TranslatorBitmap)) return B_ERROR;
773 
774 	// Grab dimension, color space, and size information from the stream
775 	BRect bounds;
776 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
777 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
778 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
779 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
780 
781 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
782 
783 	int width = bounds.IntegerWidth() + 1;
784 	int height = bounds.IntegerHeight() + 1;
785 
786 	// Function pointer to write function
787 	// It MUST point to proper function
788 	void (*converter)(jas_matrix_t **pixels, jpr_uchar_t *inscanline, int width) = write_rgba32;
789 
790 	// Default color info
791 	int out_color_space = JAS_IMAGE_CS_RGB;
792 	int out_color_components = 3;
793 
794 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors))
795 	{
796 		case B_GRAY1:
797 		{
798 			if (Settings.B_GRAY1_as_B_RGB24) {
799 				converter = write_gray1_to_rgb24;
800 			} else {
801 				out_color_components = 1;
802 				out_color_space = JAS_IMAGE_CS_GRAY;
803 				converter = write_gray1_to_gray;
804 			}
805 			break;
806 		}
807 		case B_CMAP8:
808 		{
809 			converter = write_cmap8_to_rgb24;
810 			break;
811 		}
812 		case B_GRAY8:
813 		{
814 			out_color_components = 1;
815 			out_color_space = JAS_IMAGE_CS_GRAY;
816 			converter = write_gray;
817 			break;
818 		}
819 		case B_RGB15:
820 		case B_RGBA15:
821 		{
822 			converter = write_rgb15_to_rgb24;
823 			break;
824 		}
825 		case B_RGB15_BIG:
826 		case B_RGBA15_BIG:
827 		{
828 			converter = write_rgb15b_to_rgb24;
829 			break;
830 		}
831 		case B_RGB16:
832 		{
833 			converter = write_rgb16_to_rgb24;
834 			break;
835 		}
836 		case B_RGB16_BIG:
837 		{
838 			converter = write_rgb16b_to_rgb24;
839 			break;
840 		}
841 		case B_RGB24:
842 		{
843 			converter = write_rgb24;
844 			break;
845 		}
846 		case B_RGB24_BIG:
847 		{
848 			converter = write_rgb24b;
849 			break;
850 		}
851 		case B_RGB32:
852 		{
853 			converter = write_rgb32_to_rgb24;
854 			break;
855 		}
856 		case B_RGB32_BIG:
857 		{
858 			converter = write_rgb32b_to_rgb24;
859 			break;
860 		}
861 		case B_RGBA32:
862 		{
863 		/*
864 			// In theory it should be possible to write 4 color components
865 			// to jp2, so it should be possible to have transparency.
866 			// Unfortunetly libjasper does not agree with that
867 			// For now i don't know how to modify it :(
868 
869 			out_color_components = 4;
870 			converter = write_rgba32;
871 		*/
872 			converter = write_rgb32_to_rgb24;
873 			break;
874 		}
875 		case B_RGBA32_BIG:
876 		{
877 		/*
878 			// In theory it should be possible to write 4 color components
879 			// to jp2, so it should be possible to have transparency.
880 			// Unfortunetly libjasper does not agree with that
881 			// For now i don't know how to modify it :(
882 
883 			out_color_components = 4;
884 			converter = write_rgba32b;
885 		*/
886 			converter = write_rgb32b_to_rgb24;
887 			break;
888 		}
889 		default:
890 		{
891 			(new BAlert("Error", "Unknown color space.", "Quit"))->Go();
892 			return B_ERROR;
893 		}
894 	}
895 
896 	jas_image_t *image;
897 	jas_stream_t *outs;
898 	jas_matrix_t *pixels[4];
899 	jas_image_cmptparm_t component_info[4];
900 
901 	if (jas_init())
902 		return B_ERROR;
903 
904 	if (!(outs = jas_stream_positionIOopen(out)))
905 		return B_ERROR;
906 
907 	int32 i = 0;
908 	for (i = 0; i < (long)out_color_components; i++)
909 	{
910 		(void) memset(component_info + i, 0, sizeof(jas_image_cmptparm_t));
911 		component_info[i].hstep = 1;
912 		component_info[i].vstep = 1;
913 		component_info[i].width = (unsigned int)width;
914 		component_info[i].height = (unsigned int)height;
915 		component_info[i].prec = (unsigned int)8;
916 	}
917 
918 	image = jas_image_create( (short)out_color_components, component_info, out_color_space);
919 	if (image == (jas_image_t *)NULL)
920 		return Error(outs, NULL, NULL, 0, NULL, B_ERROR);
921 
922 	jpr_uchar_t *in_scanline = (jpr_uchar_t*) malloc(in_row_bytes);
923 	if (in_scanline == NULL) return Error(outs, image, NULL, 0, NULL, B_ERROR);
924 
925 	for (i = 0; i < (long)out_color_components; i++)
926 	{
927 		pixels[i] = jas_matrix_create(1, (unsigned int)width);
928 		if (pixels[i] == (jas_matrix_t *)NULL)
929 			return Error(outs, image, pixels, i+1, in_scanline, B_ERROR);
930 	}
931 
932 	int32 y = 0;
933 	for (y = 0; y < (long)height; y++)
934 	{
935 		err = in->Read(in_scanline, in_row_bytes);
936 		if (err < in_row_bytes)
937 			return (err < B_OK) ? Error(outs, image, pixels, out_color_components, in_scanline, err) : Error(outs, image, pixels, out_color_components, in_scanline, B_ERROR);
938 
939 		converter(pixels, in_scanline, width);
940 
941 		for (i = 0; i < (long)out_color_components; i++)
942 			(void)jas_image_writecmpt(image, (short)i, 0, (unsigned int)y, (unsigned int)width, 1, pixels[i]);
943 	}
944 
945 	char opts[16];
946 	sprintf(opts, "rate=%1f", (float)Settings.Quality / 100.0);
947 	if ( jas_image_encode(image, outs, jas_image_strtofmt(Settings.JPC ? (char*)"jpc" : (char*)"jp2"), opts))
948 		return Error(outs, image, pixels, out_color_components, in_scanline, err);
949 
950 	free(in_scanline);
951 
952 	for (i = 0; i < (long)out_color_components; i++)
953 		jas_matrix_destroy(pixels[i]);
954 	jas_stream_close(outs);
955 	jas_image_destroy(image);
956 	jas_image_clearfmts();
957 
958 	return B_OK;
959 }
960 
961 //---------------------------------------------------
962 //	Decode the native format
963 //---------------------------------------------------
964 status_t
965 Decompress(BPositionIO *in, BPositionIO *out)
966 {
967 	SETTINGS Settings;
968 	LoadSettings(&Settings);
969 
970 	jas_image_t *image;
971 	jas_stream_t *ins;
972 	jas_matrix_t *pixels[4];
973 
974 	if (jas_init())
975 		return B_ERROR;
976 
977 	if (!(ins = jas_stream_positionIOopen(in)))
978 		return B_ERROR;
979 
980 	if (!(image = jas_image_decode(ins, -1, 0)))
981 		return Error(ins, NULL, NULL, 0, NULL, B_ERROR);
982 
983 	// Default color info
984 	color_space out_color_space;
985 	int out_color_components;
986 	int	in_color_components = jas_image_numcmpts(image);
987 
988 	// Function pointer to read function
989 	// It MUST point to proper function
990 	void (*converter)(jas_matrix_t **pixels, jpr_uchar_t *outscanline, int width) = NULL;
991 
992 	switch( jas_image_colorspace(image))
993 	{
994 		case JAS_IMAGE_CS_RGB:
995 			out_color_components = 4;
996 			if (in_color_components == 3) {
997 				out_color_space = B_RGB32;
998 				converter = read_rgb24_to_rgb32;
999 			} else if (in_color_components == 4) {
1000 				out_color_space = B_RGBA32;
1001 				converter = read_rgba32;
1002 			} else {
1003 				(new BAlert("Error", "Other than RGB with 3 or 4 color components not implemented.", "Quit"))->Go();
1004 				return Error(ins, image, NULL, 0, NULL, B_ERROR);
1005 			}
1006 			break;
1007 		case JAS_IMAGE_CS_GRAY:
1008 			if (Settings.B_GRAY8_as_B_RGB32) {
1009 				out_color_space = B_RGB32;
1010 				out_color_components = 4;
1011 				converter = read_gray_to_rgb32;
1012 			} else {
1013 				out_color_space = B_GRAY8;
1014 				out_color_components = 1;
1015 				converter = read_gray;
1016 			}
1017 			break;
1018 		case JAS_IMAGE_CS_YCBCR:
1019 			(new BAlert("Error", "color space YCBCR not implemented yet.", "Quit"))->Go();
1020 			return Error(ins, image, NULL, 0, NULL, B_ERROR);
1021 			break;
1022 		case JAS_IMAGE_CS_UNKNOWN:
1023 		default:
1024 			(new BAlert("Error", "color space unknown.", "Quit"))->Go();
1025 			return Error(ins, image, NULL, 0, NULL, B_ERROR);
1026 			break;
1027 	}
1028 
1029 	float width = (float)jas_image_width(image);
1030 	float height = (float)jas_image_height(image);
1031 
1032 	// Bytes count in one line of image (scanline)
1033 	int64 out_row_bytes = (int32)width * out_color_components;
1034 		// NOTE: things will go wrong if "out_row_bytes" wouldn't fit into 32 bits
1035 
1036 	// !!! Initialize this bounds rect to the size of your image
1037 	BRect bounds( 0, 0, width-1, height-1);
1038 
1039 	// Fill out the B_TRANSLATOR_BITMAP's header
1040 	TranslatorBitmap header;
1041 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1042 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1043 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1044 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1045 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1046 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(out_color_space);
1047 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(out_row_bytes);
1048 	header.dataSize = B_HOST_TO_BENDIAN_INT32((int32)(out_row_bytes * height));
1049 
1050 	// Write out the header
1051 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1052 	if (err < B_OK)	return Error(ins, image, NULL, 0, NULL, err);
1053 	else if (err < (int)sizeof(TranslatorBitmap)) return Error(ins, image, NULL, 0, NULL, B_ERROR);
1054 
1055 	jpr_uchar_t *out_scanline = (jpr_uchar_t*) malloc(out_row_bytes);
1056 	if (out_scanline == NULL) return Error(ins, image, NULL, 0, NULL, B_ERROR);
1057 
1058 	int32 i = 0;
1059 	for (i = 0; i < (long)in_color_components; i++)
1060 	{
1061 		pixels[i] = jas_matrix_create(1, (unsigned int)width);
1062 		if (pixels[i] == (jas_matrix_t *)NULL)
1063 			return Error(ins, image, pixels, i+1, out_scanline, B_ERROR);
1064 	}
1065 
1066 	int32 y = 0;
1067 	for (y = 0; y < (long)height; y++)
1068 	{
1069 		for (i = 0; i < (long)in_color_components; i++)
1070 			(void)jas_image_readcmpt(image, (short)i, 0, (unsigned int)y, (unsigned int)width, 1, pixels[i]);
1071 
1072 		converter(pixels, out_scanline, (int32)width);
1073 
1074 		err = out->Write(out_scanline, out_row_bytes);
1075 		if (err < out_row_bytes)
1076 			return (err < B_OK) ? Error(ins, image, pixels, in_color_components, out_scanline, err) : Error(ins, image, pixels, in_color_components, out_scanline, B_ERROR);
1077 	}
1078 
1079 	free(out_scanline);
1080 
1081 	for (i = 0; i < (long)in_color_components; i++)
1082 		jas_matrix_destroy(pixels[i]);
1083 	jas_stream_close(ins);
1084 	jas_image_destroy(image);
1085 	jas_image_clearfmts();
1086 
1087 	return B_OK;
1088 }
1089 
1090 //---------------------------------------------------
1091 //	Frees jpeg alocated memory
1092 //	Returns given error (B_ERROR by default)
1093 //---------------------------------------------------
1094 status_t
1095 Error(jas_stream_t *stream, jas_image_t *image, jas_matrix_t **pixels, int32 pixels_count, jpr_uchar_t *scanline, status_t error)
1096 {
1097 	if (pixels)
1098 	{
1099 		int32 i;
1100 		for (i = 0; i < (long)pixels_count; i++)
1101 			if (pixels[i] != NULL)
1102 				jas_matrix_destroy(pixels[i]);
1103 	}
1104 	if (stream)
1105 		jas_stream_close(stream);
1106 	if (image)
1107 		jas_image_destroy(image);
1108 	jas_image_clearfmts();
1109 	if (scanline)
1110 		free(scanline);
1111 
1112 	return error;
1113 }
1114