QR-Code
view release on metacpan or search on metacpan
include/qr/qr_img.h view on Meta::CPAN
#ifndef QR_IMG_H
#define QR_IMG_H
#include <stddef.h>
/* qr_img.h - what a raster logo needs from its bytes, and nothing more.
*
* A PNG or JPEG logo is never decoded: the SVG output embeds the bytes
* as a data: URI and the renderer does the decoding. The only facts the
* box geometry needs are the format (for the MIME type) and the pixel
* dimensions (for the aspect ratio), and both formats state them in
* their headers.
*
* The format comes from magic bytes, never from a file extension. A
* file named .png that is actually a JPEG is a thing that happens, and
* an <image> whose MIME type does not match its bytes renders as
* nothing in some viewers.
*/
#define QR_IMG_UNKNOWN 0
#define QR_IMG_PNG 1
#define QR_IMG_JPEG 2
#define QR_IMG_SVG 3
/* Sniff the format. SVG is anything that opens with '<' once an
* optional UTF-8 BOM and leading whitespace are skipped - the XML
* declaration, a comment, or the root element itself all qualify. */
static int qr_img_sniff(const unsigned char *p, size_t n)
{
static const unsigned char png_sig[8] =
{ 0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A };
size_t i = 0;
if (n >= 8 && memcmp(p, png_sig, 8) == 0)
return QR_IMG_PNG;
if (n >= 3 && p[0] == 0xFF && p[1] == 0xD8 && p[2] == 0xFF)
return QR_IMG_JPEG;
if (n >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
i = 3;
while (i < n && (p[i] == ' ' || p[i] == '\t' ||
p[i] == '\r' || p[i] == '\n'))
i++;
if (i < n && p[i] == '<')
return QR_IMG_SVG;
return QR_IMG_UNKNOWN;
}
/* Pixel dimensions from the header. Returns 0 and fills *w, *h, or -1
* when the bytes are truncated or structurally wrong.
*
* PNG: the IHDR chunk is required to come first, so width and height
* sit at fixed offsets - big-endian 32-bit at 16 and 20, after the
* 8-byte signature, the 4-byte chunk length, and "IHDR". No chunk walk.
*
* JPEG: segments are a 0xFF marker byte, a marker, and for most
* markers a two-byte big-endian length that includes itself. Walk them
* until a start-of-frame marker - 0xC0 to 0xCF excluding 0xC4 (DHT),
* 0xC8 (JPG extension), 0xCC (DAC) - which covers baseline SOF0 and
* progressive SOF2 alike. Height then width sit at offsets 3 and 5 of
* the segment body, after the length and the precision byte. EXIF
* orientation is deliberately ignored; the POD owns that caveat. */
static int qr_img_dims(const unsigned char *p, size_t n, int fmt,
unsigned long *w, unsigned long *h)
{
if (fmt == QR_IMG_PNG) {
if (n < 24)
return -1;
if (memcmp(p + 12, "IHDR", 4) != 0)
return -1;
*w = ((unsigned long)p[16] << 24) | ((unsigned long)p[17] << 16) |
((unsigned long)p[18] << 8) | (unsigned long)p[19];
*h = ((unsigned long)p[20] << 24) | ((unsigned long)p[21] << 16) |
((unsigned long)p[22] << 8) | (unsigned long)p[23];
return (*w == 0 || *h == 0) ? -1 : 0;
}
if (fmt == QR_IMG_JPEG) {
size_t i = 2;
while (i + 3 < n) {
unsigned int marker, len;
if (p[i] != 0xFF)
( run in 1.142 second using v1.01-cache-2.11-cpan-8dfa8b56332 )