// Common int32 requestedKeyboardOverlayImage = 0; #ifndef QB64_GUI // begin stubs // STUB: simulate generating a hardware surface int32 new_hardware_img(int32 x, int32 y, uint32 *pixels, int32 flags) { // create hardware img int32 handle; hardware_img_struct *hardware_img; handle = list_add(hardware_img_handles); hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle); hardware_img->w = x; hardware_img->h = y; hardware_img->dest_context_handle = 0; hardware_img->depthbuffer_handle = 0; hardware_img->pending_commands = 0; hardware_img->remove = 0; hardware_img->alpha_disabled = 0; hardware_img->depthbuffer_mode = DEPTHBUFFER_MODE__ON; hardware_img->valid = 1; hardware_img->source_state.PO2_fix = PO2_FIX__OFF; hardware_img->source_state.texture_wrap = TEXTURE_WRAP_MODE__UNKNOWN; hardware_img->source_state.smooth_stretched = SMOOTH_MODE__UNKNOWN; hardware_img->source_state.smooth_shrunk = SMOOTH_MODE__UNKNOWN; if (flags & NEW_HARDWARE_IMG__BUFFER_CONTENT) { hardware_img->texture_handle = 0; if (flags & NEW_HARDWARE_IMG__DUPLICATE_PROVIDED_BUFFER) { hardware_img->software_pixel_buffer = NULL; } else { free(pixels); // the buffer was meant to be consumed, so we just // free it immediately hardware_img->software_pixel_buffer = NULL; } } return handle; } #else // end stubs int32 force_NPO2_fix = 0; // This should only be set to 1 for debugging QB64 uint32 *NPO2_buffer = (uint32 *)malloc(4); int32 NPO2_buffer_size_in_pixels = 1; uint32 *NPO2_texture_generate(int32 *px, int32 *py, uint32 *pixels) { int32 ox = *px; int32 oy = *py; int32 nx = 1; int32 ny = 1; // assume not negative & not 0 while ((ox & 1) == 0) { ox >>= 1; nx <<= 1; } if (ox != 1) { // x is not a power of 2 while (ox != 0) { ox >>= 1; nx <<= 1; } nx << 1; } while ((oy & 1) == 0) { oy >>= 1; ny <<= 1; } if (oy != 1) { // y is not a power of 2 while (oy != 0) { oy >>= 1; ny <<= 1; } ny << 1; } // reset original values ox = *px; oy = *py; if (nx == ox && ny == oy) { // no action required return pixels; } int32 size_in_pixels = nx * ny; if (size_in_pixels > NPO2_buffer_size_in_pixels) { NPO2_buffer = (uint32 *)realloc(NPO2_buffer, size_in_pixels * 4); NPO2_buffer_size_in_pixels = size_in_pixels; } // copy source NPO2 rectangle into destination PO2 rectangle if (nx == ox) { // can copy as a single block memcpy(NPO2_buffer, pixels, ox * oy * 4); } else { uint32 *dst_pixel_offset = NPO2_buffer; uint32 *src_pixel_offset = pixels; while (oy--) { memcpy(dst_pixel_offset, src_pixel_offset, ox * 4); dst_pixel_offset += nx; src_pixel_offset += ox; } oy = *py; } // tidy edges - extend the right-most column and bottom-most row to avoid // pixel/color bleeding rhs column if (ox != nx) { for (int y = 0; y < oy; y++) { NPO2_buffer[ox + nx * y] = NPO2_buffer[ox + nx * y - 1]; } } // bottom row + 1 pixel for corner if (oy != ny) { for (int x = 0; x < (ox + 1); x++) { NPO2_buffer[nx * oy + x] = NPO2_buffer[nx * oy + x - nx]; } } // int maxtexsize; // glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize); // alert(maxtexsize); // alert(nx); // alert(ny); *px = nx; *py = ny; return NPO2_buffer; } int32 new_texture_handle() { GLuint texture = 0; glGenTextures(1, &texture); return (int32)texture; } int32 new_hardware_img(int32 x, int32 y, uint32 *pixels, int32 flags) { // note: non power-of-2 dimensioned textures are supported on modern 3D // cards and // even on some older cards, as long as mip-mapping is not being used // therefore, no attempt is made to convert the non-power-of-2 SCREEN // sizes via software to avoid the performance hit this would incur // create hardware img int32 handle; hardware_img_struct *hardware_img; handle = list_add(hardware_img_handles); hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle); hardware_img->w = x; hardware_img->h = y; hardware_img->dest_context_handle = 0; hardware_img->depthbuffer_handle = 0; hardware_img->pending_commands = 0; hardware_img->remove = 0; hardware_img->alpha_disabled = 0; hardware_img->depthbuffer_mode = DEPTHBUFFER_MODE__ON; hardware_img->valid = 1; hardware_img->source_state.PO2_fix = PO2_FIX__OFF; hardware_img->source_state.texture_wrap = TEXTURE_WRAP_MODE__UNKNOWN; hardware_img->source_state.smooth_stretched = SMOOTH_MODE__UNKNOWN; hardware_img->source_state.smooth_shrunk = SMOOTH_MODE__UNKNOWN; if (flags & NEW_HARDWARE_IMG__BUFFER_CONTENT) { hardware_img->texture_handle = 0; if (flags & NEW_HARDWARE_IMG__DUPLICATE_PROVIDED_BUFFER) { hardware_img->software_pixel_buffer = (uint32 *)malloc(x * y * 4); memcpy(hardware_img->software_pixel_buffer, pixels, x * y * 4); } else { hardware_img->software_pixel_buffer = pixels; } } else { hardware_img->software_pixel_buffer = NULL; hardware_img->texture_handle = new_texture_handle(); glBindTexture(GL_TEXTURE_2D, hardware_img->texture_handle); // non-power of 2 dimensions fallback support static int glerrorcode; glerrorcode = glGetError(); // clear any previous errors if (force_NPO2_fix == 0) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels); glerrorcode = glGetError(); if (glerrorcode != 0 || force_NPO2_fix == 1) { int32 nx = x, ny = y; uint32 *npixels = NPO2_texture_generate(&nx, &ny, pixels); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nx, ny, 0, GL_BGRA, GL_UNSIGNED_BYTE, npixels); hardware_img->source_state.PO2_fix = PO2_FIX__EXPANDED; hardware_img->PO2_w = nx; hardware_img->PO2_h = ny; glerrorcode = glGetError(); if (glerrorcode) { gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, x, y, GL_BGRA, GL_UNSIGNED_BYTE, pixels); glerrorcode = glGetError(); if (glerrorcode) { alert("gluBuild2DMipmaps failed"); alert(glerrorcode); } hardware_img->source_state.PO2_fix = PO2_FIX__MIPMAPPED; hardware_img->PO2_w = x; hardware_img->PO2_h = y; } } set_render_source(INVALID_HARDWARE_HANDLE); } return handle; } void hardware_img_buffer_to_texture(int32 handle) { static hardware_img_struct *hardware_img; hardware_img = (hardware_img_struct *)list_get(hardware_img_handles, handle); if (hardware_img->texture_handle == 0) { hardware_img->texture_handle = new_texture_handle(); glBindTexture(GL_TEXTURE_2D, hardware_img->texture_handle); // non-power of 2 dimensions fallback support static int glerrorcode; glerrorcode = glGetError(); // clear any previous errors if (force_NPO2_fix == 0) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, hardware_img->w, hardware_img->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, hardware_img->software_pixel_buffer); glerrorcode = glGetError(); if (glerrorcode != 0 || force_NPO2_fix == 1) { hardware_img->source_state.PO2_fix = PO2_FIX__EXPANDED; int32 x = hardware_img->w; int32 y = hardware_img->h; uint32 *pixels = NPO2_texture_generate(&x, &y, hardware_img->software_pixel_buffer); hardware_img->PO2_w = x; hardware_img->PO2_h = y; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, x, y, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels); glerrorcode = glGetError(); if (glerrorcode) { gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, hardware_img->w, hardware_img->h, GL_BGRA, GL_UNSIGNED_BYTE, hardware_img->software_pixel_buffer); glerrorcode = glGetError(); if (glerrorcode) { alert("gluBuild2DMipmaps failed"); alert(glerrorcode); } hardware_img->source_state.PO2_fix = PO2_FIX__MIPMAPPED; hardware_img->PO2_w = hardware_img->w; hardware_img->PO2_h = hardware_img->h; } } free(hardware_img->software_pixel_buffer); hardware_img->software_pixel_buffer = NULL; // 2015 critical bug fix set_render_source(INVALID_HARDWARE_HANDLE); } } void hardware_img_requires_depthbuffer(hardware_img_struct *hardware_img) { if (hardware_img->depthbuffer_handle == 0) { // inspiration... // http://www.opengl.org/wiki/Framebuffer_Object_Examples#Color_texture.2C_Depth_texture static GLuint depth_tex; # ifndef QB64_GLES glGenTextures(1, &depth_tex); glBindTexture(GL_TEXTURE_2D, depth_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, hardware_img->w, hardware_img->h, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0 /*mipmap level*/); # else glGenRenderbuffers(1, &depth_tex); glBindRenderbuffer(GL_RENDERBUFFER, depth_tex); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, hardware_img->w, hardware_img->h); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_tex); # endif // NULL means reserve texture memory, but texels are undefined glClear(GL_DEPTH_BUFFER_BIT); hardware_img->depthbuffer_handle = depth_tex; set_render_source(INVALID_HARDWARE_HANDLE); } } #endif