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