1 /*
2 * Copyright (C) 2021 Tetsuya Isaki
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include "ImageLoaderPNG.h"
27 #include "StringUtil.h"
28 #include <cstring>
29 #include <errno.h>
30 #include <png.h>
31
32 static void png_read(png_structp png, png_bytep data, png_size_t length);
33
34 // コンストラクタ
ImageLoaderPNG(InputStream * stream_,const Diag & diag_)35 ImageLoaderPNG::ImageLoaderPNG(InputStream *stream_, const Diag& diag_)
36 : inherited(stream_, diag_)
37 {
38 }
39
40 // デストラクタ
~ImageLoaderPNG()41 ImageLoaderPNG::~ImageLoaderPNG()
42 {
43 }
44
45 // stream が PNG なら true を返す。
46 bool
Check() const47 ImageLoaderPNG::Check() const
48 {
49 uint8 magic[4];
50
51 auto n = stream->Peek(&magic, sizeof(magic));
52 if (n < sizeof(magic)) {
53 Trace(diag, "%s: Read(magic) failed: %s", __method__, strerror(errno));
54 return false;
55 }
56 // マジックを確認
57 if (png_sig_cmp(magic, 0, sizeof(magic)) != 0) {
58 Trace(diag, "%s: Bad magic", __method__);
59 return false;
60 }
61 Trace(diag, "%s: OK", __method__);
62 return true;
63 }
64
65 // stream から画像をロードする。
66 bool
Load(Image & img)67 ImageLoaderPNG::Load(Image& img)
68 {
69 png_structp png;
70 png_infop info;
71 png_uint_32 width;
72 png_uint_32 height;
73 int bitdepth;
74 int color_type;
75 int interlace_type;
76 int compression_type;
77 int filter_type;
78 std::vector<png_bytep> lines;
79 bool rv;
80
81 rv = false;
82 png = NULL;
83 info = NULL;
84
85 png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
86 NULL, NULL, NULL);
87 if (__predict_false(png == NULL)) {
88 return false;
89 }
90 info = png_create_info_struct(png);
91 if (__predict_false(info == NULL)) {
92 goto done;
93 }
94
95 // libpng 内のエラーからは大域ジャンプで戻ってくるらしい…
96 if (setjmp(png_jmpbuf(png))) {
97 goto done;
98 }
99
100 // コールバック設定
101 png_set_read_fn(png, stream, png_read);
102
103 // ヘッダを読み込む
104 png_read_info(png, info);
105 png_get_IHDR(png, info, &width, &height, &bitdepth,
106 &color_type, &interlace_type, &compression_type, &filter_type);
107 Debug(diag, "IHDR width=%d height=%d bitdepth=%d", width, height, bitdepth);
108 Debug(diag, "IHDR colortype=%s interlace=%d compression=%d filter=%d",
109 ColorType2str(color_type).c_str(), interlace_type,
110 compression_type, filter_type);
111
112 // color_type によっていろいろ設定が必要。
113 // see libpng(4)
114 if (color_type == PNG_COLOR_TYPE_PALETTE) {
115 png_set_palette_to_rgb(png);
116 }
117 if ((color_type & PNG_COLOR_MASK_COLOR) == 0) {
118 if (bitdepth < 8) {
119 png_set_expand_gray_1_2_4_to_8(png);
120 }
121 png_set_gray_to_rgb(png);
122 }
123 // Alpha 無視
124 png_set_strip_alpha(png);
125
126 img.Create(width, height);
127
128 // スキャンラインメモリのポインタ配列
129 lines.resize(img.GetHeight());
130 for (int y = 0, end = lines.size(); y < end; y++) {
131 lines[y] = img.buf.data() + (y * img.GetStride());
132 }
133
134 png_read_image(png, lines.data());
135 png_read_end(png, info);
136 rv = true;
137
138 done:
139 png_destroy_read_struct(&png, &info, (png_infopp)NULL);
140 return rv;
141 }
142
143 // PNG の color type
144 /*static*/ std::string
ColorType2str(int type)145 ImageLoaderPNG::ColorType2str(int type)
146 {
147 switch (type) {
148 case PNG_COLOR_TYPE_GRAY:
149 return "Gray";
150 case PNG_COLOR_TYPE_PALETTE:
151 return "Palette";
152 case PNG_COLOR_TYPE_RGB:
153 return "RGB";
154 case PNG_COLOR_TYPE_RGBA:
155 return "RGBA";
156 case PNG_COLOR_TYPE_GRAY_ALPHA:
157 return "GrayA";
158 default:
159 return string_format("%d(?)", type);
160 }
161 }
162
163 // コールバック
164 static void
png_read(png_structp png,png_bytep data,png_size_t length)165 png_read(png_structp png, png_bytep data, png_size_t length)
166 {
167 InputStream *stream = (InputStream *)png_get_io_ptr(png);
168
169 size_t total = 0;
170 while (total < length) {
171 auto r = stream->Read((char *)data + total, length - total);
172 if (r <= 0)
173 break;
174 total += r;
175 }
176 }
177