1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <jni.h> 18 #include <time.h> 19 #include <android/log.h> 20 #include <android/bitmap.h> 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <math.h> 25 26 #define LOG_TAG "libplasma" 27 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 28 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 29 30 /* Set to 1 to enable debug log traces. */ 31 #define DEBUG 0 32 33 /* Set to 1 to optimize memory stores when generating plasma. */ 34 #define OPTIMIZE_WRITES 1 35 36 /* Return current time in milliseconds */ 37 static double now_ms(void) 38 { 39 struct timeval tv; 40 gettimeofday(&tv, NULL); 41 return tv.tv_sec*1000. + tv.tv_usec/1000.; 42 } 43 44 /* We're going to perform computations for every pixel of the target 45 * bitmap. floating-point operations are very slow on ARMv5, and not 46 * too bad on ARMv7 with the exception of trigonometric functions. 47 * 48 * For better performance on all platforms, we're going to use fixed-point 49 * arithmetic and all kinds of tricks 50 */ 51 52 typedef int32_t Fixed; 53 54 #define FIXED_BITS 16 55 #define FIXED_ONE (1 << FIXED_BITS) 56 #define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1) 57 58 #define FIXED_FROM_INT(x) ((x) << FIXED_BITS) 59 #define FIXED_TO_INT(x) ((x) >> FIXED_BITS) 60 61 #define FIXED_FROM_FLOAT(x) ((Fixed)((x)*FIXED_ONE)) 62 #define FIXED_TO_FLOAT(x) ((x)/(1.*FIXED_ONE)) 63 64 #define FIXED_MUL(x,y) (((int64_t)(x) * (y)) >> FIXED_BITS) 65 #define FIXED_DIV(x,y) (((int64_t)(x) * FIXED_ONE) / (y)) 66 67 #define FIXED_DIV2(x) ((x) >> 1) 68 #define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1) 69 70 #define FIXED_FRAC(x) ((x) & ((1 << FIXED_BITS)-1)) 71 #define FIXED_TRUNC(x) ((x) & ~((1 << FIXED_BITS)-1)) 72 73 #define FIXED_FROM_INT_FLOAT(x,f) (Fixed)((x)*(FIXED_ONE*(f))) 74 75 typedef int32_t Angle; 76 77 #define ANGLE_BITS 9 78 79 #if ANGLE_BITS < 8 80 # error ANGLE_BITS must be at least 8 81 #endif 82 83 #define ANGLE_2PI (1 << ANGLE_BITS) 84 #define ANGLE_PI (1 << (ANGLE_BITS-1)) 85 #define ANGLE_PI2 (1 << (ANGLE_BITS-2)) 86 #define ANGLE_PI4 (1 << (ANGLE_BITS-3)) 87 88 #define ANGLE_FROM_FLOAT(x) (Angle)((x)*ANGLE_PI/M_PI) 89 #define ANGLE_TO_FLOAT(x) ((x)*M_PI/ANGLE_PI) 90 91 #if ANGLE_BITS <= FIXED_BITS 92 # define ANGLE_FROM_FIXED(x) (Angle)((x) >> (FIXED_BITS - ANGLE_BITS)) 93 # define ANGLE_TO_FIXED(x) (Fixed)((x) << (FIXED_BITS - ANGLE_BITS)) 94 #else 95 # define ANGLE_FROM_FIXED(x) (Angle)((x) << (ANGLE_BITS - FIXED_BITS)) 96 # define ANGLE_TO_FIXED(x) (Fixed)((x) >> (ANGLE_BITS - FIXED_BITS)) 97 #endif 98 99 static Fixed angle_sin_tab[ANGLE_2PI+1]; 100 101 static void init_angles(void) 102 { 103 int nn; 104 for (nn = 0; nn < ANGLE_2PI+1; nn++) { 105 double radians = nn*M_PI/ANGLE_PI; 106 angle_sin_tab[nn] = FIXED_FROM_FLOAT(sin(radians)); 107 } 108 } 109 110 static __inline__ Fixed angle_sin( Angle a ) 111 { 112 return angle_sin_tab[(uint32_t)a & (ANGLE_2PI-1)]; 113 } 114 115 static __inline__ Fixed angle_cos( Angle a ) 116 { 117 return angle_sin(a + ANGLE_PI2); 118 } 119 120 static __inline__ Fixed fixed_sin( Fixed f ) 121 { 122 return angle_sin(ANGLE_FROM_FIXED(f)); 123 } 124 125 static __inline__ Fixed fixed_cos( Fixed f ) 126 { 127 return angle_cos(ANGLE_FROM_FIXED(f)); 128 } 129 130 /* Color palette used for rendering the plasma */ 131 #define PALETTE_BITS 8 132 #define PALETTE_SIZE (1 << PALETTE_BITS) 133 134 #if PALETTE_BITS > FIXED_BITS 135 # error PALETTE_BITS must be smaller than FIXED_BITS 136 #endif 137 138 static uint16_t palette[PALETTE_SIZE]; 139 140 static uint16_t make565(int red, int green, int blue) 141 { 142 return (uint16_t)( ((red << 8) & 0xf800) | 143 ((green << 2) & 0x03e0) | 144 ((blue >> 3) & 0x001f) ); 145 } 146 147 static void init_palette(void) 148 { 149 int nn, mm = 0; 150 /* fun with colors */ 151 for (nn = 0; nn < PALETTE_SIZE/4; nn++) { 152 int jj = (nn-mm)*4*255/PALETTE_SIZE; 153 palette[nn] = make565(255, jj, 255-jj); 154 } 155 156 for ( mm = nn; nn < PALETTE_SIZE/2; nn++ ) { 157 int jj = (nn-mm)*4*255/PALETTE_SIZE; 158 palette[nn] = make565(255-jj, 255, jj); 159 } 160 161 for ( mm = nn; nn < PALETTE_SIZE*3/4; nn++ ) { 162 int jj = (nn-mm)*4*255/PALETTE_SIZE; 163 palette[nn] = make565(0, 255-jj, 255); 164 } 165 166 for ( mm = nn; nn < PALETTE_SIZE; nn++ ) { 167 int jj = (nn-mm)*4*255/PALETTE_SIZE; 168 palette[nn] = make565(jj, 0, 255); 169 } 170 } 171 172 static __inline__ uint16_t palette_from_fixed( Fixed x ) 173 { 174 if (x < 0) x = -x; 175 if (x >= FIXED_ONE) x = FIXED_ONE-1; 176 int idx = FIXED_FRAC(x) >> (FIXED_BITS - PALETTE_BITS); 177 return palette[idx & (PALETTE_SIZE-1)]; 178 } 179 180 /* Angles expressed as fixed point radians */ 181 182 static void init_tables(void) 183 { 184 init_palette(); 185 init_angles(); 186 } 187 188 static void fill_plasma( AndroidBitmapInfo* info, void* pixels, double t ) 189 { 190 Fixed ft = FIXED_FROM_FLOAT(t/1000.); 191 Fixed yt1 = FIXED_FROM_FLOAT(t/1230.); 192 Fixed yt2 = yt1; 193 Fixed xt10 = FIXED_FROM_FLOAT(t/3000.); 194 Fixed xt20 = xt10; 195 196 #define YT1_INCR FIXED_FROM_FLOAT(1/100.) 197 #define YT2_INCR FIXED_FROM_FLOAT(1/163.) 198 199 int yy; 200 for (yy = 0; yy < info->height; yy++) { 201 uint16_t* line = (uint16_t*)pixels; 202 Fixed base = fixed_sin(yt1) + fixed_sin(yt2); 203 Fixed xt1 = xt10; 204 Fixed xt2 = xt20; 205 206 yt1 += YT1_INCR; 207 yt2 += YT2_INCR; 208 209 #define XT1_INCR FIXED_FROM_FLOAT(1/173.) 210 #define XT2_INCR FIXED_FROM_FLOAT(1/242.) 211 212 #if OPTIMIZE_WRITES 213 /* optimize memory writes by generating one aligned 32-bit store 214 * for every pair of pixels. 215 */ 216 uint16_t* line_end = line + info->width; 217 218 if (line < line_end) { 219 if (((uint32_t)line & 3) != 0) { 220 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 221 222 xt1 += XT1_INCR; 223 xt2 += XT2_INCR; 224 225 line[0] = palette_from_fixed(ii >> 2); 226 line++; 227 } 228 229 while (line + 2 <= line_end) { 230 Fixed i1 = base + fixed_sin(xt1) + fixed_sin(xt2); 231 xt1 += XT1_INCR; 232 xt2 += XT2_INCR; 233 234 Fixed i2 = base + fixed_sin(xt1) + fixed_sin(xt2); 235 xt1 += XT1_INCR; 236 xt2 += XT2_INCR; 237 238 uint32_t pixel = ((uint32_t)palette_from_fixed(i1 >> 2) << 16) | 239 (uint32_t)palette_from_fixed(i2 >> 2); 240 241 ((uint32_t*)line)[0] = pixel; 242 line += 2; 243 } 244 245 if (line < line_end) { 246 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 247 line[0] = palette_from_fixed(ii >> 2); 248 line++; 249 } 250 } 251 #else /* !OPTIMIZE_WRITES */ 252 int xx; 253 for (xx = 0; xx < info->width; xx++) { 254 255 Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2); 256 257 xt1 += XT1_INCR; 258 xt2 += XT2_INCR; 259 260 line[xx] = palette_from_fixed(ii / 4); 261 } 262 #endif /* !OPTIMIZE_WRITES */ 263 264 // go to next line 265 pixels = (char*)pixels + info->stride; 266 } 267 } 268 269 /* simple stats management */ 270 typedef struct { 271 double renderTime; 272 double frameTime; 273 } FrameStats; 274 275 #define MAX_FRAME_STATS 200 276 #define MAX_PERIOD_MS 1500 277 278 typedef struct { 279 double firstTime; 280 double lastTime; 281 double frameTime; 282 283 int firstFrame; 284 int numFrames; 285 FrameStats frames[ MAX_FRAME_STATS ]; 286 } Stats; 287 288 static void 289 stats_init( Stats* s ) 290 { 291 s->lastTime = now_ms(); 292 s->firstTime = 0.; 293 s->firstFrame = 0; 294 s->numFrames = 0; 295 } 296 297 static void 298 stats_startFrame( Stats* s ) 299 { 300 s->frameTime = now_ms(); 301 } 302 303 static void 304 stats_endFrame( Stats* s ) 305 { 306 double now = now_ms(); 307 double renderTime = now - s->frameTime; 308 double frameTime = now - s->lastTime; 309 int nn; 310 311 if (now - s->firstTime >= MAX_PERIOD_MS) { 312 if (s->numFrames > 0) { 313 double minRender, maxRender, avgRender; 314 double minFrame, maxFrame, avgFrame; 315 int count; 316 317 nn = s->firstFrame; 318 minRender = maxRender = avgRender = s->frames[nn].renderTime; 319 minFrame = maxFrame = avgFrame = s->frames[nn].frameTime; 320 for (count = s->numFrames; count > 0; count-- ) { 321 nn += 1; 322 if (nn >= MAX_FRAME_STATS) 323 nn -= MAX_FRAME_STATS; 324 double render = s->frames[nn].renderTime; 325 if (render < minRender) minRender = render; 326 if (render > maxRender) maxRender = render; 327 double frame = s->frames[nn].frameTime; 328 if (frame < minFrame) minFrame = frame; 329 if (frame > maxFrame) maxFrame = frame; 330 avgRender += render; 331 avgFrame += frame; 332 } 333 avgRender /= s->numFrames; 334 avgFrame /= s->numFrames; 335 336 LOGI("frame/s (avg,min,max) = (%.1f,%.1f,%.1f) " 337 "render time ms (avg,min,max) = (%.1f,%.1f,%.1f)\n", 338 1000./avgFrame, 1000./maxFrame, 1000./minFrame, 339 avgRender, minRender, maxRender); 340 } 341 s->numFrames = 0; 342 s->firstFrame = 0; 343 s->firstTime = now; 344 } 345 346 nn = s->firstFrame + s->numFrames; 347 if (nn >= MAX_FRAME_STATS) 348 nn -= MAX_FRAME_STATS; 349 350 s->frames[nn].renderTime = renderTime; 351 s->frames[nn].frameTime = frameTime; 352 353 if (s->numFrames < MAX_FRAME_STATS) { 354 s->numFrames += 1; 355 } else { 356 s->firstFrame += 1; 357 if (s->firstFrame >= MAX_FRAME_STATS) 358 s->firstFrame -= MAX_FRAME_STATS; 359 } 360 361 s->lastTime = now; 362 } 363 364 JNIEXPORT void JNICALL Java_com_example_plasma_PlasmaView_renderPlasma(JNIEnv * env, jobject obj, jobject bitmap, jlong time_ms) 365 { 366 AndroidBitmapInfo info; 367 void* pixels; 368 int ret; 369 static Stats stats; 370 static int init; 371 372 if (!init) { 373 init_tables(); 374 stats_init(&stats); 375 init = 1; 376 } 377 378 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { 379 LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); 380 return; 381 } 382 383 if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) { 384 LOGE("Bitmap format is not RGB_565 !"); 385 return; 386 } 387 388 if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { 389 LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); 390 } 391 392 stats_startFrame(&stats); 393 394 /* Now fill the values with a nice little plasma */ 395 fill_plasma(&info, pixels, time_ms ); 396 397 AndroidBitmap_unlockPixels(env, bitmap); 398 399 stats_endFrame(&stats); 400 } 401