1 /* 2 * Copyright 2002-2009, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Updated by Sikosis (beos@gravity24hr.com) 6 * 7 * Copyright 1999, Be Incorporated. All Rights Reserved. 8 * This file may be used under the terms of the Be Sample Code License. 9 */ 10 11 #include "Magnify.h" 12 13 #include <Alert.h> 14 #include <Bitmap.h> 15 #include <Catalog.h> 16 #include <Clipboard.h> 17 #include <Debug.h> 18 #include <Directory.h> 19 #include <File.h> 20 #include <FindDirectory.h> 21 #include <Locale.h> 22 #include <MenuItem.h> 23 #include <MenuField.h> 24 #include <MessageFormat.h> 25 #include <Path.h> 26 #include <PopUpMenu.h> 27 #include <Screen.h> 28 #include <ScrollView.h> 29 #include <TextView.h> 30 #include <WindowScreen.h> 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <fcntl.h> 36 #include <unistd.h> 37 #include <sys/stat.h> 38 39 40 #undef B_TRANSLATION_CONTEXT 41 #define B_TRANSLATION_CONTEXT "Magnify-Main" 42 43 44 const int32 msg_update_info = 'info'; 45 const int32 msg_show_info = 'show'; 46 const int32 msg_toggle_grid = 'grid'; 47 const int32 msg_shrink = 'shnk'; 48 const int32 msg_grow = 'grow'; 49 const int32 msg_make_square = 'sqar'; 50 const int32 msg_shrink_pixel = 'pshk'; 51 const int32 msg_grow_pixel = 'pgrw'; 52 53 const int32 msg_new_color = 'colr'; 54 const int32 msg_toggle_ruler = 'rulr'; 55 const int32 msg_copy_image = 'copy'; 56 const int32 msg_track_color = 'trak'; 57 const int32 msg_freeze = 'frez'; 58 const int32 msg_stick = 'stic'; 59 const int32 msg_dump = 'dump'; 60 const int32 msg_add_cross_hair = 'acrs'; 61 const int32 msg_remove_cross_hair = 'rcrs'; 62 const int32 msg_save = 'save'; 63 64 const rgb_color kViewGray = { 216, 216, 216, 255}; 65 const rgb_color kGridGray = {130, 130, 130, 255 }; 66 const rgb_color kWhite = { 255, 255, 255, 255}; 67 const rgb_color kBlack = { 0, 0, 0, 255}; 68 const rgb_color kDarkGray = { 96, 96, 96, 255}; 69 const rgb_color kRedColor = { 255, 10, 50, 255 }; 70 const rgb_color kGreenColor = { 10, 255, 50, 255 }; 71 const rgb_color kBlueColor = { 10, 50, 255, 255 }; 72 73 const char* const kBitmapMimeType = "image/x-vnd.Be-bitmap"; 74 75 const float kCurrentVersion = 1.2; 76 const char *kPrefsFileName = "Magnify_prefs"; 77 78 // prefs are: 79 // name = Magnify 80 // version 81 // show grid 82 // show info (rgb, location) 83 // pixel count 84 // pixel size 85 const char* const kAppName = "Magnify"; 86 const bool kDefaultShowGrid = true; 87 const bool kDefaultShowInfo = true; 88 const int32 kDefaultPixelCount = 32; 89 const int32 kDefaultPixelSize = 8; 90 91 // each info region will be: 92 // top-bottom: 5 fontheight 5 fontheight 5 93 // left-right: 10 minwindowwidth 10 94 const int32 kBorderSize = 10; 95 96 97 static float 98 FontHeight(BView* target, bool full) 99 { 100 font_height finfo; 101 target->GetFontHeight(&finfo); 102 float h = ceil(finfo.ascent) + ceil(finfo.descent); 103 104 if (full) 105 h += ceil(finfo.leading); 106 107 return h; 108 } 109 110 111 static void 112 BoundsSelection(int32 incX, int32 incY, float* x, float* y, 113 int32 xCount, int32 yCount) 114 { 115 *x += incX; 116 *y += incY; 117 118 if (*x < 0) 119 *x = xCount-1; 120 if (*x >= xCount) 121 *x = 0; 122 123 if (*y < 0) 124 *y = yCount-1; 125 if (*y >= yCount) 126 *y = 0; 127 } 128 129 130 static void 131 BuildInfoMenu(BMenu *menu) 132 { 133 BMenuItem* menuItem; 134 menuItem = new BMenuItem(B_TRANSLATE("Save image"), 135 new BMessage(msg_save),'S'); 136 menu->AddItem(menuItem); 137 // menuItem = new BMenuItem(B_TRANSLATE("Save selection"), new BMessage(msg_save),'S'); 138 // menu->AddItem(menuItem); 139 menuItem = new BMenuItem(B_TRANSLATE("Copy image"), 140 new BMessage(msg_copy_image),'C'); 141 menu->AddItem(menuItem); 142 menu->AddSeparatorItem(); 143 144 menuItem = new BMenuItem(B_TRANSLATE("Hide/Show info"), 145 new BMessage(msg_show_info),'T'); 146 menu->AddItem(menuItem); 147 menuItem = new BMenuItem(B_TRANSLATE("Add a crosshair"), 148 new BMessage(msg_add_cross_hair),'H'); 149 menu->AddItem(menuItem); 150 menuItem = new BMenuItem(B_TRANSLATE("Remove a crosshair"), 151 new BMessage(msg_remove_cross_hair), 'H', B_SHIFT_KEY); 152 menu->AddItem(menuItem); 153 menuItem = new BMenuItem(B_TRANSLATE("Hide/Show grid"), 154 new BMessage(msg_toggle_grid),'G'); 155 menu->AddItem(menuItem); 156 menu->AddSeparatorItem(); 157 158 menuItem = new BMenuItem(B_TRANSLATE("Freeze/Unfreeze image"), 159 new BMessage(msg_freeze),'F'); 160 menu->AddItem(menuItem); 161 menuItem = new BMenuItem(B_TRANSLATE("Stick coordinates"), 162 new BMessage(msg_stick), 'I'); 163 menu->AddItem(menuItem); 164 menu->AddSeparatorItem(); 165 166 menuItem = new BMenuItem(B_TRANSLATE("Make square"), 167 new BMessage(msg_make_square),'/'); 168 menu->AddItem(menuItem); 169 menuItem = new BMenuItem(B_TRANSLATE("Decrease window size"), 170 new BMessage(msg_shrink),'-'); 171 menu->AddItem(menuItem); 172 menuItem = new BMenuItem(B_TRANSLATE("Increase window size"), 173 new BMessage(msg_grow),'+'); 174 menu->AddItem(menuItem); 175 menuItem = new BMenuItem(B_TRANSLATE("Decrease pixel size"), 176 new BMessage(msg_shrink_pixel),','); 177 menu->AddItem(menuItem); 178 menuItem = new BMenuItem(B_TRANSLATE("Increase pixel size"), 179 new BMessage(msg_grow_pixel),'.'); 180 menu->AddItem(menuItem); 181 } 182 183 184 // #pragma mark - 185 186 187 // pass in pixelCount to maintain backward compatibility of setting 188 // the pixelcount from the command line 189 TApp::TApp(int32 pixelCount) 190 : BApplication("application/x-vnd.Haiku-Magnify") 191 { 192 TWindow* magWindow = new TWindow(pixelCount); 193 magWindow->Show(); 194 } 195 196 197 // #pragma mark - 198 199 200 TWindow::TWindow(int32 pixelCount) 201 : 202 BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Magnify"), 203 B_TITLED_WINDOW, B_OUTLINE_RESIZE) 204 { 205 GetPrefs(pixelCount); 206 207 // add info view 208 BRect infoRect(Bounds()); 209 infoRect.InsetBy(-1, -1); 210 fInfo = new TInfoView(infoRect); 211 AddChild(fInfo); 212 213 fFontHeight = FontHeight(fInfo, true); 214 fInfoHeight = (fFontHeight * 2) + (3 * 5); 215 216 BRect fbRect(0, 0, (fHPixelCount*fPixelSize), (fHPixelCount*fPixelSize)); 217 if (InfoIsShowing()) 218 fbRect.OffsetBy(10, fInfoHeight); 219 fFatBits = new TMagnify(fbRect, this); 220 fInfo->AddChild(fFatBits); 221 222 fFatBits->SetSelection(fShowInfo); 223 fInfo->SetMagView(fFatBits); 224 225 ResizeWindow(fHPixelCount, fVPixelCount); 226 UpdateInfoBarOnResize(); 227 228 AddShortcut('S', B_COMMAND_KEY, new BMessage(msg_save)); 229 AddShortcut('C', B_COMMAND_KEY, new BMessage(msg_copy_image)); 230 AddShortcut('T', B_COMMAND_KEY, new BMessage(msg_show_info)); 231 AddShortcut('H', B_COMMAND_KEY, new BMessage(msg_add_cross_hair)); 232 AddShortcut('H', B_SHIFT_KEY, new BMessage(msg_remove_cross_hair)); 233 AddShortcut('G', B_COMMAND_KEY, new BMessage(msg_toggle_grid)); 234 AddShortcut('F', B_COMMAND_KEY, new BMessage(msg_freeze)); 235 AddShortcut('I', B_COMMAND_KEY, new BMessage(msg_stick)); 236 AddShortcut('-', B_COMMAND_KEY, new BMessage(msg_shrink)); 237 AddShortcut('=', B_COMMAND_KEY, new BMessage(msg_grow)); 238 AddShortcut('/', B_COMMAND_KEY, new BMessage(msg_make_square)); 239 AddShortcut(',', B_COMMAND_KEY, new BMessage(msg_shrink_pixel)); 240 AddShortcut('.', B_COMMAND_KEY, new BMessage(msg_grow_pixel)); 241 } 242 243 244 TWindow::~TWindow() 245 { 246 } 247 248 249 void 250 TWindow::MessageReceived(BMessage* m) 251 { 252 bool active = fFatBits->Active(); 253 254 switch (m->what) { 255 case msg_show_info: 256 if (active) { 257 fInfoBarState = !fInfoBarState; 258 ShowInfo(!fShowInfo); 259 } 260 break; 261 262 case msg_toggle_grid: 263 if (active) 264 SetGrid(!fShowGrid); 265 break; 266 267 case msg_grow: 268 if (active) 269 ResizeWindow(true); 270 break; 271 case msg_shrink: 272 if (active) 273 ResizeWindow(false); 274 break; 275 case msg_make_square: 276 if (active) { 277 if (fHPixelCount == fVPixelCount) 278 break; 279 int32 big = (fHPixelCount > fVPixelCount) ? fHPixelCount : fVPixelCount; 280 ResizeWindow(big, big); 281 } 282 break; 283 284 case msg_shrink_pixel: 285 if (active) 286 SetPixelSize(false); 287 break; 288 case msg_grow_pixel: 289 if (active) 290 SetPixelSize(true); 291 break; 292 293 case msg_add_cross_hair: 294 if (active && fShowInfo) 295 AddCrossHair(); 296 break; 297 case msg_remove_cross_hair: 298 if (active && fShowInfo) 299 RemoveCrossHair(); 300 break; 301 302 case msg_freeze: 303 if (active) 304 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE); 305 else 306 SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE); 307 308 fFatBits->MakeActive(!fFatBits->Active()); 309 break; 310 311 case msg_stick: 312 fFatBits->MakeSticked(!fFatBits->Sticked()); 313 break; 314 315 case msg_save: { 316 // freeze the image here, unfreeze after dump or cancel 317 fFatBits->StartSave(); 318 319 BMessenger messenger(this); 320 BMessage message(msg_dump); 321 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false, 322 &message); 323 fSavePanel->SetSaveText("Bitmaps.h"); 324 fSavePanel->Show(); 325 } break; 326 case msg_dump: 327 { 328 delete fSavePanel; 329 330 entry_ref dirRef; 331 char* name; 332 m->FindRef("directory", &dirRef); 333 m->FindString((const char*)"name",(const char**) &name); 334 335 fFatBits->SaveImage(&dirRef, name); 336 } 337 break; 338 case B_CANCEL: 339 // image is frozen before the FilePanel is shown 340 fFatBits->EndSave(); 341 break; 342 343 case msg_copy_image: 344 fFatBits->CopyImage(); 345 break; 346 default: 347 BWindow::MessageReceived(m); 348 break; 349 } 350 } 351 352 353 bool 354 TWindow::QuitRequested() 355 { 356 SetPrefs(); 357 be_app->PostMessage(B_QUIT_REQUESTED); 358 return true; 359 } 360 361 362 void 363 TWindow::GetPrefs(int32 overridePixelCount) 364 { 365 BPath path; 366 char name[8]; 367 float version; 368 bool haveLoc=false; 369 BPoint loc; 370 bool showGrid = kDefaultShowGrid; 371 bool showInfo = kDefaultShowInfo; 372 bool ch1Showing=false; 373 bool ch2Showing=false; 374 int32 hPixelCount = kDefaultPixelCount; 375 int32 vPixelCount = kDefaultPixelCount; 376 int32 pixelSize = kDefaultPixelSize; 377 378 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 379 int ref = -1; 380 path.Append(kPrefsFileName); 381 if ((ref = open(path.Path(), 0)) >= 0) { 382 if (read(ref, name, 7) != 7) 383 goto ALMOST_DONE; 384 385 name[7] = 0; 386 if (strcmp(name, kAppName) != 0) 387 goto ALMOST_DONE; 388 389 read(ref, &version, sizeof(float)); 390 391 if (read(ref, &loc, sizeof(BPoint)) != sizeof(BPoint)) 392 goto ALMOST_DONE; 393 else 394 haveLoc = true; 395 396 if (read(ref, &showGrid, sizeof(bool)) != sizeof(bool)) { 397 showGrid = kDefaultShowGrid; 398 goto ALMOST_DONE; 399 } 400 401 if (read(ref, &showInfo, sizeof(bool)) != sizeof(bool)) { 402 showInfo = kDefaultShowInfo; 403 goto ALMOST_DONE; 404 } 405 406 if (read(ref, &ch1Showing, sizeof(bool)) != sizeof(bool)) { 407 ch1Showing = false; 408 goto ALMOST_DONE; 409 } 410 411 if (read(ref, &ch2Showing, sizeof(bool)) != sizeof(bool)) { 412 ch2Showing = false; 413 goto ALMOST_DONE; 414 } 415 416 if (read(ref, &hPixelCount, sizeof(int32)) != sizeof(int32)) { 417 hPixelCount = kDefaultPixelCount; 418 goto ALMOST_DONE; 419 } 420 if (read(ref, &vPixelCount, sizeof(int32)) != sizeof(int32)) { 421 vPixelCount = kDefaultPixelCount; 422 goto ALMOST_DONE; 423 } 424 425 if (read(ref, &pixelSize, sizeof(int32)) != sizeof(int32)) { 426 pixelSize = kDefaultPixelSize; 427 goto ALMOST_DONE; 428 } 429 430 ALMOST_DONE: // clean up and try to position the window 431 close(ref); 432 433 if (haveLoc && BScreen(B_MAIN_SCREEN_ID).Frame().Contains(loc)) { 434 MoveTo(loc); 435 goto DONE; 436 } 437 } 438 } 439 440 // if prefs dont yet exist or the window is not onscreen, center the window 441 CenterOnScreen(); 442 443 // set all the settings to defaults if we get here 444 DONE: 445 fShowGrid = showGrid; 446 fShowInfo = showInfo; 447 fInfoBarState = showInfo; 448 fHPixelCount = (overridePixelCount == -1) ? hPixelCount : overridePixelCount; 449 fVPixelCount = (overridePixelCount == -1) ? vPixelCount : overridePixelCount; 450 fPixelSize = pixelSize; 451 } 452 453 454 void 455 TWindow::SetPrefs() 456 { 457 BPath path; 458 459 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) { 460 long ref; 461 462 path.Append (kPrefsFileName); 463 if ((ref = creat(path.Path(), S_IRUSR | S_IWUSR)) >= 0) { 464 float version = kCurrentVersion; 465 466 lseek (ref, 0, SEEK_SET); 467 write(ref, kAppName, 7); 468 write(ref, &version, sizeof(float)); 469 470 BPoint loc = Frame().LeftTop(); 471 write(ref, &loc, sizeof(BPoint)); 472 473 write(ref, &fShowGrid, sizeof(bool)); 474 write(ref, &fShowInfo, sizeof(bool)); 475 bool ch1, ch2; 476 CrossHairsShowing(&ch1, &ch2); 477 write(ref, &ch1, sizeof(bool)); 478 write(ref, &ch2, sizeof(bool)); 479 480 write(ref, &fHPixelCount, sizeof(int32)); 481 write(ref, &fVPixelCount, sizeof(int32)); 482 write(ref, &fPixelSize, sizeof(int32)); 483 484 close(ref); 485 } 486 } 487 } 488 489 490 void 491 TWindow::FrameResized(float w, float h) 492 { 493 CalcViewablePixels(); 494 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid()); 495 UpdateInfoBarOnResize(); 496 } 497 498 499 void 500 TWindow::ScreenChanged(BRect screenSize, color_space depth) 501 { 502 BWindow::ScreenChanged(screenSize, depth); 503 // reset all bitmaps 504 fFatBits->ScreenChanged(screenSize,depth); 505 } 506 507 508 void 509 TWindow::Minimize(bool m) 510 { 511 BWindow::Minimize(m); 512 } 513 514 515 void 516 TWindow::Zoom(BPoint /*position*/, float /*width*/, float /*height*/) 517 { 518 if (fFatBits->Active()) 519 ShowInfo(!fShowInfo); 520 } 521 522 523 void 524 TWindow::CalcViewablePixels() 525 { 526 float w = Bounds().Width(); 527 float h = Bounds().Height(); 528 529 if (InfoIsShowing()) { 530 w -= 20; // remove the gutter 531 h = h-fInfoHeight-10; // remove info and gutter 532 } 533 534 bool ch1, ch2; 535 fFatBits->CrossHairsShowing(&ch1, &ch2); 536 if (ch1) 537 h -= fFontHeight; 538 if (ch2) 539 h -= fFontHeight + 5; 540 541 fHPixelCount = (int32)w / fPixelSize; // calc h pixels 542 if (fHPixelCount < 16) 543 fHPixelCount = 16; 544 545 fVPixelCount = (int32)h / fPixelSize; // calc v pixels 546 if (fVPixelCount < 4) 547 fVPixelCount = 4; 548 } 549 550 551 void 552 TWindow::GetPreferredSize(float* width, float* height) 553 { 554 *width = fHPixelCount * fPixelSize; // calc window width 555 *height = fVPixelCount * fPixelSize; // calc window height 556 if (InfoIsShowing()) { 557 *width += 20; 558 *height += fInfoHeight + 10; 559 } 560 561 bool ch1, ch2; 562 fFatBits->CrossHairsShowing(&ch1, &ch2); 563 if (ch1) 564 *height += fFontHeight; 565 if (ch2) 566 *height += fFontHeight + 5; 567 } 568 569 570 void 571 TWindow::ResizeWindow(int32 hPixelCount, int32 vPixelCount) 572 { 573 fHPixelCount = hPixelCount; 574 fVPixelCount = vPixelCount; 575 576 float width, height; 577 GetPreferredSize(&width, &height); 578 579 ResizeTo(width, height); 580 } 581 582 583 void 584 TWindow::ResizeWindow(bool direction) 585 { 586 int32 x = fHPixelCount; 587 int32 y = fVPixelCount; 588 589 if (direction) { 590 x += 4; 591 y += 4; 592 } else { 593 x -= 4; 594 y -= 4; 595 } 596 597 if (x < 4) 598 x = 4; 599 600 if (y < 4) 601 y = 4; 602 603 ResizeWindow(x, y); 604 } 605 606 607 void 608 TWindow::SetGrid(bool s) 609 { 610 if (s == fShowGrid) 611 return; 612 613 fShowGrid = s; 614 fFatBits->SetUpdate(true); 615 } 616 617 618 bool 619 TWindow::ShowGrid() 620 { 621 return fShowGrid; 622 } 623 624 625 void 626 TWindow::ShowInfo(bool i) 627 { 628 if (i == fShowInfo) 629 return; 630 631 fShowInfo = i; 632 633 if (fShowInfo) 634 fFatBits->MoveTo(10, fInfoHeight); 635 else { 636 fFatBits->MoveTo(1,1); 637 fFatBits->SetCrossHairsShowing(false, false); 638 } 639 640 fFatBits->SetSelection(fShowInfo); 641 ResizeWindow(fHPixelCount, fVPixelCount); 642 fInfo->SetInfoTextVisible(i); 643 } 644 645 646 bool 647 TWindow::InfoIsShowing() 648 { 649 return fShowInfo; 650 } 651 652 653 void 654 TWindow::UpdateInfo() 655 { 656 fInfo->Invalidate(); 657 } 658 659 660 void 661 TWindow::UpdateInfoBarOnResize() 662 { 663 float infoWidth, infoHeight; 664 fInfo->GetPreferredSize(&infoWidth, &infoHeight); 665 666 if (infoWidth > Bounds().Width() 667 || infoHeight > Bounds().Height()) { 668 ShowInfo(false); 669 } else { 670 ShowInfo(true); 671 } 672 } 673 674 675 void 676 TWindow::AddCrossHair() 677 { 678 fFatBits->AddCrossHair(); 679 680 // crosshair info needs to be added 681 // window resizes accordingly 682 float width; 683 float height; 684 GetPreferredSize(&width, &height); 685 ResizeTo(width, height); 686 } 687 688 689 void 690 TWindow::RemoveCrossHair() 691 { 692 fFatBits->RemoveCrossHair(); 693 694 // crosshair info needs to be removed 695 // window resizes accordingly 696 float width; 697 float height; 698 GetPreferredSize(&width, &height); 699 ResizeTo(width, height); 700 } 701 702 703 void 704 TWindow::CrossHairsShowing(bool* ch1, bool* ch2) 705 { 706 fFatBits->CrossHairsShowing(ch1, ch2); 707 } 708 709 710 void 711 TWindow::PixelCount(int32* h, int32 *v) 712 { 713 *h = fHPixelCount; 714 *v = fVPixelCount; 715 } 716 717 718 void 719 TWindow::SetPixelSize(int32 s) 720 { 721 if (s == fPixelSize) 722 return; 723 724 fPixelSize = s; 725 // resize window 726 // tell info that size has changed 727 // tell mag that size has changed 728 729 CalcViewablePixels(); 730 ResizeWindow(fHPixelCount, fVPixelCount); 731 } 732 733 734 void 735 TWindow::SetPixelSize(bool d) 736 { 737 if (d) { // grow 738 fPixelSize++; 739 if (fPixelSize > 16) 740 fPixelSize = 16; 741 } else { 742 fPixelSize--; 743 if (fPixelSize < 1) 744 fPixelSize = 1; 745 } 746 747 float w = Bounds().Width(); 748 float h = Bounds().Height(); 749 CalcViewablePixels(); 750 ResizeWindow(fHPixelCount, fVPixelCount); 751 752 // the window might not actually change in size 753 // in that case force the buffers to the new dimension 754 if (w == Bounds().Width() && h == Bounds().Height()) 755 fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid()); 756 } 757 758 759 int32 760 TWindow::PixelSize() 761 { 762 return fPixelSize; 763 } 764 765 766 #undef B_TRANSLATION_CONTEXT 767 #define B_TRANSLATION_CONTEXT "Magnify-Main" 768 769 770 bool 771 TWindow::IsActive() 772 { 773 return fFatBits->Active(); 774 } 775 776 777 // #pragma mark - 778 779 780 TInfoView::TInfoView(BRect frame) 781 : BBox(frame, "rgb", B_FOLLOW_ALL, 782 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS, 783 B_NO_BORDER) 784 { 785 SetFont(be_plain_font); 786 fFontHeight = FontHeight(this, true); 787 fMagView = NULL; 788 789 fSelectionColor = kBlack; 790 fCH1Loc.x = fCH1Loc.y = fCH2Loc.x = fCH2Loc.y = 0; 791 792 fInfoStr[0] = 0; 793 fRGBStr[0] = 0; 794 fCH1Str[0] = 0; 795 fCH2Str[0] = 0; 796 797 fInfoTextVisible = true; 798 } 799 800 801 TInfoView::~TInfoView() 802 { 803 } 804 805 806 void 807 TInfoView::AttachedToWindow() 808 { 809 BBox::AttachedToWindow(); 810 811 dynamic_cast<TWindow*>(Window())->PixelCount(&fHPixelCount, &fVPixelCount); 812 fPixelSize = dynamic_cast<TWindow*>(Window())->PixelSize(); 813 814 AddMenu(); 815 } 816 817 818 void 819 TInfoView::Draw(BRect updateRect) 820 { 821 PushState(); 822 SetLowColor(ViewColor()); 823 824 BRect invalRect; 825 826 int32 hPixelCount, vPixelCount; 827 dynamic_cast<TWindow*>(Window())->PixelCount(&hPixelCount, &vPixelCount); 828 int32 pixelSize = dynamic_cast<TWindow*>(Window())->PixelSize(); 829 830 MovePenTo(10, fFontHeight + 5); 831 832 static BMessageFormat format(B_TRANSLATE("%width x %height @ {0, plural, " 833 "one{# pixel/pixel} other{# pixels/pixel}}")); 834 835 BString dimensionsInfo; 836 format.Format(dimensionsInfo, pixelSize); 837 838 BString rep; 839 rep << hPixelCount; 840 dimensionsInfo.ReplaceAll("%width", rep); 841 rep = ""; 842 rep << vPixelCount; 843 dimensionsInfo.ReplaceAll("%height", rep); 844 845 invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7); 846 SetHighColor(ViewColor()); 847 FillRect(invalRect); 848 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 849 strcpy(fInfoStr, dimensionsInfo); 850 if (fInfoTextVisible) 851 DrawString(fInfoStr); 852 853 rgb_color color = { 0, 0, 0, 255 }; 854 if (fMagView) 855 color = fMagView->SelectionColor(); 856 char str[64]; 857 snprintf(str, sizeof(str), "R: %i G: %i B: %i (#%02x%02x%02x)", 858 color.red, color.green, color.blue, color.red, color.green, color.blue); 859 860 MovePenTo(10, fFontHeight*2+5); 861 invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7); 862 SetHighColor(ViewColor()); 863 FillRect(invalRect); 864 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 865 strcpy(fRGBStr,str); 866 if (fInfoTextVisible) 867 DrawString(fRGBStr); 868 869 bool ch1Showing, ch2Showing; 870 dynamic_cast<TWindow*>(Window())->CrossHairsShowing(&ch1Showing, &ch2Showing); 871 872 if (fMagView) { 873 BPoint pt1(fMagView->CrossHair1Loc()); 874 BPoint pt2(fMagView->CrossHair2Loc()); 875 876 float h = Bounds().Height(); 877 if (ch2Showing) { 878 MovePenTo(10, h-12); 879 sprintf(str, "2) x: %" B_PRIi32 " y: %" B_PRIi32 " y: %d", 880 (int32)pt2.x, (int32)pt2.y, abs((int)(pt1.y - pt2.y))); 881 invalRect.Set(10, h-12-fFontHeight, 10 + StringWidth(fCH2Str), h-10); 882 SetHighColor(ViewColor()); 883 FillRect(invalRect); 884 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 885 strcpy(fCH2Str,str); 886 if (fInfoTextVisible) 887 DrawString(fCH2Str); 888 } 889 890 if (ch1Showing && ch2Showing) { 891 MovePenTo(10, h-10-fFontHeight-2); 892 sprintf(str, "1) x: %" B_PRIi32 " y: %" B_PRIi32 " x: %d", 893 (int32)pt1.x, (int32)pt1.y, abs((int)(pt1.x - pt2.x))); 894 invalRect.Set(10, h-10-2*fFontHeight-2, 10 + StringWidth(fCH1Str), h-10-fFontHeight); 895 SetHighColor(ViewColor()); 896 FillRect(invalRect); 897 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 898 strcpy(fCH1Str,str); 899 if (fInfoTextVisible) 900 DrawString(fCH1Str); 901 } else if (ch1Showing) { 902 MovePenTo(10, h-10); 903 sprintf(str, "x: %" B_PRIi32 " y: %" B_PRIi32, (int32)pt1.x, (int32)pt1.y); 904 invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8); 905 SetHighColor(ViewColor()); 906 FillRect(invalRect); 907 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 908 strcpy(fCH1Str,str); 909 if (fInfoTextVisible) 910 DrawString(fCH1Str); 911 } 912 } 913 914 PopState(); 915 } 916 917 918 void 919 TInfoView::FrameResized(float width, float height) 920 { 921 BBox::FrameResized(width, height); 922 } 923 924 925 void 926 TInfoView::AddMenu() 927 { 928 fMenu = new TMenu(dynamic_cast<TWindow*>(Window()), ""); 929 BuildInfoMenu(fMenu); 930 931 BRect r(Bounds().Width()-27, 11, Bounds().Width()-11, 27); 932 fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true, 933 B_FOLLOW_RIGHT | B_FOLLOW_TOP); 934 AddChild(fPopUp); 935 } 936 937 938 void 939 TInfoView::SetMagView(TMagnify* magView) 940 { 941 fMagView = magView; 942 } 943 944 945 // #pragma mark - 946 947 948 TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout) 949 : BMenu(title, layout), 950 fMainWindow(mainWindow) 951 { 952 } 953 954 955 TMenu::~TMenu() 956 { 957 } 958 959 960 void 961 TMenu::AttachedToWindow() 962 { 963 bool state = true; 964 if (fMainWindow) 965 state = fMainWindow->IsActive(); 966 967 BMenuItem* menuItem = FindItem(B_TRANSLATE("Hide/Show info")); 968 if (menuItem) 969 menuItem->SetEnabled(state); 970 menuItem = FindItem(B_TRANSLATE("Add a crosshair")); 971 if (menuItem) 972 menuItem->SetEnabled(state); 973 menuItem = FindItem(B_TRANSLATE("Remove a crosshair")); 974 if (menuItem) 975 menuItem->SetEnabled(state); 976 menuItem = FindItem(B_TRANSLATE("Hide/Show grid")); 977 if (menuItem) 978 menuItem->SetEnabled(state); 979 menuItem = FindItem(B_TRANSLATE("Make square")); 980 if (menuItem) 981 menuItem->SetEnabled(state); 982 menuItem = FindItem(B_TRANSLATE("Decrease window size")); 983 if (menuItem) 984 menuItem->SetEnabled(state); 985 menuItem = FindItem(B_TRANSLATE("Increase window size")); 986 if (menuItem) 987 menuItem->SetEnabled(state); 988 menuItem = FindItem(B_TRANSLATE("Decrease pixel size")); 989 if (menuItem) 990 menuItem->SetEnabled(state); 991 menuItem = FindItem(B_TRANSLATE("Increase pixel size")); 992 if (menuItem) 993 menuItem->SetEnabled(state); 994 995 BMenu::AttachedToWindow(); 996 } 997 998 999 void 1000 TInfoView::GetPreferredSize(float* _width, float* _height) 1001 { 1002 if (_width) { 1003 float str1Width = StringWidth(fCH1Str) 1004 + StringWidth(fCH2Str) 1005 + StringWidth(fRGBStr) 1006 + 30; 1007 float str2Width = StringWidth(fInfoStr) + 30; 1008 *_width = str1Width > str2Width ? str1Width : str2Width; 1009 } 1010 1011 if (_height) 1012 *_height = fFontHeight * 2 + 10; 1013 } 1014 1015 1016 bool 1017 TInfoView::IsInfoTextVisible() 1018 { 1019 return fInfoTextVisible; 1020 } 1021 1022 1023 void 1024 TInfoView::SetInfoTextVisible(bool visible) 1025 { 1026 fInfoTextVisible = visible; 1027 Draw(Bounds()); 1028 } 1029 1030 1031 // #pragma mark - 1032 1033 1034 TMagnify::TMagnify(BRect r, TWindow* parent) 1035 : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1036 fNeedToUpdate(true), 1037 fThread(-1), 1038 fActive(true), 1039 fImageBuf(NULL), 1040 fImageView(NULL), 1041 fLastLoc(-1, -1), 1042 fSelection(-1), 1043 fShowSelection(false), 1044 fSelectionLoc(0, 0), 1045 fShowCrossHair1(false), 1046 fCrossHair1(-1, -1), 1047 fShowCrossHair2(false), 1048 fCrossHair2(-1, -1), 1049 fParent(parent), 1050 fStickCoordinates(false) 1051 { 1052 SetViewColor(B_TRANSPARENT_32_BIT); 1053 } 1054 1055 1056 TMagnify::~TMagnify() 1057 { 1058 kill_thread(fThread); 1059 delete fImageBuf; 1060 } 1061 1062 1063 void 1064 TMagnify::AttachedToWindow() 1065 { 1066 int32 width, height; 1067 fParent->PixelCount(&width, &height); 1068 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1069 1070 fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask", 1071 B_NORMAL_PRIORITY, this); 1072 1073 resume_thread(fThread); 1074 1075 MakeFocus(); 1076 } 1077 1078 1079 void 1080 TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount, 1081 int32 pixelSize, bool showGrid) 1082 { 1083 color_space colorSpace = BScreen(Window()).ColorSpace(); 1084 1085 BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1); 1086 if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height()) 1087 ResizeTo(r.Width(), r.Height()); 1088 1089 if (fImageView) { 1090 fImageBuf->Lock(); 1091 fImageView->RemoveSelf(); 1092 fImageBuf->Unlock(); 1093 1094 fImageView->Resize((int32)r.Width(), (int32)r.Height()); 1095 fImageView->SetSpace(colorSpace); 1096 } else 1097 fImageView = new TOSMagnify(r, this, colorSpace); 1098 1099 delete fImageBuf; 1100 fImageBuf = new BBitmap(r, colorSpace, true); 1101 fImageBuf->Lock(); 1102 fImageBuf->AddChild(fImageView); 1103 fImageBuf->Unlock(); 1104 } 1105 1106 1107 void 1108 TMagnify::Draw(BRect) 1109 { 1110 BRect bounds(Bounds()); 1111 DrawBitmap(fImageBuf, bounds, bounds); 1112 static_cast<TWindow*>(Window())->UpdateInfo(); 1113 } 1114 1115 1116 void 1117 TMagnify::KeyDown(const char *key, int32 numBytes) 1118 { 1119 if (!fShowSelection) 1120 BView::KeyDown(key, numBytes); 1121 1122 uint32 mods = modifiers(); 1123 1124 switch (key[0]) { 1125 case B_TAB: 1126 if (fShowCrossHair1) { 1127 fSelection++; 1128 1129 if (fShowCrossHair2) { 1130 if (fSelection > 2) 1131 fSelection = 0; 1132 } else if (fShowCrossHair1) { 1133 if (fSelection > 1) 1134 fSelection = 0; 1135 } 1136 fNeedToUpdate = true; 1137 Invalidate(); 1138 } 1139 break; 1140 1141 case B_LEFT_ARROW: 1142 if (mods & B_OPTION_KEY) 1143 NudgeMouse(-1,0); 1144 else 1145 MoveSelection(-1,0); 1146 break; 1147 case B_RIGHT_ARROW: 1148 if (mods & B_OPTION_KEY) 1149 NudgeMouse(1, 0); 1150 else 1151 MoveSelection(1,0); 1152 break; 1153 case B_UP_ARROW: 1154 if (mods & B_OPTION_KEY) 1155 NudgeMouse(0, -1); 1156 else 1157 MoveSelection(0,-1); 1158 break; 1159 case B_DOWN_ARROW: 1160 if (mods & B_OPTION_KEY) 1161 NudgeMouse(0, 1); 1162 else 1163 MoveSelection(0,1); 1164 break; 1165 1166 default: 1167 BView::KeyDown(key,numBytes); 1168 break; 1169 } 1170 } 1171 1172 1173 void 1174 TMagnify::FrameResized(float newW, float newH) 1175 { 1176 int32 w, h; 1177 PixelCount(&w, &h); 1178 1179 if (fSelectionLoc.x >= w) 1180 fSelectionLoc.x = 0; 1181 if (fSelectionLoc.y >= h) 1182 fSelectionLoc.y = 0; 1183 1184 if (fShowCrossHair1) { 1185 if (fCrossHair1.x >= w) { 1186 fCrossHair1.x = fSelectionLoc.x + 2; 1187 if (fCrossHair1.x >= w) 1188 fCrossHair1.x = 0; 1189 } 1190 if (fCrossHair1.y >= h) { 1191 fCrossHair1.y = fSelectionLoc.y + 2; 1192 if (fCrossHair1.y >= h) 1193 fCrossHair1.y = 0; 1194 } 1195 1196 if (fShowCrossHair2) { 1197 if (fCrossHair2.x >= w) { 1198 fCrossHair2.x = fCrossHair1.x + 2; 1199 if (fCrossHair2.x >= w) 1200 fCrossHair2.x = 0; 1201 } 1202 if (fCrossHair2.y >= h) { 1203 fCrossHair2.y = fCrossHair1.y + 2; 1204 if (fCrossHair2.y >= h) 1205 fCrossHair2.y = 0; 1206 } 1207 } 1208 } 1209 } 1210 1211 1212 void 1213 TMagnify::MouseDown(BPoint where) 1214 { 1215 BMessage *currentMsg = Window()->CurrentMessage(); 1216 if (currentMsg->what == B_MOUSE_DOWN) { 1217 uint32 buttons = 0; 1218 currentMsg->FindInt32("buttons", (int32 *)&buttons); 1219 1220 uint32 modifiers = 0; 1221 currentMsg->FindInt32("modifiers", (int32 *)&modifiers); 1222 1223 if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) { 1224 // secondary button was clicked or control key was down, show menu and return 1225 1226 BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info")); 1227 menu->SetFont(be_plain_font); 1228 BuildInfoMenu(menu); 1229 1230 BMenuItem *selected = menu->Go(ConvertToScreen(where)); 1231 if (selected) 1232 Window()->PostMessage(selected->Message()->what); 1233 delete menu; 1234 return; 1235 } 1236 1237 // add a mousedown looper here 1238 1239 int32 pixelSize = PixelSize(); 1240 float x = where.x / pixelSize; 1241 float y = where.y / pixelSize; 1242 1243 MoveSelectionTo(x, y); 1244 1245 // draw the frozen image 1246 // update the info region 1247 1248 fNeedToUpdate = true; 1249 Invalidate(); 1250 } 1251 } 1252 1253 1254 void 1255 TMagnify::ScreenChanged(BRect, color_space) 1256 { 1257 int32 width, height; 1258 fParent->PixelCount(&width, &height); 1259 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1260 } 1261 1262 1263 void 1264 TMagnify::SetSelection(bool state) 1265 { 1266 if (fShowSelection == state) 1267 return; 1268 1269 fShowSelection = state; 1270 fSelection = 0; 1271 Invalidate(); 1272 } 1273 1274 1275 void 1276 TMagnify::MoveSelection(int32 x, int32 y) 1277 { 1278 if (!fShowSelection) 1279 return; 1280 1281 int32 xCount, yCount; 1282 PixelCount(&xCount, &yCount); 1283 1284 float xloc, yloc; 1285 if (fSelection == 0) { 1286 xloc = fSelectionLoc.x; 1287 yloc = fSelectionLoc.y; 1288 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1289 fSelectionLoc.x = xloc; 1290 fSelectionLoc.y = yloc; 1291 } else if (fSelection == 1) { 1292 xloc = fCrossHair1.x; 1293 yloc = fCrossHair1.y; 1294 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1295 fCrossHair1.x = xloc; 1296 fCrossHair1.y = yloc; 1297 } else if (fSelection == 2) { 1298 xloc = fCrossHair2.x; 1299 yloc = fCrossHair2.y; 1300 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1301 fCrossHair2.x = xloc; 1302 fCrossHair2.y = yloc; 1303 } 1304 1305 fNeedToUpdate = true; 1306 Invalidate(); 1307 } 1308 1309 1310 void 1311 TMagnify::MoveSelectionTo(int32 x, int32 y) 1312 { 1313 if (!fShowSelection) 1314 return; 1315 1316 int32 xCount, yCount; 1317 PixelCount(&xCount, &yCount); 1318 if (x >= xCount) 1319 x = 0; 1320 if (y >= yCount) 1321 y = 0; 1322 1323 if (fSelection == 0) { 1324 fSelectionLoc.x = x; 1325 fSelectionLoc.y = y; 1326 } else if (fSelection == 1) { 1327 fCrossHair1.x = x; 1328 fCrossHair1.y = y; 1329 } else if (fSelection == 2) { 1330 fCrossHair2.x = x; 1331 fCrossHair2.y = y; 1332 } 1333 1334 fNeedToUpdate = true; 1335 Invalidate(); //Draw(Bounds()); 1336 } 1337 1338 1339 void 1340 TMagnify::ShowSelection() 1341 { 1342 } 1343 1344 1345 short 1346 TMagnify::Selection() 1347 { 1348 return fSelection; 1349 } 1350 1351 1352 bool 1353 TMagnify::SelectionIsShowing() 1354 { 1355 return fShowSelection; 1356 } 1357 1358 1359 void 1360 TMagnify::SelectionLoc(float* x, float* y) 1361 { 1362 *x = fSelectionLoc.x; 1363 *y = fSelectionLoc.y; 1364 } 1365 1366 1367 void 1368 TMagnify::SetSelectionLoc(float x, float y) 1369 { 1370 fSelectionLoc.x = x; 1371 fSelectionLoc.y = y; 1372 } 1373 1374 1375 rgb_color 1376 TMagnify::SelectionColor() 1377 { 1378 return fImageView->ColorAtSelection(); 1379 } 1380 1381 1382 void 1383 TMagnify::CrossHair1Loc(float* x, float* y) 1384 { 1385 *x = fCrossHair1.x; 1386 *y = fCrossHair1.y; 1387 } 1388 1389 1390 void 1391 TMagnify::CrossHair2Loc(float* x, float* y) 1392 { 1393 *x = fCrossHair2.x; 1394 *y = fCrossHair2.y; 1395 } 1396 1397 1398 BPoint 1399 TMagnify::CrossHair1Loc() 1400 { 1401 return fCrossHair1; 1402 } 1403 1404 1405 BPoint 1406 TMagnify::CrossHair2Loc() 1407 { 1408 return fCrossHair2; 1409 } 1410 1411 1412 void 1413 TMagnify::NudgeMouse(float x, float y) 1414 { 1415 BPoint loc; 1416 uint32 button; 1417 1418 GetMouse(&loc, &button); 1419 ConvertToScreen(&loc); 1420 loc.x += x; 1421 loc.y += y; 1422 1423 set_mouse_position((int32)loc.x, (int32)loc.y); 1424 } 1425 1426 1427 void 1428 TMagnify::WindowActivated(bool active) 1429 { 1430 if (active) 1431 MakeFocus(); 1432 } 1433 1434 1435 status_t 1436 TMagnify::MagnifyTask(void *arg) 1437 { 1438 TMagnify* view = (TMagnify*)arg; 1439 1440 // static data members can't access members, methods without 1441 // a pointer to an instance of the class 1442 TWindow* window = (TWindow*)view->Window(); 1443 1444 while (true) { 1445 if (window->Lock()) { 1446 if (view->NeedToUpdate() || view->Active()) 1447 view->Update(view->NeedToUpdate()); 1448 1449 window->Unlock(); 1450 } 1451 snooze(35000); 1452 } 1453 1454 return B_NO_ERROR; 1455 } 1456 1457 1458 void 1459 TMagnify::Update(bool force) 1460 { 1461 BPoint loc; 1462 uint32 button; 1463 static long counter = 0; 1464 1465 if (!fStickCoordinates) { 1466 GetMouse(&loc, &button); 1467 ConvertToScreen(&loc); 1468 } else 1469 loc = fLastLoc; 1470 1471 if (force || fLastLoc != loc || counter++ % 35 == 0) { 1472 if (fImageView->CreateImage(loc, force)) 1473 Invalidate(); 1474 1475 counter = 0; 1476 if (force) 1477 SetUpdate(false); 1478 } 1479 fLastLoc = loc; 1480 } 1481 1482 1483 bool 1484 TMagnify::NeedToUpdate() 1485 { 1486 return fNeedToUpdate; 1487 } 1488 1489 1490 void 1491 TMagnify::SetUpdate(bool s) 1492 { 1493 fNeedToUpdate = s; 1494 } 1495 1496 1497 void 1498 TMagnify::CopyImage() 1499 { 1500 StartSave(); 1501 be_clipboard->Lock(); 1502 be_clipboard->Clear(); 1503 1504 BMessage *message = be_clipboard->Data(); 1505 if (!message) { 1506 printf(B_TRANSLATE_CONTEXT("no clip msg\n", 1507 "In console, when clipboard is empty after clicking Copy image")); 1508 return; 1509 } 1510 1511 BMessage *embeddedBitmap = new BMessage(); 1512 (fImageView->Bitmap())->Archive(embeddedBitmap,false); 1513 status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap); 1514 if (err == B_OK) 1515 err = message->AddRect("rect", fImageView->Bitmap()->Bounds()); 1516 if (err == B_OK) 1517 be_clipboard->Commit(); 1518 1519 be_clipboard->Unlock(); 1520 EndSave(); 1521 } 1522 1523 1524 void 1525 TMagnify::AddCrossHair() 1526 { 1527 if (fShowCrossHair1 && fShowCrossHair2) 1528 return; 1529 1530 int32 w, h; 1531 PixelCount(&w, &h); 1532 1533 if (fShowCrossHair1) { 1534 fSelection = 2; 1535 fShowCrossHair2 = true; 1536 fCrossHair2.x = fCrossHair1.x + 2; 1537 if (fCrossHair2.x >= w) 1538 fCrossHair2.x = 0; 1539 fCrossHair2.y = fCrossHair1.y + 2; 1540 if (fCrossHair2.y >= h) 1541 fCrossHair2.y = 0; 1542 } else { 1543 fSelection = 1; 1544 fShowCrossHair1 = true; 1545 fCrossHair1.x = fSelectionLoc.x + 2; 1546 if (fCrossHair1.x >= w) 1547 fCrossHair1.x = 0; 1548 fCrossHair1.y = fSelectionLoc.y + 2; 1549 if (fCrossHair1.y >= h) 1550 fCrossHair1.y = 0; 1551 } 1552 Invalidate(); 1553 } 1554 1555 1556 void 1557 TMagnify::RemoveCrossHair() 1558 { 1559 if (!fShowCrossHair1 && !fShowCrossHair2) 1560 return; 1561 1562 if (fShowCrossHair2) { 1563 fSelection = 1; 1564 fShowCrossHair2 = false; 1565 } else if (fShowCrossHair1) { 1566 fSelection = 0; 1567 fShowCrossHair1 = false; 1568 } 1569 Invalidate(); 1570 } 1571 1572 1573 void 1574 TMagnify::SetCrossHairsShowing(bool ch1, bool ch2) 1575 { 1576 fShowCrossHair1 = ch1; 1577 fShowCrossHair2 = ch2; 1578 } 1579 1580 1581 void 1582 TMagnify::CrossHairsShowing(bool* ch1, bool* ch2) 1583 { 1584 *ch1 = fShowCrossHair1; 1585 *ch2 = fShowCrossHair2; 1586 } 1587 1588 1589 void 1590 TMagnify::MakeActive(bool s) 1591 { 1592 fActive = s; 1593 } 1594 1595 1596 void 1597 TMagnify::MakeSticked(bool s) 1598 { 1599 fStickCoordinates = s; 1600 } 1601 1602 1603 void 1604 TMagnify::PixelCount(int32* width, int32* height) 1605 { 1606 fParent->PixelCount(width, height); 1607 } 1608 1609 1610 int32 1611 TMagnify::PixelSize() 1612 { 1613 return fParent->PixelSize(); 1614 } 1615 1616 1617 bool 1618 TMagnify::ShowGrid() 1619 { 1620 return fParent->ShowGrid(); 1621 } 1622 1623 1624 void 1625 TMagnify::StartSave() 1626 { 1627 fImageFrozenOnSave = Active(); 1628 if (fImageFrozenOnSave) 1629 MakeActive(false); 1630 } 1631 1632 1633 void 1634 TMagnify::EndSave() 1635 { 1636 if (fImageFrozenOnSave) 1637 MakeActive(true); 1638 } 1639 1640 1641 void 1642 TMagnify::SaveImage(entry_ref* ref, char* name, bool selectionOnly) 1643 { 1644 // create a new file 1645 BFile file; 1646 BDirectory parentDir(ref); 1647 parentDir.CreateFile(name, &file); 1648 1649 // write off the bitmaps bits to the file 1650 SaveBits(&file, fImageView->Bitmap(), "Data"); 1651 1652 // unfreeze the image, image was frozen before invoke of FilePanel 1653 EndSave(); 1654 } 1655 1656 1657 void 1658 TMagnify::SaveBits(BFile* file, const BBitmap *bitmap, const char* name) const 1659 { 1660 int32 bytesPerPixel; 1661 const char *kColorSpaceName; 1662 1663 switch (bitmap->ColorSpace()) { 1664 case B_GRAY8: 1665 bytesPerPixel = 1; 1666 kColorSpaceName = "B_GRAY8"; 1667 break; 1668 1669 case B_CMAP8: 1670 bytesPerPixel = 1; 1671 kColorSpaceName = "B_CMAP8"; 1672 break; 1673 1674 case B_RGB15: 1675 case B_RGBA15: 1676 case B_RGB15_BIG: 1677 case B_RGBA15_BIG: 1678 bytesPerPixel = 2; 1679 kColorSpaceName = "B_RGB15"; 1680 break; 1681 1682 case B_RGB16: 1683 case B_RGB16_BIG: 1684 bytesPerPixel = 2; 1685 kColorSpaceName = "B_RGB16"; 1686 break; 1687 1688 case B_RGB32: 1689 case B_RGBA32: 1690 case B_RGBA32_BIG: 1691 case B_BIG_RGB_32_BIT: 1692 bytesPerPixel = 3; 1693 kColorSpaceName = "B_RGB32"; 1694 break; 1695 1696 default: 1697 printf("dump: usupported ColorSpace\n"); 1698 return; 1699 } 1700 1701 char str[1024]; 1702 // stream out the width, height and ColorSpace 1703 sprintf(str, "const int32 k%sWidth = %" B_PRIi32 ";\n", name, 1704 (int32)bitmap->Bounds().Width()+1); 1705 file->Write(str, strlen(str)); 1706 sprintf(str, "const int32 k%sHeight = %" B_PRIi32 ";\n", name, 1707 (int32)bitmap->Bounds().Height()+1); 1708 file->Write(str, strlen(str)); 1709 sprintf(str, "const color_space k%sColorSpace = %s;\n\n", name, 1710 kColorSpaceName); 1711 file->Write(str, strlen(str)); 1712 1713 // stream out the constant name for this array 1714 sprintf(str, "const unsigned char k%sBits [] = {", name); 1715 file->Write(str, strlen(str)); 1716 1717 const unsigned char *bits = (const unsigned char *)bitmap->Bits(); 1718 const int32 kMaxColumnWidth = 16; 1719 int32 bytesPerRow = bitmap->BytesPerRow(); 1720 int32 columnWidth = (bytesPerRow < kMaxColumnWidth) ? bytesPerRow : kMaxColumnWidth; 1721 1722 for (int32 remaining = bitmap->BitsLength(); remaining; ) { 1723 sprintf(str, "\n\t"); 1724 file->Write(str, strlen(str)); 1725 1726 // stream out each row, based on the number of bytes required per row 1727 // padding is in the bitmap and will be streamed as 0xff 1728 for (int32 column = 0; column < columnWidth; column++) { 1729 // stream out individual pixel components 1730 for (int32 count = 0; count < bytesPerPixel; count++) { 1731 --remaining; 1732 sprintf(str, "0x%02x", *bits++); 1733 file->Write(str, strlen(str)); 1734 1735 if (remaining) { 1736 sprintf(str, ","); 1737 file->Write(str, strlen(str)); 1738 } else 1739 break; 1740 } 1741 1742 // make sure we don't walk off the end of the bits array 1743 if (!remaining) 1744 break; 1745 } 1746 } 1747 1748 sprintf(str, "\n};\n\n"); 1749 file->Write(str, strlen(str)); 1750 } 1751 1752 1753 // #pragma mark - 1754 1755 1756 TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space) 1757 : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1758 fColorSpace(space), fParent(parent) 1759 { 1760 switch (space) { 1761 case B_CMAP8: 1762 fBytesPerPixel = 1; 1763 break; 1764 case B_RGB15: 1765 case B_RGBA15: 1766 case B_RGB15_BIG: 1767 case B_RGBA15_BIG: 1768 case B_RGB16: 1769 case B_RGB16_BIG: 1770 fBytesPerPixel = 2; 1771 break; 1772 case B_RGB24: 1773 fBytesPerPixel = 3; 1774 break; 1775 case B_RGB32: 1776 case B_RGBA32: 1777 case B_RGB32_BIG: 1778 case B_RGBA32_BIG: 1779 fBytesPerPixel = 4; 1780 break; 1781 default: 1782 // uh, oh -- a color space we don't support 1783 fprintf(stderr, "Tried to run in an unsupported color space; exiting\n"); 1784 exit(1); 1785 break; 1786 } 1787 1788 fPixel = NULL; 1789 fBitmap = NULL; 1790 fOldBits = NULL; 1791 InitObject(); 1792 } 1793 1794 1795 TOSMagnify::~TOSMagnify() 1796 { 1797 delete fPixel; 1798 delete fBitmap; 1799 free(fOldBits); 1800 } 1801 1802 1803 void 1804 TOSMagnify::SetSpace(color_space space) 1805 { 1806 fColorSpace = space; 1807 InitObject(); 1808 }; 1809 1810 1811 void 1812 TOSMagnify::InitObject() 1813 { 1814 int32 w, h; 1815 fParent->PixelCount(&w, &h); 1816 1817 delete fBitmap; 1818 BRect bitsRect(0, 0, w-1, h-1); 1819 fBitmap = new BBitmap(bitsRect, fColorSpace); 1820 1821 free(fOldBits); 1822 fOldBits = (char*)malloc(fBitmap->BitsLength()); 1823 1824 if (!fPixel) { 1825 #if B_HOST_IS_BENDIAN 1826 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true); 1827 #else 1828 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true); 1829 #endif 1830 fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0); 1831 fPixel->Lock(); 1832 fPixel->AddChild(fPixelView); 1833 fPixel->Unlock(); 1834 } 1835 } 1836 1837 1838 void 1839 TOSMagnify::FrameResized(float width, float height) 1840 { 1841 BView::FrameResized(width, height); 1842 InitObject(); 1843 } 1844 1845 1846 void 1847 TOSMagnify::Resize(int32 width, int32 height) 1848 { 1849 ResizeTo(width, height); 1850 InitObject(); 1851 } 1852 1853 1854 bool 1855 TOSMagnify::CreateImage(BPoint mouseLoc, bool force) 1856 { 1857 bool created = false; 1858 if (Window() && Window()->Lock()) { 1859 int32 width, height; 1860 fParent->PixelCount(&width, &height); 1861 int32 pixelSize = fParent->PixelSize(); 1862 1863 BRect srcRect(0, 0, width - 1, height - 1); 1864 srcRect.OffsetBy(mouseLoc.x - (width / 2), 1865 mouseLoc.y - (height / 2)); 1866 1867 if (force || CopyScreenRect(srcRect)) { 1868 srcRect.OffsetTo(BPoint(0, 0)); 1869 BRect destRect(Bounds()); 1870 1871 DrawBitmap(fBitmap, srcRect, destRect); 1872 1873 DrawGrid(width, height, destRect, pixelSize); 1874 DrawSelection(); 1875 1876 Sync(); 1877 created = true; 1878 } 1879 Window()->Unlock(); 1880 } else 1881 printf("window problem\n"); 1882 1883 return created; 1884 } 1885 1886 1887 bool 1888 TOSMagnify::CopyScreenRect(BRect srcRect) 1889 { 1890 // constrain src rect to legal screen rect 1891 BScreen screen(Window()); 1892 BRect scrnframe = screen.Frame(); 1893 1894 if (srcRect.right > scrnframe.right) 1895 srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top); 1896 if (srcRect.top < 0) 1897 srcRect.OffsetTo(srcRect.left, 0); 1898 1899 if (srcRect.bottom > scrnframe.bottom) 1900 srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height()); 1901 if (srcRect.left < 0) 1902 srcRect.OffsetTo(0, srcRect.top); 1903 1904 // save a copy of the bits for comparison later 1905 memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength()); 1906 1907 screen.ReadBitmap(fBitmap, false, &srcRect); 1908 1909 // let caller know whether bits have actually changed 1910 return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0; 1911 } 1912 1913 1914 void 1915 TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize) 1916 { 1917 // draw grid 1918 if (fParent->ShowGrid() && fParent->PixelSize() > 2) { 1919 BeginLineArray(width * height); 1920 1921 // horizontal lines 1922 for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize) 1923 AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray); 1924 1925 // vertical lines 1926 for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize) 1927 AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray); 1928 1929 EndLineArray(); 1930 } 1931 1932 SetHighColor(kGridGray); 1933 StrokeRect(destRect); 1934 } 1935 1936 1937 void 1938 TOSMagnify::DrawSelection() 1939 { 1940 if (!fParent->SelectionIsShowing()) 1941 return; 1942 1943 float x, y; 1944 int32 pixelSize = fParent->PixelSize(); 1945 int32 squareSize = pixelSize - 2; 1946 1947 fParent->SelectionLoc(&x, &y); 1948 x *= pixelSize; x++; 1949 y *= pixelSize; y++; 1950 BRect selRect(x, y, x+squareSize, y+squareSize); 1951 1952 short selection = fParent->Selection(); 1953 1954 PushState(); 1955 SetLowColor(ViewColor()); 1956 SetHighColor(kRedColor); 1957 StrokeRect(selRect); 1958 if (selection == 0) { 1959 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 1960 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 1961 } 1962 1963 bool ch1Showing, ch2Showing; 1964 fParent->CrossHairsShowing(&ch1Showing, &ch2Showing); 1965 if (ch1Showing) { 1966 SetHighColor(kBlueColor); 1967 fParent->CrossHair1Loc(&x, &y); 1968 x *= pixelSize; x++; 1969 y *= pixelSize; y++; 1970 selRect.Set(x, y,x+squareSize, y+squareSize); 1971 StrokeRect(selRect); 1972 BeginLineArray(4); 1973 AddLine(BPoint(0, y+(squareSize/2)), 1974 BPoint(x, y+(squareSize/2)), kBlueColor); // left 1975 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 1976 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 1977 AddLine(BPoint(x+(squareSize/2), 0), 1978 BPoint(x+(squareSize/2), y), kBlueColor); // top 1979 AddLine(BPoint(x+(squareSize/2), y+squareSize), 1980 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 1981 EndLineArray(); 1982 if (selection == 1) { 1983 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 1984 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 1985 } 1986 } 1987 if (ch2Showing) { 1988 SetHighColor(kBlueColor); 1989 fParent->CrossHair2Loc(&x, &y); 1990 x *= pixelSize; x++; 1991 y *= pixelSize; y++; 1992 selRect.Set(x, y,x+squareSize, y+squareSize); 1993 StrokeRect(selRect); 1994 BeginLineArray(4); 1995 AddLine(BPoint(0, y+(squareSize/2)), 1996 BPoint(x, y+(squareSize/2)), kBlueColor); // left 1997 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 1998 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 1999 AddLine(BPoint(x+(squareSize/2), 0), 2000 BPoint(x+(squareSize/2), y), kBlueColor); // top 2001 AddLine(BPoint(x+(squareSize/2), y+squareSize), 2002 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 2003 EndLineArray(); 2004 if (selection == 2) { 2005 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 2006 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 2007 } 2008 } 2009 2010 PopState(); 2011 } 2012 2013 2014 rgb_color 2015 TOSMagnify::ColorAtSelection() 2016 { 2017 float x, y; 2018 fParent->SelectionLoc(&x, &y); 2019 BRect srcRect(x, y, x, y); 2020 BRect dstRect(0, 0, 0, 0); 2021 fPixel->Lock(); 2022 fPixelView->DrawBitmap(fBitmap, srcRect, dstRect); 2023 fPixelView->Sync(); 2024 fPixel->Unlock(); 2025 2026 uint32 pixel = *((uint32*)fPixel->Bits()); 2027 rgb_color c; 2028 c.alpha = pixel >> 24; 2029 c.red = (pixel >> 16) & 0xFF; 2030 c.green = (pixel >> 8) & 0xFF; 2031 c.blue = pixel & 0xFF; 2032 2033 return c; 2034 } 2035 2036 2037 // #pragma mark - 2038 2039 2040 int 2041 main(int argc, char* argv[]) 2042 { 2043 int32 pixelCount = -1; 2044 2045 if (argc > 2) { 2046 printf(B_TRANSLATE_CONTEXT( 2047 "usage: magnify [size] (magnify size * size pixels)\n", 2048 "Console")); 2049 exit(1); 2050 } else { 2051 if (argc == 2) { 2052 pixelCount = abs(atoi(argv[1])); 2053 2054 if ((pixelCount > 100) || (pixelCount < 4)) { 2055 printf(B_TRANSLATE_CONTEXT( 2056 "usage: magnify [size] (magnify size * size pixels)\n", 2057 "Console")); 2058 printf(B_TRANSLATE_CONTEXT( 2059 " size must be > 4 and a multiple of 4\n", 2060 "Console")); 2061 exit(1); 2062 } 2063 2064 if (pixelCount % 4) { 2065 printf(B_TRANSLATE_CONTEXT( 2066 "magnify: size must be a multiple of 4\n", 2067 "Console")); 2068 exit(1); 2069 } 2070 } 2071 } 2072 2073 TApp app(pixelCount); 2074 app.Run(); 2075 return 0; 2076 } 2077