1 /* 2 * Copyright 2010-2014, Haiku Inc. All rights reserved. 3 * Copyright 2010 Wim van der Meer <WPJvanderMeer@gmail.com> 4 * Copyright Karsten Heimrich, host.haiku@gmx.de. All rights reserved. 5 * Distributed under the terms of the MIT License. 6 * 7 * Authors: 8 * Axel Dörfler 9 * Karsten Heimrich 10 * Fredrik Modéen 11 * Christophe Huriaux 12 * Wim van der Meer 13 */ 14 15 16 #include "Utility.h" 17 18 #include <Bitmap.h> 19 #include <BitmapStream.h> 20 #include <Catalog.h> 21 #include <Clipboard.h> 22 #include <Entry.h> 23 #include <File.h> 24 #include <FindDirectory.h> 25 #include <Locale.h> 26 #include <Looper.h> 27 #include <MimeType.h> 28 #include <NodeInfo.h> 29 #include <Path.h> 30 #include <Region.h> 31 #include <Screen.h> 32 #include <String.h> 33 #include <Translator.h> 34 #include <View.h> 35 36 #include <AutoDeleter.h> 37 38 39 #undef B_TRANSLATION_CONTEXT 40 #define B_TRANSLATION_CONTEXT "Screenshot" 41 42 43 const char* Utility::sDefaultFileNameBase = B_TRANSLATE_MARK_COMMENT( 44 "screenshot", "Base filename of screenshot files"); 45 46 47 Utility::Utility() 48 : 49 wholeScreen(NULL), 50 cursorBitmap(NULL), 51 cursorAreaBitmap(NULL) 52 { 53 } 54 55 56 Utility::~Utility() 57 { 58 delete wholeScreen; 59 delete cursorBitmap; 60 delete cursorAreaBitmap; 61 } 62 63 64 void 65 Utility::CopyToClipboard(const BBitmap& screenshot) const 66 { 67 if (be_clipboard->Lock()) { 68 be_clipboard->Clear(); 69 BMessage* clipboard = be_clipboard->Data(); 70 if (clipboard != NULL) { 71 BMessage* bitmap = new BMessage(); 72 screenshot.Archive(bitmap); 73 clipboard->AddMessage("image/bitmap", bitmap); 74 be_clipboard->Commit(); 75 } 76 be_clipboard->Unlock(); 77 } 78 } 79 80 81 /*! Save the screenshot to the file with the specified filename and type. 82 Note that any existing file with the same filename will be overwritten 83 without warning. 84 */ 85 status_t 86 Utility::Save(BBitmap* screenshot, const char* fileName, uint32 imageType) 87 const 88 { 89 BString fileNameString(fileName); 90 91 // Generate a default filename when none is given 92 if (fileNameString.Compare("") == 0) { 93 BPath homePath; 94 if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK) 95 return B_ERROR; 96 97 BEntry entry; 98 int32 index = 1; 99 BString extension = FileNameExtension(imageType); 100 do { 101 fileNameString.SetTo(homePath.Path()); 102 fileNameString << "/" << B_TRANSLATE_NOCOLLECT(sDefaultFileNameBase) 103 << index++ << extension; 104 entry.SetTo(fileNameString); 105 } while (entry.Exists()); 106 } 107 108 // Create the file 109 BFile file(fileNameString, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 110 if (file.InitCheck() != B_OK) 111 return B_ERROR; 112 113 // Write the screenshot bitmap to the file 114 BBitmapStream stream(screenshot); 115 BTranslatorRoster* roster = BTranslatorRoster::Default(); 116 status_t status = roster->Translate(&stream, NULL, NULL, &file, imageType, 117 B_TRANSLATOR_BITMAP); 118 119 BBitmap* bitmap; 120 stream.DetachBitmap(&bitmap); 121 // The stream takes over ownership of the bitmap 122 123 if (status != B_OK) 124 return status; 125 126 // Set the file MIME attribute (don't mind too much if this fails) 127 BNodeInfo nodeInfo(&file); 128 if (nodeInfo.InitCheck() == B_OK) 129 nodeInfo.SetType(_MimeType(imageType)); 130 131 return B_OK; 132 } 133 134 135 BBitmap* 136 Utility::MakeScreenshot(bool includeMouse, bool activeWindow, 137 bool includeBorder) const 138 { 139 if (wholeScreen == NULL) 140 return NULL; 141 142 int cursorWidth = 0; 143 int cursorHeight = 0; 144 145 if (cursorBitmap != NULL) { 146 BRect bounds = cursorBitmap->Bounds(); 147 cursorWidth = bounds.IntegerWidth() + 1; 148 cursorHeight = bounds.IntegerHeight() + 1; 149 } 150 151 if (includeMouse && cursorBitmap != NULL) { 152 // Import the cursor bitmap into wholeScreen 153 wholeScreen->ImportBits(cursorBitmap->Bits(), 154 cursorBitmap->BitsLength(), cursorBitmap->BytesPerRow(), 155 cursorBitmap->ColorSpace(), BPoint(0, 0), cursorPosition, 156 cursorWidth, cursorHeight); 157 158 } else if (cursorAreaBitmap != NULL) { 159 // Import the cursor area bitmap into wholeScreen 160 wholeScreen->ImportBits(cursorAreaBitmap->Bits(), 161 cursorAreaBitmap->BitsLength(), cursorAreaBitmap->BytesPerRow(), 162 cursorAreaBitmap->ColorSpace(), BPoint(0, 0), cursorPosition, 163 cursorWidth, cursorHeight); 164 } 165 166 BBitmap* screenshot = NULL; 167 168 if (activeWindow && activeWindowFrame.IsValid()) { 169 BRect frame(activeWindowFrame); 170 if (includeBorder) { 171 frame.InsetBy(-borderSize, -borderSize); 172 frame.top -= tabFrame.bottom - tabFrame.top; 173 } 174 175 screenshot = new BBitmap(frame.OffsetToCopy(B_ORIGIN), 176 includeBorder ? B_RGBA32 : B_RGB32, true); 177 178 if (screenshot->ImportBits(wholeScreen->Bits(), 179 wholeScreen->BitsLength(), wholeScreen->BytesPerRow(), 180 wholeScreen->ColorSpace(), frame.LeftTop(), 181 BPoint(0, 0), frame.IntegerWidth() + 1, 182 frame.IntegerHeight() + 1) != B_OK) { 183 delete screenshot; 184 return NULL; 185 } 186 187 if (includeBorder) 188 _MakeTabSpaceTransparent(screenshot, frame); 189 } else 190 screenshot = new BBitmap(wholeScreen); 191 192 return screenshot; 193 } 194 195 196 BString 197 Utility::FileNameExtension(uint32 imageType) const 198 { 199 BMimeType mimeType(_MimeType(imageType)); 200 201 BMessage message; 202 if (mimeType.GetFileExtensions(&message) == B_OK) { 203 BString extension; 204 if (message.FindString("extensions", 0, &extension) == B_OK) { 205 extension.Prepend("."); 206 return extension; 207 } 208 } 209 210 return ""; 211 } 212 213 214 status_t 215 Utility::FindTranslator(uint32 imageType, translator_id& id, 216 BString* _mimeType) const 217 { 218 translator_id* translators = NULL; 219 int32 numTranslators = 0; 220 221 BTranslatorRoster* roster = BTranslatorRoster::Default(); 222 status_t status = roster->GetAllTranslators(&translators, &numTranslators); 223 if (status != B_OK) 224 return status; 225 226 ArrayDeleter<translator_id> deleter(translators); 227 228 for (int32 x = 0; x < numTranslators; x++) { 229 const translation_format* formats = NULL; 230 int32 numFormats; 231 232 if (roster->GetOutputFormats(translators[x], &formats, &numFormats) 233 == B_OK) { 234 for (int32 i = 0; i < numFormats; ++i) { 235 if (formats[i].type == imageType) { 236 id = translators[x]; 237 if (_mimeType != NULL) 238 *_mimeType = formats[i].MIME; 239 return B_OK; 240 } 241 } 242 } 243 } 244 245 return B_ERROR; 246 } 247 248 249 BString 250 Utility::_MimeType(uint32 imageType) const 251 { 252 translator_id id; 253 BString type; 254 FindTranslator(imageType, id, &type); 255 return type; 256 } 257 258 259 /*! Makes the space around the tab transparent, and also makes sure that the 260 contents of the window aren't, as the screen does not have an alpha channel. 261 */ 262 void 263 Utility::_MakeTabSpaceTransparent(BBitmap* screenshot, BRect frame) const 264 { 265 if (!frame.IsValid() || screenshot->ColorSpace() != B_RGBA32) 266 return; 267 268 // Set the transparency to opaque on the complete bitmap 269 uint8* pixel = (uint8*)screenshot->Bits(); 270 uint32 count = screenshot->BitsLength(); 271 for (uint32 i = 0; i < count; i += 4) { 272 pixel[i + 3] = 255; 273 } 274 275 // Then make the space around the tab transparent 276 if (!frame.Contains(tabFrame)) 277 return; 278 279 float tabHeight = tabFrame.bottom - tabFrame.top; 280 281 BRegion tabSpace(frame); 282 frame.OffsetBy(0, tabHeight); 283 tabSpace.Exclude(frame); 284 tabSpace.Exclude(tabFrame); 285 frame.OffsetBy(0, -tabHeight); 286 tabSpace.OffsetBy(-frame.left, -frame.top); 287 BScreen screen; 288 BRect screenFrame = screen.Frame(); 289 tabSpace.OffsetBy(-screenFrame.left, -screenFrame.top); 290 291 BView view(screenshot->Bounds(), "bitmap", B_FOLLOW_ALL_SIDES, 0); 292 screenshot->AddChild(&view); 293 if (view.Looper() && view.Looper()->Lock()) { 294 view.SetDrawingMode(B_OP_COPY); 295 view.SetHighColor(B_TRANSPARENT_32_BIT); 296 297 for (int i = 0; i < tabSpace.CountRects(); i++) 298 view.FillRect(tabSpace.RectAt(i)); 299 300 view.Sync(); 301 view.Looper()->Unlock(); 302 } 303 screenshot->RemoveChild(&view); 304 } 305