├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── libraw-sys ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs └── src │ ├── bindings.rs │ └── lib.rs └── libraw ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples ├── simple_process_16bit.rs └── simple_process_8bit.rs ├── src ├── bit_depth.rs ├── error.rs ├── image.rs ├── lib.rs ├── processor.rs ├── rawimage.rs ├── sizes.rs └── utils.rs └── tests ├── samples_decode.rs └── samples_process.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | env: 10 | RUST_BACKTRACE: full 11 | 12 | jobs: 13 | rustfmt: 14 | name: rustfmt / linux / stable 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Install rust 22 | run: | 23 | rustup update --no-self-update stable 24 | rustup component add rustfmt 25 | 26 | - name: cargo fmt 27 | run: | 28 | cargo fmt --all -- --check 29 | 30 | clippy: 31 | name: clippy / linux / stable 32 | runs-on: ubuntu-latest 33 | 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v2 37 | with: 38 | submodules: true 39 | 40 | - name: Install rust 41 | run: | 42 | rustup update --no-self-update stable 43 | rustup component add clippy 44 | 45 | - name: cargo clippy 46 | run: | 47 | cargo clippy --all --all-targets 48 | 49 | test: 50 | name: test / ${{ matrix.name }} 51 | runs-on: ${{ matrix.os || 'ubuntu-latest' }} 52 | 53 | strategy: 54 | fail-fast: false 55 | matrix: 56 | include: 57 | - name: linux / stable 58 | rust: stable 59 | - name: linux / beta 60 | rust: beta 61 | - name: linux / nightly 62 | rust: nightly 63 | - name: linux / 1.63.0 64 | rust: 1.63.0 65 | - name: macOS / stable 66 | os: macOS-latest 67 | # TODO: fix 68 | # - name: windows / stable 69 | # os: windows-latest 70 | 71 | steps: 72 | - name: Checkout 73 | uses: actions/checkout@v2 74 | with: 75 | submodules: true 76 | 77 | - name: Install rust 78 | run: | 79 | rustup default ${{ matrix.rust }} 80 | rustup update --no-self-update ${{ matrix.rust }} 81 | 82 | - name: Use regex 1.9.6 (MSRV) 83 | if: matrix.rust == '1.63.0' 84 | run: cargo update -p regex --precise 1.9.6 85 | 86 | - name: Test (builtin bindings) 87 | run: cargo test --all 88 | 89 | - name: Test (regenerate bindings) 90 | run: cargo test --all --features bindgen 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libraw-sys/libraw"] 2 | path = libraw-sys/libraw 3 | url = https://github.com/LibRaw/LibRaw.git 4 | [submodule "samples"] 5 | path = samples 6 | url = https://github.com/syoyo/raw-images.git 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "libraw", 5 | "libraw-sys" 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2024 Paolo Barbolini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libraw-rs 2 | 3 | [![crates.io](https://img.shields.io/crates/v/libraw-rs.svg)](https://crates.io/crates/libraw-rs) 4 | [![Documentation](https://docs.rs/libraw-rs/badge.svg)](https://docs.rs/libraw-rs) 5 | [![Rustc Version 1.63.0+](https://img.shields.io/badge/rustc-1.63.0+-lightgray.svg)](https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html) 6 | [![CI](https://github.com/paolobarbolini/libraw-rs/workflows/CI/badge.svg)](https://github.com/paolobarbolini/libraw-rs/actions?query=workflow%3ACI) 7 | ![Passively Maintained](https://img.shields.io/badge/Maintenance%20Level-Passively%20Maintained-yellowgreen.svg) 8 | 9 | Bindings to the LibRaw C APIs. 10 | 11 | This library is still in it's early days, feel free to open a PR if a feature is missing. 12 | Contributions and suggestions are welcome on GitHub. 13 | 14 | ## License 15 | 16 | Licensed under either of 17 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 18 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 19 | 20 | at your option. 21 | 22 | ### Contribution 23 | 24 | Unless you explicitly state otherwise, any contribution intentionally submitted 25 | for inclusion in the work by you shall be dual licensed as above, without any 26 | additional terms or conditions. 27 | -------------------------------------------------------------------------------- /libraw-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libraw-rs-sys" 3 | version = "0.0.4+libraw-0.21.3" # remember to update html_root_url 4 | authors = ["Paolo Barbolini "] 5 | description = "FFI bindings to LibRaw" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/paolobarbolini/libraw-rs" 8 | categories = ["multimedia::images", "external-ffi-bindings"] 9 | keywords = ["raw"] 10 | readme = "README.md" 11 | edition = "2021" 12 | build = "build.rs" 13 | include = ["src/lib.rs", "src/bindings.rs", "LICENSE-*", "README.md", "libraw/libraw/*", "libraw/src/*", "libraw/internal/*", "libraw/COPYRIGHT", "libraw/LICENSE.*", "build.rs"] 14 | 15 | [lib] 16 | name = "libraw_sys" 17 | 18 | [dependencies] 19 | libc = "0.2" 20 | 21 | [build-dependencies] 22 | cc = { version = "1.0.42", features = ["parallel"] } 23 | bindgen = { version = "0.69", default-features = false, features = ["runtime"], optional = true } 24 | -------------------------------------------------------------------------------- /libraw-sys/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /libraw-sys/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /libraw-sys/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /libraw-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::Path; 3 | 4 | fn main() { 5 | let out_dir_ = env::var_os("OUT_DIR").unwrap(); 6 | let out_dir = Path::new(&out_dir_); 7 | 8 | build(out_dir); 9 | #[cfg(feature = "bindgen")] 10 | bindings(out_dir); 11 | } 12 | 13 | fn build(out_dir: &Path) { 14 | let mut libraw = cc::Build::new(); 15 | libraw.cpp(true); 16 | libraw.include("libraw/"); 17 | 18 | libraw.file("libraw/src/decoders/canon_600.cpp"); 19 | libraw.file("libraw/src/decoders/crx.cpp"); 20 | libraw.file("libraw/src/decoders/decoders_dcraw.cpp"); 21 | libraw.file("libraw/src/decoders/decoders_libraw.cpp"); 22 | libraw.file("libraw/src/decoders/decoders_libraw_dcrdefs.cpp"); 23 | libraw.file("libraw/src/decoders/dng.cpp"); 24 | libraw.file("libraw/src/decoders/fp_dng.cpp"); 25 | libraw.file("libraw/src/decoders/fuji_compressed.cpp"); 26 | libraw.file("libraw/src/decoders/generic.cpp"); 27 | libraw.file("libraw/src/decoders/kodak_decoders.cpp"); 28 | libraw.file("libraw/src/decoders/load_mfbacks.cpp"); 29 | libraw.file("libraw/src/decoders/smal.cpp"); 30 | libraw.file("libraw/src/decoders/unpack.cpp"); 31 | libraw.file("libraw/src/decoders/unpack_thumb.cpp"); 32 | libraw.file("libraw/src/demosaic/aahd_demosaic.cpp"); 33 | libraw.file("libraw/src/demosaic/ahd_demosaic.cpp"); 34 | libraw.file("libraw/src/demosaic/dcb_demosaic.cpp"); 35 | libraw.file("libraw/src/demosaic/dht_demosaic.cpp"); 36 | libraw.file("libraw/src/demosaic/misc_demosaic.cpp"); 37 | libraw.file("libraw/src/demosaic/xtrans_demosaic.cpp"); 38 | libraw.file("libraw/src/integration/dngsdk_glue.cpp"); 39 | libraw.file("libraw/src/integration/rawspeed_glue.cpp"); 40 | libraw.file("libraw/src/metadata/adobepano.cpp"); 41 | libraw.file("libraw/src/metadata/canon.cpp"); 42 | libraw.file("libraw/src/metadata/ciff.cpp"); 43 | libraw.file("libraw/src/metadata/cr3_parser.cpp"); 44 | libraw.file("libraw/src/metadata/epson.cpp"); 45 | libraw.file("libraw/src/metadata/exif_gps.cpp"); 46 | libraw.file("libraw/src/metadata/fuji.cpp"); 47 | libraw.file("libraw/src/metadata/hasselblad_model.cpp"); 48 | libraw.file("libraw/src/metadata/identify.cpp"); 49 | libraw.file("libraw/src/metadata/identify_tools.cpp"); 50 | libraw.file("libraw/src/metadata/kodak.cpp"); 51 | libraw.file("libraw/src/metadata/leica.cpp"); 52 | libraw.file("libraw/src/metadata/makernotes.cpp"); 53 | libraw.file("libraw/src/metadata/mediumformat.cpp"); 54 | libraw.file("libraw/src/metadata/minolta.cpp"); 55 | libraw.file("libraw/src/metadata/misc_parsers.cpp"); 56 | libraw.file("libraw/src/metadata/nikon.cpp"); 57 | libraw.file("libraw/src/metadata/normalize_model.cpp"); 58 | libraw.file("libraw/src/metadata/olympus.cpp"); 59 | libraw.file("libraw/src/metadata/p1.cpp"); 60 | libraw.file("libraw/src/metadata/pentax.cpp"); 61 | libraw.file("libraw/src/metadata/samsung.cpp"); 62 | libraw.file("libraw/src/metadata/sony.cpp"); 63 | libraw.file("libraw/src/metadata/tiff.cpp"); 64 | libraw.file("libraw/src/postprocessing/aspect_ratio.cpp"); 65 | libraw.file("libraw/src/postprocessing/dcraw_process.cpp"); 66 | libraw.file("libraw/src/postprocessing/mem_image.cpp"); 67 | libraw.file("libraw/src/postprocessing/postprocessing_aux.cpp"); 68 | //libraw.file("libraw/src/postprocessing/postprocessing_ph.cpp"); 69 | libraw.file("libraw/src/postprocessing/postprocessing_utils.cpp"); 70 | libraw.file("libraw/src/postprocessing/postprocessing_utils_dcrdefs.cpp"); 71 | libraw.file("libraw/src/preprocessing/ext_preprocess.cpp"); 72 | //libraw.file("libraw/src/preprocessing/preprocessing_ph.cpp"); 73 | libraw.file("libraw/src/preprocessing/raw2image.cpp"); 74 | libraw.file("libraw/src/preprocessing/subtract_black.cpp"); 75 | libraw.file("libraw/src/tables/cameralist.cpp"); 76 | libraw.file("libraw/src/tables/colorconst.cpp"); 77 | libraw.file("libraw/src/tables/colordata.cpp"); 78 | libraw.file("libraw/src/tables/wblists.cpp"); 79 | libraw.file("libraw/src/utils/curves.cpp"); 80 | libraw.file("libraw/src/utils/decoder_info.cpp"); 81 | libraw.file("libraw/src/utils/init_close_utils.cpp"); 82 | libraw.file("libraw/src/utils/open.cpp"); 83 | libraw.file("libraw/src/utils/phaseone_processing.cpp"); 84 | libraw.file("libraw/src/utils/read_utils.cpp"); 85 | libraw.file("libraw/src/utils/thumb_utils.cpp"); 86 | libraw.file("libraw/src/utils/utils_dcraw.cpp"); 87 | libraw.file("libraw/src/utils/utils_libraw.cpp"); 88 | libraw.file("libraw/src/write/apply_profile.cpp"); 89 | libraw.file("libraw/src/write/file_write.cpp"); 90 | libraw.file("libraw/src/write/tiff_writer.cpp"); 91 | //libraw.file("libraw/src/write/write_ph.cpp"); 92 | libraw.file("libraw/src/x3f/x3f_parse_process.cpp"); 93 | libraw.file("libraw/src/x3f/x3f_utils_patched.cpp"); 94 | libraw.file("libraw/src/libraw_c_api.cpp"); 95 | // libraw.file("libraw/src/libraw_cxx.cpp"); 96 | libraw.file("libraw/src/libraw_datastream.cpp"); 97 | 98 | libraw.warnings(false); 99 | libraw.extra_warnings(false); 100 | // do I really have to supress all of these? 101 | libraw.flag_if_supported("-Wno-format-truncation"); 102 | libraw.flag_if_supported("-Wno-unused-result"); 103 | libraw.flag_if_supported("-Wno-format-overflow"); 104 | // thread safety 105 | libraw.flag("-pthread"); 106 | libraw.static_flag(true); 107 | libraw.compile("raw"); 108 | 109 | println!( 110 | "cargo:rustc-link-search=native={}", 111 | out_dir.join("lib").display() 112 | ); 113 | println!("cargo:rustc-link-lib=static=raw"); 114 | } 115 | 116 | #[cfg(feature = "bindgen")] 117 | fn bindings(out_dir: &Path) { 118 | let bindings = bindgen::Builder::default() 119 | .header("libraw/libraw/libraw.h") 120 | .use_core() 121 | .ctypes_prefix("libc") 122 | .generate_comments(true) 123 | .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) 124 | // API improvements 125 | .derive_eq(true) 126 | .size_t_is_usize(true) 127 | // these are never part of the API 128 | .blocklist_function("_.*") 129 | // consts creating duplications 130 | .blocklist_item("FP_NAN") 131 | .blocklist_item("FP_INFINITE") 132 | .blocklist_item("FP_ZERO") 133 | .blocklist_item("FP_SUBNORMAL") 134 | .blocklist_item("FP_NORMAL") 135 | // Rust doesn't support long double, and bindgen can't skip it 136 | // https://github.com/rust-lang/rust-bindgen/issues/1549 137 | .blocklist_function("acoshl") 138 | .blocklist_function("acosl") 139 | .blocklist_function("asinhl") 140 | .blocklist_function("asinl") 141 | .blocklist_function("atan2l") 142 | .blocklist_function("atanhl") 143 | .blocklist_function("atanl") 144 | .blocklist_function("cbrtl") 145 | .blocklist_function("ceill") 146 | .blocklist_function("copysignl") 147 | .blocklist_function("coshl") 148 | .blocklist_function("cosl") 149 | .blocklist_function("dreml") 150 | .blocklist_function("ecvt_r") 151 | .blocklist_function("erfcl") 152 | .blocklist_function("erfl") 153 | .blocklist_function("exp2l") 154 | .blocklist_function("expl") 155 | .blocklist_function("expm1l") 156 | .blocklist_function("fabsl") 157 | .blocklist_function("fcvt_r") 158 | .blocklist_function("fdiml") 159 | .blocklist_function("finitel") 160 | .blocklist_function("floorl") 161 | .blocklist_function("fmal") 162 | .blocklist_function("fmaxl") 163 | .blocklist_function("fminl") 164 | .blocklist_function("fmodl") 165 | .blocklist_function("frexpl") 166 | .blocklist_function("gammal") 167 | .blocklist_function("hypotl") 168 | .blocklist_function("ilogbl") 169 | .blocklist_function("isinfl") 170 | .blocklist_function("isnanl") 171 | .blocklist_function("j0l") 172 | .blocklist_function("j1l") 173 | .blocklist_function("jnl") 174 | .blocklist_function("ldexpl") 175 | .blocklist_function("lgammal") 176 | .blocklist_function("lgammal_r") 177 | .blocklist_function("llrintl") 178 | .blocklist_function("llroundl") 179 | .blocklist_function("log10l") 180 | .blocklist_function("log1pl") 181 | .blocklist_function("log2l") 182 | .blocklist_function("logbl") 183 | .blocklist_function("logl") 184 | .blocklist_function("lrintl") 185 | .blocklist_function("lroundl") 186 | .blocklist_function("modfl") 187 | .blocklist_function("nanl") 188 | .blocklist_function("nearbyintl") 189 | .blocklist_function("nextafterl") 190 | .blocklist_function("nexttoward") 191 | .blocklist_function("nexttowardf") 192 | .blocklist_function("nexttowardl") 193 | .blocklist_function("powl") 194 | .blocklist_function("qecvt") 195 | .blocklist_function("qecvt_r") 196 | .blocklist_function("qfcvt") 197 | .blocklist_function("qfcvt_r") 198 | .blocklist_function("qgcvt") 199 | .blocklist_function("remainderl") 200 | .blocklist_function("remquol") 201 | .blocklist_function("rintl") 202 | .blocklist_function("roundl") 203 | .blocklist_function("scalbl") 204 | .blocklist_function("scalblnl") 205 | .blocklist_function("scalbnl") 206 | .blocklist_function("significandl") 207 | .blocklist_function("sinhl") 208 | .blocklist_function("sinl") 209 | .blocklist_function("sqrtl") 210 | .blocklist_function("strtold") 211 | .blocklist_function("tanhl") 212 | .blocklist_function("tanl") 213 | .blocklist_function("tgammal") 214 | .blocklist_function("truncl") 215 | .blocklist_function("y0l") 216 | .blocklist_function("y1l") 217 | .blocklist_function("ynl") 218 | .generate() 219 | .expect("Unable to generate bindings"); 220 | 221 | bindings 222 | .write_to_file(out_dir.join("bindings.rs")) 223 | .expect("Couldn't write bindings!"); 224 | } 225 | -------------------------------------------------------------------------------- /libraw-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "https://docs.rs/libraw-rs-sys/0.0.4")] 2 | #![allow( 3 | non_camel_case_types, 4 | non_upper_case_globals, 5 | clippy::approx_constant, 6 | clippy::redundant_static_lifetimes, 7 | non_snake_case 8 | )] 9 | #![no_std] 10 | 11 | #[cfg(feature = "bindgen")] 12 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 13 | #[cfg(not(feature = "bindgen"))] 14 | mod bindings; 15 | #[cfg(not(feature = "bindgen"))] 16 | pub use bindings::*; 17 | -------------------------------------------------------------------------------- /libraw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libraw-rs" 3 | version = "0.0.4" # remember to update html_root_url 4 | authors = ["Paolo Barbolini "] 5 | description = "LibRaw bindings" 6 | license = "MIT/Apache-2.0" 7 | repository = "https://github.com/paolobarbolini/libraw-rs" 8 | categories = ["multimedia::images", "api-bindings"] 9 | keywords = ["raw"] 10 | readme = "README.md" 11 | edition = "2021" 12 | include = ["src/**/*", "LICENSE-*", "README.md"] 13 | 14 | [lib] 15 | name = "libraw" 16 | 17 | [dependencies] 18 | libraw-rs-sys = { version = "0.0.4", path = "../libraw-sys" } 19 | 20 | [features] 21 | bindgen = ["libraw-rs-sys/bindgen"] 22 | -------------------------------------------------------------------------------- /libraw/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /libraw/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /libraw/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /libraw/examples/simple_process_16bit.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, File}; 2 | use std::io::Write; 3 | 4 | use libraw::Processor; 5 | 6 | fn main() { 7 | let buf = fs::read("raw.RW2").expect("read in"); 8 | 9 | let processor = Processor::new(); 10 | let processed = processor 11 | .process_16bit(&buf) 12 | .expect("processing successful"); 13 | 14 | let mut out = File::create("out_16bit.ppm").expect("create out"); 15 | let header = format!( 16 | "P6 {} {} {}\n", 17 | processed.width(), 18 | processed.height(), 19 | 65535 20 | ); 21 | out.write_all(header.as_ref()).expect("header"); 22 | // PPM files must be in big endian 23 | let mut out_vec = Vec::with_capacity(processed.len() * 2); 24 | for chunk in processed.iter() { 25 | out_vec.extend_from_slice(&chunk.to_be_bytes()); 26 | } 27 | out.write_all(&out_vec).expect("pixels"); 28 | } 29 | -------------------------------------------------------------------------------- /libraw/examples/simple_process_8bit.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, File}; 2 | use std::io::Write; 3 | 4 | use libraw::Processor; 5 | 6 | fn main() { 7 | let buf = fs::read("raw.RW2").expect("read in"); 8 | 9 | let processor = Processor::new(); 10 | let processed = processor.process_8bit(&buf).expect("processing successful"); 11 | 12 | let mut out = File::create("out_8bit.ppm").expect("create out"); 13 | let header = format!("P6 {} {} {}\n", processed.width(), processed.height(), 255); 14 | out.write_all(header.as_ref()).expect("header"); 15 | out.write_all(&processed).expect("pixels"); 16 | } 17 | -------------------------------------------------------------------------------- /libraw/src/bit_depth.rs: -------------------------------------------------------------------------------- 1 | pub trait BitDepth: private::Sealed {} 2 | 3 | impl BitDepth for u8 {} 4 | impl BitDepth for u16 {} 5 | 6 | mod private { 7 | pub trait Sealed {} 8 | 9 | impl Sealed for u8 {} 10 | impl Sealed for u16 {} 11 | } 12 | -------------------------------------------------------------------------------- /libraw/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error as StdError; 2 | use std::fmt::{self, Display, Formatter}; 3 | 4 | use libraw_sys as sys; 5 | 6 | pub type Result = std::result::Result; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct Error { 10 | code: i32, 11 | } 12 | 13 | impl Error { 14 | pub(crate) fn check(code: i32) -> Result<()> { 15 | if code == sys::LibRaw_errors_LIBRAW_SUCCESS { 16 | Ok(()) 17 | } else { 18 | Err(Error { code }) 19 | } 20 | } 21 | } 22 | 23 | impl Display for Error { 24 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 25 | write!(f, "libraw error: {}", self.code) 26 | } 27 | } 28 | 29 | impl StdError for Error {} 30 | -------------------------------------------------------------------------------- /libraw/src/image.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::mem; 3 | use std::ops::Deref; 4 | use std::slice; 5 | 6 | use crate::BitDepth; 7 | use libraw_sys as sys; 8 | 9 | pub struct ProcessedImage { 10 | inner: *mut sys::libraw_processed_image_t, 11 | marker_: PhantomData, 12 | } 13 | 14 | impl ProcessedImage { 15 | pub(crate) unsafe fn from_raw(ptr: *mut sys::libraw_processed_image_t) -> Self { 16 | debug_assert!(!ptr.is_null()); 17 | debug_assert_eq!((*ptr).bits as usize, mem::size_of::() * 8); 18 | 19 | Self { 20 | inner: ptr, 21 | marker_: PhantomData, 22 | } 23 | } 24 | 25 | pub fn width(&self) -> u32 { 26 | unsafe { (*self.inner).width }.into() 27 | } 28 | 29 | pub fn height(&self) -> u32 { 30 | unsafe { (*self.inner).height }.into() 31 | } 32 | } 33 | 34 | impl Deref for ProcessedImage { 35 | type Target = [u8]; 36 | 37 | fn deref(&self) -> &Self::Target { 38 | unsafe { 39 | slice::from_raw_parts( 40 | (*self.inner).data.as_ptr(), 41 | (*self.inner).data_size as usize, 42 | ) 43 | } 44 | } 45 | } 46 | 47 | impl Deref for ProcessedImage { 48 | type Target = [u16]; 49 | 50 | fn deref(&self) -> &Self::Target { 51 | unsafe { 52 | debug_assert_eq!((*self.inner).data.as_ptr() as usize % 2, 0); 53 | 54 | slice::from_raw_parts( 55 | (*self.inner).data.as_ptr() as *const u16, 56 | (*self.inner).data_size as usize / 2, 57 | ) 58 | } 59 | } 60 | } 61 | 62 | impl Drop for ProcessedImage { 63 | fn drop(&mut self) { 64 | unsafe { sys::libraw_dcraw_clear_mem(self.inner) } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /libraw/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "https://docs.rs/libraw-rs/0.0.4")] 2 | 3 | pub use self::bit_depth::BitDepth; 4 | pub use self::error::{Error, Result}; 5 | pub use self::image::ProcessedImage; 6 | pub use self::processor::Processor; 7 | pub use self::rawimage::RawImage; 8 | pub use self::sizes::Sizes; 9 | pub use self::utils::camera_list; 10 | 11 | mod bit_depth; 12 | mod error; 13 | mod image; 14 | mod processor; 15 | mod rawimage; 16 | mod sizes; 17 | mod utils; 18 | -------------------------------------------------------------------------------- /libraw/src/processor.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use crate::{BitDepth, Error, ProcessedImage, RawImage, Result}; 4 | use libraw_sys as sys; 5 | 6 | pub struct Processor { 7 | pub(crate) inner: *mut sys::libraw_data_t, 8 | } 9 | 10 | impl Processor { 11 | pub fn new() -> Self { 12 | let inner = unsafe { sys::libraw_init(0) }; 13 | Self { inner } 14 | } 15 | 16 | fn open(&self, buf: &[u8]) -> Result<()> { 17 | Error::check(unsafe { 18 | sys::libraw_open_buffer(self.inner, buf.as_ptr() as *const _, buf.len()) 19 | })?; 20 | Error::check(unsafe { sys::libraw_unpack(self.inner) })?; 21 | 22 | Ok(()) 23 | } 24 | 25 | pub fn decode(self, buf: &[u8]) -> Result { 26 | self.open(buf)?; 27 | 28 | let decoded = RawImage::new(self); 29 | Ok(decoded) 30 | } 31 | 32 | #[inline] 33 | pub fn process_8bit(self, buf: &[u8]) -> Result> { 34 | self.process(buf) 35 | } 36 | 37 | #[inline] 38 | pub fn process_16bit(self, buf: &[u8]) -> Result> { 39 | self.process(buf) 40 | } 41 | 42 | fn process(self, buf: &[u8]) -> Result> { 43 | let bps = mem::size_of::() * 8; 44 | debug_assert!(bps == 8 || bps == 16); 45 | unsafe { (*self.inner).params.output_bps = bps as i32 }; 46 | 47 | self.open(buf)?; 48 | Error::check(unsafe { sys::libraw_dcraw_process(self.inner) })?; 49 | 50 | let mut result = 0i32; 51 | let processed = unsafe { sys::libraw_dcraw_make_mem_image(self.inner, &mut result) }; 52 | Error::check(result)?; 53 | 54 | let processed = unsafe { ProcessedImage::from_raw(processed) }; 55 | Ok(processed) 56 | } 57 | } 58 | 59 | impl Drop for Processor { 60 | fn drop(&mut self) { 61 | unsafe { sys::libraw_close(self.inner) } 62 | } 63 | } 64 | 65 | impl Default for Processor { 66 | fn default() -> Self { 67 | Self::new() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /libraw/src/rawimage.rs: -------------------------------------------------------------------------------- 1 | use crate::{Processor, Sizes}; 2 | use std::ops::Deref; 3 | use std::slice; 4 | 5 | pub struct RawImage { 6 | processor: Processor, 7 | } 8 | 9 | impl RawImage { 10 | pub(crate) fn new(processor: Processor) -> Self { 11 | debug_assert!(!unsafe { (*processor.inner).rawdata.raw_alloc }.is_null()); 12 | 13 | Self { processor } 14 | } 15 | 16 | pub fn sizes(&self) -> Sizes { 17 | Sizes::new(unsafe { (*self.processor.inner).sizes }) 18 | } 19 | } 20 | 21 | impl Deref for RawImage { 22 | type Target = [u16]; 23 | 24 | fn deref(&self) -> &Self::Target { 25 | let sizes = self.sizes(); 26 | 27 | unsafe { 28 | slice::from_raw_parts( 29 | (*self.processor.inner).rawdata.raw_image, 30 | sizes.raw_width as usize * sizes.raw_height as usize, 31 | ) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libraw/src/sizes.rs: -------------------------------------------------------------------------------- 1 | use libraw_sys as sys; 2 | 3 | #[derive(Debug, Copy, Clone)] 4 | pub struct Sizes { 5 | pub raw_height: u16, 6 | pub raw_width: u16, 7 | pub height: u16, 8 | pub width: u16, 9 | pub top_margin: u16, 10 | pub left_margin: u16, 11 | pub iheight: u16, 12 | pub iwidth: u16, 13 | pub raw_pitch: u32, 14 | pub pixel_aspect: f64, 15 | pub flip: i32, 16 | } 17 | 18 | impl Sizes { 19 | pub(crate) fn new(sizes: sys::libraw_image_sizes_t) -> Self { 20 | Self { 21 | raw_height: sizes.raw_height, 22 | raw_width: sizes.raw_width, 23 | height: sizes.height, 24 | width: sizes.width, 25 | top_margin: sizes.top_margin, 26 | left_margin: sizes.left_margin, 27 | iheight: sizes.iheight, 28 | iwidth: sizes.iwidth, 29 | raw_pitch: sizes.raw_pitch, 30 | pixel_aspect: sizes.pixel_aspect, 31 | flip: sizes.flip, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libraw/src/utils.rs: -------------------------------------------------------------------------------- 1 | use libraw_sys as sys; 2 | 3 | pub fn camera_list() -> Vec { 4 | let mut list = Vec::new(); 5 | let count = unsafe { sys::libraw_cameraCount() }; 6 | let names = unsafe { sys::libraw_cameraList() }; 7 | for i in 0..count { 8 | let name = unsafe { names.offset(i as isize) }; 9 | let name = unsafe { std::ffi::CStr::from_ptr(*name) }; 10 | let name = name.to_string_lossy().into_owned(); 11 | list.push(name); 12 | } 13 | list 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | 20 | #[test] 21 | fn list_cameras() { 22 | let cameras = camera_list(); 23 | assert!(!cameras.is_empty()); 24 | // Check for some known cameras 25 | assert!(cameras.contains(&"Adobe Digital Negative (DNG)".to_string())); 26 | assert!(cameras.contains(&"Canon EOS R3".to_string())); 27 | assert!(cameras.contains(&"Leica M11".to_string())); 28 | assert!(cameras.contains(&"Nikon Z fc".to_string())); 29 | assert!(cameras.contains(&"Sony ILCE-7M4 (A7 IV)".to_string())); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libraw/tests/samples_decode.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use libraw::Processor; 4 | 5 | #[test] 6 | fn colorchart_5d2_6000k() { 7 | let buf = fs::read("../samples/images/colorchart-5D2-6000K.dng").expect("read in"); 8 | 9 | let processor = Processor::new(); 10 | let decoded = processor.decode(&buf).expect("decoding successful"); 11 | let sizes = decoded.sizes(); 12 | 13 | assert_eq!(sizes.width, 5634); 14 | assert_eq!(sizes.height, 3752); 15 | assert_eq!(sizes.raw_width, 5792); 16 | assert_eq!(sizes.raw_height, 3804); 17 | assert_eq!( 18 | sizes.raw_width as usize * sizes.raw_height as usize, 19 | decoded.len() 20 | ); 21 | } 22 | 23 | #[test] 24 | fn colorchart_eos_7d() { 25 | let buf = fs::read("../samples/images/colorchart-eos-7d.cr2").expect("read in"); 26 | 27 | let processor = Processor::new(); 28 | let decoded = processor.decode(&buf).expect("decoding successful"); 29 | let sizes = decoded.sizes(); 30 | 31 | assert_eq!(sizes.width, 5202); 32 | assert_eq!(sizes.height, 3464); 33 | assert_eq!(sizes.raw_width, 5360); 34 | assert_eq!(sizes.raw_height, 3516); 35 | assert_eq!( 36 | sizes.raw_width as usize * sizes.raw_height as usize, 37 | decoded.len() 38 | ); 39 | } 40 | 41 | #[test] 42 | fn colorchart_iphone7plus_cloudy() { 43 | let buf = fs::read("../samples/images/colorchart-iphone7plus-cloudy.dng").expect("read in"); 44 | 45 | let processor = Processor::new(); 46 | let decoded = processor.decode(&buf).expect("decoding successful"); 47 | let sizes = decoded.sizes(); 48 | 49 | // iPhone image has 90-deg clockwise rotation 50 | assert_eq!(sizes.flip, 6); 51 | // Width and Height are flipped compared to the process test because 52 | // when processing, the image is rotated 53 | assert_eq!(sizes.width, 4032); 54 | assert_eq!(sizes.height, 3024); 55 | assert_eq!(sizes.raw_width, 4032); 56 | assert_eq!(sizes.raw_height, 3024); 57 | assert_eq!( 58 | sizes.raw_width as usize * sizes.raw_height as usize, 59 | decoded.len() 60 | ); 61 | } 62 | 63 | #[test] 64 | fn komainu() { 65 | let buf = fs::read("../samples/images/komainu.nef").expect("read in"); 66 | 67 | let processor = Processor::new(); 68 | let decoded = processor.decode(&buf).expect("decoding successful"); 69 | let sizes = decoded.sizes(); 70 | 71 | assert_eq!(sizes.width, 6034); 72 | assert_eq!(sizes.height, 4028); 73 | assert_eq!(sizes.raw_width, 6080); 74 | assert_eq!(sizes.raw_height, 4028); 75 | assert_eq!( 76 | sizes.raw_width as usize * sizes.raw_height as usize, 77 | decoded.len() 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /libraw/tests/samples_process.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use libraw::Processor; 4 | 5 | macro_rules! process { 6 | ($method: ident) => { 7 | mod $method { 8 | use super::*; 9 | 10 | #[test] 11 | fn colorchart_5d2_6000k() { 12 | let buf = fs::read("../samples/images/colorchart-5D2-6000K.dng").expect("read in"); 13 | 14 | let processor = Processor::new(); 15 | let processed = processor.$method(&buf).expect("processing successful"); 16 | assert_eq!(processed.width(), 5634); 17 | assert_eq!(processed.height(), 3752); 18 | assert_eq!( 19 | processed.len(), 20 | (processed.width() * processed.height() * 3) as usize 21 | ); 22 | } 23 | 24 | #[test] 25 | fn colorchart_eos_7d() { 26 | let buf = fs::read("../samples/images/colorchart-eos-7d.cr2").expect("read in"); 27 | 28 | let processor = Processor::new(); 29 | let processed = processor.$method(&buf).expect("processing successful"); 30 | assert_eq!(processed.width(), 5202); 31 | assert_eq!(processed.height(), 3464); 32 | assert_eq!( 33 | processed.len(), 34 | (processed.width() * processed.height() * 3) as usize 35 | ); 36 | } 37 | 38 | #[test] 39 | fn colorchart_iphone7plus_cloudy() { 40 | let buf = fs::read("../samples/images/colorchart-iphone7plus-cloudy.dng") 41 | .expect("read in"); 42 | 43 | let processor = Processor::new(); 44 | let processed = processor.$method(&buf).expect("processing successful"); 45 | assert_eq!(processed.width(), 3024); 46 | assert_eq!(processed.height(), 4032); 47 | assert_eq!( 48 | processed.len(), 49 | (processed.width() * processed.height() * 3) as usize 50 | ); 51 | } 52 | 53 | #[test] 54 | fn komainu() { 55 | let buf = fs::read("../samples/images/komainu.nef").expect("read in"); 56 | 57 | let processor = Processor::new(); 58 | let processed = processor.$method(&buf).expect("processing successful"); 59 | assert_eq!(processed.width(), 6034); 60 | assert_eq!(processed.height(), 4028); 61 | assert_eq!( 62 | processed.len(), 63 | (processed.width() * processed.height() * 3) as usize 64 | ); 65 | } 66 | } 67 | }; 68 | } 69 | 70 | process!(process_8bit); 71 | process!(process_16bit); 72 | --------------------------------------------------------------------------------