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