xref: /haiku/src/add-ons/translators/jpeg/JPEGTranslator.cpp (revision 5412911f7f8ca41340b0f5cb928ed9726322ab44)
1 /*
2 
3 Copyright (c) 2002-2003, Marcin 'Shard' Konicki
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9     * Redistributions of source code must retain the above copyright notice,
10       this list of conditions and the following disclaimer.
11     * Redistributions in binary form must reproduce the above copyright notice,
12       this list of conditions and the following disclaimer in the documentation and/or
13       other materials provided with the distribution.
14     * Name "Marcin Konicki", "Shard" or any combination of them,
15       must not be used to endorse or promote products derived from this
16       software without specific prior written permission from Marcin Konicki.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 */
31 
32 //----------------------------------------------------------------------------
33 //
34 //	Include
35 //
36 //----------------------------------------------------------------------------
37 
38 #include "JPEGTranslator.h"
39 
40 
41 //----------------------------------------------------------------------------
42 //
43 //	Global variables initialization
44 //
45 //----------------------------------------------------------------------------
46 
47 // Set these accordingly
48 #define JPEG_ACRONYM "JPEG"
49 #define JPEG_FORMAT 'JPEG'
50 #define JPEG_MIME_STRING "image/jpeg"
51 #define JPEG_DESCRIPTION "JPEG image"
52 
53 // The translation kit's native file type
54 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap"
55 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap image"
56 
57 // Translation Kit required globals
58 char translatorName[] = "JPEG translator";
59 char translatorInfo[] = "© 2002-2003, Shard\n"
60 "\n"
61 "Based on IJG library © 1991-1998, Thomas G. Lane\"\n"
62 "          http://www.ijg.org/files/\n"
63 "with \"Lossless\" encoding support patch by Ken Murchison\n"
64 "          http://www.oceana.com/ftp/ljpeg/\n"
65 "\n"
66 "With some colorspace conversion routines by Magnus Hellman\n"
67 "          http://www.bebits.com/app/802\n"
68 "";
69 int32 translatorVersion = 273;	// 256 = v1.0.0
70 
71 // Define the formats we know how to read
72 translation_format inputFormats[] = {
73 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
74 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
75 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
76 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
77 	{ 0, 0, 0, 0, 0, 0 },
78 };
79 
80 // Define the formats we know how to write
81 translation_format outputFormats[] = {
82 	{ JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5,
83 		JPEG_MIME_STRING, JPEG_DESCRIPTION },
84 	{ B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5,
85 		B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION },
86 	{ 0, 0, 0, 0, 0, 0 },
87 };
88 
89 bool AreSettingsRunning = false;
90 
91 
92 //----------------------------------------------------------------------------
93 //
94 //	Functions :: SSlider
95 //
96 //----------------------------------------------------------------------------
97 
98 //---------------------------------------------------
99 //	Constructor
100 //---------------------------------------------------
101 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)
102 :	BSlider(frame, name, label, message, minValue, maxValue, posture, thumbType, resizingMode, flags)
103 {
104 	rgb_color bar_color = { 0, 0, 229, 255 };
105 	UseFillColor(true, &bar_color);
106 }
107 
108 //---------------------------------------------------
109 //	Update status string - show actual value
110 //---------------------------------------------------
111 char*
112 SSlider::UpdateText() const
113 {
114 	sprintf( (char*)statusLabel, "%ld", Value());
115 	return (char*)statusLabel;
116 }
117 
118 //---------------------------------------------------
119 //	BSlider::ResizeToPreferred + Resize width if it's too small to show label and status
120 //---------------------------------------------------
121 void
122 SSlider::ResizeToPreferred()
123 {
124 	int32 width = (int32)ceil(StringWidth( Label()) + StringWidth("9999"));
125 	if (width < 230) width = 230;
126 	float w, h;
127 	GetPreferredSize(&w, &h);
128 	ResizeTo(width, h);
129 }
130 
131 
132 //----------------------------------------------------------------------------
133 //
134 //	Functions :: TranslatorReadView
135 //
136 //----------------------------------------------------------------------------
137 
138 //---------------------------------------------------
139 //	Constructor
140 //---------------------------------------------------
141 TranslatorReadView::TranslatorReadView(const char *name, SETTINGS *settings, float x, float y)
142 :	SView(name, x, y),
143 	Settings(settings)
144 {
145 	alwaysrgb32 = new BCheckBox( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_ALWAYSRGB32, new BMessage(VIEW_MSG_SET_ALWAYSRGB32));
146 	alwaysrgb32->SetFont(be_plain_font);
147 	if (Settings->Always_B_RGB32)
148 		alwaysrgb32->SetValue(1);
149 
150 	AddChild(alwaysrgb32);
151 
152 	photoshopCMYK = new BCheckBox( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "photoshopCMYK", VIEW_LABEL_PHOTOSHOPCMYK, new BMessage(VIEW_MSG_SET_PHOTOSHOPCMYK));
153 	photoshopCMYK->SetFont(be_plain_font);
154 	if (Settings->PhotoshopCMYK)
155 		photoshopCMYK->SetValue(1);
156 
157 	AddChild(photoshopCMYK);
158 
159 	showerrorbox = new BCheckBox( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "progress", VIEW_LABEL_SHOWREADERRORBOX, new BMessage(VIEW_MSG_SET_SHOWREADERRORBOX));
160 	showerrorbox->SetFont(be_plain_font);
161 	if (Settings->ShowReadWarningBox)
162 		showerrorbox->SetValue(1);
163 
164 	AddChild(showerrorbox);
165 
166 	ResizeToPreferred();
167 }
168 
169 //---------------------------------------------------
170 //	Attached to window - set children target
171 //---------------------------------------------------
172 void
173 TranslatorReadView::AttachedToWindow()
174 {
175 	alwaysrgb32->SetTarget(this);
176 	photoshopCMYK->SetTarget(this);
177 	showerrorbox->SetTarget(this);
178 }
179 
180 //---------------------------------------------------
181 //	MessageReceived - receive GUI changes, save settings
182 //---------------------------------------------------
183 void
184 TranslatorReadView::MessageReceived(BMessage *message)
185 {
186 	switch (message->what)
187 	{
188 		case VIEW_MSG_SET_ALWAYSRGB32:
189 		{
190 			int32 value;
191 			if (message->FindInt32("be:value", &value) == B_OK) {
192 				Settings->Always_B_RGB32 = value;
193 				SaveSettings(Settings);
194 			}
195 			break;
196 		}
197 		case VIEW_MSG_SET_PHOTOSHOPCMYK:
198 		{
199 			int32 value;
200 			if (message->FindInt32("be:value", &value) == B_OK) {
201 				Settings->PhotoshopCMYK = value;
202 				SaveSettings(Settings);
203 			}
204 			break;
205 		}
206 		case VIEW_MSG_SET_SHOWREADERRORBOX:
207 		{
208 			int32 value;
209 			if (message->FindInt32("be:value", &value) == B_OK) {
210 				Settings->ShowReadWarningBox = value;
211 				SaveSettings(Settings);
212 			}
213 			break;
214 		}
215 		default:
216 			BView::MessageReceived(message);
217 			break;
218 	}
219 }
220 
221 
222 //----------------------------------------------------------------------------
223 //
224 //	Functions :: TranslatorWriteView
225 //
226 //----------------------------------------------------------------------------
227 
228 //---------------------------------------------------
229 //	Constructor
230 //---------------------------------------------------
231 TranslatorWriteView::TranslatorWriteView(const char *name, SETTINGS *settings, float x, float y)
232 :	SView(name, x, y),
233 	Settings(settings)
234 {
235 	quality = new SSlider( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "quality", VIEW_LABEL_QUALITY, new BMessage(VIEW_MSG_SET_QUALITY), 0, 100);
236 	quality->SetHashMarks(B_HASH_MARKS_BOTTOM);
237 	quality->SetHashMarkCount(10);
238 	quality->SetLimitLabels("Low", "High");
239 	quality->SetFont(be_plain_font);
240 	quality->SetValue(Settings->Quality);
241 
242 	AddChild(quality);
243 
244 	smoothing = new SSlider( BRect(10, GetPreferredHeight()+10, 10, GetPreferredHeight()), "smoothing", VIEW_LABEL_SMOOTHING, new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100);
245 	smoothing->SetHashMarks(B_HASH_MARKS_BOTTOM);
246 	smoothing->SetHashMarkCount(10);
247 	smoothing->SetLimitLabels("None", "High");
248 	smoothing->SetFont(be_plain_font);
249 	smoothing->SetValue(Settings->Smoothing);
250 
251 	AddChild(smoothing);
252 
253 	progress = new BCheckBox( BRect(10, GetPreferredHeight()+10, 10, GetPreferredHeight()), "progress", VIEW_LABEL_PROGRESSIVE, new BMessage(VIEW_MSG_SET_PROGRESSIVE));
254 	progress->SetFont(be_plain_font);
255 	if (Settings->Progressive)
256 		progress->SetValue(1);
257 
258 	AddChild(progress);
259 
260 	optimizecolors = new BCheckBox( BRect(10, GetPreferredHeight()+5, 10, GetPreferredHeight()+5), "optimizecolors", VIEW_LABEL_OPTIMIZECOLORS, new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS));
261 	optimizecolors->SetFont(be_plain_font);
262 	if (Settings->OptimizeColors)
263 		optimizecolors->SetValue(1);
264 
265 	AddChild(optimizecolors);
266 
267 	smallerfile = new BCheckBox( BRect(25, GetPreferredHeight()+5, 25, GetPreferredHeight()+5), "smallerfile", VIEW_LABEL_SMALLERFILE, new BMessage(VIEW_MSG_SET_SMALLERFILE));
268 	smallerfile->SetFont(be_plain_font);
269 	if (Settings->SmallerFile)
270 		smallerfile->SetValue(1);
271 	if (!Settings->OptimizeColors)
272 		smallerfile->SetEnabled(false);
273 
274 	AddChild(smallerfile);
275 
276 	gray1asrgb24 = new BCheckBox( BRect(10, GetPreferredHeight()+5, 25, GetPreferredHeight()+5), "gray1asrgb24", VIEW_LABEL_GRAY1ASRGB24, new BMessage(VIEW_MSG_SET_GRAY1ASRGB24));
277 	gray1asrgb24->SetFont(be_plain_font);
278 	if (Settings->B_GRAY1_as_B_RGB24)
279 		gray1asrgb24->SetValue(1);
280 
281 	AddChild(gray1asrgb24);
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 	smoothing->SetTarget(this);
294 	progress->SetTarget(this);
295 	optimizecolors->SetTarget(this);
296 	smallerfile->SetTarget(this);
297 	gray1asrgb24->SetTarget(this);
298 }
299 
300 //---------------------------------------------------
301 //	MessageReceived - receive GUI changes, save settings
302 //---------------------------------------------------
303 void
304 TranslatorWriteView::MessageReceived(BMessage *message)
305 {
306 	switch (message->what)
307 	{
308 		case VIEW_MSG_SET_QUALITY:
309 		{
310 			int32 value;
311 			if (message->FindInt32("be:value", &value) == B_OK) {
312 				Settings->Quality = value;
313 				SaveSettings(Settings);
314 			}
315 			break;
316 		}
317 		case VIEW_MSG_SET_SMOOTHING:
318 		{
319 			int32 value;
320 			if (message->FindInt32("be:value", &value) == B_OK) {
321 				Settings->Smoothing = value;
322 				SaveSettings(Settings);
323 			}
324 			break;
325 		}
326 		case VIEW_MSG_SET_PROGRESSIVE:
327 		{
328 			int32 value;
329 			if (message->FindInt32("be:value", &value) == B_OK) {
330 				Settings->Progressive = value;
331 				SaveSettings(Settings);
332 			}
333 			break;
334 		}
335 		case VIEW_MSG_SET_OPTIMIZECOLORS:
336 		{
337 			int32 value;
338 			if (message->FindInt32("be:value", &value) == B_OK) {
339 				Settings->OptimizeColors = value;
340 				SaveSettings(Settings);
341 			}
342 			smallerfile->SetEnabled(Settings->OptimizeColors);
343 			break;
344 		}
345 		case VIEW_MSG_SET_SMALLERFILE:
346 		{
347 			int32 value;
348 			if (message->FindInt32("be:value", &value) == B_OK) {
349 				Settings->SmallerFile = value;
350 				SaveSettings(Settings);
351 			}
352 			break;
353 		}
354 		case VIEW_MSG_SET_GRAY1ASRGB24:
355 		{
356 			int32 value;
357 			if (message->FindInt32("be:value", &value) == B_OK) {
358 				Settings->B_GRAY1_as_B_RGB24 = value;
359 				SaveSettings(Settings);
360 			}
361 			break;
362 		}
363 		default:
364 			BView::MessageReceived(message);
365 			break;
366 	}
367 }
368 
369 
370 //----------------------------------------------------------------------------
371 //
372 //	Functions :: TranslatorAboutView
373 //
374 //----------------------------------------------------------------------------
375 
376 //---------------------------------------------------
377 //	Constructor
378 //---------------------------------------------------
379 TranslatorAboutView::TranslatorAboutView(const char *name, float x, float y)
380 :	SView(name, x, y)
381 {
382 	BStringView *title = new BStringView( BRect(10, 0, 10, 0), "Title", translatorName);
383 	title->SetFont(be_bold_font);
384 
385 	AddChild(title);
386 
387 	BRect rect = title->Bounds();
388 	float space = title->StringWidth("    ");
389 
390 	char versionString[16];
391 	sprintf(versionString, "v%d.%d.%d", (int)(translatorVersion >> 8), (int)((translatorVersion >> 4) & 0xf), (int)(translatorVersion & 0xf));
392 
393 	BStringView *version = new BStringView( BRect(rect.right+space, rect.top, rect.right+space, rect.top), "Version", versionString);
394 	version->SetFont(be_plain_font);
395 	version->SetFontSize( 9);
396 	// Make version be in the same line as title
397 	version->ResizeToPreferred();
398 	version->MoveBy(0, rect.bottom-version->Frame().bottom);
399 
400 	AddChild(version);
401 
402 	// Now for each line in translatorInfo add BStringView
403 	BStringView *copyright;
404 	const char *current = translatorInfo;
405 	char *temp = translatorInfo;
406 	while (*current != 0) {
407 		// Find next line char
408 		temp = strchr(current, 0x0a);
409 		// If found replace it with 0 so current will look like ending here
410 		if (temp)
411 			*temp = 0;
412 		// Add BStringView showing what's under current
413 		copyright = new BStringView( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "Copyright", current);
414 		copyright->SetFont(be_plain_font);
415 		copyright->SetFontSize( 9);
416 		AddChild(copyright);
417 
418 		// If there was next line, move current there and put next line char back
419 		if (temp) {
420 			current = temp+1;
421 			*temp = 0x0a;
422 		} else
423 		// If there was no next line char break loop
424 			break;
425 	}
426 
427 	ResizeToPreferred();
428 }
429 
430 
431 //----------------------------------------------------------------------------
432 //
433 //	Functions :: TranslatorView
434 //
435 //----------------------------------------------------------------------------
436 
437 //---------------------------------------------------
438 //	Constructor
439 //---------------------------------------------------
440 TranslatorView::TranslatorView(const char *name)
441 :	SView(name),tabWidth(30), tabHeight(7+be_plain_font->Size()), activeChild(0)
442 {
443 	// Set global var to true
444 	AreSettingsRunning = true;
445 
446 	// Without this strings are not correctly aliased
447 	// THX to Jack Burton for info :)
448 	SetLowColor( ViewColor());
449 
450 	// Load settings to global Settings struct
451 	LoadSettings(&Settings);
452 
453 	// Add left and top margins
454 	float top = tabHeight+15;
455 	float left = 0;
456 
457 	// This will remember longest string width
458 	int32 nameWidth = 0;
459 
460 	SView *view = new TranslatorWriteView("Write", &Settings, left, top);
461 	AddChild(view);
462 	nameWidth = StringWidth(view->Name());
463 
464 	view = new TranslatorReadView("Read", &Settings, left, top);
465 	AddChild(view);
466 	if (nameWidth < StringWidth(view->Name()))
467 		nameWidth = StringWidth(view->Name());
468 
469 	view = new TranslatorAboutView("About", left, top);
470 	AddChild(view);
471 	if (nameWidth < StringWidth(view->Name()))
472 		nameWidth = StringWidth(view->Name());
473 
474 	tabWidth += nameWidth;
475 	if (tabWidth * CountChildren() > GetPreferredWidth())
476 		ResizePreferredBy((tabWidth * CountChildren()) - GetPreferredWidth(), 0);
477 
478 	// Add right and bottom margins
479 	ResizePreferredBy(10, 15);
480 
481 	ResizeToPreferred();
482 
483 	// Make TranslatorView resize itself with parent
484 	SetFlags( Flags() | B_FOLLOW_ALL);
485 }
486 
487 //---------------------------------------------------
488 //	Attached to window - resize parent to preferred
489 //---------------------------------------------------
490 void
491 TranslatorView::AttachedToWindow()
492 {
493 	// Hide all children except first one
494 	BView *child = NULL;
495 	int32 index = 1;
496 	while (child = ChildAt(index++))
497 		child->Hide();
498 
499 	// Hack for DataTranslations which doesn't resize visible area to requested by view
500 	// which makes some parts of bigger than usual translationviews out of visible area
501 	// so if it was loaded to DataTranslations resize window if needed
502 	BWindow *window = Window();
503 	if (!strcmp(window->Name(), "DataTranslations")) {
504 		BView *view = Parent();
505 		if (view) {
506 			BRect frame = view->Frame();
507 			if (frame.Width() < GetPreferredWidth() || (frame.Height()-48) < GetPreferredHeight()) {
508 				float x = ceil(GetPreferredWidth() - frame.Width());
509 				float y = ceil(GetPreferredHeight() - (frame.Height()-48));
510 				if (x < 0) x = 0;
511 				if (y < 0) y = 0;
512 
513 				// DataTranslations has main view called "Background"
514 				// change it's resizing mode so it will always resize with window
515 				// also make sure view will be redrawed after resize
516 				view = window->FindView("Background");
517 				if (view) {
518 					view->SetResizingMode(B_FOLLOW_ALL);
519 					view->SetFlags(B_FULL_UPDATE_ON_RESIZE);
520 				}
521 
522 				// The same with "Info..." button, except redrawing, which isn't needed
523 				view = window->FindView("Info…");
524 				if (view)
525 					view->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
526 
527 				window->ResizeBy( x, y);
528 
529 				// Let user resize window if resizing option is not already there...
530 				uint32 flags = window->Flags();
531 				if (flags & B_NOT_RESIZABLE) {
532 					// ...but first prevent too small window (so "Info..." button will not look strange ;)
533 					// max will be 800x600 which should be enough for now
534 					window->SetSizeLimits(400, 800, 66, 600);
535 
536 					flags ^= B_NOT_RESIZABLE;
537 					window->SetFlags(flags);
538 				}
539 			}
540 		}
541 	}
542 }
543 
544 //---------------------------------------------------
545 //	DrawTabs
546 //---------------------------------------------------
547 void
548 TranslatorView::Draw(BRect updateRect)
549 {
550 	// This is needed because DataTranslations app hides children
551 	// after user changes translator
552 	if (ChildAt(activeChild)->IsHidden())
553 		ChildAt(activeChild)->Show();
554 
555 	// Prepare colors used for drawing "tabs"
556 	rgb_color dark_line_color = tint_color( ViewColor(), B_DARKEN_2_TINT);
557 	rgb_color darkest_line_color = tint_color( ViewColor(), B_DARKEN_3_TINT);
558 	rgb_color light_line_color = tint_color( ViewColor(), B_LIGHTEN_MAX_TINT);
559 	rgb_color text_color = ui_color(B_MENU_ITEM_TEXT_COLOR);
560 
561 	int32 index = 0;
562 	BView *child = NULL;
563 	float left = 0;
564 
565 	// Clear
566 	SetHighColor( ViewColor());
567 	FillRect( BRect(0, 0, Frame().right, tabHeight));
568 
569 	while (child = ChildAt(index)) {
570 		// Draw outline
571 		SetHighColor(dark_line_color);
572 		StrokeLine( BPoint(left, 10), BPoint(left, tabHeight));
573 		StrokeArc( BPoint(left+10, 10), 10, 10, 90, 90);
574 		StrokeLine( BPoint(left+10, 0), BPoint(left+tabWidth-10, 0));
575 		StrokeArc( BPoint(left+tabWidth-10, 10), 9, 10, 0, 90);
576 		StrokeLine( BPoint(left+tabWidth-1, 10), BPoint(left+tabWidth-1, tabHeight));
577 		// Draw "shadow" on the right side
578 		SetHighColor(darkest_line_color);
579 		StrokeArc( BPoint(left+tabWidth-10, 10), 10, 10, 0, 50);
580 		StrokeLine( BPoint(left+tabWidth, 10), BPoint(left+tabWidth, tabHeight-1));
581 		// Draw label
582 		SetHighColor(text_color);
583 		DrawString( child->Name(), BPoint(left+(tabWidth/2)-(StringWidth(child->Name())/2), 3+be_plain_font->Size()));
584 		// Draw "light" on left and top side
585 		SetHighColor(light_line_color);
586 		StrokeArc( BPoint(left+10, 10), 9, 9, 90, 90);
587 		StrokeLine( BPoint(left+1, 10), BPoint(left+1, tabHeight));
588 		StrokeLine( BPoint(left+10, 1), BPoint(left+tabWidth-8, 1));
589 		// Draw bottom edge
590 		if (activeChild != index)
591 			StrokeLine( BPoint(left-2,tabHeight), BPoint(left+tabWidth,tabHeight));
592 		else
593 			StrokeLine( BPoint(left-2,tabHeight), BPoint(left+1,tabHeight));
594 
595 		left += tabWidth+2;
596 		index++;
597 	}
598 	// Draw bottom edge to the rigth side
599 	StrokeLine( BPoint(left-2,tabHeight), BPoint(Bounds().Width(),tabHeight));
600 }
601 
602 //---------------------------------------------------
603 //	MouseDown, check if on tab, if so change tab if needed
604 //---------------------------------------------------
605 void
606 TranslatorView::MouseDown(BPoint where)
607 {
608 	// If user clicked on tabs part of view
609 	if (where.y <= tabHeight)
610 		// If there is a tab (not whole width is occupied by tabs)
611 		if (where.x < tabWidth*CountChildren()) {
612 			// Which tab was selected?
613 			int32 index = where.x / tabWidth;
614 			if (activeChild != index) {
615 				// Hide current visible child
616 				ChildAt(activeChild)->Hide();
617 				// This loop is needed because it looks like in DataTranslations
618 				// view gets hidden more than one time when user changes translator
619 				while (ChildAt(index)->IsHidden())
620 					ChildAt(index)->Show();
621 				// Remember which one is currently visible
622 				activeChild = index;
623 				// Redraw
624 				Draw( Frame());
625 			}
626 		}
627 }
628 
629 
630 //----------------------------------------------------------------------------
631 //
632 //	Functions :: TranslatorWindow
633 //
634 //----------------------------------------------------------------------------
635 
636 //---------------------------------------------------
637 //	Constructor
638 //---------------------------------------------------
639 TranslatorWindow::TranslatorWindow(bool quit_on_close)
640 :	BWindow(BRect(100, 100, 100, 100), "JPEG Settings", B_TITLED_WINDOW, B_NOT_ZOOMABLE)
641 {
642 	BRect extent(0, 0, 0, 0);
643 	BView *config = NULL;
644 	MakeConfig(NULL, &config, &extent);
645 
646 	AddChild(config);
647 	ResizeTo(extent.Width(), extent.Height());
648 
649 	// Make application quit after this window close
650 	if (quit_on_close)
651 		SetFlags(Flags() | B_QUIT_ON_WINDOW_CLOSE);
652 }
653 
654 
655 //----------------------------------------------------------------------------
656 //
657 //	Functions :: main
658 //
659 //----------------------------------------------------------------------------
660 
661 //---------------------------------------------------
662 //	main function
663 //---------------------------------------------------
664 int
665 main() {
666 	BApplication app("application/x-vnd.Shard.JPEGTranslator");
667 
668 	TranslatorWindow *window = new TranslatorWindow();
669 	window->Show();
670 
671 	app.Run();
672 	return 0;
673 }
674 
675 //---------------------------------------------------
676 //	Hook to create and return our configuration view
677 //---------------------------------------------------
678 status_t
679 MakeConfig(BMessage *ioExtension, BView **outView, BRect *outExtent)
680 {
681 	*outView = new TranslatorView("TranslatorView");
682 	*outExtent = (*outView)->Frame();
683 	return B_OK;
684 }
685 
686 //---------------------------------------------------
687 //	Determine whether or not we can handle this data
688 //---------------------------------------------------
689 status_t
690 Identify(BPositionIO *inSource, const translation_format *inFormat, BMessage *ioExtension, translator_info *outInfo, uint32 outType)
691 {
692 
693 	if ((outType != 0) && (outType != B_TRANSLATOR_BITMAP) && (outType != JPEG_FORMAT))
694 		return B_NO_TRANSLATOR;
695 
696 	// !!! You might need to make this buffer bigger to test for your native format
697 	off_t position = inSource->Position();
698 	char header[sizeof(TranslatorBitmap)];
699 	status_t err = inSource->Read(header, sizeof(TranslatorBitmap));
700 	inSource->Seek( position, SEEK_SET);
701 	if (err < B_OK) return err;
702 
703 	if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) {
704 		outInfo->type = inputFormats[1].type;
705 		outInfo->translator = 0;
706 		outInfo->group = inputFormats[1].group;
707 		outInfo->quality = inputFormats[1].quality;
708 		outInfo->capability = inputFormats[1].capability;
709 		strcpy(outInfo->name, inputFormats[1].name);
710 		strcpy(outInfo->MIME, inputFormats[1].MIME);
711 	} else {
712 		// First 3 bytes in jpg files are always the same from what i've seen so far
713 		// check them
714 		if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) {
715 		/* this below would be safer but it slows down whole thing
716 
717 			struct jpeg_decompress_struct cinfo;
718 			struct jpeg_error_mgr jerr;
719 			cinfo.err = jpeg_std_error(&jerr);
720 			jpeg_create_decompress(&cinfo);
721 			be_jpeg_stdio_src(&cinfo, inSource);
722 			// now try to read header
723 			// it can't be read before checking first 3 bytes
724 			// because it will hang up if there is no header (not jpeg file)
725 			int result = jpeg_read_header(&cinfo, FALSE);
726 			jpeg_destroy_decompress(&cinfo);
727 			if (result == JPEG_HEADER_OK) {
728 		*/		outInfo->type = inputFormats[0].type;
729 				outInfo->translator = 0;
730 				outInfo->group = inputFormats[0].group;
731 				outInfo->quality = inputFormats[0].quality;
732 				outInfo->capability = inputFormats[0].capability;
733 				strcpy(outInfo->name, inputFormats[0].name);
734 				strcpy(outInfo->MIME, inputFormats[0].MIME);
735 				return B_OK;
736 		/*	} else
737 				return B_NO_TRANSLATOR;
738 		*/
739 		} else
740 			return B_NO_TRANSLATOR;
741 	}
742 
743 	return B_OK;
744 }
745 
746 //---------------------------------------------------
747 //	Arguably the most important method in the add-on
748 //---------------------------------------------------
749 status_t
750 Translate(BPositionIO *inSource, const translator_info *inInfo, BMessage *ioExtension, uint32 outType, BPositionIO *outDestination)
751 {
752 	// If no specific type was requested, convert to the interchange format
753 	if (outType == 0) outType = B_TRANSLATOR_BITMAP;
754 
755 	// What action to take, based on the findings of Identify()
756 	if (outType == inInfo->type) {
757 		return Copy(inSource, outDestination);
758 	} else if (inInfo->type == B_TRANSLATOR_BITMAP && outType == JPEG_FORMAT) {
759 		return Compress(inSource, outDestination);
760 	} else if (inInfo->type == JPEG_FORMAT && outType == B_TRANSLATOR_BITMAP) {
761 		return Decompress(inSource, outDestination);
762 	}
763 
764 	return B_NO_TRANSLATOR;
765 }
766 
767 //---------------------------------------------------
768 //	The user has requested the same format for input and output, so just copy
769 //---------------------------------------------------
770 status_t
771 Copy(BPositionIO *in, BPositionIO *out)
772 {
773 	int block_size = 65536;
774 	void *buffer = malloc(block_size);
775 	char temp[1024];
776 	if (buffer == NULL) {
777 		buffer = temp;
778 		block_size = 1024;
779 	}
780 	status_t err = B_OK;
781 
782 	// Read until end of file or error
783 	while (1) {
784 		ssize_t to_read = block_size;
785 		err = in->Read(buffer, to_read);
786 		// Explicit check for EOF
787 		if (err == -1) {
788 			if (buffer != temp) free(buffer);
789 			return B_OK;
790 		}
791 		if (err <= B_OK) break;
792 		to_read = err;
793 		err = out->Write(buffer, to_read);
794 		if (err != to_read) if (err >= 0) err = B_DEVICE_FULL;
795 		if (err < B_OK) break;
796 	}
797 
798 	if (buffer != temp) free(buffer);
799 	return (err >= 0) ? B_OK : err;
800 }
801 
802 //---------------------------------------------------
803 //	Encode into the native format
804 //---------------------------------------------------
805 status_t
806 Compress(BPositionIO *in, BPositionIO *out)
807 {
808 	// Load Settings
809 	SETTINGS Settings;
810 	LoadSettings(&Settings);
811 
812 	// Read info about bitmap
813 	TranslatorBitmap header;
814 	status_t err = in->Read(&header, sizeof(TranslatorBitmap));
815 	if (err < B_OK) return err;
816 	else if (err < (int)sizeof(TranslatorBitmap)) return B_ERROR;
817 
818 	// Grab dimension, color space, and size information from the stream
819 	BRect bounds;
820 	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
821 	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
822 	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
823 	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
824 
825 	int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
826 
827 	int width = bounds.IntegerWidth() + 1;
828 	int height = bounds.IntegerHeight() + 1;
829 
830 	// Function pointer to convert function
831 	// It will point to proper function if needed
832 	void (*converter)(uchar *inscanline, uchar *outscanline, int inrow_bytes) = NULL;
833 
834 	// Default color info
835 	J_COLOR_SPACE jpg_color_space = JCS_RGB;
836 	int jpg_input_components = 3;
837 	int32 out_row_bytes;
838 	int padding = 0;
839 
840 	switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors))
841 	{
842 		case B_CMAP8:
843 		{
844 			converter = convert_from_cmap8_to_24;
845 			padding = in_row_bytes - width;
846 			break;
847 		}
848 		case B_GRAY1:
849 		{
850 			if (Settings.B_GRAY1_as_B_RGB24) {
851 				converter = convert_from_gray1_to_24;
852 			} else {
853 				jpg_input_components = 1;
854 				jpg_color_space = JCS_GRAYSCALE;
855 				converter = convert_from_gray1_to_gray8;
856 			}
857 			padding = in_row_bytes - (width/8);
858 			break;
859 		}
860 		case B_GRAY8:
861 		{
862 			jpg_input_components = 1;
863 			jpg_color_space = JCS_GRAYSCALE;
864 			padding = in_row_bytes - width;
865 			break;
866 		}
867 		case B_RGB15:
868 		case B_RGBA15:
869 		{
870 			converter = convert_from_15_to_24;
871 			padding = in_row_bytes - (width * 2);
872 			break;
873 		}
874 		case B_RGB15_BIG:
875 		case B_RGBA15_BIG:
876 		{
877 			converter = convert_from_15b_to_24;
878 			padding = in_row_bytes - (width * 2);
879 			break;
880 		}
881 		case B_RGB16:
882 		{
883 			converter = convert_from_16_to_24;
884 			padding = in_row_bytes - (width * 2);
885 			break;
886 		}
887 		case B_RGB16_BIG:
888 		{
889 			converter = convert_from_16b_to_24;
890 			padding = in_row_bytes - (width * 2);
891 			break;
892 		}
893 		case B_RGB24:
894 		{
895 			converter = convert_from_24_to_24;
896 			padding = in_row_bytes - (width * 3);
897 			break;
898 		}
899 		case B_RGB24_BIG:
900 		{
901 			padding = in_row_bytes - (width * 3);
902 			break;
903 		}
904 		case B_RGB32:
905 		case B_RGBA32:
906 		{
907 			converter = convert_from_32_to_24;
908 			padding = in_row_bytes - (width * 4);
909 			break;
910 		}
911 		case B_RGB32_BIG:
912 		case B_RGBA32_BIG:
913 		{
914 			converter = convert_from_32b_to_24;
915 			padding = in_row_bytes - (width * 4);
916 			break;
917 		}
918 		case B_CMYK32:
919 		{
920 			jpg_color_space = JCS_CMYK;
921 			jpg_input_components = 4;
922 			padding = in_row_bytes - (width * 4);
923 			break;
924 		}
925 		default:
926 		{
927 			fprintf(stderr, "Wrong type: Color space not implemented.\n");
928 			return B_ERROR;
929 		}
930 	}
931 	out_row_bytes = jpg_input_components * width;
932 
933 	// Set basic things needed for jpeg writing
934 	struct jpeg_compress_struct cinfo;
935 	struct jpeg_error_mgr jerr;
936 	cinfo.err = be_jpeg_std_error(&jerr, &Settings);
937 	jpeg_create_compress(&cinfo);
938 	be_jpeg_stdio_dest(&cinfo, out);
939 
940 	// Set basic values
941 	cinfo.image_width = width;
942 	cinfo.image_height = height;
943 	cinfo.input_components = jpg_input_components;
944 	cinfo.in_color_space = jpg_color_space;
945 	jpeg_set_defaults(&cinfo);
946 
947 	// Set better accuracy
948 	cinfo.dct_method = JDCT_ISLOW;
949 
950 	// This is needed to prevent some colors loss
951 	// With it generated jpegs are as good as from Fireworks (at last! :D)
952 	if( Settings.OptimizeColors) {
953 		int index = 0;
954 		while( index < cinfo.num_components) {
955 			cinfo.comp_info[index].h_samp_factor = 1;
956 			cinfo.comp_info[index].v_samp_factor = 1;
957 			// This will make file smaller, but with worse quality more or less
958 			// like with 93%-94% (but it's subjective opinion) on tested images
959 			// but with smaller size (between 92% and 93% on tested images)
960 			if(Settings.SmallerFile)
961 				cinfo.comp_info[index].quant_tbl_no = 1;
962 			// This will make bigger file, but also better quality ;]
963 			// from my tests it seems like useless - better quality with smaller
964 			// can be acheived without this
965 //			cinfo.comp_info[index].dc_tbl_no = 1;
966 //			cinfo.comp_info[index].ac_tbl_no = 1;
967 			index++;
968 		}
969 	}
970 
971 	// Set quality
972 	jpeg_set_quality(&cinfo, Settings.Quality, true);
973 
974 	// Set progressive compression if needed
975 	// if not, turn on optimizing in libjpeg
976 	if (Settings.Progressive)
977 		jpeg_simple_progression(&cinfo);
978 	else
979 		cinfo.optimize_coding = TRUE;
980 
981 	// Set smoothing (effect like Blur)
982 	cinfo.smoothing_factor = Settings.Smoothing;
983 
984 	// Initialize compression
985 	jpeg_start_compress(&cinfo, TRUE);
986 
987 	// Declare scanlines
988 	JSAMPROW in_scanline = NULL;
989 	JSAMPROW out_scanline = NULL;
990 	JSAMPROW writeline;	// Pointer to in_scanline (default) or out_scanline (if there will be conversion)
991 
992 	// Allocate scanline
993 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
994 	in_scanline = (unsigned char *) (cinfo.mem->alloc_large) ( (j_common_ptr)&cinfo, JPOOL_PERMANENT, in_row_bytes);
995 
996 	// We need 2nd scanline storage ony for conversion
997 	if (converter != NULL) {
998 		// There will be conversion, allocate second scanline...
999 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1000 	    out_scanline = (unsigned char *) (cinfo.mem->alloc_large) ( (j_common_ptr)&cinfo, JPOOL_PERMANENT, out_row_bytes);
1001 		// ... and make it the one to write to file
1002 		writeline = out_scanline;
1003 	} else
1004 		writeline = in_scanline;
1005 
1006 	while (cinfo.next_scanline < cinfo.image_height) {
1007 		// Read scanline
1008 		err = in->Read(in_scanline, in_row_bytes);
1009 		if (err < in_row_bytes)
1010 			return (err < B_OK) ? Error( (j_common_ptr)&cinfo, err) : Error( (j_common_ptr)&cinfo, B_ERROR);
1011 
1012 		// Convert if needed
1013 		if (converter != NULL)
1014 			converter(in_scanline, out_scanline, in_row_bytes-padding);
1015 
1016 		// Write scanline
1017 	   	jpeg_write_scanlines(&cinfo, &writeline, 1);
1018 	}
1019 
1020 	jpeg_finish_compress(&cinfo);
1021 	jpeg_destroy_compress(&cinfo);
1022 
1023 	return B_OK;
1024 }
1025 
1026 //---------------------------------------------------
1027 //	Decode the native format
1028 //---------------------------------------------------
1029 status_t
1030 Decompress(BPositionIO *in, BPositionIO *out)
1031 {
1032 	// Load Settings
1033 	SETTINGS Settings;
1034 	LoadSettings(&Settings);
1035 
1036 	// Set basic things needed for jpeg reading
1037 	struct jpeg_decompress_struct cinfo;
1038 	struct jpeg_error_mgr jerr;
1039 	cinfo.err = be_jpeg_std_error(&jerr, &Settings);
1040 	jpeg_create_decompress(&cinfo);
1041 	be_jpeg_stdio_src(&cinfo, in);
1042 
1043 	// Read info about image
1044 	jpeg_read_header(&cinfo, TRUE);
1045 
1046 	// Default color info
1047 	color_space out_color_space = B_RGB32;
1048 	int out_color_components = 4;
1049 
1050 	// Function pointer to convert function
1051 	// It will point to proper function if needed
1052 	void (*converter)(uchar *inscanline, uchar *outscanline, int inrow_bytes) = convert_from_24_to_32;
1053 
1054 	// If color space isn't rgb
1055 	if (cinfo.out_color_space != JCS_RGB) {
1056 		switch (cinfo.out_color_space)
1057 		{
1058 			case JCS_UNKNOWN:		/* error/unspecified */
1059 				fprintf(stderr, "From Type: Jpeg uses unknown color type\n");
1060 				break;
1061 			case JCS_GRAYSCALE:		/* monochrome */
1062 				// Check if user wants to read only as RGB32 or not
1063 				if (!Settings.Always_B_RGB32) {
1064 					// Grayscale
1065 					out_color_space = B_GRAY8;
1066 					out_color_components = 1;
1067 					converter = NULL;
1068 				} else {
1069 					// RGB
1070 					cinfo.out_color_space = JCS_RGB;
1071 					cinfo.output_components = 3;
1072 					converter = convert_from_24_to_32;
1073 				}
1074 				break;
1075 			case JCS_YCbCr:		/* Y/Cb/Cr (also known as YUV) */
1076 				cinfo.out_color_space = JCS_RGB;
1077 				converter = convert_from_24_to_32;
1078 				break;
1079 			case JCS_YCCK:		/* Y/Cb/Cr/K */
1080 				// Let libjpeg convert it to CMYK
1081 				cinfo.out_color_space = JCS_CMYK;
1082 				// Fall through to CMYK since we need the same settings
1083 			case JCS_CMYK:		/* C/M/Y/K */
1084 				// Use proper converter
1085 				if (Settings.PhotoshopCMYK)
1086 					converter = convert_from_CMYK_to_32_photoshop;
1087 				else
1088 					converter = convert_from_CMYK_to_32;
1089 				break;
1090 			default:
1091 				fprintf(stderr, "From Type: Jpeg uses hmm... i don't know really :(\n");
1092 				break;
1093 		}
1094 	}
1095 
1096 	// Initialize decompression
1097 	jpeg_start_decompress(&cinfo);
1098 
1099 	// !!! Initialize this bounds rect to the size of your image
1100 	BRect bounds( 0, 0, cinfo.output_width-1, cinfo.output_height-1);
1101 
1102 	// Bytes count in one line of image (scanline)
1103 	int64 row_bytes = cinfo.output_width * out_color_components;
1104 
1105 	// Fill out the B_TRANSLATOR_BITMAP's header
1106 	TranslatorBitmap header;
1107 	header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
1108 	header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left);
1109 	header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
1110 	header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
1111 	header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
1112 	header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(out_color_space);
1113 	header.rowBytes = B_HOST_TO_BENDIAN_INT32(row_bytes);
1114 	header.dataSize = B_HOST_TO_BENDIAN_INT32(row_bytes * cinfo.output_height);
1115 
1116 	// Write out the header
1117 	status_t err = out->Write(&header, sizeof(TranslatorBitmap));
1118 	if (err < B_OK) return Error( (j_common_ptr)&cinfo, err);
1119 	else if (err < (int)sizeof(TranslatorBitmap)) return Error( (j_common_ptr)&cinfo, B_ERROR);
1120 
1121 	// Declare scanlines
1122 	JSAMPROW in_scanline = NULL;
1123 	JSAMPROW out_scanline = NULL;
1124 	JSAMPROW writeline;	// Pointer to in_scanline or out_scanline (if there will be conversion)
1125 
1126 	// Allocate scanline
1127 	// Use libjpeg memory allocation functions, so in case of error it will free them itself
1128     in_scanline = (unsigned char *) (cinfo.mem->alloc_large) ( (j_common_ptr)&cinfo, JPOOL_PERMANENT, row_bytes);
1129 
1130 	// We need 2nd scanline storage only for conversion
1131 	if (converter != NULL) {
1132 		// There will be conversion, allocate second scanline...
1133 		// Use libjpeg memory allocation functions, so in case of error it will free them itself
1134 	    out_scanline = (unsigned char *) (cinfo.mem->alloc_large) ( (j_common_ptr)&cinfo, JPOOL_PERMANENT, row_bytes);
1135 		// ... and make it the one to write to file
1136 		writeline = out_scanline;
1137 	} else
1138 		writeline = in_scanline;
1139 
1140 	while (cinfo.output_scanline < cinfo.output_height) {
1141 		// Read scanline
1142 		jpeg_read_scanlines(&cinfo, &in_scanline, 1);
1143 
1144 		// Convert if needed
1145 		if( converter != NULL)
1146 			converter(in_scanline, out_scanline, row_bytes);
1147 
1148   		// Write the scanline buffer to the output stream
1149 		err = out->Write(writeline, row_bytes);
1150 		if (err < row_bytes)
1151 			return (err < B_OK) ? Error( (j_common_ptr)&cinfo, err) : Error( (j_common_ptr)&cinfo, B_ERROR);
1152 	}
1153 
1154 	jpeg_finish_decompress(&cinfo);
1155 	jpeg_destroy_decompress(&cinfo);
1156 
1157 	return B_OK;
1158 }
1159 
1160 //---------------------------------------------------
1161 //	Frees jpeg alocated memory
1162 //	Returns given error (B_ERROR by default)
1163 //---------------------------------------------------
1164 status_t
1165 Error(j_common_ptr cinfo, status_t error)
1166 {
1167 	jpeg_destroy(cinfo);
1168 	return error;
1169 }
1170