Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/Signal/test/QRCodes/BitmapsImageParsingTest.swift
1 views
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import XCTest
@testable import Signal

/// Do bitmap images parse correctly from other images?
class BitmapsImageParsingTest: XCTestCase {
    func testKnownQRCode() {
        let knownQRCodePixels: [(Int, Int, Bool)] = [
            (0, 26, false),
            (1, 26, false),
            (2, 26, false),
            (3, 26, false),
            (4, 26, false),
            (5, 26, false),
            (6, 26, false),
            (7, 26, false),
            (8, 26, false),
            (9, 26, false),
            (10, 26, false),
            (11, 26, false),
            (12, 26, false),
            (13, 26, false),
            (14, 26, false),
            (15, 26, false),
            (16, 26, false),
            (17, 26, false),
            (18, 26, false),
            (19, 26, false),
            (20, 26, false),
            (21, 26, false),
            (22, 26, false),
            (23, 26, false),
            (24, 26, false),
            (25, 26, false),
            (26, 26, false),
            (0, 25, false),
            (1, 25, true),
            (2, 25, true),
            (3, 25, true),
            (4, 25, true),
            (5, 25, true),
            (6, 25, true),
            (7, 25, true),
            (8, 25, false),
            (9, 25, true),
            (10, 25, false),
            (11, 25, false),
            (12, 25, false),
            (13, 25, false),
            (14, 25, true),
            (15, 25, true),
            (16, 25, true),
            (17, 25, true),
            (18, 25, false),
            (19, 25, true),
            (20, 25, true),
            (21, 25, true),
            (22, 25, true),
            (23, 25, true),
            (24, 25, true),
            (25, 25, true),
            (26, 25, false),
            (0, 24, false),
            (1, 24, true),
            (2, 24, false),
            (3, 24, false),
            (4, 24, false),
            (5, 24, false),
            (6, 24, false),
            (7, 24, true),
            (8, 24, false),
            (9, 24, true),
            (10, 24, false),
            (11, 24, false),
            (12, 24, true),
            (13, 24, true),
            (14, 24, false),
            (15, 24, false),
            (16, 24, false),
            (17, 24, false),
            (18, 24, false),
            (19, 24, true),
            (20, 24, false),
            (21, 24, false),
            (22, 24, false),
            (23, 24, false),
            (24, 24, false),
            (25, 24, true),
            (26, 24, false),
            (0, 23, false),
            (1, 23, true),
            (2, 23, false),
            (3, 23, true),
            (4, 23, true),
            (5, 23, true),
            (6, 23, false),
            (7, 23, true),
            (8, 23, false),
            (9, 23, true),
            (10, 23, true),
            (11, 23, false),
            (12, 23, false),
            (13, 23, true),
            (14, 23, false),
            (15, 23, true),
            (16, 23, false),
            (17, 23, true),
            (18, 23, false),
            (19, 23, true),
            (20, 23, false),
            (21, 23, true),
            (22, 23, true),
            (23, 23, true),
            (24, 23, false),
            (25, 23, true),
            (26, 23, false),
            (0, 22, false),
            (1, 22, true),
            (2, 22, false),
            (3, 22, true),
            (4, 22, true),
            (5, 22, true),
            (6, 22, false),
            (7, 22, true),
            (8, 22, false),
            (9, 22, true),
            (10, 22, true),
            (11, 22, true),
            (12, 22, false),
            (13, 22, true),
            (14, 22, false),
            (15, 22, false),
            (16, 22, true),
            (17, 22, false),
            (18, 22, false),
            (19, 22, true),
            (20, 22, false),
            (21, 22, true),
            (22, 22, true),
            (23, 22, true),
            (24, 22, false),
            (25, 22, true),
            (26, 22, false),
            (0, 21, false),
            (1, 21, true),
            (2, 21, false),
            (3, 21, true),
            (4, 21, true),
            (5, 21, true),
            (6, 21, false),
            (7, 21, true),
            (8, 21, false),
            (9, 21, false),
            (10, 21, true),
            (11, 21, true),
            (12, 21, false),
            (13, 21, true),
            (14, 21, false),
            (15, 21, true),
            (16, 21, true),
            (17, 21, true),
            (18, 21, false),
            (19, 21, true),
            (20, 21, false),
            (21, 21, true),
            (22, 21, true),
            (23, 21, true),
            (24, 21, false),
            (25, 21, true),
            (26, 21, false),
            (0, 20, false),
            (1, 20, true),
            (2, 20, false),
            (3, 20, false),
            (4, 20, false),
            (5, 20, false),
            (6, 20, false),
            (7, 20, true),
            (8, 20, false),
            (9, 20, true),
            (10, 20, true),
            (11, 20, true),
            (12, 20, false),
            (13, 20, false),
            (14, 20, false),
            (15, 20, false),
            (16, 20, false),
            (17, 20, true),
            (18, 20, false),
            (19, 20, true),
            (20, 20, false),
            (21, 20, false),
            (22, 20, false),
            (23, 20, false),
            (24, 20, false),
            (25, 20, true),
            (26, 20, false),
            (0, 19, false),
            (1, 19, true),
            (2, 19, true),
            (3, 19, true),
            (4, 19, true),
            (5, 19, true),
            (6, 19, true),
            (7, 19, true),
            (8, 19, false),
            (9, 19, true),
            (10, 19, false),
            (11, 19, true),
            (12, 19, false),
            (13, 19, true),
            (14, 19, false),
            (15, 19, true),
            (16, 19, false),
            (17, 19, true),
            (18, 19, false),
            (19, 19, true),
            (20, 19, true),
            (21, 19, true),
            (22, 19, true),
            (23, 19, true),
            (24, 19, true),
            (25, 19, true),
            (26, 19, false),
            (0, 18, false),
            (1, 18, false),
            (2, 18, false),
            (3, 18, false),
            (4, 18, false),
            (5, 18, false),
            (6, 18, false),
            (7, 18, false),
            (8, 18, false),
            (9, 18, false),
            (10, 18, false),
            (11, 18, true),
            (12, 18, true),
            (13, 18, false),
            (14, 18, true),
            (15, 18, false),
            (16, 18, false),
            (17, 18, false),
            (18, 18, false),
            (19, 18, false),
            (20, 18, false),
            (21, 18, false),
            (22, 18, false),
            (23, 18, false),
            (24, 18, false),
            (25, 18, false),
            (26, 18, false),
            (0, 17, false),
            (1, 17, true),
            (2, 17, true),
            (3, 17, false),
            (4, 17, false),
            (5, 17, true),
            (6, 17, true),
            (7, 17, true),
            (8, 17, false),
            (9, 17, false),
            (10, 17, false),
            (11, 17, true),
            (12, 17, false),
            (13, 17, true),
            (14, 17, true),
            (15, 17, false),
            (16, 17, true),
            (17, 17, false),
            (18, 17, false),
            (19, 17, false),
            (20, 17, true),
            (21, 17, false),
            (22, 17, true),
            (23, 17, true),
            (24, 17, true),
            (25, 17, true),
            (26, 17, false),
            (0, 16, false),
            (1, 16, false),
            (2, 16, false),
            (3, 16, false),
            (4, 16, true),
            (5, 16, false),
            (6, 16, true),
            (7, 16, false),
            (8, 16, false),
            (9, 16, false),
            (10, 16, false),
            (11, 16, false),
            (12, 16, false),
            (13, 16, false),
            (14, 16, true),
            (15, 16, true),
            (16, 16, true),
            (17, 16, true),
            (18, 16, false),
            (19, 16, false),
            (20, 16, false),
            (21, 16, true),
            (22, 16, true),
            (23, 16, false),
            (24, 16, true),
            (25, 16, false),
            (26, 16, false),
            (0, 15, false),
            (1, 15, false),
            (2, 15, true),
            (3, 15, true),
            (4, 15, true),
            (5, 15, true),
            (6, 15, true),
            (7, 15, true),
            (8, 15, true),
            (9, 15, false),
            (10, 15, true),
            (11, 15, true),
            (12, 15, false),
            (13, 15, false),
            (14, 15, false),
            (15, 15, false),
            (16, 15, true),
            (17, 15, true),
            (18, 15, true),
            (19, 15, true),
            (20, 15, true),
            (21, 15, true),
            (22, 15, true),
            (23, 15, true),
            (24, 15, false),
            (25, 15, false),
            (26, 15, false),
            (0, 14, false),
            (1, 14, true),
            (2, 14, false),
            (3, 14, false),
            (4, 14, true),
            (5, 14, false),
            (6, 14, false),
            (7, 14, false),
            (8, 14, false),
            (9, 14, false),
            (10, 14, true),
            (11, 14, false),
            (12, 14, false),
            (13, 14, true),
            (14, 14, false),
            (15, 14, true),
            (16, 14, false),
            (17, 14, false),
            (18, 14, false),
            (19, 14, false),
            (20, 14, false),
            (21, 14, false),
            (22, 14, false),
            (23, 14, true),
            (24, 14, true),
            (25, 14, false),
            (26, 14, false),
            (0, 13, false),
            (1, 13, false),
            (2, 13, true),
            (3, 13, false),
            (4, 13, true),
            (5, 13, false),
            (6, 13, false),
            (7, 13, true),
            (8, 13, true),
            (9, 13, true),
            (10, 13, false),
            (11, 13, false),
            (12, 13, true),
            (13, 13, false),
            (14, 13, true),
            (15, 13, true),
            (16, 13, true),
            (17, 13, false),
            (18, 13, true),
            (19, 13, true),
            (20, 13, false),
            (21, 13, false),
            (22, 13, true),
            (23, 13, true),
            (24, 13, true),
            (25, 13, true),
            (26, 13, false),
            (0, 12, false),
            (1, 12, true),
            (2, 12, false),
            (3, 12, false),
            (4, 12, true),
            (5, 12, true),
            (6, 12, false),
            (7, 12, false),
            (8, 12, true),
            (9, 12, false),
            (10, 12, true),
            (11, 12, true),
            (12, 12, false),
            (13, 12, true),
            (14, 12, false),
            (15, 12, false),
            (16, 12, true),
            (17, 12, false),
            (18, 12, false),
            (19, 12, false),
            (20, 12, false),
            (21, 12, true),
            (22, 12, false),
            (23, 12, false),
            (24, 12, true),
            (25, 12, false),
            (26, 12, false),
            (0, 11, false),
            (1, 11, false),
            (2, 11, false),
            (3, 11, false),
            (4, 11, false),
            (5, 11, true),
            (6, 11, true),
            (7, 11, true),
            (8, 11, false),
            (9, 11, false),
            (10, 11, true),
            (11, 11, false),
            (12, 11, true),
            (13, 11, true),
            (14, 11, true),
            (15, 11, true),
            (16, 11, true),
            (17, 11, true),
            (18, 11, false),
            (19, 11, false),
            (20, 11, true),
            (21, 11, true),
            (22, 11, true),
            (23, 11, true),
            (24, 11, false),
            (25, 11, false),
            (26, 11, false),
            (0, 10, false),
            (1, 10, false),
            (2, 10, false),
            (3, 10, true),
            (4, 10, true),
            (5, 10, true),
            (6, 10, false),
            (7, 10, false),
            (8, 10, false),
            (9, 10, false),
            (10, 10, true),
            (11, 10, false),
            (12, 10, true),
            (13, 10, false),
            (14, 10, true),
            (15, 10, false),
            (16, 10, true),
            (17, 10, false),
            (18, 10, false),
            (19, 10, true),
            (20, 10, true),
            (21, 10, true),
            (22, 10, false),
            (23, 10, true),
            (24, 10, true),
            (25, 10, false),
            (26, 10, false),
            (0, 9, false),
            (1, 9, true),
            (2, 9, true),
            (3, 9, false),
            (4, 9, false),
            (5, 9, true),
            (6, 9, true),
            (7, 9, true),
            (8, 9, false),
            (9, 9, false),
            (10, 9, true),
            (11, 9, true),
            (12, 9, false),
            (13, 9, true),
            (14, 9, true),
            (15, 9, false),
            (16, 9, true),
            (17, 9, true),
            (18, 9, true),
            (19, 9, true),
            (20, 9, true),
            (21, 9, true),
            (22, 9, true),
            (23, 9, true),
            (24, 9, false),
            (25, 9, false),
            (26, 9, false),
            (0, 8, false),
            (1, 8, false),
            (2, 8, false),
            (3, 8, false),
            (4, 8, false),
            (5, 8, false),
            (6, 8, false),
            (7, 8, false),
            (8, 8, false),
            (9, 8, true),
            (10, 8, false),
            (11, 8, true),
            (12, 8, false),
            (13, 8, false),
            (14, 8, true),
            (15, 8, true),
            (16, 8, false),
            (17, 8, true),
            (18, 8, false),
            (19, 8, false),
            (20, 8, false),
            (21, 8, true),
            (22, 8, false),
            (23, 8, false),
            (24, 8, false),
            (25, 8, false),
            (26, 8, false),
            (0, 7, false),
            (1, 7, true),
            (2, 7, true),
            (3, 7, true),
            (4, 7, true),
            (5, 7, true),
            (6, 7, true),
            (7, 7, true),
            (8, 7, false),
            (9, 7, false),
            (10, 7, false),
            (11, 7, false),
            (12, 7, false),
            (13, 7, false),
            (14, 7, false),
            (15, 7, false),
            (16, 7, false),
            (17, 7, true),
            (18, 7, false),
            (19, 7, true),
            (20, 7, false),
            (21, 7, true),
            (22, 7, false),
            (23, 7, false),
            (24, 7, false),
            (25, 7, false),
            (26, 7, false),
            (0, 6, false),
            (1, 6, true),
            (2, 6, false),
            (3, 6, false),
            (4, 6, false),
            (5, 6, false),
            (6, 6, false),
            (7, 6, true),
            (8, 6, false),
            (9, 6, true),
            (10, 6, false),
            (11, 6, true),
            (12, 6, false),
            (13, 6, true),
            (14, 6, false),
            (15, 6, true),
            (16, 6, false),
            (17, 6, true),
            (18, 6, false),
            (19, 6, false),
            (20, 6, false),
            (21, 6, true),
            (22, 6, true),
            (23, 6, true),
            (24, 6, true),
            (25, 6, true),
            (26, 6, false),
            (0, 5, false),
            (1, 5, true),
            (2, 5, false),
            (3, 5, true),
            (4, 5, true),
            (5, 5, true),
            (6, 5, false),
            (7, 5, true),
            (8, 5, false),
            (9, 5, true),
            (10, 5, false),
            (11, 5, true),
            (12, 5, true),
            (13, 5, false),
            (14, 5, true),
            (15, 5, true),
            (16, 5, false),
            (17, 5, true),
            (18, 5, true),
            (19, 5, true),
            (20, 5, true),
            (21, 5, true),
            (22, 5, true),
            (23, 5, true),
            (24, 5, false),
            (25, 5, true),
            (26, 5, false),
            (0, 4, false),
            (1, 4, true),
            (2, 4, false),
            (3, 4, true),
            (4, 4, true),
            (5, 4, true),
            (6, 4, false),
            (7, 4, true),
            (8, 4, false),
            (9, 4, false),
            (10, 4, false),
            (11, 4, true),
            (12, 4, false),
            (13, 4, true),
            (14, 4, false),
            (15, 4, false),
            (16, 4, true),
            (17, 4, false),
            (18, 4, true),
            (19, 4, true),
            (20, 4, true),
            (21, 4, false),
            (22, 4, false),
            (23, 4, true),
            (24, 4, true),
            (25, 4, true),
            (26, 4, false),
            (0, 3, false),
            (1, 3, true),
            (2, 3, false),
            (3, 3, true),
            (4, 3, true),
            (5, 3, true),
            (6, 3, false),
            (7, 3, true),
            (8, 3, false),
            (9, 3, false),
            (10, 3, false),
            (11, 3, false),
            (12, 3, true),
            (13, 3, true),
            (14, 3, true),
            (15, 3, true),
            (16, 3, true),
            (17, 3, true),
            (18, 3, true),
            (19, 3, true),
            (20, 3, false),
            (21, 3, false),
            (22, 3, true),
            (23, 3, false),
            (24, 3, true),
            (25, 3, false),
            (26, 3, false),
            (0, 2, false),
            (1, 2, true),
            (2, 2, false),
            (3, 2, false),
            (4, 2, false),
            (5, 2, false),
            (6, 2, false),
            (7, 2, true),
            (8, 2, false),
            (9, 2, true),
            (10, 2, true),
            (11, 2, true),
            (12, 2, true),
            (13, 2, false),
            (14, 2, true),
            (15, 2, false),
            (16, 2, true),
            (17, 2, false),
            (18, 2, false),
            (19, 2, true),
            (20, 2, true),
            (21, 2, true),
            (22, 2, true),
            (23, 2, true),
            (24, 2, true),
            (25, 2, false),
            (26, 2, false),
            (0, 1, false),
            (1, 1, true),
            (2, 1, true),
            (3, 1, true),
            (4, 1, true),
            (5, 1, true),
            (6, 1, true),
            (7, 1, true),
            (8, 1, false),
            (9, 1, true),
            (10, 1, false),
            (11, 1, false),
            (12, 1, false),
            (13, 1, true),
            (14, 1, true),
            (15, 1, false),
            (16, 1, true),
            (17, 1, false),
            (18, 1, false),
            (19, 1, false),
            (20, 1, false),
            (21, 1, false),
            (22, 1, false),
            (23, 1, true),
            (24, 1, true),
            (25, 1, true),
            (26, 1, false),
            (0, 0, false),
            (1, 0, false),
            (2, 0, false),
            (3, 0, false),
            (4, 0, false),
            (5, 0, false),
            (6, 0, false),
            (7, 0, false),
            (8, 0, false),
            (9, 0, false),
            (10, 0, false),
            (11, 0, false),
            (12, 0, false),
            (13, 0, false),
            (14, 0, false),
            (15, 0, false),
            (16, 0, false),
            (17, 0, false),
            (18, 0, false),
            (19, 0, false),
            (20, 0, false),
            (21, 0, false),
            (22, 0, false),
            (23, 0, false),
            (24, 0, false),
            (25, 0, false),
        ]

        let bitmap = Bitmaps.Image(cgImage: .signalDotOrgQRCode)!

        for knownQRCodePixel in knownQRCodePixels {
            let x = knownQRCodePixel.0
            let y = knownQRCodePixel.1
            let hasPixel = knownQRCodePixel.2

            XCTAssertEqual(
                bitmap.hasVisiblePixel(at: Bitmaps.Point(x: x, y: y)),
                hasPixel,
            )
        }
    }

    func testSolidBluePng() {
        let bitmap = Bitmaps.Image(cgImage: .solidBluePng)!
        let bluePixel = Bitmaps.Image.Pixel(r: 58, g: 118, b: 240, a: 255)

        for x in 0..<bitmap.width {
            for y in 0..<bitmap.height {
                XCTAssertEqual(
                    bitmap.pixel(at: Bitmaps.Point(x: x, y: y)),
                    bluePixel,
                )
            }
        }
    }

    func testTransparency() {
        let bitmap = Bitmaps.Image(cgImage: .topHalfSemitransparentBluePng)!

        XCTAssertEqual(
            bitmap.pixel(at: Bitmaps.Point(x: 4, y: 4)),
            Bitmaps.Image.Pixel(r: 58, g: 118, b: 240, a: 255),
        )

        XCTAssertEqual(
            bitmap.pixel(at: Bitmaps.Point(x: 4, y: 24)),
            Bitmaps.Image.Pixel(r: 29, g: 59, b: 120, a: 127),
        )
    }
}

private extension CGImage {
    static let signalDotOrgQRCode: CGImage = {
        let urlData = Data("https://signal.org".utf8)

        let filter = CIFilter(name: "CIQRCodeGenerator")!
        filter.setValue("L", forKey: "inputCorrectionLevel")
        filter.setValue(urlData, forKey: "inputMessage")

        let ciImage = filter.outputImage!

        let colorParameters = [
            "inputColor0": CIColor(color: .black),
            "inputColor1": CIColor(color: .clear),
        ]

        let recoloredCIImage = ciImage.applyingFilter("CIFalseColor", parameters: colorParameters)

        let context = CIContext(options: nil)
        return context.createCGImage(
            recoloredCIImage,
            from: ciImage.extent,
        )!
    }()

    static let solidBluePng: CGImage = {
        return UIImage(
            named: "blue-rectangle",
            in: Bundle(for: BitmapsImageParsingTest.self),
            compatibleWith: nil,
        )!.cgImage!
    }()

    static let topHalfSemitransparentBluePng: CGImage = {
        return UIImage(
            named: "semitransparent",
            in: Bundle(for: BitmapsImageParsingTest.self),
            compatibleWith: nil,
        )!.cgImage!
    }()
}