2 * Copyright 2002-2009, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Updated by Sikosis (beos@gravity24hr.com)
7 * Copyright 1999, Be Incorporated. All Rights Reserved.
8 * This file may be used under the terms of the Be Sample Code License.
15 #include <BitmapStream.h>
17 #include <Clipboard.h>
19 #include <Directory.h>
21 #include <FindDirectory.h>
24 #include <MenuField.h>
25 #include <MessageFormat.h>
28 #include <PopUpMenu.h>
30 #include <ScrollView.h>
32 #include <TranslationUtils.h>
33 #include <TranslatorRoster.h>
34 #include <WindowScreen.h>
44 #undef B_TRANSLATION_CONTEXT
45 #define B_TRANSLATION_CONTEXT "Magnify-Main"
48 const int32 msg_update_info
= 'info';
49 const int32 msg_show_info
= 'show';
50 const int32 msg_toggle_grid
= 'grid';
51 const int32 msg_shrink
= 'shnk';
52 const int32 msg_grow
= 'grow';
53 const int32 msg_make_square
= 'sqar';
54 const int32 msg_shrink_pixel
= 'pshk';
55 const int32 msg_grow_pixel
= 'pgrw';
57 const int32 msg_new_color
= 'colr';
58 const int32 msg_toggle_ruler
= 'rulr';
59 const int32 msg_copy_image
= 'copy';
60 const int32 msg_track_color
= 'trak';
61 const int32 msg_freeze
= 'frez';
62 const int32 msg_stick
= 'stic';
63 const int32 msg_dump
= 'dump';
64 const int32 msg_add_cross_hair
= 'acrs';
65 const int32 msg_remove_cross_hair
= 'rcrs';
66 const int32 msg_save
= 'save';
68 const rgb_color kViewGray
= { 216, 216, 216, 255};
69 const rgb_color kGridGray
= {130, 130, 130, 255 };
70 const rgb_color kWhite
= { 255, 255, 255, 255};
71 const rgb_color kBlack
= { 0, 0, 0, 255};
72 const rgb_color kDarkGray
= { 96, 96, 96, 255};
73 const rgb_color kRedColor
= { 255, 10, 50, 255 };
74 const rgb_color kGreenColor
= { 10, 255, 50, 255 };
75 const rgb_color kBlueColor
= { 10, 50, 255, 255 };
77 const char* const kBitmapMimeType
= "image/x-vnd.Be-bitmap";
79 const float kCurrentVersion
= 1.2;
80 const char *kPrefsFileName
= "Magnify_prefs";
86 // show info (rgb, location)
89 const char* const kAppName
= "Magnify";
90 const bool kDefaultShowGrid
= true;
91 const bool kDefaultShowInfo
= true;
92 const int32 kDefaultPixelCount
= 32;
93 const int32 kDefaultPixelSize
= 8;
95 // each info region will be:
96 // top-bottom: 5 fontheight 5 fontheight 5
97 // left-right: 10 minwindowwidth 10
98 const int32 kBorderSize
= 10;
102 FontHeight(BView
* target
, bool full
)
105 target
->GetFontHeight(&finfo
);
106 float h
= ceil(finfo
.ascent
) + ceil(finfo
.descent
);
109 h
+= ceil(finfo
.leading
);
116 BoundsSelection(int32 incX
, int32 incY
, float* x
, float* y
,
117 int32 xCount
, int32 yCount
)
135 BuildInfoMenu(BMenu
*menu
)
138 menuItem
= new BMenuItem(B_TRANSLATE("Save image"),
139 new BMessage(msg_save
), 'S');
140 menu
->AddItem(menuItem
);
141 // menuItem = new BMenuItem(B_TRANSLATE("Save selection"),
142 // new BMessage(msg_save), 'S');
143 // menu->AddItem(menuItem);
144 menuItem
= new BMenuItem(B_TRANSLATE("Copy image"),
145 new BMessage(msg_copy_image
), 'C');
146 menu
->AddItem(menuItem
);
147 menu
->AddSeparatorItem();
149 menuItem
= new BMenuItem(B_TRANSLATE("Show info"),
150 new BMessage(msg_show_info
), 'T');
151 menu
->AddItem(menuItem
);
152 menuItem
= new BMenuItem(B_TRANSLATE("Add a crosshair"),
153 new BMessage(msg_add_cross_hair
), 'H');
154 menu
->AddItem(menuItem
);
155 menuItem
= new BMenuItem(B_TRANSLATE("Remove a crosshair"),
156 new BMessage(msg_remove_cross_hair
), 'H', B_SHIFT_KEY
);
157 menu
->AddItem(menuItem
);
158 menuItem
= new BMenuItem(B_TRANSLATE("Show grid"),
159 new BMessage(msg_toggle_grid
), 'G');
160 menu
->AddItem(menuItem
);
161 menu
->AddSeparatorItem();
163 menuItem
= new BMenuItem(B_TRANSLATE("Freeze image"),
164 new BMessage(msg_freeze
), 'F');
165 menu
->AddItem(menuItem
);
166 menuItem
= new BMenuItem(B_TRANSLATE("Stick coordinates"),
167 new BMessage(msg_stick
), 'I');
168 menu
->AddItem(menuItem
);
169 menu
->AddSeparatorItem();
171 menuItem
= new BMenuItem(B_TRANSLATE("Make square"),
172 new BMessage(msg_make_square
), '/');
173 menu
->AddItem(menuItem
);
174 menuItem
= new BMenuItem(B_TRANSLATE("Decrease window size"),
175 new BMessage(msg_shrink
), '-');
176 menu
->AddItem(menuItem
);
177 menuItem
= new BMenuItem(B_TRANSLATE("Increase window size"),
178 new BMessage(msg_grow
), '+');
179 menu
->AddItem(menuItem
);
180 menuItem
= new BMenuItem(B_TRANSLATE("Decrease pixel size"),
181 new BMessage(msg_shrink_pixel
), ',');
182 menu
->AddItem(menuItem
);
183 menuItem
= new BMenuItem(B_TRANSLATE("Increase pixel size"),
184 new BMessage(msg_grow_pixel
), '.');
185 menu
->AddItem(menuItem
);
189 UpdateInfoMenu(BMenu
*menu
, TWindow
*window
)
192 bool showGrid
= true;
193 bool infoBarIsVisible
= true;
194 bool stickCordinates
= true;
196 state
= window
->IsActive();
197 showGrid
= window
->ShowGrid();
198 infoBarIsVisible
= window
->InfoBarIsVisible();
199 stickCordinates
= window
->IsSticked();
201 BMenuItem
* menuItem
= menu
->FindItem(B_TRANSLATE("Show info"));
203 menuItem
->SetEnabled(state
);
204 menuItem
->SetMarked(infoBarIsVisible
);
206 menuItem
= menu
->FindItem(B_TRANSLATE("Add a crosshair"));
208 menuItem
->SetEnabled(state
);
209 menuItem
= menu
->FindItem(B_TRANSLATE("Remove a crosshair"));
211 menuItem
->SetEnabled(state
);
212 menuItem
= menu
->FindItem(B_TRANSLATE("Show grid"));
214 menuItem
->SetEnabled(state
);
215 menuItem
->SetMarked(showGrid
);
217 menuItem
= menu
->FindItem(B_TRANSLATE("Freeze image"));
219 menuItem
->SetMarked(!state
);
221 menuItem
= menu
->FindItem(B_TRANSLATE("Stick coordinates"));
223 menuItem
->SetMarked(stickCordinates
);
225 menuItem
= menu
->FindItem(B_TRANSLATE("Make square"));
227 menuItem
->SetEnabled(state
);
228 menuItem
= menu
->FindItem(B_TRANSLATE("Decrease window size"));
230 menuItem
->SetEnabled(state
);
231 menuItem
= menu
->FindItem(B_TRANSLATE("Increase window size"));
233 menuItem
->SetEnabled(state
);
234 menuItem
= menu
->FindItem(B_TRANSLATE("Decrease pixel size"));
236 menuItem
->SetEnabled(state
);
237 menuItem
= menu
->FindItem(B_TRANSLATE("Increase pixel size"));
239 menuItem
->SetEnabled(state
);
245 // pass in pixelCount to maintain backward compatibility of setting
246 // the pixelcount from the command line
247 TApp::TApp(int32 pixelCount
)
248 : BApplication("application/x-vnd.Haiku-Magnify")
250 TWindow
* magWindow
= new TWindow(pixelCount
);
258 TWindow::TWindow(int32 pixelCount
)
260 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Magnify"),
261 B_TITLED_WINDOW
, B_OUTLINE_RESIZE
)
263 GetPrefs(pixelCount
);
266 BRect
infoRect(Bounds());
267 infoRect
.InsetBy(-1, -1);
268 fInfo
= new TInfoView(infoRect
);
271 fFontHeight
= FontHeight(fInfo
, true);
272 fInfoHeight
= (fFontHeight
* 2) + (3 * 5);
274 BRect
fbRect(0, 0, (fHPixelCount
*fPixelSize
), (fHPixelCount
*fPixelSize
));
276 fbRect
.OffsetBy(10, fInfoHeight
);
277 fFatBits
= new TMagnify(fbRect
, this);
278 fInfo
->AddChild(fFatBits
);
280 fFatBits
->SetSelection(fShowInfo
);
281 fInfo
->SetMagView(fFatBits
);
283 ResizeWindow(fHPixelCount
, fVPixelCount
);
284 UpdateInfoBarOnResize();
286 AddShortcut('S', B_COMMAND_KEY
, new BMessage(msg_save
));
287 AddShortcut('C', B_COMMAND_KEY
, new BMessage(msg_copy_image
));
288 AddShortcut('T', B_COMMAND_KEY
, new BMessage(msg_show_info
));
289 AddShortcut('H', B_COMMAND_KEY
, new BMessage(msg_add_cross_hair
));
290 AddShortcut('H', B_SHIFT_KEY
, new BMessage(msg_remove_cross_hair
));
291 AddShortcut('G', B_COMMAND_KEY
, new BMessage(msg_toggle_grid
));
292 AddShortcut('F', B_COMMAND_KEY
, new BMessage(msg_freeze
));
293 AddShortcut('I', B_COMMAND_KEY
, new BMessage(msg_stick
));
294 AddShortcut('-', B_COMMAND_KEY
, new BMessage(msg_shrink
));
295 AddShortcut('=', B_COMMAND_KEY
, new BMessage(msg_grow
));
296 AddShortcut('/', B_COMMAND_KEY
, new BMessage(msg_make_square
));
297 AddShortcut(',', B_COMMAND_KEY
, new BMessage(msg_shrink_pixel
));
298 AddShortcut('.', B_COMMAND_KEY
, new BMessage(msg_grow_pixel
));
308 TWindow::MessageReceived(BMessage
* m
)
310 bool active
= fFatBits
->Active();
315 fInfoBarState
= !fInfoBarState
;
316 ShowInfo(!fShowInfo
);
320 case msg_toggle_grid
:
333 case msg_make_square
:
335 if (fHPixelCount
== fVPixelCount
)
337 int32 big
= (fHPixelCount
> fVPixelCount
) ? fHPixelCount
: fVPixelCount
;
338 ResizeWindow(big
, big
);
342 case msg_shrink_pixel
:
351 case msg_add_cross_hair
:
352 if (active
&& fShowInfo
)
355 case msg_remove_cross_hair
:
356 if (active
&& fShowInfo
)
362 SetFlags(B_OUTLINE_RESIZE
| B_NOT_ZOOMABLE
| B_NOT_RESIZABLE
);
364 SetFlags(B_OUTLINE_RESIZE
| B_NOT_ZOOMABLE
);
366 fFatBits
->MakeActive(!fFatBits
->Active());
370 fFatBits
->MakeSticked(!fFatBits
->Sticked());
374 // freeze the image here, unfreeze after dump or cancel
375 fFatBits
->StartSave();
377 BMessenger
messenger(this);
378 BMessage
message(msg_dump
);
379 fSavePanel
= new BFilePanel(B_SAVE_PANEL
, &messenger
, 0, 0, false,
381 fSavePanel
->SetSaveText("Bitmaps.png");
390 m
->FindRef("directory", &dirRef
);
391 m
->FindString((const char*)"name",(const char**) &name
);
393 fFatBits
->SaveImage(&dirRef
, name
);
397 // image is frozen before the FilePanel is shown
402 fFatBits
->CopyImage();
405 BWindow::MessageReceived(m
);
412 TWindow::QuitRequested()
415 be_app
->PostMessage(B_QUIT_REQUESTED
);
421 TWindow::GetPrefs(int32 overridePixelCount
)
428 bool showGrid
= kDefaultShowGrid
;
429 bool showInfo
= kDefaultShowInfo
;
430 bool ch1Showing
=false;
431 bool ch2Showing
=false;
432 int32 hPixelCount
= kDefaultPixelCount
;
433 int32 vPixelCount
= kDefaultPixelCount
;
434 int32 pixelSize
= kDefaultPixelSize
;
436 if (find_directory (B_USER_SETTINGS_DIRECTORY
, &path
) == B_OK
) {
438 path
.Append(kPrefsFileName
);
439 if ((ref
= open(path
.Path(), 0)) >= 0) {
440 if (read(ref
, name
, 7) != 7)
444 if (strcmp(name
, kAppName
) != 0)
447 read(ref
, &version
, sizeof(float));
449 if (read(ref
, &loc
, sizeof(BPoint
)) != sizeof(BPoint
))
454 if (read(ref
, &showGrid
, sizeof(bool)) != sizeof(bool)) {
455 showGrid
= kDefaultShowGrid
;
459 if (read(ref
, &showInfo
, sizeof(bool)) != sizeof(bool)) {
460 showInfo
= kDefaultShowInfo
;
464 if (read(ref
, &ch1Showing
, sizeof(bool)) != sizeof(bool)) {
469 if (read(ref
, &ch2Showing
, sizeof(bool)) != sizeof(bool)) {
474 if (read(ref
, &hPixelCount
, sizeof(int32
)) != sizeof(int32
)) {
475 hPixelCount
= kDefaultPixelCount
;
478 if (read(ref
, &vPixelCount
, sizeof(int32
)) != sizeof(int32
)) {
479 vPixelCount
= kDefaultPixelCount
;
483 if (read(ref
, &pixelSize
, sizeof(int32
)) != sizeof(int32
)) {
484 pixelSize
= kDefaultPixelSize
;
488 ALMOST_DONE
: // clean up and try to position the window
491 if (haveLoc
&& BScreen(B_MAIN_SCREEN_ID
).Frame().Contains(loc
)) {
498 // if prefs dont yet exist or the window is not onscreen, center the window
501 // set all the settings to defaults if we get here
503 fShowGrid
= showGrid
;
504 fShowInfo
= showInfo
;
505 fInfoBarState
= showInfo
;
506 fHPixelCount
= (overridePixelCount
== -1) ? hPixelCount
: overridePixelCount
;
507 fVPixelCount
= (overridePixelCount
== -1) ? vPixelCount
: overridePixelCount
;
508 fPixelSize
= pixelSize
;
517 if (find_directory (B_USER_SETTINGS_DIRECTORY
, &path
, true) == B_OK
) {
520 path
.Append (kPrefsFileName
);
521 if ((ref
= creat(path
.Path(), S_IRUSR
| S_IWUSR
)) >= 0) {
522 float version
= kCurrentVersion
;
524 lseek (ref
, 0, SEEK_SET
);
525 write(ref
, kAppName
, 7);
526 write(ref
, &version
, sizeof(float));
528 BPoint loc
= Frame().LeftTop();
529 write(ref
, &loc
, sizeof(BPoint
));
531 write(ref
, &fShowGrid
, sizeof(bool));
532 write(ref
, &fShowInfo
, sizeof(bool));
534 CrossHairsShowing(&ch1
, &ch2
);
535 write(ref
, &ch1
, sizeof(bool));
536 write(ref
, &ch2
, sizeof(bool));
538 write(ref
, &fHPixelCount
, sizeof(int32
));
539 write(ref
, &fVPixelCount
, sizeof(int32
));
540 write(ref
, &fPixelSize
, sizeof(int32
));
549 TWindow::FrameResized(float w
, float h
)
551 CalcViewablePixels();
552 fFatBits
->InitBuffers(fHPixelCount
, fVPixelCount
, fPixelSize
, ShowGrid());
553 UpdateInfoBarOnResize();
558 TWindow::ScreenChanged(BRect screenSize
, color_space depth
)
560 BWindow::ScreenChanged(screenSize
, depth
);
562 fFatBits
->ScreenChanged(screenSize
,depth
);
567 TWindow::Minimize(bool m
)
569 BWindow::Minimize(m
);
574 TWindow::Zoom(BPoint
/*position*/, float /*width*/, float /*height*/)
576 if (fFatBits
->Active())
577 ShowInfo(!fShowInfo
);
582 TWindow::CalcViewablePixels()
584 float w
= Bounds().Width();
585 float h
= Bounds().Height();
587 if (InfoIsShowing()) {
588 w
-= 20; // remove the gutter
589 h
= h
-fInfoHeight
-10; // remove info and gutter
593 fFatBits
->CrossHairsShowing(&ch1
, &ch2
);
597 h
-= fFontHeight
+ 5;
599 fHPixelCount
= (int32
)w
/ fPixelSize
; // calc h pixels
600 if (fHPixelCount
< 16)
603 fVPixelCount
= (int32
)h
/ fPixelSize
; // calc v pixels
604 if (fVPixelCount
< 4)
610 TWindow::GetPreferredSize(float* width
, float* height
)
612 *width
= fHPixelCount
* fPixelSize
; // calc window width
613 *height
= fVPixelCount
* fPixelSize
; // calc window height
614 if (InfoIsShowing()) {
616 *height
+= fInfoHeight
+ 10;
620 fFatBits
->CrossHairsShowing(&ch1
, &ch2
);
622 *height
+= fFontHeight
;
624 *height
+= fFontHeight
+ 5;
629 TWindow::ResizeWindow(int32 hPixelCount
, int32 vPixelCount
)
631 fHPixelCount
= hPixelCount
;
632 fVPixelCount
= vPixelCount
;
635 GetPreferredSize(&width
, &height
);
637 ResizeTo(width
, height
);
642 TWindow::ResizeWindow(bool direction
)
644 int32 x
= fHPixelCount
;
645 int32 y
= fVPixelCount
;
666 TWindow::SetGrid(bool s
)
672 fFatBits
->SetUpdate(true);
684 TWindow::ShowInfo(bool i
)
692 fFatBits
->MoveTo(10, fInfoHeight
);
694 fFatBits
->MoveTo(1,1);
695 fFatBits
->SetCrossHairsShowing(false, false);
698 fFatBits
->SetSelection(fShowInfo
);
699 ResizeWindow(fHPixelCount
, fVPixelCount
);
700 fInfo
->SetInfoTextVisible(i
);
705 TWindow::InfoIsShowing()
712 TWindow::InfoBarIsVisible()
714 return fInfoBarState
;
719 TWindow::UpdateInfo()
726 TWindow::UpdateInfoBarOnResize()
728 float infoWidth
, infoHeight
;
729 fInfo
->GetPreferredSize(&infoWidth
, &infoHeight
);
731 if (infoWidth
> Bounds().Width()
732 || infoHeight
> Bounds().Height()) {
735 ShowInfo(fInfoBarState
);
741 TWindow::AddCrossHair()
743 fFatBits
->AddCrossHair();
745 // crosshair info needs to be added
746 // window resizes accordingly
749 GetPreferredSize(&width
, &height
);
750 ResizeTo(width
, height
);
755 TWindow::RemoveCrossHair()
757 fFatBits
->RemoveCrossHair();
759 // crosshair info needs to be removed
760 // window resizes accordingly
763 GetPreferredSize(&width
, &height
);
764 ResizeTo(width
, height
);
769 TWindow::CrossHairsShowing(bool* ch1
, bool* ch2
)
771 fFatBits
->CrossHairsShowing(ch1
, ch2
);
776 TWindow::PixelCount(int32
* h
, int32
*v
)
784 TWindow::SetPixelSize(int32 s
)
791 // tell info that size has changed
792 // tell mag that size has changed
794 CalcViewablePixels();
795 ResizeWindow(fHPixelCount
, fVPixelCount
);
800 TWindow::SetPixelSize(bool d
)
812 float w
= Bounds().Width();
813 float h
= Bounds().Height();
814 CalcViewablePixels();
815 ResizeWindow(fHPixelCount
, fVPixelCount
);
817 // the window might not actually change in size
818 // in that case force the buffers to the new dimension
819 if (w
== Bounds().Width() && h
== Bounds().Height())
820 fFatBits
->InitBuffers(fHPixelCount
, fVPixelCount
, fPixelSize
, ShowGrid());
831 #undef B_TRANSLATION_CONTEXT
832 #define B_TRANSLATION_CONTEXT "Magnify-Main"
838 return fFatBits
->Active();
845 return fFatBits
->Sticked();
852 TInfoView::TInfoView(BRect frame
)
853 : BBox(frame
, "rgb", B_FOLLOW_ALL
,
854 B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
| B_FRAME_EVENTS
,
857 SetFont(be_plain_font
);
858 fFontHeight
= FontHeight(this, true);
861 fSelectionColor
= kBlack
;
862 fCH1Loc
.x
= fCH1Loc
.y
= fCH2Loc
.x
= fCH2Loc
.y
= 0;
869 fInfoTextVisible
= true;
873 TInfoView::~TInfoView()
879 TInfoView::AttachedToWindow()
881 BBox::AttachedToWindow();
882 SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
883 dynamic_cast<TWindow
*>(Window())->PixelCount(&fHPixelCount
, &fVPixelCount
);
884 fPixelSize
= dynamic_cast<TWindow
*>(Window())->PixelSize();
891 TInfoView::Draw(BRect updateRect
)
894 SetLowColor(ViewColor());
898 int32 hPixelCount
, vPixelCount
;
899 dynamic_cast<TWindow
*>(Window())->PixelCount(&hPixelCount
, &vPixelCount
);
900 int32 pixelSize
= dynamic_cast<TWindow
*>(Window())->PixelSize();
902 MovePenTo(15 + fPopUp
->Bounds().Width(), fFontHeight
+ 5);
904 static BMessageFormat
format(B_TRANSLATE("%width x %height @ {0, plural, "
905 "one{# pixel/pixel} other{# pixels/pixel}}"));
907 BString dimensionsInfo
;
908 format
.Format(dimensionsInfo
, pixelSize
);
912 dimensionsInfo
.ReplaceAll("%width", rep
);
915 dimensionsInfo
.ReplaceAll("%height", rep
);
917 invalRect
.Set(10, 5, 10 + StringWidth(fInfoStr
), fFontHeight
+7);
918 SetHighColor(ViewColor());
920 SetHighColor(ui_color(B_PANEL_TEXT_COLOR
));
921 strcpy(fInfoStr
, dimensionsInfo
);
922 if (fInfoTextVisible
)
923 DrawString(fInfoStr
);
925 rgb_color color
= { 0, 0, 0, 255 };
927 color
= fMagView
->SelectionColor();
929 snprintf(str
, sizeof(str
), "R: %i G: %i B: %i (#%02x%02x%02x)",
930 color
.red
, color
.green
, color
.blue
, color
.red
, color
.green
, color
.blue
);
932 MovePenTo(15 + fPopUp
->Bounds().Width(), fFontHeight
*2+5);
933 invalRect
.Set(10, fFontHeight
+7, 10 + StringWidth(fRGBStr
), fFontHeight
*2+7);
934 SetHighColor(ViewColor());
936 SetHighColor(ui_color(B_PANEL_TEXT_COLOR
));
938 if (fInfoTextVisible
)
941 bool ch1Showing
, ch2Showing
;
942 dynamic_cast<TWindow
*>(Window())->CrossHairsShowing(&ch1Showing
, &ch2Showing
);
945 BPoint
pt1(fMagView
->CrossHair1Loc());
946 BPoint
pt2(fMagView
->CrossHair2Loc());
948 float h
= Bounds().Height();
951 sprintf(str
, "2) x: %" B_PRIi32
" y: %" B_PRIi32
" y: %d",
952 (int32
)pt2
.x
, (int32
)pt2
.y
, abs((int)(pt1
.y
- pt2
.y
)));
953 invalRect
.Set(10, h
-12-fFontHeight
, 10 + StringWidth(fCH2Str
), h
-10);
954 SetHighColor(ViewColor());
956 SetHighColor(ui_color(B_PANEL_TEXT_COLOR
));
958 if (fInfoTextVisible
)
962 if (ch1Showing
&& ch2Showing
) {
963 MovePenTo(10, h
-10-fFontHeight
-2);
964 sprintf(str
, "1) x: %" B_PRIi32
" y: %" B_PRIi32
" x: %d",
965 (int32
)pt1
.x
, (int32
)pt1
.y
, abs((int)(pt1
.x
- pt2
.x
)));
966 invalRect
.Set(10, h
-10-2*fFontHeight
-2, 10 + StringWidth(fCH1Str
), h
-10-fFontHeight
);
967 SetHighColor(ViewColor());
969 SetHighColor(ui_color(B_PANEL_TEXT_COLOR
));
971 if (fInfoTextVisible
)
973 } else if (ch1Showing
) {
975 sprintf(str
, "x: %" B_PRIi32
" y: %" B_PRIi32
, (int32
)pt1
.x
, (int32
)pt1
.y
);
976 invalRect
.Set(10, h
-10-fFontHeight
, 10 + StringWidth(fCH1Str
), h
-8);
977 SetHighColor(ViewColor());
979 SetHighColor(ui_color(B_PANEL_TEXT_COLOR
));
981 if (fInfoTextVisible
)
991 TInfoView::FrameResized(float width
, float height
)
993 BBox::FrameResized(width
, height
);
1000 fMenu
= new TMenu(dynamic_cast<TWindow
*>(Window()), "");
1001 BuildInfoMenu(fMenu
);
1003 BRect
r(9, 11, 22, 27);
1004 fPopUp
= new BMenuField( r
, "region menu", NULL
, fMenu
, true,
1005 B_FOLLOW_LEFT
| B_FOLLOW_TOP
);
1011 TInfoView::SetMagView(TMagnify
* magView
)
1020 TMenu::TMenu(TWindow
*mainWindow
, const char *title
, menu_layout layout
)
1021 : BMenu(title
, layout
),
1022 fMainWindow(mainWindow
)
1033 TMenu::AttachedToWindow()
1035 UpdateInfoMenu(this, fMainWindow
);
1037 BMenu::AttachedToWindow();
1042 TInfoView::GetPreferredSize(float* _width
, float* _height
)
1045 float str1Width
= StringWidth(fCH1Str
)
1046 + StringWidth(fCH2Str
)
1047 + StringWidth(fRGBStr
)
1049 float str2Width
= StringWidth(fInfoStr
) + 30;
1050 *_width
= str1Width
> str2Width
? str1Width
: str2Width
;
1054 *_height
= fFontHeight
* 2 + 10;
1059 TInfoView::IsInfoTextVisible()
1061 return fInfoTextVisible
;
1066 TInfoView::SetInfoTextVisible(bool visible
)
1068 fInfoTextVisible
= visible
;
1076 TMagnify::TMagnify(BRect r
, TWindow
* parent
)
1077 : BView(r
, "MagView", B_FOLLOW_NONE
, B_WILL_DRAW
| B_FRAME_EVENTS
),
1078 fNeedToUpdate(true),
1085 fShowSelection(false),
1086 fSelectionLoc(0, 0),
1087 fShowCrossHair1(false),
1088 fCrossHair1(-1, -1),
1089 fShowCrossHair2(false),
1090 fCrossHair2(-1, -1),
1092 fStickCoordinates(false)
1097 TMagnify::~TMagnify()
1099 kill_thread(fThread
);
1105 TMagnify::AttachedToWindow()
1107 int32 width
, height
;
1108 fParent
->PixelCount(&width
, &height
);
1109 InitBuffers(width
, height
, fParent
->PixelSize(), fParent
->ShowGrid());
1111 fThread
= spawn_thread(TMagnify::MagnifyTask
, "MagnifyTask",
1112 B_NORMAL_PRIORITY
, this);
1114 resume_thread(fThread
);
1116 SetViewColor(B_TRANSPARENT_32_BIT
);
1122 TMagnify::InitBuffers(int32 hPixelCount
, int32 vPixelCount
,
1123 int32 pixelSize
, bool showGrid
)
1125 color_space colorSpace
= BScreen(Window()).ColorSpace();
1127 BRect
r(0, 0, (pixelSize
* hPixelCount
)-1, (pixelSize
* vPixelCount
)-1);
1128 if (Bounds().Width() != r
.Width() || Bounds().Height() != r
.Height())
1129 ResizeTo(r
.Width(), r
.Height());
1133 fImageView
->RemoveSelf();
1134 fImageBuf
->Unlock();
1136 fImageView
->Resize((int32
)r
.Width(), (int32
)r
.Height());
1137 fImageView
->SetSpace(colorSpace
);
1139 fImageView
= new TOSMagnify(r
, this, colorSpace
);
1142 fImageBuf
= new BBitmap(r
, colorSpace
, true);
1144 fImageBuf
->AddChild(fImageView
);
1145 fImageBuf
->Unlock();
1150 TMagnify::Draw(BRect
)
1152 BRect
bounds(Bounds());
1153 DrawBitmap(fImageBuf
, bounds
, bounds
);
1154 static_cast<TWindow
*>(Window())->UpdateInfo();
1159 TMagnify::KeyDown(const char *key
, int32 numBytes
)
1161 if (!fShowSelection
)
1162 BView::KeyDown(key
, numBytes
);
1164 uint32 mods
= modifiers();
1168 if (fShowCrossHair1
) {
1171 if (fShowCrossHair2
) {
1174 } else if (fShowCrossHair1
) {
1178 fNeedToUpdate
= true;
1184 if (mods
& B_OPTION_KEY
)
1187 MoveSelection(-1,0);
1190 if (mods
& B_OPTION_KEY
)
1196 if (mods
& B_OPTION_KEY
)
1199 MoveSelection(0,-1);
1202 if (mods
& B_OPTION_KEY
)
1209 BView::KeyDown(key
,numBytes
);
1216 TMagnify::FrameResized(float newW
, float newH
)
1221 if (fSelectionLoc
.x
>= w
)
1222 fSelectionLoc
.x
= 0;
1223 if (fSelectionLoc
.y
>= h
)
1224 fSelectionLoc
.y
= 0;
1226 if (fShowCrossHair1
) {
1227 if (fCrossHair1
.x
>= w
) {
1228 fCrossHair1
.x
= fSelectionLoc
.x
+ 2;
1229 if (fCrossHair1
.x
>= w
)
1232 if (fCrossHair1
.y
>= h
) {
1233 fCrossHair1
.y
= fSelectionLoc
.y
+ 2;
1234 if (fCrossHair1
.y
>= h
)
1238 if (fShowCrossHair2
) {
1239 if (fCrossHair2
.x
>= w
) {
1240 fCrossHair2
.x
= fCrossHair1
.x
+ 2;
1241 if (fCrossHair2
.x
>= w
)
1244 if (fCrossHair2
.y
>= h
) {
1245 fCrossHair2
.y
= fCrossHair1
.y
+ 2;
1246 if (fCrossHair2
.y
>= h
)
1255 TMagnify::MouseDown(BPoint where
)
1257 BMessage
*currentMsg
= Window()->CurrentMessage();
1258 if (currentMsg
->what
== B_MOUSE_DOWN
) {
1260 currentMsg
->FindInt32("buttons", (int32
*)&buttons
);
1262 uint32 modifiers
= 0;
1263 currentMsg
->FindInt32("modifiers", (int32
*)&modifiers
);
1265 if ((buttons
& B_SECONDARY_MOUSE_BUTTON
) || (modifiers
& B_CONTROL_KEY
)) {
1266 // secondary button was clicked or control key was down, show menu and return
1268 BPopUpMenu
*menu
= new BPopUpMenu(B_TRANSLATE("Info"), false, false);
1269 menu
->SetFont(be_plain_font
);
1270 BuildInfoMenu(menu
);
1271 UpdateInfoMenu(menu
, dynamic_cast<TWindow
*>(Window()));
1272 BMenuItem
*selected
= menu
->Go(ConvertToScreen(where
));
1274 Window()->PostMessage(selected
->Message()->what
);
1279 // add a mousedown looper here
1281 int32 pixelSize
= PixelSize();
1282 float x
= where
.x
/ pixelSize
;
1283 float y
= where
.y
/ pixelSize
;
1285 MoveSelectionTo(x
, y
);
1287 // draw the frozen image
1288 // update the info region
1290 fNeedToUpdate
= true;
1297 TMagnify::ScreenChanged(BRect
, color_space
)
1299 int32 width
, height
;
1300 fParent
->PixelCount(&width
, &height
);
1301 InitBuffers(width
, height
, fParent
->PixelSize(), fParent
->ShowGrid());
1306 TMagnify::SetSelection(bool state
)
1308 if (fShowSelection
== state
)
1311 fShowSelection
= state
;
1318 TMagnify::MoveSelection(int32 x
, int32 y
)
1320 if (!fShowSelection
)
1323 int32 xCount
, yCount
;
1324 PixelCount(&xCount
, &yCount
);
1327 if (fSelection
== 0) {
1328 xloc
= fSelectionLoc
.x
;
1329 yloc
= fSelectionLoc
.y
;
1330 BoundsSelection(x
, y
, &xloc
, &yloc
, xCount
, yCount
);
1331 fSelectionLoc
.x
= xloc
;
1332 fSelectionLoc
.y
= yloc
;
1333 } else if (fSelection
== 1) {
1334 xloc
= fCrossHair1
.x
;
1335 yloc
= fCrossHair1
.y
;
1336 BoundsSelection(x
, y
, &xloc
, &yloc
, xCount
, yCount
);
1337 fCrossHair1
.x
= xloc
;
1338 fCrossHair1
.y
= yloc
;
1339 } else if (fSelection
== 2) {
1340 xloc
= fCrossHair2
.x
;
1341 yloc
= fCrossHair2
.y
;
1342 BoundsSelection(x
, y
, &xloc
, &yloc
, xCount
, yCount
);
1343 fCrossHair2
.x
= xloc
;
1344 fCrossHair2
.y
= yloc
;
1347 fNeedToUpdate
= true;
1353 TMagnify::MoveSelectionTo(int32 x
, int32 y
)
1355 if (!fShowSelection
)
1358 int32 xCount
, yCount
;
1359 PixelCount(&xCount
, &yCount
);
1365 if (fSelection
== 0) {
1366 fSelectionLoc
.x
= x
;
1367 fSelectionLoc
.y
= y
;
1368 } else if (fSelection
== 1) {
1371 } else if (fSelection
== 2) {
1376 fNeedToUpdate
= true;
1377 Invalidate(); //Draw(Bounds());
1382 TMagnify::ShowSelection()
1388 TMagnify::Selection()
1395 TMagnify::SelectionIsShowing()
1397 return fShowSelection
;
1402 TMagnify::SelectionLoc(float* x
, float* y
)
1404 *x
= fSelectionLoc
.x
;
1405 *y
= fSelectionLoc
.y
;
1410 TMagnify::SetSelectionLoc(float x
, float y
)
1412 fSelectionLoc
.x
= x
;
1413 fSelectionLoc
.y
= y
;
1418 TMagnify::SelectionColor()
1420 return fImageView
->ColorAtSelection();
1425 TMagnify::CrossHair1Loc(float* x
, float* y
)
1433 TMagnify::CrossHair2Loc(float* x
, float* y
)
1441 TMagnify::CrossHair1Loc()
1448 TMagnify::CrossHair2Loc()
1455 TMagnify::NudgeMouse(float x
, float y
)
1460 GetMouse(&loc
, &button
);
1461 ConvertToScreen(&loc
);
1465 set_mouse_position((int32
)loc
.x
, (int32
)loc
.y
);
1470 TMagnify::WindowActivated(bool active
)
1478 TMagnify::MagnifyTask(void *arg
)
1480 TMagnify
* view
= (TMagnify
*)arg
;
1482 // static data members can't access members, methods without
1483 // a pointer to an instance of the class
1484 TWindow
* window
= (TWindow
*)view
->Window();
1487 if (window
->Lock()) {
1488 if (view
->NeedToUpdate() || view
->Active())
1489 view
->Update(view
->NeedToUpdate());
1501 TMagnify::Update(bool force
)
1505 static long counter
= 0;
1507 if (!fStickCoordinates
) {
1508 GetMouse(&loc
, &button
);
1509 ConvertToScreen(&loc
);
1513 if (force
|| fLastLoc
!= loc
|| counter
++ % 35 == 0) {
1514 if (fImageView
->CreateImage(loc
, force
))
1526 TMagnify::NeedToUpdate()
1528 return fNeedToUpdate
;
1533 TMagnify::SetUpdate(bool s
)
1540 TMagnify::CopyImage()
1543 be_clipboard
->Lock();
1544 be_clipboard
->Clear();
1546 BMessage
*message
= be_clipboard
->Data();
1548 printf(B_TRANSLATE_CONTEXT("no clip msg\n",
1549 "In console, when clipboard is empty after clicking Copy image"));
1553 BMessage
*embeddedBitmap
= new BMessage();
1554 (fImageView
->Bitmap())->Archive(embeddedBitmap
,false);
1555 status_t err
= message
->AddMessage(kBitmapMimeType
, embeddedBitmap
);
1557 err
= message
->AddRect("rect", fImageView
->Bitmap()->Bounds());
1559 be_clipboard
->Commit();
1561 be_clipboard
->Unlock();
1567 TMagnify::AddCrossHair()
1569 if (fShowCrossHair1
&& fShowCrossHair2
)
1575 if (fShowCrossHair1
) {
1577 fShowCrossHair2
= true;
1578 fCrossHair2
.x
= fCrossHair1
.x
+ 2;
1579 if (fCrossHair2
.x
>= w
)
1581 fCrossHair2
.y
= fCrossHair1
.y
+ 2;
1582 if (fCrossHair2
.y
>= h
)
1586 fShowCrossHair1
= true;
1587 fCrossHair1
.x
= fSelectionLoc
.x
+ 2;
1588 if (fCrossHair1
.x
>= w
)
1590 fCrossHair1
.y
= fSelectionLoc
.y
+ 2;
1591 if (fCrossHair1
.y
>= h
)
1599 TMagnify::RemoveCrossHair()
1601 if (!fShowCrossHair1
&& !fShowCrossHair2
)
1604 if (fShowCrossHair2
) {
1606 fShowCrossHair2
= false;
1607 } else if (fShowCrossHair1
) {
1609 fShowCrossHair1
= false;
1616 TMagnify::SetCrossHairsShowing(bool ch1
, bool ch2
)
1618 fShowCrossHair1
= ch1
;
1619 fShowCrossHair2
= ch2
;
1624 TMagnify::CrossHairsShowing(bool* ch1
, bool* ch2
)
1626 *ch1
= fShowCrossHair1
;
1627 *ch2
= fShowCrossHair2
;
1632 TMagnify::MakeActive(bool s
)
1639 TMagnify::MakeSticked(bool s
)
1641 fStickCoordinates
= s
;
1646 TMagnify::PixelCount(int32
* width
, int32
* height
)
1648 fParent
->PixelCount(width
, height
);
1653 TMagnify::PixelSize()
1655 return fParent
->PixelSize();
1660 TMagnify::ShowGrid()
1662 return fParent
->ShowGrid();
1667 TMagnify::StartSave()
1669 fImageFrozenOnSave
= Active();
1670 if (fImageFrozenOnSave
)
1678 if (fImageFrozenOnSave
)
1684 TMagnify::SaveImage(entry_ref
* ref
, char* name
)
1686 // create a new file
1688 BDirectory
parentDir(ref
);
1689 parentDir
.CreateFile(name
, &file
);
1691 // Write the screenshot bitmap to the file
1692 BBitmapStream
stream(fImageView
->Bitmap());
1693 BTranslatorRoster
* roster
= BTranslatorRoster::Default();
1694 roster
->Translate(&stream
, NULL
, NULL
, &file
, B_PNG_FORMAT
,
1695 B_TRANSLATOR_BITMAP
);
1698 stream
.DetachBitmap(&bitmap
);
1699 // The stream takes over ownership of the bitmap
1701 // unfreeze the image, image was frozen before invoke of FilePanel
1708 TOSMagnify::TOSMagnify(BRect r
, TMagnify
* parent
, color_space space
)
1709 : BView(r
, "ImageView", B_FOLLOW_NONE
, B_WILL_DRAW
| B_FRAME_EVENTS
),
1710 fColorSpace(space
), fParent(parent
)
1734 // uh, oh -- a color space we don't support
1735 fprintf(stderr
, "Tried to run in an unsupported color space; exiting\n");
1747 TOSMagnify::~TOSMagnify()
1756 TOSMagnify::SetSpace(color_space space
)
1758 fColorSpace
= space
;
1764 TOSMagnify::InitObject()
1767 fParent
->PixelCount(&w
, &h
);
1770 BRect
bitsRect(0, 0, w
-1, h
-1);
1771 fBitmap
= new BBitmap(bitsRect
, fColorSpace
);
1774 fOldBits
= (char*)malloc(fBitmap
->BitsLength());
1777 #if B_HOST_IS_BENDIAN
1778 fPixel
= new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG
, true);
1780 fPixel
= new BBitmap(BRect(0,0,0,0), B_RGBA32
, true);
1782 fPixelView
= new BView(BRect(0,0,0,0), NULL
, 0, 0);
1784 fPixel
->AddChild(fPixelView
);
1791 TOSMagnify::FrameResized(float width
, float height
)
1793 BView::FrameResized(width
, height
);
1799 TOSMagnify::Resize(int32 width
, int32 height
)
1801 ResizeTo(width
, height
);
1807 TOSMagnify::CreateImage(BPoint mouseLoc
, bool force
)
1809 bool created
= false;
1810 if (Window() && Window()->Lock()) {
1811 int32 width
, height
;
1812 fParent
->PixelCount(&width
, &height
);
1813 int32 pixelSize
= fParent
->PixelSize();
1815 BRect
srcRect(0, 0, width
- 1, height
- 1);
1816 srcRect
.OffsetBy(mouseLoc
.x
- (width
/ 2),
1817 mouseLoc
.y
- (height
/ 2));
1819 if (force
|| CopyScreenRect(srcRect
)) {
1820 srcRect
.OffsetTo(BPoint(0, 0));
1821 BRect
destRect(Bounds());
1823 DrawBitmap(fBitmap
, srcRect
, destRect
);
1825 DrawGrid(width
, height
, destRect
, pixelSize
);
1833 printf("window problem\n");
1840 TOSMagnify::CopyScreenRect(BRect srcRect
)
1842 // constrain src rect to legal screen rect
1843 BScreen
screen(Window());
1844 BRect scrnframe
= screen
.Frame();
1846 if (srcRect
.right
> scrnframe
.right
)
1847 srcRect
.OffsetTo(scrnframe
.right
- srcRect
.Width(), srcRect
.top
);
1848 if (srcRect
.top
< 0)
1849 srcRect
.OffsetTo(srcRect
.left
, 0);
1851 if (srcRect
.bottom
> scrnframe
.bottom
)
1852 srcRect
.OffsetTo(srcRect
.left
, scrnframe
.bottom
- srcRect
.Height());
1853 if (srcRect
.left
< 0)
1854 srcRect
.OffsetTo(0, srcRect
.top
);
1856 // save a copy of the bits for comparison later
1857 memcpy(fOldBits
, fBitmap
->Bits(), fBitmap
->BitsLength());
1859 screen
.ReadBitmap(fBitmap
, false, &srcRect
);
1861 // let caller know whether bits have actually changed
1862 return memcmp(fBitmap
->Bits(), fOldBits
, fBitmap
->BitsLength()) != 0;
1867 TOSMagnify::DrawGrid(int32 width
, int32 height
, BRect destRect
, int32 pixelSize
)
1870 if (fParent
->ShowGrid() && fParent
->PixelSize() > 2) {
1871 BeginLineArray(width
* height
);
1874 for (int32 i
= pixelSize
; i
< (height
* pixelSize
); i
+= pixelSize
)
1875 AddLine(BPoint(0, i
), BPoint(destRect
.right
, i
), kGridGray
);
1878 for (int32 i
= pixelSize
; i
< (width
* pixelSize
); i
+= pixelSize
)
1879 AddLine(BPoint(i
, 0), BPoint(i
, destRect
.bottom
), kGridGray
);
1884 SetHighColor(kGridGray
);
1885 StrokeRect(destRect
);
1890 TOSMagnify::DrawSelection()
1892 if (!fParent
->SelectionIsShowing())
1896 int32 pixelSize
= fParent
->PixelSize();
1897 int32 squareSize
= pixelSize
- 2;
1899 fParent
->SelectionLoc(&x
, &y
);
1900 x
*= pixelSize
; x
++;
1901 y
*= pixelSize
; y
++;
1902 BRect
selRect(x
, y
, x
+squareSize
, y
+squareSize
);
1904 short selection
= fParent
->Selection();
1907 SetLowColor(ViewColor());
1908 SetHighColor(kRedColor
);
1909 StrokeRect(selRect
);
1910 if (selection
== 0) {
1911 StrokeLine(BPoint(x
,y
), BPoint(x
+squareSize
,y
+squareSize
));
1912 StrokeLine(BPoint(x
,y
+squareSize
), BPoint(x
+squareSize
,y
));
1915 bool ch1Showing
, ch2Showing
;
1916 fParent
->CrossHairsShowing(&ch1Showing
, &ch2Showing
);
1918 SetHighColor(kBlueColor
);
1919 fParent
->CrossHair1Loc(&x
, &y
);
1920 x
*= pixelSize
; x
++;
1921 y
*= pixelSize
; y
++;
1922 selRect
.Set(x
, y
,x
+squareSize
, y
+squareSize
);
1923 StrokeRect(selRect
);
1925 AddLine(BPoint(0, y
+(squareSize
/2)),
1926 BPoint(x
, y
+(squareSize
/2)), kBlueColor
); // left
1927 AddLine(BPoint(x
+squareSize
,y
+(squareSize
/2)),
1928 BPoint(Bounds().Width(), y
+(squareSize
/2)), kBlueColor
); // right
1929 AddLine(BPoint(x
+(squareSize
/2), 0),
1930 BPoint(x
+(squareSize
/2), y
), kBlueColor
); // top
1931 AddLine(BPoint(x
+(squareSize
/2), y
+squareSize
),
1932 BPoint(x
+(squareSize
/2), Bounds().Height()), kBlueColor
); // bottom
1934 if (selection
== 1) {
1935 StrokeLine(BPoint(x
,y
), BPoint(x
+squareSize
,y
+squareSize
));
1936 StrokeLine(BPoint(x
,y
+squareSize
), BPoint(x
+squareSize
,y
));
1940 SetHighColor(kBlueColor
);
1941 fParent
->CrossHair2Loc(&x
, &y
);
1942 x
*= pixelSize
; x
++;
1943 y
*= pixelSize
; y
++;
1944 selRect
.Set(x
, y
,x
+squareSize
, y
+squareSize
);
1945 StrokeRect(selRect
);
1947 AddLine(BPoint(0, y
+(squareSize
/2)),
1948 BPoint(x
, y
+(squareSize
/2)), kBlueColor
); // left
1949 AddLine(BPoint(x
+squareSize
,y
+(squareSize
/2)),
1950 BPoint(Bounds().Width(), y
+(squareSize
/2)), kBlueColor
); // right
1951 AddLine(BPoint(x
+(squareSize
/2), 0),
1952 BPoint(x
+(squareSize
/2), y
), kBlueColor
); // top
1953 AddLine(BPoint(x
+(squareSize
/2), y
+squareSize
),
1954 BPoint(x
+(squareSize
/2), Bounds().Height()), kBlueColor
); // bottom
1956 if (selection
== 2) {
1957 StrokeLine(BPoint(x
,y
), BPoint(x
+squareSize
,y
+squareSize
));
1958 StrokeLine(BPoint(x
,y
+squareSize
), BPoint(x
+squareSize
,y
));
1967 TOSMagnify::ColorAtSelection()
1970 fParent
->SelectionLoc(&x
, &y
);
1971 BRect
srcRect(x
, y
, x
, y
);
1972 BRect
dstRect(0, 0, 0, 0);
1974 fPixelView
->DrawBitmap(fBitmap
, srcRect
, dstRect
);
1978 uint32 pixel
= *((uint32
*)fPixel
->Bits());
1980 c
.alpha
= pixel
>> 24;
1981 c
.red
= (pixel
>> 16) & 0xFF;
1982 c
.green
= (pixel
>> 8) & 0xFF;
1983 c
.blue
= pixel
& 0xFF;
1993 main(int argc
, char* argv
[])
1995 int32 pixelCount
= -1;
1998 printf(B_TRANSLATE_CONTEXT(
1999 "usage: magnify [size] (magnify size * size pixels)\n",
2004 pixelCount
= abs(atoi(argv
[1]));
2006 if ((pixelCount
> 100) || (pixelCount
< 4)) {
2007 printf(B_TRANSLATE_CONTEXT(
2008 "usage: magnify [size] (magnify size * size pixels)\n",
2010 printf(B_TRANSLATE_CONTEXT(
2011 " size must be > 4 and a multiple of 4\n",
2016 if (pixelCount
% 4) {
2017 printf(B_TRANSLATE_CONTEXT(
2018 "magnify: size must be a multiple of 4\n",
2025 TApp
app(pixelCount
);