Module values

Basics

The QR Code matrix is a 2-dimensional array of numerical values that hold a bitmask for each QR pixel (“module” as per specification), the so-called “module type” or $M_TYPE, which is represented by the QRMatrix::M_* constants. The bitmask is 12 bits wide; the first 11 bits stand for the pattern type, the highest bit designates whether the module is dark or light. You can assign different values for the several function patterns to colorize them or even draw pixel-art.

Assigning values

To map the values and properly render the modules for the given QROutputInterface, it may be necessary to overwrite the default values, that are replaced by the user defined values in QROptions::$moduleValues during the render process.

The map of QRMatrix::M_* constants => default values looks similar to the following (values with “_DARK” and “_LIGHT” suffix are for convenience):

$options->moduleValues = [
	// light
	QRMatrix::M_NULL             => false, // special value - always 0
	QRMatrix::M_DARKMODULE_LIGHT => false, // key equivalent to (QRMatrix::M_DARKMODULE & ~QRMatrix::IS_DARK)
	QRMatrix::M_DATA             => false,
	QRMatrix::M_FINDER           => false,
	QRMatrix::M_SEPARATOR        => false,
	QRMatrix::M_ALIGNMENT        => false,
	QRMatrix::M_TIMING           => false,
	QRMatrix::M_FORMAT           => false,
	QRMatrix::M_VERSION          => false,
	QRMatrix::M_QUIETZONE        => false,
	QRMatrix::M_LOGO             => false,
	QRMatrix::M_FINDER_DOT_LIGHT => false,
	// dark
	QRMatrix::M_DARKMODULE       => true,
	QRMatrix::M_DATA_DARK        => true, // key equivalent to (QRMatrix::M_DATA | QRMatrix::IS_DARK)
	QRMatrix::M_FINDER_DARK      => true,
	QRMatrix::M_SEPARATOR_DARK   => true,
	QRMatrix::M_ALIGNMENT_DARK   => true,
	QRMatrix::M_TIMING_DARK      => true,
	QRMatrix::M_FORMAT_DARK      => true,
	QRMatrix::M_VERSION_DARK     => true,
	QRMatrix::M_QUIETZONE_DARK   => true,
	QRMatrix::M_LOGO_DARK        => true,
	QRMatrix::M_FINDER_DOT       => true,
];

Not all the module values need to be specified - missing values will be filled with the internal default values for true (dark) and false (light) respectively. The QROutputInterface inheritors implement a moduleValueIsValid() method that checks if the given value is valid for that particular class:

// set an initial value that acts as default
$dark = 'rgba(0, 0, 0, 0.5)';

// try to receive user input
if(QRMarkupSVG::moduleValueIsValid($_GET['qr_dark'])){
	// module values for HTML, SVG and other markup may need special treatment,
	// e.g. only accept hexadecimal values from user input
	// as moduleValueIsValid() just checks for the general syntax
	$dark = sanitize_user_input($_GET['qr_dark']);
}

$options->moduleValues = [
	QRMatrix::M_DATA_DARK      => $dark,
	QRMatrix::M_FINDER_DARK    => $dark,
	QRMatrix::M_ALIGNMENT_DARK => $dark,
	QRMatrix::M_FINDER_DOT     => $dark,
];

The several output classes may need different substitute values (you can find examples in the test moduleValueProvider() for each output class):

// for HTML, SVG and ImageMagick
$options->moduleValues = [
	QRMatrix::M_DATA      => '#ffffff',
	QRMatrix::M_DATA_DARK => '#000000',
	// ...
];

// for the GdImage, EPS and FPDF output types
$options->moduleValues = [
	QRMatrix::M_DATA      => [255, 255, 255],
	QRMatrix::M_DATA_DARK => [0, 0, 0],
	// ...
];

// for string/text output
$options->moduleValues = [
	QRMatrix::M_DATA      => '░░',
	QRMatrix::M_DATA_DARK => '██',
	// ...
];

Handling in your own QROutputInterface

Setting module values

QROutputAbstract::setModuleValues() calls the 3 abstract methods moduleValueIsValid(), getModuleValue() and getDefaultModuleValue() to fill the internal module value map with the values given via QROptions::$moduleValues:

protected function setModuleValues():void{

	foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
		$value = ($this->options->moduleValues[$M_TYPE] ?? null);

		$this->moduleValues[$M_TYPE] = $this->moduleValueIsValid($value)
			? $this->getModuleValue($value)
			: $this->getDefaultModuleValue($defaultValue);
	}

}

In the following example we’ll create these methods for the GdImage output. Since imagecolorallocate() and other GD functions accept 3 values for RGB color (or 4 in case of RGBA), we’ll supply these as a array where each value is an integer between 0 and 255 ([RRR, GGG, BBB, (, AAA)]).

First we need to validate the input:

protected function moduleValueIsValid($value):bool{

	// nowhere near valid
	if(!is_array($value) || count($value) !== 3){
		return false;
	}

	// now iterate over the values
	foreach($value as $color){

		// non-integers won't work
		if(!is_int($color)){
			return false;
		}

		// a strict check - we could also just ignore outliers and clamp the values instead
		if($color < 0 || $color > 255){
			return false;
		}
	}

	return true; // yay!
}

Now we can prepare the value:

protected function getModuleValue($value):array{
	// we call array_values() so we don't run into string-key related issues
	return array_map(fn(int $val):int => max(0, min(255, $val)), array_values($value));
}

And finally we need to provide default values:

protected function getDefaultModuleValue(bool $isDark):array{
	return $isDark ? [0, 0, 0] : [255, 255, 255];
}

Now that everything is ready and set, we can use the values in our GD functions:

$color = imagecolorallocate($this->image, ...$this->moduleValues[$M_TYPE]);

Using the module values

The state of the $M_TYPE is set with the QRMatrix::IS_DARK constant:

// set to dark (true) with bitwise OR:
$M_TYPE = ($M_TYPE | QRMatrix::IS_DARK);

// set to light (false) with bitwise AND NOT
$M_TYPE = ($M_TYPE & ~QRMatrix::IS_DARK);

// toggle the opposite state with bitwise XOR
$M_TYPE = ($M_TYPE ^ QRMatrix::IS_DARK);

You can manually check whether the module is dark:

($value & QRMatrix::IS_DARK) === QRMatrix::IS_DARK;

However it is much more convenient to use the QRMatrix methods for that:

for($y = 0; $y < $this->moduleCount; $y++){ // rows
	for($x = 0; $x < $this->moduleCount; $x++){ // columns
		// sets current module as dark (true) with the M_DATA type
		$this->matrix->set($x, $y, true, QRMatrix::M_DATA);

		// -> true (shortcut for checkType($x, $y, QRMatrix::IS_DARK))
		$this->matrix->check($x, $y);

		// -> true (current module is of type M_DATA)
		$this->matrix->checkType($x, $y, QRMatrix::M_DATA);

		// -> true (current module is of type IS_DARK)
		$this->matrix->checkType($x, $y, QRMatrix::IS_DARK);

		// -> false, type is M_DATA
		$this->matrix->checkTypeIn($x, $y, [QRMatrix::M_FINDER_DARK, QRMatrix::M_ALIGNMENT]);
	}
}