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