Edit

kc3-lang/libqrencode/tests/view_qrcode.c

Branch :

  • Show log

    Commit

  • Author : Kentaro Fukuchi
    Date : 2017-09-11 17:16:54
    Hash : f653e232
    Message : Symbol data representation of QRcode object has been slightly changed.

  • tests/view_qrcode.c
  • #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <SDL.h>
    #include <getopt.h>
    #include <errno.h>
    #include "../config.h"
    #include "../qrspec.h"
    #include "../qrinput.h"
    #include "../split.h"
    #include "../qrencode_inner.h"
    
    static SDL_Window *window;
    static SDL_Renderer *renderer;
    static SDL_Texture *texture = NULL;
    static SDL_Surface *surface = NULL;
    static int casesensitive = 1;
    static int eightbit = 0;
    static int version = 0;
    static int size = 4;
    static int margin = -1;
    static int structured = 0;
    static int micro = 0;
    static int colorize = 0;
    static QRecLevel level = QR_ECLEVEL_L;
    static QRencodeMode hint = QR_MODE_8;
    
    static char **textv;
    static int textc;
    
    static const struct option options[] = {
    	{"help"         , no_argument      , NULL, 'h'},
    	{"level"        , required_argument, NULL, 'l'},
    	{"size"         , required_argument, NULL, 's'},
    	{"symversion"   , required_argument, NULL, 'v'},
    	{"margin"       , required_argument, NULL, 'm'},
    	{"structured"   , no_argument      , NULL, 'S'},
    	{"kanji"        , no_argument      , NULL, 'k'},
    	{"casesensitive", no_argument      , NULL, 'c'},
    	{"ignorecase"   , no_argument      , NULL, 'i'},
    	{"8bit"         , no_argument      , NULL, '8'},
    	{"micro"        , no_argument      , NULL, 'M'},
    	{"version"      , no_argument      , NULL, 'V'},
    	{NULL, 0, NULL, 0}
    };
    
    static char *optstring = "hl:s:v:m:Skci8MV";
    
    static char levelChar[4] = {'L', 'M', 'Q', 'H'};
    static void usage(int help, int longopt)
    {
    	fprintf(stderr,
    "view_qrcode version %s\n"
    "Copyright (C) 2008, 2009, 2010 Kentaro Fukuchi\n", VERSION);
    	if(help) {
    		if(longopt) {
    			fprintf(stderr,
    "Usage: view_qrcode [OPTION]... [STRING]\n"
    "Encode input data in a QR Code and display.\n\n"
    "  -h, --help   display the help message. -h displays only the help of short\n"
    "               options.\n\n"
    "  -s NUMBER, --size=NUMBER\n"
    "               specify module size in dots (pixels). (default=3)\n\n"
    "  -l {LMQH}, --level={LMQH}\n"
    "               specify error correction level from L (lowest) to H (highest).\n"
    "               (default=L)\n\n"
    "  -v NUMBER, --symversion=NUMBER\n"
    "               specify the version of the symbol. See SYMBOL VERSIONS for more\n"
    "               information. (default=auto)\n\n"
    "  -m NUMBER, --margin=NUMBER\n"
    "               specify the width of the margins. (default=4 (2 for Micro QR)))\n\n"
    "  -S, --structured\n"
    "               make structured symbols. Version must be specified.\n\n"
    "  -k, --kanji  assume that the input text contains kanji (shift-jis).\n\n"
    "  -c, --casesensitive\n"
    "               encode lower-case alphabet characters in 8-bit mode. (default)\n\n"
    "  -i, --ignorecase\n"
    "               ignore case distinctions and use only upper-case characters.\n\n"
    "  -8, --8bit   encode entire data in 8-bit mode. -k, -c and -i will be ignored.\n\n"
    "  -M, --micro  encode in a Micro QR Code. (experimental)\n\n"
    "  -V, --version\n"
    "               display the version number and copyrights of the qrencode.\n\n"
    "  [STRING]     input data. If it is not specified, data will be taken from\n"
    "               standard input.\n\n"
    "*SYMBOL VERSIONS\n"
    "               The symbol versions of QR Code range from Version 1 to Version\n"
    "               40. Each version has a different module configuration or number\n"
    "               of modules, ranging from Version 1 (21 x 21 modules) up to\n"
    "               Version 40 (177 x 177 modules). Each higher version number\n"
    "               comprises 4 additional modules per side by default. See\n"
    "               http://www.qrcode.com/en/about/version.html for a detailed\n"
    "               version list.\n"
    			);
    		} else {
    			fprintf(stderr,
    "Usage: view_qrcode [OPTION]... [STRING]\n"
    "Encode input data in a QR Code and display.\n\n"
    "  -h           display this message.\n"
    "  --help       display the usage of long options.\n"
    "  -s NUMBER    specify module size in dots (pixels). (default=3)\n"
    "  -l {LMQH}    specify error correction level from L (lowest) to H (highest).\n"
    "               (default=L)\n"
    "  -v NUMBER    specify the version of the symbol. (default=auto)\n"
    "  -m NUMBER    specify the width of the margins. (default=4 (2 for Micro))\n"
    "  -S           make structured symbols. Version must be specified.\n"
    "  -k           assume that the input text contains kanji (shift-jis).\n"
    "  -c           encode lower-case alphabet characters in 8-bit mode. (default)\n"
    "  -i           ignore case distinctions and use only upper-case characters.\n"
    "  -8           encode entire data in 8-bit mode. -k, -c and -i will be ignored.\n"
    "  -M           encode in a Micro QR Code.\n"
    "  -V           display the version number and copyrights of the qrencode.\n"
    "  [STRING]     input data. If it is not specified, data will be taken from\n"
    "               standard input.\n"
    			);
    		}
    	}
    }
    
    #define MAX_DATA_SIZE (7090 * 16) /* from the specification */
    static unsigned char *readStdin(int *length)
    {
    	unsigned char *buffer;
    	int ret;
    
    	buffer = (unsigned char *)malloc(MAX_DATA_SIZE + 1);
    	if(buffer == NULL) {
    		fprintf(stderr, "Memory allocation failed.\n");
    		exit(EXIT_FAILURE);
    	}
    
    	ret = fread(buffer, 1, MAX_DATA_SIZE, stdin);
    	if(ret == 0) {
    		fprintf(stderr, "No input data.\n");
    		exit(EXIT_FAILURE);
    	}
    	if(feof(stdin) == 0) {
    		fprintf(stderr, "Input data is too large.\n");
    		exit(EXIT_FAILURE);
    	}
    
    	buffer[ret] = '\0';
    	*length = ret;
    
    	return buffer;
    }
    
    static void draw_QRcode(QRcode *qrcode, int ox, int oy)
    {
    	int x, y, width;
    	unsigned char *p;
    	SDL_Rect rect;
    	Uint32 color[8];
    	int col;
    
    	color[0] = SDL_MapRGBA(surface->format, 255, 255, 255, 255);
    	color[1] = SDL_MapRGBA(surface->format,   0,   0,   0, 255);
    	color[2] = SDL_MapRGBA(surface->format, 192, 192, 255, 255);
    	color[3] = SDL_MapRGBA(surface->format,   0,   0,  64, 255);
    	color[4] = SDL_MapRGBA(surface->format, 255, 255, 192, 255);
    	color[5] = SDL_MapRGBA(surface->format,  64,  64,   0, 255);
    	color[6] = SDL_MapRGBA(surface->format, 255, 192, 192, 255);
    	color[7] = SDL_MapRGBA(surface->format,  64,   0,   0, 255);
    
    	ox += margin * size;
    	oy += margin * size;
    	width = qrcode->width;
    	p = qrcode->data;
    	for(y=0; y<width; y++) {
    		for(x=0; x<width; x++) {
    			rect.x = ox + x * size;
    			rect.y = oy + y * size;
    			rect.w = size;
    			rect.h = size;
    			if(!colorize) {
    				col = 0;
    			} else {
    				if(*p & 0x80) {
    					col = 6;
    				} else if(*p & 0x02) {
    					col = 4;
    				} else {
    					col = 2;
    				}
    			}
    			col += (*p & 1);
    			SDL_FillRect(surface, &rect, color[col]);
    			p++;
    		}
    	}
    }
    
    static void draw_singleQRcode(QRinput *stream, int mask)
    {
    	QRcode *qrcode;
    	int width;
    
    	QRinput_setVersionAndErrorCorrectionLevel(stream, version, level);
    	if(micro) {
    		qrcode = QRcode_encodeMaskMQR(stream, mask);
    	} else {
    		qrcode = QRcode_encodeMask(stream, mask);
    	}
    	if(qrcode == NULL) {
    		width = (11 + margin * 2) * size;
    		fprintf(stderr, "Input data does not fit to this setting.\n");
    	} else {
    		version = qrcode->version;
    		width = (qrcode->width + margin * 2) * size;
    	}
    
    	SDL_SetWindowSize(window, width, width);
    	if(surface != NULL) {
    		SDL_FreeSurface(surface);
    	}
    	surface = SDL_CreateRGBSurface(0, width, width, 32, 0, 0, 0, 0);
    	SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, 255, 255, 255, 255));
    	if(qrcode) {
    		draw_QRcode(qrcode, 0, 0);
    	}
    	if(texture != NULL) {
    		SDL_DestroyTexture(texture);
    	}
    	texture = SDL_CreateTextureFromSurface(renderer, surface);
    	QRcode_free(qrcode);
    }
    
    static void draw_structuredQRcode(QRinput_Struct *s)
    {
    	int i, w, h, n, x, y;
    	int swidth;
    	QRcode_List *qrcodes, *p;
    
    	qrcodes = QRcode_encodeInputStructured(s);
    	if(qrcodes == NULL) return;
    
    	swidth = (qrcodes->code->width + margin * 2) * size;
    	n = QRcode_List_size(qrcodes);
    	w = (n < 4)?n:4;
    	h = (n - 1) / 4 + 1;
    
    	SDL_SetWindowSize(window, swidth * w, swidth * h);
    	if(surface != NULL) {
    		SDL_FreeSurface(surface);
    	}
    	surface = SDL_CreateRGBSurface(0, swidth * w, swidth * h, 32, 0, 0, 0, 0);
    	SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, 255, 255, 255, 255));
    
    	p = qrcodes;
    	for(i=0; i<n; i++) {
    		x = (i % 4) * swidth;
    		y = (i / 4) * swidth;
    		draw_QRcode(p->code, x, y);
    		p = p->next;
    	}
    	if(texture != NULL) {
    		SDL_DestroyTexture(texture);
    	}
    	texture = SDL_CreateTextureFromSurface(renderer, surface);
    	QRcode_List_free(qrcodes);
    }
    
    static void draw_structuredQRcodeFromText(int argc, char **argv)
    {
    	QRinput_Struct *s;
    	QRinput *input;
    	int i, ret;
    
    	s = QRinput_Struct_new();
    	if(s == NULL) {
    		fprintf(stderr, "Failed to allocate memory.\n");
    		exit(EXIT_FAILURE);
    	}
    	for(i=0; i<argc; i++) {
    		input = QRinput_new2(version, level);
    		if(input == NULL) {
    			fprintf(stderr, "Failed to allocate memory.\n");
    			exit(EXIT_FAILURE);
    		}
    		if(eightbit) {
    			ret = QRinput_append(input, QR_MODE_8, strlen(argv[i]), (unsigned char *)argv[i]);
    		} else {
    			ret = Split_splitStringToQRinput(argv[i], input, hint, casesensitive);
    		}
    		if(ret < 0) {
    			perror("Encoding the input string");
    			exit(EXIT_FAILURE);
    		}
    		ret = QRinput_Struct_appendInput(s, input);
    		if(ret < 0) {
    			perror("Encoding the input string");
    			exit(EXIT_FAILURE);
    		}
    	}
    	ret = QRinput_Struct_insertStructuredAppendHeaders(s);
    	if(ret < 0) {
    		fprintf(stderr, "Too many inputs.\n");
    	}
    
    	draw_structuredQRcode(s);
    	QRinput_Struct_free(s);
    }
    
    static void draw_structuredQRcodeFromQRinput(QRinput *stream)
    {
    	QRinput_Struct *s;
    
    	QRinput_setVersion(stream, version);
    	QRinput_setErrorCorrectionLevel(stream, level);
    	s = QRinput_splitQRinputToStruct(stream);
    	if(s != NULL) {
    		draw_structuredQRcode(s);
    		QRinput_Struct_free(s);
    	} else {
    		fprintf(stderr, "Input data is too large for this setting.\n");
    	}
    }
    
    static void view(int mode, QRinput *input)
    {
    	int flag = 1;
    	int mask = -1;
    	SDL_Event event;
    	int loop;
    	int codeChanged = 1;
    
    	while(flag) {
    		if(codeChanged) {
    			if(mode) {
    				draw_structuredQRcodeFromText(textc, textv);
    			} else {
    				if(structured) {
    					draw_structuredQRcodeFromQRinput(input);
    				} else {
    					draw_singleQRcode(input, mask);
    				}
    			}
    			if(mode || structured) {
    				printf("Version %d, Level %c.\n", version, levelChar[level]);
    			} else {
    				printf("Version %d, Level %c, Mask %d.\n", version, levelChar[level], mask);
    			}
    		}
    		SDL_RenderClear(renderer);
    		SDL_RenderCopy(renderer, texture, NULL, NULL);
    		SDL_RenderPresent(renderer);
    		loop = 1;
    		codeChanged = 0;
    		while(loop) {
    			SDL_WaitEvent(&event);
    			if(event.type == SDL_KEYDOWN) {
    				codeChanged = 1;
    				switch(event.key.keysym.sym) {
    				case SDLK_RIGHT:
    					version++;
    					if(version > QRSPEC_VERSION_MAX)
    						version = QRSPEC_VERSION_MAX;
    					loop = 0;
    					break;
    				case SDLK_LEFT:
    					version--;
    					if(version < 1)
    						version = 1;
    					loop = 0;
    					break;
    				case SDLK_UP:
    					size++;
    					loop = 0;
    					break;
    				case SDLK_DOWN:
    					size--;
    					if(size < 1) size = 1;
    					loop = 0;
    					break;
    				case SDLK_0:
    				case SDLK_1:
    				case SDLK_2:
    				case SDLK_3:
    				case SDLK_4:
    				case SDLK_5:
    				case SDLK_6:
    				case SDLK_7:
    					if(!mode && !structured) {
    						mask = (event.key.keysym.sym - SDLK_0);
    						loop = 0;
    					}
    					break;
    				case SDLK_8:
    					if(!mode && !structured) {
    						mask = -1;
    						loop = 0;
    					}
    					break;
    				case SDLK_9:
    					if(!mode && !structured) {
    						mask = -2;
    						loop = 0;
    					}
    					break;
    				case SDLK_l:
    					level = QR_ECLEVEL_L;
    					loop = 0;
    					break;
    				case SDLK_m:
    					level = QR_ECLEVEL_M;
    					loop = 0;
    					break;
    				case SDLK_h:
    					level = QR_ECLEVEL_H;
    					loop = 0;
    					break;
    				case SDLK_q:
    					level = QR_ECLEVEL_Q;
    					loop = 0;
    					break;
    				case SDLK_c:
    					colorize ^= 1;
    					loop = 0;
    					break;
    				case SDLK_ESCAPE:
    					loop = 0;
    					flag = 0;
    					break;
    				default:
    					break;
    				}
    				if(event.type == SDL_QUIT) {
    					loop = 0;
    					flag = 0;
    				}
    			}
    			if (event.type == SDL_WINDOWEVENT) {
    				switch (event.window.event) {
    					case SDL_WINDOWEVENT_SHOWN:
    					case SDL_WINDOWEVENT_EXPOSED:
    					case SDL_WINDOWEVENT_SIZE_CHANGED:
    					case SDL_WINDOWEVENT_RESIZED:
    						loop = 0;
    						break;
    					default:
    						break;
    				}
    			}
    		}
    	}
    }
    
    static void view_simple(const unsigned char *str, int length)
    {
    	QRinput *input;
    	int ret;
    
    	if(micro) {
    		input = QRinput_newMQR(version, level);
    	} else {
    		input = QRinput_new2(version, level);
    	}
    	if(input == NULL) {
    		fprintf(stderr, "Memory allocation error.\n");
    		exit(EXIT_FAILURE);
    	}
    	if(eightbit) {
    		ret = QRinput_append(input, QR_MODE_8, length, str);
    	} else {
    		ret = Split_splitStringToQRinput((char *)str, input, hint, casesensitive);
    	}
    	if(ret < 0) {
    		perror("Encoding the input string");
    		exit(EXIT_FAILURE);
    	}
    
    	view(0, input);
    
    	QRinput_free(input);
    }
    
    static void view_multiText(char **argv, int argc)
    {
    	textc = argc;
    	textv = argv;
    
    	view(1, NULL);
    }
    
    int main(int argc, char **argv)
    {
    	int opt, lindex = -1;
    	unsigned char *intext = NULL;
    	int length = 0;
    	int ret;
    
    	while((opt = getopt_long(argc, argv, optstring, options, &lindex)) != -1) {
    		switch(opt) {
    			case 'h':
    				if(lindex == 0) {
    					usage(1, 1);
    				} else {
    					usage(1, 0);
    				}
    				exit(EXIT_SUCCESS);
    				break;
    			case 's':
    				size = atoi(optarg);
    				if(size <= 0) {
    					fprintf(stderr, "Invalid size: %d\n", size);
    					exit(EXIT_FAILURE);
    				}
    				break;
    			case 'v':
    				version = atoi(optarg);
    				if(version < 0) {
    					fprintf(stderr, "Invalid version: %d\n", version);
    					exit(EXIT_FAILURE);
    				}
    				break;
    			case 'l':
    				switch(*optarg) {
    					case 'l':
    					case 'L':
    						level = QR_ECLEVEL_L;
    						break;
    					case 'm':
    					case 'M':
    						level = QR_ECLEVEL_M;
    						break;
    					case 'q':
    					case 'Q':
    						level = QR_ECLEVEL_Q;
    						break;
    					case 'h':
    					case 'H':
    						level = QR_ECLEVEL_H;
    						break;
    					default:
    						fprintf(stderr, "Invalid level: %s\n", optarg);
    						exit(EXIT_FAILURE);
    						break;
    				}
    				break;
    			case 'm':
    				margin = atoi(optarg);
    				if(margin < 0) {
    					fprintf(stderr, "Invalid margin: %d\n", margin);
    					exit(EXIT_FAILURE);
    				}
    				break;
    			case 'S':
    				structured = 1;
    			case 'k':
    				hint = QR_MODE_KANJI;
    				break;
    			case 'c':
    				casesensitive = 1;
    				break;
    			case 'i':
    				casesensitive = 0;
    				break;
    			case '8':
    				eightbit = 1;
    				break;
    			case 'M':
    				micro = 1;
    				break;
    			case 'V':
    				usage(0, 0);
    				exit(EXIT_SUCCESS);
    				break;
    			default:
    				fprintf(stderr, "Try `view_qrcode --help' for more information.\n");
    				exit(EXIT_FAILURE);
    				break;
    		}
    	}
    
    	if(argc == 1) {
    		usage(1, 0);
    		exit(EXIT_SUCCESS);
    	}
    
    	if(optind < argc) {
    		intext = (unsigned char *)argv[optind];
    		length = strlen((char *)intext);
    	}
    	if(intext == NULL) {
    		intext = readStdin(&length);
    	}
    
    	if(micro && version > MQRSPEC_VERSION_MAX) {
    		fprintf(stderr, "Version should be less or equal to %d.\n", MQRSPEC_VERSION_MAX);
    		exit(EXIT_FAILURE);
    	} else if(!micro && version > QRSPEC_VERSION_MAX) {
    		fprintf(stderr, "Version should be less or equal to %d.\n", QRSPEC_VERSION_MAX);
    		exit(EXIT_FAILURE);
    	}
    
    	if(margin < 0) {
    		if(micro) {
    			margin = 2;
    		} else {
    			margin = 4;
    		}
    	}
    
    	if(micro) {
    		if(version == 0) {
    			fprintf(stderr, "Version must be specified to encode a Micro QR Code symbol.\n");
    			exit(EXIT_FAILURE);
    		}
    		if(structured) {
    			fprintf(stderr, "Micro QR Code does not support structured symbols.\n");
    			exit(EXIT_FAILURE);
    		}
    	}
    
    	if(structured && version == 0) {
    		fprintf(stderr, "Version must be specified to encode structured symbols.\n");
    		exit(EXIT_FAILURE);
    	}
    
    	if(SDL_Init(SDL_INIT_VIDEO) < 0) {
    		fprintf(stderr, "Failed initializing SDL: %s\n", SDL_GetError());
    		return -1;
    	}
    
    	ret = SDL_CreateWindowAndRenderer(100, 100, SDL_WINDOW_SHOWN, &window, &renderer);
    	if(ret < 0) {
    		fprintf(stderr, "Failed to create a window: %s\n", SDL_GetError());
    		return -1;
    	}
    
    	SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
    
    	if(structured && (argc - optind > 1)) {
    		view_multiText(argv + optind, argc - optind);
    	} else {
    		view_simple(intext, length);
    	}
    
    	SDL_Quit();
    
    	return 0;
    }