d1ee104bca7127fa2f03c96616a4b7e74101df92
[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 is ldflags "-lz"
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 void mnit_android_png_read_data(png_structp png_ptr,
36 png_bytep data, png_size_t length)
37 {
38 struct AAsset *recv = png_get_io_ptr(png_ptr);
39 int read = AAsset_read(recv, data, length);
40 }
41 void mnit_android_png_error_fn(png_structp png_ptr,
42 png_const_charp error_msg)
43 {
44 LOGW("libpng error: %s", error_msg);
45 }
46 void mnit_android_png_warning_fn(png_structp png_ptr,
47 png_const_charp warning_msg)
48 {
49 LOGW("libpng warning: %s", warning_msg);
50 }
51 `}
52
53 extern class AndroidAsset in "C" `{struct AAsset*`}
54
55 fun read(count: Int): nullable String is extern import String.as nullable, NativeString.to_s `{
56 char *buffer = malloc(sizeof(char) * (count+1));
57 int read = AAsset_read(recv, buffer, count);
58 if (read != count)
59 return null_String();
60 else
61 {
62 buffer[count] = '\0';
63 return String_as_nullable(NativeString_to_s(buffer));
64 }
65 `}
66
67 fun length: Int is extern `{
68 return AAsset_getLength(recv);
69 `}
70
71 fun to_fd: Int is extern `{
72 off_t start;
73 off_t length;
74 int fd = AAsset_openFileDescriptor(recv, &start, &length);
75 return fd;
76 `}
77
78 fun close is extern `{
79 AAsset_close(recv);
80 `}
81 end
82
83 redef class App
84 redef fun try_loading_asset(path)
85 do
86 var a = load_asset_from_apk(path)
87 if a != null then
88 if path.file_extension == "png" then
89 var png = new Opengles1Image.from_android_asset(a)
90 a.close
91 return png
92 else if path.file_extension == "txt" then
93 var len = a.length
94 var txt = a.read(len)
95 return txt
96 end
97 else
98 print "didn't get asset {path}"
99 end
100
101 return null
102 end
103
104 protected fun load_asset_from_apk(path: String): nullable AndroidAsset is extern import String.to_cstring, AndroidAsset.as nullable, native_app_glue `{
105 struct android_app *native_app_glue = App_native_app_glue(recv);
106 struct AAsset* a = AAssetManager_open(native_app_glue->activity->assetManager, String_to_cstring(path), AASSET_MODE_BUFFER);
107 if (a == NULL)
108 {
109 LOGW("nit d g a");
110 return null_AndroidAsset();
111 }
112 else
113 {
114 return AndroidAsset_as_nullable(a);
115 }
116 `}
117 end
118
119 redef class Opengles1Image
120 # Read a png from a zipped stream
121 new from_android_asset(asset: AndroidAsset) import Int.next_pow `{
122 struct mnit_opengles_Texture *recv = NULL;
123
124 png_structp png_ptr = NULL;
125 png_infop info_ptr = NULL;
126
127 png_uint_32 width, height;
128 int depth, color_type;
129 int has_alpha;
130
131 unsigned int row_bytes;
132 png_bytepp row_pointers = NULL;
133 unsigned char *pixels = NULL;
134 unsigned int i;
135
136 png_uint_32 width_pow2, height_pow2;
137 unsigned int row_bytes_pow2;
138
139 unsigned char sig[8];
140 int sig_read = AAsset_read(asset, sig, 8);
141 if (png_sig_cmp(sig, 0, sig_read)) {
142 LOGW("invalide png signature");
143 return NULL;
144 }
145
146 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
147 if (png_ptr == NULL) {
148 LOGW("png_create_read_struct failed");
149 goto close_stream;
150 }
151 png_set_error_fn(png_ptr, NULL, mnit_android_png_error_fn, mnit_android_png_warning_fn);
152
153 info_ptr = png_create_info_struct(png_ptr);
154 if (info_ptr == NULL) {
155 LOGW("png_create_info_struct failed");
156 goto close_png_ptr;
157 }
158
159 if (setjmp(png_jmpbuf(png_ptr))) {
160 LOGW("reading png file failed");
161 goto close_png_ptr;
162 }
163
164 png_set_read_fn(png_ptr, (void*)asset, mnit_android_png_read_data);
165
166 png_set_sig_bytes(png_ptr, sig_read);
167
168 png_read_info(png_ptr, info_ptr);
169
170 png_get_IHDR( png_ptr, info_ptr, &width, &height,
171 &depth, &color_type, NULL, NULL, NULL);
172 has_alpha = color_type & PNG_COLOR_MASK_ALPHA;
173
174 // If we get gray and alpha only, standardize the format of the pixels.
175 // GA is not supported by OpenGL ES 1.
176 if (!(color_type & PNG_COLOR_MASK_COLOR)) {
177 png_set_gray_to_rgb(png_ptr);
178 png_set_palette_to_rgb(png_ptr);
179 png_read_update_info(png_ptr, info_ptr);
180 }
181
182 width_pow2 = Int_next_pow(width, 2);
183 height_pow2 = Int_next_pow(height, 2);
184
185 LOGW("Loading image of w: %i, h: %i, w2: %d, h2: %d",
186 width, height, width_pow2, height_pow2);
187
188 row_bytes = png_get_rowbytes(png_ptr, info_ptr);
189 row_bytes_pow2 = row_bytes * width_pow2 / width;
190 pixels = malloc(row_bytes_pow2 * height_pow2);
191 row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height_pow2);
192
193 for (i=0; i<height; i++)
194 row_pointers[i] = (png_byte*) malloc(row_bytes);
195
196 png_read_image(png_ptr, row_pointers);
197
198 for (i = 0; i < height; i++)
199 memcpy(pixels + (row_bytes_pow2*i), row_pointers[i], row_bytes);
200
201 LOGW("OK");
202 recv = mnit_opengles_load_image((const uint_least32_t *)pixels,
203 width, height, width_pow2, height_pow2, has_alpha);
204
205 close_png_ptr:
206 if (info_ptr != NULL)
207 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
208 else
209 png_destroy_read_struct(&png_ptr, NULL, NULL);
210
211 if (pixels != NULL)
212 free(pixels);
213
214 if (row_pointers != NULL) {
215 for (i=0; i<height; i++)
216 free(row_pointers[i]);
217 free(row_pointers);
218 }
219
220 close_stream:
221 return recv;
222 `}
223 end
224
225 redef universal Int
226 # The first power of `exp` greater or equal to `self`
227 private fun next_pow(exp: Int): Int
228 do
229 var p = 0
230 while p < self do p = p*exp
231 return p
232 end
233 end