2 * Copyright 2008 ZXing authors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.google.zxing.qrcode.encoder;
19 // #include "third_party/png/png.h"
22 * JAVAPORT: This class may get thrown out in the future, or it may turn into the object which
23 * returns a MonochromeBitmapSource.
25 * @author satorux@google.com (Satoru Takabayashi) - creator
26 * @author dswitkin@google.com (Daniel Switkin) - ported from C++
28 public final class Renderer {
30 // See 7.3.7 of JISX0510:2004 (p. 11).
31 private static final int kQuietZoneSize = 4;
33 // Render QR Code as PNG image with "cell_size". On success, store
34 // the result in "result" and return true. On error, return false.
35 // The recommended cell size for desktop screens is 3. This
36 // setting generates 87x87 pixels PNG image for version 1 QR Code
37 // (21x21). 87 = (21 + 4 + 4) * 3. 4 is for surrounding white
38 // space (they call it quiet zone).
39 // Sorry for the long function but libpng's API is a bit complecated.
40 // See http://www.libpng.org/pub/png/libpng-1.2.5-manual.html for
42 public static boolean RenderAsPNG(final QRCode &qr_code, int cell_size,
44 // First, clear the result String.
48 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
50 if (png_ptr == null) {
51 Debug.LOG_ERROR("Unable to create png_strupctp");
56 png_infop info_ptr = png_create_info_struct(png_ptr);
57 if (info_ptr == null) {
58 Debug.LOG_ERROR("Unable to create png_infop");
59 png_destroy_write_struct(&png_ptr, (png_infopp)null);
63 // Calculate the width of the resulting image. Note that the height
64 // is equal to the width (i.e. the resulting image is square).
65 final int image_width = (qr_code.matrix_width() +
66 kQuietZoneSize * 2) * cell_size;
67 // Since we use 1-bit color depth, we only need 1 bit per pixel.
68 final int num_bytes_in_row = image_width / 8 +
69 (image_width % 8 == 0 ? 0 : 1);
70 // We'll use this storage later but we should prepare this before
71 // setjmp() so that this will be deleted on error. Today's lesson
72 // is that RAII isn't reliable with setjmp/longjmp!
73 scoped_array<char> row(new char[num_bytes_in_row]);
75 // Erorr handling of libpng is a bit tricky. If something bad
76 // happens in libpng, they call longjmp() to get to here.
77 if (setjmp(png_ptr.jmpbuf)) {
78 Debug.LOG_ERROR("Something bad happened in libpng");
79 png_destroy_write_struct(&png_ptr, &info_ptr);
83 // Attach the pointer to the result String and the pointer to the
85 png_set_write_fn(png_ptr, static_cast<void*>(result), PNGWriter, null);
87 // Set the image information.
88 png_set_IHDR(png_ptr, info_ptr, image_width, image_width,
89 1, // The color depth is 1 (black and white).
92 PNG_COMPRESSION_TYPE_BASE,
93 PNG_FILTER_TYPE_BASE);
95 // Write the file header information.
96 png_write_info(png_ptr, info_ptr);
98 // Quiet zone at the top.
99 FillRowWithWhite(num_bytes_in_row, row.get());
100 WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size);
102 for (int y = 0; y < qr_code.matrix_width(); ++y) {
103 FillRowWithData(num_bytes_in_row, qr_code, y, cell_size, row.get());
104 WriteRowNumTimes(png_ptr, row.get(), cell_size);
106 // Quiet zone at the bottom.
107 FillRowWithWhite(num_bytes_in_row, row.get());
108 WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size);
110 // Cleanups for libpng stuff.
111 png_write_end(png_ptr, info_ptr);
112 png_destroy_write_struct(&png_ptr, &info_ptr);
114 // Finally, it's all done!
118 // Similar to RenderAsPNG but it renders QR code from data in
119 // "bytes" with error correction level "ec_level". This is the
120 // friendliest function in the QR code library.
121 public static boolean RenderAsPNGFromData(final ByteArray& bytes, int ec_level, int cell_size,
124 if (!Encoder.Encode(bytes, ec_level, &qr_code)) {
127 return RenderAsPNG(qr_code, cell_size, result);
130 // Callback function which gets called by png_write_row().
131 private static void PNGWriter(png_structp png_ptr, png_bytep data, png_size_t length) {
132 String* out = static_cast<String*>(png_get_io_ptr(png_ptr));
133 out.append(reinterpret_cast<char*>(data), length);
136 // Fill all pixels in "row" with white.
137 private static void FillRowWithWhite(final int num_bytes_in_row, char *row) {
138 memset(row, 0xff, num_bytes_in_row);
141 // Set the bit in "row" pointed by "index" to 1 (1 is for white).
142 private static void SetBit(final int index, char *row) {
143 final int byte_index = index / 8;
144 final int bit_index = index % 8;
145 row[byte_index] |= 0x80 >> bit_index;
148 // Fill pixels in "row" with data in "qr_code".
149 private static void FillRowWithData(final int num_bytes_in_row,
150 final QRCode &qr_code,
154 memset(row, 0, num_bytes_in_row); // Fill all pixels with black.
157 for (int i = 0; i < kQuietZoneSize * cell_size; ++i) {
158 SetBit(index++, row); // Cells in the quite zone should be white.
160 for (int x = 0; x < qr_code.matrix_width(); ++x) {
161 for (int i = 0; i < cell_size; ++i) {
162 if (qr_code.at(x, y) == 0) { // White cell.
168 for (int i = 0; i < kQuietZoneSize * cell_size; ++i) {
169 SetBit(index++, row);
173 // Write pixels in "row" to "png_ptr" "num" times.
174 private static void WriteRowNumTimes(png_structp png_ptr, char *row, final int num) {
175 for (int i = 0; i < num; ++i) {
176 png_write_row(png_ptr, reinterpret_cast<png_bytep>(row));