|
1 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include <dlfcn.h> |
|
7 #include <android/log.h> |
|
8 #include "ANPBase.h" |
|
9 |
|
10 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) |
|
11 #define ASSIGN(obj, name) (obj)->name = anp_surface_##name |
|
12 |
|
13 #define CLEAR_EXCEPTION(env) if (env->ExceptionOccurred()) env->ExceptionClear(); |
|
14 |
|
15 #define ANDROID_REGION_SIZE 512 |
|
16 |
|
17 enum { |
|
18 PIXEL_FORMAT_RGBA_8888 = 1, |
|
19 PIXEL_FORMAT_RGB_565 = 4, |
|
20 }; |
|
21 |
|
22 struct SurfaceInfo { |
|
23 uint32_t w; |
|
24 uint32_t h; |
|
25 uint32_t s; |
|
26 uint32_t usage; |
|
27 uint32_t format; |
|
28 unsigned char* bits; |
|
29 uint32_t reserved[2]; |
|
30 }; |
|
31 |
|
32 typedef struct ARect { |
|
33 int32_t left; |
|
34 int32_t top; |
|
35 int32_t right; |
|
36 int32_t bottom; |
|
37 } ARect; |
|
38 |
|
39 |
|
40 // used to cache JNI method and field IDs for Surface Objects |
|
41 static struct ANPSurfaceInterfaceJavaGlue { |
|
42 bool initialized; |
|
43 jmethodID getSurfaceHolder; |
|
44 jmethodID getSurface; |
|
45 jfieldID surfacePointer; |
|
46 } gSurfaceJavaGlue; |
|
47 |
|
48 static struct ANPSurfaceFunctions { |
|
49 bool initialized; |
|
50 |
|
51 int (* lock)(void*, SurfaceInfo*, void*); |
|
52 int (* unlockAndPost)(void*); |
|
53 |
|
54 void* (* regionConstructor)(void*); |
|
55 void (* setRegion)(void*, ARect const&); |
|
56 } gSurfaceFunctions; |
|
57 |
|
58 |
|
59 static inline void* getSurface(JNIEnv* env, jobject view) { |
|
60 if (!env || !view) { |
|
61 return nullptr; |
|
62 } |
|
63 |
|
64 if (!gSurfaceJavaGlue.initialized) { |
|
65 |
|
66 jclass surfaceViewClass = env->FindClass("android/view/SurfaceView"); |
|
67 gSurfaceJavaGlue.getSurfaceHolder = env->GetMethodID(surfaceViewClass, "getHolder", "()Landroid/view/SurfaceHolder;"); |
|
68 |
|
69 jclass surfaceHolderClass = env->FindClass("android/view/SurfaceHolder"); |
|
70 gSurfaceJavaGlue.getSurface = env->GetMethodID(surfaceHolderClass, "getSurface", "()Landroid/view/Surface;"); |
|
71 |
|
72 jclass surfaceClass = env->FindClass("android/view/Surface"); |
|
73 gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, |
|
74 "mSurfacePointer", "I"); |
|
75 |
|
76 if (!gSurfaceJavaGlue.surfacePointer) { |
|
77 CLEAR_EXCEPTION(env); |
|
78 |
|
79 // It was something else in 2.2. |
|
80 gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, |
|
81 "mSurface", "I"); |
|
82 |
|
83 if (!gSurfaceJavaGlue.surfacePointer) { |
|
84 CLEAR_EXCEPTION(env); |
|
85 |
|
86 // And something else in 2.3+ |
|
87 gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass, |
|
88 "mNativeSurface", "I"); |
|
89 |
|
90 CLEAR_EXCEPTION(env); |
|
91 } |
|
92 } |
|
93 |
|
94 if (!gSurfaceJavaGlue.surfacePointer) { |
|
95 LOG("Failed to acquire surface pointer"); |
|
96 return nullptr; |
|
97 } |
|
98 |
|
99 env->DeleteLocalRef(surfaceClass); |
|
100 env->DeleteLocalRef(surfaceViewClass); |
|
101 env->DeleteLocalRef(surfaceHolderClass); |
|
102 |
|
103 gSurfaceJavaGlue.initialized = (gSurfaceJavaGlue.surfacePointer != nullptr); |
|
104 } |
|
105 |
|
106 jobject holder = env->CallObjectMethod(view, gSurfaceJavaGlue.getSurfaceHolder); |
|
107 jobject surface = env->CallObjectMethod(holder, gSurfaceJavaGlue.getSurface); |
|
108 jint surfacePointer = env->GetIntField(surface, gSurfaceJavaGlue.surfacePointer); |
|
109 |
|
110 env->DeleteLocalRef(holder); |
|
111 env->DeleteLocalRef(surface); |
|
112 |
|
113 return (void*)surfacePointer; |
|
114 } |
|
115 |
|
116 static ANPBitmapFormat convertPixelFormat(int32_t format) { |
|
117 switch (format) { |
|
118 case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_ANPBitmapFormat; |
|
119 case PIXEL_FORMAT_RGB_565: return kRGB_565_ANPBitmapFormat; |
|
120 default: return kUnknown_ANPBitmapFormat; |
|
121 } |
|
122 } |
|
123 |
|
124 static int bytesPerPixel(int32_t format) { |
|
125 switch (format) { |
|
126 case PIXEL_FORMAT_RGBA_8888: return 4; |
|
127 case PIXEL_FORMAT_RGB_565: return 2; |
|
128 default: return -1; |
|
129 } |
|
130 } |
|
131 |
|
132 static bool init() { |
|
133 if (gSurfaceFunctions.initialized) |
|
134 return true; |
|
135 |
|
136 void* handle = dlopen("libsurfaceflinger_client.so", RTLD_LAZY); |
|
137 |
|
138 if (!handle) { |
|
139 LOG("Failed to open libsurfaceflinger_client.so"); |
|
140 return false; |
|
141 } |
|
142 |
|
143 gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb"); |
|
144 gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); |
|
145 |
|
146 |
|
147 if (!gSurfaceFunctions.lock) { |
|
148 // Stuff changed in 3.0/4.0 |
|
149 handle = dlopen("libgui.so", RTLD_LAZY); |
|
150 gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionE"); |
|
151 gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv"); |
|
152 } |
|
153 |
|
154 handle = dlopen("libui.so", RTLD_LAZY); |
|
155 if (!handle) { |
|
156 LOG("Failed to open libui.so"); |
|
157 return false; |
|
158 } |
|
159 |
|
160 gSurfaceFunctions.regionConstructor = (void* (*)(void*))dlsym(handle, "_ZN7android6RegionC1Ev"); |
|
161 gSurfaceFunctions.setRegion = (void (*)(void*, ARect const&))dlsym(handle, "_ZN7android6Region3setERKNS_4RectE"); |
|
162 |
|
163 gSurfaceFunctions.initialized = (gSurfaceFunctions.lock && gSurfaceFunctions.unlockAndPost && |
|
164 gSurfaceFunctions.regionConstructor && gSurfaceFunctions.setRegion); |
|
165 LOG("Initialized? %d\n", gSurfaceFunctions.initialized); |
|
166 return gSurfaceFunctions.initialized; |
|
167 } |
|
168 |
|
169 // FIXME: All of this should be changed to use the equivalent things in AndroidBridge, bug 758612 |
|
170 static bool anp_surface_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) { |
|
171 if (!bitmap || !surfaceView) { |
|
172 return false; |
|
173 } |
|
174 |
|
175 void* surface = getSurface(env, surfaceView); |
|
176 |
|
177 if (!bitmap || !surface) { |
|
178 return false; |
|
179 } |
|
180 |
|
181 if (!init()) { |
|
182 return false; |
|
183 } |
|
184 |
|
185 void* region = nullptr; |
|
186 if (dirtyRect) { |
|
187 region = malloc(ANDROID_REGION_SIZE); |
|
188 gSurfaceFunctions.regionConstructor(region); |
|
189 |
|
190 ARect rect; |
|
191 rect.left = dirtyRect->left; |
|
192 rect.top = dirtyRect->top; |
|
193 rect.right = dirtyRect->right; |
|
194 rect.bottom = dirtyRect->bottom; |
|
195 |
|
196 gSurfaceFunctions.setRegion(region, rect); |
|
197 } |
|
198 |
|
199 SurfaceInfo info; |
|
200 int err = gSurfaceFunctions.lock(surface, &info, region); |
|
201 if (err < 0) { |
|
202 LOG("Failed to lock surface"); |
|
203 return false; |
|
204 } |
|
205 |
|
206 // the surface may have expanded the dirty region so we must to pass that |
|
207 // information back to the plugin. |
|
208 if (dirtyRect) { |
|
209 ARect* dirtyBounds = (ARect*)region; // The bounds are the first member, so this should work! |
|
210 |
|
211 dirtyRect->left = dirtyBounds->left; |
|
212 dirtyRect->right = dirtyBounds->right; |
|
213 dirtyRect->top = dirtyBounds->top; |
|
214 dirtyRect->bottom = dirtyBounds->bottom; |
|
215 } |
|
216 |
|
217 if (region) |
|
218 free(region); |
|
219 |
|
220 int bpr = info.s * bytesPerPixel(info.format); |
|
221 |
|
222 bitmap->format = convertPixelFormat(info.format); |
|
223 bitmap->width = info.w; |
|
224 bitmap->height = info.h; |
|
225 bitmap->rowBytes = bpr; |
|
226 |
|
227 if (info.w > 0 && info.h > 0) { |
|
228 bitmap->baseAddr = info.bits; |
|
229 } else { |
|
230 bitmap->baseAddr = nullptr; |
|
231 return false; |
|
232 } |
|
233 |
|
234 return true; |
|
235 } |
|
236 |
|
237 static void anp_surface_unlock(JNIEnv* env, jobject surfaceView) { |
|
238 if (!surfaceView) { |
|
239 return; |
|
240 } |
|
241 |
|
242 if (!init()) { |
|
243 return; |
|
244 } |
|
245 |
|
246 void* surface = getSurface(env, surfaceView); |
|
247 |
|
248 if (!surface) { |
|
249 return; |
|
250 } |
|
251 |
|
252 gSurfaceFunctions.unlockAndPost(surface); |
|
253 } |
|
254 |
|
255 /////////////////////////////////////////////////////////////////////////////// |
|
256 |
|
257 void InitSurfaceInterface(ANPSurfaceInterfaceV0* i) { |
|
258 ASSIGN(i, lock); |
|
259 ASSIGN(i, unlock); |
|
260 |
|
261 // setup the java glue struct |
|
262 gSurfaceJavaGlue.initialized = false; |
|
263 |
|
264 // setup the function struct |
|
265 gSurfaceFunctions.initialized = false; |
|
266 } |