Wrote a Matrix class and fixed all uses of it, as well as other small fixes like...
[zxing.git] / core / src / com / google / zxing / qrcode / encoder / Renderer.java
1 /*
2  * Copyright 2008 ZXing authors
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.google.zxing.qrcode.encoder;
18
19 //class StringPiece;
20 // #include "third_party/png/png.h"
21
22 /**
23  * JAVAPORT: This class may get thrown out in the future, or it may turn into the object which
24  * returns a MonochromeBitmapSource.
25  *
26  * @author satorux@google.com (Satoru Takabayashi) - creator
27  * @author dswitkin@google.com (Daniel Switkin) - ported from C++
28  */
29 public final class Renderer {
30
31   // See 7.3.7 of JISX0510:2004 (p. 11).
32   private static final int kQuietZoneSize = 4;
33
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
42   // details.
43   public static boolean RenderAsPNG(final QRCode &qr_code, int cell_size,
44                                     String *result) {
45     // First, clear the result String.
46     result.clear();
47
48     // Create PNG class.
49     png_structp png_ptr =  png_create_write_struct(PNG_LIBPNG_VER_STRING,
50         null, null, null);
51     if (png_ptr == null) {
52       Debug.LOG_ERROR("Unable to create png_strupctp");
53       return false;
54     }
55
56     // Create PNG info.
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);
61       return false;
62     }
63
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]);
75
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);
81       return false;
82     }
83
84     // Attach the pointer to the result String and the pointer to the
85     // writer function.
86     png_set_write_fn(png_ptr, static_cast<void*>(result), PNGWriter, null);
87
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).
91         PNG_COLOR_TYPE_GRAY,
92         PNG_INTERLACE_NONE,
93         PNG_COMPRESSION_TYPE_BASE,
94         PNG_FILTER_TYPE_BASE);
95
96     // Write the file header information.
97     png_write_info(png_ptr, info_ptr);
98
99     // Quiet zone at the top.
100     FillRowWithWhite(num_bytes_in_row, row.get());
101     WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size);
102     // Fill data.
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);
106     }
107     // Quiet zone at the bottom.
108     FillRowWithWhite(num_bytes_in_row, row.get());
109     WriteRowNumTimes(png_ptr, row.get(), kQuietZoneSize * cell_size);
110
111     // Cleanups for libpng stuff.
112     png_write_end(png_ptr, info_ptr);
113     png_destroy_write_struct(&png_ptr, &info_ptr);
114
115     // Finally, it's all done!
116     return true;
117   }
118
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,
123       String *result) {
124     QRCode qr_code;
125     if (!Encoder.Encode(bytes, ec_level, &qr_code)) {
126     return false;
127   }
128     return RenderAsPNG(qr_code, cell_size, result);
129   }
130
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);
135   }
136
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);
140   }
141
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;
147   }
148
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,
152                                       final int y,
153                                       final int cell_size,
154                                       char *row) {
155     memset(row, 0, num_bytes_in_row);  // Fill all pixels with black.
156
157     int index = 0;
158     for (int i = 0; i < kQuietZoneSize * cell_size; ++i) {
159       SetBit(index++, row);  // Cells in the quite zone should be white.
160     }
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.
164           SetBit(index, row);
165         }
166         ++index;
167       }
168     }
169     for (int i = 0; i < kQuietZoneSize * cell_size; ++i) {
170       SetBit(index++, row);
171     }
172   }
173
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));
178     }
179   }
180
181 }