Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
1052 lines (858 sloc) 31.5 KB
/*
* Copyright (c) 2011-2012 Research In Motion Limited.
*
* 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 <time.h>
#include <stdbool.h>
#include <math.h>
#include "bbutil.h"
#ifdef USING_GL11
#include <GLES/gl.h>
#include <GLES/glext.h>
#elif defined(USING_GL20)
#include <GLES2/gl2.h>
#else
#error bbutil must be compiled with either USING_GL11 or USING_GL20 flags
#endif
#include <ft2build.h>
#include FT_FREETYPE_H
#include "png.h"
EGLDisplay egl_disp;
EGLSurface egl_surf;
static EGLConfig egl_conf;
static EGLContext egl_ctx;
static screen_context_t screen_ctx;
static screen_window_t screen_win;
static screen_display_t screen_disp;
static int nbuffers = 2;
static int initialized = 0;
#ifdef USING_GL20
static GLuint text_rendering_program;
static int text_program_initialized = 0;
static GLint positionLoc;
static GLint texcoordLoc;
static GLint textureLoc;
static GLint colorLoc;
#endif
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;
};
static void
bbutil_egl_perror(const char *msg) {
static const char *errmsg[] = {
"function succeeded",
"EGL is not initialized, or could not be initialized, for the specified display",
"cannot access a requested resource",
"failed to allocate resources for the requested operation",
"an unrecognized attribute or attribute value was passed in an attribute list",
"an EGLConfig argument does not name a valid EGLConfig",
"an EGLContext argument does not name a valid EGLContext",
"the current surface of the calling thread is no longer valid",
"an EGLDisplay argument does not name a valid EGLDisplay",
"arguments are inconsistent",
"an EGLNativePixmapType argument does not refer to a valid native pixmap",
"an EGLNativeWindowType argument does not refer to a valid native window",
"one or more argument values are invalid",
"an EGLSurface argument does not name a valid surface configured for rendering",
"a power management event has occurred",
"unknown error code"
};
int message_index = eglGetError() - EGL_SUCCESS;
if (message_index < 0 || message_index > 14)
message_index = 15;
fprintf(stderr, "%s: %s\n", msg, errmsg[message_index]);
}
int
bbutil_init_egl(screen_context_t 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_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, 0,
EGL_NONE};
#ifdef USING_GL11
usage = SCREEN_USAGE_OPENGL_ES1 | SCREEN_USAGE_ROTATION;
attrib_list[9] = EGL_OPENGL_ES_BIT;
#elif defined(USING_GL20)
usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_ROTATION;
attrib_list[9] = EGL_OPENGL_ES2_BIT;
EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
#else
fprintf(stderr, "bbutil should be compiled with either USING_GL11 or USING_GL20 -D flags\n");
return EXIT_FAILURE;
#endif
//Simple egl initialization
screen_ctx = ctx;
egl_disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (egl_disp == EGL_NO_DISPLAY) {
bbutil_egl_perror("eglGetDisplay");
bbutil_terminate();
return EXIT_FAILURE;
}
rc = eglInitialize(egl_disp, NULL, NULL);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglInitialize");
bbutil_terminate();
return EXIT_FAILURE;
}
rc = eglBindAPI(EGL_OPENGL_ES_API);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglBindApi");
bbutil_terminate();
return EXIT_FAILURE;
}
if(!eglChooseConfig(egl_disp, attrib_list, &egl_conf, 1, &num_configs)) {
bbutil_terminate();
return EXIT_FAILURE;
}
#ifdef USING_GL20
egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, attributes);
#elif defined(USING_GL11)
egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, NULL);
#endif
if (egl_ctx == EGL_NO_CONTEXT) {
bbutil_egl_perror("eglCreateContext");
bbutil_terminate();
return EXIT_FAILURE;
}
rc = screen_create_window(&screen_win, screen_ctx);
if (rc) {
perror("screen_create_window");
bbutil_terminate();
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)");
bbutil_terminate();
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)");
bbutil_terminate();
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");
bbutil_terminate();
return EXIT_FAILURE;
}
int screen_resolution[2];
rc = screen_get_display_property_iv(screen_disp, SCREEN_PROPERTY_SIZE, screen_resolution);
if (rc) {
perror("screen_get_display_property_iv");
bbutil_terminate();
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");
bbutil_terminate();
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");
bbutil_terminate();
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");
bbutil_terminate();
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");
bbutil_terminate();
return EXIT_FAILURE;
}
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &angle);
if (rc) {
perror("screen_set_window_property_iv");
bbutil_terminate();
return EXIT_FAILURE;
}
rc = screen_create_window_buffers(screen_win, nbuffers);
if (rc) {
perror("screen_create_window_buffers");
bbutil_terminate();
return EXIT_FAILURE;
}
egl_surf = eglCreateWindowSurface(egl_disp, egl_conf, screen_win, NULL);
if (egl_surf == EGL_NO_SURFACE) {
bbutil_egl_perror("eglCreateWindowSurface");
bbutil_terminate();
return EXIT_FAILURE;
}
rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglMakeCurrent");
bbutil_terminate();
return EXIT_FAILURE;
}
rc = eglSwapInterval(egl_disp, interval);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglSwapInterval");
bbutil_terminate();
return EXIT_FAILURE;
}
initialized = 1;
return EXIT_SUCCESS;
}
void
bbutil_terminate() {
//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;
}
void
bbutil_swap() {
int rc = eglSwapBuffers(egl_disp, egl_surf);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglSwapBuffers");
}
}
/* Finds the next power of 2 */
static inline int
nextp2(int x)
{
int val = 1;
while(val < x) val <<= 1;
return val;
}
font_t* bbutil_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));
if (!font) {
fprintf(stderr, "Unable to allocate memory for font structure\n");
return NULL;
}
font->initialized = 0;
font->pt = point_size;
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*) calloc(2 * font_tex_width * font_tex_height, sizeof(GLubyte));
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 = bmp.width;
glyph_height = 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);
free(font_texture_data);
FT_Done_Face(face);
FT_Done_FreeType(library);
font->initialized = 1;
return font;
}
void bbutil_render_text(font_t* font, const char* msg, float x, float y, float r, float g, float b, float a) {
int i, c;
GLfloat *vertices;
GLfloat *texture_coords;
GLushort* 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;
}
const int msg_len = strlen(msg);
vertices = (GLfloat*) malloc(sizeof(GLfloat) * 8 * msg_len);
texture_coords = (GLfloat*) malloc(sizeof(GLfloat) * 8 * msg_len);
indices = (GLushort*) malloc(sizeof(GLushort) * 6 * msg_len);
for(i = 0; i < msg_len; ++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];
}
#ifdef USING_GL11
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glColor4f(r, g, b, a);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texture_coords);
glBindTexture(GL_TEXTURE_2D, font->font_texture);
glDrawElements(GL_TRIANGLES, 6 * msg_len, GL_UNSIGNED_SHORT, indices);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
#elif defined USING_GL20
if (!text_program_initialized) {
GLint status;
// Create shaders if this hasn't been done already
const char* v_source =
"precision mediump float;"
"attribute vec2 a_position;"
"attribute vec2 a_texcoord;"
"varying vec2 v_texcoord;"
"void main()"
"{"
" gl_Position = vec4(a_position, 0.0, 1.0);"
" v_texcoord = a_texcoord;"
"}";
const char* f_source =
"precision lowp float;"
"varying vec2 v_texcoord;"
"uniform sampler2D u_font_texture;"
"uniform vec4 u_col;"
"void main()"
"{"
" vec4 temp = texture2D(u_font_texture, v_texcoord);"
" gl_FragColor = u_col * temp;"
"}";
// Compile the vertex shader
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
if (!vs) {
fprintf(stderr, "Failed to create vertex shader: %d\n", glGetError());
return;
} else {
glShaderSource(vs, 1, &v_source, 0);
glCompileShader(vs);
glGetShaderiv(vs, GL_COMPILE_STATUS, &status);
if (GL_FALSE == status) {
GLchar log[256];
glGetShaderInfoLog(vs, 256, NULL, log);
fprintf(stderr, "Failed to compile vertex shader: %s\n", log);
glDeleteShader(vs);
}
}
// Compile the fragment shader
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
if (!fs) {
fprintf(stderr, "Failed to create fragment shader: %d\n", glGetError());
return;
} else {
glShaderSource(fs, 1, &f_source, 0);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &status);
if (GL_FALSE == status) {
GLchar log[256];
glGetShaderInfoLog(fs, 256, NULL, log);
fprintf(stderr, "Failed to compile fragment shader: %s\n", log);
glDeleteShader(vs);
glDeleteShader(fs);
return;
}
}
// Create and link the program
text_rendering_program = glCreateProgram();
if (text_rendering_program)
{
glAttachShader(text_rendering_program, vs);
glAttachShader(text_rendering_program, fs);
glLinkProgram(text_rendering_program);
glGetProgramiv(text_rendering_program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
GLchar log[256];
glGetProgramInfoLog(fs, 256, NULL, log);
fprintf(stderr, "Failed to link text rendering shader program: %s\n", log);
glDeleteProgram(text_rendering_program);
text_rendering_program = 0;
return;
}
} else {
fprintf(stderr, "Failed to create a shader program\n");
glDeleteShader(vs);
glDeleteShader(fs);
return;
}
// We don't need the shaders anymore - the program is enough
glDeleteShader(fs);
glDeleteShader(vs);
glUseProgram(text_rendering_program);
// Store the locations of the shader variables we need later
positionLoc = glGetAttribLocation(text_rendering_program, "a_position");
texcoordLoc = glGetAttribLocation(text_rendering_program, "a_texcoord");
textureLoc = glGetUniformLocation(text_rendering_program, "u_font_texture");
colorLoc = glGetUniformLocation(text_rendering_program, "u_col");
text_program_initialized = 1;
}
glEnable(GL_BLEND);
//Map text coordinates from (0...surface width, 0...surface height) to (-1...1, -1...1)
//this make our vertex shader very simple and also works irrespective of orientation changes
EGLint surface_width, surface_height;
eglQuerySurface(egl_disp, egl_surf, EGL_WIDTH, &surface_width);
eglQuerySurface(egl_disp, egl_surf, EGL_HEIGHT, &surface_height);
for(i = 0; i < 4 * msg_len; ++i) {
vertices[2 * i + 0] = 2 * vertices[2 * i + 0] / surface_width - 1.0f;
vertices[2 * i + 1] = 2 * vertices[2 * i + 1] / surface_height - 1.0f;
}
//Render text
glUseProgram(text_rendering_program);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, font->font_texture);
glUniform1i(textureLoc, 0);
glUniform4f(colorLoc, r, g, b, a);
glEnableVertexAttribArray(positionLoc);
glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(texcoordLoc);
glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, GL_FALSE, 0, texture_coords);
//Draw the string
glDrawElements(GL_TRIANGLES, 6 * msg_len, GL_UNSIGNED_SHORT, indices);
glDisableVertexAttribArray(positionLoc);
glDisableVertexAttribArray(texcoordLoc);
#else
fprintf(stderr, "bbutil should be compiled with either USING_GL11 or USING_GL20 -D flags\n");
#endif
free(vertices);
free(texture_coords);
free(indices);
}
void bbutil_destroy_font(font_t* font) {
if (!font) {
return;
}
glDeleteTextures(1, &(font->font_texture));
free(font);
}
void bbutil_measure_text(font_t* font, const char* msg, float* width, float* height) {
int i, c;
if (!msg) {
return;
}
const int msg_len =strlen(msg);
if (width) {
//Width of a text rectangle is a sum advances for every glyph in a string
*width = 0.0f;
for(i = 0; i < msg_len; ++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 < msg_len; ++i) {
c = msg[i];
if (*height < font->height[c]) {
*height = font->height[c];
}
}
}
}
int bbutil_load_texture(const char* filename, int* width, int* height, float* tex_x, float* tex_y, unsigned int *tex) {
int i;
GLuint format;
//header for testing if it is a png
png_byte header[8];
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;
}
//setup 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;
}
//init 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 stuff
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);
if ((tex_width != image_width) || (tex_height != image_height) ) {
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);
} else {
glTexImage2D(GL_TEXTURE_2D, 0, format, tex_width, tex_height, 0, format, GL_UNSIGNED_BYTE, image_data);
}
GLint err = glGetError();
//clean up memory and close stuff
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;
}
}
int bbutil_calculate_dpi(screen_context_t ctx) {
int rc;
int screen_phys_size[2];
rc = screen_get_display_property_iv(screen_disp, SCREEN_PROPERTY_PHYSICAL_SIZE, screen_phys_size);
if (rc) {
perror("screen_get_display_property_iv");
bbutil_terminate();
return EXIT_FAILURE;
}
//Simulator will return 0,0 for physical size of the screen, so use 170 as default dpi
if ((screen_phys_size[0] == 0) && (screen_phys_size[1] == 0)) {
return 170;
} else {
int screen_resolution[2];
rc = screen_get_display_property_iv(screen_disp, SCREEN_PROPERTY_SIZE, screen_resolution);
if (rc) {
perror("screen_get_display_property_iv");
bbutil_terminate();
return EXIT_FAILURE;
}
double diagonal_pixels = sqrt(screen_resolution[0] * screen_resolution[0] + screen_resolution[1] * screen_resolution[1]);
double 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 + 0.5);
}
}
int bbutil_rotate_screen_surface(int angle) {
int rc, rotation, skip = 1, temp;;
EGLint interval = 1;
int size[2];
if ((angle != 0) && (angle != 90) && (angle != 180) && (angle != 270)) {
fprintf(stderr, "Invalid angle\n");
return EXIT_FAILURE;
}
rc = screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &rotation);
if (rc) {
perror("screen_set_window_property_iv");
return EXIT_FAILURE;
}
rc = screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size);
if (rc) {
perror("screen_set_window_property_iv");
return EXIT_FAILURE;
}
switch (angle - rotation) {
case -270:
case -90:
case 90:
case 270:
temp = size[0];
size[0] = size[1];
size[1] = temp;
skip = 0;
break;
}
if (!skip) {
rc = eglMakeCurrent(egl_disp, NULL, NULL, NULL);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglMakeCurrent");
return EXIT_FAILURE;
}
rc = eglDestroySurface(egl_disp, egl_surf);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglMakeCurrent");
return EXIT_FAILURE;
}
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_SOURCE_SIZE, size);
if (rc) {
perror("screen_set_window_property_iv");
return EXIT_FAILURE;
}
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_BUFFER_SIZE, size);
if (rc) {
perror("screen_set_window_property_iv");
return EXIT_FAILURE;
}
egl_surf = eglCreateWindowSurface(egl_disp, egl_conf, screen_win, NULL);
if (egl_surf == EGL_NO_SURFACE) {
bbutil_egl_perror("eglCreateWindowSurface");
return EXIT_FAILURE;
}
rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglMakeCurrent");
return EXIT_FAILURE;
}
rc = eglSwapInterval(egl_disp, interval);
if (rc != EGL_TRUE) {
bbutil_egl_perror("eglSwapInterval");
return EXIT_FAILURE;
}
}
rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_ROTATION, &angle);
if (rc) {
perror("screen_set_window_property_iv");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Something went wrong with that request. Please try again.