lib/mnit: expand images to the nearest power of 2 for OpenGL ES 1.0
[nit.git] / lib / mnit_android / android_assets.nit
index 27911f3..d1ee104 100644 (file)
@@ -21,7 +21,7 @@
 # * The Android ndk
 # * zlib (which is included in the Android ndk)
 # * libpng which must be provided by the Nit compilation framework
-module android_assets
+module android_assets is ldflags "-lz"
 
 import mnit
 import android_app
@@ -32,8 +32,6 @@ in "C header" `{
 `}
 
 in "C" `{
-       extern struct android_app *mnit_java_app;
-
        void mnit_android_png_read_data(png_structp png_ptr,
                        png_bytep data, png_size_t length)
        {
@@ -52,9 +50,9 @@ in "C" `{
        }
 `}
 
-extern AndroidAsset in "C" `{struct AAsset*`}
+extern class AndroidAsset in "C" `{struct AAsset*`}
 
-       fun read(count: Int): nullable String is extern import String as nullable, NativeString.to_s `{
+       fun read(count: Int): nullable String is extern import String.as nullable, NativeString.to_s `{
                char *buffer = malloc(sizeof(char) * (count+1));
                int read = AAsset_read(recv, buffer, count);
                if (read != count)
@@ -88,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
@@ -103,8 +101,9 @@ redef class App
                return null
        end
 
-       protected fun load_asset_from_apk(path: String): nullable AndroidAsset is extern import String.to_cstring, AndroidAsset as nullable `{
-               struct AAsset* a = AAssetManager_open(mnit_java_app->activity->assetManager, String_to_cstring(path), AASSET_MODE_BUFFER);
+       protected fun load_asset_from_apk(path: String): nullable AndroidAsset is extern import String.to_cstring, AndroidAsset.as nullable, native_app_glue  `{
+               struct android_app *native_app_glue = App_native_app_glue(recv);
+               struct AAsset* a = AAssetManager_open(native_app_glue->activity->assetManager, String_to_cstring(path), AASSET_MODE_BUFFER);
                if (a == NULL)
                {
                        LOGW("nit d g a");
@@ -119,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;
@@ -130,17 +129,20 @@ redef class Opengles1Image
                int has_alpha;
 
                unsigned int row_bytes;
-               png_bytepp row_pointers;
-               unsigned char *pixels;
+               png_bytepp row_pointers = NULL;
+               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");
@@ -167,32 +169,38 @@ redef class Opengles1Image
 
                png_get_IHDR(   png_ptr, info_ptr, &width, &height,
                                                &depth, &color_type, NULL, NULL, NULL);
-               if (color_type == PNG_COLOR_TYPE_RGBA)
-                       has_alpha = 1;
-               else if (color_type == PNG_COLOR_TYPE_RGB)
-                       has_alpha = 0;
-               else {
-                       LOGW("unknown color_type");
-                       goto close_png_ptr;
+               has_alpha = color_type & PNG_COLOR_MASK_ALPHA;
+
+               // If we get gray and alpha only, standardize the format of the pixels.
+               // GA is not supported by OpenGL ES 1.
+               if (!(color_type & PNG_COLOR_MASK_COLOR)) {
+                       png_set_gray_to_rgb(png_ptr);
+                       png_set_palette_to_rgb(png_ptr);
+                       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);
+               for (i=0; i<height; i++)
+                       row_pointers[i] = (png_byte*) malloc(row_bytes);
 
                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, has_alpha);
                LOGW("OK");
+               recv = mnit_opengles_load_image((const uint_least32_t *)pixels,
+                       width, height, width_pow2, height_pow2, has_alpha);
 
        close_png_ptr:
                if (info_ptr != NULL)
@@ -200,7 +208,26 @@ redef class Opengles1Image
                else
                        png_destroy_read_struct(&png_ptr, NULL, NULL);
 
+               if (pixels != NULL)
+                       free(pixels);
+
+               if (row_pointers != NULL) {
+                       for (i=0; i<height; i++)
+                               free(row_pointers[i]);
+                       free(row_pointers);
+               }
+
        close_stream:
                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