lib: intro the android implementation of mnit
[nit.git] / lib / mnit_android / android_assets.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Implements the `mnit::assets` services with a wraper around the filesystem
18 # API provided by the Android ndk.
19 #
20 # This module relies heavily on 3 C libraries:
21 # * The Android ndk
22 # * zlib (which is included in the Android ndk)
23 # * libpng which must be provided by the Nit compilation framework
24 module android_assets
25
26 import mnit
27 import android_app
28
29 in "C header" `{
30 #include <png.h>
31 #include <zlib.h>
32 `}
33
34 in "C" `{
35 extern struct android_app *mnit_java_app;
36
37 void mnit_android_png_read_data(png_structp png_ptr,
38 png_bytep data, png_size_t length)
39 {
40 struct AAsset *recv = png_get_io_ptr(png_ptr);
41 int read = AAsset_read(recv, data, length);
42 }
43 void mnit_android_png_error_fn(png_structp png_ptr,
44 png_const_charp error_msg)
45 {
46 LOGW("libpng error: %s", error_msg);
47 }
48 void mnit_android_png_warning_fn(png_structp png_ptr,
49 png_const_charp warning_msg)
50 {
51 LOGW("libpng warning: %s", warning_msg);
52 }
53 `}
54
55 extern AndroidAsset in "C" `{struct AAsset*`}
56
57 fun read(count: Int): nullable String is extern import String as nullable, NativeString.to_s `{
58 char *buffer = malloc(sizeof(char) * (count+1));
59 int read = AAsset_read(recv, buffer, count);
60 if (read != count)
61 return null_String();
62 else
63 {
64 buffer[count] = '\0';
65 return String_as_nullable(NativeString_to_s(buffer));
66 }
67 `}
68
69 fun length: Int is extern `{
70 return AAsset_getLength(recv);
71 `}
72
73 fun to_fd: Int is extern `{
74 off_t start;
75 off_t length;
76 int fd = AAsset_openFileDescriptor(recv, &start, &length);
77 return fd;
78 `}
79
80 fun close is extern `{
81 AAsset_close(recv);
82 `}
83 end
84
85 redef class App
86 redef fun try_loading_asset(path)
87 do
88 var a = load_asset_from_apk(path)
89 if a != null then
90 if path.file_extension == "png" then
91 var png = new Opengles1Image.from_android_asset(a)
92 a.close
93 return png
94 else if path.file_extension == "txt" then
95 var len = a.length
96 var txt = a.read(len)
97 return txt
98 end
99 else
100 print "didn't get asset {path}"
101 end
102
103 return null
104 end
105
106 protected fun load_asset_from_apk(path: String): nullable AndroidAsset is extern import String.to_cstring, AndroidAsset as nullable `{
107 struct AAsset* a = AAssetManager_open(mnit_java_app->activity->assetManager, String_to_cstring(path), AASSET_MODE_BUFFER);
108 if (a == NULL)
109 {
110 LOGW("nit d g a");
111 return null_AndroidAsset();
112 }
113 else
114 {
115 return AndroidAsset_as_nullable(a);
116 }
117 `}
118 end
119
120 redef class Opengles1Image
121 # Read a png from a zipped stream
122 new from_android_asset(asset: AndroidAsset) is extern `{
123 struct mnit_opengles_Texture *recv = NULL;
124
125 png_structp png_ptr = NULL;
126 png_infop info_ptr = NULL;
127
128 png_uint_32 width, height;
129 int depth, color_type;
130 int has_alpha;
131
132 unsigned int row_bytes;
133 png_bytepp row_pointers;
134 unsigned char *pixels;
135 unsigned int i;
136
137 unsigned char sig[8];
138 int sig_read = AAsset_read(asset, sig, 8);
139 if (png_sig_cmp(sig, 0, sig_read)) {
140 LOGW("invalide png signature");
141 return NULL;
142 }
143
144 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
145 if (png_ptr == NULL) {
146 LOGW("png_create_read_struct failed");
147 goto close_stream;
148 }
149 png_set_error_fn(png_ptr, NULL, mnit_android_png_error_fn, mnit_android_png_warning_fn);
150
151 info_ptr = png_create_info_struct(png_ptr);
152 if (info_ptr == NULL) {
153 LOGW("png_create_info_struct failed");
154 goto close_png_ptr;
155 }
156
157 if (setjmp(png_jmpbuf(png_ptr))) {
158 LOGW("reading png file failed");
159 goto close_png_ptr;
160 }
161
162 png_set_read_fn(png_ptr, (void*)asset, mnit_android_png_read_data);
163
164 png_set_sig_bytes(png_ptr, sig_read);
165
166 png_read_info(png_ptr, info_ptr);
167
168 png_get_IHDR( png_ptr, info_ptr, &width, &height,
169 &depth, &color_type, NULL, NULL, NULL);
170 if (color_type == PNG_COLOR_TYPE_RGBA)
171 has_alpha = 1;
172 else if (color_type == PNG_COLOR_TYPE_RGB)
173 has_alpha = 0;
174 else {
175 LOGW("unknown color_type");
176 goto close_png_ptr;
177 }
178
179 LOGW("w: %i, h: %i", width, height);
180
181 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
182 pixels = malloc(row_bytes * height);
183 row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
184
185 for (i=0; i<height; i++)
186 row_pointers[i] = (png_byte*) malloc(row_bytes);
187
188 png_read_image(png_ptr, row_pointers);
189
190 for (i = 0; i < height; i++)
191 memcpy(pixels + (row_bytes*i),
192 row_pointers[i], row_bytes);
193
194 recv = mnit_opengles_load_image((const uint_least32_t *)pixels, width, height, has_alpha);
195 LOGW("OK");
196
197 close_png_ptr:
198 if (info_ptr != NULL)
199 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
200 else
201 png_destroy_read_struct(&png_ptr, NULL, NULL);
202
203 close_stream:
204 return recv;
205 `}
206 end