/* * Copyright 2002-2009, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Updated by Sikosis (beos@gravity24hr.com) * * Copyright 1999, Be Incorporated. All Rights Reserved. * This file may be used under the terms of the Be Sample Code License. */ #include "Magnify.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Magnify-Main" const int32 msg_update_info = 'info'; const int32 msg_show_info = 'show'; const int32 msg_toggle_grid = 'grid'; const int32 msg_shrink = 'shnk'; const int32 msg_grow = 'grow'; const int32 msg_make_square = 'sqar'; const int32 msg_shrink_pixel = 'pshk'; const int32 msg_grow_pixel = 'pgrw'; const int32 msg_mouse_left = 'mslf'; const int32 msg_mouse_right = 'msrt'; const int32 msg_mouse_up = 'msup'; const int32 msg_mouse_down = 'msdn'; const int32 msg_new_color = 'colr'; const int32 msg_toggle_ruler = 'rulr'; const int32 msg_copy_image = 'copy'; const int32 msg_track_color = 'trak'; const int32 msg_freeze = 'frez'; const int32 msg_stick = 'stic'; const int32 msg_dump = 'dump'; const int32 msg_add_cross_hair = 'acrs'; const int32 msg_remove_cross_hair = 'rcrs'; const int32 msg_save = 'save'; const rgb_color kViewGray = { 216, 216, 216, 255}; const rgb_color kGridGray = {130, 130, 130, 255 }; const rgb_color kWhite = { 255, 255, 255, 255}; const rgb_color kBlack = { 0, 0, 0, 255}; const rgb_color kDarkGray = { 96, 96, 96, 255}; const rgb_color kRedColor = { 255, 10, 50, 255 }; const rgb_color kGreenColor = { 10, 255, 50, 255 }; const rgb_color kBlueColor = { 10, 50, 255, 255 }; const char* const kBitmapMimeType = "image/x-vnd.Be-bitmap"; const float kCurrentVersion = 1.2; const char *kPrefsFileName = "Magnify_prefs"; // prefs are: // name = Magnify // version // show grid // show info (rgb, location) // pixel count // pixel size const char* const kAppName = "Magnify"; const bool kDefaultShowGrid = true; const bool kDefaultShowInfo = true; const int32 kDefaultPixelCount = 32; const int32 kDefaultPixelSize = 8; // each info region will be: // top-bottom: 5 fontheight 5 fontheight 5 // left-right: 10 minwindowwidth 10 const int32 kBorderSize = 10; static property_info sProperties[] = { { "Info", { B_GET_PROPERTY, B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, "Show/hide info.", 0, { B_BOOL_TYPE } }, { "Grid", { B_GET_PROPERTY, B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, "Show/hide grid.", 0, { B_BOOL_TYPE } }, { "MakeSquare", { B_EXECUTE_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, "Make the view square.", 0, }, { "Zoom", { B_GET_PROPERTY, B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, "Gets/sets the zoom factor (1-16).", 0, { B_INT32_TYPE } }, { "Stick", { B_GET_PROPERTY, B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, "Stick/unstick coordinates.", 0, { B_BOOL_TYPE } }, { "CopyImage", { B_EXECUTE_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, "Copy image to clipboard.", 0, }, { 0 } }; static float FontHeight(BView* target, bool full) { font_height finfo; target->GetFontHeight(&finfo); float h = ceil(finfo.ascent) + ceil(finfo.descent); if (full) h += ceil(finfo.leading); return h; } static void BoundsSelection(int32 incX, int32 incY, float* x, float* y, int32 xCount, int32 yCount) { *x += incX; *y += incY; if (*x < 0) *x = xCount-1; if (*x >= xCount) *x = 0; if (*y < 0) *y = yCount-1; if (*y >= yCount) *y = 0; } static void BuildInfoMenu(BMenu *menu) { BMenuItem* menuItem; menuItem = new BMenuItem(B_TRANSLATE("Save image"), new BMessage(msg_save), 'S'); menu->AddItem(menuItem); // menuItem = new BMenuItem(B_TRANSLATE("Save selection"), // new BMessage(msg_save), 'S'); // menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Copy image"), new BMessage(msg_copy_image), 'C'); menu->AddItem(menuItem); menu->AddSeparatorItem(); menuItem = new BMenuItem(B_TRANSLATE("Show info"), new BMessage(msg_show_info), 'T'); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Add a crosshair"), new BMessage(msg_add_cross_hair), 'H'); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Remove a crosshair"), new BMessage(msg_remove_cross_hair), 'H', B_SHIFT_KEY); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Show grid"), new BMessage(msg_toggle_grid), 'G'); menu->AddItem(menuItem); menu->AddSeparatorItem(); menuItem = new BMenuItem(B_TRANSLATE("Freeze image"), new BMessage(msg_freeze), 'F'); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Stick coordinates"), new BMessage(msg_stick), 'I'); menu->AddItem(menuItem); menu->AddSeparatorItem(); menuItem = new BMenuItem(B_TRANSLATE("Make square"), new BMessage(msg_make_square), '/'); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Decrease window size"), new BMessage(msg_shrink), '-'); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Increase window size"), new BMessage(msg_grow), '+'); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Decrease pixel size"), new BMessage(msg_shrink_pixel), ','); menu->AddItem(menuItem); menuItem = new BMenuItem(B_TRANSLATE("Increase pixel size"), new BMessage(msg_grow_pixel), '.'); menu->AddItem(menuItem); } static void UpdateInfoMenu(BMenu *menu, TWindow *window) { bool state = true; bool showGrid = true; bool infoBarIsVisible = true; bool stickCordinates = true; if (window) { state = window->IsActive(); showGrid = window->ShowGrid(); infoBarIsVisible = window->InfoBarIsVisible(); stickCordinates = window->IsSticked(); } BMenuItem* menuItem = menu->FindItem(B_TRANSLATE("Show info")); if (menuItem) { menuItem->SetEnabled(state); menuItem->SetMarked(infoBarIsVisible); } menuItem = menu->FindItem(B_TRANSLATE("Add a crosshair")); if (menuItem) menuItem->SetEnabled(state); menuItem = menu->FindItem(B_TRANSLATE("Remove a crosshair")); if (menuItem) menuItem->SetEnabled(state); menuItem = menu->FindItem(B_TRANSLATE("Show grid")); if (menuItem) { menuItem->SetEnabled(state); menuItem->SetMarked(showGrid); } menuItem = menu->FindItem(B_TRANSLATE("Freeze image")); if (menuItem) { menuItem->SetMarked(!state); } menuItem = menu->FindItem(B_TRANSLATE("Stick coordinates")); if (menuItem) { menuItem->SetMarked(stickCordinates); } menuItem = menu->FindItem(B_TRANSLATE("Make square")); if (menuItem) menuItem->SetEnabled(state); menuItem = menu->FindItem(B_TRANSLATE("Decrease window size")); if (menuItem) menuItem->SetEnabled(state); menuItem = menu->FindItem(B_TRANSLATE("Increase window size")); if (menuItem) menuItem->SetEnabled(state); menuItem = menu->FindItem(B_TRANSLATE("Decrease pixel size")); if (menuItem) menuItem->SetEnabled(state); menuItem = menu->FindItem(B_TRANSLATE("Increase pixel size")); if (menuItem) menuItem->SetEnabled(state); } // #pragma mark - // pass in pixelCount to maintain backward compatibility of setting // the pixelcount from the command line TApp::TApp(int32 pixelCount) : BApplication("application/x-vnd.Haiku-Magnify") { TWindow* magWindow = new TWindow(pixelCount); magWindow->Show(); } // #pragma mark - TWindow::TWindow(int32 pixelCount) : BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Magnify"), B_TITLED_WINDOW, B_OUTLINE_RESIZE) { GetPrefs(pixelCount); // add info view BRect infoRect(Bounds()); infoRect.InsetBy(-1, -1); fInfo = new TInfoView(infoRect); AddChild(fInfo); fFontHeight = FontHeight(fInfo, true); fInfoHeight = (fFontHeight * 2) + (3 * 5); BRect fbRect(0, 0, (fHPixelCount*fPixelSize), (fHPixelCount*fPixelSize)); if (InfoIsShowing()) fbRect.OffsetBy(10, fInfoHeight); fFatBits = new TMagnify(fbRect, this); fInfo->AddChild(fFatBits); fFatBits->SetSelection(fShowInfo); fInfo->SetMagView(fFatBits); ResizeWindow(fHPixelCount, fVPixelCount); UpdateInfoBarOnResize(); AddShortcut('S', B_COMMAND_KEY, new BMessage(msg_save)); AddShortcut('C', B_COMMAND_KEY, new BMessage(msg_copy_image)); AddShortcut('T', B_COMMAND_KEY, new BMessage(msg_show_info)); AddShortcut('H', B_COMMAND_KEY, new BMessage(msg_add_cross_hair)); AddShortcut('H', B_SHIFT_KEY, new BMessage(msg_remove_cross_hair)); AddShortcut('G', B_COMMAND_KEY, new BMessage(msg_toggle_grid)); AddShortcut('F', B_COMMAND_KEY, new BMessage(msg_freeze)); AddShortcut('I', B_COMMAND_KEY, new BMessage(msg_stick)); AddShortcut('-', B_COMMAND_KEY, new BMessage(msg_shrink)); AddShortcut('=', B_COMMAND_KEY, new BMessage(msg_grow)); AddShortcut('/', B_COMMAND_KEY, new BMessage(msg_make_square)); AddShortcut(',', B_COMMAND_KEY, new BMessage(msg_shrink_pixel)); AddShortcut('.', B_COMMAND_KEY, new BMessage(msg_grow_pixel)); AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_left)); AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_right)); AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_up)); AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(msg_mouse_down)); } TWindow::~TWindow() { } status_t TWindow::GetSupportedSuites(BMessage* msg) { msg->AddString("suites", "suite/x-vnd.Haiku-Magnify"); BPropertyInfo propertyInfo(sProperties); msg->AddFlat("messages", &propertyInfo); return BWindow::GetSupportedSuites(msg); } BHandler* TWindow::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, int32 what, const char* property) { BPropertyInfo propertyInfo(sProperties); if (propertyInfo.FindMatch(msg, index, specifier, what, property) >= 0) return this; return BWindow::ResolveSpecifier(msg, index, specifier, what, property); } void TWindow::MessageReceived(BMessage* m) { bool active = fFatBits->Active(); switch (m->what) { case B_EXECUTE_PROPERTY: case B_GET_PROPERTY: case B_SET_PROPERTY: { int32 index; BMessage specifier; int32 what; const char* property; if (m->GetCurrentSpecifier(&index, &specifier, &what, &property) != B_OK) return BWindow::MessageReceived(m); status_t result = B_OK; BMessage reply(B_REPLY); BPropertyInfo propertyInfo(sProperties); switch (propertyInfo.FindMatch(m, index, &specifier, what, property)) { case 0: if (m->what == B_GET_PROPERTY) result = reply.AddBool("result", fInfoBarState); else if (m->what == B_SET_PROPERTY) { bool showInfo; result = m->FindBool("data", &showInfo); if (result == B_OK) { fInfoBarState = showInfo; ShowInfo(fInfoBarState); } } break; case 1: if (m->what == B_GET_PROPERTY) result = reply.AddBool("result", fShowGrid); else if (m->what == B_SET_PROPERTY) { bool showGrid; result = m->FindBool("data", &showGrid); if (result == B_OK) SetGrid(showGrid); } break; case 2: if (fHPixelCount != fVPixelCount) { int32 big = fHPixelCount > fVPixelCount ? fHPixelCount : fVPixelCount; ResizeWindow(big, big); } break; case 3: if (m->what == B_GET_PROPERTY) result = reply.AddInt32("result", fPixelSize); else if (m->what == B_SET_PROPERTY) { int32 zoom; result = m->FindInt32("data", &zoom); if (result == B_OK) SetPixelSize(zoom); } break; case 4: if (m->what == B_GET_PROPERTY) result = reply.AddBool("result", fFatBits->Sticked()); else if (m->what == B_SET_PROPERTY) { bool stick; result = m->FindBool("data", &stick); if (result == B_OK) fFatBits->MakeSticked(stick); } break; case 5: fFatBits->CopyImage(); break; default: return BWindow::MessageReceived(m); } if (result != B_OK) { reply.what = B_MESSAGE_NOT_UNDERSTOOD; reply.AddString("message", strerror(result)); reply.AddInt32("error", result); } m->SendReply(&reply); break; } case msg_show_info: if (active) { fInfoBarState = !fInfoBarState; ShowInfo(!fShowInfo); } break; case msg_toggle_grid: if (active) SetGrid(!fShowGrid); break; case msg_grow: if (active) ResizeWindow(true); break; case msg_shrink: if (active) ResizeWindow(false); break; case msg_make_square: if (active) { if (fHPixelCount == fVPixelCount) break; int32 big = (fHPixelCount > fVPixelCount) ? fHPixelCount : fVPixelCount; ResizeWindow(big, big); } break; case msg_shrink_pixel: if (active) SetPixelSize(false); break; case msg_grow_pixel: if (active) SetPixelSize(true); break; case msg_mouse_left: if (active) fFatBits->NudgeMouse(-1, 0); break; case msg_mouse_right: if (active) fFatBits->NudgeMouse(1, 0); break; case msg_mouse_up: if (active) fFatBits->NudgeMouse(0, -1); break; case msg_mouse_down: if (active) fFatBits->NudgeMouse(0, 1); break; case msg_add_cross_hair: if (active && fShowInfo) AddCrossHair(); break; case msg_remove_cross_hair: if (active && fShowInfo) RemoveCrossHair(); break; case msg_freeze: if (active) SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE); else SetFlags(B_OUTLINE_RESIZE | B_NOT_ZOOMABLE); fFatBits->MakeActive(!fFatBits->Active()); break; case msg_stick: fFatBits->MakeSticked(!fFatBits->Sticked()); break; case msg_save: { // freeze the image here, unfreeze after dump or cancel fFatBits->StartSave(); BMessenger messenger(this); BMessage message(msg_dump); fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false, &message); fSavePanel->SetSaveText("Bitmaps.png"); fSavePanel->Show(); } break; case msg_dump: { delete fSavePanel; entry_ref dirRef; char* name; m->FindRef("directory", &dirRef); m->FindString((const char*)"name",(const char**) &name); fFatBits->SaveImage(&dirRef, name); } break; case B_CANCEL: // image is frozen before the FilePanel is shown fFatBits->EndSave(); break; case msg_copy_image: fFatBits->CopyImage(); break; default: BWindow::MessageReceived(m); break; } } bool TWindow::QuitRequested() { SetPrefs(); be_app->PostMessage(B_QUIT_REQUESTED); return true; } void TWindow::GetPrefs(int32 overridePixelCount) { BPath path; char name[8]; float version; bool haveLoc=false; BPoint loc; bool showGrid = kDefaultShowGrid; bool showInfo = kDefaultShowInfo; bool ch1Showing=false; bool ch2Showing=false; int32 hPixelCount = kDefaultPixelCount; int32 vPixelCount = kDefaultPixelCount; int32 pixelSize = kDefaultPixelSize; if (find_directory (B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { int ref = -1; path.Append(kPrefsFileName); if ((ref = open(path.Path(), 0)) >= 0) { if (read(ref, name, 7) != 7) goto ALMOST_DONE; name[7] = 0; if (strcmp(name, kAppName) != 0) goto ALMOST_DONE; read(ref, &version, sizeof(float)); if (read(ref, &loc, sizeof(BPoint)) != sizeof(BPoint)) goto ALMOST_DONE; else haveLoc = true; if (read(ref, &showGrid, sizeof(bool)) != sizeof(bool)) { showGrid = kDefaultShowGrid; goto ALMOST_DONE; } if (read(ref, &showInfo, sizeof(bool)) != sizeof(bool)) { showInfo = kDefaultShowInfo; goto ALMOST_DONE; } if (read(ref, &ch1Showing, sizeof(bool)) != sizeof(bool)) { ch1Showing = false; goto ALMOST_DONE; } if (read(ref, &ch2Showing, sizeof(bool)) != sizeof(bool)) { ch2Showing = false; goto ALMOST_DONE; } if (read(ref, &hPixelCount, sizeof(int32)) != sizeof(int32)) { hPixelCount = kDefaultPixelCount; goto ALMOST_DONE; } if (read(ref, &vPixelCount, sizeof(int32)) != sizeof(int32)) { vPixelCount = kDefaultPixelCount; goto ALMOST_DONE; } if (read(ref, &pixelSize, sizeof(int32)) != sizeof(int32)) { pixelSize = kDefaultPixelSize; goto ALMOST_DONE; } ALMOST_DONE: // clean up and try to position the window close(ref); if (haveLoc && BScreen(B_MAIN_SCREEN_ID).Frame().Contains(loc)) { MoveTo(loc); goto DONE; } } } // if prefs dont yet exist or the window is not onscreen, center the window CenterOnScreen(); // set all the settings to defaults if we get here DONE: fShowGrid = showGrid; fShowInfo = showInfo; fInfoBarState = showInfo; fHPixelCount = (overridePixelCount == -1) ? hPixelCount : overridePixelCount; fVPixelCount = (overridePixelCount == -1) ? vPixelCount : overridePixelCount; fPixelSize = pixelSize; } void TWindow::SetPrefs() { BPath path; if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) { long ref; path.Append (kPrefsFileName); if ((ref = creat(path.Path(), S_IRUSR | S_IWUSR)) >= 0) { float version = kCurrentVersion; lseek (ref, 0, SEEK_SET); write(ref, kAppName, 7); write(ref, &version, sizeof(float)); BPoint loc = Frame().LeftTop(); write(ref, &loc, sizeof(BPoint)); write(ref, &fShowGrid, sizeof(bool)); write(ref, &fShowInfo, sizeof(bool)); bool ch1, ch2; CrossHairsShowing(&ch1, &ch2); write(ref, &ch1, sizeof(bool)); write(ref, &ch2, sizeof(bool)); write(ref, &fHPixelCount, sizeof(int32)); write(ref, &fVPixelCount, sizeof(int32)); write(ref, &fPixelSize, sizeof(int32)); close(ref); } } } void TWindow::FrameResized(float w, float h) { CalcViewablePixels(); fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid()); UpdateInfoBarOnResize(); } void TWindow::ScreenChanged(BRect screenSize, color_space depth) { BWindow::ScreenChanged(screenSize, depth); // reset all bitmaps fFatBits->ScreenChanged(screenSize,depth); } void TWindow::Minimize(bool m) { BWindow::Minimize(m); } void TWindow::Zoom(BPoint /*position*/, float /*width*/, float /*height*/) { if (fFatBits->Active()) ShowInfo(!fShowInfo); } void TWindow::CalcViewablePixels() { float w = Bounds().Width(); float h = Bounds().Height(); if (InfoIsShowing()) { w -= 20; // remove the gutter h = h-fInfoHeight-10; // remove info and gutter } bool ch1, ch2; fFatBits->CrossHairsShowing(&ch1, &ch2); if (ch1) h -= fFontHeight; if (ch2) h -= fFontHeight + 5; fHPixelCount = (int32)w / fPixelSize; // calc h pixels if (fHPixelCount < 16) fHPixelCount = 16; fVPixelCount = (int32)h / fPixelSize; // calc v pixels if (fVPixelCount < 4) fVPixelCount = 4; } void TWindow::GetPreferredSize(float* width, float* height) { *width = fHPixelCount * fPixelSize; // calc window width *height = fVPixelCount * fPixelSize; // calc window height if (InfoIsShowing()) { *width += 20; *height += fInfoHeight + 10; } bool ch1, ch2; fFatBits->CrossHairsShowing(&ch1, &ch2); if (ch1) *height += fFontHeight; if (ch2) *height += fFontHeight + 5; } void TWindow::ResizeWindow(int32 hPixelCount, int32 vPixelCount) { fHPixelCount = hPixelCount; fVPixelCount = vPixelCount; float width, height; GetPreferredSize(&width, &height); ResizeTo(width, height); } void TWindow::ResizeWindow(bool direction) { int32 x = fHPixelCount; int32 y = fVPixelCount; if (direction) { x += 4; y += 4; } else { x -= 4; y -= 4; } if (x < 4) x = 4; if (y < 4) y = 4; ResizeWindow(x, y); } void TWindow::SetGrid(bool s) { if (s == fShowGrid) return; fShowGrid = s; fFatBits->SetUpdate(true); } bool TWindow::ShowGrid() { return fShowGrid; } void TWindow::ShowInfo(bool i) { if (i == fShowInfo) return; fShowInfo = i; if (fShowInfo) fFatBits->MoveTo(10, fInfoHeight); else { fFatBits->MoveTo(1,1); fFatBits->SetCrossHairsShowing(false, false); } fFatBits->SetSelection(fShowInfo); ResizeWindow(fHPixelCount, fVPixelCount); fInfo->SetInfoTextVisible(i); } bool TWindow::InfoIsShowing() { return fShowInfo; } bool TWindow::InfoBarIsVisible() { return fInfoBarState; } void TWindow::UpdateInfo() { fInfo->Invalidate(); } void TWindow::UpdateInfoBarOnResize() { float infoWidth, infoHeight; fInfo->GetPreferredSize(&infoWidth, &infoHeight); if (infoWidth > Bounds().Width() || infoHeight > Bounds().Height()) { ShowInfo(false); } else { ShowInfo(fInfoBarState); } } void TWindow::AddCrossHair() { fFatBits->AddCrossHair(); // crosshair info needs to be added // window resizes accordingly float width; float height; GetPreferredSize(&width, &height); ResizeTo(width, height); } void TWindow::RemoveCrossHair() { fFatBits->RemoveCrossHair(); // crosshair info needs to be removed // window resizes accordingly float width; float height; GetPreferredSize(&width, &height); ResizeTo(width, height); } void TWindow::CrossHairsShowing(bool* ch1, bool* ch2) { fFatBits->CrossHairsShowing(ch1, ch2); } void TWindow::PixelCount(int32* h, int32 *v) { *h = fHPixelCount; *v = fVPixelCount; } void TWindow::SetPixelSize(int32 s) { if (s > 100) s = 100; else if (s < 1) s = 1; if (s == fPixelSize) return; fPixelSize = s; // resize window // tell info that size has changed // tell mag that size has changed float w = Bounds().Width(); float h = Bounds().Height(); CalcViewablePixels(); ResizeWindow(fHPixelCount, fVPixelCount); // the window might not actually change in size // in that case force the buffers to the new dimension if (w == Bounds().Width() && h == Bounds().Height()) fFatBits->InitBuffers(fHPixelCount, fVPixelCount, fPixelSize, ShowGrid()); } void TWindow::SetPixelSize(bool plus) { int32 pixelSize; if (plus) { if (fPixelSize >= 16) return; pixelSize = fPixelSize + 1; } else { pixelSize = fPixelSize / 2; if (pixelSize < 16) { if (fPixelSize > 16) pixelSize = (fPixelSize + 16) / 2; else pixelSize = fPixelSize - 1; } } SetPixelSize(pixelSize); } int32 TWindow::PixelSize() { return fPixelSize; } #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Magnify-Main" bool TWindow::IsActive() { return fFatBits->Active(); } bool TWindow::IsSticked() { return fFatBits->Sticked(); } // #pragma mark - TInfoView::TInfoView(BRect frame) : BBox(frame, "rgb", B_FOLLOW_ALL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS, B_NO_BORDER) { SetFont(be_plain_font); fFontHeight = FontHeight(this, true); fMagView = NULL; fSelectionColor = kBlack; fCH1Loc.x = fCH1Loc.y = fCH2Loc.x = fCH2Loc.y = 0; fInfoStr[0] = 0; fRGBStr[0] = 0; fCH1Str[0] = 0; fCH2Str[0] = 0; fInfoTextVisible = true; } TInfoView::~TInfoView() { } void TInfoView::AttachedToWindow() { BBox::AttachedToWindow(); SetViewUIColor(B_PANEL_BACKGROUND_COLOR); dynamic_cast(Window())->PixelCount(&fHPixelCount, &fVPixelCount); fPixelSize = dynamic_cast(Window())->PixelSize(); AddMenu(); } void TInfoView::Draw(BRect updateRect) { PushState(); SetLowColor(ViewColor()); BRect invalRect; int32 hPixelCount, vPixelCount; dynamic_cast(Window())->PixelCount(&hPixelCount, &vPixelCount); int32 pixelSize = dynamic_cast(Window())->PixelSize(); MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight + 5); static BStringFormat format(B_TRANSLATE_COMMENT("%width × %height @ {0, plural, " "one{# pixel/pixel} other{# pixels/pixel}}", "The '×' is the Unicode multiplication sign U+00D7")); BString dimensionsInfo; format.Format(dimensionsInfo, pixelSize); BString rep; rep << hPixelCount; dimensionsInfo.ReplaceAll("%width", rep); rep = ""; rep << vPixelCount; dimensionsInfo.ReplaceAll("%height", rep); invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); strcpy(fInfoStr, dimensionsInfo); if (fInfoTextVisible) DrawString(fInfoStr); rgb_color color = { 0, 0, 0, 255 }; if (fMagView) color = fMagView->SelectionColor(); char str[64]; snprintf(str, sizeof(str), "R: %i G: %i B: %i (#%02x%02x%02x)", color.red, color.green, color.blue, color.red, color.green, color.blue); MovePenTo(15 + fPopUp->Bounds().Width(), fFontHeight*2+5); invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); strcpy(fRGBStr,str); if (fInfoTextVisible) DrawString(fRGBStr); bool ch1Showing, ch2Showing; dynamic_cast(Window())->CrossHairsShowing(&ch1Showing, &ch2Showing); if (fMagView) { BPoint pt1(fMagView->CrossHair1Loc()); BPoint pt2(fMagView->CrossHair2Loc()); float h = Bounds().Height(); if (ch2Showing) { MovePenTo(10, h-12); sprintf(str, "2) x: %" B_PRIi32 " y: %" B_PRIi32 " y: %d", (int32)pt2.x, (int32)pt2.y, abs((int)(pt1.y - pt2.y))); invalRect.Set(10, h-12-fFontHeight, 10 + StringWidth(fCH2Str), h-10); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); strcpy(fCH2Str,str); if (fInfoTextVisible) DrawString(fCH2Str); } if (ch1Showing && ch2Showing) { MovePenTo(10, h-10-fFontHeight-2); sprintf(str, "1) x: %" B_PRIi32 " y: %" B_PRIi32 " x: %d", (int32)pt1.x, (int32)pt1.y, abs((int)(pt1.x - pt2.x))); invalRect.Set(10, h-10-2*fFontHeight-2, 10 + StringWidth(fCH1Str), h-10-fFontHeight); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); strcpy(fCH1Str,str); if (fInfoTextVisible) DrawString(fCH1Str); } else if (ch1Showing) { MovePenTo(10, h-10); sprintf(str, "x: %" B_PRIi32 " y: %" B_PRIi32, (int32)pt1.x, (int32)pt1.y); invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); strcpy(fCH1Str,str); if (fInfoTextVisible) DrawString(fCH1Str); } } PopState(); } void TInfoView::FrameResized(float width, float height) { BBox::FrameResized(width, height); } void TInfoView::AddMenu() { fMenu = new TMenu(dynamic_cast(Window()), ""); BuildInfoMenu(fMenu); BRect r(9, 11, 22, 27); fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true, B_FOLLOW_LEFT | B_FOLLOW_TOP); AddChild(fPopUp); } void TInfoView::SetMagView(TMagnify* magView) { fMagView = magView; } // #pragma mark - TMenu::TMenu(TWindow *mainWindow, const char *title, menu_layout layout) : BMenu(title, layout), fMainWindow(mainWindow) { } TMenu::~TMenu() { } void TMenu::AttachedToWindow() { UpdateInfoMenu(this, fMainWindow); BMenu::AttachedToWindow(); } void TInfoView::GetPreferredSize(float* _width, float* _height) { if (_width) { float str1Width = StringWidth(fCH1Str) + StringWidth(fCH2Str) + StringWidth(fRGBStr) + 30; float str2Width = StringWidth(fInfoStr) + 30; *_width = str1Width > str2Width ? str1Width : str2Width; } if (_height) *_height = fFontHeight * 2 + 10; } bool TInfoView::IsInfoTextVisible() { return fInfoTextVisible; } void TInfoView::SetInfoTextVisible(bool visible) { fInfoTextVisible = visible; Draw(Bounds()); } // #pragma mark - TMagnify::TMagnify(BRect r, TWindow* parent) : BView(r, "MagView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), fNeedToUpdate(true), fThread(-1), fActive(true), fImageBuf(NULL), fImageView(NULL), fLastLoc(-1, -1), fSelection(-1), fShowSelection(false), fSelectionLoc(0, 0), fShowCrossHair1(false), fCrossHair1(-1, -1), fShowCrossHair2(false), fCrossHair2(-1, -1), fParent(parent), fStickCoordinates(false) { } TMagnify::~TMagnify() { kill_thread(fThread); delete fImageBuf; } void TMagnify::AttachedToWindow() { int32 width, height; fParent->PixelCount(&width, &height); InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); fThread = spawn_thread(TMagnify::MagnifyTask, "MagnifyTask", B_NORMAL_PRIORITY, this); resume_thread(fThread); SetViewColor(B_TRANSPARENT_32_BIT); MakeFocus(); } void TMagnify::InitBuffers(int32 hPixelCount, int32 vPixelCount, int32 pixelSize, bool showGrid) { color_space colorSpace = BScreen(Window()).ColorSpace(); BRect r(0, 0, (pixelSize * hPixelCount)-1, (pixelSize * vPixelCount)-1); if (Bounds().Width() != r.Width() || Bounds().Height() != r.Height()) ResizeTo(r.Width(), r.Height()); if (fImageView) { fImageBuf->Lock(); fImageView->RemoveSelf(); fImageBuf->Unlock(); fImageView->Resize((int32)r.Width(), (int32)r.Height()); fImageView->SetSpace(colorSpace); } else fImageView = new TOSMagnify(r, this, colorSpace); delete fImageBuf; fImageBuf = new BBitmap(r, colorSpace, true); fImageBuf->Lock(); fImageBuf->AddChild(fImageView); fImageBuf->Unlock(); } void TMagnify::Draw(BRect) { BRect bounds(Bounds()); DrawBitmap(fImageBuf, bounds, bounds); static_cast(Window())->UpdateInfo(); } void TMagnify::KeyDown(const char *key, int32 numBytes) { if (!fShowSelection) BView::KeyDown(key, numBytes); switch (key[0]) { case B_TAB: if (fShowCrossHair1) { fSelection++; if (fShowCrossHair2) { if (fSelection > 2) fSelection = 0; } else if (fShowCrossHair1) { if (fSelection > 1) fSelection = 0; } fNeedToUpdate = true; Invalidate(); } break; case B_LEFT_ARROW: MoveSelection(-1,0); break; case B_RIGHT_ARROW: MoveSelection(1,0); break; case B_UP_ARROW: MoveSelection(0,-1); break; case B_DOWN_ARROW: MoveSelection(0,1); break; default: BView::KeyDown(key,numBytes); break; } } void TMagnify::FrameResized(float newW, float newH) { int32 w, h; PixelCount(&w, &h); if (fSelectionLoc.x >= w) fSelectionLoc.x = 0; if (fSelectionLoc.y >= h) fSelectionLoc.y = 0; if (fShowCrossHair1) { if (fCrossHair1.x >= w) { fCrossHair1.x = fSelectionLoc.x + 2; if (fCrossHair1.x >= w) fCrossHair1.x = 0; } if (fCrossHair1.y >= h) { fCrossHair1.y = fSelectionLoc.y + 2; if (fCrossHair1.y >= h) fCrossHair1.y = 0; } if (fShowCrossHair2) { if (fCrossHair2.x >= w) { fCrossHair2.x = fCrossHair1.x + 2; if (fCrossHair2.x >= w) fCrossHair2.x = 0; } if (fCrossHair2.y >= h) { fCrossHair2.y = fCrossHair1.y + 2; if (fCrossHair2.y >= h) fCrossHair2.y = 0; } } } } void TMagnify::MouseDown(BPoint where) { BMessage *currentMsg = Window()->CurrentMessage(); if (currentMsg->what == B_MOUSE_DOWN) { uint32 buttons = 0; currentMsg->FindInt32("buttons", (int32 *)&buttons); uint32 modifiers = 0; currentMsg->FindInt32("modifiers", (int32 *)&modifiers); if ((buttons & B_SECONDARY_MOUSE_BUTTON) || (modifiers & B_CONTROL_KEY)) { // secondary button was clicked or control key was down, show menu and return BPopUpMenu *menu = new BPopUpMenu(B_TRANSLATE("Info"), false, false); menu->SetFont(be_plain_font); BuildInfoMenu(menu); UpdateInfoMenu(menu, dynamic_cast(Window())); BMenuItem *selected = menu->Go(ConvertToScreen(where)); if (selected) Window()->PostMessage(selected->Message()->what); delete menu; return; } // add a mousedown looper here int32 pixelSize = PixelSize(); float x = where.x / pixelSize; float y = where.y / pixelSize; MoveSelectionTo(x, y); // draw the frozen image // update the info region fNeedToUpdate = true; Invalidate(); } } void TMagnify::ScreenChanged(BRect, color_space) { int32 width, height; fParent->PixelCount(&width, &height); InitBuffers(width, height, fParent->PixelSize(), fParent->ShowGrid()); } void TMagnify::SetSelection(bool state) { if (fShowSelection == state) return; fShowSelection = state; fSelection = 0; Invalidate(); } void TMagnify::MoveSelection(int32 x, int32 y) { if (!fShowSelection) return; int32 xCount, yCount; PixelCount(&xCount, &yCount); float xloc, yloc; if (fSelection == 0) { xloc = fSelectionLoc.x; yloc = fSelectionLoc.y; BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); fSelectionLoc.x = xloc; fSelectionLoc.y = yloc; } else if (fSelection == 1) { xloc = fCrossHair1.x; yloc = fCrossHair1.y; BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); fCrossHair1.x = xloc; fCrossHair1.y = yloc; } else if (fSelection == 2) { xloc = fCrossHair2.x; yloc = fCrossHair2.y; BoundsSelection(x, y, &xloc, &yloc, xCount, yCount); fCrossHair2.x = xloc; fCrossHair2.y = yloc; } fNeedToUpdate = true; Invalidate(); } void TMagnify::MoveSelectionTo(int32 x, int32 y) { if (!fShowSelection) return; int32 xCount, yCount; PixelCount(&xCount, &yCount); if (x >= xCount) x = 0; if (y >= yCount) y = 0; if (fSelection == 0) { fSelectionLoc.x = x; fSelectionLoc.y = y; } else if (fSelection == 1) { fCrossHair1.x = x; fCrossHair1.y = y; } else if (fSelection == 2) { fCrossHair2.x = x; fCrossHair2.y = y; } fNeedToUpdate = true; Invalidate(); //Draw(Bounds()); } void TMagnify::ShowSelection() { } short TMagnify::Selection() { return fSelection; } bool TMagnify::SelectionIsShowing() { return fShowSelection; } void TMagnify::SelectionLoc(float* x, float* y) { *x = fSelectionLoc.x; *y = fSelectionLoc.y; } void TMagnify::SetSelectionLoc(float x, float y) { fSelectionLoc.x = x; fSelectionLoc.y = y; } rgb_color TMagnify::SelectionColor() { return fImageView->ColorAtSelection(); } void TMagnify::CrossHair1Loc(float* x, float* y) { *x = fCrossHair1.x; *y = fCrossHair1.y; } void TMagnify::CrossHair2Loc(float* x, float* y) { *x = fCrossHair2.x; *y = fCrossHair2.y; } BPoint TMagnify::CrossHair1Loc() { return fCrossHair1; } BPoint TMagnify::CrossHair2Loc() { return fCrossHair2; } void TMagnify::NudgeMouse(float x, float y) { BPoint loc; uint32 button; GetMouse(&loc, &button); ConvertToScreen(&loc); loc.x += x; loc.y += y; set_mouse_position((int32)loc.x, (int32)loc.y); } void TMagnify::WindowActivated(bool active) { if (active) MakeFocus(); } status_t TMagnify::MagnifyTask(void *arg) { TMagnify* view = (TMagnify*)arg; // static data members can't access members, methods without // a pointer to an instance of the class TWindow* window = (TWindow*)view->Window(); while (true) { if (window->Lock()) { if (view->NeedToUpdate() || view->Active()) view->Update(view->NeedToUpdate()); window->Unlock(); } snooze(35000); } return B_NO_ERROR; } void TMagnify::Update(bool force) { BPoint loc; uint32 button; static long counter = 0; if (!fStickCoordinates) { GetMouse(&loc, &button); ConvertToScreen(&loc); } else loc = fLastLoc; if (force || fLastLoc != loc || counter++ % 35 == 0) { if (fImageView->CreateImage(loc, force)) Invalidate(); counter = 0; if (force) SetUpdate(false); } fLastLoc = loc; } bool TMagnify::NeedToUpdate() { return fNeedToUpdate; } void TMagnify::SetUpdate(bool s) { fNeedToUpdate = s; } void TMagnify::CopyImage() { StartSave(); be_clipboard->Lock(); be_clipboard->Clear(); BMessage *message = be_clipboard->Data(); if (!message) { puts(B_TRANSLATE_CONTEXT("no clip msg", "In console, when clipboard is empty after clicking Copy image")); return; } BMessage *embeddedBitmap = new BMessage(); (fImageView->Bitmap())->Archive(embeddedBitmap,false); status_t err = message->AddMessage(kBitmapMimeType, embeddedBitmap); if (err == B_OK) err = message->AddRect("rect", fImageView->Bitmap()->Bounds()); if (err == B_OK) be_clipboard->Commit(); be_clipboard->Unlock(); EndSave(); } void TMagnify::AddCrossHair() { if (fShowCrossHair1 && fShowCrossHair2) return; int32 w, h; PixelCount(&w, &h); if (fShowCrossHair1) { fSelection = 2; fShowCrossHair2 = true; fCrossHair2.x = fCrossHair1.x + 2; if (fCrossHair2.x >= w) fCrossHair2.x = 0; fCrossHair2.y = fCrossHair1.y + 2; if (fCrossHair2.y >= h) fCrossHair2.y = 0; } else { fSelection = 1; fShowCrossHair1 = true; fCrossHair1.x = fSelectionLoc.x + 2; if (fCrossHair1.x >= w) fCrossHair1.x = 0; fCrossHair1.y = fSelectionLoc.y + 2; if (fCrossHair1.y >= h) fCrossHair1.y = 0; } Invalidate(); } void TMagnify::RemoveCrossHair() { if (!fShowCrossHair1 && !fShowCrossHair2) return; if (fShowCrossHair2) { fSelection = 1; fShowCrossHair2 = false; } else if (fShowCrossHair1) { fSelection = 0; fShowCrossHair1 = false; } Invalidate(); } void TMagnify::SetCrossHairsShowing(bool ch1, bool ch2) { fShowCrossHair1 = ch1; fShowCrossHair2 = ch2; } void TMagnify::CrossHairsShowing(bool* ch1, bool* ch2) { *ch1 = fShowCrossHair1; *ch2 = fShowCrossHair2; } void TMagnify::MakeActive(bool s) { fActive = s; } void TMagnify::MakeSticked(bool s) { fStickCoordinates = s; } void TMagnify::PixelCount(int32* width, int32* height) { fParent->PixelCount(width, height); } int32 TMagnify::PixelSize() { return fParent->PixelSize(); } bool TMagnify::ShowGrid() { return fParent->ShowGrid(); } void TMagnify::StartSave() { fImageFrozenOnSave = Active(); if (fImageFrozenOnSave) MakeActive(false); } void TMagnify::EndSave() { if (fImageFrozenOnSave) MakeActive(true); } void TMagnify::SaveImage(entry_ref* ref, char* name) { // create a new file BFile file; BDirectory parentDir(ref); parentDir.CreateFile(name, &file); // Write the screenshot bitmap to the file BBitmapStream stream(fImageView->Bitmap()); BTranslatorRoster* roster = BTranslatorRoster::Default(); roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT, B_TRANSLATOR_BITMAP); BBitmap* bitmap; stream.DetachBitmap(&bitmap); // The stream takes over ownership of the bitmap // unfreeze the image, image was frozen before invoke of FilePanel EndSave(); } // #pragma mark - TOSMagnify::TOSMagnify(BRect r, TMagnify* parent, color_space space) : BView(r, "ImageView", B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), fColorSpace(space), fParent(parent) { switch (space) { case B_CMAP8: fBytesPerPixel = 1; break; case B_RGB15: case B_RGBA15: case B_RGB15_BIG: case B_RGBA15_BIG: case B_RGB16: case B_RGB16_BIG: fBytesPerPixel = 2; break; case B_RGB24: fBytesPerPixel = 3; break; case B_RGB32: case B_RGBA32: case B_RGB32_BIG: case B_RGBA32_BIG: fBytesPerPixel = 4; break; default: // uh, oh -- a color space we don't support fprintf(stderr, "Tried to run in an unsupported color space; exiting\n"); exit(1); break; } fPixel = NULL; fBitmap = NULL; fOldBits = NULL; InitObject(); } TOSMagnify::~TOSMagnify() { delete fPixel; delete fBitmap; free(fOldBits); } void TOSMagnify::SetSpace(color_space space) { fColorSpace = space; InitObject(); }; void TOSMagnify::InitObject() { int32 w, h; fParent->PixelCount(&w, &h); delete fBitmap; BRect bitsRect(0, 0, w-1, h-1); fBitmap = new BBitmap(bitsRect, fColorSpace); free(fOldBits); fOldBits = (char*)malloc(fBitmap->BitsLength()); if (!fPixel) { #if B_HOST_IS_BENDIAN fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32_BIG, true); #else fPixel = new BBitmap(BRect(0,0,0,0), B_RGBA32, true); #endif fPixelView = new BView(BRect(0,0,0,0), NULL, 0, 0); fPixel->Lock(); fPixel->AddChild(fPixelView); fPixel->Unlock(); } } void TOSMagnify::FrameResized(float width, float height) { BView::FrameResized(width, height); InitObject(); } void TOSMagnify::Resize(int32 width, int32 height) { ResizeTo(width, height); InitObject(); } bool TOSMagnify::CreateImage(BPoint mouseLoc, bool force) { bool created = false; if (Window() && Window()->Lock()) { int32 width, height; fParent->PixelCount(&width, &height); int32 pixelSize = fParent->PixelSize(); BRect srcRect(0, 0, width - 1, height - 1); srcRect.OffsetBy(mouseLoc.x - (width / 2), mouseLoc.y - (height / 2)); if (force || CopyScreenRect(srcRect)) { srcRect.OffsetTo(BPoint(0, 0)); BRect destRect(Bounds()); DrawBitmap(fBitmap, srcRect, destRect); DrawGrid(width, height, destRect, pixelSize); DrawSelection(); Sync(); created = true; } Window()->Unlock(); } else puts("window problem"); return created; } bool TOSMagnify::CopyScreenRect(BRect srcRect) { // constrain src rect to legal screen rect BScreen screen(Window()); BRect scrnframe = screen.Frame(); if (srcRect.right > scrnframe.right) srcRect.OffsetTo(scrnframe.right - srcRect.Width(), srcRect.top); if (srcRect.top < 0) srcRect.OffsetTo(srcRect.left, 0); if (srcRect.bottom > scrnframe.bottom) srcRect.OffsetTo(srcRect.left, scrnframe.bottom - srcRect.Height()); if (srcRect.left < 0) srcRect.OffsetTo(0, srcRect.top); // save a copy of the bits for comparison later memcpy(fOldBits, fBitmap->Bits(), fBitmap->BitsLength()); screen.ReadBitmap(fBitmap, false, &srcRect); // let caller know whether bits have actually changed return memcmp(fBitmap->Bits(), fOldBits, fBitmap->BitsLength()) != 0; } void TOSMagnify::DrawGrid(int32 width, int32 height, BRect destRect, int32 pixelSize) { // draw grid if (fParent->ShowGrid() && fParent->PixelSize() > 2) { BeginLineArray(width * height); // horizontal lines for (int32 i = pixelSize; i < (height * pixelSize); i += pixelSize) AddLine(BPoint(0, i), BPoint(destRect.right, i), kGridGray); // vertical lines for (int32 i = pixelSize; i < (width * pixelSize); i += pixelSize) AddLine(BPoint(i, 0), BPoint(i, destRect.bottom), kGridGray); EndLineArray(); } SetHighColor(kGridGray); StrokeRect(destRect); } void TOSMagnify::DrawSelection() { if (!fParent->SelectionIsShowing()) return; float x, y; int32 pixelSize = fParent->PixelSize(); int32 squareSize = pixelSize - 2; fParent->SelectionLoc(&x, &y); x *= pixelSize; x++; y *= pixelSize; y++; BRect selRect(x, y, x+squareSize, y+squareSize); short selection = fParent->Selection(); PushState(); SetLowColor(ViewColor()); SetHighColor(kRedColor); StrokeRect(selRect); if (selection == 0) { StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); } bool ch1Showing, ch2Showing; fParent->CrossHairsShowing(&ch1Showing, &ch2Showing); if (ch1Showing) { SetHighColor(kBlueColor); fParent->CrossHair1Loc(&x, &y); x *= pixelSize; x++; y *= pixelSize; y++; selRect.Set(x, y,x+squareSize, y+squareSize); StrokeRect(selRect); BeginLineArray(4); AddLine(BPoint(0, y+(squareSize/2)), BPoint(x, y+(squareSize/2)), kBlueColor); // left AddLine(BPoint(x+squareSize,y+(squareSize/2)), BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right AddLine(BPoint(x+(squareSize/2), 0), BPoint(x+(squareSize/2), y), kBlueColor); // top AddLine(BPoint(x+(squareSize/2), y+squareSize), BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom EndLineArray(); if (selection == 1) { StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); } } if (ch2Showing) { SetHighColor(kBlueColor); fParent->CrossHair2Loc(&x, &y); x *= pixelSize; x++; y *= pixelSize; y++; selRect.Set(x, y,x+squareSize, y+squareSize); StrokeRect(selRect); BeginLineArray(4); AddLine(BPoint(0, y+(squareSize/2)), BPoint(x, y+(squareSize/2)), kBlueColor); // left AddLine(BPoint(x+squareSize,y+(squareSize/2)), BPoint(Bounds().Width(), y+(squareSize/2)), kBlueColor); // right AddLine(BPoint(x+(squareSize/2), 0), BPoint(x+(squareSize/2), y), kBlueColor); // top AddLine(BPoint(x+(squareSize/2), y+squareSize), BPoint(x+(squareSize/2), Bounds().Height()), kBlueColor); // bottom EndLineArray(); if (selection == 2) { StrokeLine(BPoint(x,y), BPoint(x+squareSize,y+squareSize)); StrokeLine(BPoint(x,y+squareSize), BPoint(x+squareSize,y)); } } PopState(); } rgb_color TOSMagnify::ColorAtSelection() { float x, y; fParent->SelectionLoc(&x, &y); BRect srcRect(x, y, x, y); BRect dstRect(0, 0, 0, 0); fPixel->Lock(); fPixelView->DrawBitmap(fBitmap, srcRect, dstRect); fPixelView->Sync(); fPixel->Unlock(); uint32 pixel = *((uint32*)fPixel->Bits()); rgb_color c; c.alpha = pixel >> 24; c.red = (pixel >> 16) & 0xFF; c.green = (pixel >> 8) & 0xFF; c.blue = pixel & 0xFF; return c; } // #pragma mark - int main(int argc, char* argv[]) { int32 pixelCount = -1; if (argc > 2) { puts(B_TRANSLATE_CONTEXT( "usage: magnify [size] (magnify size * size pixels)", "Console")); exit(1); } else { if (argc == 2) { pixelCount = abs(atoi(argv[1])); if ((pixelCount > 100) || (pixelCount < 4)) { puts(B_TRANSLATE_CONTEXT( "usage: magnify [size] (magnify size * size pixels)", "Console")); puts(B_TRANSLATE_CONTEXT( " size must be > 4 and a multiple of 4", "Console")); exit(1); } if (pixelCount % 4) { puts(B_TRANSLATE_CONTEXT( "magnify: size must be a multiple of 4", "Console")); exit(1); } } } TApp app(pixelCount); app.Run(); return 0; }