This code sample uses Screen with the FreeType library and OpenGL ES for text rendering.
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <ctype.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/keycodes.h> #include <screen/screen.h> #include <ft2build.h> #include FT_FREETYPE_H #include <math.h> #include <EGL/egl.h> #include <GLES/gl.h> #include "png.h" static float width, height; static GLuint background; static GLfloat vertices[8]; static GLfloat tex_coord[8]; static float pos_x, pos_y; struct font_t { unsigned int font_texture; float pt; float advance[128]; float width[128]; float height[128]; float tex_x1[128]; float tex_x2[128]; float tex_y1[128]; float tex_y2[128]; float offset_x[128]; float offset_y[128]; int initialized; }; typedef struct font_t font_t; EGLDisplay egl_disp; EGLSurface egl_surf; static EGLConfig egl_conf; static EGLContext egl_ctx; static screen_window_t screen_win = NULL; static screen_display_t screen_disp; static int nbuffers = 2; static int initialized = 0; static font_t* font; /* Utility function to calculate the dpi based on the display size */ int calculate_dpi() { int screen_phys_size[2] = { 0, 0 }; screen_get_display_property_iv(screen_disp, SCREEN_PROPERTY_PHYSICAL_SIZE, screen_phys_size); /* If using a simulator, {0,0} is returned for physical size of the screen, so use 170 as the default dpi when this is the case. */ if ((screen_phys_size[0] == 0) && (screen_phys_size[1] == 0)) { return 170; } else{ int screen_resolution[2] = { 0, 0 }; screen_get_display_property_iv(screen_disp, SCREEN_PROPERTY_SIZE, screen_resolution); int diagonal_pixels = sqrt(screen_resolution[0] * screen_resolution[0] + screen_resolution[1] * screen_resolution[1]); int diagonal_inches = 0.0393700787 * sqrt(screen_phys_size[0] * screen_phys_size[0] + screen_phys_size[1] * screen_phys_size[1]); return (int)(diagonal_pixels / diagonal_inches); } } static inline int nextp2(int x) { int val = 1; while(val < x) val <<= 1; return val; } /* Utility function to load and ready the font for use by OpenGL */ font_t* load_font(const char* path, int point_size, int dpi) { FT_Library library; FT_Face face; int c; int i, j; font_t* font; if (!initialized) { fprintf(stderr, "EGL has not been initialized\n"); return NULL; } if (!path){ fprintf(stderr, "Invalid path to font file\n"); return NULL; } if(FT_Init_FreeType(&library)) { fprintf(stderr, "Error loading Freetype library\n"); return NULL; } if (FT_New_Face(library, path,0,&face)) { fprintf(stderr, "Error loading font %s\n", path); return NULL; } if(FT_Set_Char_Size ( face, point_size * 64, point_size * 64, dpi, dpi)) { fprintf(stderr, "Error initializing character parameters\n"); return NULL; } font = (font_t*) malloc(sizeof(font_t)); font->initialized = 0; glGenTextures(1, &(font->font_texture)); /*Let each glyph reside in 32x32 section of the font texture */ int segment_size_x = 0, segment_size_y = 0; int num_segments_x = 16; int num_segments_y = 8; FT_GlyphSlot slot; FT_Bitmap bmp; int glyph_width, glyph_height; /*First calculate the max width and height of a character in a passed font*/ for(c = 0; c < 128; c++) { if(FT_Load_Char(face, c, FT_LOAD_RENDER)) { fprintf(stderr, "FT_Load_Char failed\n"); free(font); return NULL; } slot = face->glyph; bmp = slot->bitmap; glyph_width = bmp.width; glyph_height = bmp.rows; if (glyph_width > segment_size_x) { segment_size_x = glyph_width; } if (glyph_height > segment_size_y) { segment_size_y = glyph_height; } } int font_tex_width = nextp2(num_segments_x * segment_size_x); int font_tex_height = nextp2(num_segments_y * segment_size_y); int bitmap_offset_x = 0, bitmap_offset_y = 0; GLubyte* font_texture_data = (GLubyte*) malloc(sizeof(GLubyte) * 2 * font_tex_width * font_tex_height); memset((void*)font_texture_data, 0, sizeof(GLubyte) * 2 * font_tex_width * font_tex_height); if (!font_texture_data) { fprintf(stderr, "Failed to allocate memory for font texture\n"); free(font); return NULL; } /* Fill font texture bitmap with individual bmp data and record appropriate size, texture coordinates and offsets for every glyph */ for(c = 0; c < 128; c++) { if(FT_Load_Char(face, c, FT_LOAD_RENDER)) { fprintf(stderr, "FT_Load_Char failed\n"); free(font); return NULL; } slot = face->glyph; bmp = slot->bitmap; glyph_width = nextp2(bmp.width); glyph_height = nextp2(bmp.rows); div_t temp = div(c, num_segments_x); bitmap_offset_x = segment_size_x * temp.rem; bitmap_offset_y = segment_size_y * temp.quot; for (j = 0; j < glyph_height; j++) { for (i = 0; i < glyph_width; i++) { font_texture_data[2 * ((bitmap_offset_x + i) + (j + bitmap_offset_y) * font_tex_width) + 0] = font_texture_data[2 * ((bitmap_offset_x + i) + (j + bitmap_offset_y) * font_tex_width) + 1] = (i >= bmp.width || j >= bmp.rows)? 0 : bmp.buffer[i + bmp.width * j]; } } font->advance[c] = (float)(slot->advance.x >> 6); font->tex_x1[c] = (float)bitmap_offset_x / (float) font_tex_width; font->tex_x2[c] = (float)(bitmap_offset_x + bmp.width) / (float)font_tex_width; font->tex_y1[c] = (float)bitmap_offset_y / (float) font_tex_height; font->tex_y2[c] = (float)(bitmap_offset_y + bmp.rows) / (float)font_tex_height; font->width[c] = bmp.width; font->height[c] = bmp.rows; font->offset_x[c] = (float)slot->bitmap_left; font->offset_y[c] = (float)((slot->metrics.horiBearingY-face->glyph->metrics.height) >> 6); } glBindTexture(GL_TEXTURE_2D, font->font_texture); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, font_tex_width, font_tex_height, 0, GL_LUMINANCE_ALPHA , GL_UNSIGNED_BYTE, font_texture_data); int err = glGetError(); free(font_texture_data); FT_Done_Face(face); FT_Done_FreeType(library); if (err != 0) { fprintf(stderr, "GL Error 0x%x", err); free(font); return NULL; } font->initialized = 1; return font; } /* Utility function to load background texture */ int load_texture(const char* filename, int* width, int* height, float* tex_x, float* tex_y, unsigned int *tex) { int i; GLuint format; png_byte header[8]; /* header for testing if it is a png */ if (!tex) { return EXIT_FAILURE; } /* Open file as binary */ FILE *fp = fopen(filename, "rb"); if (!fp) { return EXIT_FAILURE; } /* Read the header */ fread(header, 1, 8, fp); /* Test if png */ int is_png = !png_sig_cmp(header, 0, 8); if (!is_png) { fclose(fp); return EXIT_FAILURE; } /* Create png struct */ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { fclose(fp); return EXIT_FAILURE; } /* Create png info struct */ png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); fclose(fp); return EXIT_FAILURE; } /* Create png info struct */ png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); fclose(fp); return EXIT_FAILURE; } /* Set up error handling (required without using custom error handlers above) */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); return EXIT_FAILURE; } /* Initialize png reading */ png_init_io(png_ptr, fp); /* Let libpng know you already read the first 8 bytes */ png_set_sig_bytes(png_ptr, 8); /* Read all the info up to the image data */ png_read_info(png_ptr, info_ptr); /* Variables to pass to get info */ int bit_depth, color_type; png_uint_32 image_width, image_height; /* Get info about png */ png_get_IHDR(png_ptr, info_ptr, &image_width, &image_height, &bit_depth, &color_type, NULL, NULL, NULL); switch (color_type) { case PNG_COLOR_TYPE_RGBA: format = GL_RGBA; break; case PNG_COLOR_TYPE_RGB: format = GL_RGB; break; default: fprintf(stderr,"Unsupported PNG color type (%d) for texture: %s", (int)color_type, filename); fclose(fp); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return NULL; } /* Update the png info struct. */ png_read_update_info(png_ptr, info_ptr); /* Row size in bytes. */ int rowbytes = png_get_rowbytes(png_ptr, info_ptr); /* Allocate the image_data as a big block, to be given to opengl */ png_byte *image_data = (png_byte*) malloc(sizeof(png_byte) * rowbytes * image_height); if (!image_data) { /* clean up memory and close file */ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); return EXIT_FAILURE; } /* Row_pointers is for pointing to image_data for reading the png with libpng */ png_bytep *row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * image_height); if (!row_pointers) { /* clean up memory and close stuff */ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); free(image_data); fclose(fp); return EXIT_FAILURE; } /* Set the individual row_pointers to point at the correct offsets of image_data */ for (i = 0; i < image_height; i++) { row_pointers[image_height - 1 - i] = image_data + i * rowbytes; } /* Read the png into image_data through row_pointers */ png_read_image(png_ptr, row_pointers); int tex_width, tex_height; tex_width = nextp2(image_width); tex_height = nextp2(image_height); glGenTextures(1, tex); glBindTexture(GL_TEXTURE_2D, (*tex)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, format, tex_width, tex_height, 0, format, GL_UNSIGNED_BYTE, NULL); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_width, image_height, format, GL_UNSIGNED_BYTE, image_data); GLint err = glGetError(); /* Clean up memory and close file */ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); free(image_data); free(row_pointers); fclose(fp); if (err == 0) { /* Return physical with and height of texture if pointers are not null */ if(width) { *width = image_width; } if (height) { *height = image_height; } /* Return modified texture coordinates if pointers are not null */ if(tex_x) { *tex_x = ((float) image_width - 0.5f) / ((float)tex_width); } if(tex_y) { *tex_y = ((float) image_height - 0.5f) / ((float)tex_height); } return EXIT_SUCCESS; } else { fprintf(stderr, "GL error %i \n", err); return EXIT_FAILURE; } } /* Utility function to perform EGL cleanup */ void egl_cleanup() { /* Typical EGL cleanup */ if (egl_disp != EGL_NO_DISPLAY) { eglMakeCurrent(egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (egl_surf != EGL_NO_SURFACE) { eglDestroySurface(egl_disp, egl_surf); egl_surf = EGL_NO_SURFACE; } if (egl_ctx != EGL_NO_CONTEXT) { eglDestroyContext(egl_disp, egl_ctx); egl_ctx = EGL_NO_CONTEXT; } if (screen_win != NULL) { screen_destroy_window(screen_win); screen_win = NULL; } eglTerminate(egl_disp); egl_disp = EGL_NO_DISPLAY; } eglReleaseThread(); initialized = 0; } /* Utility function to initialize and configure a EGL rendering surface */ int init_egl(screen_context_t screen_ctx) { int usage; int format = SCREEN_FORMAT_RGBX8888; EGLint interval = 1; int rc, num_configs; EGLint attrib_list[]= { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, EGL_NONE}; /* Assuming GL_ES_1 */ usage = SCREEN_USAGE_OPENGL_ES1 | SCREEN_USAGE_ROTATION; /* Establish a connection to the default display */ egl_disp = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (egl_disp == EGL_NO_DISPLAY) { egl_cleanup(); return EXIT_FAILURE; } /* Initialize EGL on the display */ rc = eglInitialize(egl_disp, NULL, NULL); if (rc != EGL_TRUE) { egl_cleanup(); return EXIT_FAILURE; } /* Calling eglBindAPI() to specify the current rendering API is not necessary * because OpenGL ES is the default rendering API. rc = eglBindAPI(EGL_OPENGL_ES_API); if (rc != EGL_TRUE) { egl_cleanup(); return EXIT_FAILURE; }*/ if(!eglChooseConfig(egl_disp, attrib_list, &egl_conf, 1, &num_configs)) { egl_cleanup(); return EXIT_FAILURE; } egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, NULL); if (egl_ctx == EGL_NO_CONTEXT) { egl_cleanup(); return EXIT_FAILURE; } rc = screen_create_window(&screen_win, screen_ctx); if (rc) { perror("screen_create_window"); egl_cleanup(); return EXIT_FAILURE; } rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_FORMAT, &format); if (rc) { perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)"); egl_cleanup(); return EXIT_FAILURE; } rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &usage); if (rc) { perror("screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)"); egl_cleanup(); return EXIT_FAILURE; } rc = screen_get_window_property_pv(screen_win, SCREEN_PROPERTY_DISPLAY, (void **)&screen_disp); if (rc) { perror("screen_get_window_property_pv"); egl_cleanup(); return EXIT_FAILURE; } int angle = atoi(getenv("ORIENTATION")); screen_display_mode_t screen_mode; rc = screen_get_display_property_pv(screen_disp, SCREEN_PROPERTY_MODE, (void**)&screen_mode); if (rc) { perror("screen_get_display_property_pv"); egl_cleanup(); return EXIT_FAILURE; } int size[2]; rc = screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size); if (rc) { perror("screen_get_window_property_iv"); egl_cleanup(); return EXIT_FAILURE; } int buffer_size[2] = {size[0], size[1]}; if ((angle == 0) || (angle == 180)) { if (((screen_mode.width > screen_mode.height) && (size[0] < size[1])) || ((screen_mode.width < screen_mode.height) && (size[0] > size[1]))) { buffer_size[1] = size[0]; buffer_size[0] = size[1]; } } else if ((angle == 90) || (angle == 270)){ if (((screen_mode.width > screen_mode.height) && (size[0] > size[1])) || ((screen_mode.width < screen_mode.height && size[0] < size[1]))) { buffer_size[1] = size[0]; buffer_size[0] = size[1]; } } else { fprintf(stderr, "Navigator returned an unexpected orientation angle.\n"); egl_cleanup(); return EXIT_FAILURE; } rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, buffer_size); if (rc) { perror("screen_set_window_property_iv"); egl_cleanup(); return EXIT_FAILURE; } rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &angle); if (rc) { perror("screen_set_window_property_iv"); egl_cleanup(); return EXIT_FAILURE; } rc = screen_create_window_buffers(screen_win, nbuffers); if (rc) { perror("screen_create_window_buffers"); egl_cleanup(); return EXIT_FAILURE; } egl_surf = eglCreateWindowSurface(egl_disp, egl_conf, screen_win, NULL); if (egl_surf == EGL_NO_SURFACE) { egl_cleanup(); return EXIT_FAILURE; } rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx); if (rc != EGL_TRUE) { egl_cleanup(); return EXIT_FAILURE; } rc = eglSwapInterval(egl_disp, interval); if (rc != EGL_TRUE) { egl_cleanup(); return EXIT_FAILURE; } initialized = 1; return EXIT_SUCCESS; } void measure_text(font_t* font, const char* msg, float* width, float* height) { int i, c; if (!msg) { return; } if (width) { /* Width of a text rectangle is a sum advances for every glyph in a string */ *width = 0.0f; for(i = 0; i < strlen(msg); ++i) { c = msg[i]; *width += font->advance[c]; } } if (height) { /* Height of a text rectangle is a high of a tallest glyph in a string */ *height = 0.0f; for(i = 0; i < strlen(msg); ++i) { c = msg[i]; if (*height < font->height[c]) { *height = font->height[c]; } } } } int init() { EGLint surface_width, surface_height; /* We are going to load MyriadPro-Bold as it looks a little better and scale it to fit out bubble nicely. */ int dpi = calculate_dpi(); /*font = load_font( "/usr/fonts/font_repository/adobe/MyriadPro-Bold.otf", 15, dpi); */ font = load_font( "/usr/fonts/font_repository/monotype/georgiab.ttf", 8, dpi); if (!font) { return EXIT_FAILURE; } /* Load background texture */ float tex_x, tex_y; if (EXIT_SUCCESS != load_texture("app/native/HelloWorld_smaller_bubble.png", NULL, NULL, &tex_x, &tex_y, &background)) { fprintf(stderr, "Unable to load background texture\n"); } /* Query width and height of the window surface created by utility code */ eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width); eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height); EGLint err = eglGetError(); if (err != 0x3000) { fprintf(stderr, "Unable to query EGL surface dimensions\n"); return EXIT_FAILURE; } width = (float) surface_width; height = (float) surface_height; /* Initialize GL for 2D rendering */ glViewport(0, 0, (int) width, (int) height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0.0f, width / height, 0.0f, 1.0f, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* Set world coordinates to coincide with screen pixels */ glScalef(1.0f / height, 1.0f / height, 1.0f); float text_width, text_height; measure_text(font, "Hello world", &text_width, &text_height); pos_x = (width - text_width) / 2; pos_y = height / 2; /* Setup background polygon */ vertices[0] = 0.0f; vertices[1] = 0.0f; vertices[2] = width; vertices[3] = 0.0f; vertices[4] = 0.0f; vertices[5] = height; vertices[6] = width; vertices[7] = height; tex_coord[0] = 0.0f; tex_coord[1] = 0.0f; tex_coord[2] = tex_x; tex_coord[3] = 0.0f; tex_coord[4] = 0.0f; tex_coord[5] = tex_y; tex_coord[6] = tex_x; tex_coord[7] = tex_y; return EXIT_SUCCESS; } void render_text(font_t* font, const char* msg, float x, float y) { int i, c; GLfloat *vertices; GLfloat *texture_coords; GLshort* indices; float pen_x = 0.0f; if (!font) { fprintf(stderr, "Font must not be null\n"); return; } if (!font->initialized) { fprintf(stderr, "Font has not been loaded\n"); return; } if (!msg) { return; } int texture_enabled; glGetIntegerv(GL_TEXTURE_2D, &texture_enabled); if (!texture_enabled) { glEnable(GL_TEXTURE_2D); } int blend_enabled; glGetIntegerv(GL_BLEND, &blend_enabled); if (!blend_enabled) { glEnable(GL_BLEND); } int gl_blend_src, gl_blend_dst; glGetIntegerv(GL_BLEND_SRC, &gl_blend_src); glGetIntegerv(GL_BLEND_DST, &gl_blend_dst); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); int vertex_array_enabled; glGetIntegerv(GL_VERTEX_ARRAY, &vertex_array_enabled); if (!vertex_array_enabled) { glEnableClientState(GL_VERTEX_ARRAY); } int texture_array_enabled; glGetIntegerv(GL_TEXTURE_COORD_ARRAY, &texture_array_enabled); if (!texture_array_enabled) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); } vertices = (GLfloat*) malloc(sizeof(GLfloat) * 8 * strlen(msg)); texture_coords = (GLfloat*) malloc(sizeof(GLfloat) * 8 * strlen(msg)); indices = (GLshort*) malloc(sizeof(GLfloat) * 5 * strlen(msg)); for(i = 0; i < strlen(msg); ++i) { c = msg[i]; vertices[8 * i + 0] = x + pen_x + font->offset_x[c]; vertices[8 * i + 1] = y + font->offset_y[c]; vertices[8 * i + 2] = vertices[8 * i + 0] + font->width[c]; vertices[8 * i + 3] = vertices[8 * i + 1]; vertices[8 * i + 4] = vertices[8 * i + 0]; vertices[8 * i + 5] = vertices[8 * i + 1] + font->height[c]; vertices[8 * i + 6] = vertices[8 * i + 2]; vertices[8 * i + 7] = vertices[8 * i + 5]; texture_coords[8 * i + 0] = font->tex_x1[c]; texture_coords[8 * i + 1] = font->tex_y2[c]; texture_coords[8 * i + 2] = font->tex_x2[c]; texture_coords[8 * i + 3] = font->tex_y2[c]; texture_coords[8 * i + 4] = font->tex_x1[c]; texture_coords[8 * i + 5] = font->tex_y1[c]; texture_coords[8 * i + 6] = font->tex_x2[c]; texture_coords[8 * i + 7] = font->tex_y1[c]; indices[i * 6 + 0] = 4 * i + 0; indices[i * 6 + 1] = 4 * i + 1; indices[i * 6 + 2] = 4 * i + 2; indices[i * 6 + 3] = 4 * i + 2; indices[i * 6 + 4] = 4 * i + 1; indices[i * 6 + 5] = 4 * i + 3; /* Assume we are only working with typewriter fonts */ pen_x += font->advance[c]; } glVertexPointer(2, GL_FLOAT, 0, vertices); glTexCoordPointer(2, GL_FLOAT, 0, texture_coords); glBindTexture(GL_TEXTURE_2D, font->font_texture); glDrawElements(GL_TRIANGLES, 6 * strlen(msg), GL_UNSIGNED_SHORT, indices); if (!texture_array_enabled) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); } if (!vertex_array_enabled) { glDisableClientState(GL_VERTEX_ARRAY); } if (!texture_enabled) { glDisable(GL_TEXTURE_2D); } glBlendFunc(gl_blend_src, gl_blend_dst); if (!blend_enabled) { glDisable(GL_BLEND); } free(vertices); free(texture_coords); free(indices); } int render() { /* Typical rendering pass */ glClear(GL_COLOR_BUFFER_BIT); /* Render background quad first */ glEnable(GL_TEXTURE_2D); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vertices); glTexCoordPointer(2, GL_FLOAT, 0, tex_coord); glBindTexture(GL_TEXTURE_2D, background); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_TEXTURE_2D); /* Set color to use for text rendering */ glColor4f(0.35f, 0.35f, 0.35f, 1.0f); /* Render the text onto the screen */ render_text(font, "Hello world", pos_x, pos_y); /* Update the screen; Posting of the new frame requires a call to eglSwapBuffers. * For now, this is true even when using single buffering. If an * event has occured that invalidates the surface we are currently * using, eglSwapBuffers() will return EGL_FALSE and set the error * code to EGL_BAD_NATIVE_WINDOW. */ int rc = eglSwapBuffers(egl_disp, egl_surf); return rc; } int main(int argc, char **argv) { int rc; /* a return code */ screen_event_t screen_ev; /* a screen event to handle */ screen_context_t screen_ctx; /* a connection to the screen windowing system */ int vis = 1; /* an indicator if our window is visible */ int pause = 0; /* an indicator if rendering is frozen */ const int exit_area_size = 20; /* a size of area on the window where a user can * contact (using a pointer) to exit this application */ int size[2] = { 0, 0 }; /* the width and height of your window; will default to * the size of display since the window property wasn't * explicitly set */ int val; /* a variable used to set/get window properties */ int pos[2] = { 0, 0 }; /* the x,y position of your pointer */ /*Create a screen context that will be the connection to the windowing system; *this is used to create an EGL surface to receive libscreen events */ screen_create_context(&screen_ctx, 0); /* Initialize EGL for rendering with GL ES 1.1; * this initialization includes initializing and configuring EGL as well as creating * a native window with the appropriate properties to be used as the EGL rendering * surface. */ if (EXIT_SUCCESS != init_egl(screen_ctx)) { fprintf(stderr, "Unable to initialize EGL\n"); screen_destroy_context(screen_ctx); return 0; } /* Initialize application data; * this initialization includes loading the font and background and initializing * the viewport and geometry for your application. */ if (EXIT_SUCCESS != init()) { fprintf(stderr, "Unable to initialize app logic\n"); egl_cleanup(); screen_destroy_context(screen_ctx); return 0; } /* Create a screen event that will be used to retrieve events into so that these * events can be handled.*/ rc = screen_create_event(&screen_ev); if (rc) { fprintf(stderr, "screen_create_event\n"); egl_cleanup(); screen_destroy_context(screen_ctx); return 0; } /* This is your main application loop. It keeps on running unless a close event is * received from the windowing system or an error occurs. The application loop consists * of two parts. The first part processes any events that have been put in your queue. * The second part does the rendering. When the window is visible, you don't wait if * the event queue is empty; you move on to the rendering part immediately. * When the window is not visible we skip the rendering part. */ while (1){ /* The first part of the loop is to handle screen events. * We start the loop by processing any events that might be in our * queue. The only event that is of interest to us are the resize * and close events. The timeout variable is set to 0 (no wait) or * forever depending if the window is visible or not. */ while (!screen_get_event(screen_ctx, screen_ev, vis ? 0 : ~0)) { rc = screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TYPE, &val); if (rc || val == SCREEN_EVENT_NONE) { break; } switch (val) { case SCREEN_EVENT_CLOSE: /* All we have to do when we receive the close event is * to exit the application loop. Because we have a loop * within a loop, a simple break won't work. We'll just * use a goto to take us out of here.*/ goto end; case SCREEN_EVENT_PROPERTY: /* We are interested in visibility changes so we can pause * or unpause the rendering. */ screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_NAME, &val); switch (val) { case SCREEN_PROPERTY_VISIBLE: /* The new visibility status is not included in the * event, so we must get it ourselves. */ screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_VISIBLE, &vis); break; } break; case SCREEN_EVENT_POINTER: /* To provide a way of gracefully terminating our application, * we will exit if there is a pointer select event in the upper * right corner of our window. This should happen if the mouse's * left button is clicked or if a touch screen display is pressed. * The event will come as a screen pointer event, with an (x,y) * coordinate relative to the window's upper left corner and a * select value. We have to verify ourselves that the coordinates * of the pointer are in the upper right hand area. */ screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_BUTTONS, &val); if (val) { screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_POSITION, pos); screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_SIZE, size); fprintf(stderr, "window width: %d, window height: %d\n", size[0], size[1]); fprintf(stderr, "pointer x: %d, pointer y: %d\n", pos[0], pos[1]); if (pos[0] >= size[0] - exit_area_size && pos[1] < exit_area_size) { goto end; } } break; } } /* The second part of the application loop is the rendering. You want * to skip the rendering part if your window is not visible. This will * leave the CPU and GPU to other applications and make the system a * little bit more responsive while you are invisible. */ if (vis && !pause) { rc = render(); if (rc != EGL_TRUE) break; } } end: /* Destroy the font */ if (font) { glDeleteTextures(1, &(font->font_texture)); free(font); } /* Terminate EGL setup */ egl_cleanup(); /* Clean up screen */ screen_destroy_event(screen_ev); screen_destroy_context(screen_ctx); return EXIT_SUCCESS; }