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