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 */
37static 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
52typedef 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
75typedef 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
99static Fixed  angle_sin_tab[ANGLE_2PI+1];
100
101static 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
110static __inline__ Fixed angle_sin( Angle  a )
111{
112    return angle_sin_tab[(uint32_t)a & (ANGLE_2PI-1)];
113}
114
115static __inline__ Fixed angle_cos( Angle  a )
116{
117    return angle_sin(a + ANGLE_PI2);
118}
119
120static __inline__ Fixed fixed_sin( Fixed  f )
121{
122    return angle_sin(ANGLE_FROM_FIXED(f));
123}
124
125static __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
138static uint16_t  palette[PALETTE_SIZE];
139
140static 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
147static 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
172static __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
182static void init_tables(void)
183{
184    init_palette();
185    init_angles();
186}
187
188static 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 */
270typedef struct {
271    double  renderTime;
272    double  frameTime;
273} FrameStats;
274
275#define  MAX_FRAME_STATS  200
276#define  MAX_PERIOD_MS    1500
277
278typedef 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
288static void
289stats_init( Stats*  s )
290{
291    s->lastTime = now_ms();
292    s->firstTime = 0.;
293    s->firstFrame = 0;
294    s->numFrames  = 0;
295}
296
297static void
298stats_startFrame( Stats*  s )
299{
300    s->frameTime = now_ms();
301}
302
303static void
304stats_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
364JNIEXPORT 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