1 /* $Id: image_io.cpp 618367 2020-10-19 14:54:50Z grichenk $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Mike DiCuccio
27 *
28 * File Description:
29 * CImageIO -- framework for reading/writing images
30 */
31
32 #include <ncbi_pch.hpp>
33 #include <util/image/image_io.hpp>
34 #include <util/image/image_exception.hpp>
35 #include <corelib/stream_utils.hpp>
36 #include <util/error_codes.hpp>
37
38 #include "image_io_raw.hpp"
39 #include "image_io_bmp.hpp"
40 #include "image_io_gif.hpp"
41 #include "image_io_jpeg.hpp"
42 #include "image_io_png.hpp"
43 #include "image_io_sgi.hpp"
44 #include "image_io_tiff.hpp"
45
46
47 #define NCBI_USE_ERRCODE_X Util_Image
48
49
50 BEGIN_NCBI_SCOPE
51
52 //
53 // file magic signatures for various formats
54 //
55
56 // we read at most 16 bytes
57 static const int kMaxMagic = 16;
58
59 struct SMagicInfo
60 {
61 CImageIO::EType m_Type;
62 unsigned int m_Length;
63 unsigned char m_Signature[kMaxMagic];
64 };
65 static const struct SMagicInfo kMagicTable[] = {
66 { CImageIO::eBmp, 2, "BM" },
67 { CImageIO::eGif, 4, "GIF8" },
68 { CImageIO::eJpeg, 2, "\xff\xd8" },
69 { CImageIO::ePng, 4, "\x89PNG" },
70 { CImageIO::eSgi, 2, "\x1\xda" },
71 { CImageIO::eTiff, 4, "II*\0" },
72 { CImageIO::eTiff, 4, "MM\0*" }, // this one might need to be { 'M', 'M', 0, '*' }
73 { CImageIO::eXpm, 7, "/* XPM */" },
74 { CImageIO::eRaw, 4, "RAW\0" },
75
76 // must be last
77 { CImageIO::eUnknown, 0, "" }
78 };
79
80 // extract an image's type from its magic number
GetTypeFromMagic(const string & file)81 CImageIO::EType CImageIO::GetTypeFromMagic(const string& file)
82 {
83 CNcbiIfstream i_file (file.c_str());
84 if (!i_file) {
85 return eUnknown;
86 }
87
88 return GetTypeFromMagic(i_file);
89 }
90
GetTypeFromMagic(CNcbiIstream & istr)91 CImageIO::EType CImageIO::GetTypeFromMagic(CNcbiIstream& istr)
92 {
93 // magic numbers live in the first few bytes
94 unsigned char magic[kMaxMagic];
95 memset(magic, 0x00, kMaxMagic);
96
97 istr.read((char *)magic, kMaxMagic);
98 istr.seekg(-istr.gcount(), ios::cur);
99 //CStreamUtils::Pushback(istr, (CT_CHAR_TYPE*)magic, istr.gcount());
100
101 EType type = eUnknown;
102
103 // we just compare against our (small) table of known image magic values
104 for (const SMagicInfo* i = kMagicTable; i->m_Length; ++i) {
105 if (memcmp(magic, i->m_Signature, i->m_Length) == 0) {
106 type = i->m_Type;
107 break;
108 }
109 }
110
111 return type;
112 }
113
114
115 //
116 // magic from extension info
117 //
118 struct SExtMagicInfo
119 {
120 CImageIO::EType m_Type;
121 const char* m_Ext;
122 };
123
124 static const SExtMagicInfo kExtensionMagicTable[] = {
125 { CImageIO::eBmp, "bmp" },
126 { CImageIO::eGif, "gif" },
127 { CImageIO::eJpeg, "jpeg" },
128 { CImageIO::eJpeg, "jpg" },
129 { CImageIO::ePng, "png" },
130 { CImageIO::eSgi, "sgi" },
131 { CImageIO::eTiff, "tif" },
132 { CImageIO::eTiff, "tiff" },
133 { CImageIO::eXpm, "xpm" },
134 { CImageIO::eRaw, "raw" },
135
136 // must be last
137 { CImageIO::eUnknown, NULL }
138 };
139
140 //
141 // GetTypeFromFileName()
142 // This function will determine a file's expected type from a given file name
143 //
GetTypeFromFileName(const string & fname)144 CImageIO::EType CImageIO::GetTypeFromFileName(const string& fname)
145 {
146 string::size_type pos = fname.find_last_of(".");
147 if (pos == string::npos) {
148 return eUnknown;
149 }
150
151 string ext(fname.substr(pos + 1, fname.length() - pos - 1));
152 ext = NStr::ToLower(ext);
153
154 // compare the extension against our table
155 for (const SExtMagicInfo* i = kExtensionMagicTable; i->m_Ext; ++i) {
156 if (ext == i->m_Ext) {
157 return i->m_Type;
158 }
159 }
160 return eUnknown;
161 }
162
163 /// read just the image information
ReadImageInfo(const string & file,size_t * width,size_t * height,size_t * depth,EType * type)164 bool CImageIO::ReadImageInfo(const string& file,
165 size_t* width, size_t* height, size_t* depth,
166 EType* type)
167 {
168 CNcbiIfstream istr(file.c_str(), ios::in|ios::binary);
169 return ReadImageInfo(istr, width, height, depth, type);
170 }
171
172
ReadImageInfo(CNcbiIstream & istr,size_t * width,size_t * height,size_t * depth,EType * type)173 bool CImageIO::ReadImageInfo(CNcbiIstream& istr,
174 size_t* width, size_t* height, size_t* depth,
175 EType* type)
176 {
177 try {
178 /// first, set up a simple buffer - we hold the first 16k
179 /// or so of the image. this will allow us to manage things like TIFF
180 /// image directories
181
182 EType image_type = GetTypeFromMagic(istr);
183 if (type) {
184 *type = image_type;
185 }
186 CRef<CImageIOHandler> handler(x_GetHandler(image_type));
187
188 size_t pos = istr.tellg();
189 bool res = handler->ReadImageInfo(istr, width, height, depth);
190 istr.seekg(pos, ios::beg);
191 return res;
192 }
193 catch (CImageException& e) {
194 ERR_POST_X(3, Error << "Error reading image: " << e.what());
195 }
196 return false;
197 }
198
199
200
201 //
202 // ReadImage()
203 // read an image from disk, returning its contents
204 //
ReadImage(const string & file,EType type)205 CImage* CImageIO::ReadImage(const string& file, EType type)
206 {
207 CNcbiIfstream istr(file.c_str(), ios::in|ios::binary);
208 return ReadImage(istr, type);
209 }
210
211
ReadImage(CNcbiIstream & istr,EType type)212 CImage* CImageIO::ReadImage(CNcbiIstream& istr, EType type)
213 {
214 try {
215 if (type == eUnknown) {
216 type = GetTypeFromMagic(istr);
217 }
218 CRef<CImageIOHandler> handler(x_GetHandler(type));
219 return handler->ReadImage(istr);
220 }
221 catch (CImageException& e) {
222 ERR_POST_X(4, Error << "Error reading image: " << e.what());
223 return NULL;
224 }
225 // all other exceptions are explicitly not caught - handler in user code
226 }
227
228
229 //
230 // ReadSubImage()
231 // read part of an image from disk, returning its contents
232 //
ReadSubImage(const string & file,size_t x,size_t y,size_t w,size_t h,EType type)233 CImage* CImageIO::ReadSubImage(const string& file,
234 size_t x, size_t y, size_t w, size_t h,
235 EType type)
236 {
237 CNcbiIfstream istr(file.c_str(), ios::in|ios::binary);
238 return ReadSubImage(istr, x, y, w, h, type);
239 }
240
241
ReadSubImage(CNcbiIstream & istr,size_t x,size_t y,size_t w,size_t h,EType type)242 CImage* CImageIO::ReadSubImage(CNcbiIstream& istr,
243 size_t x, size_t y, size_t w, size_t h,
244 EType type)
245 {
246 try {
247 if (type == eUnknown) {
248 type = GetTypeFromMagic(istr);
249 }
250 CRef<CImageIOHandler> handler(x_GetHandler(type));
251 return handler->ReadImage(istr, x, y, w, h);
252 }
253 catch (CImageException& e) {
254 ERR_POST_X(5, Error << "Error reading subimage: " << e.what());
255 return NULL;
256 }
257 // all other exceptions are explicitly not caught - handler in user code
258 }
259
260
261 //
262 // WriteImage()
263 // write an image to disk
264 //
WriteImage(const CImage & image,const string & file,EType type,ECompress compress)265 bool CImageIO::WriteImage(const CImage& image, const string& file,
266 EType type, ECompress compress)
267 {
268 try {
269 if (type == eUnknown) {
270 type = GetTypeFromFileName(file);
271 }
272 }
273 catch (CImageException& e) {
274 ERR_POST_X(6, Error << "Error writing image: " << e.what());
275 return false;
276 }
277
278 CNcbiOfstream ostr(file.c_str(), ios::out|ios::binary);
279 if (ostr.good()) {
280 return WriteImage(image, ostr, type, compress);
281 }
282
283 return false;
284 }
285
286
WriteImage(const CImage & image,CNcbiOstream & ostr,EType type,ECompress compress)287 bool CImageIO::WriteImage(const CImage& image, CNcbiOstream& ostr,
288 EType type, ECompress compress)
289 {
290 try {
291 CRef<CImageIOHandler> handler(x_GetHandler(type));
292 handler->WriteImage(image, ostr, compress);
293 return true;
294 }
295 catch (CImageException& e) {
296 ERR_POST_X(7, Error << "Error writing image: " << e.what());
297 return false;
298 }
299 // all other exceptions are explicitly not caught - handler in user code
300 }
301
302
303 //
304 // WriteSubImage()
305 // write part of an image to disk
306 //
WriteSubImage(const CImage & image,const string & file,size_t x,size_t y,size_t w,size_t h,EType type,ECompress compress)307 bool CImageIO::WriteSubImage(const CImage& image,
308 const string& file,
309 size_t x, size_t y, size_t w, size_t h,
310 EType type, ECompress compress)
311 {
312 try {
313 if (type == eUnknown) {
314 type = GetTypeFromFileName(file);
315 }
316 }
317 catch (CImageException& e) {
318 ERR_POST_X(8, Error << "Error writing image: " << e.what());
319 return false;
320 }
321
322 CNcbiOfstream ostr(file.c_str(), ios::out|ios::binary);
323 if (ostr.good()) {
324 return WriteSubImage(image, ostr, x, y, w, h, type, compress);
325 }
326
327 return false;
328 }
329
330
WriteSubImage(const CImage & image,CNcbiOstream & ostr,size_t x,size_t y,size_t w,size_t h,EType type,ECompress compress)331 bool CImageIO::WriteSubImage(const CImage& image,
332 CNcbiOstream& ostr,
333 size_t x, size_t y, size_t w, size_t h,
334 EType type, ECompress compress)
335 {
336 try {
337 CRef<CImageIOHandler> handler(x_GetHandler(type));
338 handler->WriteImage(image, ostr, x, y, w, h, compress);
339 return true;
340 }
341 catch (CImageException& e) {
342 ERR_POST_X(9, Error << "Error writing image: " << e.what());
343 return false;
344 }
345 // all other exceptions are explicitly not caught - handler in user code
346 }
347
348
349 //
350 // x_GetImageHandler()
351 // Retrieve a CImageIOHandler-derived class to handle reading and writing for a
352 // given image formag
353 //
x_GetHandler(EType type)354 CImageIOHandler* CImageIO::x_GetHandler(EType type)
355 {
356 switch (type) {
357 default:
358 case eUnknown:
359 NCBI_THROW(CImageException, eInvalidImage,
360 "Image format not supported");
361
362 case eJpeg:
363 return new CImageIOJpeg();
364
365 case eBmp:
366 return new CImageIOBmp();
367
368 case ePng:
369 return new CImageIOPng();
370
371 case eSgi:
372 return new CImageIOSgi();
373
374 case eGif:
375 return new CImageIOGif();
376
377 case eRaw:
378 return new CImageIORaw();
379
380 case eTiff:
381 return new CImageIOTiff();
382 }
383 }
384
385
386 END_NCBI_SCOPE
387