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
Utility()47 Utility::Utility()
48 :
49 wholeScreen(NULL),
50 cursorBitmap(NULL),
51 cursorAreaBitmap(NULL)
52 {
53 }
54
55
~Utility()56 Utility::~Utility()
57 {
58 delete wholeScreen;
59 delete cursorBitmap;
60 delete cursorAreaBitmap;
61 }
62
63
64 void
CopyToClipboard(const BBitmap & screenshot) const65 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
Save(BBitmap * screenshot,const char * fileName,uint32 imageType) const86 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*
MakeScreenshot(bool includeMouse,bool activeWindow,bool includeBorder) const136 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
FileNameExtension(uint32 imageType) const180 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
FindTranslator(uint32 imageType,translator_id & id,BString * _mimeType) const198 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
_MimeType(uint32 imageType) const233 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
_MakeTabSpaceTransparent(BBitmap * screenshot,BRect frame) const246 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