From 9f07ffa384d19bcc4ca0f232015bbfa8325d1d1b Mon Sep 17 00:00:00 2001 From: Karsten Heimrich Date: Fri, 19 Sep 2008 23:14:23 +0000 Subject: [PATCH] * reorganize options * implement silent mode using the existing BWindow code git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27637 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/screenshot/Jamfile | 4 +- src/apps/screenshot/PNGDump.cpp | 199 +++++++++++++++++++++++ src/apps/screenshot/PNGDump.h | 20 +++ src/apps/screenshot/Screenshot.cpp | 12 +- src/apps/screenshot/ScreenshotWindow.cpp | 121 +++++++++----- src/apps/screenshot/ScreenshotWindow.h | 13 +- 6 files changed, 317 insertions(+), 52 deletions(-) create mode 100644 src/apps/screenshot/PNGDump.cpp create mode 100644 src/apps/screenshot/PNGDump.h diff --git a/src/apps/screenshot/Jamfile b/src/apps/screenshot/Jamfile index 1d2e3734ab..a575a47b07 100644 --- a/src/apps/screenshot/Jamfile +++ b/src/apps/screenshot/Jamfile @@ -1,12 +1,14 @@ SubDir HAIKU_TOP src apps screenshot ; +UseLibraryHeaders png zlib ; UsePrivateHeaders interface ; Application Screenshot : main.cpp + PNGDump.cpp Screenshot.cpp ScreenshotWindow.cpp - : be translation + : be translation libpng.so libz.so : Screenshot.rdef ; diff --git a/src/apps/screenshot/PNGDump.cpp b/src/apps/screenshot/PNGDump.cpp new file mode 100644 index 0000000000..4acf24c26d --- /dev/null +++ b/src/apps/screenshot/PNGDump.cpp @@ -0,0 +1,199 @@ +/* + * Copyright 2001-2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Stephan Aßmus + */ + +/** Function for saving a generic framebuffer to a PNG file */ + +#include "PNGDump.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include + + +#define TRACE_PNGDUMP +#ifdef TRACE_PNGDUMP +# define TRACE(x) printf x +#else +# define TRACE(x) ; +#endif + + +status_t +SaveToPNG(const char* filename, const BRect& bounds, color_space space, + const void* bits, int32 bitsLength, int32 bytesPerRow) +{ + int32 width = bounds.IntegerWidth() + 1; + int32 height = bounds.IntegerHeight() + 1; + + TRACE(("SaveToPNG: %s (%ldx%ld)\n", filename, width, height)); + + FILE *file = fopen(filename, "wb"); + if (file == NULL) { + TRACE(("Couldn't open file: %s\n", strerror(errno))); + return errno; + } + + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (png == NULL) { + TRACE(("Couldn't create write struct\n")); + fclose(file); + return B_NO_MEMORY; + } + + png_infop info = png_create_info_struct(png); + if (info == NULL) { + TRACE(("Couldn't create info struct\n")); + png_destroy_write_struct(&png, NULL); + fclose(file); + return B_NO_MEMORY; + } + + if (setjmp(png->jmpbuf)) { + png_destroy_write_struct(&png, NULL); + fclose(file); + return B_ERROR; + } + + png_init_io(png, file); + png_set_bgr(png); + + // TODO: support other color spaces if needed + + switch (space) { + case B_RGB32: + case B_RGBA32: + { + // create file without alpha channel + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, info); + + // convert from 32 bit RGB to 24 bit RGB while saving + png_byte* src = (png_byte*)bits; + int srcRowBytes = width * 4; + int dstRowBytes = width * 3; + int srcRowOffset = bytesPerRow - srcRowBytes; + png_byte tempRow[dstRowBytes]; + for (int row = 0; row < height; row++) { + for (int i = 0; i < dstRowBytes; i += 3, src += 4) { + tempRow[i] = src[0]; + tempRow[i + 1] = src[1]; + tempRow[i + 2] = src[2]; + } + src += srcRowOffset; + png_write_row(png, tempRow); + } + break; + } + + case B_RGB16: + { + // create file without alpha channel + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, info); + + // convert from 16 bit RGB to 24 bit RGB while saving + uint16* src = (uint16 *)bits; + int dstRowBytes = width * 3; + png_byte tempRow[dstRowBytes]; + for (int row = 0; row < height; row++) { + for (int i = 0; i < dstRowBytes; i += 3, src++) { + tempRow[i + 2] = (*src & 0xf800) >> 8; + tempRow[i + 1] = (*src & 0x07e0) >> 3; + tempRow[i] = (*src & 0x001f) << 3; + } + src = (uint16 *)((uint8 *)bits + row * bytesPerRow); + png_write_row(png, tempRow); + } + break; + } + + case B_RGB15: + { + // create file without alpha channel + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, info); + + // convert from 15 bit RGB to 24 bit RGB while saving + uint16* src = (uint16 *)bits; + int dstRowBytes = width * 3; + png_byte tempRow[dstRowBytes]; + for (int row = 0; row < height; row++) { + for (int i = 0; i < dstRowBytes; i += 3, src++) { + tempRow[i + 2] = (*src & 0x7c00) >> 7; + tempRow[i + 1] = (*src & 0x03e0) >> 2; + tempRow[i] = (*src & 0x001f) << 3; + } + src = (uint16 *)((uint8 *)bits + row * bytesPerRow); + png_write_row(png, tempRow); + } + break; + } + + case B_CMAP8: + { + // create file without alpha channel + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, info); + + // convert from 8 bit CMAP to 24 bit RGB while saving + const color_map *colorMap = system_colors(); + uint8* src = (uint8 *)bits; + int dstRowBytes = width * 3; + png_byte tempRow[dstRowBytes]; + for (int row = 0; row < height; row++) { + for (int i = 0; i < dstRowBytes; i += 3, src++) { + tempRow[i + 2] = colorMap->color_list[*src].red; + tempRow[i + 1] = colorMap->color_list[*src].green; + tempRow[i] = colorMap->color_list[*src].blue; + } + src = (uint8 *)bits + row * bytesPerRow; + png_write_row(png, tempRow); + } + break; + } + + default: + { + TRACE(("Unsupported color space %x\n", space)); + png_destroy_write_struct(&png, NULL); + fclose(file); + return B_ERROR; + } + } + + png_write_end(png, info); + png_destroy_write_struct(&png, NULL); + + fclose(file); + + // Set the file type manually, so that it doesn't have to be + // picked up by the registrar or Tracker, first + BNode node(filename); + BNodeInfo nodeInfo(&node); + if (nodeInfo.InitCheck() == B_OK) + nodeInfo.SetType("image/png"); + + return B_OK; +} diff --git a/src/apps/screenshot/PNGDump.h b/src/apps/screenshot/PNGDump.h new file mode 100644 index 0000000000..6c4b2f4fd7 --- /dev/null +++ b/src/apps/screenshot/PNGDump.h @@ -0,0 +1,20 @@ +/* + * Copyright 2001-2005, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + */ +#ifndef PNGDUMP_H +#define PNGDUMP_H + + +#include + +class BRect; + + +status_t SaveToPNG(const char* filename, const BRect& bounds, color_space space, + const void* bits, int32 bitsLength, int32 bytesPerRow); + +#endif diff --git a/src/apps/screenshot/Screenshot.cpp b/src/apps/screenshot/Screenshot.cpp index 43c28e4d3e..72eedb0e4a 100644 --- a/src/apps/screenshot/Screenshot.cpp +++ b/src/apps/screenshot/Screenshot.cpp @@ -49,11 +49,14 @@ Screenshot::RefsReceived(BMessage* message) bool grabActiveWindow = false; message->FindBool("window", &grabActiveWindow); + bool saveScreenshotSilent = false; + message->FindBool("silent", &saveScreenshotSilent); + bool showConfigureWindow = false; message->FindBool("configure", &showConfigureWindow); new ScreenshotWindow(delay * 1000000, includeBorder, includeCursor, - grabActiveWindow, showConfigureWindow); + grabActiveWindow, showConfigureWindow, saveScreenshotSilent); } @@ -66,6 +69,7 @@ Screenshot::ArgvReceived(int32 argc, char** argv) bool includeCursor = false; bool grabActiveWindow = false; bool showConfigureWindow = false; + bool saveScreenshotSilent = false; for (int32 i = 0; i < argc; i++) { if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) @@ -76,6 +80,8 @@ Screenshot::ArgvReceived(int32 argc, char** argv) includeCursor = true; else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--window") == 0) grabActiveWindow = true; + else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--silent") == 0) + saveScreenshotSilent = true; else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--options") == 0) showConfigureWindow = true; else if (strcmp(argv[i], "-d") == 0 @@ -90,7 +96,7 @@ Screenshot::ArgvReceived(int32 argc, char** argv) } fArgvReceived = true; new ScreenshotWindow(delay, includeBorder, includeCursor, grabActiveWindow, - showConfigureWindow); + showConfigureWindow, saveScreenshotSilent); } @@ -104,6 +110,8 @@ Screenshot::_ShowHelp() const printf("..-b, --border Include the window border with the screenshot\n"); printf("..-w, --window Use active window instead of the entire screen\n"); printf("..-d, --delay=seconds Take screenshot after specified delay [in seconds]\n"); + printf("..-s, --silent Saves the screenshot without to application window\n"); + printf("........................overwrites options, saves to home directory as png\n"); printf("\n"); printf("Note: OPTION -b, --border takes only effect when used with -w, --window\n"); diff --git a/src/apps/screenshot/ScreenshotWindow.cpp b/src/apps/screenshot/ScreenshotWindow.cpp index db58422841..d71388473b 100644 --- a/src/apps/screenshot/ScreenshotWindow.cpp +++ b/src/apps/screenshot/ScreenshotWindow.cpp @@ -5,19 +5,27 @@ #include "ScreenshotWindow.h" +#include "PNGDump.h" + + +#include #include #include #include #include #include +#include +#include +#include #include #include #include #include #include #include -#include +#include #include +#include #include #include #include @@ -45,24 +53,9 @@ enum { }; -ScreenshotWindow::ScreenshotWindow() - : BWindow(BRect(0, 0, 200.0, 100.0), "Save Screenshot", B_TITLED_WINDOW, - B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_QUIT_ON_WINDOW_CLOSE | - B_AVOID_FRONT | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE), - fScreenshot(NULL), - fDelay(0), - fIncludeBorder(false), - fIncludeCursor(false), - fGrabActiveWindow(false), - fShowConfigWindow(false) -{ - _InitWindow(); - _CenterAndShow(); -} - - ScreenshotWindow::ScreenshotWindow(bigtime_t delay, bool includeBorder, - bool includeCursor, bool grabActiveWindow, bool showConfigWindow) + bool includeCursor, bool grabActiveWindow, bool showConfigWindow, + bool saveScreenshotSilent) : BWindow(BRect(0, 0, 200.0, 100.0), "Take Screenshot", B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_QUIT_ON_WINDOW_CLOSE | B_AVOID_FRONT | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE), @@ -71,7 +64,8 @@ ScreenshotWindow::ScreenshotWindow(bigtime_t delay, bool includeBorder, fIncludeBorder(includeBorder), fIncludeCursor(includeCursor), fGrabActiveWindow(grabActiveWindow), - fShowConfigWindow(showConfigWindow) + fShowConfigWindow(showConfigWindow), + fSaveScreenshotSilent(saveScreenshotSilent) { _InitWindow(); _CenterAndShow(); @@ -167,7 +161,7 @@ ScreenshotWindow::_InitWindow() void ScreenshotWindow::_SetupFirstLayoutItem(BCardLayout* layout) { - BStringView* stringView = new BStringView("", "Take Screenshot"); + BStringView* stringView = new BStringView("", "Options"); stringView->SetFont(be_bold_font); stringView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); @@ -195,10 +189,6 @@ ScreenshotWindow::_SetupFirstLayoutItem(BCardLayout* layout) new BMessage(kShowCursor)); fShowCursor->SetValue(fIncludeCursor); - BStringView* stringView3 = new BStringView("", "Options"); - stringView3->SetFont(be_bold_font); - stringView3->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); - BBox* divider = new BBox(B_FANCY_BORDER, NULL); divider->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1)); @@ -212,31 +202,36 @@ ScreenshotWindow::_SetupFirstLayoutItem(BCardLayout* layout) .Add(stringView) .Add(BGridLayoutBuilder() .Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 0) - .Add(fActiveWindow, 1, 0) + .Add(fWholeDesktop, 1, 0) .Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 1) - .Add(fWholeDesktop, 1, 1) - .SetInsets(0.0, 5.0, 0.0, 5.0)) - .Add(BGroupLayoutBuilder(B_HORIZONTAL, 5.0) + .Add(fActiveWindow, 1, 1) + .SetInsets(0.0, 5.0, 0.0, 0.0)) + .AddGroup(B_HORIZONTAL) + .AddStrut(30.0) + .Add(fWindowBorder) + .End() + .AddStrut(10.0) + .AddGroup(B_HORIZONTAL) + .AddStrut(15.0) + .Add(fShowCursor) + .End() + .AddStrut(5.0) + .AddGroup(B_HORIZONTAL, 5.0) .AddStrut(10.0) .Add(fDelayControl->CreateLabelLayoutItem()) .Add(fDelayControl->CreateTextViewLayoutItem()) - .Add(stringView2)) + .Add(stringView2) + .End() .AddStrut(10.0) - .Add(stringView3) - .Add(BGridLayoutBuilder() - .Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 0) - .Add(fWindowBorder, 1, 0) - .Add(BSpaceLayoutItem::CreateHorizontalStrut(15.0), 0, 1) - .Add(fShowCursor, 1, 1) - .SetInsets(0.0, 5.0, 0.0, 5.0)) .AddGlue() .Add(divider) - .AddStrut(5) - .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10.0) + .AddStrut(10) + .AddGroup(B_HORIZONTAL, 10.0) .Add(fBackToSave) .AddGlue() .Add(new BButton("", "Cancel", new BMessage(B_QUIT_REQUESTED))) - .Add(fTakeScreenshot)) + .Add(fTakeScreenshot) + .End() .SetInsets(10.0, 10.0, 10.0, 10.0) ); @@ -255,7 +250,7 @@ ScreenshotWindow::_SetupSecondLayoutItem(BCardLayout* layout) fPreviewBox->SetExplicitMinSize(BSize(200.0, B_SIZE_UNSET)); fPreviewBox->SetFlags(fPreviewBox->Flags() | B_FULL_UPDATE_ON_RESIZE); - fNameControl = new BTextControl("", "Name:", "Screenshot", NULL); + fNameControl = new BTextControl("", "Name:", "screenshot", NULL); BMessage message(kImageOutputFormat); fTranslatorMenu = new BMenu("Please select"); @@ -286,7 +281,7 @@ ScreenshotWindow::_SetupSecondLayoutItem(BCardLayout* layout) .Add(menuField2->CreateMenuBarLayoutItem(), 1, 2); gridLayout->SetMinColumnWidth(1, menuField->StringWidth("SomethingLongHere")); - fFinishScreenshot = new BButton("", "OK", new BMessage(kFinishScreenshot)); + fFinishScreenshot = new BButton("", "Save", new BMessage(kFinishScreenshot)); fFinishScreenshot->SetEnabled(false); layout->AddView(1, BGroupLayoutBuilder(B_VERTICAL) @@ -296,9 +291,9 @@ ScreenshotWindow::_SetupSecondLayoutItem(BCardLayout* layout) .Add(gridLayout->View()) .AddGlue() .End()) - .AddStrut(5) + .AddStrut(10) .Add(divider) - .AddStrut(5) + .AddStrut(10) .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10.0) .Add(new BButton("", "Options", new BMessage(kShowOptions))) .AddGlue() @@ -323,6 +318,12 @@ ScreenshotWindow::_DisallowChar(BTextView* textView) void ScreenshotWindow::_CenterAndShow() { + if (fSaveScreenshotSilent) { + _SaveScreenshotSilent(); + be_app_messenger.SendMessage(B_QUIT_REQUESTED); + return; + } + BSize size = GetLayout()->PreferredSize(); ResizeTo(size.Width(), size.Height()); @@ -386,10 +387,10 @@ ScreenshotWindow::_GetActiveWindowFrame(BRect* frame) free(windowInfo); if (fIncludeBorder) { - // TODO: this is wrong for windows with titlebar, change once + // TODO: that's wrong for windows without titlebar, change once // we can access the decorator or get it via window info frame->InsetBy(-5.0, -5.0); - frame->top -= 17.0; + frame->top -= 22.0; } BRect screenFrame(BScreen(this).Frame()); @@ -409,3 +410,33 @@ ScreenshotWindow::_GetActiveWindowFrame(BRect* frame) free(tokens); return status; } + + +void +ScreenshotWindow::_SaveScreenshotSilent() const +{ + if (!fScreenshot) + return; + + BPath homePath; + if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK) { + fprintf(stderr, "failed to find user home directory\n"); + return; + } + + BPath path; + BEntry entry; + int32 index = 1; + do { + char filename[32]; + sprintf(filename, "screenshot%ld.png", index++); + path = homePath; + path.Append(filename); + entry.SetTo(path.Path()); + } while (entry.Exists()); + + // Dump to PNG + SaveToPNG(path.Path(), fScreenshot->Bounds(), fScreenshot->ColorSpace(), + fScreenshot->Bits(), fScreenshot->BitsLength(), + fScreenshot->BytesPerRow()); +} diff --git a/src/apps/screenshot/ScreenshotWindow.h b/src/apps/screenshot/ScreenshotWindow.h index 872dcef2d2..aa6dd63bb0 100644 --- a/src/apps/screenshot/ScreenshotWindow.h +++ b/src/apps/screenshot/ScreenshotWindow.h @@ -18,10 +18,12 @@ class BTextView; class ScreenshotWindow : public BWindow { public: - ScreenshotWindow(); - ScreenshotWindow(bigtime_t delay, bool includeBorder, - bool includeCursor, bool grabActiveWindow, - bool showConfigWindow); + ScreenshotWindow(bigtime_t delay = 0, + bool includeBorder = false, + bool includeCursor = false, + bool grabActiveWindow = false, + bool showConfigWindow = false, + bool saveScreenshotSilent = false); virtual ~ScreenshotWindow(); virtual void MessageReceived(BMessage* message); @@ -36,6 +38,8 @@ private: void _TakeScreenshot(); status_t _GetActiveWindowFrame(BRect* frame); + void _SaveScreenshotSilent() const; + private: BBox* fPreviewBox; BRadioButton* fActiveWindow; @@ -57,6 +61,7 @@ private: bool fIncludeCursor; bool fGrabActiveWindow; bool fShowConfigWindow; + bool fSaveScreenshotSilent; int32 fTranslator; int32 fImageFileType;