source: trunk/liveimg.c @ 217

Revision 217, 15.3 KB checked in by reyalp, 16 months ago (diff)

comments, enable optimization for non-debug

  • Property svn:eol-style set to native
Line 
1/*
2 *
3 * Copyright (C) 2010-2012 <reyalp (at) gmail dot com>
4 *
5 *  This program is free software; you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation; either version 2 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program; if not, write to the Free Software
17 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19/*
20 * functions for handling remote camera display
21 *
22 */
23
24#if !defined(CHDKPTP_LIVEVIEW)
25#error "live view support not enabled"
26#endif
27
28#include <stdint.h>
29#include <string.h>
30#include <stdlib.h>
31#include <lua.h>
32#include <lualib.h>
33#include <lauxlib.h>
34#if defined(CHDKPTP_CD)
35#include <cd.h>
36#include <cdlua.h>
37#endif
38#include "core/live_view.h"
39#include "lbuf.h"
40#include "liveimg.h"
41/*
42planar img
43TODO would make sense to use a CD bitmap for this but not public
44also may want image handling without CD, but probably want packed rather than planar
45
46TODO if we do packed, might want to make planar and packed use same struct with type flag
47*/
48typedef struct {
49        unsigned width;
50        unsigned height;
51        uint8_t *data;
52        uint8_t *r;
53        uint8_t *g;
54        uint8_t *b;
55        uint8_t *a;
56} liveimg_pimg_t;
57
58typedef struct {
59        uint8_t r;
60        uint8_t g;
61        uint8_t b;
62        uint8_t a;
63} palette_entry_rgba_t;
64
65typedef struct {
66        uint8_t a;
67        uint8_t y;
68        int8_t u;
69        int8_t v;
70} palette_entry_ayuv_t;
71
72typedef struct {
73        int8_t v;
74        int8_t u;
75        uint8_t y;
76        uint8_t a;
77} palette_entry_vuya_t;
78
79typedef void (*yuv_palette_to_rgba_fn)(const char *pal_yuv, uint8_t pixel,palette_entry_rgba_t *pal_rgb);
80
81void palette_type1_to_rgba(const char *palette, uint8_t pixel, palette_entry_rgba_t *pal_rgb);
82void palette_type2_to_rgba(const char *palette, uint8_t pixel, palette_entry_rgba_t *pal_rgb);
83void palette_type3_to_rgba(const char *palette, uint8_t pixel, palette_entry_rgba_t *pal_rgb);
84
85void yuv_live_to_cd_rgb(const char *p_yuv,
86                                                unsigned buf_width, unsigned buf_height,
87                                                unsigned x_offset, unsigned y_offset,
88                                                unsigned width,unsigned height,
89                                                int skip,
90                                                uint8_t *r,uint8_t *g,uint8_t *b);
91
92// from a540, playback mode
93static const char palette_type1_default[]={
940x00, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, 0xff, 0x60, 0xee, 0x62, 0xff, 0xb9, 0x00, 0x00,
950x7f, 0x00, 0x00, 0x00, 0xff, 0x7e, 0xa1, 0xb3, 0xff, 0xcc, 0xb8, 0x5e, 0xff, 0x5f, 0x00, 0x00,
960xff, 0x94, 0xc5, 0x5d, 0xff, 0x8a, 0x50, 0xb0, 0xff, 0x4b, 0x3d, 0xd4, 0x7f, 0x28, 0x00, 0x00,
970x7f, 0x00, 0x7b, 0xe2, 0xff, 0x30, 0x00, 0x00, 0xff, 0x69, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
98};
99
100typedef struct {
101        yuv_palette_to_rgba_fn to_rgba;
102} palette_convert_t;
103
104// type implied from index
105// TODO only one function for now
106palette_convert_t palette_funcs[] = {
107        {NULL},                                         // type 0 - no palette, we could have a default func here
108        {palette_type1_to_rgba},        // type 1 - ayuv, 16 entries double 4 bit index
109        {palette_type2_to_rgba},        // type 2 - like type 1, but with 2 bit alpha lookup - UNTESTED
110        {palette_type3_to_rgba},        // type 3 - vuya, 256 entries, 2 bit alpha lookup
111};
112
113#define N_PALETTE_FUNCS (sizeof(palette_funcs)/sizeof(palette_funcs[0]))
114
115static palette_convert_t* get_palette_convert(unsigned type) {
116        if(type<N_PALETTE_FUNCS) {
117                return &(palette_funcs[type]);
118        }
119        return NULL;
120}
121
122static uint8_t clip_yuv(int v) {
123        if (v<0) return 0;
124        if (v>255) return 255;
125        return v;
126}
127
128static uint8_t yuv_to_r(uint8_t y, int8_t v) {
129        return clip_yuv(((y<<12) +          v*5743 + 2048)>>12);
130}
131
132static uint8_t yuv_to_g(uint8_t y, int8_t u, int8_t v) {
133        return clip_yuv(((y<<12) - u*1411 - v*2925 + 2048)>>12);
134}
135
136static uint8_t yuv_to_b(uint8_t y, int8_t u) {
137        return clip_yuv(((y<<12) + u*7258          + 2048)>>12);
138}
139
140static uint8_t clamp_uint8(unsigned v) {
141        return (v>255)?255:v;
142}
143
144static int8_t clamp_int8(int v) {
145        if(v>127) {
146                return 127;
147        }
148        if(v<-128) {
149                return -128;
150        }
151        return v;
152}
153
154void palette_type1_to_rgba(const char *palette, uint8_t pixel,palette_entry_rgba_t *pal_rgb) {
155        const palette_entry_ayuv_t *pal = (const palette_entry_ayuv_t *)palette;
156        unsigned i1 = pixel & 0xF;
157        unsigned i2 = (pixel & 0xF0)>>4;
158        int8_t u,v;
159        uint8_t y;
160        pal_rgb->a = (pal[i1].a + pal[i2].a)>>1;
161        // TODO not clear if these should be /2 or not
162        y = clamp_uint8(pal[i1].y + pal[i2].y);
163        u = clamp_int8(pal[i1].u + pal[i2].u);
164        v = clamp_int8(pal[i1].v + pal[i2].v);
165        pal_rgb->r = yuv_to_r(y,v);
166        pal_rgb->g = yuv_to_g(y,u,v);
167        pal_rgb->b = yuv_to_b(y,u);
168}
169
170static const uint8_t alpha2_lookup[] = {128,171,214,255};
171// like above, but with alpha lookup
172void palette_type2_to_rgba(const char *palette, uint8_t pixel,palette_entry_rgba_t *pal_rgb) {
173        const palette_entry_ayuv_t *pal = (const palette_entry_ayuv_t *)palette;
174        unsigned i1 = pixel & 0xF;
175        unsigned i2 = (pixel & 0xF0)>>4;
176        int8_t u,v;
177        uint8_t y;
178        uint8_t a = (pal[i1].a + pal[i2].a)>>1;
179        pal_rgb->a = alpha2_lookup[a&3];
180        // TODO not clear if these should be /2 or not
181        y = clamp_uint8(pal[i1].y + pal[i2].y);
182        u = clamp_int8(pal[i1].u + pal[i2].u);
183        v = clamp_int8(pal[i1].v + pal[i2].v);
184        pal_rgb->r = yuv_to_r(y,v);
185        pal_rgb->g = yuv_to_g(y,u,v);
186        pal_rgb->b = yuv_to_b(y,u);
187}
188
189void palette_type3_to_rgba(const char *palette, uint8_t pixel,palette_entry_rgba_t *pal_rgb) {
190        const palette_entry_vuya_t *pal = (const palette_entry_vuya_t *)palette;
191        int8_t u,v;
192        uint8_t y;
193        // special case for index 0
194        if(pixel == 0) {
195                pal_rgb->a = pal_rgb->r = pal_rgb->g = pal_rgb->b = 0;
196                return;
197        }
198        pal_rgb->a = alpha2_lookup[pal[pixel].a&3];
199        pal_rgb->r = yuv_to_r(pal[pixel].y,pal[pixel].v);
200        pal_rgb->g = yuv_to_g(pal[pixel].y,pal[pixel].u,pal[pixel].v);
201        pal_rgb->b = yuv_to_b(pal[pixel].y,pal[pixel].u);
202}
203
204void yuv_live_to_cd_rgb(const char *p_yuv,
205                                                unsigned buf_width, unsigned buf_height,
206                                                unsigned x_offset, unsigned y_offset,
207                                                unsigned width,unsigned height,
208                                                int skip,
209                                                uint8_t *r,uint8_t *g,uint8_t *b) {
210        unsigned x,y;
211        unsigned y_inc = (buf_width*12)/8;
212        const char *p;
213        // flip for CD
214        for(y=y_offset + height-1;y>y_offset;y--) {
215                p = p_yuv + y * y_inc + (x_offset*12)/8;
216                for(x=x_offset;x<width+x_offset;x+=4,p+=6) {
217                        *r++ = yuv_to_r(p[1],p[2]);
218                        *g++ = yuv_to_g(p[1],p[0],p[2]);
219                        *b++ = yuv_to_b(p[1],p[0]);
220
221                        *r++ = yuv_to_r(p[3],p[2]);
222                        *g++ = yuv_to_g(p[3],p[0],p[2]);
223                        *b++ = yuv_to_b(p[3],p[0]);
224                        if(!skip) {
225                                // TODO it might be better to use the next pixels U and V values
226                                *r++ = yuv_to_r(p[4],p[2]);
227                                *g++ = yuv_to_g(p[4],p[0],p[2]);
228                                *b++ = yuv_to_b(p[4],p[0]);
229
230                                *r++ = yuv_to_r(p[5],p[2]);
231                                *g++ = yuv_to_g(p[5],p[0],p[2]);
232                                *b++ = yuv_to_b(p[5],p[0]);
233                        }
234                }
235        }
236}
237
238static void pimg_destroy(liveimg_pimg_t *im) {
239        free(im->data);
240        im->width = im->height = 0;
241        im->data = im->r = im->g = im->b = im->a = NULL;
242}
243
244static int pimg_gc(lua_State *L) {
245        liveimg_pimg_t *im = (liveimg_pimg_t *)luaL_checkudata(L,1,LIVEIMG_PIMG_META);
246        pimg_destroy(im);
247        return 0;
248}
249
250static int pimg_get_width(lua_State *L) {
251        liveimg_pimg_t *im = (liveimg_pimg_t *)luaL_checkudata(L,1,LIVEIMG_PIMG_META);
252        if(!im->data) {
253                return luaL_error(L,"dead pimg");
254        }
255        lua_pushnumber(L,im->width);
256        return 1;
257}
258
259static int pimg_get_height(lua_State *L) {
260        liveimg_pimg_t *im = (liveimg_pimg_t *)luaL_checkudata(L,1,LIVEIMG_PIMG_META);
261        if(!im->data) {
262                return luaL_error(L,"dead pimg");
263        }
264        lua_pushnumber(L,im->height);
265        return 1;
266}
267
268static int pimg_kill(lua_State *L) {
269        liveimg_pimg_t *im = (liveimg_pimg_t *)luaL_checkudata(L,1,LIVEIMG_PIMG_META);
270        pimg_destroy(im);
271        return 0;
272}
273
274/*
275create a new pimg and push it on the stack
276TODO might want to pass in width, height or data, but need to handle rgb vs rgba
277*/
278int pimg_create(lua_State *L) {
279        liveimg_pimg_t *im = (liveimg_pimg_t *)lua_newuserdata(L,sizeof(liveimg_pimg_t));
280        if(!im) {
281                return 0;
282        }
283        im->width = im->height = 0;
284        im->data = im->r = im->g = im->b = im->a = NULL;
285        luaL_getmetatable(L, LIVEIMG_PIMG_META);
286        lua_setmetatable(L, -2);
287
288        return 1;
289}
290
291int pimg_init_rgb(liveimg_pimg_t *im,unsigned width,unsigned height) {
292        unsigned size = width*height;
293        if(!size) {
294                return 0;
295        }
296        im->data=malloc(size*3);
297        if(!im->data) {
298                return 0;
299        }
300        im->width = width;
301        im->height = height;
302        im->r=im->data;
303        im->g=im->r+size;
304        im->b=im->g+size;
305        im->a=NULL;
306        return 1;
307}
308
309/*
310TODO stupid copy/paste
311*/
312int pimg_init_rgba(liveimg_pimg_t *im,unsigned width,unsigned height) {
313        unsigned size = width*height;
314        if(!size) {
315                return 0;
316        }
317        im->data=malloc(size*4);
318        if(!im->data) {
319                return 0;
320        }
321        im->width = width;
322        im->height = height;
323        im->r=im->data;
324        im->g=im->r+size;
325        im->b=im->g+size;
326        im->a=im->b+size;
327        return 1;
328}
329
330/*
331check whether given stack index is an pimg, and if so, return it
332*/
333liveimg_pimg_t * pimg_get(lua_State *L,int i) {
334        if(!lua_isuserdata(L,i)) {
335                return NULL;
336        }
337        if(lua_islightuserdata(L,i)) {
338                return NULL;
339        }
340        if(!lua_getmetatable(L,i)) {
341                return NULL;
342        }
343        lua_getfield(L,LUA_REGISTRYINDEX,LIVEIMG_PIMG_META);
344        int r = lua_rawequal(L,-1,-2);
345        lua_pop(L,2);
346        if(r) {
347                return lua_touserdata(L,i);
348        }
349        return NULL;
350}
351
352/*
353convert viewport data to RGB pimg
354pimg=liveimg.get_viewport_pimg(pimg,base_info,vid_info,skip)
355pimg: pimg to re-use, created if nil, replaced if size doesn't match
356vid_info, base_info: from handler
357skip: boolean - if true, each U Y V Y Y Y is converted to 2 pixels, otherwise 4
358returns nil if info does not contain a live view
359*/
360static int liveimg_get_viewport_pimg(lua_State *L) {
361        lv_vid_info *vi;
362        lv_base_info *bi;
363        liveimg_pimg_t *im = pimg_get(L,1);
364        lBuf_t *base_lb = luaL_checkudata(L,2,LBUF_META);
365        lBuf_t *vi_lb = luaL_checkudata(L,3,LBUF_META);
366        int skip = lua_toboolean(L,4);
367        // pixel aspect ratio
368        int par = (skip == 1)?2:1;
369
370        bi = (lv_base_info *)base_lb->bytes;
371        vi = (lv_vid_info *)vi_lb->bytes;
372
373        if(!vi->vp_buffer_start) {
374                lua_pushnil(L);
375                return 1;
376        }
377
378        unsigned vwidth = vi->vp_width/par;
379        unsigned dispsize = vwidth*vi->vp_height;
380
381        if(im && dispsize != im->width*im->height) {
382                pimg_destroy(im);
383                im = NULL;
384        }
385        if(im) {
386                lua_pushvalue(L, 1); // copy im onto top for return
387        } else { // create an new im
388                pimg_create(L);
389                im = luaL_checkudata(L,-1,LIVEIMG_PIMG_META);
390                if(!pimg_init_rgb(im,vwidth,vi->vp_height)) {
391                        return luaL_error(L,"failed to create image");
392                }
393        }
394
395        yuv_live_to_cd_rgb(vi_lb->bytes+vi->vp_buffer_start,
396                                                bi->vp_buffer_width,
397                                                bi->vp_max_height,
398                                                vi->vp_xoffset,
399                                                vi->vp_yoffset,
400                                                vi->vp_width,
401                                                vi->vp_height,
402                                                skip,
403                                                im->r,im->g,im->b);
404        return 1;
405}
406
407static void convert_palette(palette_entry_rgba_t *pal_rgba,lv_vid_info *vi) {
408        const char *pal=NULL;
409        palette_convert_t *convert=get_palette_convert(vi->palette_type);
410        yuv_palette_to_rgba_fn fn = NULL;
411        if(convert && vi->palette_buffer_start) {
412                fn = convert->to_rgba;
413                pal = ((char *)vi + vi->palette_buffer_start);
414        } else {
415                pal = palette_type1_default;
416                fn = palette_type1_to_rgba;
417        }
418        int i;
419        for(i=0;i<255;i++) {
420                fn(pal,i,&pal_rgba[i]);
421        }
422}
423
424/*
425convert bitmap data to RGBA pimg
426pimg=liveimg.get_bitmap_pimg(pimg,base_info,vid_info,skip)
427pimg: pimg to re-use, created if nil, replaced if size doesn't match
428vid_info, base_info: from handler
429skip: boolean - if true, every other pixel in the x axis is discarded (for viewports with a 1:2 par)
430returns nil if info does not contain a bitmap
431*/
432static int liveimg_get_bitmap_pimg(lua_State *L) {
433        palette_entry_rgba_t pal_rgba[256];
434
435        lv_vid_info *vi;
436        lv_base_info *bi;
437        liveimg_pimg_t *im = pimg_get(L,1);
438        lBuf_t *base_lb = luaL_checkudata(L,2,LBUF_META);
439        lBuf_t *vi_lb = luaL_checkudata(L,3,LBUF_META);
440        int skip = lua_toboolean(L,4);
441        // pixel aspect ratio
442        int par = (skip == 1)?2:1;
443
444        bi = (lv_base_info *)base_lb->bytes;
445        vi = (lv_vid_info *)vi_lb->bytes;
446
447        if(!vi->bm_buffer_start) {
448                lua_pushnil(L);
449                return 1;
450        }
451
452        convert_palette(pal_rgba,vi);
453
454        unsigned vwidth = bi->bm_max_width/par;
455        unsigned dispsize = vwidth*bi->bm_max_height;
456
457        if(im && dispsize != im->width*im->height) {
458                pimg_destroy(im);
459                im = NULL;
460        }
461        if(im) {
462                lua_pushvalue(L, 1); // copy im onto top for return
463        } else { // create an new im
464                pimg_create(L);
465                im = luaL_checkudata(L,-1,LIVEIMG_PIMG_META);
466                if(!pimg_init_rgba(im,vwidth,bi->bm_max_height)) {
467                        return luaL_error(L,"failed to create image");
468                }
469        }
470
471        int y_inc = bi->bm_buffer_width;
472        int x_inc = par;
473        int x,y;
474        int height = bi->bm_max_height;
475
476        uint8_t *p=((uint8_t *)vi + vi->bm_buffer_start) + (height-1)*y_inc;
477
478        uint8_t *r = im->r;
479        uint8_t *g = im->g;
480        uint8_t *b = im->b;
481        uint8_t *a = im->a;
482
483        for(y=0;y<height;y++,p-=y_inc) {
484                for(x=0;x<bi->bm_max_width;x+=x_inc) {
485                        palette_entry_rgba_t *c =&pal_rgba[*(p+x)];
486                        *r++ = c->r;
487                        *g++ = c->g;
488                        *b++ = c->b;
489                        *a++ = c->a;
490                }
491        }
492        return 1;
493}
494
495#if defined(CHDKPTP_CD)
496/*
497pimg:put_to_cd_canvas(canvas, x, y, width, height, xmin, xmax, ymin, ymax)
498*/
499static int pimg_put_to_cd_canvas(lua_State *L) {
500        liveimg_pimg_t *im = (liveimg_pimg_t *)luaL_checkudata(L,1,LIVEIMG_PIMG_META);
501        cdCanvas *cnv = cdlua_checkcanvas(L,2);
502        if(!im->data) {
503                return luaL_error(L,"dead pimg");
504        }
505        // left, bottom
506        int x=luaL_optint(L,3,0);
507        int y=luaL_optint(L,4,0);
508        // target width, height. 0 = default
509        int width=luaL_optint(L,5,0);
510        int height=luaL_optint(L,6,0);
511        // sub image
512        int xmin=luaL_optint(L,7,0);
513        int xmax=luaL_optint(L,8,0);
514        int ymin=luaL_optint(L,9,0);
515        int ymax=luaL_optint(L,10,0);
516        cdCanvasPutImageRectRGB(cnv,
517                                                        im->width,im->height, // image size
518                                                        im->r,im->g,im->b, // data
519                                                        x,y,
520                                                        width,height,
521                                                        xmin,xmax,ymin,ymax);
522        return 0;
523}
524
525/*
526as above, but with alpha
527*/
528static int pimg_blend_to_cd_canvas(lua_State *L) {
529        liveimg_pimg_t *im = (liveimg_pimg_t *)luaL_checkudata(L,1,LIVEIMG_PIMG_META);
530        cdCanvas *cnv = cdlua_checkcanvas(L,2);
531        if(!im->data) {
532                return luaL_error(L,"dead pimg");
533        }
534        if(!im->a) {
535                return luaL_error(L,"pimg has no alpha channel");
536        }
537        // left, bottom
538        int x=luaL_optint(L,3,0);
539        int y=luaL_optint(L,4,0);
540        // target width, height. 0 = default
541        int width=luaL_optint(L,5,0);
542        int height=luaL_optint(L,6,0);
543        // sub image
544        int xmin=luaL_optint(L,7,0);
545        int xmax=luaL_optint(L,8,0);
546        int ymin=luaL_optint(L,9,0);
547        int ymax=luaL_optint(L,10,0);
548        cdCanvasPutImageRectRGBA(cnv,
549                                                        im->width,im->height, // image size
550                                                        im->r,im->g,im->b,im->a, // data
551                                                        x,y,
552                                                        width,height,
553                                                        xmin,xmax,ymin,ymax);
554        return 0;
555}
556
557#endif
558
559static const luaL_Reg liveimg_funcs[] = {
560  {"get_bitmap_pimg", liveimg_get_bitmap_pimg},
561  {"get_viewport_pimg", liveimg_get_viewport_pimg},
562  {NULL, NULL}
563};
564
565static const luaL_Reg pimg_methods[] = {
566#if defined(CHDKPTP_CD)
567  {"put_to_cd_canvas", pimg_put_to_cd_canvas},
568  {"blend_to_cd_canvas", pimg_blend_to_cd_canvas},
569#endif
570  {"width", pimg_get_width},
571  {"height", pimg_get_height},
572  {"kill", pimg_kill},
573  {NULL, NULL}
574};
575
576static const luaL_Reg pimg_meta_methods[] = {
577  {"__gc", pimg_gc},
578  {NULL, NULL}
579};
580
581// TODO based on lbuf,
582// would be nice to have a way to extend lbuf with additional custom bindings
583void liveimg_open(lua_State *L) {
584        luaL_newmetatable(L,LIVEIMG_PIMG_META);
585        luaL_register(L, NULL, pimg_meta_methods); 
586
587        /* use a table of methods for the __index method */
588        lua_newtable(L);
589        luaL_register(L, NULL, pimg_methods); 
590        lua_setfield(L,-2,"__index");
591
592        /* global lib */
593        lua_newtable(L);
594        luaL_register(L, "liveimg", liveimg_funcs); 
595        lua_pop(L,3);
596}
597
Note: See TracBrowser for help on using the repository browser.