/* * Copyright 2002-2008, 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 const int32 msg_help = 'help'; 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_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 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 CenterWindowOnScreen(BWindow* w) { BRect screenFrame = (BScreen(B_MAIN_SCREEN_ID).Frame()); BPoint pt; pt.x = screenFrame.Width()/2 - w->Bounds().Width()/2; pt.y = screenFrame.Height()/2 - w->Bounds().Height()/2; if (screenFrame.Contains(pt)) w->MoveTo(pt); } 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("About Magnify" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED)); menu->AddItem(menuItem); menuItem = new BMenuItem("Help" B_UTF8_ELLIPSIS, new BMessage(msg_help)); menu->AddItem(menuItem); menu->AddSeparatorItem(); menuItem = new BMenuItem("Save Image", new BMessage(msg_save),'S'); menu->AddItem(menuItem); // menuItem = new BMenuItem("Save Selection", new BMessage(msg_save),'S'); // menu->AddItem(menuItem); menuItem = new BMenuItem("Copy Image", new BMessage(msg_copy_image),'C'); menu->AddItem(menuItem); menu->AddSeparatorItem(); menuItem = new BMenuItem("Hide/Show Info", new BMessage(msg_show_info),'T'); menu->AddItem(menuItem); menuItem = new BMenuItem("Add a Crosshair", new BMessage(msg_add_cross_hair),'H'); menu->AddItem(menuItem); menuItem = new BMenuItem("Remove a Crosshair", new BMessage(msg_remove_cross_hair), 'H', B_SHIFT_KEY); menu->AddItem(menuItem); menuItem = new BMenuItem("Hide/Show Grid", new BMessage(msg_toggle_grid),'G'); menu->AddItem(menuItem); menu->AddSeparatorItem(); menuItem = new BMenuItem("Freeze/Unfreeze image", new BMessage(msg_freeze),'F'); menu->AddItem(menuItem); menuItem = new BMenuItem("Stick Coordinates", new BMessage(msg_stick), 'I'); menu->AddItem(menuItem); menu->AddSeparatorItem(); menuItem = new BMenuItem("Make Square", new BMessage(msg_make_square),'/'); menu->AddItem(menuItem); menuItem = new BMenuItem("Decrease Window Size", new BMessage(msg_shrink),'-'); menu->AddItem(menuItem); menuItem = new BMenuItem("Increase Window Size", new BMessage(msg_grow),'+'); menu->AddItem(menuItem); menuItem = new BMenuItem("Decrease Pixel Size", new BMessage(msg_shrink_pixel),','); menu->AddItem(menuItem); menuItem = new BMenuItem("Increase Pixel Size", new BMessage(msg_grow_pixel),'.'); menu->AddItem(menuItem); } // #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(); } void TApp::MessageReceived(BMessage* msg) { switch (msg->what) { case B_ABOUT_REQUESTED: AboutRequested(); break; default: BApplication::MessageReceived(msg); break; } } void TApp::ReadyToRun() { BApplication::ReadyToRun(); } void TApp::AboutRequested() { (new BAlert("", "Magnify!\n\n" B_UTF8_COPYRIGHT "2002-2006 Haiku\n(C)1999 Be Inc.\n\n" "Now with even more features and recompiled for Haiku.", "OK"))->Go(); } // #pragma mark - TWindow::TWindow(int32 pixelCount) : BWindow(BRect(0, 0, 0, 0), "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); AddShortcut('I', B_COMMAND_KEY, new BMessage(B_ABOUT_REQUESTED)); 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)); } TWindow::~TWindow() { } void TWindow::MessageReceived(BMessage* m) { bool active = fFatBits->Active(); switch (m->what) { case B_ABOUT_REQUESTED: be_app->MessageReceived(m); break; case msg_help: ShowHelp(); break; case msg_show_info: if (active) 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_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.h"); 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 CenterWindowOnScreen(this); // set all the settings to defaults if we get here DONE: fShowGrid = showGrid; fShowInfo = 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()); } 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); } bool TWindow::InfoIsShowing() { return fShowInfo; } void TWindow::UpdateInfo() { fInfo->Invalidate(); } 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 == fPixelSize) return; fPixelSize = s; // resize window // tell info that size has changed // tell mag that size has changed CalcViewablePixels(); ResizeWindow(fHPixelCount, fVPixelCount); } void TWindow::SetPixelSize(bool d) { if (d) { // grow fPixelSize++; if (fPixelSize > 16) fPixelSize = 16; } else { fPixelSize--; if (fPixelSize < 1) fPixelSize = 1; } 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()); } int32 TWindow::PixelSize() { return fPixelSize; } void TWindow::ShowHelp() { BRect r(0, 0, 375, 240); BWindow* w = new BWindow(r, "Magnify Help", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_NOT_RESIZABLE); r.right -= B_V_SCROLL_BAR_WIDTH; r.bottom -= B_H_SCROLL_BAR_HEIGHT; BRect r2(r); r2.InsetBy(4, 4); BTextView* text = new BTextView(r, "text", r2, B_FOLLOW_ALL, B_WILL_DRAW); text->MakeSelectable(false); text->MakeEditable(false); BScrollView* scroller = new BScrollView("", text, B_FOLLOW_ALL, 0, true, true); w->AddChild(scroller); text->Insert("General:\n"); text->Insert(" 32 x 32 - the top left numbers are the number of visible\n"); text->Insert(" pixels (width x height)\n"); text->Insert(" 8 pixels/pixel - represents the number of pixels that are\n"); text->Insert(" used to magnify a pixel\n"); text->Insert(" R:152 G:52 B:10 - the RGB values for the pixel under\n"); text->Insert(" the red square\n"); text->Insert("\n\n"); text->Insert("Copy/Save:\n"); text->Insert(" copy - copies the current image to the clipboard\n"); text->Insert(" save - prompts the user for a file to save to and writes out\n"); text->Insert(" the bits of the image\n"); text->Insert("\n\n"); text->Insert("Info:\n"); text->Insert(" hide/show info - hides/shows all these new features\n"); text->Insert(" note: when showing, a red square will appear which signifies\n"); text->Insert(" which pixel's rgb values will be displayed\n"); text->Insert(" add/remove crosshairs - 2 crosshairs can be added (or removed)\n"); text->Insert(" to aid in the alignment and placement of objects.\n"); text->Insert(" The crosshairs are represented by blue squares and blue lines.\n"); text->Insert(" hide/show grid - hides/shows the grid that separates each pixel\n"); text->Insert("\n\n"); text->Insert(" freeze - freezes/unfreezes magnification of whatever the\n"); text->Insert(" cursor is currently over\n"); text->Insert("\n\n"); text->Insert("Sizing & Resizing:\n"); text->Insert(" make square - sets the width and the height to the larger\n"); text->Insert(" of the two making a square image\n"); text->Insert(" increase/decrease window size - grows or shrinks the window\n"); text->Insert(" size by 4 pixels.\n"); text->Insert(" note: this window can also be resized to any size via the\n"); text->Insert(" resizing region of the window\n"); text->Insert(" increase/decrease pixel size - increases or decreases the number\n"); text->Insert(" of pixels used to magnify a 'real' pixel. Range is 1 to 16.\n"); text->Insert("\n\n"); text->Insert("Navigation:\n"); text->Insert(" arrow keys - move the current selection (rgb indicator or crosshair)\n"); text->Insert(" around 1 pixel at a time\n"); text->Insert(" option-arrow key - moves the mouse location 1 pixel at a time\n"); text->Insert(" x marks the selection - the current selection has an 'x' in it\n"); CenterWindowOnScreen(w); w->Show(); } bool TWindow::IsActive() { return fFatBits->Active(); } // #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; } TInfoView::~TInfoView() { } void TInfoView::AttachedToWindow() { BBox::AttachedToWindow(); 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(10, fFontHeight + 5); char str[64]; sprintf(str, "%li x %li @ %li pixels/pixel", hPixelCount, vPixelCount, pixelSize); invalRect.Set(10, 5, 10 + StringWidth(fInfoStr), fFontHeight+7); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(0, 0, 0, 255); strcpy(fInfoStr, str); DrawString(fInfoStr); rgb_color c = { 0,0,0, 255 }; uchar index = 0; if (fMagView) { c = fMagView->SelectionColor(); BScreen s; index = s.IndexForColor(c); } MovePenTo(10, fFontHeight*2+5); sprintf(str, "R: %i G: %i B: %i (0x%x)", c.red, c.green, c.blue, index); invalRect.Set(10, fFontHeight+7, 10 + StringWidth(fRGBStr), fFontHeight*2+7); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(0,0,0,255); strcpy(fRGBStr,str); 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: %li y: %li y: %i", (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(0,0,0,255); strcpy(fCH2Str,str); DrawString(fCH2Str); } if (ch1Showing && ch2Showing) { MovePenTo(10, h-10-fFontHeight-2); sprintf(str, "1) x: %li y: %li x: %i", (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(0,0,0,255); strcpy(fCH1Str,str); DrawString(fCH1Str); } else if (ch1Showing) { MovePenTo(10, h-10); sprintf(str, "x: %li y: %li", (int32)pt1.x, (int32)pt1.y); invalRect.Set(10, h-10-fFontHeight, 10 + StringWidth(fCH1Str), h-8); SetHighColor(ViewColor()); FillRect(invalRect); SetHighColor(0,0,0,255); strcpy(fCH1Str,str); 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(Bounds().Width()-27, 11, Bounds().Width()-11, 27); fPopUp = new BMenuField( r, "region menu", NULL, fMenu, true, B_FOLLOW_RIGHT | 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() { bool state = true; if (fMainWindow) state = fMainWindow->IsActive(); BMenuItem* menuItem = FindItem("Hide/Show Info"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Add a Crosshair"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Remove a Crosshair"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Hide/Show Grid"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Make Square"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Decrease Window Size"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Increase Window Size"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Decrease Pixel Size"); if (menuItem) menuItem->SetEnabled(state); menuItem = FindItem("Increase Pixel Size"); if (menuItem) menuItem->SetEnabled(state); BMenu::AttachedToWindow(); } // #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) { SetViewColor(B_TRANSPARENT_32_BIT); } 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); 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); uint32 mods = modifiers(); 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: if (mods & B_OPTION_KEY) NudgeMouse(-1,0); else MoveSelection(-1,0); break; case B_RIGHT_ARROW: if (mods & B_OPTION_KEY) NudgeMouse(1, 0); else MoveSelection(1,0); break; case B_UP_ARROW: if (mods & B_OPTION_KEY) NudgeMouse(0, -1); else MoveSelection(0,-1); break; case B_DOWN_ARROW: if (mods & B_OPTION_KEY) NudgeMouse(0, 1); else 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("Info"); menu->SetFont(be_plain_font); BuildInfoMenu(menu); 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; ulong 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(); } long 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; ulong 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) { printf("no clip msg\n"); 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, bool selectionOnly) { // create a new file BFile file; BDirectory parentDir(ref); parentDir.CreateFile(name, &file); // write off the bitmaps bits to the file SaveBits(&file, fImageView->Bitmap(), "Data"); // unfreeze the image, image was frozen before invoke of FilePanel EndSave(); } void TMagnify::SaveBits(BFile* file, const BBitmap *bitmap, const char* name) const { int32 bytesPerPixel; const char *kColorSpaceName; switch (bitmap->ColorSpace()) { case B_GRAY8: bytesPerPixel = 1; kColorSpaceName = "B_GRAY8"; break; case B_CMAP8: bytesPerPixel = 1; kColorSpaceName = "B_CMAP8"; break; case B_RGB15: case B_RGBA15: case B_RGB15_BIG: case B_RGBA15_BIG: bytesPerPixel = 2; kColorSpaceName = "B_RGB15"; break; case B_RGB16: case B_RGB16_BIG: bytesPerPixel = 2; kColorSpaceName = "B_RGB16"; break; case B_RGB32: case B_RGBA32: case B_RGBA32_BIG: case B_BIG_RGB_32_BIT: bytesPerPixel = 3; kColorSpaceName = "B_RGB32"; break; default: printf("dump: usupported ColorSpace\n"); return; } char str[1024]; // stream out the width, height and ColorSpace sprintf(str, "const int32 k%sWidth = %ld;\n", name, (int32)bitmap->Bounds().Width()+1); file->Write(str, strlen(str)); sprintf(str, "const int32 k%sHeight = %ld;\n", name, (int32)bitmap->Bounds().Height()+1); file->Write(str, strlen(str)); sprintf(str, "const color_space k%sColorSpace = %s;\n\n", name, kColorSpaceName); file->Write(str, strlen(str)); // stream out the constant name for this array sprintf(str, "const unsigned char k%sBits [] = {", name); file->Write(str, strlen(str)); const unsigned char *bits = (const unsigned char *)bitmap->Bits(); const int32 kMaxColumnWidth = 16; int32 bytesPerRow = bitmap->BytesPerRow(); int32 columnWidth = (bytesPerRow < kMaxColumnWidth) ? bytesPerRow : kMaxColumnWidth; for (int32 remaining = bitmap->BitsLength(); remaining; ) { sprintf(str, "\n\t"); file->Write(str, strlen(str)); // stream out each row, based on the number of bytes required per row // padding is in the bitmap and will be streamed as 0xff for (int32 column = 0; column < columnWidth; column++) { // stream out individual pixel components for (int32 count = 0; count < bytesPerPixel; count++) { --remaining; sprintf(str, "0x%02x", *bits++); file->Write(str, strlen(str)); if (remaining) { sprintf(str, ","); file->Write(str, strlen(str)); } else break; } // make sure we don't walk off the end of the bits array if (!remaining) break; } } sprintf(str, "\n};\n\n"); file->Write(str, strlen(str)); } // #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 printf("window problem\n"); 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) { printf("usage: magnify [size] (magnify size * size pixels)\n"); exit(1); } else { if (argc == 2) { pixelCount = abs(atoi(argv[1])); if ((pixelCount > 100) || (pixelCount < 4)) { printf("usage: magnify [size] (magnify size * size pixels)\n"); printf(" size must be > 4 and a multiple of 4\n"); exit(1); } if (pixelCount % 4) { printf("magnify: size must be a multiple of 4\n"); exit(1); } } } TApp app(pixelCount); app.Run(); return 0; }