2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "Screenshot.h"
28 #include "Application.h"
29 #include "windowing/WindowingFactory.h"
30 #include "pictures/Picture.h"
32 #ifdef TARGET_RASPBERRY_PI
33 #include "xbmc/linux/RBP.h"
36 #ifdef HAS_VIDEO_PLAYBACK
37 #include "cores/VideoRenderers/RenderManager.h"
40 #include "filesystem/File.h"
41 #include "guilib/GraphicContext.h"
43 #include "utils/JobManager.h"
44 #include "utils/URIUtils.h"
45 #include "utils/log.h"
46 #include "settings/SettingPath.h"
47 #include "settings/Settings.h"
48 #include "settings/windows/GUIControlSettings.h"
51 using namespace XFILE;
53 CScreenshotSurface::CScreenshotSurface()
61 bool CScreenshotSurface::capture()
63 #if defined(TARGET_RASPBERRY_PI)
64 g_RBP.GetDisplaySize(m_width, m_height);
65 m_buffer = g_RBP.CaptureDisplay(m_width, m_height, &m_stride, true, false);
69 LPDIRECT3DSURFACE9 lpSurface = NULL, lpBackbuffer = NULL;
70 g_graphicsContext.Lock();
71 if (g_application.m_pPlayer->IsPlayingVideo())
73 #ifdef HAS_VIDEO_PLAYBACK
74 g_renderManager.SetupScreenshot();
77 g_application.RenderNoPresent();
79 if (FAILED(g_Windowing.Get3DDevice()->CreateOffscreenPlainSurface(g_Windowing.GetWidth(), g_Windowing.GetHeight(), D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpSurface, NULL)))
82 if (FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &lpBackbuffer)))
85 // now take screenshot
86 if (SUCCEEDED(g_Windowing.Get3DDevice()->GetRenderTargetData(lpBackbuffer, lpSurface)))
90 lpSurface->GetDesc(&desc);
91 if (SUCCEEDED(lpSurface->LockRect(&lr, NULL, D3DLOCK_READONLY)))
94 m_height = desc.Height;
96 m_buffer = new unsigned char[m_height * m_stride];
97 memcpy(m_buffer, lr.pBits, m_height * m_stride);
98 lpSurface->UnlockRect();
102 CLog::Log(LOGERROR, "%s LockRect failed", __FUNCTION__);
107 CLog::Log(LOGERROR, "%s GetBackBuffer failed", __FUNCTION__);
109 lpSurface->Release();
110 lpBackbuffer->Release();
112 g_graphicsContext.Unlock();
114 #elif defined(HAS_GL) || defined(HAS_GLES)
116 g_graphicsContext.BeginPaint();
117 if (g_application.m_pPlayer->IsPlayingVideo())
119 #ifdef HAS_VIDEO_PLAYBACK
120 g_renderManager.SetupScreenshot();
123 g_application.RenderNoPresent();
125 glReadBuffer(GL_BACK);
127 //get current viewport
129 glGetIntegerv(GL_VIEWPORT, viewport);
131 m_width = viewport[2] - viewport[0];
132 m_height = viewport[3] - viewport[1];
133 m_stride = m_width * 4;
134 unsigned char* surface = new unsigned char[m_stride * m_height];
136 //read pixels from the backbuffer
138 glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)surface);
140 glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)surface);
142 g_graphicsContext.EndPaint();
144 //make a new buffer and copy the read image to it with the Y axis inverted
145 m_buffer = new unsigned char[m_stride * m_height];
146 for (int y = 0; y < m_height; y++)
149 // we need to save in BGRA order so XOR Swap RGBA -> BGRA
150 unsigned char* swap_pixels = surface + (m_height - y - 1) * m_stride;
151 for (int x = 0; x < m_width; x++, swap_pixels+=4)
153 std::swap(swap_pixels[0], swap_pixels[2]);
156 memcpy(m_buffer + y * m_stride, surface + (m_height - y - 1) *m_stride, m_stride);
162 //nothing to take a screenshot from
169 void CScreenShot::TakeScreenshot(const CStdString &filename, bool sync)
172 CScreenshotSurface surface;
173 if (!surface.capture())
175 CLog::Log(LOGERROR, "Screenshot %s failed", filename.c_str());
179 CLog::Log(LOGDEBUG, "Saving screenshot %s", filename.c_str());
181 //set alpha byte to 0xFF
182 for (int y = 0; y < surface.m_height; y++)
184 unsigned char* alphaptr = surface.m_buffer - 1 + y * surface.m_stride;
185 for (int x = 0; x < surface.m_width; x++)
186 *(alphaptr += 4) = 0xFF;
189 //if sync is true, the png file needs to be completely written when this function returns
192 if (!CPicture::CreateThumbnailFromSurface(surface.m_buffer, surface.m_width, surface.m_height, surface.m_stride, filename))
193 CLog::Log(LOGERROR, "Unable to write screenshot %s", filename.c_str());
195 delete [] surface.m_buffer;
199 //make sure the file exists to avoid concurrency issues
200 FILE* fp = fopen(filename.c_str(), "w");
204 CLog::Log(LOGERROR, "Unable to create file %s", filename.c_str());
206 //write .png file asynchronous with CThumbnailWriter, prevents stalling of the render thread
207 //buffer is deleted from CThumbnailWriter
208 CThumbnailWriter* thumbnailwriter = new CThumbnailWriter(surface.m_buffer, surface.m_width, surface.m_height, surface.m_stride, filename);
209 CJobManager::GetInstance().AddJob(thumbnailwriter, NULL);
213 void CScreenShot::TakeScreenshot()
215 static bool savingScreenshots = false;
216 static vector<CStdString> screenShots;
217 bool promptUser = false;
220 // check to see if we have a screenshot folder yet
221 CSettingPath *screenshotSetting = (CSettingPath*)CSettings::Get().GetSetting("debug.screenshotpath");
222 if (screenshotSetting != NULL)
224 strDir = screenshotSetting->GetValue();
227 if (CGUIControlButtonSetting::GetPath(screenshotSetting))
228 strDir = screenshotSetting->GetValue();
232 if (strDir.IsEmpty())
234 strDir = "special://temp/";
235 if (!savingScreenshots)
238 savingScreenshots = true;
242 URIUtils::RemoveSlashAtEnd(strDir);
244 if (!strDir.IsEmpty())
246 CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(strDir, "screenshot%03d.png"), 999);
250 TakeScreenshot(file, false);
251 if (savingScreenshots)
252 screenShots.push_back(file);
254 { // grab the real directory
256 if (screenshotSetting != NULL)
258 newDir = screenshotSetting->GetValue();
261 if (CGUIControlButtonSetting::GetPath(screenshotSetting))
262 newDir = screenshotSetting->GetValue();
266 if (!newDir.IsEmpty())
268 for (unsigned int i = 0; i < screenShots.size(); i++)
270 CStdString file = CUtil::GetNextFilename(URIUtils::AddFileToFolder(newDir, "screenshot%03d.png"), 999);
271 CFile::Cache(screenShots[i], file);
275 savingScreenshots = false;
280 CLog::Log(LOGWARNING, "Too many screen shots or invalid folder");