#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PNG_SKIP_SETJMP_CHECK
#include <png.h>
#include "qrencode.h"
#include "qrpng.h"
#ifdef HEADER
typedef struct qrpng {
/* Size of a module in pixels. */
unsigned int scale;
/* Size of the quietzone in modules. */
unsigned int quietzone;
char * filename;
/* PNG stuff. */
png_structp png;
png_infop info;
png_byte ** row_pointers;
/* Number of blocks in the image part (not including the quiet zone). */
int size;
/* Actual size of the PNG image. The image is always square, so
the width is equal to the height. */
unsigned int img_size;
png_byte * quiet;
}
qrpng_t;
typedef enum qrpng_status {
qrpng_ok,
qrpng_bad_scale,
qrpng_bad_quietzone,
qrpng_bad_filename,
qrpng_bounds,
}
qrpng_status_t;
#define QUIETZONE 4
#define MAX_SCALE 100
#define MAX_QUIETZONE 100
#define QRPNG_DEFAULT_QUIET QUIETZONE
#define QRPNG_MINIMUM_QUIET 0
#define QRPNG_MAXIMUM_QUIET 100
#define QRPNG_DEFAULT_SCALE 3
#define QRPNG_MINIMUM_SCALE 1
#define QRPNG_MAXIMUM_SCALE MAX_SCALE
#endif /* def HEADER */
/* Set bit "x" in "f", which is "unsigned char *". */
#define SETBIT(f,x) f[((x)>>3)] |= 0x80 >> ((x) & 7)
qrpng_status_t
qrpng_make_png (qr_t * qr, qrpng_t * qrpng)
{
size_t x;
size_t y;
int size;
if (qrpng->scale < 1 || qrpng->scale > MAX_SCALE) {
return qrpng_bad_scale;
}
if (qrpng->quietzone > MAX_QUIETZONE) {
return qrpng_bad_quietzone;
}
qrpng->size = qr->WD;
size = qr->WD + qrpng->quietzone * 2;
qrpng->img_size = size * qrpng->scale;
qrpng->png = png_create_write_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (! qrpng->png) {
fprintf (stderr, "png_create_write_struct failed\n");
exit (1);
}
#ifdef USESETJMP
if (setjmp (png_jmpbuf (png))) {
fprintf (stderr, "libpng borked\n");
exit (1);
}
#endif /* def USESETJMP */
qrpng->info = png_create_info_struct (qrpng->png);
png_set_IHDR (qrpng->png, qrpng->info, qrpng->img_size, qrpng->img_size, 1,
PNG_COLOR_TYPE_GRAY,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
qrpng->row_pointers = png_malloc (qrpng->png,
qrpng->img_size * sizeof (png_byte *));
/* Fill top and bottom quiet zones. */
qrpng->quiet = calloc (qrpng->img_size, sizeof (png_byte));
for (y = 0; y < qrpng->quietzone * qrpng->scale; y++) {
/* Counting from the top. */
int from_top;
from_top = (qr->WD + qrpng->quietzone * 2)*qrpng->scale - y - 1;
qrpng->row_pointers[y] = qrpng->quiet;
qrpng->row_pointers[from_top] = qrpng->quiet;
}
for (y = 0; y < qr->WD; y++) {
int repeat;
png_byte * line;
int bits[qr->WD];
line = calloc (qrpng->img_size / 8 + 1, sizeof (png_byte));
/* Get the bits from qr->qrframe. */
for (x = 0; x < qr->WD; x++) {
bits[x] = QRBIT (qrframe, x, y);
}
/* Put the bits in, repeatedly, to "line", then repeatedly use
"line" in "qrpng->row_pointers". */
for (x = 0; x < qr->WD; x++) {
if (bits[x]) {
int r;
int o;
o = (x + qrpng->quietzone) * qrpng->scale;
for (r = 0; r < qrpng->scale; r++) {
int p;
p = o + r;
if (p > qrpng->img_size - qrpng->quietzone * qrpng->scale) {
return qrpng_bounds;
}
SETBIT(line, p);
}
}
}
for (repeat = 0; repeat < qrpng->scale; repeat++) {
qrpng->row_pointers[(y+qrpng->quietzone)*qrpng->scale + repeat]
= line;
}
}
png_set_rows (qrpng->png, qrpng->info, qrpng->row_pointers);
return qrpng_ok;
}
qrpng_status_t
qrpng_write (qrpng_t * qrpng)
{
FILE * f;
if (! qrpng->filename) {
return qrpng_bad_filename;
}
f = fopen (qrpng->filename, "wb");
if (! f) {
fprintf (stderr, "fopen failed\n");
exit (1);
}
png_init_io (qrpng->png, f);
png_write_png (qrpng->png, qrpng->info, PNG_TRANSFORM_INVERT_MONO, NULL);
fclose (f);
return qrpng_ok;
}
qrpng_status_t
qrpng_free (qrpng_t * qrpng)
{
int y;
png_destroy_write_struct (& qrpng->png, & qrpng->info);
free (qrpng->quiet);
for (y = 0; y < qrpng->size; y++) {
free (qrpng->row_pointers[(y+qrpng->quietzone)*qrpng->scale]);
}
free (qrpng->row_pointers);
return qrpng_ok;
}