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;
20 // #include "third_party/png/png.h"
23 * JAVAPORT: This class may get thrown out in the future, or it may turn into the object which
24 * returns a MonochromeBitmapSource.
26 * @author satorux@google.com (Satoru Takabayashi) - creator
27 * @author dswitkin@google.com (Daniel Switkin) - ported from C++
29 public final class Renderer {
31 // See 7.3.7 of JISX0510:2004 (p. 11).
32 private static final int kQuietZoneSize = 4;
34 // Render QR Code as PNG image with "cell_size". On success, store
35 // the result in "result" and return true. On error, return false.
36 // The recommended cell size for desktop screens is 3. This
37 // setting generates 87x87 pixels PNG image for version 1 QR Code
38 // (21x21). 87 = (21 + 4 + 4) * 3. 4 is for surrounding white
39 // space (they call it quiet zone).
40 // Sorry for the long function but libpng's API is a bit complecated.
41 // See http://www.libpng.org/pub/png/libpng-1.2.5-manual.html for
43 public static boolean RenderAsPNG(final QRCode &qr_code, int cell_size,
45 // First, clear the result String.
49 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
51 if (png_ptr == null) {
52 Debug.LOG_ERROR("Unable to create png_strupctp");
57 png_infop info_ptr = png_create_info_struct(png_ptr);
58 if (info_ptr == null) {
59 Debug.LOG_ERROR("Unable to create png_infop");
60 png_destroy_write_struct(&png_ptr, (png_infopp)null);
64 // Calculate the width of the resulting image. Note that the height
65 // is equal to the width (i.e. the resulting image is square).
66 final int image_width = (qr_code.matrix_width() +
67 kQuietZoneSize * 2) * cell_size;
68 // Since we use 1-bit color depth, we only need 1 bit per pixel.
69 final int num_bytes_in_row = image_width / 8 +
70 (image_width % 8 == 0 ? 0 : 1);
71 // We'll use this storage later but we should prepare this before
72 // setjmp() so that this will be deleted on error. Today's lesson
73 // is that RAII isn't reliable with setjmp/longjmp!
74 scoped_array<char> row(new char[num_bytes_in_row]);
76 // Erorr handling of libpng is a bit tricky. If something bad
77 // happens in libpng, they call longjmp() to get to here.
78 if (setjmp(png_ptr.jmpbuf)) {
79 Debug.LOG_ERROR("Something bad happened in libpng");
80 png_destroy_write_struct(&png_ptr, &info_ptr);
84 // Attach the pointer to the result String and the pointer to the
86 png_set_write_fn(png_ptr, static_cast<void*>(result), PNGWriter, null);
88 // Set the image information.
89 png_set_IHDR(png_ptr, info_ptr, image_width, image_width,
90 1, // The color depth is 1 (black and white).
93 PNG_COMPRESSION_TYPE_BASE,
94 PNG_FILTER_TYPE_BASE);
96 // Write the file header information.
97 png_write_info(png_ptr, info_ptr);
99 // Quiet zone at the top.
100 FillRowWithWhite(num_bytes_in_row, row.get());
101 WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size);
103 for (int y = 0; y < qr_code.matrix_width(); ++y) {
104 FillRowWithData(num_bytes_in_row, qr_code, y, cell_size, row.get());
105 WriteRowNumTimes(png_ptr, row.get(), cell_size);
107 // Quiet zone at the bottom.
108 FillRowWithWhite(num_bytes_in_row, row.get());
109 WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size);
111 // Cleanups for libpng stuff.
112 png_write_end(png_ptr, info_ptr);
113 png_destroy_write_struct(&png_ptr, &info_ptr);
115 // Finally, it's all done!
119 // Similar to RenderAsPNG but it renders QR code from data in
120 // "bytes" with error correction level "ec_level". This is the
121 // friendliest function in the QR code library.
122 public static boolean RenderAsPNGFromData(final StringPiece& bytes, int ec_level, int cell_size,
125 if (!Encoder.Encode(bytes, ec_level, &qr_code)) {
128 return RenderAsPNG(qr_code, cell_size, result);
131 // Callback function which gets called by png_write_row().
132 private static void PNGWriter(png_structp png_ptr, png_bytep data, png_size_t length) {
133 String* out = static_cast<String*>(png_get_io_ptr(png_ptr));
134 out.append(reinterpret_cast<char*>(data), length);
137 // Fill all pixels in "row" with white.
138 private static void FillRowWithWhite(final int num_bytes_in_row, char *row) {
139 memset(row, 0xff, num_bytes_in_row);
142 // Set the bit in "row" pointed by "index" to 1 (1 is for white).
143 private static void SetBit(final int index, char *row) {
144 final int byte_index = index / 8;
145 final int bit_index = index % 8;
146 row[byte_index] |= 0x80 >> bit_index;
149 // Fill pixels in "row" with data in "qr_code".
150 private static void FillRowWithData(final int num_bytes_in_row,
151 final QRCode &qr_code,
155 memset(row, 0, num_bytes_in_row); // Fill all pixels with black.
158 for (int i = 0; i < kQuietZoneSize * cell_size; ++i) {
159 SetBit(index++, row); // Cells in the quite zone should be white.
161 for (int x = 0; x < qr_code.matrix_width(); ++x) {
162 for (int i = 0; i < cell_size; ++i) {
163 if (qr_code.at(x, y) == 0) { // White cell.
169 for (int i = 0; i < kQuietZoneSize * cell_size; ++i) {
170 SetBit(index++, row);
174 // Write pixels in "row" to "png_ptr" "num" times.
175 private static void WriteRowNumTimes(png_structp png_ptr, char *row, final int num) {
176 for (int i = 0; i < num; ++i) {
177 png_write_row(png_ptr, reinterpret_cast<png_bytep>(row));