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