static void save_screenshot_of_opengl_framebuffer(const char *path) { #if 0 static int count = 0; count += 1; time_t t = time(NULL); #ifdef WINDOWS struct tm *tm_p = localtime(&t); #else struct tm tm_struct; struct tm *tm_p = &tm_struct; localtime_r(&t, tm_p); #endif char strbuf[20]; strftime(strbuf, 20, "%Y-%m-%d-%H-%M", tm_p); char *name = g_strdup_printf("%s-%s-%03d.png", g_fs_ml_video_screenshots_prefix, strbuf, g_fs_ml_video_screenshot); char *path = g_build_filename(g_fs_ml_video_screenshots_dir, name, NULL); #endif fs_log("writing screenshot to %s\n", path); int w = fs_ml_video_width(); int h = fs_ml_video_height(); fs_log("reading opengl frame buffer (%d x %d)\n", w, h); void *out_data = g_malloc(w * h * 4); // when using GL_RGB, remeber to temporarily set GL_UNPACK_ALIGNMENT so // all rows will be contiguous (the OpenGL default is to align rows on // 4-byte boundaries //GLint unpack_alignment; //glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment); //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, out_data); //glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); // flip image vertically int stride = w * 4; void *tmp = g_malloc(stride); void *line1 = out_data; void *line2 = out_data + stride * (h - 1); for (int i = 0; i < h / 2; i++) { memcpy(tmp, line1, stride); memcpy(line1, line2, stride); memcpy(line2, tmp, stride); line1 += stride; line2 -= stride; } g_free(tmp); int result = fs_image_save_data(path, out_data, w, h, 4); if (result) { fs_log("saved screenshot\n"); } else { fs_log("error saving screenshot\n"); } g_free(out_data); #if 0 g_free(name); g_free(path); #endif }
static void update_texture() { fs_emu_video_buffer *buffer = fs_emu_lock_video_buffer(); // unlocked in fs_emu_video_after_update uint8_t *frame = buffer->data; if (frame == NULL) { return; } int is_new_frame = 1; static int last_seq_no = -1; if (buffer->seq == last_seq_no + 1) { // normal } else if (buffer->seq == last_seq_no) { //fs_log("WARNING: repeated frame %d\n", info->seq_no); g_fs_emu_repeated_frames++; if (g_fs_emu_repeated_frames > 9999) { g_fs_emu_repeated_frames = 9999; } is_new_frame = 0; } else { int lost_frame_count = buffer->seq - last_seq_no - 1; g_fs_emu_lost_frames += lost_frame_count; //fs_log("lost %d frame(s)\n", lost_frame_count); } last_seq_no = buffer->seq; int width = buffer->width; int height = buffer->height; int bpp = buffer->bpp; if (g_fs_emu_screenshot) { static int count = 1; g_fs_emu_screenshot = 0; gchar *name, *path; time_t t = time(NULL); struct tm *tm_struct = localtime(&t); char strbuf[20]; strftime(strbuf, 20, "%Y-%m-%d_%H-%M-%S", tm_struct); name = g_strdup_printf("%s_%d.png", strbuf, count); count += 1; path = g_build_filename( g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP), name, NULL); fs_log("writing screenshot to %s\n", path); int len = width * height * bpp; uint8_t *out_data = malloc(width * height * 3); uint8_t *op = out_data; if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_BGRA) { for (int x = 0; x < len; x += 4) { *op++ = frame[x + 2]; *op++ = frame[x + 1]; *op++ = frame[x + 0]; } } else { for (int x = 0; x < len; x += 4) { *op++ = frame[x + 0]; *op++ = frame[x + 1]; *op++ = frame[x + 2]; } } int result = fs_image_save_data(path, out_data, width, height, 3); if (result) { fs_log("saved screenshot\n"); } else { fs_log("error saving screenshot\n"); } g_free(name); g_free(path); } int format = 0; if (bpp == 3) { format = GL_RGB; } else if (bpp == 4) { #if 0 if (fs_emu_get_video_format() == GL_BGRA) { format = GL_BGRA; } else { format = GL_RGBA; } #endif format = fs_emu_get_video_format(); } else { //fs_log("na..\n"); return; //fs_emu_fatal("bpp is neither 3 nor 4\n"); } if (g_fs_emu_video_crop_mode) { g_crop = buffer->crop; if (g_crop.w == 0) { g_crop.w = width; } if (g_crop.h == 0) { g_crop.h = height; } } else { g_crop.x = 0; g_crop.y = 0; g_crop.w = buffer->width; g_crop.h = buffer->height; } int upload_x, upload_y, upload_w, upload_h; g_effective_viewport_mode = g_viewport_mode; if (buffer->flags & FS_EMU_FORCE_VIEWPORT_CROP_FLAG) { g_effective_viewport_mode = FS_EMU_VIEWPORT_MODE_CROP; } if (g_effective_viewport_mode == FS_EMU_VIEWPORT_MODE_CROP) { fix_border(buffer, &upload_x, &upload_y, &upload_w, &upload_h); } else { upload_x = 0; upload_y = 0; upload_w = buffer->width; upload_h = buffer->height; } if (g_fs_emu_scanlines && (buffer->flags & FS_EMU_NO_SCANLINES_FLAG) == 0) { //printf("new frame? %d\n", is_new_frame); if (is_new_frame) { if (g_scanline_buffer_width != buffer->width || g_scanline_buffer_height != buffer->height) { if (g_scanline_buffer) { free(g_scanline_buffer); } g_scanline_buffer = malloc(buffer->width * buffer->height * 4); g_scanline_buffer_width = buffer->width; g_scanline_buffer_height = buffer->height; } fs_emu_render_scanlines(g_scanline_buffer, buffer, upload_x, upload_y, upload_w, upload_h, g_fs_emu_scanlines_dark, g_fs_emu_scanlines_light); } if (g_scanline_buffer) { frame = g_scanline_buffer; } } //fs_log("%d %d %d %d %d %d %d\n", width, height, bpp, // g_crop.x, g_crop.y, g_crop.w, g_crop.h); g_frame_width = width; g_frame_height = height; g_frame_aspect = buffer->aspect; create_texture_if_needed(width, height); fs_gl_bind_texture(g_frame_texture); uint8_t *data_start = frame + ((upload_y * width) + upload_x) * 4; #ifndef HAVE_GLES fs_gl_unpack_row_length(width); glTexSubImage2D(GL_TEXTURE_2D, 0, upload_x, upload_y, upload_w, upload_h, format, GL_UNSIGNED_BYTE, data_start); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, upload_w, upload_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_start); for (int y = 0; y < upload_h; y++) { char *row = data_start + ((y + upload_y)*width + upload_x) * 4; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_y, upload_w, 1, GL_RGBA, GL_UNSIGNED_BYTE, row); } #endif CHECK_GL_ERROR(); #if 1 int hq_border = 2; if (hq_border >= 1) { if (upload_y > 0) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_y - 1, width, 1, format, GL_UNSIGNED_BYTE, frame + (upload_y) * width * bpp); CHECK_GL_ERROR(); } if (upload_y + upload_h < g_frame_texture_height) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_y + upload_h, width, 1, format, GL_UNSIGNED_BYTE, frame + (upload_y + upload_h - 1) * width * bpp); CHECK_GL_ERROR(); } if (upload_x > 0) { glTexSubImage2D(GL_TEXTURE_2D, 0, upload_x - 1, upload_y, 1, upload_h, format, GL_UNSIGNED_BYTE, frame + ((upload_y) * width + upload_x) * bpp); CHECK_GL_ERROR(); } if (upload_x + upload_w < g_frame_texture_width) { glTexSubImage2D(GL_TEXTURE_2D, 0, upload_x + upload_w, upload_y, 1, upload_h, format, GL_UNSIGNED_BYTE, frame + ((upload_y) * width + upload_x + upload_w - 1) * bpp); CHECK_GL_ERROR(); } } #if 0 if (hq_border >= 2) { if (upload_y + upload_h < g_frame_texture.height - 1) { //printf("1\n"); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, upload_y + upload_h + 1, width, 1, format, GL_UNSIGNED_BYTE, frame + (upload_y + upload_h - 1) * width * bpp); //frame + (upload_y + upload_h - 2) * width * bpp); } if (upload_x + upload_w < g_frame_texture.width - 1) { glTexSubImage2D(GL_TEXTURE_2D, 0, upload_x + upload_w + 1, upload_y, 1, upload_h, format, GL_UNSIGNED_BYTE, frame + ((upload_y) * width + upload_x + upload_w - 1) * bpp); } } #endif #endif }