Merge: Small clean up and improvements to MNit from WBTW
authorJean Privat <jean@pryen.org>
Wed, 22 Apr 2015 06:46:03 +0000 (13:46 +0700)
committerJean Privat <jean@pryen.org>
Wed, 22 Apr 2015 06:46:03 +0000 (13:46 +0700)
The new OpenGL parameters affect the display of images for a better pixelated look:
* When zoomed out, the image is smoother, the color values are interpolated from the nearest source pixels.
* When zoomed in, the image is displayed pixelated.

You may recognize the `Int::next_pow` method, `inkscape_tools` use something similar for the same purpose. Actually, with this PR, `inkscape_tools` may not need it anymore and produce smaller PNG files.

Pull-Request: #1284
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>

lib/mnit/opengles1.nit
lib/mnit_android/android_assets.nit
lib/mnit_linux/linux_opengles1.nit

index eb1061b..b11d8dd 100644 (file)
@@ -55,17 +55,12 @@ in "C header" `{
 
        GLenum mnit_opengles_error_code;
 
-       struct mnit_opengles_Texture *mnit_opengles_load_image( const uint_least32_t *pixels, int width, int height, int has_alpha );
+       struct mnit_opengles_Texture *mnit_opengles_load_image(
+               const uint_least32_t *pixels, int width, int height,
+               int width_pow2, int height_pow2, int has_alpha);
 `}
 
 in "C" `{
-       #define LOGW(...) ((void)fprintf(stderr, "# warn: %s (%i)\n", __VA_ARGS__))
-       #ifdef DEBUG
-               #define LOGI(...) ((void)fprintf(stderr, "# info: %s (%i)\n", __VA_ARGS__))
-       #else
-               #define LOGI(...) (void)0
-       #endif
-
        extern NativeWindowType mnit_window;
        extern EGLNativeDisplayType mnit_native_display;
 
@@ -86,7 +81,9 @@ in "C" `{
                {1.0f, 0.0f}
        };
 
-       struct mnit_opengles_Texture *mnit_opengles_load_image( const uint_least32_t *pixels, int width, int height, int has_alpha )
+       struct mnit_opengles_Texture *mnit_opengles_load_image(
+               const uint_least32_t *pixels, int width, int height,
+               int width_pow2, int height_pow2, int has_alpha)
        {
                struct mnit_opengles_Texture *image = malloc(sizeof(struct mnit_opengles_Texture));
                int format = has_alpha? GL_RGBA: GL_RGB;
@@ -100,36 +97,37 @@ in "C" `{
 
                image->src_xo = 0;
                image->src_yo = 0;
-               image->src_xi = 1.0;
-               image->src_yi = 1.0;
+               image->src_xi = ((float)width)/width_pow2;
+               image->src_yi = ((float)height)/height_pow2;
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                       LOGW("error loading image after malloc", mnit_opengles_error_code);
+                       PRINT_ERROR("error loading image after malloc: %i", mnit_opengles_error_code);
                }
 
                glGenTextures(1, &image->texture);
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                       LOGW("error loading image after glGenTextures", mnit_opengles_error_code);
+                       PRINT_ERROR("error loading image after glGenTextures: %i", mnit_opengles_error_code);
                }
 
                glBindTexture(GL_TEXTURE_2D, image->texture);
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                       LOGW("error loading image glBindTexture", mnit_opengles_error_code);
+                       PRINT_ERROR("error loading image glBindTexture: %i", mnit_opengles_error_code);
                }
 
-               glTexImage2D(   GL_TEXTURE_2D, 0, format, width, height,
+               glTexImage2D(   GL_TEXTURE_2D, 0, format, width_pow2, height_pow2,
                                                0, format, GL_UNSIGNED_BYTE, (GLvoid*)pixels);
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                       LOGW("error loading image after glTexImage2D", mnit_opengles_error_code);
+                       PRINT_ERROR("error loading image after glTexImage2D: %i", mnit_opengles_error_code);
                }
 
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                       LOGW("error loading image after gtTexParameter", mnit_opengles_error_code);
+                       PRINT_ERROR("error loading image after gtTexParameter: %i", mnit_opengles_error_code);
                }
 
                return image;
@@ -162,27 +160,27 @@ class Opengles1Display
 
                EGLDisplay display = eglGetDisplay(mnit_native_display);
                if ( display == EGL_NO_DISPLAY) {
-                       LOGW("Unable to eglGetDisplay", 0);
+                       PRINT_ERROR("Unable to eglGetDisplay");
                        return -1;
                }
 
                if ( eglInitialize(display, 0, 0) == EGL_FALSE) {
-                       LOGW("Unable to eglInitialize", 0);
+                       PRINT_ERROR("Unable to eglInitialize");
                        return -1;
                }
 
                if ( eglChooseConfig(display, attribs, &config, 1, &numConfigs) == EGL_FALSE) {
-                       LOGW("Unable to eglChooseConfig", 0);
+                       PRINT_ERROR("Unable to eglChooseConfig");
                        return -1;
                }
 
                if ( numConfigs == 0 ) {
-                       LOGW("No configs available for egl", 0);
+                       PRINT_ERROR("No configs available for egl");
                        return -1;
                }
 
                if ( eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format) == EGL_FALSE) {
-                       LOGW("Unable to eglGetConfigAttrib", 0);
+                       PRINT_ERROR("Unable to eglGetConfigAttrib");
                        return -1;
                }
 
@@ -193,7 +191,7 @@ class Opengles1Display
                context = eglCreateContext(display, config, NULL, NULL);
 
                if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
-                       LOGW("Unable to eglMakeCurrent", 0);
+                       PRINT_ERROR("Unable to eglMakeCurrent");
                        return -1;
                }
 
@@ -207,11 +205,6 @@ class Opengles1Display
                mnit_width = w;
                mnit_height = h;
 
-               LOGI("surface", (int)surface);
-               LOGI("display", (int)display);
-               LOGI("width", w);
-               LOGI("height", h);
-
                glViewport(0, 0, mnit_width, mnit_height);
                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
@@ -304,7 +297,7 @@ class Opengles1Display
                glDisable(GL_TEXTURE_2D);
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                  LOGW("error drawing", mnit_opengles_error_code);
+                  PRINT_ERROR("error drawing: %i", mnit_opengles_error_code);
                }
        `}
 
@@ -354,7 +347,7 @@ class Opengles1Display
                glDisable(GL_TEXTURE_2D);
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                  LOGW("error drawing", mnit_opengles_error_code);
+                  PRINT_ERROR("error drawing: %i", mnit_opengles_error_code);
                }
        `}
 
@@ -409,7 +402,7 @@ class Opengles1Display
                glDisable(GL_TEXTURE_2D);
 
                if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
-                  LOGW("error drawing", mnit_opengles_error_code);
+                  PRINT_ERROR("error drawing: %i", mnit_opengles_error_code);
                }
        `}
 
@@ -466,10 +459,12 @@ extern class Opengles1Image in "C" `{struct mnit_opengles_Texture *`}
                image->scale = recv->scale;
                image->blended = recv->blended;
 
-               image->src_xo = ((float)x)/recv->width;
-               image->src_yo = ((float)y)/recv->height;
-               image->src_xi = ((float)x+w)/recv->width;
-               image->src_yi = ((float)y+h)/recv->height;
+               float r_dx = recv->src_xi - recv->src_xo;
+               float r_dy = recv->src_yi - recv->src_yo;
+               image->src_xo = recv->src_xo + ((float)x)/recv->width*r_dx;
+               image->src_yo = recv->src_yo + ((float)y)/recv->height*r_dy;
+               image->src_xi = recv->src_xo + ((float)x+w)/recv->width*r_dx;
+               image->src_yi = recv->src_yo + ((float)y+h)/recv->height*r_dy;
 
                return Opengles1Image_as_Image( image );
     `}
index 8f61c90..1720c57 100644 (file)
@@ -86,7 +86,7 @@ redef class App
                var a = load_asset_from_apk(path)
                if a != null then
                        if path.file_extension == "png" then
-                               var png = new Opengles1Image.from_android_asset(a)      
+                               var png = new Opengles1Image.from_android_asset(a)
                                a.close
                                return png
                        else if path.file_extension == "txt" then
@@ -118,7 +118,7 @@ end
 
 redef class Opengles1Image
        # Read a png from a zipped stream
-       new from_android_asset(asset: AndroidAsset) is extern `{
+       new from_android_asset(asset: AndroidAsset) import Int.next_pow `{
                struct mnit_opengles_Texture *recv = NULL;
 
                png_structp png_ptr = NULL;
@@ -133,13 +133,16 @@ redef class Opengles1Image
                unsigned char *pixels = NULL;
                unsigned int i;
 
+               png_uint_32 width_pow2, height_pow2;
+               unsigned int row_bytes_pow2;
+
                unsigned char sig[8];
                int sig_read = AAsset_read(asset, sig, 8);
                if (png_sig_cmp(sig, 0, sig_read)) {
                        LOGW("invalide png signature");
                        return NULL;
                }
-               
+
                png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
                if (png_ptr == NULL) {
                        LOGW("png_create_read_struct failed");
@@ -176,11 +179,16 @@ redef class Opengles1Image
                        png_read_update_info(png_ptr, info_ptr);
                }
 
-               LOGW("w: %i, h: %i", width, height);
+               width_pow2 = Int_next_pow(width, 2);
+               height_pow2 = Int_next_pow(height, 2);
+
+               LOGW("Loading image of w: %i, h: %i, w2: %d, h2: %d",
+                       width, height, width_pow2, height_pow2);
 
                row_bytes = png_get_rowbytes(png_ptr, info_ptr);
-               pixels = malloc(row_bytes * height);
-               row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
+               row_bytes_pow2 = row_bytes * width_pow2 / width;
+               pixels = malloc(row_bytes_pow2 * height_pow2);
+               row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height_pow2);
 
                for (i=0; i<height; i++)
                        row_pointers[i] = (png_byte*) malloc(row_bytes);
@@ -188,11 +196,16 @@ redef class Opengles1Image
                png_read_image(png_ptr, row_pointers);
 
                for (i = 0; i < height; i++)
-                       memcpy(pixels + (row_bytes*i),
-                                       row_pointers[i], row_bytes);
+                       memcpy(pixels + (row_bytes_pow2*i), row_pointers[i], row_bytes);
+
+               recv = mnit_opengles_load_image((const uint_least32_t *)pixels,
+                       width, height, width_pow2, height_pow2, has_alpha);
 
-               recv = mnit_opengles_load_image((const uint_least32_t *)pixels, width, height, has_alpha);
-               LOGW("OK");
+               // Calculate the size of the client-side memory allocated and freed
+               float size = ((float)row_bytes_pow2) * height_pow2/1024.0/1024.0;
+               static float total_size = 0;
+               total_size += size;
+               LOGI("Loaded OK %fmb out of %fmb", size, total_size);
 
        close_png_ptr:
                if (info_ptr != NULL)
@@ -213,3 +226,13 @@ redef class Opengles1Image
                return recv;
        `}
 end
+
+redef universal Int
+       # The first power of `exp` greater or equal to `self`
+       private fun next_pow(exp: Int): Int
+       do
+               var p = 0
+               while p < self do p = p*exp
+               return p
+       end
+end
index 106bc07..084abcc 100644 (file)
@@ -78,7 +78,9 @@ end
 
 redef extern class Opengles1Image
        new from_sdl_image( sdl_image: SDLImage ) is extern `{
-               return mnit_opengles_load_image( sdl_image->pixels, sdl_image->w, sdl_image->h, sdl_image->format->Amask );
+               return mnit_opengles_load_image( sdl_image->pixels,
+                       sdl_image->w, sdl_image->h,
+                       sdl_image->w, sdl_image->h, sdl_image->format->Amask );
        `}
 
        # using sdl
@@ -91,7 +93,9 @@ redef extern class Opengles1Image
                        fprintf(stderr, "SDL failed to load image <%s>: %s\n", String_to_cstring(path), IMG_GetError());
                        return NULL;
                } else {
-                       opengles_image = mnit_opengles_load_image( sdl_image->pixels, sdl_image->w, sdl_image->h, sdl_image->format->Amask );
+                       opengles_image = mnit_opengles_load_image( sdl_image->pixels,
+                               sdl_image->w, sdl_image->h,
+                               sdl_image->w, sdl_image->h, sdl_image->format->Amask );
                        SDL_FreeSurface(sdl_image);
                        return opengles_image;
                }