xref: /haiku/src/apps/screenshot/Utility.cpp (revision 46b7da1f4f40f7157d74fc7fb26ff9ec7f2416f2)
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