/* * libopenraw - ordiag.cpp * * Copyright (C) 2007-2024 Hubert Figuière * Copyright (C) 2008 Novell, Inc. * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include #include #include #include #include #include #include #include #include #include "dumputils.hpp" /** @addtogroup tools * @{ */ /** * @brief Dump a RawFile. (functor) */ class OrDiag { public: /** @brief Constructor * @param out The output stream * @param extract_thumbs If "all" extract all thumbnails, otherwise try * to guess the size. * @param dev_mode If true the output format for devlopment. */ OrDiag(std::ostream & out, const std::string & extract_thumbs, bool dev_mode) : m_out(out) , m_extract_all_thumbs(false) , m_dev_mode(dev_mode) { m_extract_all_thumbs = (extract_thumbs == "all"); if (!m_extract_all_thumbs) { try { int size = boost::lexical_cast(extract_thumbs); m_thumb_sizes.insert(size); } catch(...) { } } } std::string cfaPatternToString(ORMosaicInfoRef pattern) { if(pattern == NULL) { return "(null)"; } std::string out; uint16_t size = 0; const uint8_t* patternPattern = or_mosaicinfo_get_pattern(pattern, &size); for(uint16_t i = 0; i < size; ++i) { switch(patternPattern[i]) { case OR_PATTERN_COLOUR_RED: out.push_back('R'); break; case OR_PATTERN_COLOUR_GREEN: out.push_back('G'); break; case OR_PATTERN_COLOUR_BLUE: out.push_back('B'); break; default: out.push_back('*'); break; } } return out; } std::string cfaPatternToString(::or_cfa_pattern t) { switch(t) { case OR_CFA_PATTERN_NONE: return "None"; break; case OR_CFA_PATTERN_NON_RGB22: return "Non RGB 2x2"; break; case OR_CFA_PATTERN_RGGB: return "R,G,G,B"; break; case OR_CFA_PATTERN_GBRG: return "G,B,R,G"; break; case OR_CFA_PATTERN_BGGR: return "B,G,G,R"; break; case OR_CFA_PATTERN_GRBG: return "G,R,B,G"; break; default: break; } return str(boost::format("Unknown %1%") % t); }; std::string dataTypeToString(or_data_type t) { switch(t) { case OR_DATA_TYPE_NONE: return "None"; break; case OR_DATA_TYPE_PIXMAP_8RGB: return "8bits per channel RGB pixmap"; break; case OR_DATA_TYPE_JPEG: return "JPEG data"; break; case OR_DATA_TYPE_TIFF: return "TIFF container"; break; case OR_DATA_TYPE_PNG: return "PNG container"; break; case OR_DATA_TYPE_RAW: return "RAW data"; break; case OR_DATA_TYPE_COMPRESSED_RAW: return "Compressed RAW data"; break; case OR_DATA_TYPE_UNKNOWN: return "Unknown type"; break; default: break; } return "Invalid"; } /** @brief Extract thumbnail to a file * @return The filename. If empty, nothing was done. */ std::string extractThumb(ORThumbnailRef thumb) { FILE* f; size_t s; std::string ext; switch(or_thumbnail_format(thumb)) { case OR_DATA_TYPE_PIXMAP_8RGB: ext = "ppm"; break; case OR_DATA_TYPE_JPEG: ext = "jpg"; break; default: break; } if (ext.empty()) { return ""; } uint32_t x, y; or_thumbnail_dimensions(thumb, &x, &y); uint32_t dim = std::max(x, y); std::string name(str(boost::format("thumb_%1%.%2%") % dim % ext)); f = fopen(name.c_str(), "wb"); if (or_thumbnail_format(thumb) == OR_DATA_TYPE_PIXMAP_8RGB) { // ppm preemble. fprintf(f, "P6\n"); fprintf(f, "%d %d\n", x, y); fprintf(f, "%d\n", /*(componentsize == 2) ? 0xffff :*/ 0xff); } size_t dataSize = or_thumbnail_data_size(thumb); s = fwrite(or_thumbnail_data(thumb), 1, dataSize, f); if(s != dataSize) { std::cerr << "short write of " << s << " bytes\n"; } fclose(f); return name; } /** @brief Dump the previews of the raw file to the output stream. */ void dumpPreviews(ORRawFileRef rf) { size_t size = 0; const uint32_t * previews = or_rawfile_get_thumbnail_sizes(rf, &size); m_out << boost::format("\tNumber of previews: %1%\n") % size; m_out << "\tAvailable previews:\n"; for(size_t i = 0; i < size; i++) { m_out << boost::format("\t\tSize %1%\n") % previews[i]; ::or_error err = OR_ERROR_NONE; ORThumbnailRef thumb = or_rawfile_get_thumbnail(rf, previews[i], &err); if (err != OR_ERROR_NONE) { m_out << boost::format("\t\t\tError getting thumbnail %1%\n") % err; } else { m_out << boost::format("\t\t\tFormat %1%\n") % dataTypeToString(or_thumbnail_format(thumb)); uint32_t x, y; or_thumbnail_dimensions(thumb, &x, &y); m_out << boost::format("\t\t\tDimensions: width = %1% height = %2%\n") % x % y; m_out << boost::format("\t\t\tByte size: %1%\n") % or_thumbnail_data_size(thumb); } if (m_extract_all_thumbs || m_thumb_sizes.find(previews[i]) != m_thumb_sizes.end()) { std::string name = extractThumb(thumb); m_out << boost::format("\t\t\tOutput as %1%\n") % name; } or_thumbnail_release(thumb); } } void dumpRawData(ORRawFileRef rf) { ::or_error err = OR_ERROR_NONE; ORRawDataRef rd = or_rawfile_get_rawdata(rf, 0, &err); if (err == OR_ERROR_NONE) { m_out << "\tRAW data\n"; or_data_type dataType = or_rawdata_format(rd); m_out << boost::format("\t\tType: %1%") % dataTypeToString(dataType); if(dataType == OR_DATA_TYPE_COMPRESSED_RAW) { m_out << boost::format(" (compression = %1%)\n") % or_rawdata_get_compression(rd); } else { m_out << "\n"; } m_out << boost::format("\t\tByte size: %1%\n") % or_rawdata_data_size(rd); uint32_t x, y; or_rawdata_dimensions(rd, &x, &y); m_out << boost::format("\t\tDimensions: width = %1% height = %2%\n") % x % y; // Active Area uint32_t aa_x, aa_y, aa_width, aa_height; or_rawdata_get_active_area(rd, &aa_x, &aa_y, &aa_width, &aa_height); m_out << boost::format("\t\tActive Area (x,y,w,h): %1% %2% %3% %4%\n") % aa_x % aa_y % aa_width % aa_height; // CFA ORMosaicInfoRef pattern = or_rawdata_get_mosaicinfo(rd); ::or_cfa_pattern patternType = pattern ? or_mosaicinfo_get_type(pattern) : OR_CFA_PATTERN_NON_RGB22; m_out << boost::format("\t\tBayer Type: %1%\n") % cfaPatternToString(patternType); if(patternType == OR_CFA_PATTERN_NON_RGB22) { m_out << boost::format("\t\tPattern: %1%\n") % cfaPatternToString(pattern); } m_out << boost::format("\t\tBits per channel: %1%\n") % or_rawdata_bpc(rd); uint16_t black, white; or_rawdata_levels(rd, &black, &white); m_out << boost::format( "\t\tValues: black = %1% white = %2%\n") % black % white; double wb[4] = { 0.0, 0.0, 0.0, 0.0 }; or_rawdata_as_shot_neutral(rd, wb); m_out << boost::format("\t\tWB as shot neutral: [ %1% %2% %3% %4% ]\n") % wb[0] % wb[1] % wb[2] % wb[3]; uint32_t matrix_size = 0; const double *matrix = or_rawdata_get_colour_matrix(rd, 1, &matrix_size); if (matrix) { m_out << boost::format("\t\tColour Matrix 1: "); for (uint32_t i = 0; i < matrix_size; i++) { if (i > 0) { m_out << ", "; } m_out << matrix[i]; } m_out << "\n"; } } else { m_out << boost::format("\tNo Raw Data found! (error = %1%)\n") % err; } or_rawdata_release(rd); } void dumpMetaData(ORRawFileRef rf) { int32_t o = or_rawfile_get_orientation(rf); m_out << "\tMeta data\n"; m_out << "\t\tMakerNotes\n"; auto mnote = or_rawfile_get_ifd(rf, OR_IFD_MNOTE); if (!mnote) { m_out << "\t\t\tNo MakerNote found!\n"; } else { const char* makernote_id = or_ifd_get_makernote_id(mnote); m_out << boost::format("\t\t\tType = %1%\n") % (makernote_id ? makernote_id : "(null)"); auto num_entries = or_ifd_count_tags(mnote); m_out << boost::format("\t\t\tNum entries = %1%\n") % num_entries; mnote = nullptr; } m_out << boost::format("\t\tOrientation: %1%\n") % o; double matrix[9]; uint32_t size = 9; auto origin = or_rawfile_get_colour_matrix_origin(rf); std::string os; switch (origin) { case OR_COLOUR_MATRIX_BUILTIN: os = "Built-in"; break; case OR_COLOUR_MATRIX_PROVIDED: os = "Provided"; break; default: os = "Unknown"; } m_out << boost::format("\t\tColour Matrix Origin: %1%\n") % os; ExifLightsourceValue calIll = static_cast(or_rawfile_get_calibration_illuminant1(rf)); m_out << boost::format("\t\tCalibration Illuminant 1: %1%\n") % static_cast(calIll); ::or_error err = or_rawfile_get_colourmatrix1(rf, matrix, &size); if(err == OR_ERROR_NONE) { if (m_dev_mode) { std::vector int_matrix; std::transform(&matrix[0], &matrix[9], std::back_inserter(int_matrix), [](double v) -> int64_t { return rintl(v * 10000.0); }); m_out << boost::format("\t\tColour Matrix 1: %1%, %2%, %3%, " "%4%, %5%, %6%, %7%, %8%, %9%\n") % int_matrix[0] % int_matrix[1] % int_matrix[2] % int_matrix[3] % int_matrix[4] % int_matrix[5] % int_matrix[6] % int_matrix[7] % int_matrix[8]; } else { m_out << boost::format("\t\tColour Matrix 1: %1%, %2%, %3%, " "%4%, %5%, %6%, %7%, %8%, %9%\n") % matrix[0] % matrix[1] % matrix[2] % matrix[3] % matrix[4] % matrix[5] % matrix[6] % matrix[7] % matrix[8]; } } else { m_out << "\t\tNo Colour Matrix 1\n"; } calIll = static_cast(or_rawfile_get_calibration_illuminant2(rf)); m_out << boost::format("\t\tCalibration Illuminant 2: %1%\n") % static_cast(calIll); size = 9; err = or_rawfile_get_colourmatrix2(rf, matrix, &size); if(err == OR_ERROR_NONE) { if (m_dev_mode) { std::vector int_matrix; std::transform(&matrix[0], &matrix[9], std::back_inserter(int_matrix), [](double v) -> int64_t { return rintl(v * 10000.0); }); m_out << boost::format("\t\tColour Matrix 2: %1%, %2%, %3%, " "%4%, %5%, %6%, %7%, %8%, %9%\n") % int_matrix[0] % int_matrix[1] % int_matrix[2] % int_matrix[3] % int_matrix[4] % int_matrix[5] % int_matrix[6] % int_matrix[7] % int_matrix[8]; } else { m_out << boost::format("\t\tColour Matrix 2: %1%, %2%, %3%, " "%4%, %5%, %6%, %7%, %8%, %9%\n") % matrix[0] % matrix[1] % matrix[2] % matrix[3] % matrix[4] % matrix[5] % matrix[6] % matrix[7] % matrix[8]; } } else { m_out << "\t\tNo Colour Matrix 2\n"; } } void operator()(const std::string &s) { m_out << boost::format("Dumping %1%\n") % s; ORRawFileRef rf = or_rawfile_new(s.c_str(), OR_RAWFILE_TYPE_UNKNOWN); //std::unique_ptr rf(RawFile::newRawFile(s.c_str())); if (rf == NULL) { m_out << "unrecognized file\n"; } else { dump_file_info(m_out, rf, m_dev_mode); dumpPreviews(rf); dumpRawData(rf); dumpMetaData(rf); } or_rawfile_release(rf); } private: std::ostream & m_out; bool m_extract_all_thumbs; bool m_dev_mode; std::set m_thumb_sizes; }; void print_help() { std::cerr << "ordiag [-v] [-h] [-t all|] [-d 0-9] [files...]\n"; std::cerr << "Print libopenraw diagnostics\n"; std::cerr << "\t-h: show this help\n"; std::cerr << "\t-D: developer mode: display some data a format suited for development\n"; std::cerr << "\t-v: show version\n"; std::cerr << "\t-d level: set debug / verbosity to level\n"; std::cerr << "\t-t [all|]: extract thumbnails. all or .\n"; std::cerr << "\tfiles: the files to diagnose\n"; } void print_version() { std::cerr << "ordiag version 0.1 - (c) 2007-2014 Hubert Figuiere\n"; } int main(int argc, char **argv) { int done = 0; int dbl = 0; bool dev_mode = false; std::string extract_thumbs; std::vector files; int o; while((o = getopt(argc, argv, "hvdDt:")) != -1) { switch (o) { case 'h': print_help(); done = 1; break; case 'v': print_version(); done = 1; break; case 'D': dev_mode = true; break; case 'd': dbl++; break; case 't': if(optarg) { extract_thumbs = optarg; } else { print_help(); done = 1; } break; case '?': break; default: break; } } if (done) { return 1; } for ( ; optind < argc; optind++) { files.push_back(argv[optind]); } if (files.empty()) { std::cerr << "missing file name.\n"; if (dbl) { print_version(); } print_help(); return 1; } if (dbl >=2) { or_debug_set_level(DEBUG2); } // do the business. for_each(files.begin(), files.end(), OrDiag(std::cout, extract_thumbs, dev_mode)); return 0; } /** @} */ /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)) indent-tabs-mode:nil fill-column:80 End: */