#define PNG_SCREENSHOTS 1 #include #include #include #include "sysconfig.h" #include "sysdeps.h" #include "options.h" #include "custom.h" #include "dxwrap.h" #include "win32.h" #include "win32gfx.h" #include "direct3d.h" #include "opengl.h" #include "registry.h" #include "gfxfilter.h" #include "xwin.h" #include "drawing.h" #include "fsdb.h" #include "zfile.h" #include "gfxboard.h" #include "png.h" int screenshotmode = PNG_SCREENSHOTS; int screenshot_originalsize = 0; int screenshot_clipmode = 0; int screenshot_multi = 0; extern bool need_genlock_data; extern uae_u8 **row_map_genlock; static bool usealpha(void) { return need_genlock_data != 0 && row_map_genlock && currprefs.genlock_image && currprefs.genlock_alpha; } static void namesplit (TCHAR *s) { int l; l = _tcslen (s) - 1; while (l >= 0) { if (s[l] == '.') s[l] = 0; if (s[l] == '\\' || s[l] == '/' || s[l] == ':' || s[l] == '?') { l++; break; } l--; } if (l > 0) memmove (s, s + l, (_tcslen (s + l) + 1) * sizeof (TCHAR)); } static int toclipboard (BITMAPINFO *bi, void *bmp) { struct AmigaMonitor *mon = &AMonitors[0]; int v = 0; uae_u8 *dib = 0; HANDLE hg; if (!OpenClipboard(mon->hMainWnd)) return v; EmptyClipboard (); hg = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, bi->bmiHeader.biSize + bi->bmiHeader.biSizeImage); if (hg) { dib = (uae_u8*)GlobalLock (hg); if (dib) { memcpy (dib, &bi->bmiHeader, bi->bmiHeader.biSize + bi->bmiHeader.biClrUsed * sizeof(RGBQUAD)); memcpy (dib + bi->bmiHeader.biSize + +bi->bmiHeader.biClrUsed * sizeof(RGBQUAD), bmp, bi->bmiHeader.biSizeImage); } GlobalUnlock (hg); if (SetClipboardData (CF_DIB, hg)) v = 1; } CloseClipboard (); if (!v) GlobalFree (hg); return v; } static HDC surface_dc, offscreen_dc; static int surface_dc_monid; static BITMAPINFO *bi; // bitmap info static LPVOID lpvBits = NULL; // pointer to bitmap bits array static HBITMAP offscreen_bitmap; static int screenshot_prepared; static int screenshot_multi_start; void screenshot_free(void) { if (surface_dc) releasehdc(surface_dc_monid, surface_dc); surface_dc = NULL; if(offscreen_dc) DeleteDC(offscreen_dc); offscreen_dc = NULL; if(offscreen_bitmap) DeleteObject(offscreen_bitmap); offscreen_bitmap = NULL; if(lpvBits) free(lpvBits); lpvBits = NULL; screenshot_prepared = FALSE; } static int rgb_rb, rgb_gb, rgb_bb, rgb_rs, rgb_gs, rgb_bs, rgb_ab, rgb_as; static int screenshot_prepare(int monid, int imagemode, struct vidbuffer *vb, bool standard) { struct AmigaMonitor *mon = &AMonitors[monid]; int width, height; HGDIOBJ hgdiobj; int bits; int depth = usealpha() ? 32 : 24; bool renderTarget = true; lockrtg(); screenshot_free (); if (!standard) { regqueryint(NULL, _T("Screenshot_Original"), &screenshot_originalsize); regqueryint(NULL, _T("Screenshot_ClipMode"), &screenshot_clipmode); } else { screenshot_originalsize = 1; screenshot_clipmode = 0; } if (imagemode < 0) imagemode = screenshot_originalsize; if (imagemode) { int spitch, dpitch; uae_u8 *src, *dst, *mem; bool needfree = false; uae_u8 *palette = NULL; int rgb_bb2, rgb_gb2, rgb_rb2, rgb_ab2; int rgb_bs2, rgb_gs2, rgb_rs2, rgb_as2; uae_u8 pal[256 * 3]; int screenshot_width = 0, screenshot_height = 0; int screenshot_xoffset = -1, screenshot_yoffset = -1; if (gfxboard_isgfxboardscreen(monid)) { src = mem = gfxboard_getrtgbuffer(monid, &width, &height, &spitch, &bits, pal); needfree = true; rgb_bb2 = 8; rgb_gb2 = 8; rgb_rb2 = 8; rgb_ab2 = 8; rgb_bs2 = 0; rgb_gs2 = 8; rgb_rs2 = 16; rgb_as2 = 24; } else if (vb) { width = vb->outwidth; height = vb->outheight; spitch = vb->rowbytes; bits = vb->pixbytes * 8; src = mem = vb->bufmem; rgb_bb2 = rgb_bb; rgb_gb2 = rgb_gb; rgb_rb2 = rgb_rb; rgb_ab2 = rgb_ab; rgb_bs2 = rgb_bs; rgb_gs2 = rgb_gs; rgb_rs2 = rgb_rs; rgb_as2 = rgb_as; } else { src = mem = getfilterbuffer(monid, &width, &height, &spitch, &bits); needfree = true; rgb_bb2 = rgb_bb; rgb_gb2 = rgb_gb; rgb_rb2 = rgb_rb; rgb_ab2 = rgb_ab; rgb_bs2 = rgb_bs; rgb_gs2 = rgb_gs; rgb_rs2 = rgb_rs; rgb_as2 = rgb_as; } if (src == NULL) { if (WIN32GFX_IsPicassoScreen(mon)) renderTarget = false; goto donormal; } if (width == 0 || height == 0) { if (needfree) { if (WIN32GFX_IsPicassoScreen(mon)) gfxboard_freertgbuffer(0, mem); else freefilterbuffer(0, mem); } goto donormal; } int screenshot_xmult = 1; int screenshot_ymult = 1; screenshot_width = width; screenshot_height = height; if (!standard) { screenshot_xmult = currprefs.screenshot_xmult + 1; screenshot_ymult = currprefs.screenshot_ymult + 1; if (currprefs.screenshot_width > 0) screenshot_width = currprefs.screenshot_width; if (currprefs.screenshot_height > 0) screenshot_height = currprefs.screenshot_height; screenshot_xoffset = currprefs.screenshot_xoffset; screenshot_yoffset = currprefs.screenshot_yoffset; } if (!WIN32GFX_IsPicassoScreen(mon) && screenshot_clipmode == 1) { int cw, ch, cx, cy, crealh = 0; if (get_custom_limits(&cw, &ch, &cx, &cy, &crealh)) { int maxw = currprefs.screenshot_max_width << currprefs.gfx_resolution; int maxh = currprefs.screenshot_max_height << currprefs.gfx_vresolution; int minw = currprefs.screenshot_min_width << currprefs.gfx_resolution; int minh = currprefs.screenshot_min_height << currprefs.gfx_vresolution; if (minw > AMIGA_WIDTH_MAX << currprefs.gfx_resolution) minw = AMIGA_WIDTH_MAX << currprefs.gfx_resolution; if (minh > AMIGA_HEIGHT_MAX << currprefs.gfx_resolution) minh = AMIGA_HEIGHT_MAX << currprefs.gfx_resolution; if (maxw < minw) maxw = minw; if (maxh < minh) maxh = minh; screenshot_width = cw; screenshot_height = ch; screenshot_xoffset = cx; screenshot_yoffset = cy; if (screenshot_width < minw && minw > 0) { screenshot_xoffset -= (minw - screenshot_width) / 2; screenshot_width = minw; } if (screenshot_height < minh && minh > 0) { screenshot_yoffset -= (minh - screenshot_height) / 2; screenshot_height = minh; } if (screenshot_width > maxw && maxw > 0) { screenshot_xoffset += (screenshot_width - maxw) / 2; screenshot_width = maxw; } if (screenshot_height > maxh && maxh > 0) { screenshot_yoffset += (screenshot_height - maxh) / 2; screenshot_height = maxh; } } } if (screenshot_xmult < 1) screenshot_xmult = 1; if (screenshot_ymult < 1) screenshot_ymult = 1; int maxw_output = standard ? 0 : currprefs.screenshot_output_width; int maxh_output = standard ? 0 : currprefs.screenshot_output_height; while (maxw_output > screenshot_width * screenshot_xmult && screenshot_xmult < 8) { screenshot_xmult++; } while (maxh_output > screenshot_height * screenshot_ymult && screenshot_ymult < 8) { screenshot_ymult++; } int xoffset = screenshot_xoffset < 0 ? (screenshot_width - width) / 2 : -screenshot_xoffset; int yoffset = screenshot_yoffset < 0 ? (screenshot_height - height) / 2 : -screenshot_yoffset; ZeroMemory (bi, sizeof(bi)); bi->bmiHeader.biClrUsed = bits <= 8 ? (1 << bits) : 0; bi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bi->bmiHeader.biWidth = screenshot_width * screenshot_xmult; bi->bmiHeader.biHeight = screenshot_height * screenshot_ymult; bi->bmiHeader.biPlanes = 1; bi->bmiHeader.biBitCount = bits <= 8 ? 8 : depth; bi->bmiHeader.biCompression = BI_RGB; dpitch = ((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8; bi->bmiHeader.biSizeImage = dpitch * bi->bmiHeader.biHeight; bi->bmiHeader.biXPelsPerMeter = 0; bi->bmiHeader.biYPelsPerMeter = 0; bi->bmiHeader.biClrImportant = 0; if (bits <= 8) { for (int i = 0; i < bi->bmiHeader.biClrUsed; i++) { bi->bmiColors[i].rgbRed = pal[i * 3 + 0]; bi->bmiColors[i].rgbGreen = pal[i * 3 + 1]; bi->bmiColors[i].rgbBlue = pal[i * 3 + 2]; } } if (!(lpvBits = xmalloc(uae_u8, bi->bmiHeader.biSizeImage))) { if (needfree) { if (WIN32GFX_IsPicassoScreen(mon)) gfxboard_freertgbuffer(monid, mem); else freefilterbuffer(monid, mem); } goto oops; } int dpitch2 = dpitch; dpitch *= screenshot_ymult; dst = (uae_u8*)lpvBits; dst += dpitch * screenshot_height; if (yoffset > 0) { if (yoffset >= screenshot_height - height) yoffset = screenshot_height - height; dst -= dpitch * yoffset; } else if (yoffset < 0) { yoffset = -yoffset; if (yoffset >= height - screenshot_height) yoffset = height - screenshot_height; src += spitch * yoffset; } int xoffset2 = 0; if (xoffset < 0) { xoffset2 = -xoffset; xoffset = 0; } int dbpx = bits / 8; int sbpx = bits / 8; if (sbpx == 3) sbpx = 4; int xmult = screenshot_xmult; int ymult = screenshot_ymult; for (int y = 0; y < screenshot_height && y < height; y++) { uae_u8 *s, *d; dst -= dpitch; d = dst; s = src; if (xoffset > 0) { d += xoffset * dbpx; } else if (xoffset2 > 0) { s += xoffset2 * sbpx; } for (int x = 0; x < screenshot_width && x < width; x++) { int xx = x * screenshot_xmult; if (bits <= 8) { uae_u8 *d2 = d; for (int y2 = 0; y2 < ymult; y2++) { for (int x2 = 0; x2 < xmult; x2++) { d2[xx + x2] = s[x]; } d2 += dpitch2; } } else { int shift; uae_u32 v = 0; uae_u32 v2, v2a, v2b, v2c, v2d; if (bits == 16) v = ((uae_u16*)s)[x]; else if (bits == 32) v = ((uae_u32*)s)[x]; shift = 8 - rgb_bb2; v2 = (v >> rgb_bs2) & ((1 << rgb_bb2) - 1); v2 <<= shift; if (rgb_bb2 < 8) v2 |= (v2 >> shift) & ((1 < shift) - 1); v2a = v2; shift = 8 - rgb_gb2; v2 = (v >> rgb_gs2) & ((1 << rgb_gb2) - 1); v2 <<= (8 - rgb_gb2); if (rgb_gb < 8) v2 |= (v2 >> shift) & ((1 < shift) - 1); v2b = v2; shift = 8 - rgb_rb2; v2 = (v >> rgb_rs2) & ((1 << rgb_rb2) - 1); v2 <<= (8 - rgb_rb2); if (rgb_rb < 8) v2 |= (v2 >> shift) & ((1 < shift) - 1); v2c = v2; if (depth == 32) { shift = 8 - rgb_ab2; v2 = (v >> rgb_as2) & ((1 << rgb_ab2) - 1); v2 <<= (8 - rgb_ab2); if (rgb_ab < 8) v2 |= (v2 >> shift) & ((1 < shift) - 1); v2d = v2; } uae_u8 *d2 = d; for (int y2 = 0; y2 < ymult; y2++) { for (int x2 = 0; x2 < xmult; x2++) { if (depth == 32) { d2[(xx + x2) * 4 + 0] = v2a; d2[(xx + x2) * 4 + 1] = v2b; d2[(xx + x2) * 4 + 2] = v2c; d2[(xx + x2) * 4 + 3] = v2d; } else { d2[(xx + x2) * 3 + 0] = v2a; d2[(xx + x2) * 3 + 1] = v2b; d2[(xx + x2) * 3 + 2] = v2c; } } d2 += dpitch2; } } } src += spitch; } if (needfree) { if (gfxboard_isgfxboardscreen(monid)) gfxboard_freertgbuffer(monid, mem); else freefilterbuffer(monid, mem); } } else { donormal: bool d3dcaptured = false; width = WIN32GFX_GetWidth(mon); height = WIN32GFX_GetHeight(mon); if (!renderTarget && WIN32GFX_IsPicassoScreen(mon)) { struct picasso96_state_struct *state = &picasso96_state[monid]; width = state->Width; height = state->Height; } if (D3D_isenabled(0) == 2) { int w, h, pitch, bits = 32; void *data; bool got = D3D11_capture(monid, &data, &w, &h, &pitch, renderTarget); int dpitch = (((width * depth + 31) & ~31) / 8); lpvBits = xmalloc(uae_u8, dpitch * height); ZeroMemory(bi, sizeof(bi)); bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi->bmiHeader.biWidth = width; bi->bmiHeader.biHeight = height; bi->bmiHeader.biPlanes = 1; bi->bmiHeader.biBitCount = depth; bi->bmiHeader.biCompression = BI_RGB; bi->bmiHeader.biSizeImage = dpitch * bi->bmiHeader.biHeight; bi->bmiHeader.biXPelsPerMeter = 0; bi->bmiHeader.biYPelsPerMeter = 0; bi->bmiHeader.biClrUsed = 0; bi->bmiHeader.biClrImportant = 0; if (got && lpvBits) { for (int y = 0; y < h && y < height; y++) { uae_u8 *d = (uae_u8*)lpvBits + (height - y - 1) * dpitch; uae_u32 *s = (uae_u32*)((uae_u8*)data + y * pitch); for (int x = 0; x < w && x < width; x++) { uae_u32 v = *s++; d[0] = v >> 0; d[1] = v >> 8; d[2] = v >> 16; if (depth == 32) { d[3] = v >> 24; d += 4; } else { d += 3; } } } } if (got) D3D11_capture(monid, NULL, NULL, NULL, NULL, renderTarget); d3dcaptured = true; } else if (D3D_isenabled(0) == 1) { int w, h, bits; HRESULT hr; D3DLOCKED_RECT l; LPDIRECT3DSURFACE9 s = D3D_capture(monid, &w, &h, &bits, renderTarget); if (s) { hr = s->LockRect(&l, NULL, D3DLOCK_READONLY); if (SUCCEEDED(hr)) { int dpitch = (((width * depth + 31) & ~31) / 8); lpvBits = xmalloc(uae_u8, dpitch * height); ZeroMemory(bi, sizeof(bi)); bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi->bmiHeader.biWidth = width; bi->bmiHeader.biHeight = height; bi->bmiHeader.biPlanes = 1; bi->bmiHeader.biBitCount = depth; bi->bmiHeader.biCompression = BI_RGB; bi->bmiHeader.biSizeImage = dpitch * bi->bmiHeader.biHeight; bi->bmiHeader.biXPelsPerMeter = 0; bi->bmiHeader.biYPelsPerMeter = 0; bi->bmiHeader.biClrUsed = 0; bi->bmiHeader.biClrImportant = 0; if (lpvBits) { if (bits == 32) { for (int y = 0; y < h && y < height; y++) { uae_u8 *d = (uae_u8*)lpvBits + (height - y - 1) * dpitch; uae_u32 *s = (uae_u32*)((uae_u8*)l.pBits + y * l.Pitch); for (int x = 0; x < w && x < width; x++) { uae_u32 v = *s++; d[0] = v >> 0; d[1] = v >> 8; d[2] = v >> 16; if (depth == 32) { d[3] = v >> 24; d += 4; } else { d += 3; } } } } else if (bits == 16 || bits == 15) { for (int y = 0; y < h && y < height; y++) { uae_u8 *d = (uae_u8*)lpvBits + (height - y - 1) * dpitch; uae_u16 *s = (uae_u16*)((uae_u8*)l.pBits + y * l.Pitch); for (int x = 0; x < w && x < width; x++) { uae_u16 v = s[x]; uae_u16 v2 = v; if (bits == 16) { v2 = v & 31; v2 |= ((v & (31 << 5)) << 1) | (((v >> 5) & 1) << 5); v2 |= (v & (31 << 10)) << 1; } else { v2 = v & 31; v2 |= (v >> 1) & (31 << 5); v2 |= (v >> 1) & (31 << 10); } ((uae_u16*)d)[0] = v2; d += 2; } } } } s->UnlockRect(); d3dcaptured = true; } } } if (!d3dcaptured) { surface_dc = gethdc(monid); surface_dc_monid = monid; if (surface_dc == NULL) goto oops; // need a HBITMAP to convert it to a DIB if ((offscreen_bitmap = CreateCompatibleBitmap (surface_dc, width, height)) == NULL) goto oops; // error // The bitmap is empty, so let's copy the contents of the surface to it. // For that we need to select it into a device context. if ((offscreen_dc = CreateCompatibleDC (surface_dc)) == NULL) goto oops; // error // select offscreen_bitmap into offscreen_dc hgdiobj = SelectObject (offscreen_dc, offscreen_bitmap); // now we can copy the contents of the surface to the offscreen bitmap BitBlt (offscreen_dc, 0, 0, width, height, surface_dc, 0, 0, SRCCOPY); // de-select offscreen_bitmap SelectObject (offscreen_dc, hgdiobj); ZeroMemory (bi, sizeof(bi)); bi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bi->bmiHeader.biWidth = width; bi->bmiHeader.biHeight = height; bi->bmiHeader.biPlanes = 1; bi->bmiHeader.biBitCount = 24; bi->bmiHeader.biCompression = BI_RGB; bi->bmiHeader.biSizeImage = (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8) * bi->bmiHeader.biHeight; bi->bmiHeader.biXPelsPerMeter = 0; bi->bmiHeader.biYPelsPerMeter = 0; bi->bmiHeader.biClrUsed = 0; bi->bmiHeader.biClrImportant = 0; // Reserve memory for bitmap bits if (!(lpvBits = xmalloc (uae_u8, bi->bmiHeader.biSizeImage))) goto oops; // out of memory // Have GetDIBits convert offscreen_bitmap to a DIB (device-independent bitmap): if (!GetDIBits (offscreen_dc, offscreen_bitmap, 0, bi->bmiHeader.biHeight, lpvBits, bi, DIB_RGB_COLORS)) goto oops; // GetDIBits FAILED releasehdc(monid, surface_dc); surface_dc = NULL; } if (!lpvBits) goto oops; } unlockrtg(); screenshot_prepared = TRUE; return 1; oops: screenshot_free (); unlockrtg(); return 0; } static int screenshot_prepare(int monid, struct vidbuffer *vb) { return screenshot_prepare(monid, 1, vb, false); } int screenshot_prepare(int monid, int imagemode) { return screenshot_prepare(monid, imagemode, NULL, false); } int screenshot_prepare(int monid) { return screenshot_prepare(monid, -1); } void Screenshot_RGBinfo (int rb, int gb, int bb, int ab, int rs, int gs, int bs, int as) { if (!bi) bi = xcalloc (BITMAPINFO, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)); rgb_rb = rb; rgb_gb = gb; rgb_bb = rb; rgb_ab = ab; rgb_rs = rs; rgb_gs = gs; rgb_bs = bs; rgb_as = as; } #if PNG_SCREENSHOTS > 0 static void _cdecl pngtest_blah (png_structp png_ptr, png_const_charp message) { #if 1 if (message) { TCHAR *msg = au(message); write_log (_T("libpng warning: '%s'\n"), msg); xfree (msg); } #endif } static int savepng(FILE *fp, bool alpha) { png_structp png_ptr; png_infop info_ptr; png_bytep *row_pointers; int h = bi->bmiHeader.biHeight; int w = bi->bmiHeader.biWidth; int d = bi->bmiHeader.biBitCount; png_color pngpal[256]; int i; png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, pngtest_blah, pngtest_blah, pngtest_blah); if (!png_ptr) return 1; info_ptr = png_create_info_struct (png_ptr); if (!info_ptr) { png_destroy_write_struct (&png_ptr, NULL); return 2; } if (setjmp(png_jmpbuf (png_ptr))) { png_destroy_write_struct (&png_ptr, &info_ptr); return 3; } png_init_io (png_ptr, fp); png_set_filter (png_ptr, 0, PNG_FILTER_NONE); png_set_IHDR (png_ptr, info_ptr, w, h, 8, d <= 8 ? PNG_COLOR_TYPE_PALETTE : (alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (d <= 8) { for (i = 0; i < (1 << d); i++) { pngpal[i].red = bi->bmiColors[i].rgbRed; pngpal[i].green = bi->bmiColors[i].rgbGreen; pngpal[i].blue = bi->bmiColors[i].rgbBlue; } png_set_PLTE (png_ptr, info_ptr, pngpal, 1 << d); } row_pointers = xmalloc (png_bytep, h); for (i = 0; i < h; i++) { int j = h - i - 1; row_pointers[i] = (uae_u8*)lpvBits + j * (((w * (d <= 8 ? 8 : (alpha ? 32 : 24)) + 31) & ~31) / 8); } png_set_rows (png_ptr, info_ptr, row_pointers); png_write_png (png_ptr,info_ptr, PNG_TRANSFORM_BGR, NULL); png_destroy_write_struct (&png_ptr, &info_ptr); xfree (row_pointers); return 0; } static void __cdecl write_data_fn(png_structp p, png_bytep data, png_size_t len) { struct zfile *zf = (struct zfile*)png_get_io_ptr(p); zfile_fwrite(data, 1, len, zf); } static void __cdecl output_flush_fn(png_structp p) { } static struct zfile *savepngzfile(bool alpha) { png_structp png_ptr; png_infop info_ptr; png_bytep *row_pointers; int h = bi->bmiHeader.biHeight; int w = bi->bmiHeader.biWidth; int d = bi->bmiHeader.biBitCount; png_color pngpal[256]; int i; struct zfile *zf; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, pngtest_blah, pngtest_blah, pngtest_blah); if (!png_ptr) return NULL; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, NULL); return NULL; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return NULL; } zf = zfile_fopen_empty(NULL, _T("screenshot")); if (!zf) { png_destroy_write_struct(&png_ptr, &info_ptr); return NULL; } png_set_write_fn(png_ptr, zf, write_data_fn, output_flush_fn); png_set_filter(png_ptr, 0, PNG_FILTER_NONE); png_set_IHDR(png_ptr, info_ptr, w, h, 8, d <= 8 ? PNG_COLOR_TYPE_PALETTE : (alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (d <= 8) { for (i = 0; i < (1 << d); i++) { pngpal[i].red = bi->bmiColors[i].rgbRed; pngpal[i].green = bi->bmiColors[i].rgbGreen; pngpal[i].blue = bi->bmiColors[i].rgbBlue; } png_set_PLTE(png_ptr, info_ptr, pngpal, 1 << d); } row_pointers = xmalloc(png_bytep, h); for (i = 0; i < h; i++) { int j = h - i - 1; row_pointers[i] = (uae_u8*)lpvBits + j * (((w * (d <= 8 ? 8 : (alpha ? 32 : 24)) + 31) & ~31) / 8); } png_set_rows(png_ptr, info_ptr, row_pointers); png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL); png_destroy_write_struct(&png_ptr, &info_ptr); xfree(row_pointers); return zf; } #endif static int savebmp (FILE *fp, bool alpha) { BITMAPFILEHEADER bfh; // write the file header, bitmap information and pixel data bfh.bfType = 19778; bfh.bfSize = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER) + (bi->bmiHeader.biClrUsed * sizeof(RGBQUAD)) + bi->bmiHeader.biSizeImage; bfh.bfReserved1 = 0; bfh.bfReserved2 = 0; bfh.bfOffBits = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER) + bi->bmiHeader.biClrUsed * sizeof(RGBQUAD); if (fwrite (&bfh, 1, sizeof (BITMAPFILEHEADER), fp) < sizeof (BITMAPFILEHEADER)) return 1; // failed to write bitmap file header if (fwrite (bi, 1, sizeof (BITMAPINFOHEADER), fp) < sizeof (BITMAPINFOHEADER)) return 2; // failed to write bitmap information header if (bi->bmiHeader.biClrUsed) { if (fwrite (bi->bmiColors, 1, bi->bmiHeader.biClrUsed * sizeof(RGBQUAD), fp) < bi->bmiHeader.biClrUsed * sizeof(RGBQUAD)) return 3; // failed to write bitmap file header } if (fwrite (lpvBits, 1, bi->bmiHeader.biSizeImage, fp) < bi->bmiHeader.biSizeImage) return 4; // failed to write the bitmap return 0; } static void screenshot_prepare_multi(void) { screenshot_multi_start = true; } static int filenumber = 0; static int dirnumber = 1; /* Captures the Amiga display (DirectDraw, D3D or OpenGL) surface and saves it to file as a 24bit bitmap. */ int screenshotf(int monid, const TCHAR *spath, int mode, int doprepare, int imagemode, struct vidbuffer *vb) { static int recursive; FILE *fp = NULL; int failed = 0; int screenshot_max = 1000; // limit 999 iterations / screenshots const TCHAR *format = _T("%s%s%s%03d.%s"); bool alpha = usealpha(); HBITMAP offscreen_bitmap = NULL; // bitmap that is converted to a DIB HDC offscreen_dc = NULL; // offscreen DC that we can select offscreen bitmap into if(recursive) return 0; recursive++; if (vb) { if (!screenshot_prepare(monid, vb)) goto oops; } else if (!screenshot_prepared || doprepare) { if (!screenshot_prepare(monid, imagemode)) goto oops; } if (mode == 0) { toclipboard (bi, lpvBits); } else { TCHAR filename[MAX_DPATH]; TCHAR path[MAX_DPATH]; TCHAR name[MAX_DPATH]; TCHAR underline[] = _T("_"); if (spath) { fp = _tfopen (spath, _T("wb")); if (fp) { #if PNG_SCREENSHOTS > 0 if (screenshotmode) failed = savepng (fp, alpha); else #endif failed = savebmp (fp, alpha); fclose(fp); fp = NULL; if (failed) write_log(_T("Screenshot status %d ('%s')\n"), failed, spath); if (failed) _tunlink(spath); } goto oops; } fetch_path (_T("ScreenshotPath"), path, sizeof (path) / sizeof (TCHAR)); CreateDirectory (path, NULL); if (screenshot_multi) { TCHAR *p = path + _tcslen(path); while(dirnumber < 1000) { _stprintf(p, _T("%03d\\"), dirnumber); if (!screenshot_multi_start) break; filenumber = 0; if (!my_existsdir(path) && !my_existsfile(path)) break; dirnumber++; } format = _T("%s%s%s%05d.%s"); screenshot_max = 100000; screenshot_multi_start = 0; if (dirnumber == 1000) { failed = 1; goto oops; } CreateDirectory(path, NULL); } name[0] = 0; if (currprefs.floppyslots[0].dfxtype >= 0) _tcscpy (name, currprefs.floppyslots[0].df); else if (currprefs.cdslots[0].inuse) _tcscpy (name, currprefs.cdslots[0].name); if (!name[0]) underline[0] = 0; namesplit (name); while(++filenumber < screenshot_max) { _stprintf (filename, format, path, name, underline, filenumber, screenshotmode ? _T("png") : _T("bmp")); if ((fp = _tfopen (filename, _T("rb"))) == NULL) // does file not exist? { int nok = 0; if ((fp = _tfopen (filename, _T("wb"))) == NULL) { write_log(_T("Screenshot error, can't open \"%s\" err=%d\n"), filename, GetLastError()); goto oops; // error } #if PNG_SCREENSHOTS > 0 if (screenshotmode) nok = savepng (fp, alpha); else #endif nok = savebmp (fp, alpha); fclose(fp); if (nok && fp) { _tunlink(filename); } fp = NULL; if (nok) { write_log(_T("Screenshot error %d ('%s')\n"), nok, filename); goto oops; } write_log (_T("Screenshot saved as \"%s\"\n"), filename); failed = 0; break; } fclose (fp); fp = NULL; } } oops: if(fp) fclose (fp); if (doprepare) screenshot_free (); recursive--; if (failed) screenshot_multi = 0; return failed == 0; } #include "drawing.h" void screenshot(int monid, int mode, int doprepare) { if (monid < 0) { monid = getfocusedmonitor(); } if (mode == 2) { screenshot_multi = 10; screenshot_prepare_multi(); } else if (mode == 3) { screenshot_multi = -1; screenshot_prepare_multi(); } else if (mode == 4) { screenshot_multi = 0; filenumber = 0; } else { screenshotf(monid, NULL, mode, doprepare, -1, NULL); } } void screenshot_reset(void) { screenshot_free(); } uae_u8 *save_screenshot(int monid, size_t *len) { #if 0 struct amigadisplay *ad = &adisplays[monid]; if (ad->picasso_on) return NULL; struct vidbuf_description *avidinfo = &adisplays[monid].gfxvidinfo; struct vidbuffer vb; int w = avidinfo->drawbuffer.inwidth; int h = avidinfo->drawbuffer.inheight; if (!programmedmode) { h = (maxvpos + lof_display - minfirstline) << currprefs.gfx_vresolution; } if (interlace_seen && currprefs.gfx_vresolution > 0) { h -= 1 << (currprefs.gfx_vresolution - 1); } if (w > 0 && h > 0) { allocvidbuffer(monid, &vb, w, h, avidinfo->drawbuffer.pixbytes * 8); set_custom_limits(-1, -1, -1, -1); draw_frame(&vb); if (screenshot_prepare(monid, -1, &vb, true)) { struct zfile *zf = savepngzfile(false); uae_u8 *data = zfile_getdata(zf, 0, -1, len); zfile_fclose(zf); screenshot_free(); return data; } } #endif return NULL; }