diff --git a/main/data/theme.css b/main/data/theme.css index 3e076248..059585b7 100644 --- a/main/data/theme.css +++ b/main/data/theme.css @@ -369,4 +369,8 @@ box.dino-input-error label.input-status-highlight-once { .dino-call-window .own-video { box-shadow: 0 0 2px 0 rgba(0,0,0,0.5); -} \ No newline at end of file +} + +.qrcode-container { + background: white; /* Color of the quiet zone. MUST have the same "reflectance" as light modules of the QR code. */ +} diff --git a/plugins/omemo/data/contact_details_dialog.ui b/plugins/omemo/data/contact_details_dialog.ui index 188bf06e..62aded6b 100644 --- a/plugins/omemo/data/contact_details_dialog.ui +++ b/plugins/omemo/data/contact_details_dialog.ui @@ -278,7 +278,6 @@ True - 10 True diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index b268cc13..18884784 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -92,20 +92,14 @@ public class ContactDetailsDialog : Gtk.Dialog { copy_button.clicked.connect(() => {Clipboard.get_default(get_display()).set_text(fingerprint, fingerprint.length);}); int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; - Pixbuf qr_pixbuf = new QRcode(@"xmpp:$(account.bare_jid)?omemo-sid-$(sid)=$(fingerprint)", 2).to_pixbuf(); - qr_pixbuf = qr_pixbuf.scale_simple(150, 150, InterpType.NEAREST); - Pixbuf pixbuf = new Pixbuf( - qr_pixbuf.colorspace, - qr_pixbuf.has_alpha, - qr_pixbuf.bits_per_sample, - 170, - 170 - ); - pixbuf.fill(uint32.MAX); - qr_pixbuf.copy_area(0, 0, 150, 150, pixbuf, 10, 10); + const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 + const int MODULE_SIZE_PX = 4; // arbitrary + Pixbuf qr_pixbuf = new QRcode(@"xmpp:$(account.bare_jid)?omemo-sid-$(sid)=$(fingerprint)", 2).to_pixbuf(MODULE_SIZE_PX); + qrcode_image.set_from_pixbuf(qr_pixbuf); + qrcode_image.margin = QUIET_ZONE_MODULES*MODULE_SIZE_PX; + qrcode_popover.get_style_context().add_class("qrcode-container"); - qrcode_image.set_from_pixbuf(pixbuf); show_qrcode_button.clicked.connect(qrcode_popover.popup); } diff --git a/plugins/omemo/vapi/libqrencode.vapi b/plugins/omemo/vapi/libqrencode.vapi index fc77c855..253e239a 100644 --- a/plugins/omemo/vapi/libqrencode.vapi +++ b/plugins/omemo/vapi/libqrencode.vapi @@ -36,15 +36,28 @@ namespace Qrencode { [CCode (cname = "QRcode_encodeString")] public QRcode (string str, int version = 0, ECLevel level = ECLevel.L, EncodeMode hint = EncodeMode.EIGHT_BIT, bool casesensitive = true); - public Pixbuf to_pixbuf() { - uint8[] bitmap = new uint8[3*width*width]; - for (int i = 0; i < width*width; i++) { - uint8 color = (data[i] & 1) == 1 ? 0 : 255; - bitmap[i*3] = color; - bitmap[i*3+1] = color; - bitmap[i*3+2] = color; + public Pixbuf to_pixbuf(int module_size) { + GLib.assert(module_size > 0); + var src_w = width; + var src = data[0:width*width]; + var dst_w = src_w*module_size; + var dst = new uint8[dst_w*dst_w*3]; + for (int src_y = 0; src_y < src_w; src_y++) { + for (int repeat_y = 0; repeat_y < module_size; repeat_y++) { + var dst_y = src_y*module_size + repeat_y; + for (int src_x = 0; src_x < src_w; src_x++) { + uint8 color = (src[src_y*src_w + src_x] & 1) == 1 ? 0 : 255; + for (int repeat_x = 0; repeat_x < module_size; repeat_x++) { + var dst_x = src_x*module_size + repeat_x; + var px_idx = dst_y*dst_w + dst_x; + dst[px_idx*3+0] = color; + dst[px_idx*3+1] = color; + dst[px_idx*3+2] = color; + } + } + } } - return new Pixbuf.from_data(bitmap, Colorspace.RGB, false, 8, width, width, width*3); + return new Pixbuf.from_data(dst, Colorspace.RGB, false, 8, dst_w, dst_w, dst_w*3); } } }