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 color = { 0, 0, 0, 255 }; 913 if (fMagView) 914 color = fMagView->SelectionColor(); 915 char str[64]; 916 snprintf(str, sizeof(str), "R: %i G: %i B: %i (#%02x%02x%02x)", 917 color.red, color.green, color.blue, color.red, color.green, color.blue); 918 919 MovePenTo(10, fFontHeight*2+5); 920 invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7); 921 SetHighColor(ViewColor()); 922 FillRect(invalRect); 923 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 924 strcpy(fRGBStr,str); 925 DrawString(fRGBStr); 926 927 bool ch1Showing, ch2Showing; 928 dynamic_cast<TWindow*>(Window())->CrossHairsShowing(&ch1Showing, &ch2Showing); 929 930 if (fMagView) { 931 BPoint pt1(fMagView->CrossHair1Loc()); 932 BPoint pt2(fMagView->CrossHair2Loc()); 933 934 float h = Bounds().Height(); 935 if (ch2Showing) { 936 MovePenTo(10, h-12); 937 sprintf(str, "2) x: %li y: %li y: %i", (int32)pt2.x, (int32)pt2.y, 938 abs((int)(pt1.y - pt2.y))); 939 invalRect.Set(10, h-12-fFontHeight, 10 + StringWidth(fCH2Str), h-10); 940 SetHighColor(ViewColor()); 941 FillRect(invalRect); 942 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 943 strcpy(fCH2Str,str); 944 DrawString(fCH2Str); 945 } 946 947 if (ch1Showing && ch2Showing) { 948 MovePenTo(10, h-10-fFontHeight-2); 949 sprintf(str, "1) x: %li y: %li x: %i", (int32)pt1.x, (int32)pt1.y, 950 abs((int)(pt1.x - pt2.x))); 951 invalRect.Set(10, h-10-2*fFontHeight-2, 10 + StringWidth(fCH1Str), h-10-fFontHeight); 952 SetHighColor(ViewColor()); 953 FillRect(invalRect); 954 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 955 strcpy(fCH1Str,str); 956 DrawString(fCH1Str); 957 } else if (ch1Showing) { 958 MovePenTo(10, h-10); 959 sprintf(str, "x: %li y: %li", (int32)pt1.x, (int32)pt1.y); 960 invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8); 961 SetHighColor(ViewColor()); 962 FillRect(invalRect); 963 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 964 strcpy(fCH1Str,str); 965 DrawString(fCH1Str); 966 } 967 } 968 969 PopState(); 970 } 971 972 973 void 974 TInfoView::FrameResized(float width, float height) 975 { 976 BBox::FrameResized(width, height); 977 } 978 979 980 void 981 TInfoView::AddMenu() 982 { 983 fMenu = new TMenu(dynamic_cast<TWindow*>(Window()), ""); 984 BuildInfoMenu(fMenu); 985 986 BRect r(Bounds().Width()-27, 11, Bounds().Width()-11, 27); 987 fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true, 988 B_FOLLOW_RIGHT | B_FOLLOW_TOP); 989 AddChild(fPopUp); 990 } 991 992 993 void 994 TInfoView::SetMagView(TMagnify* magView) 995 { 996 fMagView = magView; 997 } 998 999 1000 // #pragma mark - 1001 1002 1003 TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout) 1004 : BMenu(title, layout), 1005 fMainWindow(mainWindow) 1006 { 1007 } 1008 1009 1010 TMenu::~TMenu() 1011 { 1012 } 1013 1014 1015 void 1016 TMenu::AttachedToWindow() 1017 { 1018 bool state = true; 1019 if (fMainWindow) 1020 state = fMainWindow->IsActive(); 1021 1022 BMenuItem* menuItem = FindItem(B_TRANSLATE("Hide/Show info")); 1023 if (menuItem) 1024 menuItem->SetEnabled(state); 1025 menuItem = FindItem(B_TRANSLATE("Add a crosshair")); 1026 if (menuItem) 1027 menuItem->SetEnabled(state); 1028 menuItem = FindItem(B_TRANSLATE("Remove a crosshair")); 1029 if (menuItem) 1030 menuItem->SetEnabled(state); 1031 menuItem = FindItem(B_TRANSLATE("Hide/Show grid")); 1032 if (menuItem) 1033 menuItem->SetEnabled(state); 1034 menuItem = FindItem(B_TRANSLATE("Make square")); 1035 if (menuItem) 1036 menuItem->SetEnabled(state); 1037 menuItem = FindItem(B_TRANSLATE("Decrease window size")); 1038 if (menuItem) 1039 menuItem->SetEnabled(state); 1040 menuItem = FindItem(B_TRANSLATE("Increase window size")); 1041 if (menuItem) 1042 menuItem->SetEnabled(state); 1043 menuItem = FindItem(B_TRANSLATE("Decrease pixel size")); 1044 if (menuItem) 1045 menuItem->SetEnabled(state); 1046 menuItem = FindItem(B_TRANSLATE("Increase pixel size")); 1047 if (menuItem) 1048 menuItem->SetEnabled(state); 1049 1050 BMenu::AttachedToWindow(); 1051 } 1052 1053 1054 // #pragma mark - 1055 1056 1057 TMagnify::TMagnify(BRect r, TWindow* parent) 1058 : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1059 fNeedToUpdate(true), 1060 fThread(-1), 1061 fActive(true), 1062 fImageBuf(NULL), 1063 fImageView(NULL), 1064 fLastLoc(-1, -1), 1065 fSelection(-1), 1066 fShowSelection(false), 1067 fSelectionLoc(0, 0), 1068 fShowCrossHair1(false), 1069 fCrossHair1(-1, -1), 1070 fShowCrossHair2(false), 1071 fCrossHair2(-1, -1), 1072 fParent(parent), 1073 fStickCoordinates(false) 1074 { 1075 SetViewColor(B_TRANSPARENT_32_BIT); 1076 } 1077 1078 1079 TMagnify::~TMagnify() 1080 { 1081 kill_thread(fThread); 1082 delete fImageBuf; 1083 } 1084 1085 1086 void 1087 TMagnify::AttachedToWindow() 1088 { 1089 int32 width, height; 1090 fParent->PixelCount(&width, &height); 1091 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1092 1093 fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask", 1094 B_NORMAL_PRIORITY, this); 1095 1096 resume_thread(fThread); 1097 1098 MakeFocus(); 1099 } 1100 1101 1102 void 1103 TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount, 1104 int32 pixelSize, bool showGrid) 1105 { 1106 color_space colorSpace = BScreen(Window()).ColorSpace(); 1107 1108 BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1); 1109 if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height()) 1110 ResizeTo(r.Width(), r.Height()); 1111 1112 if (fImageView) { 1113 fImageBuf->Lock(); 1114 fImageView->RemoveSelf(); 1115 fImageBuf->Unlock(); 1116 1117 fImageView->Resize((int32)r.Width(), (int32)r.Height()); 1118 fImageView->SetSpace(colorSpace); 1119 } else 1120 fImageView = new TOSMagnify(r, this, colorSpace); 1121 1122 delete fImageBuf; 1123 fImageBuf = new BBitmap(r, colorSpace, true); 1124 fImageBuf->Lock(); 1125 fImageBuf->AddChild(fImageView); 1126 fImageBuf->Unlock(); 1127 } 1128 1129 1130 void 1131 TMagnify::Draw(BRect) 1132 { 1133 BRect bounds(Bounds()); 1134 DrawBitmap(fImageBuf, bounds, bounds); 1135 static_cast<TWindow*>(Window())->UpdateInfo(); 1136 } 1137 1138 1139 void 1140 TMagnify::KeyDown(const char *key, int32 numBytes) 1141 { 1142 if (!fShowSelection) 1143 BView::KeyDown(key, numBytes); 1144 1145 uint32 mods = modifiers(); 1146 1147 switch (key[0]) { 1148 case B_TAB: 1149 if (fShowCrossHair1) { 1150 fSelection++; 1151 1152 if (fShowCrossHair2) { 1153 if (fSelection > 2) 1154 fSelection = 0; 1155 } else if (fShowCrossHair1) { 1156 if (fSelection > 1) 1157 fSelection = 0; 1158 } 1159 fNeedToUpdate = true; 1160 Invalidate(); 1161 } 1162 break; 1163 1164 case B_LEFT_ARROW: 1165 if (mods & B_OPTION_KEY) 1166 NudgeMouse(-1,0); 1167 else 1168 MoveSelection(-1,0); 1169 break; 1170 case B_RIGHT_ARROW: 1171 if (mods & B_OPTION_KEY) 1172 NudgeMouse(1, 0); 1173 else 1174 MoveSelection(1,0); 1175 break; 1176 case B_UP_ARROW: 1177 if (mods & B_OPTION_KEY) 1178 NudgeMouse(0, -1); 1179 else 1180 MoveSelection(0,-1); 1181 break; 1182 case B_DOWN_ARROW: 1183 if (mods & B_OPTION_KEY) 1184 NudgeMouse(0, 1); 1185 else 1186 MoveSelection(0,1); 1187 break; 1188 1189 default: 1190 BView::KeyDown(key,numBytes); 1191 break; 1192 } 1193 } 1194 1195 1196 void 1197 TMagnify::FrameResized(float newW, float newH) 1198 { 1199 int32 w, h; 1200 PixelCount(&w, &h); 1201 1202 if (fSelectionLoc.x >= w) 1203 fSelectionLoc.x = 0; 1204 if (fSelectionLoc.y >= h) 1205 fSelectionLoc.y = 0; 1206 1207 if (fShowCrossHair1) { 1208 if (fCrossHair1.x >= w) { 1209 fCrossHair1.x = fSelectionLoc.x + 2; 1210 if (fCrossHair1.x >= w) 1211 fCrossHair1.x = 0; 1212 } 1213 if (fCrossHair1.y >= h) { 1214 fCrossHair1.y = fSelectionLoc.y + 2; 1215 if (fCrossHair1.y >= h) 1216 fCrossHair1.y = 0; 1217 } 1218 1219 if (fShowCrossHair2) { 1220 if (fCrossHair2.x >= w) { 1221 fCrossHair2.x = fCrossHair1.x + 2; 1222 if (fCrossHair2.x >= w) 1223 fCrossHair2.x = 0; 1224 } 1225 if (fCrossHair2.y >= h) { 1226 fCrossHair2.y = fCrossHair1.y + 2; 1227 if (fCrossHair2.y >= h) 1228 fCrossHair2.y = 0; 1229 } 1230 } 1231 } 1232 } 1233 1234 1235 void 1236 TMagnify::MouseDown(BPoint where) 1237 { 1238 BMessage *currentMsg = Window()->CurrentMessage(); 1239 if (currentMsg->what == B_MOUSE_DOWN) { 1240 uint32 buttons = 0; 1241 currentMsg->FindInt32("buttons", (int32 *)&buttons); 1242 1243 uint32 modifiers = 0; 1244 currentMsg->FindInt32("modifiers", (int32 *)&modifiers); 1245 1246 if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) { 1247 // secondary button was clicked or control key was down, show menu and return 1248 1249 BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info")); 1250 menu->SetFont(be_plain_font); 1251 BuildInfoMenu(menu); 1252 1253 BMenuItem *selected = menu->Go(ConvertToScreen(where)); 1254 if (selected) 1255 Window()->PostMessage(selected->Message()->what); 1256 delete menu; 1257 return; 1258 } 1259 1260 // add a mousedown looper here 1261 1262 int32 pixelSize = PixelSize(); 1263 float x = where.x / pixelSize; 1264 float y = where.y / pixelSize; 1265 1266 MoveSelectionTo(x, y); 1267 1268 // draw the frozen image 1269 // update the info region 1270 1271 fNeedToUpdate = true; 1272 Invalidate(); 1273 } 1274 } 1275 1276 1277 void 1278 TMagnify::ScreenChanged(BRect, color_space) 1279 { 1280 int32 width, height; 1281 fParent->PixelCount(&width, &height); 1282 InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); 1283 } 1284 1285 1286 void 1287 TMagnify::SetSelection(bool state) 1288 { 1289 if (fShowSelection == state) 1290 return; 1291 1292 fShowSelection = state; 1293 fSelection = 0; 1294 Invalidate(); 1295 } 1296 1297 1298 void 1299 TMagnify::MoveSelection(int32 x, int32 y) 1300 { 1301 if (!fShowSelection) 1302 return; 1303 1304 int32 xCount, yCount; 1305 PixelCount(&xCount, &yCount); 1306 1307 float xloc, yloc; 1308 if (fSelection == 0) { 1309 xloc = fSelectionLoc.x; 1310 yloc = fSelectionLoc.y; 1311 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1312 fSelectionLoc.x = xloc; 1313 fSelectionLoc.y = yloc; 1314 } else if (fSelection == 1) { 1315 xloc = fCrossHair1.x; 1316 yloc = fCrossHair1.y; 1317 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1318 fCrossHair1.x = xloc; 1319 fCrossHair1.y = yloc; 1320 } else if (fSelection == 2) { 1321 xloc = fCrossHair2.x; 1322 yloc = fCrossHair2.y; 1323 BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); 1324 fCrossHair2.x = xloc; 1325 fCrossHair2.y = yloc; 1326 } 1327 1328 fNeedToUpdate = true; 1329 Invalidate(); 1330 } 1331 1332 1333 void 1334 TMagnify::MoveSelectionTo(int32 x, int32 y) 1335 { 1336 if (!fShowSelection) 1337 return; 1338 1339 int32 xCount, yCount; 1340 PixelCount(&xCount, &yCount); 1341 if (x >= xCount) 1342 x = 0; 1343 if (y >= yCount) 1344 y = 0; 1345 1346 if (fSelection == 0) { 1347 fSelectionLoc.x = x; 1348 fSelectionLoc.y = y; 1349 } else if (fSelection == 1) { 1350 fCrossHair1.x = x; 1351 fCrossHair1.y = y; 1352 } else if (fSelection == 2) { 1353 fCrossHair2.x = x; 1354 fCrossHair2.y = y; 1355 } 1356 1357 fNeedToUpdate = true; 1358 Invalidate(); //Draw(Bounds()); 1359 } 1360 1361 1362 void 1363 TMagnify::ShowSelection() 1364 { 1365 } 1366 1367 1368 short 1369 TMagnify::Selection() 1370 { 1371 return fSelection; 1372 } 1373 1374 1375 bool 1376 TMagnify::SelectionIsShowing() 1377 { 1378 return fShowSelection; 1379 } 1380 1381 1382 void 1383 TMagnify::SelectionLoc(float* x, float* y) 1384 { 1385 *x = fSelectionLoc.x; 1386 *y = fSelectionLoc.y; 1387 } 1388 1389 1390 void 1391 TMagnify::SetSelectionLoc(float x, float y) 1392 { 1393 fSelectionLoc.x = x; 1394 fSelectionLoc.y = y; 1395 } 1396 1397 1398 rgb_color 1399 TMagnify::SelectionColor() 1400 { 1401 return fImageView->ColorAtSelection(); 1402 } 1403 1404 1405 void 1406 TMagnify::CrossHair1Loc(float* x, float* y) 1407 { 1408 *x = fCrossHair1.x; 1409 *y = fCrossHair1.y; 1410 } 1411 1412 1413 void 1414 TMagnify::CrossHair2Loc(float* x, float* y) 1415 { 1416 *x = fCrossHair2.x; 1417 *y = fCrossHair2.y; 1418 } 1419 1420 1421 BPoint 1422 TMagnify::CrossHair1Loc() 1423 { 1424 return fCrossHair1; 1425 } 1426 1427 1428 BPoint 1429 TMagnify::CrossHair2Loc() 1430 { 1431 return fCrossHair2; 1432 } 1433 1434 1435 void 1436 TMagnify::NudgeMouse(float x, float y) 1437 { 1438 BPoint loc; 1439 ulong button; 1440 1441 GetMouse(&loc, &button); 1442 ConvertToScreen(&loc); 1443 loc.x += x; 1444 loc.y += y; 1445 1446 set_mouse_position((int32)loc.x, (int32)loc.y); 1447 } 1448 1449 1450 void 1451 TMagnify::WindowActivated(bool active) 1452 { 1453 if (active) 1454 MakeFocus(); 1455 } 1456 1457 1458 long 1459 TMagnify::MagnifyTask(void *arg) 1460 { 1461 TMagnify* view = (TMagnify*)arg; 1462 1463 // static data members can't access members, methods without 1464 // a pointer to an instance of the class 1465 TWindow* window = (TWindow*)view->Window(); 1466 1467 while (true) { 1468 if (window->Lock()) { 1469 if (view->NeedToUpdate() || view->Active()) 1470 view->Update(view->NeedToUpdate()); 1471 1472 window->Unlock(); 1473 } 1474 snooze(35000); 1475 } 1476 1477 return B_NO_ERROR; 1478 } 1479 1480 1481 void 1482 TMagnify::Update(bool force) 1483 { 1484 BPoint loc; 1485 ulong button; 1486 static long counter = 0; 1487 1488 if (!fStickCoordinates) { 1489 GetMouse(&loc, &button); 1490 ConvertToScreen(&loc); 1491 } else 1492 loc = fLastLoc; 1493 1494 if (force || fLastLoc != loc || counter++ % 35 == 0) { 1495 if (fImageView->CreateImage(loc, force)) 1496 Invalidate(); 1497 1498 counter = 0; 1499 if (force) 1500 SetUpdate(false); 1501 } 1502 fLastLoc = loc; 1503 } 1504 1505 1506 bool 1507 TMagnify::NeedToUpdate() 1508 { 1509 return fNeedToUpdate; 1510 } 1511 1512 1513 void 1514 TMagnify::SetUpdate(bool s) 1515 { 1516 fNeedToUpdate = s; 1517 } 1518 1519 1520 void 1521 TMagnify::CopyImage() 1522 { 1523 StartSave(); 1524 be_clipboard->Lock(); 1525 be_clipboard->Clear(); 1526 1527 BMessage *message = be_clipboard->Data(); 1528 if (!message) { 1529 printf(B_TRANSLATE_CONTEXT("no clip msg\n", 1530 "In console, when clipboard is empty after clicking Copy image")); 1531 return; 1532 } 1533 1534 BMessage *embeddedBitmap = new BMessage(); 1535 (fImageView->Bitmap())->Archive(embeddedBitmap,false); 1536 status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap); 1537 if (err == B_OK) 1538 err = message->AddRect("rect", fImageView->Bitmap()->Bounds()); 1539 if (err == B_OK) 1540 be_clipboard->Commit(); 1541 1542 be_clipboard->Unlock(); 1543 EndSave(); 1544 } 1545 1546 1547 void 1548 TMagnify::AddCrossHair() 1549 { 1550 if (fShowCrossHair1 && fShowCrossHair2) 1551 return; 1552 1553 int32 w, h; 1554 PixelCount(&w, &h); 1555 1556 if (fShowCrossHair1) { 1557 fSelection = 2; 1558 fShowCrossHair2 = true; 1559 fCrossHair2.x = fCrossHair1.x + 2; 1560 if (fCrossHair2.x >= w) 1561 fCrossHair2.x = 0; 1562 fCrossHair2.y = fCrossHair1.y + 2; 1563 if (fCrossHair2.y >= h) 1564 fCrossHair2.y = 0; 1565 } else { 1566 fSelection = 1; 1567 fShowCrossHair1 = true; 1568 fCrossHair1.x = fSelectionLoc.x + 2; 1569 if (fCrossHair1.x >= w) 1570 fCrossHair1.x = 0; 1571 fCrossHair1.y = fSelectionLoc.y + 2; 1572 if (fCrossHair1.y >= h) 1573 fCrossHair1.y = 0; 1574 } 1575 Invalidate(); 1576 } 1577 1578 1579 void 1580 TMagnify::RemoveCrossHair() 1581 { 1582 if (!fShowCrossHair1 && !fShowCrossHair2) 1583 return; 1584 1585 if (fShowCrossHair2) { 1586 fSelection = 1; 1587 fShowCrossHair2 = false; 1588 } else if (fShowCrossHair1) { 1589 fSelection = 0; 1590 fShowCrossHair1 = false; 1591 } 1592 Invalidate(); 1593 } 1594 1595 1596 void 1597 TMagnify::SetCrossHairsShowing(bool ch1, bool ch2) 1598 { 1599 fShowCrossHair1 = ch1; 1600 fShowCrossHair2 = ch2; 1601 } 1602 1603 1604 void 1605 TMagnify::CrossHairsShowing(bool* ch1, bool* ch2) 1606 { 1607 *ch1 = fShowCrossHair1; 1608 *ch2 = fShowCrossHair2; 1609 } 1610 1611 1612 void 1613 TMagnify::MakeActive(bool s) 1614 { 1615 fActive = s; 1616 } 1617 1618 1619 void 1620 TMagnify::MakeSticked(bool s) 1621 { 1622 fStickCoordinates = s; 1623 } 1624 1625 1626 void 1627 TMagnify::PixelCount(int32* width, int32* height) 1628 { 1629 fParent->PixelCount(width, height); 1630 } 1631 1632 1633 int32 1634 TMagnify::PixelSize() 1635 { 1636 return fParent->PixelSize(); 1637 } 1638 1639 1640 bool 1641 TMagnify::ShowGrid() 1642 { 1643 return fParent->ShowGrid(); 1644 } 1645 1646 1647 void 1648 TMagnify::StartSave() 1649 { 1650 fImageFrozenOnSave = Active(); 1651 if (fImageFrozenOnSave) 1652 MakeActive(false); 1653 } 1654 1655 1656 void 1657 TMagnify::EndSave() 1658 { 1659 if (fImageFrozenOnSave) 1660 MakeActive(true); 1661 } 1662 1663 1664 void 1665 TMagnify::SaveImage(entry_ref* ref, char* name, bool selectionOnly) 1666 { 1667 // create a new file 1668 BFile file; 1669 BDirectory parentDir(ref); 1670 parentDir.CreateFile(name, &file); 1671 1672 // write off the bitmaps bits to the file 1673 SaveBits(&file, fImageView->Bitmap(), "Data"); 1674 1675 // unfreeze the image, image was frozen before invoke of FilePanel 1676 EndSave(); 1677 } 1678 1679 1680 void 1681 TMagnify::SaveBits(BFile* file, const BBitmap *bitmap, const char* name) const 1682 { 1683 int32 bytesPerPixel; 1684 const char *kColorSpaceName; 1685 1686 switch (bitmap->ColorSpace()) { 1687 case B_GRAY8: 1688 bytesPerPixel = 1; 1689 kColorSpaceName = "B_GRAY8"; 1690 break; 1691 1692 case B_CMAP8: 1693 bytesPerPixel = 1; 1694 kColorSpaceName = "B_CMAP8"; 1695 break; 1696 1697 case B_RGB15: 1698 case B_RGBA15: 1699 case B_RGB15_BIG: 1700 case B_RGBA15_BIG: 1701 bytesPerPixel = 2; 1702 kColorSpaceName = "B_RGB15"; 1703 break; 1704 1705 case B_RGB16: 1706 case B_RGB16_BIG: 1707 bytesPerPixel = 2; 1708 kColorSpaceName = "B_RGB16"; 1709 break; 1710 1711 case B_RGB32: 1712 case B_RGBA32: 1713 case B_RGBA32_BIG: 1714 case B_BIG_RGB_32_BIT: 1715 bytesPerPixel = 3; 1716 kColorSpaceName = "B_RGB32"; 1717 break; 1718 1719 default: 1720 printf("dump: usupported ColorSpace\n"); 1721 return; 1722 } 1723 1724 char str[1024]; 1725 // stream out the width, height and ColorSpace 1726 sprintf(str, "const int32 k%sWidth = %ld;\n", name, (int32)bitmap->Bounds().Width()+1); 1727 file->Write(str, strlen(str)); 1728 sprintf(str, "const int32 k%sHeight = %ld;\n", name, (int32)bitmap->Bounds().Height()+1); 1729 file->Write(str, strlen(str)); 1730 sprintf(str, "const color_space k%sColorSpace = %s;\n\n", name, kColorSpaceName); 1731 file->Write(str, strlen(str)); 1732 1733 // stream out the constant name for this array 1734 sprintf(str, "const unsigned char k%sBits [] = {", name); 1735 file->Write(str, strlen(str)); 1736 1737 const unsigned char *bits = (const unsigned char *)bitmap->Bits(); 1738 const int32 kMaxColumnWidth = 16; 1739 int32 bytesPerRow = bitmap->BytesPerRow(); 1740 int32 columnWidth = (bytesPerRow < kMaxColumnWidth) ? bytesPerRow : kMaxColumnWidth; 1741 1742 for (int32 remaining = bitmap->BitsLength(); remaining; ) { 1743 sprintf(str, "\n\t"); 1744 file->Write(str, strlen(str)); 1745 1746 // stream out each row, based on the number of bytes required per row 1747 // padding is in the bitmap and will be streamed as 0xff 1748 for (int32 column = 0; column < columnWidth; column++) { 1749 // stream out individual pixel components 1750 for (int32 count = 0; count < bytesPerPixel; count++) { 1751 --remaining; 1752 sprintf(str, "0x%02x", *bits++); 1753 file->Write(str, strlen(str)); 1754 1755 if (remaining) { 1756 sprintf(str, ","); 1757 file->Write(str, strlen(str)); 1758 } else 1759 break; 1760 } 1761 1762 // make sure we don't walk off the end of the bits array 1763 if (!remaining) 1764 break; 1765 } 1766 } 1767 1768 sprintf(str, "\n};\n\n"); 1769 file->Write(str, strlen(str)); 1770 } 1771 1772 1773 // #pragma mark - 1774 1775 1776 TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space) 1777 : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1778 fColorSpace(space), fParent(parent) 1779 { 1780 switch (space) { 1781 case B_CMAP8: 1782 fBytesPerPixel = 1; 1783 break; 1784 case B_RGB15: 1785 case B_RGBA15: 1786 case B_RGB15_BIG: 1787 case B_RGBA15_BIG: 1788 case B_RGB16: 1789 case B_RGB16_BIG: 1790 fBytesPerPixel = 2; 1791 break; 1792 case B_RGB24: 1793 fBytesPerPixel = 3; 1794 break; 1795 case B_RGB32: 1796 case B_RGBA32: 1797 case B_RGB32_BIG: 1798 case B_RGBA32_BIG: 1799 fBytesPerPixel = 4; 1800 break; 1801 default: 1802 // uh, oh -- a color space we don't support 1803 fprintf(stderr, "Tried to run in an unsupported color space; exiting\n"); 1804 exit(1); 1805 break; 1806 } 1807 1808 fPixel = NULL; 1809 fBitmap = NULL; 1810 fOldBits = NULL; 1811 InitObject(); 1812 } 1813 1814 1815 TOSMagnify::~TOSMagnify() 1816 { 1817 delete fPixel; 1818 delete fBitmap; 1819 free(fOldBits); 1820 } 1821 1822 1823 void 1824 TOSMagnify::SetSpace(color_space space) 1825 { 1826 fColorSpace = space; 1827 InitObject(); 1828 }; 1829 1830 1831 void 1832 TOSMagnify::InitObject() 1833 { 1834 int32 w, h; 1835 fParent->PixelCount(&w, &h); 1836 1837 delete fBitmap; 1838 BRect bitsRect(0, 0, w-1, h-1); 1839 fBitmap = new BBitmap(bitsRect, fColorSpace); 1840 1841 free(fOldBits); 1842 fOldBits = (char*)malloc(fBitmap->BitsLength()); 1843 1844 if (!fPixel) { 1845 #if B_HOST_IS_BENDIAN 1846 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true); 1847 #else 1848 fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true); 1849 #endif 1850 fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0); 1851 fPixel->Lock(); 1852 fPixel->AddChild(fPixelView); 1853 fPixel->Unlock(); 1854 } 1855 } 1856 1857 1858 void 1859 TOSMagnify::FrameResized(float width, float height) 1860 { 1861 BView::FrameResized(width, height); 1862 InitObject(); 1863 } 1864 1865 1866 void 1867 TOSMagnify::Resize(int32 width, int32 height) 1868 { 1869 ResizeTo(width, height); 1870 InitObject(); 1871 } 1872 1873 1874 bool 1875 TOSMagnify::CreateImage(BPoint mouseLoc, bool force) 1876 { 1877 bool created = false; 1878 if (Window() && Window()->Lock()) { 1879 int32 width, height; 1880 fParent->PixelCount(&width, &height); 1881 int32 pixelSize = fParent->PixelSize(); 1882 1883 BRect srcRect(0, 0, width - 1, height - 1); 1884 srcRect.OffsetBy(mouseLoc.x - (width / 2), 1885 mouseLoc.y - (height / 2)); 1886 1887 if (force || CopyScreenRect(srcRect)) { 1888 srcRect.OffsetTo(BPoint(0, 0)); 1889 BRect destRect(Bounds()); 1890 1891 DrawBitmap(fBitmap, srcRect, destRect); 1892 1893 DrawGrid(width, height, destRect, pixelSize); 1894 DrawSelection(); 1895 1896 Sync(); 1897 created = true; 1898 } 1899 Window()->Unlock(); 1900 } else 1901 printf("window problem\n"); 1902 1903 return created; 1904 } 1905 1906 1907 bool 1908 TOSMagnify::CopyScreenRect(BRect srcRect) 1909 { 1910 // constrain src rect to legal screen rect 1911 BScreen screen(Window()); 1912 BRect scrnframe = screen.Frame(); 1913 1914 if (srcRect.right > scrnframe.right) 1915 srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top); 1916 if (srcRect.top < 0) 1917 srcRect.OffsetTo(srcRect.left, 0); 1918 1919 if (srcRect.bottom > scrnframe.bottom) 1920 srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height()); 1921 if (srcRect.left < 0) 1922 srcRect.OffsetTo(0, srcRect.top); 1923 1924 // save a copy of the bits for comparison later 1925 memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength()); 1926 1927 screen.ReadBitmap(fBitmap, false, &srcRect); 1928 1929 // let caller know whether bits have actually changed 1930 return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0; 1931 } 1932 1933 1934 void 1935 TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize) 1936 { 1937 // draw grid 1938 if (fParent->ShowGrid() && fParent->PixelSize() > 2) { 1939 BeginLineArray(width * height); 1940 1941 // horizontal lines 1942 for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize) 1943 AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray); 1944 1945 // vertical lines 1946 for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize) 1947 AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray); 1948 1949 EndLineArray(); 1950 } 1951 1952 SetHighColor(kGridGray); 1953 StrokeRect(destRect); 1954 } 1955 1956 1957 void 1958 TOSMagnify::DrawSelection() 1959 { 1960 if (!fParent->SelectionIsShowing()) 1961 return; 1962 1963 float x, y; 1964 int32 pixelSize = fParent->PixelSize(); 1965 int32 squareSize = pixelSize - 2; 1966 1967 fParent->SelectionLoc(&x, &y); 1968 x *= pixelSize; x++; 1969 y *= pixelSize; y++; 1970 BRect selRect(x, y, x+squareSize, y+squareSize); 1971 1972 short selection = fParent->Selection(); 1973 1974 PushState(); 1975 SetLowColor(ViewColor()); 1976 SetHighColor(kRedColor); 1977 StrokeRect(selRect); 1978 if (selection == 0) { 1979 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 1980 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 1981 } 1982 1983 bool ch1Showing, ch2Showing; 1984 fParent->CrossHairsShowing(&ch1Showing, &ch2Showing); 1985 if (ch1Showing) { 1986 SetHighColor(kBlueColor); 1987 fParent->CrossHair1Loc(&x, &y); 1988 x *= pixelSize; x++; 1989 y *= pixelSize; y++; 1990 selRect.Set(x, y,x+squareSize, y+squareSize); 1991 StrokeRect(selRect); 1992 BeginLineArray(4); 1993 AddLine(BPoint(0, y+(squareSize/2)), 1994 BPoint(x, y+(squareSize/2)), kBlueColor); // left 1995 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 1996 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 1997 AddLine(BPoint(x+(squareSize/2), 0), 1998 BPoint(x+(squareSize/2), y), kBlueColor); // top 1999 AddLine(BPoint(x+(squareSize/2), y+squareSize), 2000 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 2001 EndLineArray(); 2002 if (selection == 1) { 2003 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 2004 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 2005 } 2006 } 2007 if (ch2Showing) { 2008 SetHighColor(kBlueColor); 2009 fParent->CrossHair2Loc(&x, &y); 2010 x *= pixelSize; x++; 2011 y *= pixelSize; y++; 2012 selRect.Set(x, y,x+squareSize, y+squareSize); 2013 StrokeRect(selRect); 2014 BeginLineArray(4); 2015 AddLine(BPoint(0, y+(squareSize/2)), 2016 BPoint(x, y+(squareSize/2)), kBlueColor); // left 2017 AddLine(BPoint(x+squareSize,y+(squareSize/2)), 2018 BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right 2019 AddLine(BPoint(x+(squareSize/2), 0), 2020 BPoint(x+(squareSize/2), y), kBlueColor); // top 2021 AddLine(BPoint(x+(squareSize/2), y+squareSize), 2022 BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom 2023 EndLineArray(); 2024 if (selection == 2) { 2025 StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); 2026 StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); 2027 } 2028 } 2029 2030 PopState(); 2031 } 2032 2033 2034 rgb_color 2035 TOSMagnify::ColorAtSelection() 2036 { 2037 float x, y; 2038 fParent->SelectionLoc(&x, &y); 2039 BRect srcRect(x, y, x, y); 2040 BRect dstRect(0, 0, 0, 0); 2041 fPixel->Lock(); 2042 fPixelView->DrawBitmap(fBitmap, srcRect, dstRect); 2043 fPixelView->Sync(); 2044 fPixel->Unlock(); 2045 2046 uint32 pixel = *((uint32*)fPixel->Bits()); 2047 rgb_color c; 2048 c.alpha = pixel >> 24; 2049 c.red = (pixel >> 16) & 0xFF; 2050 c.green = (pixel >> 8) & 0xFF; 2051 c.blue = pixel & 0xFF; 2052 2053 return c; 2054 } 2055 2056 2057 // #pragma mark - 2058 2059 2060 int 2061 main(int argc, char* argv[]) 2062 { 2063 int32 pixelCount = -1; 2064 2065 if (argc > 2) { 2066 printf(B_TRANSLATE_CONTEXT( 2067 "usage: magnify [size] (magnify size * size pixels)\n", 2068 "Console")); 2069 exit(1); 2070 } else { 2071 if (argc == 2) { 2072 pixelCount = abs(atoi(argv[1])); 2073 2074 if ((pixelCount > 100) || (pixelCount < 4)) { 2075 printf(B_TRANSLATE_CONTEXT( 2076 "usage: magnify [size] (magnify size * size pixels)\n", 2077 "Console")); 2078 printf(B_TRANSLATE_CONTEXT( 2079 " size must be > 4 and a multiple of 4\n", 2080 "Console")); 2081 exit(1); 2082 } 2083 2084 if (pixelCount % 4) { 2085 printf(B_TRANSLATE_CONTEXT( 2086 "magnify: size must be a multiple of 4\n", 2087 "Console")); 2088 exit(1); 2089 } 2090 } 2091 } 2092 2093 TApp app(pixelCount); 2094 app.Run(); 2095 return 0; 2096 } 2097