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