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