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 if (includeMouse && cursorBitmap != NULL) { 143 // Import the cursor bitmap into wholeScreen 144 wholeScreen->ImportBits(cursorBitmap, 145 B_ORIGIN, cursorPosition, cursorBitmap->Bounds().Size()); 146 } else if (cursorAreaBitmap != NULL) { 147 // Import the cursor area bitmap into wholeScreen 148 wholeScreen->ImportBits(cursorAreaBitmap, 149 B_ORIGIN, cursorPosition, cursorAreaBitmap->Bounds().Size()); 150 } 151 152 BBitmap* screenshot = NULL; 153 154 if (activeWindow && activeWindowFrame.IsValid()) { 155 BRect frame(activeWindowFrame); 156 if (includeBorder) { 157 frame.InsetBy(-borderSize, -borderSize); 158 frame.top -= tabFrame.bottom - tabFrame.top; 159 } 160 161 screenshot = new BBitmap(frame.OffsetToCopy(B_ORIGIN), 162 includeBorder ? B_RGBA32 : B_RGB32, true); 163 164 if (screenshot->ImportBits(wholeScreen, frame.LeftTop(), 165 B_ORIGIN, frame.Size()) != B_OK) { 166 delete screenshot; 167 return NULL; 168 } 169 170 if (includeBorder) 171 _MakeTabSpaceTransparent(screenshot, frame); 172 } else 173 screenshot = new BBitmap(wholeScreen); 174 175 return screenshot; 176 } 177 178 179 BString 180 Utility::FileNameExtension(uint32 imageType) const 181 { 182 BMimeType mimeType(_MimeType(imageType)); 183 184 BMessage message; 185 if (mimeType.GetFileExtensions(&message) == B_OK) { 186 BString extension; 187 if (message.FindString("extensions", 0, &extension) == B_OK) { 188 extension.Prepend("."); 189 return extension; 190 } 191 } 192 193 return ""; 194 } 195 196 197 status_t 198 Utility::FindTranslator(uint32 imageType, translator_id& id, 199 BString* _mimeType) const 200 { 201 translator_id* translators = NULL; 202 int32 numTranslators = 0; 203 204 BTranslatorRoster* roster = BTranslatorRoster::Default(); 205 status_t status = roster->GetAllTranslators(&translators, &numTranslators); 206 if (status != B_OK) 207 return status; 208 209 ArrayDeleter<translator_id> deleter(translators); 210 211 for (int32 x = 0; x < numTranslators; x++) { 212 const translation_format* formats = NULL; 213 int32 numFormats; 214 215 if (roster->GetOutputFormats(translators[x], &formats, &numFormats) 216 == B_OK) { 217 for (int32 i = 0; i < numFormats; ++i) { 218 if (formats[i].type == imageType) { 219 id = translators[x]; 220 if (_mimeType != NULL) 221 *_mimeType = formats[i].MIME; 222 return B_OK; 223 } 224 } 225 } 226 } 227 228 return B_ERROR; 229 } 230 231 232 BString 233 Utility::_MimeType(uint32 imageType) const 234 { 235 translator_id id; 236 BString type; 237 FindTranslator(imageType, id, &type); 238 return type; 239 } 240 241 242 /*! Makes the space around the tab transparent, and also makes sure that the 243 contents of the window aren't, as the screen does not have an alpha channel. 244 */ 245 void 246 Utility::_MakeTabSpaceTransparent(BBitmap* screenshot, BRect frame) const 247 { 248 if (!frame.IsValid() || screenshot->ColorSpace() != B_RGBA32) 249 return; 250 251 // Set the transparency to opaque on the complete bitmap 252 uint8* pixel = (uint8*)screenshot->Bits(); 253 uint32 count = screenshot->BitsLength(); 254 for (uint32 i = 0; i < count; i += 4) { 255 pixel[i + 3] = 255; 256 } 257 258 // Then make the space around the tab transparent 259 if (!frame.Contains(tabFrame)) 260 return; 261 262 float tabHeight = tabFrame.bottom - tabFrame.top; 263 264 BRegion tabSpace(frame); 265 frame.OffsetBy(0, tabHeight); 266 tabSpace.Exclude(frame); 267 tabSpace.Exclude(tabFrame); 268 frame.OffsetBy(0, -tabHeight); 269 tabSpace.OffsetBy(-frame.left, -frame.top); 270 BScreen screen; 271 BRect screenFrame = screen.Frame(); 272 tabSpace.OffsetBy(-screenFrame.left, -screenFrame.top); 273 274 BView view(screenshot->Bounds(), "bitmap", B_FOLLOW_ALL_SIDES, 0); 275 screenshot->AddChild(&view); 276 if (view.Looper() && view.Looper()->Lock()) { 277 view.SetDrawingMode(B_OP_COPY); 278 view.SetHighColor(B_TRANSPARENT_32_BIT); 279 280 for (int i = 0; i < tabSpace.CountRects(); i++) 281 view.FillRect(tabSpace.RectAt(i)); 282 283 view.Sync(); 284 view.Looper()->Unlock(); 285 } 286 screenshot->RemoveChild(&view); 287 } 288