From 958df9f2e8d8bb60470142fbd8498de2af2f5658 Mon Sep 17 00:00:00 2001 From: Ansa89 Date: Wed, 5 Oct 2016 11:55:45 +0200 Subject: [PATCH] Add possibility to save QR code in PNG file format --- Makefile | 2 +- cfg/checks/check_features.mk | 6 ++ cfg/checks/qr_png.mk | 15 +++++ cfg/targets/help.mk | 1 + src/file_transfers.c | 4 +- src/global_commands.c | 47 ++++++++++----- src/help.c | 4 ++ src/line_info.c | 12 ++-- src/osx_video.h | 6 +- src/osx_video.m | 80 +++++++++++++------------ src/qr_code.c | 110 ++++++++++++++++++++++++++++++++++- src/qr_code.h | 14 ++++- 12 files changed, 234 insertions(+), 67 deletions(-) create mode 100644 cfg/checks/qr_png.mk diff --git a/Makefile b/Makefile index 9c55ae6..48fa71f 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ all: $(BUILD_DIR)/toxic $(BUILD_DIR)/toxic: $(OBJ) @echo " LD $(@:$(BUILD_DIR)/%=%)" - @$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS) + $(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS) $(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO) @echo " CC $(@:$(BUILD_DIR)/)osx_video.o" diff --git a/cfg/checks/check_features.mk b/cfg/checks/check_features.mk index b488b6c..f739cba 100644 --- a/cfg/checks/check_features.mk +++ b/cfg/checks/check_features.mk @@ -34,6 +34,12 @@ ifneq ($(DESK_NOTIFY), disabled) -include $(CHECKS_DIR)/desktop_notifications.mk endif +# Check if we want build QR exported as PNG support +QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi) +ifneq ($(QR_PNG), disabled) + -include $(CHECKS_DIR)/qr_png.mk +endif + # Check if we can build Toxic CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") ifneq ($(CHECK_LIBS), error) diff --git a/cfg/checks/qr_png.mk b/cfg/checks/qr_png.mk new file mode 100644 index 0000000..c3f20dc --- /dev/null +++ b/cfg/checks/qr_png.mk @@ -0,0 +1,15 @@ +# Variables for QR exported as PNG support +PNG_LIBS = libpng +PNG_CFLAGS = -DQRPNG + +# Check if we can build QR exported as PNG support +CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error") +ifneq ($(CHECK_PNG_LIBS), error) + LIBS += $(PNG_LIBS) + CFLAGS += $(PNG_CFLAGS) +else ifneq ($(MAKECMDGOALS), clean) + MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) + $(warning WARNING -- Toxic will be compiled without QR exported as PNG support) + $(warning WARNING -- You need these libraries for QR exported as PNG support) + $(warning WARNING -- $(MISSING_PNG_LIBS)) +endif diff --git a/cfg/targets/help.mk b/cfg/targets/help.mk index fba1c8f..b99c897 100644 --- a/cfg/targets/help.mk +++ b/cfg/targets/help.mk @@ -14,6 +14,7 @@ help: @echo " DISABLE_AV: Set to \"1\" to force building without audio call support" @echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support" @echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" + @echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support" @echo " USER_CFLAGS: Add custom flags to default CFLAGS" @echo " USER_LDFLAGS: Add custom flags to default LDFLAGS" @echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" diff --git a/src/file_transfers.c b/src/file_transfers.c index f3bc42b..b15c9e7 100644 --- a/src/file_transfers.c +++ b/src/file_transfers.c @@ -145,8 +145,8 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t for (i = 0; i < MAX_FILES; ++i) { struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? - &Friends.list[friendnum].file_sender[i] : - &Friends.list[friendnum].file_receiver[i]; + &Friends.list[friendnum].file_sender[i] : + &Friends.list[friendnum].file_receiver[i]; if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) return ft; diff --git a/src/global_commands.c b/src/global_commands.c index a153c87..144ddb2 100644 --- a/src/global_commands.c +++ b/src/global_commands.c @@ -131,7 +131,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg case TOX_ERR_FRIEND_ADD_NULL: - /* fallthrough */ + /* fallthrough */ default: errmsg = "Faile to add friend: Unknown error."; break; @@ -452,25 +452,42 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA char dir[data_file_len + 1]; size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); - char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1]; - snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); +#ifdef QRPNG - FILE *output = fopen(qr_path, "wb"); + if (argc == 0) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'"); + return; + } else if (!strcmp(argv[1], "txt")) { - if (output == NULL) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); +#endif /* QRPNG */ + char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1]; + snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); + + if (ID_to_QRcode_txt(id_string, qr_path) == -1) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); + return; + } + + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); + +#ifdef QRPNG + } else if (!strcmp(argv[1], "png")) { + char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1]; + snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); + + if (ID_to_QRcode_png(id_string, qr_path) == -1) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); + return; + } + + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); + + } else { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]); return; } - if (ID_to_QRcode(id_string, output) == -1) { - fclose(output); - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); - return; - } - - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); - - fclose(output); +#endif /* QRPNG */ } void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) diff --git a/src/help.c b/src/help.c index ced0747..13b127c 100644 --- a/src/help.c +++ b/src/help.c @@ -158,7 +158,11 @@ static void help_draw_global(ToxWindow *self) wprintw(win, " /log or : Enable/disable logging\n"); wprintw(win, " /group : Create a group chat where type: text | audio\n"); wprintw(win, " /myid : Print your Tox ID\n"); +#ifdef QRPNG + wprintw(win, " /myqr or : Print your Tox ID's QR code to a file.\n"); +#else wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n"); +#endif /* QRPNG */ wprintw(win, " /clear : Clear window history\n"); wprintw(win, " /close : Close the current chat window\n"); wprintw(win, " /quit or /exit : Exit Toxic\n"); diff --git a/src/line_info.c b/src/line_info.c index 66d8fba..c4103b1 100644 --- a/src/line_info.c +++ b/src/line_info.c @@ -159,14 +159,14 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons switch (type) { case IN_ACTION: - /* fallthrough */ + /* fallthrough */ case OUT_ACTION: len += strlen(user_settings->line_normal) + 2; break; case IN_MSG: - /* fallthrough */ + /* fallthrough */ case OUT_MSG: len += strlen(user_settings->line_normal) + 3; break; @@ -307,10 +307,10 @@ void line_info_print(ToxWindow *self) switch (type) { case OUT_MSG: - /* fallthrough */ + /* fallthrough */ case OUT_MSG_READ: - /* fallthrough */ + /* fallthrough */ case IN_MSG: wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); @@ -365,10 +365,10 @@ void line_info_print(ToxWindow *self) case OUT_ACTION_READ: - /* fallthrough */ + /* fallthrough */ case OUT_ACTION: - /* fallthrough */ + /* fallthrough */ case IN_ACTION: wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); diff --git a/src/osx_video.h b/src/osx_video.h index 413ba32..4a04b67 100644 --- a/src/osx_video.h +++ b/src/osx_video.h @@ -37,10 +37,10 @@ void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t * #ifdef __OBJC__ @interface OSXVideo : -NSObject + NSObject - (instancetype)initWithDeviceNames: -(char **)device_names AmtDevices: -(int *)size; + (char **)device_names AmtDevices: + (int *)size; @end #endif /* __OBJC__ */ diff --git a/src/osx_video.m b/src/osx_video.m index b745675..83a69a5 100644 --- a/src/osx_video.m +++ b/src/osx_video.m @@ -127,19 +127,20 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t } - (instancetype)initWithDeviceNames: -(char **)device_names AmtDevices: -(int *)size { + (char **)device_names AmtDevices: + (int *)size +{ _session = [[AVCaptureSession alloc] init]; -NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; + NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; int i; for (i = 0; i < [devices count]; ++i) { -AVCaptureDevice *device = [devices objectAtIndex: i]; + AVCaptureDevice *device = [devices objectAtIndex: i]; char *video_input_name; NSString *localizedName = [device localizedName]; -video_input_name = (char *)malloc(strlen([localizedName cStringUsingEncoding: NSUTF8StringEncoding]) + 1); -strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8StringEncoding]); + video_input_name = (char *)malloc(strlen([localizedName cStringUsingEncoding: NSUTF8StringEncoding]) + 1); + strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8StringEncoding]); device_names[i] = video_input_name; } @@ -151,7 +152,8 @@ strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8Stri return self; } -- (void)dealloc { +- (void)dealloc +{ pthread_mutex_destroy(&_frameLock); [_session release]; [_linkerVideo release]; @@ -160,16 +162,17 @@ strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8Stri } - (int)openVideoDeviceIndex: -(uint32_t)device_idx Width: -(uint16_t *)width Height: -(uint16_t *)height { + (uint32_t)device_idx Width: + (uint16_t *)width Height: + (uint16_t *)height +{ pthread_mutex_init(&_frameLock, NULL); pthread_mutex_lock(&_frameLock); _processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL); -NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; -AVCaptureDevice *device = [devices objectAtIndex: device_idx]; + NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; + AVCaptureDevice *device = [devices objectAtIndex: device_idx]; NSError *error = NULL; -AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; + AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; if ( error != NULL ) { [input release]; @@ -177,7 +180,7 @@ AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device err } [_session beginConfiguration]; -[_session addInput: input]; + [_session addInput: input]; //_session.sessionPreset = AVCaptureSessionPreset640x480; //*width = 640; //*height = 480; @@ -200,19 +203,20 @@ AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device err } _linkerVideo = [[AVCaptureVideoDataOutput alloc] init]; -[_linkerVideo setSampleBufferDelegate: self queue: _processingQueue]; + [_linkerVideo setSampleBufferDelegate: self queue: _processingQueue]; // TODO possibly get a better pixel format if (_shouldMangleDimensions) { -[_linkerVideo setVideoSettings: @ { + [_linkerVideo setVideoSettings: @ { (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), (id)kCVPixelBufferWidthKey: @640, (id)kCVPixelBufferHeightKey: @480 }]; } else { -[_linkerVideo setVideoSettings: @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}]; + [_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}]; } -[_session addOutput: _linkerVideo]; + + [_session addOutput: _linkerVideo]; [_session startRunning]; pthread_mutex_unlock(&_frameLock); @@ -220,21 +224,23 @@ AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device err } - (void)closeVideoDeviceIndex: -(uint32_t)device_idx { -NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; -AVCaptureDevice *device = [devices objectAtIndex: device_idx]; + (uint32_t)device_idx +{ + NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; + AVCaptureDevice *device = [devices objectAtIndex: device_idx]; NSError *error = NULL; -AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; + AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; [_session stopRunning]; -[_session removeOutput: _linkerVideo]; -[_session removeInput: input]; + [_session removeOutput: _linkerVideo]; + [_session removeInput: input]; [_linkerVideo release]; } - (void)captureOutput: -(AVCaptureOutput *)captureOutput didOutputSampleBuffer: -(CMSampleBufferRef)sampleBuffer fromConnection: -(AVCaptureConnection *)connection { + (AVCaptureOutput *)captureOutput didOutputSampleBuffer: + (CMSampleBufferRef)sampleBuffer fromConnection: + (AVCaptureConnection *)connection +{ pthread_mutex_lock(&_frameLock); CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer); @@ -248,15 +254,17 @@ AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device err // we're not going to do anything to it, so it's safe to lock it always CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly); } + pthread_mutex_unlock(&_frameLock); } - (int)getVideoFrameY: -(uint8_t *)y U: -(uint8_t *)u V: -(uint8_t *)v Width: -(uint16_t *)width Height: -(uint16_t *)height { + (uint8_t *)y U: + (uint8_t *)u V: + (uint8_t *)v Width: + (uint16_t *)width Height: + (uint16_t *)height +{ if (!_currentFrame) { return -1; } @@ -293,7 +301,7 @@ static OSXVideo *_OSXVideo = nil; int osx_video_init(char **device_names, int *size) { -_OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size]; + _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size]; if ( _OSXVideo == nil ) return -1; @@ -312,12 +320,12 @@ int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height) if ( _OSXVideo == nil ) return -1; -return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height]; + return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height]; } void osx_video_close_device(uint32_t device_idx) { -[_OSXVideo closeVideoDeviceIndex: device_idx]; + [_OSXVideo closeVideoDeviceIndex: device_idx]; } int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height) @@ -325,7 +333,7 @@ int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, u if ( _OSXVideo == nil ) return -1; -return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height]; + return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height]; } /* * End of C-interface for OSXVideo diff --git a/src/qr_code.c b/src/qr_code.c index 1341126..de31d95 100644 --- a/src/qr_code.c +++ b/src/qr_code.c @@ -28,18 +28,25 @@ #include "windows.h" #include "qr_code.h" +#ifdef QRPNG +#include +#define INCHES_PER_METER (100.0/2.54) +#endif /* QRPNG */ + #define BORDER_LEN 1 #define CHAR_1 "\342\226\210" #define CHAR_2 "\342\226\204" #define CHAR_3 "\342\226\200" -/* Converts a tox ID string into a QRcode and prints it to the given file stream. +/* Converts a tox ID string into a QRcode and prints it into the given filename. * * Returns 0 on success. * Returns -1 on failure. */ -int ID_to_QRcode(const char *tox_id, FILE *fp) +int ID_to_QRcode_txt(const char *tox_id, const char *outfile) { + FILE *fp = fopen(outfile, "wb"); + if (fp == NULL) return -1; @@ -83,7 +90,106 @@ int ID_to_QRcode(const char *tox_id, FILE *fp) fprintf(fp, "\n"); } + fclose(fp); QRcode_free(qr_obj); return 0; } + +#ifdef QRPNG +/* Converts a tox ID string into a QRcode and prints it into the given filename as png. + * + * Returns 0 on success. + * Returns -1 on failure. + */ +int ID_to_QRcode_png(const char *tox_id, const char *outfile) +{ + static FILE *fp; + unsigned char *row, *p; + unsigned char black[4] = {0, 0, 0, 255}; + size_t x, y, xx, yy, real_width; + size_t margin = BORDER_LEN; + size_t size = 5; + size_t dpi = 72; + png_structp png_ptr; + png_infop info_ptr; + + fp = fopen(outfile, "wb"); + + if (fp == NULL) + return -1; + + QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0); + + if (qr_obj == NULL) + return -1; + + real_width = (qr_obj->width + margin * 2) * size; + row = malloc(real_width * 4); + + if (row == NULL) + return -1; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) + return -1; + + info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr == NULL) + return -1; + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return -1; + } + + png_init_io(png_ptr, fp); + png_set_IHDR(png_ptr, info_ptr, real_width, real_width, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_pHYs(png_ptr, info_ptr, dpi * INCHES_PER_METER, + dpi * INCHES_PER_METER, PNG_RESOLUTION_METER); + png_write_info(png_ptr, info_ptr); + + /* top margin */ + memset(row, 0xff, real_width * 4); + + for (y = 0; y < margin * size; y++) + png_write_row(png_ptr, row); + + /* data */ + p = qr_obj->data; + + for (y = 0; y < qr_obj->width; y++) { + memset(row, 0xff, real_width * 4); + + for (x = 0; x < qr_obj->width; x++) { + for (xx = 0; xx < size; xx++) + if (*p & 1) + memcpy(&row[((margin + x) * size + xx) * 4], black, 4); + + p++; + } + + for (yy = 0; yy < size; yy++) + png_write_row(png_ptr, row); + } + + /* bottom margin */ + memset(row, 0xff, real_width * 4); + + for (y = 0; y < margin * size; y++) + png_write_row(png_ptr, row); + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + free(row); + QRcode_free(qr_obj); + + return 0; +} +#endif /* QRPNG */ diff --git a/src/qr_code.h b/src/qr_code.h index eb94ddf..f33724f 100644 --- a/src/qr_code.h +++ b/src/qr_code.h @@ -25,11 +25,21 @@ #define QRCODE_FILENAME_EXT ".QRcode" -/* Converts a tox ID string into a QRcode and prints it to the given file stream. +/* Converts a tox ID string into a QRcode and prints it into the given filename. * * Returns 0 on success. * Returns -1 on failure. */ -int ID_to_QRcode(const char *tox_id, FILE *fp); +int ID_to_QRcode_txt(const char *tox_id, const char *outfile); + +#ifdef QRPNG +#define QRCODE_FILENAME_EXT_PNG ".QRcode.png" +/* Converts a tox ID string into a QRcode and prints it into the given filename as png. + * + * Returns 0 on success. + * Returns -1 on failure. + */ +int ID_to_QRcode_png(const char *tox_id, const char *outfile); +#endif /* QRPNG */ #endif /* QR_CODE */