source: trunk/core/raw.c @ 1414

Revision 1414, 19.6 KB checked in by philmoz, 2 years ago (diff)

Add ALPHA version for SX40HS, firmware 1.00f and 1.00g (thx ERR99).

  • Property svn:eol-style set to native
Line 
1#include "platform.h"
2#include "conf.h"
3#include "stdlib.h"
4#include "raw.h"
5#include "console.h"
6#if DNG_SUPPORT
7    #include "dng.h"
8    #include "math.h"
9    #include "keyboard.h"
10    #include "action_stack.h"
11    #include "gui_draw.h"
12    #include "gui_mbox.h"
13    #include "gui_lang.h"
14#endif
15#ifdef OPT_CURVES
16    #include "curves.h"
17#endif
18#include "shot_histogram.h"
19
20//-------------------------------------------------------------------
21#define RAW_TARGET_DIRECTORY    "A/DCIM/%03dCANON"
22//#define RAW_TMP_FILENAME        "HDK_RAW.TMP"
23#define RAW_TARGET_FILENAME     "%s%04d%s"
24#define RAW_BRACKETING_FILENAME "%s%04d_%02d%s"
25
26#define PATH_BADPIXEL_BIN "A/CHDK/badpixel.bin"
27#define PATH_BAD_TMP_BIN "A/CHDK/bad_tmp.bin"
28
29//-------------------------------------------------------------------
30static char fn[64];
31static char dir[32];
32static int develop_raw=0;
33
34//-------------------------------------------------------------------
35void raw_prepare_develop(const char* filename) {
36    if (filename) {
37        develop_raw=1;
38        strcpy(fn,filename);
39    } else {
40        develop_raw=0;
41    }
42}
43
44//-------------------------------------------------------------------
45void patch_bad_pixels(void);
46//-------------------------------------------------------------------
47
48char* get_raw_image_addr(void) {
49    if (!conf.raw_cache) return hook_raw_image_addr();
50    else return (char*) ((int)hook_raw_image_addr()&~CAM_UNCACHED_BIT);
51}
52
53char* get_alt_raw_image_addr(void) {    // return inactive buffer for cameras with multiple RAW buffers (otherwise return active buffer)
54    if (!conf.raw_cache) return hook_alt_raw_image_addr();
55    else return (char*) ((int)hook_alt_raw_image_addr()&~CAM_UNCACHED_BIT);
56}
57
58//-------------------------------------------------------------------
59
60#if DNG_SUPPORT
61#define INIT_BADPIXEL_COUNT -1
62#define INIT_BADPIXEL_FILE -2
63
64int init_badpixel_bin_flag; // contants above to count/create file, > 0 num bad pixel
65int raw_init_badpixel_bin() {
66    int count;
67    unsigned short c[2];
68    FILE*f;
69    if(init_badpixel_bin_flag == INIT_BADPIXEL_FILE) {
70        f=fopen(PATH_BAD_TMP_BIN,"w+b");
71    } else if (init_badpixel_bin_flag == INIT_BADPIXEL_COUNT) {
72        f=NULL;
73    } else {
74        return 0;
75    }
76    count = 0;
77#ifdef DNG_VERT_RLE_BADPIXELS
78    for (c[0]=CAM_ACTIVE_AREA_X1; c[0]<CAM_ACTIVE_AREA_X2; c[0]++)
79    {
80        for (c[1]=CAM_ACTIVE_AREA_Y1; c[1]<CAM_ACTIVE_AREA_Y2; c[1]++)
81        {
82            if (get_raw_pixel(c[0],c[1])==0)
83            {
84                unsigned short l;
85                for (l=0; l<7; l++) if (get_raw_pixel(c[0],c[1]+l+1)!=0) break;
86                c[1] = c[1] | (l << 13);
87                if (f) fwrite(c, 1, 4, f);
88                c[1] = (c[1] & 0x1FFF) + l;
89                count = count + l + 1;
90            }
91        }
92    }
93#else
94    for (c[1]=CAM_ACTIVE_AREA_Y1; c[1]<CAM_ACTIVE_AREA_Y2; c[1]++)
95    {
96        for (c[0]=CAM_ACTIVE_AREA_X1; c[0]<CAM_ACTIVE_AREA_X2; c[0]++)
97        {
98            if (get_raw_pixel(c[0],c[1])==0)
99            {
100                if (f) fwrite(c, 1, 4, f);
101                count++;
102            }
103        }
104    }
105#endif
106    if (f) fclose(f);
107    init_badpixel_bin_flag = count;
108    state_shooting_progress = SHOOTING_PROGRESS_PROCESSING;
109    return 1;
110}
111
112unsigned short get_raw_pixel(unsigned int x,unsigned  int y);
113
114static unsigned char gamma[256];
115
116void fill_gamma_buf(void) {
117    int i;
118    if (gamma[255]) return;
119#if defined(CAMERA_sx30) || defined(CAMERA_sx40hs) || defined(CAMERA_g12) || defined(CAMERA_ixus310_elph500hs)
120    for (i=0; i<12; i++) gamma[i]=255*pow(i/255.0, 0.5);
121    for (i=12; i<64; i++) gamma[i]=255*pow(i/255.0, 0.4);
122    for (i=64; i<=255; i++) gamma[i]=255*pow(i/255.0, 0.25);
123#else
124    for (i=0; i<=255; i++) gamma[i]=255*pow(i/255.0, 0.5);
125#endif
126}
127
128void create_thumbnail(char* buf) {
129    unsigned int i, j, x, y;
130    unsigned char r, g, b;
131    for (i=0; i<DNG_TH_HEIGHT; i++)
132        for (j=0; j<DNG_TH_WIDTH; j++) {
133            x=CAM_ACTIVE_AREA_X1+((CAM_ACTIVE_AREA_X2-CAM_ACTIVE_AREA_X1)*j)/DNG_TH_WIDTH;
134            y=CAM_ACTIVE_AREA_Y1+((CAM_ACTIVE_AREA_Y2-CAM_ACTIVE_AREA_Y1)*i)/DNG_TH_HEIGHT;
135#if cam_CFAPattern==0x02010100    // Red  Green  Green  Blue
136            r=gamma[get_raw_pixel((x/2)*2,(y/2)*2)>>(CAM_SENSOR_BITS_PER_PIXEL-8)]; // red pixel
137            g=gamma[6*(get_raw_pixel((x/2)*2+1,(y/2)*2)>>(CAM_SENSOR_BITS_PER_PIXEL-8))/10]; // green pixel
138            b=gamma[get_raw_pixel((x/2)*2+1,(y/2)*2+1)>>(CAM_SENSOR_BITS_PER_PIXEL-8)]; //blue pixel
139#elif cam_CFAPattern==0x01000201 // Green  Blue  Red  Green
140            r=gamma[get_raw_pixel((x/2)*2,(y/2)*2+1)>>(CAM_SENSOR_BITS_PER_PIXEL-8)]; // red pixel
141            g=gamma[6*(get_raw_pixel((x/2)*2,(y/2)*2)>>(CAM_SENSOR_BITS_PER_PIXEL-8))/10]; // green pixel
142            b=gamma[get_raw_pixel((x/2)*2+1,(y/2)*2)>>(CAM_SENSOR_BITS_PER_PIXEL-8)]; //blue pixel
143#else
144    #error please define new pattern here
145#endif
146            *buf++=r; *buf++=g; *buf++=b;
147        }
148}
149#else // no DNG_SUPPORT
150    static inline int __attribute__((always_inline)) raw_init_badpixel_bin(void) {return 0;}
151#endif
152//-------------------------------------------------------------------
153
154int raw_savefile() {
155    int ret = 0;
156    int fd;
157    static struct utimbuf t;
158    static int br_counter;
159#if DNG_SUPPORT
160    struct t_data_for_exif* exif_data = NULL;
161    char *thumbnail_buf = NULL;
162    if (conf.dng_raw) exif_data=capture_data_for_exif();
163#endif   
164    if (state_kbd_script_run && shot_histogram_isenabled()) build_shot_histogram();
165
166    // Get pointers to RAW buffers (will be the same on cameras that don't have two or more buffers)
167    char* rawadr = get_raw_image_addr();
168    char* altrawadr = get_alt_raw_image_addr();
169
170    // count/save badpixels if requested
171    if(raw_init_badpixel_bin()) {
172        return 0;
173    }
174
175    if (develop_raw) {
176        started();
177        fd = open(fn, O_RDONLY, 0777);
178        if (fd>=0) {
179            read(fd, get_raw_image_addr(), hook_raw_size());
180            close(fd);
181        }
182#ifdef OPT_CURVES
183        if (conf.curve_enable) curve_apply();
184#endif
185        finished();
186        develop_raw=0;
187        return 0;
188    }
189
190    if (conf.bad_pixel_removal) patch_bad_pixels();
191
192    shooting_bracketing();
193
194    if(conf.tv_bracket_value || conf.av_bracket_value || conf.iso_bracket_value || conf.subj_dist_bracket_value) {
195        if(state_shooting_progress != SHOOTING_PROGRESS_PROCESSING)
196            br_counter = 1;
197        else
198            br_counter++;
199    }
200    else
201        br_counter=0;
202
203    // got here second time in a row. Skip second RAW saving.
204    if (conf.raw_save_first_only && state_shooting_progress == SHOOTING_PROGRESS_PROCESSING) {
205        return 0;
206    }
207
208    state_shooting_progress = SHOOTING_PROGRESS_PROCESSING;
209
210    if (conf.save_raw && is_raw_enabled())
211    {
212        int timer; char txt[30];
213
214        started();
215
216        t.actime = t.modtime = time(NULL);
217
218        mkdir_if_not_exist("A/DCIM");
219#if defined(CAM_DATE_FOLDER_NAMING)
220        if (conf.raw_in_dir)
221            get_target_dir_name(dir);
222        else
223            sprintf(dir, RAW_TARGET_DIRECTORY, 100);
224#else
225        sprintf(dir, RAW_TARGET_DIRECTORY, (conf.raw_in_dir)?get_target_dir_num():100);
226#endif
227        mkdir_if_not_exist(dir);
228
229        sprintf(fn, "%s/", dir);
230        if(br_counter && conf.bracketing_add_raw_suffix && (shooting_get_drive_mode()!=1)) {
231            sprintf(fn+strlen(fn),
232                    RAW_BRACKETING_FILENAME,
233                    img_prefixes[conf.raw_prefix],
234                    get_target_file_num(),
235                    br_counter,
236                    conf.dng_raw&&conf.raw_dng_ext ? ".DNG" : img_exts[conf.raw_ext]);
237        } else {
238            sprintf(fn+strlen(fn),
239                    RAW_TARGET_FILENAME,
240                    img_prefixes[conf.raw_prefix],
241                    get_target_file_num(),
242                    conf.dng_raw&&conf.raw_dng_ext ? ".DNG" : img_exts[conf.raw_ext]);
243        }
244        fd = open(fn, O_WRONLY|O_CREAT, 0777);
245        if (fd>=0) {
246            timer=get_tick_count();
247#if DNG_SUPPORT
248            if (conf.dng_raw)
249            {
250                fill_gamma_buf();
251                create_dng_header(exif_data);
252                thumbnail_buf = malloc(DNG_TH_WIDTH*DNG_TH_HEIGHT*3);
253                if (get_dng_header() && thumbnail_buf) {
254                    patch_bad_pixels_b();
255                    create_thumbnail(thumbnail_buf);
256                    write(fd, get_dng_header(), get_dng_header_size());
257                    write(fd, thumbnail_buf, DNG_TH_WIDTH*DNG_TH_HEIGHT*3);
258                    reverse_bytes_order2(rawadr, altrawadr, hook_raw_size());
259                }
260                // Write alternate (inactive) buffer that we reversed the bytes into above (if only one buffer then it will be the active buffer instead)
261                write(fd, (char*)(((unsigned long)altrawadr)|CAM_UNCACHED_BIT), hook_raw_size());
262            }
263            else
264            {
265                // Write active RAW buffer
266                write(fd, (char*)(((unsigned long)rawadr)|CAM_UNCACHED_BIT), hook_raw_size());
267            }
268            close(fd);
269            utime(fn, &t);
270
271            if (conf.dng_raw) {
272                if (get_dng_header() && thumbnail_buf) {
273                    if (rawadr == altrawadr)    // If only one RAW buffer then we have to swap the bytes back
274                        reverse_bytes_order2(rawadr, altrawadr, hook_raw_size());
275                    //unpatch_bad_pixels_b();
276                }
277                if (get_dng_header()) free_dng_header();
278                if (thumbnail_buf) free(thumbnail_buf);
279            }
280#else
281            // Write active RAW buffer
282            write(fd, (char*)(((unsigned long)rawadr)|CAM_UNCACHED_BIT), hook_raw_size());
283            close(fd);
284            utime(fn, &t);
285#endif
286            if (conf.raw_timer) {
287                timer=get_tick_count()-timer;
288                sprintf(txt, "saving time=%d", timer);
289                console_add_line(txt);
290            }
291        }
292
293        finished();
294
295        ret = (fd >= 0);
296    }
297
298#ifdef OPT_CURVES
299    if (conf.curve_enable) curve_apply();
300#endif
301    return ret;
302}
303
304//-------------------------------------------------------------------
305void raw_postprocess() {
306}
307
308//-------------------------------------------------------------------
309
310void set_raw_pixel(unsigned int x, unsigned int y, unsigned short value) {
311#if CAM_SENSOR_BITS_PER_PIXEL==10
312    unsigned char* addr=(unsigned char*)get_raw_image_addr()+y*RAW_ROWLEN+(x/8)*CAM_SENSOR_BITS_PER_PIXEL;
313    switch (x%8) {
314        case 0: addr[0]=(addr[0]&0x3F)|(value<<6); addr[1]=value>>2;                  break;
315        case 1: addr[0]=(addr[0]&0xC0)|(value>>4); addr[3]=(addr[3]&0x0F)|(value<<4); break;
316        case 2: addr[2]=(addr[2]&0x03)|(value<<2); addr[3]=(addr[3]&0xF0)|(value>>6); break;
317        case 3: addr[2]=(addr[2]&0xFC)|(value>>8); addr[5]=value;                     break;
318        case 4: addr[4]=value>>2;                  addr[7]=(addr[7]&0x3F)|(value<<6); break;
319        case 5: addr[6]=(addr[6]&0x0F)|(value<<4); addr[7]=(addr[7]&0xC0)|(value>>4); break;
320        case 6: addr[6]=(addr[6]&0xF0)|(value>>6); addr[9]=(addr[9]&0x03)|(value<<2); break;
321        case 7: addr[8]=value;                     addr[9]=(addr[9]&0xFC)|(value>>8); break;
322    }
323#elif CAM_SENSOR_BITS_PER_PIXEL==12
324    unsigned char* addr=(unsigned char*)get_raw_image_addr()+y*RAW_ROWLEN+(x/4)*6;
325    switch (x%4) {
326        case 0: addr[0] = (addr[0]&0x0F) | (unsigned char)(value << 4);  addr[1] = (unsigned char)(value >> 4);  break;
327        case 1: addr[0] = (addr[0]&0xF0) | (unsigned char)(value >> 8);  addr[3] = (unsigned char)value;         break;
328        case 2: addr[2] = (unsigned char)(value >> 4);  addr[5] = (addr[5]&0x0F) | (unsigned char)(value << 4);  break;
329        case 3: addr[4] = (unsigned char)value; addr[5] = (addr[5]&0xF0) | (unsigned char)(value >> 8);  break;
330    }
331#else
332    #error define set_raw_pixel for sensor bit depth
333#endif
334}
335
336//-------------------------------------------------------------------
337unsigned short get_raw_pixel(unsigned int x,unsigned  int y) {
338#if CAM_SENSOR_BITS_PER_PIXEL==10
339    unsigned char* addr=(unsigned char*)get_raw_image_addr()+y*RAW_ROWLEN+(x/8)*CAM_SENSOR_BITS_PER_PIXEL;
340    switch (x%8) {
341        case 0: return ((0x3fc&(((unsigned short)addr[1])<<2)) | (addr[0] >> 6));
342        case 1: return ((0x3f0&(((unsigned short)addr[0])<<4)) | (addr[3] >> 4));
343        case 2: return ((0x3c0&(((unsigned short)addr[3])<<6)) | (addr[2] >> 2));
344        case 3: return ((0x300&(((unsigned short)addr[2])<<8)) | (addr[5]));
345        case 4: return ((0x3fc&(((unsigned short)addr[4])<<2)) | (addr[7] >> 6));
346        case 5: return ((0x3f0&(((unsigned short)addr[7])<<4)) | (addr[6] >> 4));
347        case 6: return ((0x3c0&(((unsigned short)addr[6])<<6)) | (addr[9] >> 2));
348        case 7: return ((0x300&(((unsigned short)addr[9])<<8)) | (addr[8]));
349    }
350#elif CAM_SENSOR_BITS_PER_PIXEL==12
351    unsigned char* addr=(unsigned char*)get_raw_image_addr()+y*RAW_ROWLEN+(x/4)*6;
352    switch (x%4) {
353        case 0: return ((unsigned short)(addr[1]) << 4) | (addr[0] >> 4);
354        case 1: return ((unsigned short)(addr[0] & 0x0F) << 8) | (addr[3]);
355        case 2: return ((unsigned short)(addr[2]) << 4) | (addr[5] >> 4);
356        case 3: return ((unsigned short)(addr[5] & 0x0F) << 8) | (addr[4]);
357    }
358#else
359    #error define get_raw_pixel for sensor bit depth
360#endif
361    return 0;
362}
363
364//-------------------------------------------------------------------
365void patch_bad_pixel(unsigned int x,unsigned  int y) {
366    int sum=0;
367    int nzero=0;
368    int i,j;
369    int val;
370    if ((x>=2) && (x<CAM_RAW_ROWPIX-2) && (y>=2) && (y<CAM_RAW_ROWS-2)) {
371        if ((conf.bad_pixel_removal==1) || conf.dng_raw) {  // interpolation or DNG saving
372            for (i=-2; i<=2; i+=2)
373                for (j=-2; j<=2; j+=2)
374                    if ((i!=0) && (j!=0)) {
375                        val=get_raw_pixel(x+i, y+j);
376                        if (val) {sum+=val; nzero++;}
377                    }
378                if (nzero) set_raw_pixel(x,y,sum/nzero);
379        } else if (conf.bad_pixel_removal==2)  // or this makes RAW converter (internal/external)
380            set_raw_pixel(x,y,0);
381    }
382}
383
384struct point{
385    int x;
386    int y;
387    struct point *next;
388} *pixel_list=NULL;
389
390void patch_bad_pixels(void) {
391    struct point *pixel=pixel_list;
392    while (pixel) {
393        patch_bad_pixel((*pixel).x,(*pixel).y);
394        pixel=(*pixel).next;
395    }
396}
397
398void make_pixel_list(char * ptr) {
399    int x,y;
400    struct point *pixel;
401    char *endptr;
402    while(*ptr) {
403        while (*ptr==' ' || *ptr=='\t') ++ptr;    // whitespaces
404        x=strtol(ptr, &endptr, 0);
405        if (endptr != ptr) {
406            ptr = endptr;
407            if (*ptr++==',') {
408                while (*ptr==' ' || *ptr=='\t') ++ptr;    // whitespaces
409                    if (*ptr!='\n' && *ptr!='\r') {
410                        y=strtol(ptr, &endptr, 0);
411                        if (endptr != ptr) {
412                            ptr = endptr;
413                            pixel=malloc(sizeof(struct point));
414                            if (pixel) {
415                                (*pixel).x=x;
416                                (*pixel).y=y;
417                                (*pixel).next=pixel_list;
418                                pixel_list=pixel;
419                            }
420                        }
421                    }
422                }
423        }
424        while (*ptr && *ptr!='\n') ++ptr;    // unless end of line
425        if (*ptr) ++ptr;
426    }
427}
428
429#define PIXELS_BUF_SIZE 8192
430void load_bad_pixels_list(const char* filename) {
431    char *buf;
432    int fd;
433
434    if (filename) {
435        buf = umalloc(PIXELS_BUF_SIZE);
436        if (!buf) return;
437
438        fd = open(filename, O_RDONLY, 0777);
439        if (fd>=0) {
440            int rcnt = read(fd, buf, PIXELS_BUF_SIZE);
441            if (rcnt > 0) {
442                if (rcnt == PIXELS_BUF_SIZE)
443                buf[PIXELS_BUF_SIZE-1] = 0;
444                else
445                buf[rcnt] = 0;
446            }
447            close(fd);
448        }
449        make_pixel_list(buf);   
450        ufree(buf);
451    }
452
453}
454
455#if DNG_SUPPORT
456short* binary_list=NULL;
457int binary_count=-1;
458
459void load_bad_pixels_list_b(char* filename) {
460    struct stat st;
461    long filesize;
462    void* ptr;
463    FILE *fd;
464    binary_count=-1;
465    if (stat(filename,&st)!=0) return;
466    filesize=st.st_size;
467    if (filesize%(2*sizeof(short)) != 0) return;
468        if (filesize == 0) { binary_count = 0; return; }        // Allow empty badpixel.bin file
469    ptr=malloc(filesize);
470    if (!ptr) return;
471    fd=fopen(filename, "rb");
472    if (fd) {
473        fread(ptr,1, filesize,fd);
474        fclose(fd);
475        binary_list=ptr;
476        binary_count=filesize/(2*sizeof(short));
477    }
478    else free(ptr);
479}
480
481void unload_bad_pixels_list_b(void) {
482    if (binary_list) free(binary_list);
483    binary_list=NULL;
484    binary_count=-1;
485}
486
487void patch_bad_pixels_b(void) {
488    int i;
489    short* ptr=binary_list;
490#ifdef DNG_VERT_RLE_BADPIXELS
491    short y, cnt;
492    for (i=0; i<binary_count; i++, ptr+=2)
493    {
494        y = ptr[1] & 0x1FFF;
495        cnt = (ptr[1] >> 13) & 7;
496        for (; cnt>=0; cnt--, y++)
497            if (get_raw_pixel(ptr[0], y)==0)
498                patch_bad_pixel(ptr[0], y);
499    }
500#else
501    for (i=0; i<binary_count; i++, ptr+=2)
502        if (get_raw_pixel(ptr[0], ptr[1])==0)
503            patch_bad_pixel(ptr[0], ptr[1]);
504#endif
505}
506/*
507void unpatch_bad_pixels_b(void) {
508    int i;
509    short* ptr=binary_list;
510    for (i=0; i<binary_count; i++, ptr+=2) set_raw_pixel(ptr[0], ptr[1], 0);
511}
512*/
513int badpixel_list_loaded_b(void) {
514        return (binary_count >= 0) ? 1 : 0;
515}
516
517// -----------------------------------------------
518
519enum BadpixelFSM {
520    BADPIX_START,
521    BADPIX_S1,
522    BADPIX_S2
523};
524
525int badpixel_task_stack(long p) {
526    static unsigned int badpix_cnt1;
527
528    switch(p) {
529        case BADPIX_START:
530            action_pop();
531
532            console_clear();
533            console_add_line("Wait please... ");
534            console_add_line("This takes a few seconds,");
535            console_add_line("don't panic!");
536
537            init_badpixel_bin_flag = INIT_BADPIXEL_COUNT;
538
539            shooting_set_tv96_direct(96, SET_LATER);
540            action_push(BADPIX_S1);
541            action_push(AS_SHOOT);
542            action_push_delay(3000);
543            break;
544        case BADPIX_S1:
545            action_pop();
546
547            badpix_cnt1 = init_badpixel_bin_flag;
548            init_badpixel_bin_flag = INIT_BADPIXEL_FILE;
549            shooting_set_tv96_direct(96, SET_LATER);
550
551            action_push(BADPIX_S2);
552            action_push(AS_SHOOT);
553            break;
554        case BADPIX_S2:
555            action_pop();
556
557            console_clear();
558            if (badpix_cnt1 == init_badpixel_bin_flag) {
559                // TODO script asked confirmation first
560                // should sanity check bad pixel count at least,
561                // wrong buffer address could make badpixel bigger than available mem
562                char msg[32];
563                console_add_line("badpixel.bin created.");
564                sprintf(msg, "Bad pixel count: %d", badpix_cnt1);
565                console_add_line(msg);
566                remove(PATH_BADPIXEL_BIN);
567                rename(PATH_BAD_TMP_BIN,PATH_BADPIXEL_BIN);
568            } else {
569                console_add_line("badpixel.bin failed.");
570                console_add_line("Please try again.");
571            }
572            init_badpixel_bin_flag = 0;
573            remove(PATH_BAD_TMP_BIN);
574
575            action_push_delay(3000);
576            break;
577        default:
578            action_stack_standard(p);
579            break;
580    }
581
582    return 1;
583}
584
585
586void create_badpixel_bin() {
587    if (!(mode_get() & MODE_REC)) {
588        gui_mbox_init(LANG_ERROR, LANG_MSG_RECMODE_REQUIRED, MBOX_BTN_OK|MBOX_TEXT_CENTER, NULL);
589        return;
590    }
591
592    gui_set_mode(GUI_MODE_ALT);
593    action_stack_create(&badpixel_task_stack, BADPIX_START);
594}
595
596#endif
Note: See TracBrowser for help on using the repository browser.