void load_texture(fs_emu_texture *texture) { fs_image *image = texture->image; //printf("loading texture from image %p\n", image); if (!image) { return; } unsigned int opengl_texture; glGenTextures(1, &opengl_texture); //texture->opengl_context_stamp = g_fs_ml_opengl_context_stamp; fs_gl_bind_texture(opengl_texture); fs_gl_unpack_row_length(0); int gl_buffer_format = GL_RGBA; if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_BGRA) { gl_buffer_format = GL_BGRA; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0, gl_buffer_format, GL_UNSIGNED_BYTE, image->data); CHECK_GL_ERROR(); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, // GL_LINEAR_MIPMAP_LINEAR); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, // GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR(); //glGenerateMipmapEXT(GL_TEXTURE_2D); texture->texture = opengl_texture; }
static void create_texture_if_needed(int width, int height) { //g_frame_texture.video_version = g_fs_emu_video_version; if (g_frame_texture && g_frame_texture_width >= width && g_frame_texture_height >= height) { return; } fs_gl_bind_texture(0); if (g_frame_texture) { glDeleteTextures(1, &g_frame_texture); CHECK_GL_ERROR(); } g_frame_texture_width = 1; while (g_frame_texture_width < width) { g_frame_texture_width *= 2; } g_frame_texture_height = 1; while (g_frame_texture_height < height) { g_frame_texture_height *= 2; } glGenTextures(1, &g_frame_texture); CHECK_GL_ERROR(); //g_frame_texture.opengl_context_stamp = g_fs_ml_opengl_context_stamp; //fs_emu_set_texture(&g_frame_texture); fs_gl_bind_texture(g_frame_texture); // with high quality borders, there should be no reason to initialize // the texture to black void *data = NULL; //void *data = g_malloc0(g_frame_texture_width * g_frame_texture_height * 4); #ifndef HAVE_GLES fs_gl_unpack_row_length(0); #endif glTexImage2D(GL_TEXTURE_2D, 0, fs_emu_get_texture_format(), g_frame_texture_width, g_frame_texture_height, 0, fs_emu_get_video_format(), GL_UNSIGNED_BYTE, data); CHECK_GL_ERROR(); if (data) { g_free(data); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); CHECK_GL_ERROR(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); CHECK_GL_ERROR(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); CHECK_GL_ERROR(); }
static inline void copy_pixel(unsigned char **dst, unsigned char **src) { unsigned char *sp = *src; unsigned char *dp = *dst; if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_BGRA) { dp[0] = ((int) sp[2]) * sp[3] / 255; dp[1] = ((int) sp[1]) * sp[3] / 255; dp[2] = ((int) sp[0]) * sp[3] / 255; } else { dp[0] = ((int) sp[0]) * sp[3] / 255; dp[1] = ((int) sp[1]) * sp[3] / 255; dp[2] = ((int) sp[2]) * sp[3] / 255; } dp[3] = sp[3]; *dst = *dst + 4; *src = *src + 4; }
int fs_emu_font_render(fs_emu_font *font, const char *text, float x, float y, float r, float g, float b, float alpha) { if (font->image == NULL) { return 0; } if (text == NULL || *text == '\0') { return 0 ; } if (!g_initialized) { initialize(); } /* if (g_fs_ml_opengl_context_stamp != g_video_version) { GList* list = g_cache; while (list) { cache_item *item = (cache_item *) list->data; g_free(item->text); g_free(item); list = list->next; } g_list_free(g_cache); g_cache = NULL; initialize_cache(); } */ // find cached text entry, if any //sanity_check(); GList* list = g_cache; while (list) { cache_item *item = (cache_item *) list->data; if (item->font == font && strcmp(item->text, text) == 0) { break; } list = list->next; } if (list) { cache_item *item = (cache_item *) list->data; g_cache = g_list_delete_link(g_cache, list); sanity_check(); fs_gl_blending(1); fs_gl_texturing(1); //fs_emu_ortho(); //fs_emu_set_texture(NULL); fs_gl_bind_texture(g_text_texture); //glColor4f(1.0, 1.0, 1.0, alpha); //printf("rendering %f %f %f %f...\n", item->x1, item->x2, item->y1, item->y2); fs_gl_color4f(r * alpha, g * alpha, b * alpha, alpha); //glColor4f(r * alpha, g * alpha, b * alpha, alpha); GLfloat tex[] = { item->x1, item->y2, item->x2, item->y2, item->x2, item->y1, item->x1, item->y1 }; GLfloat vert[] = { x, y, x + item->width, y, x + item->width, y + item->height, x, y + item->height }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vert); glTexCoordPointer(2, GL_FLOAT, 0, tex); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); g_cache = g_list_prepend(g_cache, item); sanity_check(); return item->width; } // calculate size of text //printf(":: %s\n", text); int chars = 0; int required_width = 0; int required_height = font->h; unsigned char *cp = (unsigned char *) text; for(; *cp; cp++) { unsigned char c = *cp; //printf(" %p\n", font); //printf(" %d\n", c); //printf(" %d\n", font->w[c]); if (required_width + font->w[c] > TEXTURE_WIDTH) { break; } required_width += font->w[c]; chars++; } //fs_log("width: %d, height: %d\n", required_width, required_height); //float tw = font->texture->width; //float th = font->texture->height; // FIXME: clear g_buffer //int length = strlen(text); //for (int i = 0; i < length; i++) { cp = (unsigned char *) text; //glBegin(GL_QUADS); //glColor4f(1.0, 1.0, 1.0, 1.0); //int x2 = 0; int dx = 0; int dy = 0; for(int i = 0; i < chars; i++) { unsigned char c = *cp++; //unsigned char = (unsigned char) text[i]; int sx = font->x[c]; int sy = font->y[c]; int sw = font->w[c]; int sh = font->h; //printf("%d %d %d %d\n", sx, sy, sw, sh); // draw character int *sl = ((int *) font->image->data) + font->image->width * sy + sx; int ss = font->image->width; // source stride //int *sl = sp; int *dl = ((int *) g_buffer) + TEXTURE_WIDTH * dy + dx; int ds = TEXTURE_WIDTH; // destination stride //int *dl = dp; for (int y = 0; y < sh; y++) { //printf("%d\n", y); int *sp = sl; int *dp = dl; for (int x = 0; x < sw; x++) { //printf("%d %d\n", x, y); //*dp++ = 0xff0000ff; *dp++ = *sp++; //int a = *sp++; } sl += ss; dl += ds; } dx += sw; /* //fs_log("%d %d %d %d\n", font->x[c], font->y[c], w, h); float s1 = font->x[c] / tw; float s2 = (font->x[c] + w) / tw; //double t1 = 1.0 - (font->y[c]) / th; //double t2 = 1.0 - (font->y[c] + h) / th; float t1 = (font->y[c]) / th; float t2 = (font->y[c] + h) / th; //fs_log("%d %d %d %d\n", x, y, w, h); glTexCoord2f(s1, t2); //glVertex2f(x, y); glVertex2f(x2, 0); glTexCoord2f(s2, t2); //glVertex2f(x + w, y); glVertex2f(x2 + w, 0); glTexCoord2f(s2, t1); //glVertex2f(x + w, y + h); glVertex2f(x2 + w, h); glTexCoord2f(s1, t1); //glVertex2f(x, y + h); glVertex2f(x2, h); x2 += w; */ } //printf("...\n"); /* glEnd(); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopAttrib(); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glPopMatrix(); glDeleteFramebuffersEXT(1, &frame_buffer); //glDeleteRenderbuffersEXT(1, &depth_buffer); fs_emu_set_texture(NULL); if (mipmapping) { fs_gl_bind_texture(render_texture); glGenerateMipmapEXT(GL_TEXTURE_2D); fs_gl_bind_texture(0); } */ GList *last = g_list_last(g_cache); cache_item *last_item = (cache_item *) last->data; int position = last_item->position; fs_gl_bind_texture(g_text_texture); #ifndef HAVE_GLES fs_gl_unpack_row_length(TEXTURE_WIDTH); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, position * 32, required_width, required_height, fs_emu_get_video_format(), GL_UNSIGNED_BYTE, g_buffer); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, required_width, required_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, g_buffer); for (int y = 0; y < required_height; y++) { char *row = g_buffer + ((y + position * 32)*TEXTURE_WIDTH) * 4; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, position * 32, required_width, 1, GL_RGBA, GL_UNSIGNED_BYTE, row); } #endif cache_item *item = g_malloc(sizeof(cache_item)); item->font = font; item->text = g_strdup(text); item->width = required_width; item->height = required_height; item->position = position; item->x1 = 0; item->x2 = required_width / (1.0 * TEXTURE_WIDTH); item->y1 = (item->position * 32) / (1.0 * TEXTURE_HEIGHT); item->y2 = (item->position * 32 + required_height) / (1.0 * TEXTURE_HEIGHT); //item->texture = render_texture; g_cache = g_list_prepend(g_cache, item); sanity_check(); if (last_item->text) { g_free(last_item->text); } /* if (last_item->texture) { glDeleteTextures(1, &last_item->texture); } */ g_free(last_item); g_cache = g_list_delete_link(g_cache, last); sanity_check(); // now the text is in the cache, so call function again return fs_emu_font_render(font, text, x, y, r, g, b, alpha); }
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 }
int main(int argc, char *argv[]) { fs_uae_argc = argc; fs_uae_argv = argv; fs_set_argv(argc, argv); #ifdef WITH_CEF cef_init(argc, argv); #endif char **arg; arg = argv + 1; while (arg && *arg) { if (strcmp(*arg, "--list-joysticks") == 0) { list_joysticks(); exit(0); } else if (strcmp(*arg, "--list-devices") == 0) { list_joysticks(); exit(0); } else if (strcmp(*arg, "--version") == 0) { printf("%s\n", PACKAGE_VERSION); exit(0); } else if (strcmp(*arg, "--help") == 0) { printf(COPYRIGHT_NOTICE, PACKAGE_VERSION, OS_NAME_2, ARCH_NAME_2); printf(EXTRA_HELP_TEXT); exit(0); } arg++; } fs_init(); int error = fs_data_init("fs-uae", "fs-uae.dat"); if (error) { printf("WARNING: error (%d) loading fs-uae.dat\n", error); } fs_set_prgname("fs-uae"); fs_set_application_name("Amiga Emulator"); amiga_set_log_function(log_to_libfsemu); //result = parse_options(argc, argv); printf(COPYRIGHT_NOTICE, PACKAGE_VERSION, OS_NAME_2, ARCH_NAME_2); fs_log(COPYRIGHT_NOTICE, PACKAGE_VERSION, OS_NAME_2, ARCH_NAME_2); char *current_dir = g_get_current_dir(); fs_log("current directory is %s\n", current_dir); g_free(current_dir); amiga_init(); #if 0 // FIXME: disabling fullscreen spaces must be done before // SDL_INIT_VIDEO, but we need to check config to see if this should // be done, and we initialize SDL early to check for config file // (catch 22)... // FIXME: check fullscreen_spaces option SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0"); #endif #ifdef MACOSX SDL_Init(SDL_INIT_EVERYTHING); SDL_PumpEvents(); SDL_Event event; fs_log("OS X: Check for pending SDL_DROPFILE event\n"); while (SDL_PollEvent(&event)) { fs_log("Got SDL event 0x%x\n", event.type); if (event.type == SDL_DROPFILE) { if (event.drop.file != NULL) { g_fs_uae_config_file_path = strdup(event.drop.file); } SDL_free(event.drop.file); } } #endif // skip first entry arg = argv + 1; if (g_fs_uae_config_file_path == NULL) { while (arg && *arg) { const gchar *test_path = *arg; if (test_path && fs_path_exists(test_path)) { if (check_extension(test_path, ".fs-uae")) { g_fs_uae_config_file_path = g_strdup(test_path); } else if (check_extension(test_path, ".conf")) { g_fs_uae_config_file_path = g_strdup(test_path); } else if (check_extension(test_path, ".adf")) { g_fs_uae_disk_file_path = g_strdup(test_path); } else if (check_extension(test_path, ".ipf")) { g_fs_uae_disk_file_path = g_strdup(test_path); } else if (check_extension(test_path, ".dms")) { g_fs_uae_disk_file_path = g_strdup(test_path); } } arg++; } } /* Parse options first, in case base_dir, logging options etc is * specified on the command line. */ fs_config_parse_options(argc - 1, argv + 1); fs_log("\n"); fs_log(LOG_LINE); fs_log("libfsemu init\n"); fs_log(LOG_LINE); fs_log("\n"); fs_emu_path_set_expand_function(fs_uae_expand_path); fs_emu_init_overlays(overlay_names); fs_emu_init(); // then load the config file load_config_file(); init_i18n(); if (g_fs_uae_disk_file_path) { fs_config_set_string(OPTION_FLOPPY_DRIVE_0, g_fs_uae_disk_file_path); g_warn_about_missing_config_file = 0; } if (g_warn_about_missing_config_file) { fs_emu_warning(_("No configuration file was found")); } fs_log("\n"); fs_log(LOG_LINE); fs_log("fs-uae init\n"); fs_log(LOG_LINE); fs_log("\n"); configure_logging(fs_config_get_const_string("log")); fs_emu_set_state_check_function(amiga_get_state_checksum); fs_emu_set_rand_check_function(amiga_get_rand_checksum); // force creation of some recommended default directories fs_uae_kickstarts_dir(); fs_uae_configurations_dir(); fs_uae_init_path_resolver(); fs_uae_plugins_init(); // must be called early, before fs_emu_init -affects video output fs_uae_configure_amiga_model(); // force creation of state directories //fs_uae_flash_memory_dir(); //fs_uae_save_states_dir(); //fs_uae_floppy_overlays_dir(); fs_uae_state_dir(); const char *controllers_dir = fs_uae_controllers_dir(); if (controllers_dir) { fs_emu_set_controllers_dir(controllers_dir); } const char *logs_dir = fs_uae_logs_dir(); if (logs_dir) { char *log_file; log_file = g_build_filename(logs_dir, "FS-UAE.log", NULL); if (fs_path_exists(log_file)) { g_unlink(log_file); } g_free(log_file); log_file = g_build_filename(logs_dir, "FS-UAE.log.txt", NULL); if (fs_path_exists(log_file)) { g_unlink(log_file); } g_free(log_file); log_file = g_build_filename(logs_dir, "Emulator.log.txt", NULL); if (fs_path_exists(log_file)) { g_unlink(log_file); } g_free(log_file); log_file = g_build_filename(logs_dir, "fs-uae.log.txt", NULL); fs_config_set_log_file(log_file); g_free(log_file); } fs_config_set_string_if_unset("themes_dir", fs_uae_themes_dir()); fs_emu_set_pause_function(pause_function); //fs_uae_init_input(); fs_emu_init_2(FS_EMU_INIT_EVERYTHING); // we initialize the recording module either it is used or not, so it // can delete state-specific recordings (if necessary) when states are // saved fs_uae_init_recording(); int deterministic_mode = 0; const char* record_file = fs_config_get_const_string("record"); if (record_file) { fs_log("record file specified: %s, forcing deterministic mode\n", record_file); deterministic_mode = 1; fs_uae_enable_recording(record_file); } else { fs_log("not running in record mode\n"); } if (fs_emu_netplay_enabled() || fs_config_get_boolean(OPTION_DETERMINISTIC) == 1) { deterministic_mode = 1; } if (deterministic_mode) { amiga_set_deterministic_mode(); } if (logs_dir) { if (fs_emu_netplay_enabled()) { char *sync_log_file = g_build_filename(logs_dir, "Synchronization.log", NULL); amiga_set_synchronization_log_file(sync_log_file); free(sync_log_file); } } #ifdef FS_EMU_DRIVERS fs_emu_audio_stream_options **options = fs_emu_audio_alloc_stream_options(2); options[0]->frequency = fs_emu_audio_output_frequency(); /* 12 * 2352 is CDDA_BUFFERS * 2352 (blkdev_cdimage.cpp) */ options[1]->buffer_size = 12 * 2352; // begin playing with only one buffer queued options[1]->min_buffers = 1; fs_emu_audio_configure(options); amiga_set_audio_buffer_size(options[0]->buffer_size); fs_emu_audio_free_stream_options(options); #else // this stream is for paula output and drive clicks // FIXME: could mix drive clicks in its own stream instead, -might // give higher quality mixing fs_emu_audio_stream_options options; options.struct_size = sizeof(fs_emu_audio_stream_options); fs_emu_init_audio_stream_options(&options); options.frequency = fs_emu_audio_output_frequency(); fs_emu_init_audio_stream(0, &options); amiga_set_audio_buffer_size(options.buffer_size); // this stream is for CD audio output (CDTV/CD32) fs_emu_init_audio_stream_options(&options); // 12 * 2352 is CDDA_BUFFERS * 2352 (blkdev_cdimage.cpp) options.buffer_size = 12 * 2352; // begin playing with only one buffer queued options.min_buffers = 1; fs_emu_init_audio_stream(1, &options); #endif amiga_set_audio_callback(audio_callback_function); amiga_set_cd_audio_callback(audio_callback_function); amiga_set_event_function(event_handler); amiga_set_led_function(led_function); amiga_on_update_leds(on_update_leds); amiga_set_media_function(media_function); amiga_set_init_function(on_init); if (fs_config_get_boolean(OPTION_JIT_COMPILER) == 1) { amiga_init_jit_compiler(); } #ifdef WITH_LUA amiga_init_lua(fs_emu_acquire_lua, fs_emu_release_lua); amiga_init_lua_state(fs_emu_get_lua_state()); fs_uae_init_lua_state(fs_emu_get_lua_state()); #endif if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_RGBA) { amiga_set_video_format(AMIGA_VIDEO_FORMAT_RGBA); } else if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_BGRA) { amiga_set_video_format(AMIGA_VIDEO_FORMAT_BGRA); } else if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_R5G6B5) { amiga_set_video_format(AMIGA_VIDEO_FORMAT_R5G6B5); } else if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_R5G5B5A1) { amiga_set_video_format(AMIGA_VIDEO_FORMAT_R5G5B5A1); } else { fs_emu_warning("Unsupported video format requested"); } amiga_add_rtg_resolution(672, 540); amiga_add_rtg_resolution(960, 540); amiga_add_rtg_resolution(672 * 2, 540 * 2); amiga_add_rtg_resolution(fs_emu_get_windowed_width(), fs_emu_get_windowed_height()); amiga_add_rtg_resolution(fs_emu_get_fullscreen_width(), fs_emu_get_fullscreen_height()); fs_uae_init_video(); //fs_uae_init_keyboard(); fs_uae_init_mouse(); fs_uae_configure_menu(); fs_emu_run(main_function); fs_log("fs-uae shutting down, fs_emu_run returned\n"); if (g_rmdir(fs_uae_state_dir()) == 0) { fs_log("state dir %s was removed because it was empty\n", fs_uae_state_dir()); } else { fs_log("state dir %s was not removed (non-empty)\n", fs_uae_state_dir()); } fs_log("end of main function\n"); cleanup_old_files(); #ifdef WITH_CEF cef_destroy(); #endif return 0; }
int fs_emu_font_render(fs_emu_font *font, const char *text, float x, float y, float r, float g, float b, float alpha) { if (font == NULL || font->image == NULL) { return 0; } if (text == NULL || *text == '\0') { return 0 ; } if (!g_initialized) { initialize(); } // find cached text entry, if any GList* list = g_cache; while (list) { cache_item *item = (cache_item *) list->data; if (item->font == font && strcmp(item->text, text) == 0) { break; } list = list->next; } if (list) { cache_item *item = (cache_item *) list->data; g_cache = g_list_delete_link(g_cache, list); sanity_check(); fs_gl_blending(1); fs_gl_texturing(1); //fs_emu_ortho(); //fs_emu_set_texture(NULL); fs_gl_bind_texture(g_text_texture); //glColor4f(1.0, 1.0, 1.0, alpha); //printf("rendering %f %f %f %f...\n", item->x1, item->x2, item->y1, item->y2); fs_gl_color4f(r, g, b, alpha); //glColor4f(r * alpha, g * alpha, b * alpha, alpha); #ifdef USE_GLES GLfloat tex[] = { item->x1, item->y2, item->x2, item->y2, item->x2, item->y1, item->x1, item->y1 }; GLfloat vert[] = { x, y, x + item->width, y, x + item->width, y + item->height, x, y + item->height }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vert); glTexCoordPointer(2, GL_FLOAT, 0, tex); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); #else glBegin(GL_QUADS); glTexCoord2f(item->x1, item->y2); glVertex2f(x, y); glTexCoord2f(item->x2, item->y2); glVertex2f(x + item->width, y); glTexCoord2f(item->x2, item->y1); glVertex2f(x + item->width, y + item->height); glTexCoord2f(item->x1, item->y1); glVertex2f(x, y + item->height); glEnd(); #endif g_cache = g_list_prepend(g_cache, item); sanity_check(); return item->width; } // calculate size of text //printf(":: %s\n", text); int num_chars = 0; int required_width = 0; int required_height = font->h; //char *base_text = fs_utf8_strup(text, -1); const char *base_text = text; unsigned const char *c = (unsigned const char*) base_text; int continuations = 0; int cp = 0; while(*c) { //if ((*c & 0b10000000) == 0b00000000) { if ((*c & 0x80) == 0x0) { continuations = 0; //cp = *c & 0b01111111; cp = *c & 0x7f; } //else if ((*c & 0b11000000) == 0b10000000) { else if ((*c & 0xc0) == 0x80) { continuations--; cp = cp << 6; //cp = cp | (*c & 0b00111111); cp = cp | (*c & 0x3f); } //else if ((*c & 0b11111110) == 0b11111100) { // 1111110x else if ((*c & 0xfe) == 0xfc) { // 1111110x continuations = 5; //cp = *c & 0b00000001; cp = *c & 0x1; } //else if ((*c & 0b11111100) == 0b11111000) { // 111110xx else if ((*c & 0xfc) == 0xf8) { // 111110xx continuations = 4; //cp = *c & 0b00000011; cp = *c & 0x3; } //else if ((*c & 0b11111000) == 0b11110000) { // 11110xxx else if ((*c & 0xf8) == 0xf0) { // 11110xxx continuations = 3; //cp = *c & 0b00000111; cp = *c & 0x7; } //else if ((*c & 0b11110000) == 0b11100000) { // 1110xxxx else if ((*c & 0xf0) == 0xe0) { // 1110xxxx continuations = 2; //cp = *c & 0b00001111; cp = *c & 0xf; } //else if ((*c & 0b11100000) == 0b11000000) { // 110xxxxx else if ((*c & 0xe0) == 0xc0) { // 110xxxxx continuations = 1; //cp = *c & 0b00011111; cp = *c & 0x1f; } ++c; if (continuations) { continue; } cp = fix_char(font, cp); //printf("%d\n", cp); if (required_width + font->w[cp] > g_texture_width) { break; } required_width += font->w[cp]; num_chars++; } int dx = 0; int dy = 0; c = (unsigned const char*) base_text; //printf("base_text: %s\n", c); continuations = 0; cp = 0; int k = 0; while(*c) { //printf("%d\n", *c); //if ((*c & 0b10000000) == 0b00000000) { if ((*c & 0x80) == 0x0) { continuations = 0; //cp = *c & 0b01111111; cp = *c & 0x7f; } //else if ((*c & 0b11000000) == 0b10000000) { else if ((*c & 0xc0) == 0x80) { continuations--; cp = cp << 6; //cp = cp | (*c & 0b00111111); cp = cp | (*c & 0x3f); } //else if ((*c & 0b11111110) == 0b11111100) { // 1111110x else if ((*c & 0xfe) == 0xfc) { // 1111110x continuations = 5; //cp = *c & 0b00000001; cp = *c & 0x1; } //else if ((*c & 0b11111100) == 0b11111000) { // 111110xx else if ((*c & 0xfc) == 0xf8) { // 111110xx continuations = 4; //cp = *c & 0b00000011; cp = *c & 0x3; } //else if ((*c & 0b11111000) == 0b11110000) { // 11110xxx else if ((*c & 0xf8) == 0xf0) { // 11110xxx continuations = 3; //cp = *c & 0b00000111; cp = *c & 0x7; } //else if ((*c & 0b11110000) == 0b11100000) { // 1110xxxx else if ((*c & 0xf0) == 0xe0) { // 1110xxxx continuations = 2; //cp = *c & 0b00001111; cp = *c & 0xf; } //else if ((*c & 0b11100000) == 0b11000000) { // 110xxxxx else if ((*c & 0xe0) == 0xc0) { // 110xxxxx continuations = 1; //cp = *c & 0b00011111; cp = *c & 0x1f; } ++c; if (continuations) { continue; } if (++k > num_chars) { // there may be more chars left in the original string, // but there is no room for the next char in the texture break; } cp = fix_char(font, cp); int sx = font->x[cp]; int sy = font->y[cp]; int sw = font->w[cp]; int sh = font->h; //if (c > 256) { // printf("%d %d %d %d %d\n", c, sx, sy, sw, sh); //} // draw character //printf("-----------> %d\n", cp); int *sl = ((int *) font->image->data) + font->image->width * sy + sx; int ss = font->image->width; // source stride //int *sl = sp; int *dl = ((int *) g_buffer) + g_texture_width * dy + dx; int ds = g_texture_width; // destination stride //int *dl = dp; for (int y = 0; y < sh; y++) { //printf("%d\n", y); int *sp = sl; int *dp = dl; for (int x = 0; x < sw; x++) { //printf("%d %d\n", x, y); //*dp++ = 0xff0000ff; //*dp++ = *sp++; int a = *sp; sp++; *dp = a; dp++; //int a = *sp++; } sl += ss; dl += ds; } dx += sw; } //free(utext); //free(base_text); GList *last = g_list_last(g_cache); cache_item *last_item = (cache_item *) last->data; int position = last_item->position; fs_gl_bind_texture(g_text_texture); int gl_buffer_format = GL_RGBA; if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_BGRA) { gl_buffer_format = GL_BGRA; } #ifdef USE_GLES /* GLES does not support unpack padding of buffer. we have to update line-wise (or create a new one) */ uint8_t *buf = g_buffer; for(int y=0;y<required_height;y++) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, position * 32 + y, required_width, 1, gl_buffer_format, GL_UNSIGNED_BYTE, buf); buf += g_texture_width * 4; } #else fs_gl_unpack_row_length(g_texture_width); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, position * 32, required_width, required_height, gl_buffer_format, GL_UNSIGNED_BYTE, g_buffer); #endif cache_item *item = malloc(sizeof(cache_item)); item->font = font; item->text = g_strdup(text); item->width = required_width; item->height = required_height; item->position = position; item->x1 = 0; item->x2 = required_width / (1.0 * g_texture_width); item->y1 = (item->position * 32) / (1.0 * g_texture_height); item->y2 = (item->position * 32 + required_height) / (1.0 * g_texture_height); //item->texture = render_texture; g_cache = g_list_prepend(g_cache, item); sanity_check(); if (last_item->text) { free(last_item->text); } /* if (last_item->texture) { glDeleteTextures(1, &last_item->texture); } */ free(last_item); g_cache = g_list_delete_link(g_cache, last); sanity_check(); // now the text is in the cache, so call function again return fs_emu_font_render(font, text, x, y, r, g, b, alpha); }
fs_emu_texture *fs_emu_texture_new_from_file(const char *name) { char *full_name; char *path; if (fs_path_exists(name)) { full_name = fs_strdup(name); path = fs_strdup(name); } else { full_name = fs_strconcat(name, ".png", NULL); path = fs_get_program_data_file(full_name); if (path == NULL) { fs_emu_warning("Could not find texture %s\n", full_name); return NULL; } } fs_image *image = fs_image_new_from_file(path); fs_emu_log("loading texture \"%s\"\n", path); free(path); if (image == NULL) { fs_emu_warning("Could not load texture from %s\n", full_name); free(full_name); return NULL; } free(full_name); if (fs_emu_get_video_format() == FS_EMU_VIDEO_FORMAT_BGRA) { // convert to premultiplied alpha if (image->format == FS_IMAGE_FORMAT_RGBA) { int num_pixels = image->width * image->height; unsigned char *pixels = image->data; for (int i = 0; i < num_pixels; i++) { unsigned char alpha = pixels[3]; unsigned char temp = pixels[2]; pixels[2] = ((int) pixels[0]) * alpha / 255; pixels[1] = ((int) pixels[1]) * alpha / 255; pixels[0] = ((int) temp) * alpha / 255; pixels += 4; } } else { // FIXME: should swap R and B here... } } else { // convert to premultiplied alpha if (image->format == FS_IMAGE_FORMAT_RGBA) { int num_pixels = image->width * image->height; unsigned char *pixels = image->data; for (int i = 0; i < num_pixels; i++) { unsigned char alpha = pixels[3]; // should really divide by 255, but 256 is faster... //pixels[0] = ((int) pixels[0]) * alpha / 256; //pixels[1] = ((int) pixels[1]) * alpha / 256; //pixels[2] = ((int) pixels[2]) * alpha / 256; pixels[0] = ((int) pixels[0]) * alpha / 255; pixels[1] = ((int) pixels[1]) * alpha / 255; pixels[2] = ((int) pixels[2]) * alpha / 255; //pixels[0] = (unsigned char) ((pixels[0] * alpha + 0.5) / 255.0); //pixels[1] = (unsigned char) ((pixels[1] * alpha + 0.5) / 255.0); //pixels[2] = (unsigned char) ((pixels[2] * alpha + 0.5) / 255.0); pixels += 4; } } } fs_emu_texture *texture = fs_new(fs_emu_texture, 1); texture->width = image->width; texture->height = image->height; texture->image = image; load_texture(texture); fs_emu_set_texture(texture); fs_gl_add_context_notification(context_notification_handler, texture); return texture; }