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