Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
10 KiB

  1. /*
  2. Copyright 1999 Microsoft Corporation
  3. Capture a window's screen bits in PNG format
  4. Walter Smith (wsmith)
  5. Rajesh Soy (nsoy) - modified 05/08/2000
  6. */
  7. #ifdef THIS_FILE
  8. #undef THIS_FILE
  9. #endif
  10. static char __szTraceSourceFile[] = __FILE__;
  11. #define THIS_FILE __szTraceSourceFile
  12. #include "stdafx.h"
  13. #define NOTRACE
  14. #include "png.h"
  15. #include <dbgtrace.h>
  16. HBITMAP CopyScreenRectToDIB(LPRECT lpRect, PVOID* ppvBits)
  17. {
  18. TraceFunctEnter("CopyScreenRectToDIB");
  19. HDC hScrDC = NULL, hMemDC = NULL; // screen DC and memory DC
  20. HBITMAP hBitmap = NULL, hOldBitmap = NULL; // handles to deice-dependent bitmaps
  21. int nX = 0, nY = 0, nX2 = 0, nY2 = 0; // coordinates of rectangle to grab
  22. int nWidth = 0, nHeight = 0; // DIB width and height
  23. int xScrn = 0, yScrn = 0; // screen resolution
  24. BITMAPINFO* pbminfo = NULL; // BITMAPINFO for rectangle
  25. size_t cbInfo = 0; // Size of *pbminfo in bytes
  26. // check for an empty rectangle
  27. if (IsRectEmpty(lpRect))
  28. return NULL;
  29. // create a DC for the screen and create
  30. // a memory DC compatible to screen DC
  31. if ((hScrDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL)) == NULL)
  32. goto Done;
  33. if ((hMemDC = CreateCompatibleDC(hScrDC)) == NULL)
  34. goto Done;
  35. // get points of rectangle to grab
  36. nX = lpRect->left;
  37. nY = lpRect->top;
  38. nX2 = lpRect->right;
  39. nY2 = lpRect->bottom;
  40. // get screen resolution
  41. xScrn = GetDeviceCaps(hScrDC, HORZRES);
  42. yScrn = GetDeviceCaps(hScrDC, VERTRES);
  43. //make sure bitmap rectangle is visible
  44. //
  45. // NTRAID#NTBUG9-154242-2000/08/13-jasonr
  46. //
  47. // I commented out this code because it prevented us from handling multimon
  48. // scenarios. For multimon the virtual screen buffer extends beyond the
  49. // coordinates of the primary monitor. For example if you attach a second
  50. // monitor to the left of the primary, it will have negative coordinates.
  51. //
  52. // The AV was occurring because this code caused us to allocate a buffer
  53. // that was smaller than the caller expected, ultimately causing the PNG
  54. // code to walk off the end of it.
  55. //
  56. // It may be that we need further changes to correctly support ALL multimon
  57. // scenarios. For example, if a window straddles the boundary between two
  58. // monitors (parts of the window appear on both monitors), then I don't know
  59. // that the existing code will copy all of the window -- it might only copy
  60. // one of the two parts. Since that is not a common scenario, I don't think
  61. // we have to fix it, but we'll see who complains about it.
  62. //
  63. /*
  64. if (nX < 0)
  65. {
  66. DebugTrace(0, "nX < 0");
  67. nX = 0;
  68. lpRect->left = 0;
  69. }
  70. if (nY < 0)
  71. {
  72. DebugTrace(0, "nY < 0");
  73. nY = 0;
  74. lpRect->top = 0;
  75. }
  76. if (nX2 > xScrn)
  77. {
  78. DebugTrace(0, "nX2 > xScrn");
  79. nX2 = xScrn;
  80. lpRect->right = xScrn;
  81. }
  82. if (nY2 > yScrn)
  83. {
  84. DebugTrace(0, "nY2 > yScrn");
  85. nY2 = yScrn;
  86. lpRect->bottom = yScrn;
  87. }
  88. */
  89. nWidth = nX2 - nX;
  90. nHeight = nY2 - nY;
  91. DebugTrace(0, "nWidth: %d", nWidth);
  92. DebugTrace(0, "nHeight: %d", nHeight);
  93. cbInfo = offsetof(BITMAPINFO, bmiColors[256]);
  94. pbminfo = (BITMAPINFO*) alloca(cbInfo);
  95. ZeroMemory(pbminfo, cbInfo);
  96. pbminfo->bmiHeader.biSize = sizeof(BITMAPINFO);
  97. pbminfo->bmiHeader.biWidth = nWidth;
  98. pbminfo->bmiHeader.biHeight = - nHeight; // negative height = top-down bitmap
  99. pbminfo->bmiHeader.biPlanes = 1;
  100. pbminfo->bmiHeader.biBitCount = 8;
  101. pbminfo->bmiHeader.biCompression = BI_RGB;
  102. pbminfo->bmiHeader.biSizeImage = 0;
  103. pbminfo->bmiHeader.biXPelsPerMeter = 96; // presumably this doesn't matter
  104. pbminfo->bmiHeader.biYPelsPerMeter = 96; // or this either
  105. pbminfo->bmiHeader.biClrUsed = 256;
  106. pbminfo->bmiHeader.biClrImportant = 0;
  107. for (int i = 0; i < 256; i++) {
  108. pbminfo->bmiColors[i].rgbRed = (char)i;
  109. pbminfo->bmiColors[i].rgbGreen = (char)i;
  110. pbminfo->bmiColors[i].rgbBlue = (char)i;
  111. }
  112. if ((hBitmap = CreateDIBSection(hScrDC, pbminfo, DIB_RGB_COLORS, ppvBits, NULL, 0)) == NULL)
  113. goto Done;
  114. // select new bitmap into memory DC
  115. hOldBitmap = (HBITMAP) SelectObject(hMemDC, hBitmap);
  116. if ((hOldBitmap == NULL) || (hOldBitmap == (HBITMAP)(ULONG_PTR) GDI_ERROR))
  117. goto Done;
  118. // bitblt screen DC to memory DC
  119. BitBlt(hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY);
  120. // select old bitmap back into memory DC and get handle to
  121. // bitmap of the screen
  122. hBitmap = (HBITMAP) SelectObject(hMemDC, hOldBitmap);
  123. // clean up
  124. Done:
  125. if (hScrDC != NULL)
  126. DeleteDC(hScrDC);
  127. if (hMemDC != NULL)
  128. DeleteDC(hMemDC);
  129. GdiFlush();
  130. // return handle to the bitmap
  131. return hBitmap;
  132. }
  133. class BitWriter {
  134. public:
  135. BitWriter()
  136. {
  137. m_cbData = 0;
  138. m_pData = (BYTE*) malloc(4096);
  139. if (m_pData == NULL)
  140. m_maxData = 0;
  141. else
  142. m_maxData = 4096;
  143. }
  144. ~BitWriter()
  145. {
  146. if (m_pData != NULL)
  147. free(m_pData);
  148. }
  149. DWORD GetLength()
  150. {
  151. return m_cbData;
  152. }
  153. BYTE* GetData()
  154. {
  155. return m_pData;
  156. }
  157. BYTE* DetachData()
  158. {
  159. BYTE* pData = m_pData;
  160. m_pData = NULL;
  161. return pData;
  162. }
  163. bool IsBad()
  164. {
  165. return (m_pData == NULL);
  166. }
  167. void Write(BYTE* pNewData, DWORD cbNewData)
  168. {
  169. if (IsBad())
  170. return;
  171. if (m_cbData + cbNewData > m_maxData) {
  172. DWORD newMaxData = m_maxData + max(4096, m_maxData - m_cbData + cbNewData);
  173. BYTE* newPData = (BYTE*) realloc(m_pData, newMaxData);
  174. if (newPData == 0) {
  175. free(m_pData);
  176. m_pData = NULL;
  177. return;
  178. }
  179. m_maxData = newMaxData;
  180. m_pData = newPData;
  181. }
  182. memcpy(m_pData + m_cbData, pNewData, cbNewData);
  183. m_cbData += cbNewData;
  184. }
  185. private:
  186. BYTE* m_pData;
  187. DWORD m_cbData;
  188. DWORD m_maxData;
  189. };
  190. void __stdcall PNGWriteDataCallback(png_structp pPNG, png_bytep pData, png_size_t cbData)
  191. {
  192. BitWriter* pbw = (BitWriter*) png_get_io_ptr(pPNG);
  193. pbw->Write(pData, cbData);
  194. }
  195. void __stdcall PNGFlushDataCallback(png_structp pPNG)
  196. {
  197. }
  198. void GetWindowImage(HWND hwnd, BYTE** ppData, DWORD* pcbData)
  199. {
  200. TraceFunctEnter("GetWindowImage");
  201. ASSERT_WRITE_PTR(ppData);
  202. ASSERT_WRITE_PTR(pcbData);
  203. BitWriter bitWriter;
  204. DebugTrace(0, "Calling png_create_write_struct...");
  205. png_structp pPNG = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
  206. ThrowIfNull(pPNG);
  207. DebugTrace(0, "Calling png_set_write_fn...");
  208. png_set_write_fn(pPNG, &bitWriter, PNGWriteDataCallback, PNGFlushDataCallback);
  209. DebugTrace(0, "Calling png_create_info_struct...");
  210. png_infop pInfo = png_create_info_struct(pPNG);
  211. ThrowIfNull(pInfo);
  212. RECT rWnd;
  213. DebugTrace(0, "Calling GetWindowRect...");
  214. ThrowIfZero(GetWindowRect(hwnd, &rWnd));
  215. PVOID pvBits;
  216. DebugTrace(0, "Calling CopyScreenRectToDIB...");
  217. HBITMAP hContents = CopyScreenRectToDIB(&rWnd, &pvBits);
  218. int width = rWnd.right - rWnd.left;
  219. int height = rWnd.bottom - rWnd.top;
  220. DebugTrace(0, "width: %d", width);
  221. DebugTrace(0, "height: %d", height);
  222. try {
  223. // The PNG library uses setjmp/longjmp to "throw" errors.
  224. // I'm uncertain of the interaction between setjmp and C++,
  225. // so keep the inside of the try block SIMPLE.
  226. DebugTrace(0, "Calling setjmp...");
  227. if (setjmp(pPNG->jmpbuf))
  228. {
  229. FatalTrace(0, "setjmp failed");
  230. throw E_FAIL;
  231. }
  232. DebugTrace(0, "Calling png_set_IHDR...");
  233. png_set_IHDR(pPNG, pInfo, width, height,
  234. 2, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
  235. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  236. DebugTrace(0, "Calling png_write_info...");
  237. png_write_info(pPNG, pInfo);
  238. // DIBs round rows up to DWORD boundaries
  239. int rowBytes = ((width + 3) / 4) * 4;
  240. DebugTrace(0, "rowBytes: %d", rowBytes);
  241. DebugTrace(0, "Allocating memory for aRowPtrs...");
  242. png_byte** aRowPtrs = (png_byte**) alloca(height * sizeof(png_byte*));
  243. if(NULL == aRowPtrs)
  244. {
  245. FatalTrace(0, "aRowPtrs is NULL");
  246. throw E_OUTOFMEMORY;
  247. }
  248. DebugTrace(0, "Assigning aRowPtrs");
  249. for (int i = 0; i < height; i++)
  250. {
  251. aRowPtrs[i] = ((png_byte*) pvBits) + i*rowBytes;
  252. }
  253. // Round our pixels down to the right bit depth.
  254. // It seems as if the PNG lib would do this for us, but png_set_shift
  255. // makes it pack THEN shift, which takes the LEAST significant bits.
  256. DebugTrace(0, "Rounding pixels down to right depth...");
  257. for (int iRow = 0; iRow < height; iRow++) {
  258. png_byte* pRow = aRowPtrs[iRow];
  259. for (int iPixel = 0; iPixel < width; iPixel++) {
  260. int b = pRow[iPixel];
  261. b += 0x3F;
  262. if (b >= 0x100)
  263. b = 0xFF;
  264. b >>= 6;
  265. pRow[iPixel] = (char)b;
  266. }
  267. }
  268. DebugTrace(0, "calling png_set_packing...");
  269. png_set_packing(pPNG);
  270. DebugTrace(0, "calling png_write_image...");
  271. png_write_image(pPNG, aRowPtrs);
  272. DebugTrace(0, "calling png_write_end...");
  273. png_write_end(pPNG, pInfo);
  274. DebugTrace(0, "We are done with PNG...");
  275. }
  276. catch (...) {
  277. png_destroy_write_struct(&pPNG, &pInfo);
  278. DeleteObject(hContents);
  279. throw;
  280. }
  281. DebugTrace(0, "Calling png_destroy_write_struct...");
  282. png_destroy_write_struct(&pPNG, &pInfo);
  283. DebugTrace(0, "Calling DeleteObject...");
  284. DeleteObject(hContents);
  285. DebugTrace(0, "Calling bitWriter.IsBad...");
  286. if (bitWriter.IsBad())
  287. {
  288. FatalTrace(0, "bitWriter IsBad");
  289. throw E_FAIL;
  290. }
  291. *ppData = bitWriter.DetachData();
  292. *pcbData = bitWriter.GetLength();
  293. }